// Copyright 2021, 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. //! NCI API module use crate::{CommandSender, LogicalConnectionsRegistry, Result}; use bytes::Bytes; use log::{debug, error}; use nfc_hal::{HalEvent, HalEventRegistry, HalEventStatus}; use nfc_packets::nci::RfMappingConfiguration; use nfc_packets::nci::{self, CommandBuilder, DataPacket, Opcode}; use nfc_packets::nci::{ConnCloseCommandBuilder, ConnCreateCommandBuilder}; use nfc_packets::nci::{DestParam, DestParamTypes, DestTypes}; use nfc_packets::nci::{FeatureEnable, PacketBoundaryFlag, ResetType}; use nfc_packets::nci::{InitCommandBuilder, ResetCommandBuilder}; use nfc_packets::nci::{InitResponse, ResponseChild}; use pdl_runtime::Packet; use tokio::sync::oneshot; type ConnCallback = fn(u8, u16, &[u8]); struct NfcData { init_response: Option, rf_callback: Option, hci_callback: Option, } type RespCallback = fn(u16, &[u8]); /// NCI API object to manage static API data pub struct NciApi { /// Command Sender external interface commands: Option, /// Interface to Logical Connections Registry connections: Option, /// The NFC response callback callback: Option, /// HalEventRegistry is used to register for HAL events hal_events: Option, nfc_data: NfcData, } impl NciApi { /// NciApi constructor pub fn new() -> NciApi { let nfc_data = NfcData { init_response: None, rf_callback: None, hci_callback: None }; NciApi { commands: None, connections: None, callback: None, hal_events: None, nfc_data } } /** **************************************************************************** ** ** Function nfc_enable ** ** Description This function enables NFC. Prior to calling NFC_Enable: ** - the NFCC must be powered up, and ready to receive ** commands. ** ** This function opens the NCI transport (if applicable), ** resets the NFC controller, and initializes the NFC ** subsystems. ** ** When the NFC startup procedure is completed, an ** NFC_ENABLE_REVT is returned to the application using the ** tNFC_RESPONSE_CBACK. ** ** Returns tNFC_STATUS ** *******************************************************************************/ /// extern tNFC_STATUS NFC_Enable(tNFC_RESPONSE_CBACK* p_cback); pub async fn nfc_enable(&mut self, callback: RespCallback) { let nci = crate::init().await; self.commands = Some(nci.commands); self.connections = Some(nci.connections); self.callback = Some(callback); self.hal_events = Some(nci.hal_events); } /** **************************************************************************** ** ** Function NFC_Disable ** ** Description This function performs clean up routines for shutting down ** NFC and closes the NCI transport (if using dedicated NCI ** transport). ** ** When the NFC shutdown procedure is completed, an ** NFC_DISABLED_REVT is returned to the application using the ** tNFC_RESPONSE_CBACK. ** ** Returns nothing ** *******************************************************************************/ /// extern void NFC_Disable(void); pub async fn nfc_disable(&mut self) { let (tx, rx) = oneshot::channel::(); if let Some(mut event) = self.hal_events.take() { event.register(HalEvent::CloseComplete, tx).await; if let Some(cmd) = self.commands.take() { drop(cmd); } if let Some(conn) = self.connections.take() { drop(conn); } let status = rx.await.unwrap(); debug!("Shutdown complete {:?}.", status); if let Some(cb) = self.callback.take() { cb(1, &[]); } } } /** **************************************************************************** ** ** Function NFC_Init ** ** Description This function initializes control blocks for NFC ** ** Returns nothing ** *******************************************************************************/ /// extern void NFC_Init(tHAL_NFC_ENTRY* p_hal_entry_tbl); pub async fn nfc_init(&mut self) -> Result<()> { let pbf = PacketBoundaryFlag::CompleteOrFinal; if let Some(cmd) = self.commands.as_mut() { let reset = cmd .send_and_notify( ResetCommandBuilder { gid: 0, pbf, reset_type: ResetType::ResetConfig } .build() .into(), ) .await?; let _notification_packet = reset.notification.await?; let init = cmd .send( InitCommandBuilder { gid: 0, pbf, feature_enable: FeatureEnable::Rfu } .build() .into(), ) .await?; if let ResponseChild::InitResponse(irp) = init.specialize() { if let Some(conn) = self.connections.as_mut() { // Open static RF connection // TODO: use channels instead of callcacks here // the data can be tranlated to c-callback at the shim level conn.open(0, self.nfc_data.rf_callback, 0, 0).await; // Open static HCI connection conn.open( 1, /* TODO: link constants to the c header */ self.nfc_data.hci_callback, irp.get_max_data_payload(), irp.get_num_of_credits(), ) .await; } self.nfc_data.init_response = Some(irp); } } Ok(()) } /** ***************************************************************************** ** ** Function NFC_GetLmrtSize ** ** Description Called by application wto query the Listen Mode Routing ** Table size supported by NFCC ** ** Returns Listen Mode Routing Table size ** *******************************************************************************/ /// extern uint16_t NFC_GetLmrtSize(void); pub async fn nfc_get_lmrt_size(&mut self) -> u16 { if let Some(ir) = &self.nfc_data.init_response { ir.get_max_rout_tbls_size() } else { 0 } } /** ***************************************************************************** ** ** Function NFC_SetConfig ** ** Description This function is called to send the configuration parameter ** TLV to NFCC. The response from NFCC is reported by ** tNFC_RESPONSE_CBACK as NFC_SET_CONFIG_REVT. ** ** Parameters tlv_size - the length of p_param_tlvs. ** p_param_tlvs - the parameter ID/Len/Value list ** ** Returns tNFC_STATUS ** *******************************************************************************/ /// extern tNFC_STATUS NFC_SetConfig(uint8_t tlv_size, uint8_t* p_param_tlvs); pub async fn nfc_set_config(&mut self, param_tlvs: &[u8]) -> Result { let pbf = PacketBoundaryFlag::CompleteOrFinal; if let Some(cmd) = self.commands.as_mut() { let raw = cmd .send( CommandBuilder { gid: 0, pbf, op: Opcode::CoreSetConfig, payload: Some(Bytes::copy_from_slice(param_tlvs)), } .build(), ) .await? .encode_to_bytes()?; if let Some(cb) = self.callback { cb(2, &raw[3..]); } Ok(raw[3]) } else { Ok(nci::Status::NotInitialized as u8) } } /** ***************************************************************************** ** ** Function NFC_GetConfig ** ** Description This function is called to retrieve the parameter TLV from ** NFCC. The response from NFCC is reported by ** tNFC_RESPONSE_CBACK as NFC_GET_CONFIG_REVT. ** ** Parameters num_ids - the number of parameter IDs ** p_param_ids - the parameter ID list. ** ** Returns tNFC_STATUS ** *******************************************************************************/ /// extern tNFC_STATUS NFC_GetConfig(uint8_t num_ids, uint8_t* p_param_ids); pub async fn nfc_get_config(&mut self, param_tlvs: &[u8]) -> Result { let pbf = PacketBoundaryFlag::CompleteOrFinal; if let Some(cmd) = self.commands.as_mut() { let raw = cmd .send( CommandBuilder { gid: 0, pbf, op: Opcode::CoreGetConfig, payload: Some(Bytes::copy_from_slice(param_tlvs)), } .build(), ) .await? .encode_to_bytes()?; if let Some(cb) = self.callback { cb(3, &raw[3..]); } Ok(raw[3]) } else { Ok(nci::Status::NotInitialized as u8) } } /** **************************************************************************** ** ** Function NFC_ConnCreate ** ** Description This function is called to create a logical connection with ** NFCC for data exchange. ** The response from NFCC is reported in tNFC_CONN_CBACK ** as NFC_CONN_CREATE_CEVT. ** ** Parameters dest_type - the destination type ** id - the NFCEE ID or RF Discovery ID . ** protocol - the protocol ** p_cback - the data callback function to receive data from ** NFCC ** ** Returns tNFC_STATUS ** *******************************************************************************/ //extern tNFC_STATUS NFC_ConnCreate(uint8_t dest_type, uint8_t id, // uint8_t protocol, tNFC_CONN_CBACK* p_cback); pub async fn nfc_conn_create( &mut self, dest_type: u8, id: u8, protocol: u8, callback: ConnCallback, ) -> Result { let pbf = PacketBoundaryFlag::CompleteOrFinal; let mut destparams: Vec = vec![]; let dt = DestTypes::try_from(dest_type).unwrap(); match dt { DestTypes::NfccLpbk => (), DestTypes::Remote => { let parameter = vec![id, protocol]; destparams.push(DestParam { ptype: DestParamTypes::RfDisc, parameter }); } DestTypes::Nfcee => { let parameter: Vec = vec![id, protocol]; destparams.push(DestParam { ptype: DestParamTypes::Nfcee, parameter }); } _ => return Ok(nci::Status::InvalidParam as u8), } if let Some(cmd) = self.commands.as_mut() { let rp = cmd .send(ConnCreateCommandBuilder { gid: 0, pbf, dt, destparams }.build().into()) .await?; if let ResponseChild::ConnCreateResponse(ccrp) = rp.specialize() { let status = ccrp.get_status(); if status == nci::Status::Ok { if let Some(conn) = self.connections.as_mut() { conn.open( ccrp.get_conn_id(), Some(callback), ccrp.get_mpps(), ccrp.get_ncreds(), ) .await; let conn_create_evt = [status as u8, dest_type, id, ccrp.get_mpps(), ccrp.get_ncreds()]; callback(ccrp.get_conn_id(), 0, &conn_create_evt[..]); } else { return Ok(nci::Status::NotInitialized as u8); } } Ok(status as u8) } else { Ok(nci::Status::Failed as u8) } } else { Ok(nci::Status::NotInitialized as u8) } } /** **************************************************************************** ** ** Function NFC_ConnClose ** ** Description This function is called to close a logical connection with ** NFCC. ** The response from NFCC is reported in tNFC_CONN_CBACK ** as NFC_CONN_CLOSE_CEVT. ** ** Parameters conn_id - the connection id. ** ** Returns tNFC_STATUS ** *******************************************************************************/ //extern tNFC_STATUS NFC_ConnClose(uint8_t conn_id); pub async fn nfc_conn_close(&mut self, conn_id: u8) -> Result { let pbf = PacketBoundaryFlag::CompleteOrFinal; if let Some(conn) = self.connections.as_mut() { if let Some(cb) = conn.close(conn_id).await { if let Some(cmd) = self.commands.as_mut() { let rp = cmd .send(ConnCloseCommandBuilder { gid: 0, pbf, conn_id }.build().into()) .await?; if let ResponseChild::ConnCloseResponse(ccrp) = rp.specialize() { let status = ccrp.get_status() as u8; let conn_close_evt = [status]; cb(conn_id, 1, &conn_close_evt[..]); return Ok(status); } else { return Ok(nci::Status::Failed as u8); } } } else { return Ok(nci::Status::InvalidParam as u8); } } Ok(nci::Status::NotInitialized as u8) } /** ***************************************************************************** ** ** Function NFC_SetStaticRfCback ** ** Description This function is called to update the data callback function ** to receive the data for the given connection id. ** ** Parameters p_cback - the connection callback function ** ** Returns Nothing ** *******************************************************************************/ //extern void NFC_SetStaticRfCback(tNFC_CONN_CBACK* p_cback); pub async fn nfc_set_static_rf_callback(&mut self, callback: ConnCallback) { self.nfc_data.rf_callback = Some(callback); if let Some(conn) = self.connections.as_mut() { conn.set_static_callback(0, Some(callback)).await; } } /** ***************************************************************************** ** ** Function NFC_SetStaticHciCback ** ** Description This function to update the data callback function ** to receive the data for the static Hci connection id. ** ** Parameters p_cback - the connection callback function ** ** Returns Nothing ** *******************************************************************************/ //extern void NFC_SetStaticHciCback(tNFC_CONN_CBACK* p_cback); pub async fn nfc_set_static_hci_callback(&mut self, callback: ConnCallback) { self.nfc_data.hci_callback = Some(callback); if let Some(conn) = self.connections.as_mut() { conn.set_static_callback(1, Some(callback)).await; } } /******************************************************************************* ** ** Function NFC_SetReassemblyFlag ** ** Description This function is called to set if nfc will reassemble ** nci packet as much as its buffer can hold or it should not ** reassemble but forward the fragmented nci packet to layer ** above. If nci data pkt is fragmented, nfc may send multiple ** NFC_DATA_CEVT with status NFC_STATUS_CONTINUE before sending ** NFC_DATA_CEVT with status NFC_STATUS_OK based on reassembly ** configuration and reassembly buffer size ** ** Parameters reassembly - flag to indicate if nfc may reassemble or not ** ** Returns Nothing ** *******************************************************************************/ //extern void NFC_SetReassemblyFlag(bool reassembly); /** **************************************************************************** ** ** Function NFC_SendData ** ** Description This function is called to send the given data packet ** to the connection identified by the given connection id. ** ** Parameters conn_id - the connection id. ** p_data - the data packet ** ** Returns tNFC_STATUS ** *******************************************************************************/ //extern tNFC_STATUS NFC_SendData(uint8_t conn_id, NFC_HDR* p_data); pub async fn nfc_send_data(&mut self, conn_id: u8, data: &[u8]) -> Result { if let Some(conn) = self.connections.as_mut() { match DataPacket::parse(data) { Ok(pkt) => { conn.send_packet(conn_id, pkt).await; return Ok(nci::Status::Ok as u8); } Err(e) => { error!("Data packet is invalid:{:?}", e); return Ok(nci::Status::InvalidParam as u8); } } } Ok(nci::Status::NotInitialized as u8) } /** **************************************************************************** ** ** Function NFC_FlushData ** ** Description This function is called to discard the tx data queue of ** the given connection id. ** ** Parameters conn_id - the connection id. ** ** Returns tNFC_STATUS ** *******************************************************************************/ //extern tNFC_STATUS NFC_FlushData(uint8_t conn_id); pub async fn nfc_flush_data(&mut self, conn_id: u8) -> Result { if let Some(conn) = self.connections.as_mut() { if conn.flush_data(conn_id).await { Ok(nci::Status::Ok as u8) } else { Ok(nci::Status::Failed as u8) } } else { Ok(nci::Status::NotInitialized as u8) } } /** **************************************************************************** ** ** Function NFC_DiscoveryMap ** ** Description This function is called to set the discovery interface ** mapping. The response from NFCC is reported by ** tNFC_DISCOVER_CBACK as. NFC_MAP_DEVT. ** ** Parameters num - the number of items in p_params. ** p_maps - the discovery interface mappings ** p_cback - the discovery callback function ** ** Returns tNFC_STATUS ** *******************************************************************************/ // extern tNFC_STATUS NFC_DiscoveryMap(uint8_t num, tNFC_DISCOVER_MAPS* p_maps, // tNFC_DISCOVER_CBACK* p_cback); pub async fn nfc_discovery_map(&mut self, _maps: Vec) -> Result { Ok(0) } /******************************************************************************* ** ** Function NFC_DiscoveryStart ** ** Description This function is called to start Polling and/or Listening. ** The response from NFCC is reported by tNFC_DISCOVER_CBACK ** as NFC_START_DEVT. The notification from NFCC is reported by ** tNFC_DISCOVER_CBACK as NFC_RESULT_DEVT. ** ** Parameters num_params - the number of items in p_params. ** p_params - the discovery parameters ** p_cback - the discovery callback function ** ** Returns tNFC_STATUS ** *******************************************************************************/ // extern tNFC_STATUS NFC_DiscoveryStart(uint8_t num_params, // tNFC_DISCOVER_PARAMS* p_params, // tNFC_DISCOVER_CBACK* p_cback); /******************************************************************************* ** ** Function NFC_DiscoverySelect ** ** Description If tNFC_DISCOVER_CBACK reports status=NFC_MULTIPLE_PROT, ** the application needs to use this function to select the ** the logical endpoint to continue. The response from NFCC is ** reported by tNFC_DISCOVER_CBACK as NFC_SELECT_DEVT. ** ** Parameters rf_disc_id - The ID identifies the remote device. ** protocol - the logical endpoint on the remote device ** rf_interface - the RF interface to communicate with NFCC ** ** Returns tNFC_STATUS ** *******************************************************************************/ // extern tNFC_STATUS NFC_DiscoverySelect(uint8_t rf_disc_id, uint8_t protocol, // uint8_t rf_interface); } impl Default for NciApi { fn default() -> Self { Self::new() } }