// 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, pi_bit_rate: u8, pn_nfc_dep_psl: u8, pn_atr_req_gen_bytes: Vec, /// [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, /// [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, li_b_h_info_resp: Vec, li_a_bit_rate: u8, li_a_rats_tc1: u8, ln_wt: u8, ln_atr_res_gen_bytes: Vec, 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, } /// State of an NFCC instance. #[allow(missing_docs)] pub struct State { pub config_parameters: ConfigParameters, pub logical_connections: [Option; MAX_LOGICAL_CONNECTIONS as usize], pub discover_configuration: Vec, pub discover_map: Vec, pub nfcee_state: NfceeState, pub rf_state: RfState, pub rf_poll_responses: Vec, pub rf_activation_parameters: Vec, 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_tx: mpsc::UnboundedSender, state: State, } impl ConfigParameters { fn get(&self, id: nci::ConfigParameterId) -> Result> { 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 { 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_tx: mpsc::UnboundedSender, ) -> 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) -> Result<()> { self.nci_writer.write(&packet.into().encode_to_vec()?).await } async fn send_data(&mut self, packet: impl Into) -> Result<()> { self.nci_writer.write(&packet.into().encode_to_vec()?).await } async fn send_rf(&self, packet: impl Into) -> 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, ¶meter.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 = (|| { // 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 = None; let mut rf_protocol_type: Option = 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(&mut self, future: impl Future) -> Result { 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_tx: mpsc::UnboundedSender, ) -> 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?; } } }