Add card detection
This commit is contained in:
@@ -0,0 +1,306 @@
|
||||
mod types;
|
||||
|
||||
use crate::driver::Pn532;
|
||||
use crate::error::{Error, StatusCode};
|
||||
use crate::interface::Interface;
|
||||
|
||||
use types::*;
|
||||
pub use types::Block as FelicaBlock;
|
||||
pub(crate) use types::AsBlock;
|
||||
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};
|
||||
|
||||
impl<B: Interface> Pn532<B> {
|
||||
/// 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>> {
|
||||
self.buffer[0] = PN532_COMMAND_INLISTPASSIVETARGET;
|
||||
self.buffer[1] = 1;
|
||||
self.buffer[2] = BaudRate::Felica212kbps.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[7] = 0;
|
||||
self.send(8)?;
|
||||
|
||||
match self.read_timeout(timeout_ms) {
|
||||
Err(Error::Timeout) => return Ok(None),
|
||||
Err(e) => return Err(e),
|
||||
Ok(_) => {}
|
||||
}
|
||||
|
||||
if self.buffer[0] == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
if self.buffer[0] != 1 {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
|
||||
self.in_listed_tag = self.buffer[1];
|
||||
let response_length = self.buffer[2];
|
||||
if response_length != 18 && response_length != 20 {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
|
||||
let mut idm = [0u8; 8];
|
||||
idm.copy_from_slice(&self.buffer[4..12]);
|
||||
self.felica_idm = idm;
|
||||
|
||||
let mut pmm = [0u8; 8];
|
||||
pmm.copy_from_slice(&self.buffer[12..20]);
|
||||
self.felica_pmm = pmm;
|
||||
|
||||
let system_code_response = if response_length == 20 {
|
||||
Some(u16::from_be_bytes([self.buffer[20], self.buffer[21]]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Some(FelicaPollingResponse {
|
||||
idm,
|
||||
pmm,
|
||||
system_code_response,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Send a raw FeliCa command to the currently in-listed card.
|
||||
///
|
||||
/// Returns the number of bytes written to `response`.
|
||||
pub fn felica_send_command(
|
||||
&mut self,
|
||||
command: &[u8],
|
||||
response: &mut [u8],
|
||||
) -> Result<usize, Error<B::TransportError>> {
|
||||
if command.len() > 0xFE {
|
||||
return Err(Error::InvalidParam);
|
||||
}
|
||||
self.buffer[0] = PN532_COMMAND_INDATAEXCHANGE;
|
||||
self.buffer[1] = self.in_listed_tag;
|
||||
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 response_len = self.buffer[1] as usize - 1;
|
||||
if len - 2 != response_len {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
if response_len > response.len() {
|
||||
return Err(Error::NoSpace);
|
||||
}
|
||||
response[..response_len].copy_from_slice(&self.buffer[2..2 + response_len]);
|
||||
Ok(response_len)
|
||||
}
|
||||
|
||||
/// Send a FeliCa "Request Service" command.
|
||||
pub fn felica_request_service(
|
||||
&mut self,
|
||||
node_code_list: &[u16],
|
||||
key_versions: &mut [u16],
|
||||
) -> Result<(), Error<B::TransportError>> {
|
||||
let num_node = node_code_list.len();
|
||||
if num_node > FELICA_REQ_SERVICE_MAX_NODE_NUM || num_node > key_versions.len() {
|
||||
return Err(Error::InvalidParam);
|
||||
}
|
||||
|
||||
let mut cmd = [0u8; 1 + 8 + 1 + 2 * FELICA_REQ_SERVICE_MAX_NODE_NUM];
|
||||
let mut j = 0;
|
||||
cmd[j] = FELICA_CMD_REQUEST_SERVICE;
|
||||
j += 1;
|
||||
cmd[j..j + 8].copy_from_slice(&self.felica_idm);
|
||||
j += 8;
|
||||
cmd[j] = num_node as u8;
|
||||
j += 1;
|
||||
for &code in node_code_list {
|
||||
cmd[j] = code as u8;
|
||||
cmd[j + 1] = (code >> 8) as u8;
|
||||
j += 2;
|
||||
}
|
||||
|
||||
let mut response = [0u8; 10 + 2 * FELICA_REQ_SERVICE_MAX_NODE_NUM];
|
||||
let response_len = self.felica_send_command(&cmd[..j], &mut response)?;
|
||||
if response_len != 10 + 2 * num_node {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
for (i, kv) in key_versions.iter_mut().enumerate().take(num_node) {
|
||||
*kv = u16::from_le_bytes([response[10 + i * 2], response[10 + i * 2 + 1]]);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a FeliCa "Request Response" command, returning the card's mode.
|
||||
pub fn felica_request_response(&mut self) -> 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);
|
||||
|
||||
let mut response = [0u8; 10];
|
||||
let response_len = self.felica_send_command(&cmd, &mut response)?;
|
||||
if response_len != 10 {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
Ok(response[9])
|
||||
}
|
||||
|
||||
/// Send a FeliCa "Read Without Encryption" command.
|
||||
///
|
||||
/// 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],
|
||||
block_data: &mut [[u8; 16]],
|
||||
) -> Result<(), Error<B::TransportError>> {
|
||||
let num_service = service_code_list.len();
|
||||
let num_block = block_list.len();
|
||||
if num_service > FELICA_READ_MAX_SERVICE_NUM
|
||||
|| num_block > FELICA_READ_MAX_BLOCK_NUM
|
||||
|| num_block > block_data.len()
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
let mut response = [0u8; 12 + 16 * FELICA_READ_MAX_BLOCK_NUM];
|
||||
let response_len = self.felica_send_command(&cmd[..j], &mut response)?;
|
||||
if response_len != 12 + 16 * num_block {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
if response[9] != 0 || response[10] != 0 {
|
||||
return Err(Error::Status(StatusCode::from_repr(response[9]).unwrap()));
|
||||
}
|
||||
|
||||
let mut k = 12;
|
||||
for block in block_data.iter_mut().take(num_block) {
|
||||
block.copy_from_slice(&response[k..k + 16]);
|
||||
k += 16;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a FeliCa "Write Without Encryption" command.
|
||||
pub fn felica_write_without_encryption(
|
||||
&mut self,
|
||||
service_code_list: &[u16],
|
||||
block_list: &[impl AsBlock],
|
||||
block_data: &[[u8; 16]],
|
||||
) -> Result<(), Error<B::TransportError>> {
|
||||
let num_service = service_code_list.len();
|
||||
let num_block = block_list.len();
|
||||
if num_service > FELICA_WRITE_MAX_SERVICE_NUM
|
||||
|| num_block > FELICA_WRITE_MAX_BLOCK_NUM
|
||||
|| num_block > block_data.len()
|
||||
{
|
||||
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;
|
||||
}
|
||||
cmd[j] = num_block as u8;
|
||||
j += 1;
|
||||
for block in block_list {
|
||||
let block = block.as_block();
|
||||
cmd[j] = (block >> 8) as u8;
|
||||
cmd[j + 1] = block as u8;
|
||||
j += 2;
|
||||
}
|
||||
for block in block_data.iter().take(num_block) {
|
||||
cmd[j..j + 16].copy_from_slice(block);
|
||||
j += 16;
|
||||
}
|
||||
|
||||
let mut response = [0u8; 11];
|
||||
let response_len = self.felica_send_command(&cmd[..j], &mut response)?;
|
||||
if response_len != 11 {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
if response[9] != 0 || response[10] != 0 {
|
||||
return Err(Error::Status(StatusCode::from_repr(response[9]).unwrap()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a FeliCa "Request System Code" command.
|
||||
///
|
||||
/// Returns the number of system codes written to `system_code_list`.
|
||||
pub fn felica_request_system_code(
|
||||
&mut self,
|
||||
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);
|
||||
|
||||
let mut response = [0u8; 10 + 2 * 16];
|
||||
let response_len = self.felica_send_command(&cmd, &mut response)?;
|
||||
if response_len < 10 {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
let num_system_code = response[9] as usize;
|
||||
if response_len < 10 + 2 * num_system_code {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
if num_system_code > system_code_list.len() {
|
||||
return Err(Error::NoSpace);
|
||||
}
|
||||
for (i, out) in system_code_list.iter_mut().enumerate().take(num_system_code) {
|
||||
*out = AsServiceCode::from_service_code(
|
||||
u16::from_be_bytes([response[10 + i * 2], response[10 + i * 2 + 1]])
|
||||
);
|
||||
}
|
||||
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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user