Casimir: Implement Listen mode discovery for NFC-A/ISO-DEP
Bug: 297993136
Test: manual
Change-Id: Ic37973e481e55ced4c162ca834fa50ea9e4960a1
diff --git a/tools/casimir/src/controller.rs b/tools/casimir/src/controller.rs
index e9b27cc..0843148 100644
--- a/tools/casimir/src/controller.rs
+++ b/tools/casimir/src/controller.rs
@@ -63,7 +63,12 @@
rf_protocol: rf::Protocol,
},
ListenSleep,
- ListenActive,
+ ListenActive {
+ id: u16,
+ rf_interface: nci::RfInterfaceType,
+ rf_technology: rf::Technology,
+ rf_protocol: rf::Protocol,
+ },
WaitForHostSelect,
WaitForSelectResponse {
id: u16,
@@ -269,7 +274,7 @@
for parameter in cmd.get_parameters().iter() {
match parameter.id {
nci::ConfigParameterId::Rfu(_) => invalid_parameters.push(parameter.id),
- // TODO:
+ // 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
@@ -590,11 +595,11 @@
(RfState::PollActive { .. }, Discover) => (nci::Status::Ok, RfState::Discovery),
(RfState::ListenSleep, IdleMode) => (nci::Status::Ok, RfState::Idle),
(RfState::ListenSleep, _) => (nci::Status::SemanticError, RfState::ListenSleep),
- (RfState::ListenActive, IdleMode) => (nci::Status::Ok, RfState::Idle),
- (RfState::ListenActive, SleepMode | SleepAfMode) => {
+ (RfState::ListenActive { .. }, IdleMode) => (nci::Status::Ok, RfState::Idle),
+ (RfState::ListenActive { .. }, SleepMode | SleepAfMode) => {
(nci::Status::Ok, RfState::ListenSleep)
}
- (RfState::ListenActive, Discover) => (nci::Status::Ok, RfState::Discovery),
+ (RfState::ListenActive { .. }, Discover) => (nci::Status::Ok, RfState::Discovery),
(RfState::WaitForHostSelect, IdleMode) => (nci::Status::Ok, RfState::Idle),
(RfState::WaitForHostSelect, _) => {
(nci::Status::SemanticError, RfState::WaitForHostSelect)
@@ -700,6 +705,13 @@
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,
@@ -721,12 +733,12 @@
)
.await
}
- RfState::PollActive { rf_protocol, rf_interface, .. } => unimplemented!(
+ RfState::PollActive { rf_protocol, rf_interface, .. }
+ | RfState::ListenActive { rf_protocol, rf_interface, .. } => unimplemented!(
"unsupported combination of RF protocol {:?} and interface {:?}",
rf_protocol,
rf_interface
),
- RfState::ListenActive => unimplemented!("unsupported data in active listen mode"),
_ => {
println!(" > ignored RF data packet while not in active listen or poll mode");
Ok(())
@@ -762,23 +774,32 @@
return Ok(());
}
- match cmd.get_technology() {
- rf::Technology::NfcA => {
- // Configured for T4AT tag emulation.
- let int_protocol = 0x01;
- self.send_rf(rf::NfcAPollResponseBuilder {
- protocol: rf::Protocol::Undetermined,
- receiver: cmd.get_sender(),
- sender: self.id,
- nfcid1: self.nfcid1(),
- int_protocol,
- })
- .await?;
+ let technology = cmd.get_technology();
+ if 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 => {
+ // Configured for T4AT tag emulation.
+ let int_protocol = 0x01;
+ self.send_rf(rf::NfcAPollResponseBuilder {
+ protocol: rf::Protocol::Undetermined,
+ receiver: cmd.get_sender(),
+ sender: self.id,
+ nfcid1: self.nfcid1(),
+ int_protocol,
+ })
+ .await?
+ }
+ rf::Technology::NfcB => todo!(),
+ rf::Technology::NfcF => todo!(),
+ _ => (),
}
-
- rf::Technology::NfcB => todo!(),
- rf::Technology::NfcF => todo!(),
- rf::Technology::NfcV => todo!(),
}
Ok(())
@@ -827,9 +848,57 @@
Ok(())
}
- async fn t4at_select_command(&self, _cmd: rf::T4ATSelectCommand) -> Result<()> {
+ async fn t4at_select_command(&self, cmd: rf::T4ATSelectCommand) -> Result<()> {
println!("+ t4at_select_command()");
+ let mut state = self.state.lock().await;
+ if state.rf_state != RfState::Discovery {
+ 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.
+
+ state.rf_state = RfState::ListenActive {
+ id: cmd.get_sender(),
+ rf_technology: rf::Technology::NfcA,
+ rf_protocol: rf::Protocol::IsoDep,
+ rf_interface: nci::RfInterfaceType::IsoDep,
+ };
+
+ self.send_rf(rf::T4ATSelectResponseBuilder {
+ receiver: cmd.get_sender(),
+ sender: self.id,
+ // [DIGITAL] 14.6.2 RATS Response (Answer To Select)
+ // TODO(henrichataing): this value is just a valid default;
+ // construct the RATS response from global capabilities.
+ rats_response: vec![0x2, 0x0],
+ })
+ .await?;
+
+ self.send_control(nci::RfIntfActivatedNotificationBuilder {
+ rf_discovery_id: 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()
+ .to_vec(),
+ })
+ .await?;
+
Ok(())
}
@@ -894,20 +963,28 @@
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: STATIC_RF_CONN,
- cr: 1, // TODO: credit based control flow
+ cr: 1, // TODO(henrichataing): credit based control flow
payload: Some(bytes::Bytes::copy_from_slice(data.get_data())),
})
.await
}
- (RfState::PollActive { id, .. }, _) if id != data.get_sender() => {
+ (RfState::PollActive { id, .. }, _) | (RfState::ListenActive { id, .. }, _)
+ if id != data.get_sender() =>
+ {
println!(" > ignored RF data packet sent from an un-selected device");
Ok(())
}
- (RfState::PollActive { .. }, _) => {
+ (RfState::PollActive { .. }, _) | (RfState::ListenActive { .. }, _) => {
unimplemented!("unsupported combination of technology and protocol")
}
(_, _) => {
diff --git a/tools/casimir/src/packets.rs b/tools/casimir/src/packets.rs
index 0cef4fd..4855130 100644
--- a/tools/casimir/src/packets.rs
+++ b/tools/casimir/src/packets.rs
@@ -62,9 +62,9 @@
}
}
-impl TryFrom<&nci::RfTechnologyAndMode> for rf::Technology {
+impl TryFrom<nci::RfTechnologyAndMode> for rf::Technology {
type Error = nci::RfTechnologyAndMode;
- fn try_from(protocol: &nci::RfTechnologyAndMode) -> Result<Self, Self::Error> {
+ fn try_from(protocol: nci::RfTechnologyAndMode) -> Result<Self, Self::Error> {
Ok(match protocol {
nci::RfTechnologyAndMode::NfcAPassivePollMode
| nci::RfTechnologyAndMode::NfcAPassiveListenMode => rf::Technology::NfcA,
@@ -73,7 +73,7 @@
nci::RfTechnologyAndMode::NfcFPassivePollMode
| nci::RfTechnologyAndMode::NfcFPassiveListenMode => rf::Technology::NfcF,
nci::RfTechnologyAndMode::NfcVPassivePollMode => rf::Technology::NfcV,
- _ => return Err(*protocol),
+ _ => return Err(protocol),
})
}
}