168 lines
5.3 KiB
Rust
168 lines
5.3 KiB
Rust
use crate::types::{LoraPublisher, SharedI2c};
|
|
use defmt::Debug2Format;
|
|
use embassy_sync::once_lock::OnceLock;
|
|
use embassy_time::{Delay, Timer};
|
|
use esp_hal::gpio;
|
|
use esp_hal::rng::Trng;
|
|
use pn532::types::felica::felica_lite_s::auth::{CardKey, CardKeyVersion};
|
|
use pn532::types::felica::felica_lite_s::config::{MemoryConfig, RFParameter};
|
|
use pn532::types::felica::{CardSession, FelicaLiteSBlock};
|
|
use pn532::Pn532;
|
|
|
|
static CARD_KEY: OnceLock<CardKey> = OnceLock::new();
|
|
const CARD_KEY_STR: &str = env!("CARD_KEY");
|
|
|
|
fn get_card_key() -> &'static CardKey {
|
|
CARD_KEY.get_or_init(|| {
|
|
let mut buffer = [0u8; 16];
|
|
hex::decode_to_slice(CARD_KEY_STR, &mut buffer).unwrap();
|
|
CardKey::from(buffer)
|
|
})
|
|
}
|
|
|
|
pub struct CardReader<'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> {
|
|
pub fn new(i2c: SharedI2c<'a>, irq: gpio::Input<'a>, reset: gpio::Output<'a>) -> Self {
|
|
Self {
|
|
driver: Pn532::new(pn532::I2cInterface::with_reset_irq(i2c, Delay, reset, irq)),
|
|
}
|
|
}
|
|
|
|
pub fn init(&mut self) -> Result<(), crate::error::Error> {
|
|
self.driver.begin()?;
|
|
self.driver.sam_config()?;
|
|
self.driver.set_passive_activation_retries(5)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[embassy_executor::task]
|
|
pub async fn start_card_reader(
|
|
mut card_reader: CardReader<'static>,
|
|
_publisher: LoraPublisher<'static>,
|
|
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();
|
|
defmt::trace!("card_key: {}", card_key);
|
|
let driver = &mut card_reader.driver;
|
|
loop {
|
|
Timer::after_millis(10).await;
|
|
let Some(response) = (match driver.felica_polling(Default::default()) {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
defmt::error!("card_reader driver error: {}", e);
|
|
continue;
|
|
}
|
|
}) else {
|
|
defmt::trace!("No card detected");
|
|
continue;
|
|
};
|
|
defmt::info!("Polling card: {}", Debug2Format(&response));
|
|
defmt::debug!("IDm: {}", response.idm.into_bytes());
|
|
let card = CardSession::from(response);
|
|
|
|
// if let Err(e) = card_reader.driver.felica_write_without_encryption(
|
|
// &response.idm, &response.pmm,
|
|
// &[FelicaLiteSServiceCode::Write],
|
|
// &[FelicaLiteSBlock::CK],
|
|
// &[*card_key]
|
|
// ) {
|
|
// defmt::error!("card_reader write ck error: {}", e);
|
|
// continue;
|
|
// }
|
|
let id = driver.get_id(&card)?;
|
|
defmt::debug!("ID: {}", id);
|
|
|
|
if let Some(card_id) = option_env!("CARD_ID") {
|
|
let mut id = [0u8; 6];
|
|
hex::decode_to_slice(card_id,&mut id)?;
|
|
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(None) => {
|
|
defmt::warn!("card key is invalid");
|
|
continue;
|
|
}
|
|
Err(e) => {
|
|
defmt::error!("card_reader auth error: {}", e);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
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,
|
|
Err(e) => {
|
|
defmt::error!("card_reader read with mac error: {}", e);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
match driver.release() {
|
|
Ok(_) => {}
|
|
Err(e) => {
|
|
defmt::error!("card_reader release error: {}", e);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
// InRelease switches the RF field off when no target remains
|
|
// (UM0701-02 §7.3.11), so re-enable it before the next poll.
|
|
if let Err(e) = driver.set_rf_field(0x00, 0x01) {
|
|
defmt::error!("card_reader RF field error: {}", e);
|
|
}
|
|
Timer::after_millis(200).await;
|
|
}
|
|
}
|