mod types; 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 types::*; impl Pn532 { /// Poll for a FeliCa card. pub fn felica_polling( &mut self, system_code: u16, request_code: u8, timeout_ms: u16, ) -> Result, Error> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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(()) } }