blob: 6a27539f439322e80d0d683a6a7fb3bc88d2bf75 [file] [log] [blame]
// Copyright 2023, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of the NFCC.
use crate::packets::{nci, rf};
use anyhow::Result;
use core::time::Duration;
use futures::StreamExt;
use log::{debug, error, info, trace, warn};
use pdl_runtime::Packet;
use std::convert::TryFrom;
use std::future::Future;
use std::pin::pin;
use std::time::Instant;
use tokio::sync::mpsc;
use tokio::time;
const NCI_VERSION: nci::NciVersion = nci::NciVersion::Version20;
const MANUFACTURER_ID: u8 = 0x02;
const MANUFACTURER_SPECIFIC_INFORMATION: [u8; 26] =
[5, 3, 3, 19, 4, 25, 1, 7, 0, 0, 68, 100, 214, 0, 0, 90, 172, 0, 0, 0, 1, 44, 176, 153, 243, 0];
/// Read-only configuration parameters
const PB_ATTRIB_PARAM1: u8 = 0x00;
const LF_T3T_MAX: u8 = 16;
const LLCP_VERSION: u8 = 0x00;
/// Writable configuration parameters with default
/// value defined by the NFCC.
const TOTAL_DURATION: u16 = 1000;
const PA_DEVICES_LIMIT: u8 = 255;
const PB_DEVICES_LIMIT: u8 = 255;
const PF_DEVICES_LIMIT: u8 = 255;
const PV_DEVICES_LIMIT: u8 = 255;
const LA_BIT_FRAME_SDD: u8 = 0x10;
const LA_PLATFORM_CONFIG: u8 = 0x0c;
const LA_SEL_INFO: u8 = 0x60; // Supports ISO-DEP and NFC-DEP.
const LB_SENSB_INFO: u8 = 0x1; // Supports ISO-DEP.
const LB_SFGI: u8 = 0;
const LB_FWI_ADC_FO: u8 = 0x00;
const LF_PROTOCOL_TYPE: u8 = 0x02; // Supports NFC-DEP.
const LI_A_RATS_TB1: u8 = 0x70;
const LI_A_RATS_TC1: u8 = 0x02;
const MAX_LOGICAL_CONNECTIONS: u8 = 2;
const MAX_ROUTING_TABLE_SIZE: u16 = 512;
const MAX_CONTROL_PACKET_PAYLOAD_SIZE: u8 = 255;
const MAX_DATA_PACKET_PAYLOAD_SIZE: u8 = 255;
const NUMBER_OF_CREDITS: u8 = 1;
const MAX_NFCV_RF_FRAME_SIZE: u16 = 512;
/// Time in milliseconds that Casimir waits for poll responses after
/// sending a poll command.
const POLL_RESPONSE_TIMEOUT: u64 = 200;
/// All configuration parameters of the NFCC.
/// The configuration is filled with default values from the specification
/// See [NCI] Table 46: Common Parameters for Discovery Configuration
/// for the format of each parameter and the default value.
#[derive(Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub struct ConfigParameters {
total_duration: u16,
/// [NCI] Table 47: Values for CON_DISCOVERY_PARAM.
con_discovery_param: u8,
power_state: u8,
pa_bail_out: u8,
pa_devices_limit: u8,
pb_afi: u8,
pb_bail_out: u8,
pb_attrib_param1: u8,
/// [NCI] Table 26: Values for PB_SENSB_REQ_PARAM.
pb_sensb_req_param: u8,
pb_devices_limit: u8,
pf_bit_rate: u8,
pf_bail_out: u8,
pf_devices_limit: u8,
pi_b_h_info: Vec<u8>,
pi_bit_rate: u8,
pn_nfc_dep_psl: u8,
pn_atr_req_gen_bytes: Vec<u8>,
/// [NCI] Table 30: Values for PN_ATR_REQ_CONFIG.
pn_atr_req_config: u8,
pv_devices_limit: u8,
la_bit_frame_sdd: u8,
la_platform_config: u8,
/// [NCI] Table 34: LA_SEL_INFO Coding.
la_sel_info: u8,
la_nfcid1: Vec<u8>,
/// [NCI] Table 36: LB_SENSB_INFO Values.
lb_sensb_info: u8,
lb_nfcid0: [u8; 4],
lb_application_data: u32,
lb_sfgi: u8,
/// [NCI] Table 37: LB_FWI_ADC_FO Values.
lb_fwi_adc_fo: u8,
lb_bit_rate: u8,
lf_t3t_identifiers_1: [u8; 18],
lf_t3t_identifiers_2: [u8; 18],
lf_t3t_identifiers_3: [u8; 18],
lf_t3t_identifiers_4: [u8; 18],
lf_t3t_identifiers_5: [u8; 18],
lf_t3t_identifiers_6: [u8; 18],
lf_t3t_identifiers_7: [u8; 18],
lf_t3t_identifiers_8: [u8; 18],
lf_t3t_identifiers_9: [u8; 18],
lf_t3t_identifiers_10: [u8; 18],
lf_t3t_identifiers_11: [u8; 18],
lf_t3t_identifiers_12: [u8; 18],
lf_t3t_identifiers_13: [u8; 18],
lf_t3t_identifiers_14: [u8; 18],
lf_t3t_identifiers_15: [u8; 18],
lf_t3t_identifiers_16: [u8; 18],
lf_t3t_pmm_default: [u8; 8],
lf_t3t_max: u8,
lf_t3t_flags: u16,
lf_t3t_rd_allowed: u8,
/// [NCI] Table 39: Supported Protocols for Listen F.
lf_protocol_type: u8,
li_a_rats_tb1: u8,
li_a_hist_by: Vec<u8>,
li_b_h_info_resp: Vec<u8>,
li_a_bit_rate: u8,
li_a_rats_tc1: u8,
ln_wt: u8,
ln_atr_res_gen_bytes: Vec<u8>,
ln_atr_res_config: u8,
pacm_bit_rate: u8,
/// [NCI] Table 23: RF Field Information Configuration Parameter.
rf_field_info: u8,
rf_nfcee_action: u8,
nfcdep_op: u8,
/// [NCI] Table 115: LLCP Version Parameter.
llcp_version: u8,
/// [NCI] Table 65: Value Field for NFCC Configuration Control.
nfcc_config_control: u8,
}
/// State of an NFCC logical connection with the DH.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum LogicalConnection {
RemoteNfcEndpoint { rf_discovery_id: u8, rf_protocol_type: nci::RfProtocolType },
}
/// State of the RF Discovery of an NFCC instance.
/// The state WaitForAllDiscoveries is not represented as it is implied
/// by the discovery routine.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum RfState {
Idle,
Discovery,
PollActive {
id: u16,
rf_interface: nci::RfInterfaceType,
rf_technology: rf::Technology,
rf_protocol: rf::Protocol,
},
ListenSleep {
id: u16,
},
ListenActive {
id: u16,
rf_interface: nci::RfInterfaceType,
rf_technology: rf::Technology,
rf_protocol: rf::Protocol,
},
WaitForHostSelect,
WaitForSelectResponse {
id: u16,
rf_discovery_id: usize,
rf_interface: nci::RfInterfaceType,
rf_technology: rf::Technology,
rf_protocol: rf::Protocol,
},
}
/// State of the emulated eSE (ST) NFCEE.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum NfceeState {
Enabled,
Disabled,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum RfMode {
Poll,
Listen,
}
/// Poll responses received in the context of RF discovery in active
/// Listen mode.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RfPollResponse {
id: u16,
rf_protocol: rf::Protocol,
rf_technology: rf::Technology,
rf_technology_specific_parameters: Vec<u8>,
}
/// State of an NFCC instance.
#[allow(missing_docs)]
pub struct State {
pub config_parameters: ConfigParameters,
pub logical_connections: [Option<LogicalConnection>; MAX_LOGICAL_CONNECTIONS as usize],
pub discover_configuration: Vec<nci::DiscoverConfiguration>,
pub discover_map: Vec<nci::MappingConfiguration>,
pub nfcee_state: NfceeState,
pub rf_state: RfState,
pub rf_poll_responses: Vec<RfPollResponse>,
pub rf_activation_parameters: Vec<u8>,
pub passive_observe_mode: nci::PassiveObserveMode,
pub start_time: std::time::Instant,
}
/// State of an NFCC instance.
pub struct Controller<'a> {
id: u16,
nci_stream: nci::StreamRefMut<'a>,
nci_writer: nci::Writer,
rf_rx: mpsc::UnboundedReceiver<rf::RfPacket>,
rf_tx: mpsc::UnboundedSender<rf::RfPacket>,
state: State,
}
impl ConfigParameters {
fn get(&self, id: nci::ConfigParameterId) -> Result<Vec<u8>> {
match id {
nci::ConfigParameterId::TotalDuration => Ok(self.total_duration.to_le_bytes().to_vec()),
nci::ConfigParameterId::ConDiscoveryParam => {
Ok(self.con_discovery_param.to_le_bytes().to_vec())
}
nci::ConfigParameterId::PowerState => Ok(vec![self.power_state]),
nci::ConfigParameterId::PaBailOut => Ok(vec![self.pa_bail_out]),
nci::ConfigParameterId::PaDevicesLimit => Ok(vec![self.pa_devices_limit]),
nci::ConfigParameterId::PbAfi => Ok(vec![self.pb_afi]),
nci::ConfigParameterId::PbBailOut => Ok(vec![self.pb_bail_out]),
nci::ConfigParameterId::PbAttribParam1 => Ok(vec![self.pb_attrib_param1]),
nci::ConfigParameterId::PbSensbReqParam => Ok(vec![self.pb_sensb_req_param]),
nci::ConfigParameterId::PbDevicesLimit => Ok(vec![self.pb_devices_limit]),
nci::ConfigParameterId::PfBitRate => Ok(vec![self.pf_bit_rate]),
nci::ConfigParameterId::PfBailOut => Ok(vec![self.pf_bail_out]),
nci::ConfigParameterId::PfDevicesLimit => Ok(vec![self.pf_devices_limit]),
nci::ConfigParameterId::PiBHInfo => Ok(self.pi_b_h_info.clone()),
nci::ConfigParameterId::PiBitRate => Ok(vec![self.pi_bit_rate]),
nci::ConfigParameterId::PnNfcDepPsl => Ok(vec![self.pn_nfc_dep_psl]),
nci::ConfigParameterId::PnAtrReqGenBytes => Ok(self.pn_atr_req_gen_bytes.clone()),
nci::ConfigParameterId::PnAtrReqConfig => Ok(vec![self.pn_atr_req_config]),
nci::ConfigParameterId::PvDevicesLimit => Ok(vec![self.pv_devices_limit]),
nci::ConfigParameterId::LaBitFrameSdd => Ok(vec![self.la_bit_frame_sdd]),
nci::ConfigParameterId::LaPlatformConfig => Ok(vec![self.la_platform_config]),
nci::ConfigParameterId::LaSelInfo => Ok(vec![self.la_sel_info]),
nci::ConfigParameterId::LaNfcid1 => Ok(self.la_nfcid1.clone()),
nci::ConfigParameterId::LbSensbInfo => Ok(vec![self.lb_sensb_info]),
nci::ConfigParameterId::LbNfcid0 => Ok(self.lb_nfcid0.to_vec()),
nci::ConfigParameterId::LbApplicationData => {
Ok(self.lb_application_data.to_le_bytes().to_vec())
}
nci::ConfigParameterId::LbSfgi => Ok(vec![self.lb_sfgi]),
nci::ConfigParameterId::LbFwiAdcFo => Ok(vec![self.lb_fwi_adc_fo]),
nci::ConfigParameterId::LbBitRate => Ok(vec![self.lb_bit_rate]),
nci::ConfigParameterId::LfT3tIdentifiers1 => Ok(self.lf_t3t_identifiers_1.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers2 => Ok(self.lf_t3t_identifiers_2.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers3 => Ok(self.lf_t3t_identifiers_3.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers4 => Ok(self.lf_t3t_identifiers_4.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers5 => Ok(self.lf_t3t_identifiers_5.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers6 => Ok(self.lf_t3t_identifiers_6.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers7 => Ok(self.lf_t3t_identifiers_7.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers8 => Ok(self.lf_t3t_identifiers_8.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers9 => Ok(self.lf_t3t_identifiers_9.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers10 => Ok(self.lf_t3t_identifiers_10.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers11 => Ok(self.lf_t3t_identifiers_11.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers12 => Ok(self.lf_t3t_identifiers_12.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers13 => Ok(self.lf_t3t_identifiers_13.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers14 => Ok(self.lf_t3t_identifiers_14.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers15 => Ok(self.lf_t3t_identifiers_15.to_vec()),
nci::ConfigParameterId::LfT3tIdentifiers16 => Ok(self.lf_t3t_identifiers_16.to_vec()),
nci::ConfigParameterId::LfT3tPmmDefault => Ok(self.lf_t3t_pmm_default.to_vec()),
nci::ConfigParameterId::LfT3tMax => Ok(vec![self.lf_t3t_max]),
nci::ConfigParameterId::LfT3tFlags => Ok(self.lf_t3t_flags.to_le_bytes().to_vec()),
nci::ConfigParameterId::LfT3tRdAllowed => Ok(vec![self.lf_t3t_rd_allowed]),
nci::ConfigParameterId::LfProtocolType => Ok(vec![self.lf_protocol_type]),
nci::ConfigParameterId::LiARatsTb1 => Ok(vec![self.li_a_rats_tb1]),
nci::ConfigParameterId::LiAHistBy => Ok(self.li_a_hist_by.clone()),
nci::ConfigParameterId::LiBHInfoResp => Ok(self.li_b_h_info_resp.clone()),
nci::ConfigParameterId::LiABitRate => Ok(vec![self.li_a_bit_rate]),
nci::ConfigParameterId::LiARatsTc1 => Ok(vec![self.li_a_rats_tc1]),
nci::ConfigParameterId::LnWt => Ok(vec![self.ln_wt]),
nci::ConfigParameterId::LnAtrResGenBytes => Ok(self.ln_atr_res_gen_bytes.clone()),
nci::ConfigParameterId::LnAtrResConfig => Ok(vec![self.ln_atr_res_config]),
nci::ConfigParameterId::PacmBitRate => Ok(vec![self.pacm_bit_rate]),
nci::ConfigParameterId::RfFieldInfo => Ok(vec![self.rf_field_info]),
nci::ConfigParameterId::RfNfceeAction => Ok(vec![self.rf_nfcee_action]),
nci::ConfigParameterId::NfcdepOp => Ok(vec![self.nfcdep_op]),
nci::ConfigParameterId::LlcpVersion => Ok(vec![self.llcp_version]),
nci::ConfigParameterId::NfccConfigControl => Ok(vec![self.nfcc_config_control]),
_ => Err(anyhow::anyhow!("unknown config parameter ID")),
}
}
fn set(&mut self, id: nci::ConfigParameterId, value: &[u8]) -> Result<()> {
match id {
nci::ConfigParameterId::TotalDuration => {
self.total_duration = u16::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::ConDiscoveryParam => {
self.con_discovery_param = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PowerState => {
self.power_state = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PaBailOut => {
self.pa_bail_out = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PaDevicesLimit => {
self.pa_devices_limit = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PbAfi => {
self.pb_afi = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PbBailOut => {
self.pb_bail_out = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PbAttribParam1 => {
self.pb_attrib_param1 = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PbSensbReqParam => {
self.pb_sensb_req_param = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PbDevicesLimit => {
self.pb_devices_limit = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PfBitRate => {
self.pf_bit_rate = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PfBailOut => {
self.pf_bail_out = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PfDevicesLimit => {
self.pf_devices_limit = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PiBHInfo => {
self.pi_b_h_info = value.to_vec();
Ok(())
}
nci::ConfigParameterId::PiBitRate => {
self.pi_bit_rate = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PnNfcDepPsl => {
self.pn_nfc_dep_psl = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PnAtrReqGenBytes => {
self.pn_atr_req_gen_bytes = value.to_vec();
Ok(())
}
nci::ConfigParameterId::PnAtrReqConfig => {
self.pn_atr_req_config = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PvDevicesLimit => {
self.pv_devices_limit = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LaBitFrameSdd => {
self.la_bit_frame_sdd = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LaPlatformConfig => {
self.la_platform_config = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LaSelInfo => {
self.la_sel_info = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LaNfcid1 => {
self.la_nfcid1 = value.to_vec();
Ok(())
}
nci::ConfigParameterId::LbSensbInfo => {
self.lb_sensb_info = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LbNfcid0 => {
self.lb_nfcid0 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LbApplicationData => {
self.lb_application_data = u32::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LbSfgi => {
self.lb_sfgi = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LbFwiAdcFo => {
self.lb_fwi_adc_fo = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LbBitRate => {
self.lb_bit_rate = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers1 => {
self.lf_t3t_identifiers_1 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers2 => {
self.lf_t3t_identifiers_2 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers3 => {
self.lf_t3t_identifiers_3 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers4 => {
self.lf_t3t_identifiers_4 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers5 => {
self.lf_t3t_identifiers_5 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers6 => {
self.lf_t3t_identifiers_6 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers7 => {
self.lf_t3t_identifiers_7 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers8 => {
self.lf_t3t_identifiers_8 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers9 => {
self.lf_t3t_identifiers_9 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers10 => {
self.lf_t3t_identifiers_10 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers11 => {
self.lf_t3t_identifiers_11 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers12 => {
self.lf_t3t_identifiers_12 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers13 => {
self.lf_t3t_identifiers_13 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers14 => {
self.lf_t3t_identifiers_14 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers15 => {
self.lf_t3t_identifiers_15 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tIdentifiers16 => {
self.lf_t3t_identifiers_16 = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tPmmDefault => {
self.lf_t3t_pmm_default = value.try_into()?;
Ok(())
}
nci::ConfigParameterId::LfT3tMax => Err(anyhow::anyhow!("read-only config parameter")),
nci::ConfigParameterId::LfT3tFlags => {
self.lf_t3t_flags = u16::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LfT3tRdAllowed => {
self.lf_t3t_rd_allowed = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LfProtocolType => {
self.lf_protocol_type = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LiARatsTb1 => {
self.li_a_rats_tb1 = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LiAHistBy => {
self.li_a_hist_by = value.to_vec();
Ok(())
}
nci::ConfigParameterId::LiBHInfoResp => {
self.li_b_h_info_resp = value.to_vec();
Ok(())
}
nci::ConfigParameterId::LiABitRate => {
self.li_a_bit_rate = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LiARatsTc1 => {
self.li_a_rats_tc1 = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LnWt => {
self.ln_wt = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LnAtrResGenBytes => {
self.ln_atr_res_gen_bytes = value.to_vec();
Ok(())
}
nci::ConfigParameterId::LnAtrResConfig => {
self.ln_atr_res_config = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::PacmBitRate => {
self.pacm_bit_rate = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::RfFieldInfo => {
self.rf_field_info = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::RfNfceeAction => {
self.rf_nfcee_action = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::NfcdepOp => {
self.nfcdep_op = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::LlcpVersion => {
self.llcp_version = u8::from_le_bytes(value.try_into()?);
Ok(())
}
nci::ConfigParameterId::NfccConfigControl => {
self.nfcc_config_control = u8::from_le_bytes(value.try_into()?);
Ok(())
}
_ => Err(anyhow::anyhow!("unknown config parameter ID")),
}
}
}
impl Default for ConfigParameters {
fn default() -> Self {
ConfigParameters {
total_duration: TOTAL_DURATION,
con_discovery_param: 0x01,
power_state: 0x02,
pa_bail_out: 0x00,
pa_devices_limit: PA_DEVICES_LIMIT,
pb_afi: 0x00,
pb_bail_out: 0x00,
pb_attrib_param1: PB_ATTRIB_PARAM1,
pb_sensb_req_param: 0x00,
pb_devices_limit: PB_DEVICES_LIMIT,
pf_bit_rate: 0x01,
pf_bail_out: 0x00,
pf_devices_limit: PF_DEVICES_LIMIT,
pi_b_h_info: vec![],
pi_bit_rate: 0x00,
pn_nfc_dep_psl: 0x00,
pn_atr_req_gen_bytes: vec![],
pn_atr_req_config: 0x30,
pv_devices_limit: PV_DEVICES_LIMIT,
la_bit_frame_sdd: LA_BIT_FRAME_SDD,
la_platform_config: LA_PLATFORM_CONFIG,
la_sel_info: LA_SEL_INFO,
la_nfcid1: vec![0x08, 0x00, 0x00, 0x00],
lb_sensb_info: LB_SENSB_INFO,
lb_nfcid0: [0x08, 0x00, 0x00, 0x00],
lb_application_data: 0x00000000,
lb_sfgi: LB_SFGI,
lb_fwi_adc_fo: LB_FWI_ADC_FO,
lb_bit_rate: 0x00,
lf_t3t_identifiers_1: [0; 18],
lf_t3t_identifiers_2: [0; 18],
lf_t3t_identifiers_3: [0; 18],
lf_t3t_identifiers_4: [0; 18],
lf_t3t_identifiers_5: [0; 18],
lf_t3t_identifiers_6: [0; 18],
lf_t3t_identifiers_7: [0; 18],
lf_t3t_identifiers_8: [0; 18],
lf_t3t_identifiers_9: [0; 18],
lf_t3t_identifiers_10: [0; 18],
lf_t3t_identifiers_11: [0; 18],
lf_t3t_identifiers_12: [0; 18],
lf_t3t_identifiers_13: [0; 18],
lf_t3t_identifiers_14: [0; 18],
lf_t3t_identifiers_15: [0; 18],
lf_t3t_identifiers_16: [0; 18],
lf_t3t_pmm_default: [0xff; 8],
lf_t3t_max: LF_T3T_MAX,
lf_t3t_flags: 0x0000,
lf_t3t_rd_allowed: 0x00,
lf_protocol_type: LF_PROTOCOL_TYPE,
li_a_rats_tb1: LI_A_RATS_TB1,
li_a_hist_by: vec![],
li_b_h_info_resp: vec![],
li_a_bit_rate: 0x00,
li_a_rats_tc1: LI_A_RATS_TC1,
ln_wt: 10,
ln_atr_res_gen_bytes: vec![],
ln_atr_res_config: 0x30,
pacm_bit_rate: 0x01,
rf_field_info: 0x00,
rf_nfcee_action: 0x01,
// [NCI] Table 101: NFC-DEP Operation Parameter.
nfcdep_op: 0x1f,
llcp_version: LLCP_VERSION,
nfcc_config_control: 0x00,
}
}
}
impl State {
/// Craft the NFCID1 used by this instance in NFC-A poll responses.
/// Returns a dynamically generated NFCID1 (4 byte long and starts with 08h).
fn nfcid1(&self) -> Vec<u8> {
if self.config_parameters.la_nfcid1.len() == 4
&& self.config_parameters.la_nfcid1[0] == 0x08
{
vec![0x08, 186, 7, 99] // TODO(hchataing) pseudo random
} else {
self.config_parameters.la_nfcid1.clone()
}
}
/// Select the interface to be preferably used for the selected protocol.
fn select_interface(
&self,
mode: RfMode,
rf_protocol: nci::RfProtocolType,
) -> nci::RfInterfaceType {
for config in self.discover_map.iter() {
match (mode, config.mode.poll_mode, config.mode.listen_mode) {
_ if config.rf_protocol != rf_protocol => (),
(RfMode::Poll, nci::FeatureFlag::Enabled, _)
| (RfMode::Listen, _, nci::FeatureFlag::Enabled) => return config.rf_interface,
_ => (),
}
}
// [NCI] 6.2 RF Interface Mapping Configuration
//
// The NFCC SHALL set the default mapping of RF Interface to RF Protocols /
// Modes to the following values:
//
// • If the NFCC supports the ISO-DEP RF interface, the NFCC SHALL map the
// ISO-DEP RF Protocol to the ISO-DEP RF Interface for Poll Mode and
// Listen Mode.
// • If the NFCC supports the NFC-DEP RF interface, the NFCC SHALL map the
// NFC-DEP RF Protocol to the NFC-DEP RF Interface for Poll Mode and
// Listen Mode.
// • If the NFCC supports the NDEF RF interface, the NFCC SHALL map the
// NDEF RF Protocol to the NDEF RF Interface for Poll Mode.
// • Otherwise, the NFCC SHALL map to the Frame RF Interface by default
match rf_protocol {
nci::RfProtocolType::IsoDep => nci::RfInterfaceType::IsoDep,
nci::RfProtocolType::NfcDep => nci::RfInterfaceType::NfcDep,
nci::RfProtocolType::Ndef if mode == RfMode::Poll => nci::RfInterfaceType::Ndef,
_ => nci::RfInterfaceType::Frame,
}
}
/// Insert a poll response into the discovery list.
/// The response is not inserted if the device was already discovered
/// with the same parameters.
fn add_poll_response(&mut self, poll_response: RfPollResponse) {
if !self.rf_poll_responses.contains(&poll_response) {
self.rf_poll_responses.push(poll_response);
}
}
}
impl<'a> Controller<'a> {
/// Create a new NFCC instance with default configuration.
pub fn new(
id: u16,
nci_stream: nci::StreamRefMut<'a>,
nci_writer: nci::Writer,
rf_rx: mpsc::UnboundedReceiver<rf::RfPacket>,
rf_tx: mpsc::UnboundedSender<rf::RfPacket>,
) -> Controller<'a> {
Controller {
id,
nci_stream,
nci_writer,
rf_rx,
rf_tx,
state: State {
config_parameters: Default::default(),
logical_connections: [None; MAX_LOGICAL_CONNECTIONS as usize],
discover_map: vec![],
discover_configuration: vec![],
nfcee_state: NfceeState::Disabled,
rf_state: RfState::Idle,
rf_poll_responses: vec![],
rf_activation_parameters: vec![],
passive_observe_mode: nci::PassiveObserveMode::Disable,
start_time: Instant::now(),
},
}
}
async fn send_control(&mut self, packet: impl Into<nci::ControlPacket>) -> Result<()> {
self.nci_writer.write(&packet.into().encode_to_vec()?).await
}
async fn send_data(&mut self, packet: impl Into<nci::DataPacket>) -> Result<()> {
self.nci_writer.write(&packet.into().encode_to_vec()?).await
}
async fn send_rf(&self, packet: impl Into<rf::RfPacket>) -> Result<()> {
self.rf_tx.send(packet.into())?;
Ok(())
}
async fn core_reset(&mut self, cmd: nci::CoreResetCommand) -> Result<()> {
info!("[{}] CORE_RESET_CMD", self.id);
info!(" ResetType: {:?}", cmd.get_reset_type());
match cmd.get_reset_type() {
nci::ResetType::KeepConfig => (),
nci::ResetType::ResetConfig => self.state.config_parameters = Default::default(),
}
for i in 0..MAX_LOGICAL_CONNECTIONS {
self.state.logical_connections[i as usize] = None;
}
self.state.discover_map.clear();
self.state.discover_configuration.clear();
self.state.rf_state = RfState::Idle;
self.state.rf_poll_responses.clear();
self.send_control(nci::CoreResetResponseBuilder { status: nci::Status::Ok }).await?;
self.send_control(nci::CoreResetNotificationBuilder {
trigger: nci::ResetTrigger::ResetCommand,
config_status: match cmd.get_reset_type() {
nci::ResetType::KeepConfig => nci::ConfigStatus::ConfigKept,
nci::ResetType::ResetConfig => nci::ConfigStatus::ConfigReset,
},
nci_version: NCI_VERSION,
manufacturer_id: MANUFACTURER_ID,
manufacturer_specific_information: MANUFACTURER_SPECIFIC_INFORMATION.to_vec(),
})
.await?;
Ok(())
}
async fn core_init(&mut self, _cmd: nci::CoreInitCommand) -> Result<()> {
info!("[{}] CORE_INIT_CMD", self.id);
self.send_control(nci::CoreInitResponseBuilder {
status: nci::Status::Ok,
nfcc_features: nci::NfccFeatures {
discovery_frequency_configuration: nci::FeatureFlag::Disabled,
discovery_configuration_mode: nci::DiscoveryConfigurationMode::DhOnly,
hci_network_support: nci::FeatureFlag::Enabled,
active_communication_mode: nci::FeatureFlag::Enabled,
technology_based_routing: nci::FeatureFlag::Enabled,
protocol_based_routing: nci::FeatureFlag::Enabled,
aid_based_routing: nci::FeatureFlag::Enabled,
system_code_based_routing: nci::FeatureFlag::Enabled,
apdu_pattern_based_routing: nci::FeatureFlag::Enabled,
forced_nfcee_routing: nci::FeatureFlag::Enabled,
battery_off_state: nci::FeatureFlag::Disabled,
switched_off_state: nci::FeatureFlag::Enabled,
switched_on_substates: nci::FeatureFlag::Enabled,
rf_configuration_in_switched_off_state: nci::FeatureFlag::Disabled,
proprietary_capabilities: 0,
},
max_logical_connections: MAX_LOGICAL_CONNECTIONS,
max_routing_table_size: MAX_ROUTING_TABLE_SIZE,
max_control_packet_payload_size: MAX_CONTROL_PACKET_PAYLOAD_SIZE,
max_data_packet_payload_size: MAX_DATA_PACKET_PAYLOAD_SIZE,
number_of_credits: NUMBER_OF_CREDITS,
max_nfcv_rf_frame_size: MAX_NFCV_RF_FRAME_SIZE,
supported_rf_interfaces: vec![
nci::RfInterface { interface: nci::RfInterfaceType::Frame, extensions: vec![] },
nci::RfInterface { interface: nci::RfInterfaceType::IsoDep, extensions: vec![] },
nci::RfInterface { interface: nci::RfInterfaceType::NfcDep, extensions: vec![] },
nci::RfInterface {
interface: nci::RfInterfaceType::NfceeDirect,
extensions: vec![],
},
],
})
.await?;
Ok(())
}
async fn core_set_config(&mut self, cmd: nci::CoreSetConfigCommand) -> Result<()> {
info!("[{}] CORE_SET_CONFIG_CMD", self.id);
let mut invalid_parameters = vec![];
for parameter in cmd.get_parameters().iter() {
info!(" Type: {:?}", parameter.id);
info!(" Value: {:?}", parameter.value);
match parameter.id {
nci::ConfigParameterId::Rfu(_) => invalid_parameters.push(parameter.id),
// TODO(henrichataing):
// [NCI] 5.2.1 State RFST_IDLE
// Unless otherwise specified, discovery related configuration
// defined in Sections 6.1, 6.2, 6.3 and 7.1 SHALL only be set
// while in IDLE state.
//
// Respond with Semantic Error as indicated by
// [NCI] 3.2.2 Exception Handling for Control Messages
// An unexpected Command SHALL NOT cause any action by the NFCC.
// Unless otherwise specified, the NFCC SHALL send a Response
// with a Status value of STATUS_SEMANTIC_ERROR and no
// additional fields.
_ => {
if self.state.config_parameters.set(parameter.id, &parameter.value).is_err() {
invalid_parameters.push(parameter.id)
}
}
}
}
self.send_control(nci::CoreSetConfigResponseBuilder {
status: if invalid_parameters.is_empty() {
// A Status of STATUS_OK SHALL indicate that all configuration parameters
// have been set to these new values in the NFCC.
nci::Status::Ok
} else {
// If the DH tries to set a parameter that is not applicable for the NFCC,
// the NFCC SHALL respond with a CORE_SET_CONFIG_RSP with a Status field
// of STATUS_INVALID_PARAM and including one or more invalid Parameter ID(s).
// All other configuration parameters SHALL have been set to the new values
// in the NFCC.
warn!(
"[{}] rejecting unknown configuration parameter ids: {:?}",
self.id, invalid_parameters
);
nci::Status::InvalidParam
},
parameters: invalid_parameters,
})
.await?;
Ok(())
}
async fn core_get_config(&mut self, cmd: nci::CoreGetConfigCommand) -> Result<()> {
info!("[{}] CORE_GET_CONFIG_CMD", self.id);
let mut valid_parameters = vec![];
let mut invalid_parameters = vec![];
for id in cmd.get_parameters() {
info!(" ID: {:?}", id);
match self.state.config_parameters.get(*id) {
Ok(value) => {
valid_parameters.push(nci::ConfigParameter { id: *id, value: value.to_vec() })
}
Err(_) => invalid_parameters.push(nci::ConfigParameter { id: *id, value: vec![] }),
}
}
self.send_control(if invalid_parameters.is_empty() {
// If the NFCC is able to respond with all requested parameters, the
// NFCC SHALL respond with the CORE_GET_CONFIG_RSP with a Status
// of STATUS_OK.
nci::CoreGetConfigResponseBuilder {
status: nci::Status::Ok,
parameters: valid_parameters,
}
} else {
// If the DH tries to retrieve any parameter(s) that are not available
// in the NFCC, the NFCC SHALL respond with a CORE_GET_CONFIG_RSP with
// a Status field of STATUS_INVALID_PARAM, containing each unavailable
// Parameter ID with a Parameter Len field of value zero.
nci::CoreGetConfigResponseBuilder {
status: nci::Status::InvalidParam,
parameters: invalid_parameters,
}
})
.await?;
Ok(())
}
async fn core_conn_create(&mut self, cmd: nci::CoreConnCreateCommand) -> Result<()> {
info!("[{}] CORE_CONN_CREATE_CMD", self.id);
let result: std::result::Result<u8, nci::Status> = (|| {
// Retrieve an unused connection ID for the logical connection.
let conn_id = {
(0..MAX_LOGICAL_CONNECTIONS)
.find(|conn_id| self.state.logical_connections[*conn_id as usize].is_none())
.ok_or(nci::Status::Rejected)?
};
// Check that the selected destination type is supported and validate
// the destination specific parameters.
let logical_connection = match cmd.get_destination_type() {
// If the value of Destination Type is that of a Remote NFC
// Endpoint (0x02), then only the Destination-specific Parameter
// with Type 0x00 or proprietary parameters (as defined in Table 16)
// SHALL be present.
nci::DestinationType::RemoteNfcEndpoint => {
let mut rf_discovery_id: Option<u8> = None;
let mut rf_protocol_type: Option<nci::RfProtocolType> = None;
for parameter in cmd.get_parameters() {
match parameter.id {
nci::DestinationSpecificParameterId::RfDiscovery => {
rf_discovery_id = parameter.value.first().cloned();
rf_protocol_type = parameter
.value
.get(1)
.and_then(|t| nci::RfProtocolType::try_from(*t).ok());
}
_ => return Err(nci::Status::Rejected),
}
}
LogicalConnection::RemoteNfcEndpoint {
rf_discovery_id: rf_discovery_id.ok_or(nci::Status::Rejected)?,
rf_protocol_type: rf_protocol_type.ok_or(nci::Status::Rejected)?,
}
}
nci::DestinationType::NfccLoopback | nci::DestinationType::Nfcee => {
return Err(nci::Status::Rejected)
}
};
// The combination of Destination Type and Destination Specific
// Parameters SHALL uniquely identify a single destination for the
// Logical Connection.
if self
.state
.logical_connections
.iter()
.any(|c| c.as_ref() == Some(&logical_connection))
{
return Err(nci::Status::Rejected);
}
// Create the connection.
self.state.logical_connections[conn_id as usize] = Some(logical_connection);
Ok(conn_id)
})();
self.send_control(match result {
Ok(conn_id) => nci::CoreConnCreateResponseBuilder {
status: nci::Status::Ok,
max_data_packet_payload_size: MAX_DATA_PACKET_PAYLOAD_SIZE,
initial_number_of_credits: 0xff,
conn_id: nci::ConnId::from_dynamic(conn_id),
},
Err(status) => nci::CoreConnCreateResponseBuilder {
status,
max_data_packet_payload_size: 0,
initial_number_of_credits: 0xff,
conn_id: 0.try_into().unwrap(),
},
})
.await?;
Ok(())
}
async fn core_conn_close(&mut self, cmd: nci::CoreConnCloseCommand) -> Result<()> {
info!("[{}] CORE_CONN_CLOSE_CMD", self.id);
let conn_id = match cmd.get_conn_id() {
nci::ConnId::StaticRf | nci::ConnId::StaticHci => {
warn!("[{}] core_conn_close called with static conn_id", self.id);
self.send_control(nci::CoreConnCloseResponseBuilder {
status: nci::Status::Rejected,
})
.await?;
return Ok(());
}
nci::ConnId::Dynamic(id) => nci::ConnId::to_dynamic(id),
};
let status = if conn_id >= MAX_LOGICAL_CONNECTIONS
|| self.state.logical_connections[conn_id as usize].is_none()
{
// If there is no connection associated to the Conn ID in the CORE_CONN_CLOSE_CMD, the
// NFCC SHALL reject the connection closure request by sending a CORE_CONN_CLOSE_RSP
// with a Status of STATUS_REJECTED.
nci::Status::Rejected
} else {
// When it receives a CORE_CONN_CLOSE_CMD for an existing connection, the NFCC SHALL
// accept the connection closure request by sending a CORE_CONN_CLOSE_RSP with a Status of
// STATUS_OK, and the Logical Connection is closed.
self.state.logical_connections[conn_id as usize] = None;
nci::Status::Ok
};
self.send_control(nci::CoreConnCloseResponseBuilder { status }).await?;
Ok(())
}
async fn core_set_power_sub_state(
&mut self,
cmd: nci::CoreSetPowerSubStateCommand,
) -> Result<()> {
info!("[{}] CORE_SET_POWER_SUB_STATE_CMD", self.id);
info!(" State: {:?}", cmd.get_power_state());
self.send_control(nci::CoreSetPowerSubStateResponseBuilder { status: nci::Status::Ok })
.await?;
Ok(())
}
async fn rf_discover_map(&mut self, cmd: nci::RfDiscoverMapCommand) -> Result<()> {
info!("[{}] RF_DISCOVER_MAP_CMD", self.id);
self.state.discover_map.clone_from(cmd.get_mapping_configurations());
self.send_control(nci::RfDiscoverMapResponseBuilder { status: nci::Status::Ok }).await?;
Ok(())
}
async fn rf_set_listen_mode_routing(
&mut self,
_cmd: nci::RfSetListenModeRoutingCommand,
) -> Result<()> {
info!("[{}] RF_SET_LISTEN_MODE_ROUTING_CMD", self.id);
self.send_control(nci::RfSetListenModeRoutingResponseBuilder { status: nci::Status::Ok })
.await?;
Ok(())
}
async fn rf_get_listen_mode_routing(
&mut self,
_cmd: nci::RfGetListenModeRoutingCommand,
) -> Result<()> {
info!("[{}] RF_GET_LISTEN_MODE_ROUTING_CMD", self.id);
self.send_control(nci::RfGetListenModeRoutingResponseBuilder {
status: nci::Status::Ok,
more_to_follow: 0,
routing_entries: vec![],
})
.await?;
Ok(())
}
async fn rf_discover(&mut self, cmd: nci::RfDiscoverCommand) -> Result<()> {
info!("[{}] RF_DISCOVER_CMD", self.id);
for config in cmd.get_configurations() {
info!(" TechMode: {:?}", config.technology_and_mode);
}
if self.state.rf_state != RfState::Idle {
warn!("[{}] rf_discover received in {:?} state", self.id, self.state.rf_state);
self.send_control(nci::RfDiscoverResponseBuilder {
status: nci::Status::SemanticError,
})
.await?;
return Ok(());
}
self.state.discover_configuration.clone_from(cmd.get_configurations());
self.state.rf_state = RfState::Discovery;
self.send_control(nci::RfDiscoverResponseBuilder { status: nci::Status::Ok }).await?;
Ok(())
}
async fn rf_discover_select(&mut self, cmd: nci::RfDiscoverSelectCommand) -> Result<()> {
info!("[{}] RF_DISCOVER_SELECT_CMD", self.id);
info!(" DiscoveryID: {:?}", cmd.get_rf_discovery_id());
info!(" Protocol: {:?}", cmd.get_rf_protocol());
info!(" Interface: {:?}", cmd.get_rf_interface());
if self.state.rf_state != RfState::WaitForHostSelect {
warn!("[{}] rf_discover_select received in {:?} state", self.id, self.state.rf_state);
self.send_control(nci::RfDiscoverSelectResponseBuilder {
status: nci::Status::SemanticError,
})
.await?;
return Ok(());
}
let rf_discovery_id = match cmd.get_rf_discovery_id() {
nci::RfDiscoveryId::Rfu(_) => {
warn!("[{}] rf_discover_select with reserved rf_discovery_id", self.id);
self.send_control(nci::RfDiscoverSelectResponseBuilder {
status: nci::Status::Rejected,
})
.await?;
return Ok(());
}
nci::RfDiscoveryId::Id(id) => nci::RfDiscoveryId::to_index(id),
};
// If the RF Discovery ID, RF Protocol or RF Interface is not valid,
// the NFCC SHALL respond with RF_DISCOVER_SELECT_RSP with a Status of
// STATUS_REJECTED.
if rf_discovery_id >= self.state.rf_poll_responses.len() {
warn!("[{}] rf_discover_select with invalid rf_discovery_id", self.id);
self.send_control(nci::RfDiscoverSelectResponseBuilder {
status: nci::Status::Rejected,
})
.await?;
return Ok(());
}
if cmd.get_rf_protocol() != self.state.rf_poll_responses[rf_discovery_id].rf_protocol.into()
{
warn!("[{}] rf_discover_select with invalid rf_protocol", self.id);
self.send_control(nci::RfDiscoverSelectResponseBuilder {
status: nci::Status::Rejected,
})
.await?;
return Ok(());
}
self.send_control(nci::RfDiscoverSelectResponseBuilder { status: nci::Status::Ok }).await?;
// Send RF select command to the peer to activate the device.
// The command has varying parameters based on the activated protocol.
self.activate_poll_interface(
rf_discovery_id,
cmd.get_rf_protocol(),
cmd.get_rf_interface(),
)
.await?;
Ok(())
}
async fn rf_deactivate(&mut self, cmd: nci::RfDeactivateCommand) -> Result<()> {
info!("[{}] RF_DEACTIVATE_CMD", self.id);
info!(" Type: {:?}", cmd.get_deactivation_type());
use nci::DeactivationType::*;
let (status, mut next_state) = match (self.state.rf_state, cmd.get_deactivation_type()) {
(RfState::Idle, _) => (nci::Status::SemanticError, RfState::Idle),
(RfState::Discovery, IdleMode) => (nci::Status::Ok, RfState::Idle),
(RfState::Discovery, _) => (nci::Status::SemanticError, RfState::Discovery),
(RfState::PollActive { .. }, IdleMode) => (nci::Status::Ok, RfState::Idle),
(RfState::PollActive { .. }, SleepMode | SleepAfMode) => {
(nci::Status::Ok, RfState::WaitForHostSelect)
}
(RfState::PollActive { .. }, Discovery) => (nci::Status::Ok, RfState::Discovery),
(RfState::ListenSleep { .. }, IdleMode) => (nci::Status::Ok, RfState::Idle),
(RfState::ListenSleep { .. }, _) => (nci::Status::SemanticError, self.state.rf_state),
(RfState::ListenActive { .. }, IdleMode) => (nci::Status::Ok, RfState::Idle),
(RfState::ListenActive { id, .. }, SleepMode | SleepAfMode) => {
(nci::Status::Ok, RfState::ListenSleep { id })
}
(RfState::ListenActive { .. }, Discovery) => (nci::Status::Ok, RfState::Discovery),
(RfState::WaitForHostSelect, IdleMode) => (nci::Status::Ok, RfState::Idle),
(RfState::WaitForHostSelect, _) => {
(nci::Status::SemanticError, RfState::WaitForHostSelect)
}
(RfState::WaitForSelectResponse { .. }, IdleMode) => (nci::Status::Ok, RfState::Idle),
(RfState::WaitForSelectResponse { .. }, _) => {
(nci::Status::SemanticError, self.state.rf_state)
}
};
// Update the state now to prevent interface activation from
// completing if a remote device is being selected.
(next_state, self.state.rf_state) = (self.state.rf_state, next_state);
self.send_control(nci::RfDeactivateResponseBuilder { status }).await?;
// Deactivate the active RF interface if applicable
// (next_state is the previous state in this context).
match next_state {
RfState::PollActive { .. } | RfState::ListenActive { .. } => {
info!("[{}] RF_DEACTIVATE_NTF", self.id);
info!(" Type: {:?}", cmd.get_deactivation_type());
info!(" Reason: DH_Request");
self.field_info(rf::FieldStatus::FieldOff, 255).await?;
self.send_control(nci::RfDeactivateNotificationBuilder {
deactivation_type: cmd.get_deactivation_type(),
deactivation_reason: nci::DeactivationReason::DhRequest,
})
.await?
}
_ => (),
}
// Deselect the remote device if applicable.
match next_state {
RfState::PollActive { id, rf_protocol, rf_technology, .. }
| RfState::WaitForSelectResponse { id, rf_protocol, rf_technology, .. } => {
self.send_rf(rf::DeactivateNotificationBuilder {
receiver: id,
protocol: rf_protocol,
technology: rf_technology,
power_level: 255,
sender: self.id,
type_: cmd.get_deactivation_type().into(),
reason: rf::DeactivateReason::EndpointRequest,
})
.await?
}
_ => (),
}
Ok(())
}
async fn nfcee_discover(&mut self, _cmd: nci::NfceeDiscoverCommand) -> Result<()> {
info!("[{}] NFCEE_DISCOVER_CMD", self.id);
self.send_control(nci::NfceeDiscoverResponseBuilder {
status: nci::Status::Ok,
number_of_nfcees: 1,
})
.await?;
self.send_control(nci::NfceeDiscoverNotificationBuilder {
nfcee_id: nci::NfceeId::hci_nfcee(0x86),
nfcee_status: nci::NfceeStatus::Disabled,
supported_nfcee_protocols: vec![],
nfcee_information: vec![nci::NfceeInformation {
r#type: nci::NfceeInformationType::HostId,
value: vec![0xc0],
}],
nfcee_supply_power: nci::NfceeSupplyPower::NfccHasNoControl,
})
.await?;
Ok(())
}
async fn nfcee_mode_set(&mut self, cmd: nci::NfceeModeSetCommand) -> Result<()> {
info!("[{}] NFCEE_MODE_SET_CMD", self.id);
info!(" NFCEE ID: {:?}", cmd.get_nfcee_id());
info!(" NFCEE Mode: {:?}", cmd.get_nfcee_mode());
if cmd.get_nfcee_id() != nci::NfceeId::hci_nfcee(0x86) {
warn!("[{}] nfcee_mode_set with invalid nfcee_id", self.id);
self.send_control(nci::NfceeModeSetResponseBuilder { status: nci::Status::Ok }).await?;
return Ok(());
}
self.state.nfcee_state = match cmd.get_nfcee_mode() {
nci::NfceeMode::Enable => NfceeState::Enabled,
nci::NfceeMode::Disable => NfceeState::Disabled,
};
self.send_control(nci::NfceeModeSetResponseBuilder { status: nci::Status::Ok }).await?;
self.send_control(nci::NfceeModeSetNotificationBuilder { status: nci::Status::Ok }).await?;
if self.state.nfcee_state == NfceeState::Enabled {
// Android host stack expects this notification to know when the
// NFCEE completes start-up. The list of information entries is
// filled with defaults observed on real phones.
self.send_data(nci::DataPacketBuilder {
mt: nci::MessageType::Data,
conn_id: nci::ConnId::StaticHci,
cr: 0,
payload: Some(bytes::Bytes::copy_from_slice(&[0x81, 0x43, 0xc0, 0x01])),
})
.await?;
self.send_control(nci::RfNfceeDiscoveryReqNotificationBuilder {
information_entries: vec![
nci::InformationEntry {
r#type: nci::InformationEntryType::AddDiscoveryRequest,
nfcee_id: nci::NfceeId::hci_nfcee(0x86),
rf_technology_and_mode: nci::RfTechnologyAndMode::NfcFPassiveListenMode,
rf_protocol: nci::RfProtocolType::T3t,
},
nci::InformationEntry {
r#type: nci::InformationEntryType::AddDiscoveryRequest,
nfcee_id: nci::NfceeId::hci_nfcee(0x86),
rf_technology_and_mode: nci::RfTechnologyAndMode::NfcAPassiveListenMode,
rf_protocol: nci::RfProtocolType::IsoDep,
},
nci::InformationEntry {
r#type: nci::InformationEntryType::AddDiscoveryRequest,
nfcee_id: nci::NfceeId::hci_nfcee(0x86),
rf_technology_and_mode: nci::RfTechnologyAndMode::NfcBPassiveListenMode,
rf_protocol: nci::RfProtocolType::IsoDep,
},
],
})
.await?;
}
Ok(())
}
async fn android_get_caps(&mut self, _cmd: nci::AndroidGetCapsCommand) -> Result<()> {
info!("[{}] ANDROID_GET_CAPS_CMD", self.id);
let cap_tlvs = vec![
nci::CapTlv { t: nci::CapTlvType::PassiveObserverMode, v: vec![1] },
nci::CapTlv { t: nci::CapTlvType::PollingFrameNotification, v: vec![1] },
];
self.send_control(nci::AndroidGetCapsResponseBuilder {
status: nci::Status::Ok,
android_version: 0,
tlvs: cap_tlvs,
})
.await?;
Ok(())
}
async fn android_passive_observe_mode(
&mut self,
cmd: nci::AndroidPassiveObserveModeCommand,
) -> Result<()> {
info!("[{}] ANDROID_PASSIVE_OBSERVE_MODE_CMD", self.id);
info!(" Mode: {:?}", cmd.get_passive_observe_mode());
self.state.passive_observe_mode = cmd.get_passive_observe_mode();
self.send_control(nci::AndroidPassiveObserveModeResponseBuilder {
status: nci::Status::Ok,
})
.await?;
Ok(())
}
async fn android_query_passive_observe_mode(
&mut self,
_cmd: nci::AndroidQueryPassiveObserveModeCommand,
) -> Result<()> {
info!("[{}] ANDROID_QUERY_PASSIVE_OBSERVE_MODE_CMD", self.id);
self.send_control(nci::AndroidQueryPassiveObserveModeResponseBuilder {
status: nci::Status::Ok,
passive_observe_mode: self.state.passive_observe_mode,
})
.await?;
Ok(())
}
async fn receive_command(&mut self, packet: nci::ControlPacket) -> Result<()> {
use nci::AndroidPacketChild::*;
use nci::ControlPacketChild::*;
use nci::CorePacketChild::*;
use nci::NfceePacketChild::*;
use nci::ProprietaryPacketChild::*;
use nci::RfPacketChild::*;
match packet.specialize() {
CorePacket(packet) => match packet.specialize() {
CoreResetCommand(cmd) => self.core_reset(cmd).await,
CoreInitCommand(cmd) => self.core_init(cmd).await,
CoreSetConfigCommand(cmd) => self.core_set_config(cmd).await,
CoreGetConfigCommand(cmd) => self.core_get_config(cmd).await,
CoreConnCreateCommand(cmd) => self.core_conn_create(cmd).await,
CoreConnCloseCommand(cmd) => self.core_conn_close(cmd).await,
CoreSetPowerSubStateCommand(cmd) => self.core_set_power_sub_state(cmd).await,
_ => unimplemented!("unsupported core oid {:?}", packet.get_oid()),
},
RfPacket(packet) => match packet.specialize() {
RfDiscoverMapCommand(cmd) => self.rf_discover_map(cmd).await,
RfSetListenModeRoutingCommand(cmd) => self.rf_set_listen_mode_routing(cmd).await,
RfGetListenModeRoutingCommand(cmd) => self.rf_get_listen_mode_routing(cmd).await,
RfDiscoverCommand(cmd) => self.rf_discover(cmd).await,
RfDiscoverSelectCommand(cmd) => self.rf_discover_select(cmd).await,
RfDeactivateCommand(cmd) => self.rf_deactivate(cmd).await,
_ => unimplemented!("unsupported rf oid {:?}", packet.get_oid()),
},
NfceePacket(packet) => match packet.specialize() {
NfceeDiscoverCommand(cmd) => self.nfcee_discover(cmd).await,
NfceeModeSetCommand(cmd) => self.nfcee_mode_set(cmd).await,
_ => unimplemented!("unsupported nfcee oid {:?}", packet.get_oid()),
},
ProprietaryPacket(packet) => match packet.specialize() {
AndroidPacket(packet) => match packet.specialize() {
AndroidGetCapsCommand(cmd) => self.android_get_caps(cmd).await,
AndroidPassiveObserveModeCommand(cmd) => {
self.android_passive_observe_mode(cmd).await
}
AndroidQueryPassiveObserveModeCommand(cmd) => {
self.android_query_passive_observe_mode(cmd).await
}
_ => {
unimplemented!("unsupported android oid {:?}", packet.get_android_sub_oid())
}
},
_ => unimplemented!("unsupported proprietary oid {:?}", packet.get_oid()),
},
_ => unimplemented!("unsupported gid {:?}", packet.get_gid()),
}
}
async fn rf_conn_data(&mut self, packet: nci::DataPacket) -> Result<()> {
info!("[{}] received data on RF logical connection", self.id);
// TODO(henrichataing) implement credit based control flow.
match self.state.rf_state {
RfState::PollActive {
id,
rf_technology,
rf_protocol: rf::Protocol::IsoDep,
rf_interface: nci::RfInterfaceType::IsoDep,
..
}
| RfState::ListenActive {
id,
rf_technology,
rf_protocol: rf::Protocol::IsoDep,
rf_interface: nci::RfInterfaceType::IsoDep,
..
} => {
self.send_rf(rf::DataBuilder {
receiver: id,
sender: self.id,
power_level: 255,
protocol: rf::Protocol::IsoDep,
technology: rf_technology,
data: packet.get_payload().into(),
})
.await?;
// Resplenish the credit count for the RF Connection.
self.send_control(
nci::CoreConnCreditsNotificationBuilder {
connections: vec![nci::ConnectionCredits {
conn_id: nci::ConnId::StaticRf,
credits: 1,
}],
}
.build(),
)
.await
}
RfState::PollActive {
rf_protocol: rf::Protocol::IsoDep,
rf_interface: nci::RfInterfaceType::Frame,
..
} => {
println!("ISO-DEP frame data {:?}", packet.get_payload());
match packet.get_payload() {
// RATS command
// TODO(henrichataing) Send back the response received from
// the peer in the RF packet.
[0xe0, _] => {
warn!("[{}] frame RATS command", self.id);
self.send_data(nci::DataPacketBuilder {
mt: nci::MessageType::Data,
conn_id: nci::ConnId::StaticRf,
cr: 0,
payload: Some(bytes::Bytes::copy_from_slice(
&self.state.rf_activation_parameters,
)),
})
.await?
}
// DESELECT command
// TODO(henrichataing) check if the command should be
// forwarded to the peer, and if it warrants a response
[0xc2] => warn!("[{}] unimplemented frame DESELECT command", self.id),
// SLP_REQ command
// No response is expected for this command.
// TODO(henrichataing) forward a deactivation request to
// the peer and deactivate the local interface.
[0x50, 0x00] => warn!("[{}] unimplemented frame SLP_REQ command", self.id),
_ => unimplemented!(),
};
// Resplenish the credit count for the RF Connection.
self.send_control(
nci::CoreConnCreditsNotificationBuilder {
connections: vec![nci::ConnectionCredits {
conn_id: nci::ConnId::StaticRf,
credits: 1,
}],
}
.build(),
)
.await
}
RfState::PollActive { rf_protocol, rf_interface, .. }
| RfState::ListenActive { rf_protocol, rf_interface, .. } => unimplemented!(
"unsupported combination of RF protocol {:?} and interface {:?}",
rf_protocol,
rf_interface
),
_ => {
warn!(
"[{}] ignored RF data packet while not in active listen or poll mode",
self.id
);
Ok(())
}
}
}
async fn hci_conn_data(&mut self, packet: nci::DataPacket) -> Result<()> {
info!("[{}] received data on HCI logical connection", self.id);
// TODO: parse and understand HCI Control Protocol (HCP)
// to accurately respond to the requests. For now it is sufficient
// to return hardcoded answers to identified requests.
let response = match packet.get_payload() {
// ANY_OPEN_PIPE()
[0x81, 0x03] => vec![0x81, 0x80],
// ANY_GET_PARAMETER(index=1)
[0x81, 0x02, 0x01] => vec![0x81, 0x80, 0xd7, 0xfe, 0x65, 0x66, 0xc7, 0xfe, 0x65, 0x66],
// ANY_GET_PARAMETER(index=4)
[0x81, 0x02, 0x04] => vec![0x81, 0x80, 0x00, 0xc0, 0x01],
// ANY_SET_PARAMETER()
[0x81, 0x01, 0x03, 0x02, 0xc0]
| [0x81, 0x01, 0x03, _, _, _]
| [0x81, 0x01, 0x01, _, 0x00, 0x00, 0x00, _, 0x00, 0x00, 0x00] => vec![0x81, 0x80],
// ADM_CLEAR_ALL_PIPE()
[0x81, 0x14, 0x02, 0x01] => vec![0x81, 0x80],
_ => {
error!("unimplemented HCI command : {:?}", packet.get_payload());
unimplemented!()
}
};
self.send_data(nci::DataPacketBuilder {
mt: nci::MessageType::Data,
conn_id: nci::ConnId::StaticHci,
cr: 0,
payload: Some(bytes::Bytes::copy_from_slice(&response)),
})
.await?;
// Resplenish the credit count for the HCI Connection.
self.send_control(
nci::CoreConnCreditsNotificationBuilder {
connections: vec![nci::ConnectionCredits {
conn_id: nci::ConnId::StaticHci,
credits: 1,
}],
}
.build(),
)
.await
}
async fn dynamic_conn_data(&self, _conn_id: u8, _packet: nci::DataPacket) -> Result<()> {
info!("[{}] received data on dynamic logical connection", self.id);
todo!()
}
async fn receive_data(&mut self, packet: nci::DataPacket) -> Result<()> {
info!("[{}] receive_data({})", self.id, u8::from(packet.get_conn_id()));
match packet.get_conn_id() {
nci::ConnId::StaticRf => self.rf_conn_data(packet).await,
nci::ConnId::StaticHci => self.hci_conn_data(packet).await,
nci::ConnId::Dynamic(id) => self.dynamic_conn_data(*id, packet).await,
}
}
async fn field_info(&mut self, field_status: rf::FieldStatus, power_level: u8) -> Result<()> {
if self.state.config_parameters.rf_field_info != 0 {
self.send_control(nci::RfFieldInfoNotificationBuilder {
rf_field_status: match field_status {
rf::FieldStatus::FieldOn => nci::RfFieldStatus::FieldDetected,
rf::FieldStatus::FieldOff => nci::RfFieldStatus::NoFieldDetected,
},
})
.await?;
}
self.send_control(nci::AndroidPollingLoopNotificationBuilder {
polling_frames: vec![nci::PollingFrame {
frame_type: nci::PollingFrameType::RemoteField,
flags: 0,
timestamp: (self.state.start_time.elapsed().as_micros() as u32).to_be_bytes(),
gain: power_level,
payload: vec![field_status.into()],
}],
})
.await?;
Ok(())
}
async fn poll_command(&mut self, cmd: rf::PollCommand) -> Result<()> {
trace!("[{}] poll_command()", self.id);
if self.state.rf_state != RfState::Discovery {
return Ok(());
}
let technology = cmd.get_technology();
// Android proprietary extension for polling frame notifications.
// The NFCC should send the NCI_ANDROID_POLLING_FRAME_NTF to the Host
// after each polling loop frame
// This notification is independent of whether Passive Observe Mode is
// active or not. When Passive Observe Mode is active, the NFCC
// should always send this notification before proceeding with the
// transaction.
self.send_control(nci::AndroidPollingLoopNotificationBuilder {
polling_frames: vec![nci::PollingFrame {
frame_type: match technology {
rf::Technology::NfcA => nci::PollingFrameType::Reqa,
rf::Technology::NfcB => nci::PollingFrameType::Reqb,
rf::Technology::NfcF => nci::PollingFrameType::Reqf,
rf::Technology::NfcV => nci::PollingFrameType::Reqv,
rf::Technology::Raw => nci::PollingFrameType::Unknown,
},
flags: 0,
timestamp: (self.state.start_time.elapsed().as_micros() as u32).to_be_bytes(),
gain: cmd.get_power_level(),
payload: cmd.get_payload().to_vec(),
}],
})
.await?;
// When the Passive Observe Mode is active, the NFCC shall not respond
// to any poll requests during the polling loop in Listen Mode, until
// explicitly authorized by the Host.
if self.state.passive_observe_mode == nci::PassiveObserveMode::Enable {
return Ok(());
}
if self.state.discover_configuration.iter().any(|config| {
matches!(
(config.technology_and_mode, technology),
(nci::RfTechnologyAndMode::NfcAPassiveListenMode, rf::Technology::NfcA)
| (nci::RfTechnologyAndMode::NfcBPassiveListenMode, rf::Technology::NfcB)
| (nci::RfTechnologyAndMode::NfcFPassiveListenMode, rf::Technology::NfcF)
)
}) {
match technology {
rf::Technology::NfcA => {
self.send_rf(rf::NfcAPollResponseBuilder {
protocol: rf::Protocol::Undetermined,
receiver: cmd.get_sender(),
sender: self.id,
power_level: 255,
nfcid1: self.state.nfcid1(),
int_protocol: self.state.config_parameters.la_sel_info >> 5,
bit_frame_sdd: self.state.config_parameters.la_bit_frame_sdd,
})
.await?
}
// TODO(b/346715736) implement support for NFC-B technology
rf::Technology::NfcB => (),
rf::Technology::NfcF => todo!(),
_ => (),
}
}
Ok(())
}
async fn nfca_poll_response(&mut self, cmd: rf::NfcAPollResponse) -> Result<()> {
info!("[{}] nfca_poll_response()", self.id);
if self.state.rf_state != RfState::Discovery {
return Ok(());
}
let int_protocol = cmd.get_int_protocol();
let rf_protocols = match int_protocol {
0b00 => [rf::Protocol::T2t].iter(),
0b01 => [rf::Protocol::IsoDep].iter(),
0b10 => [rf::Protocol::NfcDep].iter(),
0b11 => [rf::Protocol::NfcDep, rf::Protocol::IsoDep].iter(),
_ => return Ok(()),
};
let sens_res = match cmd.get_nfcid1().len() {
4 => 0x00,
7 => 0x40,
10 => 0x80,
_ => panic!(),
} | cmd.get_bit_frame_sdd() as u16;
let sel_res = int_protocol << 5;
for rf_protocol in rf_protocols {
self.state.add_poll_response(RfPollResponse {
id: cmd.get_sender(),
rf_protocol: *rf_protocol,
rf_technology: rf::Technology::NfcA,
rf_technology_specific_parameters:
nci::NfcAPollModeTechnologySpecificParametersBuilder {
sens_res,
nfcid1: cmd.get_nfcid1().clone(),
sel_res,
}
.build()
.encode_to_vec()?,
})
}
Ok(())
}
async fn t4at_select_command(&mut self, cmd: rf::T4ATSelectCommand) -> Result<()> {
info!("[{}] t4at_select_command()", self.id);
match self.state.rf_state {
RfState::Discovery => (),
RfState::ListenSleep { id } if id == cmd.get_sender() => (),
_ => return Ok(()),
};
// TODO(henrichataing): validate that the protocol and technology are
// valid for the current discovery settings.
// TODO(henrichataing): use listen mode routing table to decide which
// interface should be used for the activating device.
self.state.rf_state = RfState::ListenActive {
id: cmd.get_sender(),
rf_technology: rf::Technology::NfcA,
rf_protocol: rf::Protocol::IsoDep,
rf_interface: nci::RfInterfaceType::IsoDep,
};
// [DIGITAL] 14.6.2 RATS Response (Answer To Select)
// Construct the response from the values passed in the configuration
// parameters. The TL byte is excluded from the response.
let mut rats_response = vec![
0x78, // TC(1), TB(1), TA(1) transmitted, FSCI=8
0x80, // TA(1)
self.state.config_parameters.li_a_rats_tb1,
self.state.config_parameters.li_a_rats_tc1,
];
rats_response.extend_from_slice(&self.state.config_parameters.li_a_hist_by);
self.send_rf(rf::T4ATSelectResponseBuilder {
receiver: cmd.get_sender(),
sender: self.id,
power_level: 255,
rats_response,
})
.await?;
info!("[{}] RF_INTF_ACTIVATED_NTF", self.id);
info!(" DiscoveryID: {:?}", nci::RfDiscoveryId::from_index(0));
info!(" Interface: ISO-DEP");
info!(" Protocol: ISO-DEP");
info!(" ActivationTechnology: NFC_A_PASSIVE_LISTEN");
info!(" RATS: {}", cmd.get_param());
self.send_control(nci::RfIntfActivatedNotificationBuilder {
rf_discovery_id: nci::RfDiscoveryId::from_index(0),
rf_interface: nci::RfInterfaceType::IsoDep,
rf_protocol: nci::RfProtocolType::IsoDep,
activation_rf_technology_and_mode: nci::RfTechnologyAndMode::NfcAPassiveListenMode,
max_data_packet_payload_size: MAX_DATA_PACKET_PAYLOAD_SIZE,
initial_number_of_credits: 1,
// No parameters are currently defined for NFC-A Listen Mode.
rf_technology_specific_parameters: vec![],
data_exchange_rf_technology_and_mode: nci::RfTechnologyAndMode::NfcAPassiveListenMode,
data_exchange_transmit_bit_rate: nci::BitRate::BitRate106KbitS,
data_exchange_receive_bit_rate: nci::BitRate::BitRate106KbitS,
activation_parameters: nci::NfcAIsoDepListenModeActivationParametersBuilder {
param: cmd.get_param(),
}
.build()
.encode_to_vec()?,
})
.await?;
Ok(())
}
async fn t4at_select_response(&mut self, cmd: rf::T4ATSelectResponse) -> Result<()> {
info!("[{}] t4at_select_response()", self.id);
let (id, rf_discovery_id, rf_interface, rf_protocol) = match self.state.rf_state {
RfState::WaitForSelectResponse {
id,
rf_discovery_id,
rf_interface,
rf_protocol,
..
} => (id, rf_discovery_id, rf_interface, rf_protocol),
_ => return Ok(()),
};
if cmd.get_sender() != id {
return Ok(());
}
self.state.rf_state = RfState::PollActive {
id,
rf_protocol: self.state.rf_poll_responses[rf_discovery_id].rf_protocol,
rf_technology: self.state.rf_poll_responses[rf_discovery_id].rf_technology,
rf_interface,
};
// Save the activation parameters for the RF frame interface
// implementation. Note: TL is not included in the RATS response
// and needs to be added manually to the activation parameters.
self.state.rf_activation_parameters = vec![cmd.get_rats_response().len() as u8];
self.state.rf_activation_parameters.extend_from_slice(cmd.get_rats_response());
info!("[{}] RF_INTF_ACTIVATED_NTF", self.id);
info!(" DiscoveryID: {:?}", nci::RfDiscoveryId::from_index(rf_discovery_id));
info!(" Interface: {:?}", rf_interface);
info!(" Protocol: {:?}", rf_protocol);
info!(" ActivationTechnology: NFC_A_PASSIVE_POLL");
info!(" RATS: {:?}", cmd.get_rats_response());
self.send_control(nci::RfIntfActivatedNotificationBuilder {
rf_discovery_id: nci::RfDiscoveryId::from_index(rf_discovery_id),
rf_interface,
rf_protocol: rf_protocol.into(),
activation_rf_technology_and_mode: nci::RfTechnologyAndMode::NfcAPassivePollMode,
max_data_packet_payload_size: MAX_DATA_PACKET_PAYLOAD_SIZE,
initial_number_of_credits: 1,
rf_technology_specific_parameters: self.state.rf_poll_responses[rf_discovery_id]
.rf_technology_specific_parameters
.clone(),
data_exchange_rf_technology_and_mode: nci::RfTechnologyAndMode::NfcAPassivePollMode,
data_exchange_transmit_bit_rate: nci::BitRate::BitRate106KbitS,
data_exchange_receive_bit_rate: nci::BitRate::BitRate106KbitS,
// TODO(hchataing) the activation parameters should be empty
// when the RF frame interface is used, since the protocol
// activation is managed by the DH.
activation_parameters: nci::NfcAIsoDepPollModeActivationParametersBuilder {
rats_response: cmd.get_rats_response().clone(),
}
.build()
.encode_to_vec()?,
})
.await?;
Ok(())
}
async fn data_packet(&mut self, data: rf::Data) -> Result<()> {
info!("[{}] data_packet()", self.id);
match (self.state.rf_state, data.get_protocol()) {
(
RfState::PollActive {
id, rf_technology, rf_protocol: rf::Protocol::IsoDep, ..
},
rf::Protocol::IsoDep,
)
| (
RfState::ListenActive {
id, rf_technology, rf_protocol: rf::Protocol::IsoDep, ..
},
rf::Protocol::IsoDep,
) if data.get_sender() == id && data.get_technology() == rf_technology => {
self.send_data(nci::DataPacketBuilder {
mt: nci::MessageType::Data,
conn_id: nci::ConnId::StaticRf,
cr: 1, // TODO(henrichataing): credit based control flow
payload: Some(bytes::Bytes::copy_from_slice(data.get_data())),
})
.await
}
(RfState::PollActive { id, .. }, _) | (RfState::ListenActive { id, .. }, _)
if id != data.get_sender() =>
{
warn!("[{}] ignored RF data packet sent from an un-selected device", self.id);
Ok(())
}
(RfState::PollActive { .. }, _) | (RfState::ListenActive { .. }, _) => {
unimplemented!("unsupported combination of technology and protocol")
}
(_, _) => {
warn!("[{}] ignored RF data packet received in inactive state", self.id);
Ok(())
}
}
}
async fn deactivate_notification(&mut self, cmd: rf::DeactivateNotification) -> Result<()> {
info!("[{}] deactivate_notification()", self.id);
use rf::DeactivateType::*;
let mut next_state = match (self.state.rf_state, cmd.get_type_()) {
(RfState::PollActive { id, .. }, IdleMode) if id == cmd.get_sender() => RfState::Idle,
(RfState::PollActive { id, .. }, SleepMode | SleepAfMode) if id == cmd.get_sender() => {
RfState::WaitForHostSelect
}
(RfState::PollActive { id, .. }, Discovery) if id == cmd.get_sender() => {
RfState::Discovery
}
(RfState::ListenSleep { id, .. }, IdleMode) if id == cmd.get_sender() => RfState::Idle,
(RfState::ListenSleep { id, .. }, Discovery) if id == cmd.get_sender() => {
RfState::Discovery
}
(RfState::ListenActive { id, .. }, IdleMode) if id == cmd.get_sender() => RfState::Idle,
(RfState::ListenActive { id, .. }, SleepMode | SleepAfMode)
if id == cmd.get_sender() =>
{
RfState::ListenSleep { id }
}
(RfState::ListenActive { id, .. }, Discovery) if id == cmd.get_sender() => {
RfState::Discovery
}
(_, _) => self.state.rf_state,
};
// Update the state now to prevent interface activation from
// completing if a remote device is being selected.
(next_state, self.state.rf_state) = (self.state.rf_state, next_state);
// Deactivate the active RF interface if applicable.
if next_state != self.state.rf_state {
self.field_info(rf::FieldStatus::FieldOff, 255).await?;
self.send_control(nci::RfDeactivateNotificationBuilder {
deactivation_type: cmd.get_type_().into(),
deactivation_reason: cmd.get_reason().into(),
})
.await?
}
Ok(())
}
async fn receive_rf(&mut self, packet: rf::RfPacket) -> Result<()> {
use rf::RfPacketChild::*;
match packet.specialize() {
PollCommand(cmd) => self.poll_command(cmd).await,
FieldInfo(cmd) => self.field_info(cmd.get_field_status(), cmd.get_power_level()).await,
NfcAPollResponse(cmd) => self.nfca_poll_response(cmd).await,
// [NCI] 5.2.2 State RFST_DISCOVERY
// If discovered by a Remote NFC Endpoint in Listen mode, once the
// Remote NFC Endpoint has established any underlying protocol(s) needed
// by the configured RF Interface, the NFCC SHALL send
// RF_INTF_ACTIVATED_NTF (Listen Mode) to the DH and the state is
// changed to RFST_LISTEN_ACTIVE.
T4ATSelectCommand(cmd) => self.t4at_select_command(cmd).await,
T4ATSelectResponse(cmd) => self.t4at_select_response(cmd).await,
SelectCommand(_) => unimplemented!(),
DeactivateNotification(cmd) => self.deactivate_notification(cmd).await,
Data(cmd) => self.data_packet(cmd).await,
_ => unimplemented!(),
}
}
/// Activity for activating an RF interface for a discovered device.
///
/// The method send a notification when the interface is successfully
/// activated, or when the device activation fails.
///
/// * `rf_discovery_id` - index of the discovered device
/// * `rf_interface` - interface to activate
///
/// The RF state is changed to WaitForSelectResponse when
/// the select command is successfully sent.
async fn activate_poll_interface(
&mut self,
rf_discovery_id: usize,
rf_protocol: nci::RfProtocolType,
rf_interface: nci::RfInterfaceType,
) -> Result<()> {
info!("[{}] activate_poll_interface({:?})", self.id, rf_interface);
let rf_technology = self.state.rf_poll_responses[rf_discovery_id].rf_technology;
match (rf_protocol, rf_technology) {
(nci::RfProtocolType::T2t, rf::Technology::NfcA) => {
self.send_rf(rf::SelectCommandBuilder {
sender: self.id,
receiver: self.state.rf_poll_responses[rf_discovery_id].id,
technology: rf::Technology::NfcA,
power_level: 255,
protocol: rf::Protocol::T2t,
})
.await?
}
(nci::RfProtocolType::IsoDep, rf::Technology::NfcA) => {
self.send_rf(rf::T4ATSelectCommandBuilder {
sender: self.id,
receiver: self.state.rf_poll_responses[rf_discovery_id].id,
power_level: 255,
// [DIGITAL] 14.6.1.6 The FSD supported by the
// Reader/Writer SHALL be FSD T4AT,MIN
// (set to 256 in Appendix B.6).
param: 0x80,
})
.await?
}
(nci::RfProtocolType::NfcDep, rf::Technology::NfcA) => {
self.send_rf(rf::NfcDepSelectCommandBuilder {
sender: self.id,
receiver: self.state.rf_poll_responses[rf_discovery_id].id,
power_level: 255,
technology: rf::Technology::NfcA,
lr: 0,
})
.await?
}
_ => todo!(),
}
self.state.rf_state = RfState::WaitForSelectResponse {
id: self.state.rf_poll_responses[rf_discovery_id].id,
rf_discovery_id,
rf_interface,
rf_protocol: rf_protocol.into(),
rf_technology,
};
Ok(())
}
async fn run_until<O>(&mut self, future: impl Future<Output = O>) -> Result<O> {
let mut future = pin!(future);
loop {
tokio::select! {
packet = self.nci_stream.next() => {
let packet = packet.ok_or(anyhow::anyhow!("nci channel closed"))??;
let header = nci::PacketHeader::parse(&packet[0..3])?;
match header.get_mt() {
nci::MessageType::Data => {
self.receive_data(nci::DataPacket::parse(&packet)?).await?
}
nci::MessageType::Command => {
self.receive_command(nci::ControlPacket::parse(&packet)?).await?
}
mt => {
return Err(anyhow::anyhow!(
"unexpected message type {:?} in received NCI packet",
mt
))
}
}
},
rf_packet = self.rf_rx.recv() => {
self.receive_rf(
rf_packet.ok_or(anyhow::anyhow!("rf_rx channel closed"))?,
)
.await?
},
output = &mut future => break Ok(output)
}
}
}
/// Timer handler method. This function is invoked at regular interval
/// on the NFCC instance and is used to drive internal timers.
async fn tick(&mut self) -> Result<()> {
if self.state.rf_state != RfState::Discovery {
return Ok(());
}
//info!("[{}] poll", self.id);
// [NCI] 5.2.2 State RFST_DISCOVERY
//
// In this state the NFCC stays in Poll Mode and/or Listen Mode (based
// on the discovery configuration) until at least one Remote NFC
// Endpoint is detected or the RF Discovery Process is stopped by
// the DH.
//
// The following implements the Poll Mode Discovery, Listen Mode
// Discover is implicitly implemented in response to poll and
// select commands.
// RF Discovery is ongoing and no peer device has been discovered
// so far. Send a RF poll command for all enabled technologies.
self.state.rf_poll_responses.clear();
for configuration in self.state.discover_configuration.iter() {
self.send_rf(rf::PollCommandBuilder {
sender: self.id,
receiver: u16::MAX,
protocol: rf::Protocol::Undetermined,
technology: match configuration.technology_and_mode {
nci::RfTechnologyAndMode::NfcAPassivePollMode => rf::Technology::NfcA,
nci::RfTechnologyAndMode::NfcBPassivePollMode => rf::Technology::NfcB,
nci::RfTechnologyAndMode::NfcFPassivePollMode => rf::Technology::NfcF,
nci::RfTechnologyAndMode::NfcVPassivePollMode => rf::Technology::NfcV,
_ => continue,
},
power_level: 255,
payload: Some(bytes::Bytes::new()),
})
.await?
}
// Wait for poll responses to return.
self.run_until(time::sleep(Duration::from_millis(POLL_RESPONSE_TIMEOUT))).await?;
// Check if device was activated in Listen mode during
// the poll interval, or if the discovery got cancelled.
if self.state.rf_state != RfState::Discovery || self.state.rf_poll_responses.is_empty() {
return Ok(());
}
// While polling, if the NFCC discovers just one Remote NFC Endpoint
// that supports just one protocol, the NFCC SHALL try to automatically
// activate it. The NFCC SHALL first establish any underlying
// protocol(s) with the Remote NFC Endpoint that are needed by the
// configured RF Interface. On completion, the NFCC SHALL activate the
// RF Interface and send RF_INTF_ACTIVATED_NTF (Poll Mode) to the DH.
// At this point, the state is changed to RFST_POLL_ACTIVE. If the
// protocol activation is not successful, the NFCC SHALL send
// CORE_GENERIC_ERROR_NTF to the DH with status
// DISCOVERY_TARGET_ACTIVATION_FAILED and SHALL stay in the
// RFST_DISCOVERY state.
if self.state.rf_poll_responses.len() == 1 {
let rf_protocol = self.state.rf_poll_responses[0].rf_protocol.into();
let rf_interface = self.state.select_interface(RfMode::Poll, rf_protocol);
return self.activate_poll_interface(0, rf_protocol, rf_interface).await;
}
debug!("[{}] received {} poll response(s)", self.id, self.state.rf_poll_responses.len());
// While polling, if the NFCC discovers more than one Remote NFC
// Endpoint, or a Remote NFC Endpoint that supports more than one RF
// Protocol, it SHALL start sending RF_DISCOVER_NTF messages to the DH.
// At this point, the state is changed to RFST_W4_ALL_DISCOVERIES.
self.state.rf_state = RfState::WaitForHostSelect;
let last_index = self.state.rf_poll_responses.len() - 1;
for (index, response) in self.state.rf_poll_responses.clone().iter().enumerate() {
self.send_control(nci::RfDiscoverNotificationBuilder {
rf_discovery_id: nci::RfDiscoveryId::from_index(index),
rf_protocol: response.rf_protocol.into(),
rf_technology_and_mode: match response.rf_technology {
rf::Technology::NfcA => nci::RfTechnologyAndMode::NfcAPassivePollMode,
rf::Technology::NfcB => nci::RfTechnologyAndMode::NfcBPassivePollMode,
_ => todo!(),
},
rf_technology_specific_parameters: response
.rf_technology_specific_parameters
.clone(),
notification_type: if index == last_index {
nci::DiscoverNotificationType::LastNotification
} else {
nci::DiscoverNotificationType::MoreNotifications
},
})
.await?
}
Ok(())
}
/// Main NFCC instance routine.
pub async fn run(
id: u16,
nci_stream: nci::StreamRefMut<'a>,
nci_writer: nci::Writer,
rf_rx: mpsc::UnboundedReceiver<rf::RfPacket>,
rf_tx: mpsc::UnboundedSender<rf::RfPacket>,
) -> Result<()> {
// Local controller state.
let mut nfcc = Controller::new(id, nci_stream, nci_writer, rf_rx, rf_tx);
// Timer for tick events.
let mut timer = time::interval(Duration::from_millis(1000));
loop {
nfcc.run_until(timer.tick()).await?;
nfcc.tick().await?;
}
}
}