blob: eae509669ce0ac55e482d2896f44b59bfa952da6 [file] [log] [blame]
// 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<InitResponse>,
rf_callback: Option<ConnCallback>,
hci_callback: Option<ConnCallback>,
}
type RespCallback = fn(u16, &[u8]);
/// NCI API object to manage static API data
pub struct NciApi {
/// Command Sender external interface
commands: Option<CommandSender>,
/// Interface to Logical Connections Registry
connections: Option<LogicalConnectionsRegistry>,
/// The NFC response callback
callback: Option<RespCallback>,
/// HalEventRegistry is used to register for HAL events
hal_events: Option<HalEventRegistry>,
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::<HalEventStatus>();
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<u8> {
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<u8> {
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<u8> {
let pbf = PacketBoundaryFlag::CompleteOrFinal;
let mut destparams: Vec<DestParam> = 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<u8> = 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<u8> {
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<u8> {
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<u8> {
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<RfMappingConfiguration>) -> Result<u8> {
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()
}
}