Finish pn532 crate
This commit is contained in:
+1
-1
@@ -3,7 +3,7 @@ runner = "espflash flash --monitor -B 921600 --chip esp32s3 --log-format defmt"
|
|||||||
linker = "/home/fromost/.rustup/toolchains/esp/xtensa-esp-elf/esp-15.2.0_20250920/xtensa-esp-elf/bin/xtensa-esp32s3-elf-gcc"
|
linker = "/home/fromost/.rustup/toolchains/esp/xtensa-esp-elf/esp-15.2.0_20250920/xtensa-esp-elf/bin/xtensa-esp32s3-elf-gcc"
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
DEFMT_LOG = "info,lora=trace,eeprom24x=debug,pn532=trace"
|
DEFMT_LOG = "info,lora=trace,eeprom24x=debug,pn532=debug"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
rustflags = [
|
rustflags = [
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
*.pdf filter=lfs, diff=lfs merge=lfs -text
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="ComposeUnknownValues" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
+6
-6
@@ -15,21 +15,21 @@ test = false
|
|||||||
debug = []
|
debug = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
esp-hal = { version = "~1.1", features = ["defmt", "esp32s3", "unstable"] }
|
esp-hal = { version = "~1.2", features = ["defmt", "esp32s3", "unstable"] }
|
||||||
esp-rtos = { version = "0.3.0", features = [
|
esp-rtos = { version = "0.4.0", features = [
|
||||||
"defmt",
|
"defmt",
|
||||||
"embassy",
|
"embassy",
|
||||||
"esp-alloc",
|
"esp-alloc",
|
||||||
"esp32s3",
|
"esp32s3",
|
||||||
] }
|
] }
|
||||||
esp-alloc = { version = "0.10.0", features = ["defmt"] }
|
esp-alloc = { version = "0.11.0", features = ["defmt"] }
|
||||||
esp-backtrace = { version = "0.19.0", features = [
|
esp-backtrace = { version = "0.20.0", features = [
|
||||||
"defmt",
|
"defmt",
|
||||||
"esp32s3",
|
"esp32s3",
|
||||||
"panic-handler",
|
"panic-handler",
|
||||||
] }
|
] }
|
||||||
esp-println = { version = "0.17.0", features = ["defmt-espflash", "esp32s3"] }
|
esp-println = { version = "0.18.0", features = ["defmt-espflash", "esp32s3"] }
|
||||||
esp-bootloader-esp-idf = { version = "0.5.0", features = ["defmt", "esp32s3"] }
|
esp-bootloader-esp-idf = { version = "0.6.0", features = ["defmt", "esp32s3"] }
|
||||||
|
|
||||||
embassy-executor = { version = "0.10.0", features = ["defmt"] }
|
embassy-executor = { version = "0.10.0", features = ["defmt"] }
|
||||||
embassy-time = { version = "0.5.0", features = ["defmt", "generic-queue-8", "defmt-timestamp-uptime"] }
|
embassy-time = { version = "0.5.0", features = ["defmt", "generic-queue-8", "defmt-timestamp-uptime"] }
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ fn main() {
|
|||||||
println!("cargo:rerun-if-changed=Cargo.toml");
|
println!("cargo:rerun-if-changed=Cargo.toml");
|
||||||
patch_crate::run().expect("Failed while patching");
|
patch_crate::run().expect("Failed while patching");
|
||||||
|
|
||||||
let iter = dotenvy::from_path_iter(".env")
|
let environment = dotenvy::from_path_iter(".env")
|
||||||
.expect("failed to read .env");
|
.expect("failed to read .env");
|
||||||
|
|
||||||
for item in iter {
|
for item in environment {
|
||||||
let (key, value) = item.expect("invalid .env entry");
|
let (key, value) = item.expect("invalid .env entry");
|
||||||
|
|
||||||
println!("cargo::rustc-env={key}={value}");
|
println!("cargo::rustc-env={key}={value}");
|
||||||
|
|||||||
Binary file not shown.
+24
-17
@@ -1,14 +1,14 @@
|
|||||||
//! High-level PN532 command driver.
|
//! High-level PN532 command driver.
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use crate::commands::*;
|
use crate::commands::*;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::interface::Interface;
|
use crate::interface::Interface;
|
||||||
use crate::{NoIrq, NoReset, StatusCode};
|
use crate::types::constants::{PN532_GPIO_P32, PN532_GPIO_P34, PN532_GPIO_VALIDATION_BIT};
|
||||||
|
use crate::{StatusCode};
|
||||||
|
use core::marker::PhantomData;
|
||||||
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::constants::{PN532_GPIO_P32, PN532_GPIO_P34, PN532_GPIO_VALIDATION_BIT};
|
|
||||||
|
|
||||||
/// Driver for the NXP PN532 NFC controller.
|
/// Driver for the NXP PN532 NFC controller.
|
||||||
pub struct Pn532<RST, IRQ, DELAY, B, const N: usize = 64>
|
pub struct Pn532<RST, IRQ, DELAY, B, const N: usize = 64>
|
||||||
@@ -16,7 +16,7 @@ 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) interface: B,
|
pub(crate) interface: B,
|
||||||
pub(crate) in_listed_tag: u8,
|
pub(crate) in_listed_tag: u8,
|
||||||
@@ -31,7 +31,7 @@ where
|
|||||||
RST: OutputPin,
|
RST: OutputPin,
|
||||||
IRQ: InputPin,
|
IRQ: InputPin,
|
||||||
DELAY: DelayNs,
|
DELAY: DelayNs,
|
||||||
B: Interface<RST, IRQ, DELAY>
|
B: Interface<RST, IRQ, DELAY>,
|
||||||
{
|
{
|
||||||
/// Create a driver over the given transport interface.
|
/// Create a driver over the given transport interface.
|
||||||
pub fn new(interface: B) -> Self {
|
pub fn new(interface: B) -> Self {
|
||||||
@@ -45,19 +45,24 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialise the PN532: pulse the reset pin (if provided) and wait for it
|
/// Initialize the PN532: pulse the reset pin (if provided) and wait for it
|
||||||
/// to become ready.
|
/// to become ready.
|
||||||
pub fn begin(&mut self) -> Result<(), Error<B::TransportError>> {
|
pub fn begin(&mut self) -> Result<(), Error<B::TransportError>> {
|
||||||
if let Err(Error::UnusedPin) = self.interface.begin() {
|
match self.interface.begin() {
|
||||||
crate::debug!("No reset pin")
|
Ok(_) => Ok(()),
|
||||||
|
Err(Error::UnusedPin) => {
|
||||||
|
crate::debug!("Unused pin");
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- internal helpers ---------------------------------------------------
|
// -- internal helpers ---------------------------------------------------
|
||||||
|
|
||||||
pub(crate) fn send(&mut self, header_len: usize) -> Result<(), Error<B::TransportError>> {
|
pub(crate) fn send(&mut self, header_len: usize) -> Result<(), Error<B::TransportError>> {
|
||||||
self.interface.write_command(&self.buffer[..header_len], &[])
|
self.interface
|
||||||
|
.write_command(&self.buffer[..header_len], &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_with_body(
|
pub(crate) fn send_with_body(
|
||||||
@@ -73,8 +78,12 @@ where
|
|||||||
self.interface.read_response(&mut self.buffer, 1000)
|
self.interface.read_response(&mut self.buffer, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_timeout(&mut self, timeout: Duration) -> Result<usize, Error<B::TransportError>> {
|
pub(crate) fn read_timeout(
|
||||||
self.interface.read_response(&mut self.buffer, timeout.as_millis() as u16)
|
&mut self,
|
||||||
|
timeout: Duration,
|
||||||
|
) -> Result<usize, Error<B::TransportError>> {
|
||||||
|
self.interface
|
||||||
|
.read_response(&mut self.buffer, timeout.as_millis() as u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- generic PN532 functions -------------------------------------------
|
// -- generic PN532 functions -------------------------------------------
|
||||||
@@ -120,10 +129,10 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set the PN532's GPIO pins (see the PN532 user manual for valid pins).
|
/// Set the PN532's GPIO pins (see the PN532 user manual for valid pins).
|
||||||
pub fn write_gpio(&mut self, pinstate: u8) -> Result<(), Error<B::TransportError>> {
|
pub fn write_gpio(&mut self, pin_state: u8) -> Result<(), Error<B::TransportError>> {
|
||||||
let pinstate = pinstate | (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34);
|
let pin_state = pin_state | (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34);
|
||||||
self.buffer[0] = PN532Command::WriteGpio.into_bits();
|
self.buffer[0] = PN532Command::WriteGpio.into_bits();
|
||||||
self.buffer[1] = PN532_GPIO_VALIDATION_BIT | pinstate;
|
self.buffer[1] = PN532_GPIO_VALIDATION_BIT | pin_state;
|
||||||
self.buffer[2] = 0x00;
|
self.buffer[2] = 0x00;
|
||||||
self.send(3)?;
|
self.send(3)?;
|
||||||
let len = self.read()?;
|
let len = self.read()?;
|
||||||
@@ -190,8 +199,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Release the card.
|
/// Release the card.
|
||||||
pub fn release(&mut self) -> Result<(), Error<B::TransportError>> {
|
pub fn release(&mut self) -> Result<(), Error<B::TransportError>> {
|
||||||
self.buffer[0] = PN532Command::InRelease.into_bits();
|
self.buffer[0] = PN532Command::InRelease.into_bits();
|
||||||
|
|||||||
@@ -118,11 +118,7 @@ where
|
|||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
timeout_ms: u16,
|
timeout_ms: u16,
|
||||||
) -> Result<usize, Error<Self::TransportError>> {
|
) -> Result<usize, Error<Self::TransportError>> {
|
||||||
// Wait for the PN532 to signal data is ready.
|
|
||||||
self.wait_ready(timeout_ms)?;
|
self.wait_ready(timeout_ms)?;
|
||||||
|
|
||||||
// Read the full response frame in a single transaction, stripping the
|
|
||||||
// RDY byte. Frame layout: PREAMBLE(3) + LEN + LCS + TFI + CMD + payload + DCS + POSTAMBLE.
|
|
||||||
let frame_len = buf.len() + 9;
|
let frame_len = buf.len() + 9;
|
||||||
if frame_len > MAX_DATA_LEN + 10 {
|
if frame_len > MAX_DATA_LEN + 10 {
|
||||||
return Err(Error::NoSpace);
|
return Err(Error::NoSpace);
|
||||||
@@ -148,18 +144,11 @@ where
|
|||||||
IRQ: InputPin,
|
IRQ: InputPin,
|
||||||
{
|
{
|
||||||
/// Wait until the PN532 signals data is ready.
|
/// Wait until the PN532 signals data is ready.
|
||||||
///
|
|
||||||
/// With an IRQ pin this blocks until the pin goes low; without one it polls
|
|
||||||
/// the I2C status byte. Either way the status byte is consumed, so the next
|
|
||||||
/// read returns the frame itself.
|
|
||||||
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;
|
||||||
loop {
|
loop {
|
||||||
match self.irq.is_low() {
|
match self.irq.is_low() {
|
||||||
Ok(true) => {
|
Ok(true) => {
|
||||||
// IRQ asserted (active-low): data is ready. Poll the status
|
|
||||||
// byte to confirm and consume it so the next read gets the
|
|
||||||
// frame itself.
|
|
||||||
let mut status = [0u8; 1];
|
let mut status = [0u8; 1];
|
||||||
match self.i2c.read(PN532_I2C_ADDRESS, &mut status) {
|
match self.i2c.read(PN532_I2C_ADDRESS, &mut status) {
|
||||||
Ok(()) if status[0] & 1 == 1 => return Ok(()),
|
Ok(()) if status[0] & 1 == 1 => return Ok(()),
|
||||||
@@ -173,8 +162,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(false) | Err(_) => {
|
Ok(false) | Err(_) => {
|
||||||
// IRQ connected but not asserted yet (or a read error):
|
|
||||||
// keep waiting.
|
|
||||||
self.delay.delay_ms(1);
|
self.delay.delay_ms(1);
|
||||||
elapsed += 1;
|
elapsed += 1;
|
||||||
if timeout_ms != 0 && elapsed >= timeout_ms {
|
if timeout_ms != 0 && elapsed >= timeout_ms {
|
||||||
|
|||||||
@@ -40,66 +40,39 @@ where
|
|||||||
let cipher = TdesEde2::new(&key);
|
let cipher = TdesEde2::new(&key);
|
||||||
|
|
||||||
Ok(SessionKeys::new(&cipher, &rc))
|
Ok(SessionKeys::new(&cipher, &rc))
|
||||||
|
|
||||||
// crate::trace!("RC: {:02X}", rc);
|
|
||||||
// crate::trace!("CK DES key: {:02X}", des_key);
|
|
||||||
// crate::trace!("RC DES input: {:02X}", des_rc);
|
|
||||||
// crate::trace!("SK1 DES: {:02X}", sk1_des);
|
|
||||||
// crate::trace!("SK1: {:02X}", sk1);
|
|
||||||
// crate::trace!("SK2 DES: {:02X}", sk2_des);
|
|
||||||
// crate::trace!("SK2: {:02X}", sk2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_mac<const U: usize>(
|
/// 2-key Triple DES-CBC MAC over `block_list` followed by `block_data`.
|
||||||
|
pub(crate) fn compute_mac(
|
||||||
&mut self,
|
&mut self,
|
||||||
session: &AuthSession,
|
session: &AuthSession,
|
||||||
input: &MacInput<U>
|
key_bytes: [u8; 16],
|
||||||
|
block_list: &[u8; 8],
|
||||||
|
block_data: &[u8],
|
||||||
) -> [u8; 8] {
|
) -> [u8; 8] {
|
||||||
// data layout from get_plain_text():
|
|
||||||
//
|
|
||||||
// [0..8] = block list
|
|
||||||
// [8..24] = ID block
|
|
||||||
// [24..40] = CKV block
|
|
||||||
// [40..56] = MAC_A block (NOT used in MAC calculation)
|
|
||||||
|
|
||||||
let block_list = &input.block_list();
|
|
||||||
let block_data = &input.block_data();
|
|
||||||
|
|
||||||
// FeliCa Lite-S uses the session keys with their byte order reversed
|
|
||||||
// at the DES interface.
|
|
||||||
let key_bytes = session.sk.as_des();
|
|
||||||
|
|
||||||
let key = Key::<TdesEde2>::try_from(key_bytes).unwrap();
|
let key = Key::<TdesEde2>::try_from(key_bytes).unwrap();
|
||||||
let cipher = TdesEde2::new(&key);
|
let cipher = TdesEde2::new(&key);
|
||||||
|
|
||||||
// RC1 is also byte-reversed at the DES interface.
|
|
||||||
let mut state_bytes = session.rc.rc1();
|
let mut state_bytes = session.rc.rc1();
|
||||||
state_bytes.reverse();
|
state_bytes.reverse();
|
||||||
|
|
||||||
// The initial plaintext for MAC_A is the block-number list.
|
let mut block_list = block_list.clone();
|
||||||
// Byte order is reversed before the DES operation.
|
block_list.reverse();
|
||||||
let mut first = [0u8; 8];
|
|
||||||
first.copy_from_slice(block_list);
|
|
||||||
first.reverse();
|
|
||||||
|
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
state_bytes[i] ^= first[i];
|
state_bytes[i] ^= block_list[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut state = Block::<TdesEde2>::try_from(state_bytes).unwrap();
|
let mut state_block = Block::<TdesEde2>::try_from(state_bytes).unwrap();
|
||||||
cipher.encrypt_block(&mut state);
|
cipher.encrypt_block(&mut state_block);
|
||||||
|
|
||||||
// Process ID and CKV.
|
|
||||||
//
|
|
||||||
// Each 8-byte piece is byte-reversed before entering the
|
|
||||||
// 2-key 3DES CBC operation.
|
|
||||||
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);
|
||||||
input.reverse();
|
input.reverse();
|
||||||
|
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
input[i] ^= state[i];
|
input[i] ^= state_block[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::trace!("MAC input: {:02X}", input);
|
crate::trace!("MAC input: {:02X}", input);
|
||||||
@@ -107,16 +80,26 @@ where
|
|||||||
let mut block = Block::<TdesEde2>::try_from(input).unwrap();
|
let mut block = Block::<TdesEde2>::try_from(input).unwrap();
|
||||||
cipher.encrypt_block(&mut block);
|
cipher.encrypt_block(&mut block);
|
||||||
|
|
||||||
state = block;
|
state_block = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse the final result back to FeliCa byte order.
|
let mut mac: [u8; 8] = state_block.0;
|
||||||
let mut mac: [u8; 8] = state.0;
|
|
||||||
mac.reverse();
|
mac.reverse();
|
||||||
crate::trace!("mac: {:02X}, maca: {:02X}", mac, input.maca());
|
crate::trace!("mac: {:02X}", mac);
|
||||||
mac
|
mac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute the MAC_A value for a Write With MAC operation.
|
||||||
|
pub(crate) fn compute_write_mac(
|
||||||
|
&mut self,
|
||||||
|
session: &AuthSession,
|
||||||
|
plain: &[u8; 24],
|
||||||
|
) -> [u8; 8] {
|
||||||
|
let mut header = [0u8; 8];
|
||||||
|
header.copy_from_slice(&plain[0..8]);
|
||||||
|
self.compute_mac(session, session.sk.as_des_write(), &header, &plain[8..24])
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn check_auth(
|
pub(crate) fn check_auth(
|
||||||
&mut self,
|
&mut self,
|
||||||
card: &CardSession,
|
card: &CardSession,
|
||||||
@@ -133,7 +116,12 @@ where
|
|||||||
let input = MacInput::new(
|
let input = MacInput::new(
|
||||||
&data, block_list
|
&data, block_list
|
||||||
);
|
);
|
||||||
let mac = self.compute_mac(session, &input);
|
let mac = self.compute_mac(
|
||||||
|
session,
|
||||||
|
session.sk.as_des(),
|
||||||
|
&input.block_list(),
|
||||||
|
&input.block_data(),
|
||||||
|
);
|
||||||
Ok(mac == input.maca())
|
Ok(mac == input.maca())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
use embedded_hal::digital::{InputPin, OutputPin};
|
use embedded_hal::digital::{InputPin, OutputPin};
|
||||||
use crate::{Interface, Pn532};
|
use crate::{Error, Interface, Pn532};
|
||||||
|
use crate::types::felica::{CardSession, FelicaLiteSBlock};
|
||||||
|
use crate::types::felica::felica_lite_s::auth::{AuthSession, CardKey, CardKeyVersion};
|
||||||
|
use crate::types::felica::felica_lite_s::config::MemoryConfig;
|
||||||
|
|
||||||
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
|
||||||
@@ -9,7 +12,92 @@ where
|
|||||||
DELAY: DelayNs,
|
DELAY: DelayNs,
|
||||||
B: Interface<RST, IRQ, DELAY>
|
B: Interface<RST, IRQ, DELAY>
|
||||||
{
|
{
|
||||||
pub fn setup_first_issuance(&mut self) {
|
pub fn set_id(&mut self, card: &CardSession, arbitrary: [u8; 6], dfc: Option<[u8; 2]>) -> Result<(), Error<B::TransportError>> {
|
||||||
todo!()
|
let dfc = dfc.unwrap_or_default();
|
||||||
|
let mut block = [0u8; 16];
|
||||||
|
block[8] = dfc[0];
|
||||||
|
block[9] = dfc[1];
|
||||||
|
block[10..16].copy_from_slice(&arbitrary);
|
||||||
|
self.write_block(
|
||||||
|
card,
|
||||||
|
&[FelicaLiteSBlock::ID],
|
||||||
|
&[block]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_id(&mut self, card: &CardSession) -> Result<[u8; 16], Error<B::TransportError>> {
|
||||||
|
let mut block = [[0u8; 16]];
|
||||||
|
self.read_block(
|
||||||
|
card,
|
||||||
|
&[FelicaLiteSBlock::ID],
|
||||||
|
&mut block
|
||||||
|
)?;
|
||||||
|
crate::trace!("ID block: {:02X}", block);
|
||||||
|
Ok(block[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ck(&mut self, card: &CardSession, key: &CardKey) -> Result<(), Error<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>> {
|
||||||
|
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]];
|
||||||
|
self.read_block(
|
||||||
|
card,
|
||||||
|
&[FelicaLiteSBlock::CKV],
|
||||||
|
&mut block
|
||||||
|
)?;
|
||||||
|
crate::trace!("CKV block: {:02X}", block);
|
||||||
|
Ok(CardKeyVersion::from(block[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mc(&mut self, card: &CardSession) -> Result<MemoryConfig, Error<B::TransportError>> {
|
||||||
|
let mut block = [[0u8; 16]];
|
||||||
|
self.read_block(
|
||||||
|
card,
|
||||||
|
&[FelicaLiteSBlock::MemoryConfig],
|
||||||
|
&mut block
|
||||||
|
)?;
|
||||||
|
crate::trace!("MC block: {:02X}", block[0]);
|
||||||
|
Ok(MemoryConfig::from(block[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mc(&mut self, card: &CardSession, config: MemoryConfig) -> Result<(), Error<B::TransportError>> {
|
||||||
|
let block: [u8; 16] = config.into();
|
||||||
|
self.write_block(
|
||||||
|
card,
|
||||||
|
&[FelicaLiteSBlock::MemoryConfig],
|
||||||
|
&[block]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,11 +23,12 @@ where
|
|||||||
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_auth(card, &session)?;
|
||||||
if is_valid_auth {
|
|
||||||
Ok(Some(session))
|
if !is_valid_auth {
|
||||||
} else {
|
return Ok(None);
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
|
self.try_external_authentication(card, &session)?;
|
||||||
|
Ok(Some(session))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_with_mac(
|
pub fn read_with_mac(
|
||||||
@@ -40,7 +41,12 @@ where
|
|||||||
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(session, &input);
|
let mac = self.compute_mac(
|
||||||
|
session,
|
||||||
|
session.sk.as_des(),
|
||||||
|
&input.block_list(),
|
||||||
|
&input.block_data(),
|
||||||
|
);
|
||||||
if mac != input.maca() {
|
if mac != input.maca() {
|
||||||
return Err(crate::Error::FelicaError(Error::MACMismatch));
|
return Err(crate::Error::FelicaError(Error::MACMismatch));
|
||||||
}
|
}
|
||||||
@@ -48,6 +54,47 @@ where
|
|||||||
Ok(block_data[0])
|
Ok(block_data[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write one data block protected by a Write With MAC (manual §5.4.4).
|
||||||
|
pub fn write_with_mac(
|
||||||
|
&mut self,
|
||||||
|
card: &CardSession,
|
||||||
|
session: &AuthSession,
|
||||||
|
block: impl AsBlock,
|
||||||
|
data: &[u8; 16],
|
||||||
|
) -> Result<(), crate::Error<B::TransportError>> {
|
||||||
|
let mut wcnt_block = [[0u8; 16]];
|
||||||
|
self.read_block(card, &[FelicaLiteSBlock::WCNT], &mut wcnt_block)?;
|
||||||
|
let mut wcnt = [0u8; 3];
|
||||||
|
wcnt.copy_from_slice(&wcnt_block[0][0..3]);
|
||||||
|
|
||||||
|
let mut plain = [0u8; 24];
|
||||||
|
plain[0..3].copy_from_slice(&wcnt);
|
||||||
|
plain[4] = block.as_block() as u8;
|
||||||
|
plain[6] = FelicaLiteSBlock::MACA.as_block() as u8;
|
||||||
|
plain[8..24].copy_from_slice(data);
|
||||||
|
|
||||||
|
let mac = self.compute_write_mac(session, &plain);
|
||||||
|
|
||||||
|
let mut mac_a = [0u8; 16];
|
||||||
|
mac_a[0..8].copy_from_slice(&mac);
|
||||||
|
mac_a[8..11].copy_from_slice(&wcnt);
|
||||||
|
|
||||||
|
let block_list = [block.as_block(), FelicaLiteSBlock::MACA as u16];
|
||||||
|
let block_data = [*data, mac_a];
|
||||||
|
self.write_block(card, &block_list, &block_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Complete External Authentication (manual §5.4.2, Figure 5-10).
|
||||||
|
pub(crate) fn try_external_authentication(
|
||||||
|
&mut self,
|
||||||
|
card: &CardSession,
|
||||||
|
session: &AuthSession,
|
||||||
|
) -> Result<(), crate::Error<B::TransportError>> {
|
||||||
|
let mut state = [0u8; 16];
|
||||||
|
state[0] = 0x01; // EXT_AUTH: after authentication
|
||||||
|
self.write_with_mac(card, session, FelicaLiteSBlock::STATE, &state)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_block<const U: usize>(
|
pub fn read_block<const U: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
card: &CardSession,
|
card: &CardSession,
|
||||||
|
|||||||
@@ -184,12 +184,13 @@ where
|
|||||||
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) {
|
||||||
|
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
|
||||||
|
}
|
||||||
if response_len != 12 + 16 * U {
|
if response_len != 12 + 16 * U {
|
||||||
return Err(Error::InvalidFrame);
|
return Err(Error::InvalidFrame);
|
||||||
}
|
}
|
||||||
if response[9] != 0 || response[10] != 0 {
|
|
||||||
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut k = 12;
|
let mut k = 12;
|
||||||
for block in block_data.iter_mut().take(U) {
|
for block in block_data.iter_mut().take(U) {
|
||||||
@@ -253,12 +254,13 @@ where
|
|||||||
&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) {
|
||||||
|
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
|
||||||
|
}
|
||||||
if response_len != RESPONSE_SIZE {
|
if response_len != RESPONSE_SIZE {
|
||||||
return Err(Error::InvalidFrame);
|
return Err(Error::InvalidFrame);
|
||||||
}
|
}
|
||||||
if response[9] != 0 || response[10] != 0 {
|
|
||||||
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use core::ops::Deref;
|
|||||||
|
|
||||||
#[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 CardKey(pub [u8; 16]);
|
pub struct CardKey([u8; 16]);
|
||||||
|
|
||||||
impl CardKey {
|
impl CardKey {
|
||||||
/// FeliCa byte order:
|
/// FeliCa byte order:
|
||||||
@@ -51,3 +51,40 @@ impl Display for CardKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct CardKeyVersion([u8; 2]);
|
||||||
|
|
||||||
|
impl CardKeyVersion {
|
||||||
|
pub fn new(version: u16) -> Self {
|
||||||
|
let bytes = version.to_be_bytes();
|
||||||
|
CardKeyVersion(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_block(&self) -> [u8; 16] {
|
||||||
|
let mut block = [0u8; 16];
|
||||||
|
block[0] = self.0[0];
|
||||||
|
block[1] = self.0[1];
|
||||||
|
block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[u8; 16]> for CardKeyVersion {
|
||||||
|
fn from(val: [u8; 16]) -> Self {
|
||||||
|
CardKeyVersion([val[0], val[1]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CardKeyVersion {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
{
|
||||||
|
write!(f, "{}", hex::encode_upper(self.0))
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
{
|
||||||
|
write!(f, "{:?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,11 +41,21 @@ impl SessionKeys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_des(&self) -> [u8; 16] {
|
pub fn as_des(&self) -> [u8; 16] {
|
||||||
|
Self::key_des(&self.sk1, &self.sk2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Session keys in DES byte order with the halves swapped (`SK2 || SK1`),
|
||||||
|
/// as required by the data-write MAC generation.
|
||||||
|
pub fn as_des_write(&self) -> [u8; 16] {
|
||||||
|
Self::key_des(&self.sk2, &self.sk1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_des(first: &[u8; 8], second: &[u8; 8]) -> [u8; 16] {
|
||||||
let mut key_bytes = [0u8; 16];
|
let mut key_bytes = [0u8; 16];
|
||||||
|
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
key_bytes[i] = self.sk1[7 - i];
|
key_bytes[i] = first[7 - i];
|
||||||
key_bytes[8 + i] = self.sk2[7 - i];
|
key_bytes[8 + i] = second[7 - i];
|
||||||
}
|
}
|
||||||
|
|
||||||
key_bytes
|
key_bytes
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
use bitfields::bitflag;
|
||||||
|
|
||||||
|
#[bitflag(u8)]
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum AccessLevel {
|
||||||
|
ReadOnly = 0,
|
||||||
|
#[base]
|
||||||
|
ReadWrite = 1,
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
mod access_level;
|
||||||
|
use access_level::AccessLevel;
|
||||||
|
|
||||||
|
use bitfields::bitfield;
|
||||||
|
|
||||||
|
#[bitfield([u8; 4], from_endian = little, into_endian = little)]
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct Access {
|
||||||
|
#[bits(16)]
|
||||||
|
pub scratch_pad: ScratchPadAccessLevel,
|
||||||
|
/// 0xFF: ReadWrite
|
||||||
|
/// Other than 0xFF: Readonly
|
||||||
|
#[bits(8, default = 0)]
|
||||||
|
pub system_block: u8,
|
||||||
|
#[bits(1)]
|
||||||
|
pub is_ck_ckv_rewritable: bool,
|
||||||
|
#[bits(7)]
|
||||||
|
_reserved: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitfield([u8; 7], from_endian = little, into_endian = little)]
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct Authentication {
|
||||||
|
#[bits(16)]
|
||||||
|
pub scratch_pad_read: ScratchPadAuth,
|
||||||
|
#[bits(16)]
|
||||||
|
pub scratch_pad_write: ScratchPadAuth,
|
||||||
|
#[bits(16)]
|
||||||
|
pub scratch_pad_mac_write: ScratchPadAuth,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub state_with_mac: bool,
|
||||||
|
#[bits(7, default = 0)]
|
||||||
|
_reserved_state: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitfield(u16)]
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct ScratchPadAccessLevel {
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad0: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad1: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad2: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad3: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad4: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad5: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad6: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad7: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad8: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad9: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad10: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad11: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad12: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub pad13: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub reg: AccessLevel,
|
||||||
|
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||||
|
pub mc: AccessLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitfield(u16)]
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct ScratchPadAuth {
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad0: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad1: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad2: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad3: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad4: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad5: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad6: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad7: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad8: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad9: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad10: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad11: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad12: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub pad13: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
pub reg: bool,
|
||||||
|
#[bits(1, default = false)]
|
||||||
|
_reserved: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitfield(u8)]
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct RFParameter {
|
||||||
|
#[bits(3, default = 0x07)]
|
||||||
|
pub parameter: u8,
|
||||||
|
#[bits(5, default = 0x1F)]
|
||||||
|
_reserved: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct MemoryConfig {
|
||||||
|
pub access: Access,
|
||||||
|
pub rf: RFParameter,
|
||||||
|
pub auth: Authentication,
|
||||||
|
/// 0x01: compatible
|
||||||
|
/// 0x00: incompatible
|
||||||
|
/// 0x02~0xFF: prohibited
|
||||||
|
pub ndef: u8,
|
||||||
|
_reserved: [u8; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MemoryConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
access: Access::from([0xFF, 0xFF, 0xFF, 0x00]),
|
||||||
|
rf: RFParameter::default(),
|
||||||
|
ndef: 0x00,
|
||||||
|
auth: Authentication::default(),
|
||||||
|
_reserved: [0u8; 3],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<[u8; 16]> for MemoryConfig {
|
||||||
|
fn into(self) -> [u8; 16] {
|
||||||
|
let mut block = [0u8; 16];
|
||||||
|
|
||||||
|
let access = self.access.into_bytes();
|
||||||
|
|
||||||
|
block[0] = access[0];
|
||||||
|
block[1] = access[1];
|
||||||
|
block[2] = access[2];
|
||||||
|
block[3] = self.ndef;
|
||||||
|
block[4] = self.rf.into_bits();
|
||||||
|
block[5] = access[3];
|
||||||
|
|
||||||
|
block[6..13].copy_from_slice(&self.auth.into_bytes());
|
||||||
|
|
||||||
|
block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[u8; 16]> for MemoryConfig {
|
||||||
|
fn from(value: [u8; 16]) -> Self {
|
||||||
|
let access = Access::from([value[0], value[1], value[2], value[5]]);
|
||||||
|
|
||||||
|
let rf = RFParameter::from(value[4]);
|
||||||
|
|
||||||
|
let auth: [u8; 7] = value[6..13].try_into().unwrap();
|
||||||
|
let auth = Authentication::from(auth);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
access,
|
||||||
|
rf,
|
||||||
|
auth,
|
||||||
|
ndef: value[3],
|
||||||
|
_reserved: value[13..16].try_into().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,10 @@
|
|||||||
///! 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;
|
||||||
|
|
||||||
use bitfields::bitflag;
|
use bitfields::bitflag;
|
||||||
use super::{AsBlock, IDm, PMm};
|
use super::AsBlock;
|
||||||
use super::AsServiceCode;
|
use super::AsServiceCode;
|
||||||
|
|
||||||
/// Service Code
|
/// Service Code
|
||||||
@@ -68,7 +69,9 @@ pub enum Block {
|
|||||||
/// Write counter block.
|
/// Write counter block.
|
||||||
WCNT = 0x8090,
|
WCNT = 0x8090,
|
||||||
/// MAC_A block.
|
/// MAC_A block.
|
||||||
MACA = 0x8091
|
MACA = 0x8091,
|
||||||
|
/// STATE block (EXT_AUTH / POLL_DIS).
|
||||||
|
STATE = 0x8092
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsBlock for Block {
|
impl AsBlock for Block {
|
||||||
|
|||||||
@@ -7,25 +7,8 @@ use bitfields::bitflag;
|
|||||||
pub enum CardICType {
|
pub enum CardICType {
|
||||||
#[base]
|
#[base]
|
||||||
Unknown = 0x00,
|
Unknown = 0x00,
|
||||||
S140 = 0x50,
|
S966 = 0xF1,
|
||||||
SA40_2P = 0x51,
|
S965 = 0xF0,
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://www.sony.co.jp/en/Products/felica/business/tech-support/list.html
|
/// https://www.sony.co.jp/en/Products/felica/business/tech-support/list.html
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use core::fmt::{Display, Formatter};
|
use core::fmt::{Display, Formatter};
|
||||||
use bitfields::bitfield;
|
use bitfields::bitfield;
|
||||||
|
|
||||||
#[bitfield([u8; 8], from_endian = big, into_endian = big)]
|
#[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 {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use core::fmt::{Display, Formatter};
|
|||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
use bitfields::bitfield;
|
use bitfields::bitfield;
|
||||||
|
|
||||||
#[bitfield([u8; 8])]
|
#[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 PMm {
|
pub struct PMm {
|
||||||
|
|||||||
+35
-36
@@ -13,7 +13,6 @@ use embassy_sync::pubsub::PubSubChannel;
|
|||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use esp_hal::gpio::{InputConfig, Level, Pull};
|
use esp_hal::gpio::{InputConfig, Level, Pull};
|
||||||
use esp_hal::i2c::master::I2c;
|
use esp_hal::i2c::master::I2c;
|
||||||
use esp_hal::interrupt::software::SoftwareInterruptControl;
|
|
||||||
use esp_hal::peripherals::Peripherals;
|
use esp_hal::peripherals::Peripherals;
|
||||||
use esp_hal::rng::{Trng, TrngSource};
|
use esp_hal::rng::{Trng, TrngSource};
|
||||||
use esp_hal::spi::master::{Config, Spi};
|
use esp_hal::spi::master::{Config, Spi};
|
||||||
@@ -22,6 +21,7 @@ use esp_hal::{gpio, Async, Blocking};
|
|||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
pub(crate) static LED_STATE: AtomicBool = AtomicBool::new(false);
|
pub(crate) static LED_STATE: AtomicBool = AtomicBool::new(false);
|
||||||
|
static TRNG_SOURCE: StaticCell<TrngSource> = StaticCell::new();
|
||||||
|
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
spawner: Spawner,
|
spawner: Spawner,
|
||||||
@@ -29,16 +29,19 @@ pub struct Board {
|
|||||||
clock: Clock<'static>,
|
clock: Clock<'static>,
|
||||||
storage: RomStorage<'static>,
|
storage: RomStorage<'static>,
|
||||||
card_reader: CardReader<'static>,
|
card_reader: CardReader<'static>,
|
||||||
trng: Trng
|
trng: Trng,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Board {
|
impl Board {
|
||||||
pub async fn new(spawner: Spawner, peripherals: Peripherals) -> Result<Self, crate::error::Error> {
|
pub async fn new(
|
||||||
|
spawner: Spawner,
|
||||||
|
peripherals: Peripherals,
|
||||||
|
) -> Result<Self, crate::error::Error> {
|
||||||
let timer_group = TimerGroup::new(peripherals.TIMG0);
|
let timer_group = TimerGroup::new(peripherals.TIMG0);
|
||||||
let sw_ctrl = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
esp_rtos::start(timer_group.timer0, peripherals.FROM_CPU_INTR0);
|
||||||
esp_rtos::start(timer_group.timer0, sw_ctrl.software_interrupt0);
|
|
||||||
|
|
||||||
static SPI_BUS: StaticCell<Mutex<CriticalSectionRawMutex, Spi<'static, Async>>> = StaticCell::new();
|
static SPI_BUS: StaticCell<Mutex<CriticalSectionRawMutex, Spi<'static, Async>>> =
|
||||||
|
StaticCell::new();
|
||||||
let spi = Spi::new(peripherals.SPI2, Config::default())?
|
let spi = Spi::new(peripherals.SPI2, Config::default())?
|
||||||
.into_async()
|
.into_async()
|
||||||
.with_sck(peripherals.GPIO9)
|
.with_sck(peripherals.GPIO9)
|
||||||
@@ -46,21 +49,22 @@ impl Board {
|
|||||||
.with_miso(peripherals.GPIO11);
|
.with_miso(peripherals.GPIO11);
|
||||||
let spi_bus = SPI_BUS.init(Mutex::new(spi));
|
let spi_bus = SPI_BUS.init(Mutex::new(spi));
|
||||||
|
|
||||||
static I2C_BUS: StaticCell<BlockingMutex<NoopRawMutex, RefCell<I2c<'static, Blocking>>>> = StaticCell::new();
|
static I2C_BUS: StaticCell<BlockingMutex<NoopRawMutex, RefCell<I2c<'static, Blocking>>>> =
|
||||||
let async_i2c = I2c::new(
|
StaticCell::new();
|
||||||
peripherals.I2C0,
|
let async_i2c = I2c::new(peripherals.I2C0, esp_hal::i2c::master::Config::default())?
|
||||||
esp_hal::i2c::master::Config::default()
|
|
||||||
)?
|
|
||||||
.with_sda(peripherals.GPIO21)
|
.with_sda(peripherals.GPIO21)
|
||||||
.with_scl(peripherals.GPIO19);
|
.with_scl(peripherals.GPIO19);
|
||||||
let i2c_bus = I2C_BUS.init(BlockingMutex::new(RefCell::new(async_i2c)));
|
let i2c_bus = I2C_BUS.init(BlockingMutex::new(RefCell::new(async_i2c)));
|
||||||
|
|
||||||
let irq = gpio::Input::new(peripherals.GPIO6, InputConfig::default().with_pull(Pull::Up));
|
let irq = gpio::Input::new(
|
||||||
|
peripherals.GPIO6,
|
||||||
|
InputConfig::default().with_pull(Pull::Up),
|
||||||
|
);
|
||||||
let reset = gpio::Output::new(peripherals.GPIO5, Level::High, Default::default());
|
let reset = gpio::Output::new(peripherals.GPIO5, Level::High, Default::default());
|
||||||
let card_reader = CardReader::new(SharedI2c::new(i2c_bus), irq, reset);
|
let card_reader = CardReader::new(SharedI2c::new(i2c_bus), irq, reset);
|
||||||
|
|
||||||
let vext = gpio::Output::new(peripherals.GPIO36, Level::Low, Default::default());
|
|
||||||
let lora = {
|
let lora = {
|
||||||
|
let vext = gpio::Output::new(peripherals.GPIO36, Level::Low, Default::default());
|
||||||
let reset = gpio::Output::new(peripherals.GPIO12, Level::Low, Default::default());
|
let reset = gpio::Output::new(peripherals.GPIO12, Level::Low, Default::default());
|
||||||
let busy = gpio::Input::new(peripherals.GPIO13, InputConfig::default());
|
let busy = gpio::Input::new(peripherals.GPIO13, InputConfig::default());
|
||||||
let dio1 = gpio::Input::new(peripherals.GPIO14, InputConfig::default());
|
let dio1 = gpio::Input::new(peripherals.GPIO14, InputConfig::default());
|
||||||
@@ -74,12 +78,10 @@ impl Board {
|
|||||||
let led = gpio::Output::new(peripherals.GPIO35, Level::Low, Default::default());
|
let led = gpio::Output::new(peripherals.GPIO35, Level::Low, Default::default());
|
||||||
spawner.spawn(led_task(led)?);
|
spawner.spawn(led_task(led)?);
|
||||||
|
|
||||||
let storage = RomStorage::new(
|
let storage = RomStorage::new(SharedI2c::new(i2c_bus));
|
||||||
SharedI2c::new(i2c_bus)
|
|
||||||
);
|
|
||||||
|
|
||||||
let trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1);
|
let trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1);
|
||||||
core::mem::forget(trng_source);
|
TRNG_SOURCE.init(trng_source);
|
||||||
let trng = Trng::try_new()?;
|
let trng = Trng::try_new()?;
|
||||||
|
|
||||||
let instance = Self {
|
let instance = Self {
|
||||||
@@ -88,12 +90,15 @@ impl Board {
|
|||||||
clock,
|
clock,
|
||||||
storage,
|
storage,
|
||||||
card_reader,
|
card_reader,
|
||||||
trng
|
trng,
|
||||||
};
|
};
|
||||||
Ok(instance)
|
Ok(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(mut self) -> Result<(), crate::error::Error> {
|
pub async fn start(mut self) -> Result<(), crate::error::Error> {
|
||||||
|
static LORA_CHANNEL: StaticCell<LoraPubSub> = StaticCell::new();
|
||||||
|
let lora_channel = LORA_CHANNEL.init(PubSubChannel::new());
|
||||||
|
|
||||||
self.card_reader.init()?;
|
self.card_reader.init()?;
|
||||||
self.lora.try_join().await?;
|
self.lora.try_join().await?;
|
||||||
defmt::info!("Board started");
|
defmt::info!("Board started");
|
||||||
@@ -102,30 +107,24 @@ impl Board {
|
|||||||
let time = self.lora.get_time(self.clock.get_datetime()?).await?;
|
let time = self.lora.get_time(self.clock.get_datetime()?).await?;
|
||||||
let datetime = self.clock.update_time(time)?;
|
let datetime = self.clock.update_time(time)?;
|
||||||
let app_data = AppData {
|
let app_data = AppData {
|
||||||
last_boot_time: datetime
|
last_boot_time: datetime,
|
||||||
};
|
};
|
||||||
defmt::debug!("stored app data: {}", self.storage.get_data::<AppData>()?);
|
defmt::debug!("stored app data: {}", self.storage.get_data::<AppData>()?);
|
||||||
self.storage.write_data(&app_data).await?;
|
self.storage.write_data(&app_data).await?;
|
||||||
|
|
||||||
static LORA_CHANNEL: StaticCell<LoraPubSub> = StaticCell::new();
|
self.spawner
|
||||||
let lora_channel = LORA_CHANNEL.init(PubSubChannel::new());
|
.spawn(components::card_reader::start_card_reader(
|
||||||
|
|
||||||
self.spawner.spawn(
|
|
||||||
components::card_reader::start_card_reader(
|
|
||||||
self.card_reader,
|
self.card_reader,
|
||||||
lora_channel.publisher()?,
|
lora_channel.publisher()?,
|
||||||
self.trng.clone()
|
self.trng.clone(),
|
||||||
)?
|
)?);
|
||||||
);
|
self.spawner.spawn(components::lora::start_send_data_task(
|
||||||
self.spawner.spawn(
|
self.lora,
|
||||||
components::lora::start_send_data_task(
|
lora_channel.subscriber()?,
|
||||||
self.lora,
|
)?);
|
||||||
lora_channel.subscriber()?
|
self.spawner.spawn(components::lora::start_heartbeat_task(
|
||||||
)?
|
lora_channel.publisher()?,
|
||||||
);
|
)?);
|
||||||
self.spawner.spawn(
|
|
||||||
components::lora::start_heartbeat_task(lora_channel.publisher()?)?
|
|
||||||
);
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
Timer::after_millis(100).await;
|
Timer::after_millis(100).await;
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ use embassy_sync::once_lock::OnceLock;
|
|||||||
use embassy_time::{Delay, Timer};
|
use embassy_time::{Delay, Timer};
|
||||||
use esp_hal::gpio;
|
use esp_hal::gpio;
|
||||||
use esp_hal::rng::Trng;
|
use esp_hal::rng::Trng;
|
||||||
use pn532::types::felica::{CardSession, FelicaLiteSBlock, FelicaPollingRequestCode};
|
use pn532::types::felica::felica_lite_s::auth::{CardKey, CardKeyVersion};
|
||||||
use pn532::{Pn532};
|
use pn532::types::felica::felica_lite_s::config::{MemoryConfig, RFParameter};
|
||||||
use pn532::types::felica::felica_lite_s::auth::CardKey;
|
use pn532::types::felica::{CardSession, FelicaLiteSBlock};
|
||||||
use pn532::types::BaudRate;
|
use pn532::Pn532;
|
||||||
|
|
||||||
static CARD_KEY: OnceLock<CardKey> = OnceLock::new();
|
static CARD_KEY: OnceLock<CardKey> = OnceLock::new();
|
||||||
const CARD_KEY_STR: &str = env!("CARD_KEY");
|
const CARD_KEY_STR: &str = env!("CARD_KEY");
|
||||||
@@ -16,24 +16,30 @@ fn get_card_key() -> &'static CardKey {
|
|||||||
CARD_KEY.get_or_init(|| {
|
CARD_KEY.get_or_init(|| {
|
||||||
let mut buffer = [0u8; 16];
|
let mut buffer = [0u8; 16];
|
||||||
hex::decode_to_slice(CARD_KEY_STR, &mut buffer).unwrap();
|
hex::decode_to_slice(CARD_KEY_STR, &mut buffer).unwrap();
|
||||||
CardKey(buffer)
|
CardKey::from(buffer)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CardReader<'a> {
|
pub struct CardReader<'a> {
|
||||||
driver: Pn532<gpio::Output<'a>, gpio::Input<'a>, Delay, pn532::I2cInterface<SharedI2c<'a>, Delay, gpio::Output<'a>, gpio::Input<'a>>>
|
driver: Pn532<
|
||||||
|
gpio::Output<'a>,
|
||||||
|
gpio::Input<'a>,
|
||||||
|
Delay,
|
||||||
|
pn532::I2cInterface<SharedI2c<'a>, Delay, gpio::Output<'a>, gpio::Input<'a>>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CardReader<'a> {
|
impl<'a> CardReader<'a> {
|
||||||
pub fn new(i2c: SharedI2c<'a>, irq: gpio::Input<'a>, reset: gpio::Output<'a>) -> Self {
|
pub fn new(i2c: SharedI2c<'a>, irq: gpio::Input<'a>, reset: gpio::Output<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
driver: Pn532::new(pn532::I2cInterface::with_reset_irq(i2c, Delay, reset, irq))
|
driver: Pn532::new(pn532::I2cInterface::with_reset_irq(i2c, Delay, reset, irq)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&mut self) -> Result<(), crate::error::Error> {
|
pub fn init(&mut self) -> Result<(), crate::error::Error> {
|
||||||
self.driver.begin()?;
|
self.driver.begin()?;
|
||||||
self.driver.sam_config()?;
|
self.driver.sam_config()?;
|
||||||
|
self.driver.set_passive_activation_retries(5)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,16 +48,29 @@ impl<'a> CardReader<'a> {
|
|||||||
pub async fn start_card_reader(
|
pub async fn start_card_reader(
|
||||||
mut card_reader: CardReader<'static>,
|
mut card_reader: CardReader<'static>,
|
||||||
_publisher: LoraPublisher<'static>,
|
_publisher: LoraPublisher<'static>,
|
||||||
mut trng: Trng
|
mut trng: Trng,
|
||||||
) -> () {
|
) -> () {
|
||||||
|
loop {
|
||||||
|
match _start_card_reader(&mut card_reader, &_publisher, &mut trng).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
defmt::error!("Error while reading card: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn _start_card_reader(
|
||||||
|
card_reader: &mut CardReader<'static>,
|
||||||
|
_publisher: &LoraPublisher<'static>,
|
||||||
|
trng: &mut Trng,
|
||||||
|
) -> Result<(), crate::error::Error> {
|
||||||
let card_key = get_card_key();
|
let card_key = get_card_key();
|
||||||
defmt::trace!("card_key: {}", card_key);
|
defmt::trace!("card_key: {}", card_key);
|
||||||
|
let driver = &mut card_reader.driver;
|
||||||
loop {
|
loop {
|
||||||
Timer::after_millis(50).await;
|
Timer::after_millis(10).await;
|
||||||
let Some(response) = (match card_reader.driver.felica_polling(
|
let Some(response) = (match driver.felica_polling(Default::default()) {
|
||||||
Default::default()
|
|
||||||
) {
|
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
defmt::error!("card_reader driver error: {}", e);
|
defmt::error!("card_reader driver error: {}", e);
|
||||||
@@ -62,6 +81,7 @@ pub async fn start_card_reader(
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
defmt::info!("Polling card: {}", Debug2Format(&response));
|
defmt::info!("Polling card: {}", Debug2Format(&response));
|
||||||
|
defmt::debug!("IDm: {}", response.idm.into_bytes());
|
||||||
let card = CardSession::from(response);
|
let card = CardSession::from(response);
|
||||||
|
|
||||||
// if let Err(e) = card_reader.driver.felica_write_without_encryption(
|
// if let Err(e) = card_reader.driver.felica_write_without_encryption(
|
||||||
@@ -73,23 +93,55 @@ pub async fn start_card_reader(
|
|||||||
// defmt::error!("card_reader write ck error: {}", e);
|
// defmt::error!("card_reader write ck error: {}", e);
|
||||||
// continue;
|
// continue;
|
||||||
// }
|
// }
|
||||||
|
let id = driver.get_id(&card)?;
|
||||||
|
defmt::debug!("ID: {}", id);
|
||||||
|
|
||||||
let auth_result = card_reader.driver.try_authenticate(
|
if let Some(card_id) = option_env!("CARD_ID") {
|
||||||
&mut trng, &card, card_key
|
let mut id = [0u8; 6];
|
||||||
);
|
hex::decode_to_slice(card_id,&mut id)?;
|
||||||
let session = match auth_result {
|
driver.set_id(&card, id, None)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut mc = driver.get_mc(&card)?;
|
||||||
|
defmt::info!("Config: {}", mc);
|
||||||
|
mc.access.set_system_block(0x00);
|
||||||
|
mc.ndef = 0x00;
|
||||||
|
mc.rf = RFParameter::from(0xFF);
|
||||||
|
mc.access.set_is_ck_ckv_rewritable(true);
|
||||||
|
|
||||||
|
let mut read_auth = mc.auth.scratch_pad_read();
|
||||||
|
read_auth.set_pad0(true);
|
||||||
|
mc.auth.set_scratch_pad_read(read_auth);
|
||||||
|
|
||||||
|
let mut write_auth = mc.auth.scratch_pad_write();
|
||||||
|
write_auth.set_pad0(true);
|
||||||
|
mc.auth.set_scratch_pad_write(write_auth);
|
||||||
|
|
||||||
|
let mut mac_auth = mc.auth.scratch_pad_mac_write();
|
||||||
|
mac_auth.set_pad0(true);
|
||||||
|
mc.auth.set_scratch_pad_mac_write(mac_auth);
|
||||||
|
|
||||||
|
let block: [u8; 16] = mc.into();
|
||||||
|
defmt::debug!("1st insurance Config: {}", block);
|
||||||
|
driver.set_mc(&card, mc)?;
|
||||||
|
|
||||||
|
let session = match driver.try_authenticate(trng, &card, card_key) {
|
||||||
Ok(Some(sk)) => sk,
|
Ok(Some(sk)) => sk,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
defmt::warn!("card key is invalid");
|
defmt::warn!("card key is invalid");
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
defmt::error!("card_reader auth error: {}", e);
|
defmt::error!("card_reader auth error: {}", e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let read_result = card_reader.driver.read_with_mac(&card, &session, FelicaLiteSBlock::PAD0);
|
|
||||||
let _read_data = match read_result {
|
driver.set_ck_with_mac(&card, &session, &card_key)?;
|
||||||
|
driver.set_ckv_with_mac(&card, &session, &CardKeyVersion::new(1))?;
|
||||||
|
driver.get_ckv(&card)?;
|
||||||
|
|
||||||
|
let _read_data = match driver.read_with_mac(&card, &session, FelicaLiteSBlock::PAD0) {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
defmt::error!("card_reader read with mac error: {}", e);
|
defmt::error!("card_reader read with mac error: {}", e);
|
||||||
@@ -97,8 +149,8 @@ pub async fn start_card_reader(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match card_reader.driver.release() {
|
match driver.release() {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
defmt::error!("card_reader release error: {}", e);
|
defmt::error!("card_reader release error: {}", e);
|
||||||
continue;
|
continue;
|
||||||
@@ -107,7 +159,7 @@ pub async fn start_card_reader(
|
|||||||
|
|
||||||
// InRelease switches the RF field off when no target remains
|
// InRelease switches the RF field off when no target remains
|
||||||
// (UM0701-02 §7.3.11), so re-enable it before the next poll.
|
// (UM0701-02 §7.3.11), so re-enable it before the next poll.
|
||||||
if let Err(e) = card_reader.driver.set_rf_field(0x00, 0x01) {
|
if let Err(e) = driver.set_rf_field(0x00, 0x01) {
|
||||||
defmt::error!("card_reader RF field error: {}", e);
|
defmt::error!("card_reader RF field error: {}", e);
|
||||||
}
|
}
|
||||||
Timer::after_millis(200).await;
|
Timer::after_millis(200).await;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use embassy_time::{Duration, Timer};
|
|||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
const ADDR_SIZE: usize = 256;
|
const BLOCK_SIZE: usize = 256;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub(crate) enum StorageError {
|
pub(crate) enum StorageError {
|
||||||
@@ -44,7 +44,7 @@ impl<'d> RomStorage<'d> {
|
|||||||
Ok(addr.clone())
|
Ok(addr.clone())
|
||||||
} else {
|
} else {
|
||||||
let size = self.addr_table.len();
|
let size = self.addr_table.len();
|
||||||
let addr = size as u32 * ADDR_SIZE as u32;
|
let addr = size as u32 * BLOCK_SIZE as u32;
|
||||||
if addr >= u16::MAX as u32 {
|
if addr >= u16::MAX as u32 {
|
||||||
return Err(StorageError::OutOfAddress)
|
return Err(StorageError::OutOfAddress)
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ impl<'d> RomStorage<'d> {
|
|||||||
let addr = self.get_or_insert_addr::<T>()?;
|
let addr = self.get_or_insert_addr::<T>()?;
|
||||||
let bytes = postcard::to_allocvec(data)?;
|
let bytes = postcard::to_allocvec(data)?;
|
||||||
let bytes_len = bytes.len();
|
let bytes_len = bytes.len();
|
||||||
if bytes_len > ADDR_SIZE {
|
if bytes_len > BLOCK_SIZE {
|
||||||
return Err(StorageError::StorageOverSize(bytes_len).into());
|
return Err(StorageError::StorageOverSize(bytes_len).into());
|
||||||
}
|
}
|
||||||
for (i, chunk) in bytes.chunks(32).enumerate() {
|
for (i, chunk) in bytes.chunks(32).enumerate() {
|
||||||
@@ -74,7 +74,7 @@ impl<'d> RomStorage<'d> {
|
|||||||
|
|
||||||
pub fn get_data<T: DeserializeOwned + 'static>(&mut self) -> Result<T, crate::error::Error> {
|
pub fn get_data<T: DeserializeOwned + 'static>(&mut self) -> Result<T, crate::error::Error> {
|
||||||
let addr = self.get_or_insert_addr::<T>()?;
|
let addr = self.get_or_insert_addr::<T>()?;
|
||||||
let mut bytes = [0u8; ADDR_SIZE];
|
let mut bytes = [0u8; BLOCK_SIZE];
|
||||||
self.driver.read_data(addr, &mut bytes)
|
self.driver.read_data(addr, &mut bytes)
|
||||||
.map_err(|e| StorageError::EEPROMError(e))?;
|
.map_err(|e| StorageError::EEPROMError(e))?;
|
||||||
let data = postcard::from_bytes(&bytes)?;
|
let data = postcard::from_bytes(&bytes)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user