1 // Copyright 2021, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! NCI API module
16 
17 use crate::{CommandSender, LogicalConnectionsRegistry, Result};
18 use bytes::Bytes;
19 use log::{debug, error};
20 use nfc_hal::{HalEvent, HalEventRegistry, HalEventStatus};
21 use nfc_packets::nci::RfMappingConfiguration;
22 use nfc_packets::nci::{self, CommandBuilder, DataPacket, Opcode};
23 use nfc_packets::nci::{ConnCloseCommandBuilder, ConnCreateCommandBuilder};
24 use nfc_packets::nci::{DestParam, DestParamTypes, DestTypes};
25 use nfc_packets::nci::{FeatureEnable, PacketBoundaryFlag, ResetType};
26 use nfc_packets::nci::{InitCommandBuilder, ResetCommandBuilder};
27 use nfc_packets::nci::{InitResponse, ResponseChild};
28 use pdl_runtime::Packet;
29 use tokio::sync::oneshot;
30 
31 type ConnCallback = fn(u8, u16, &[u8]);
32 
33 struct NfcData {
34     init_response: Option<InitResponse>,
35     rf_callback: Option<ConnCallback>,
36     hci_callback: Option<ConnCallback>,
37 }
38 
39 type RespCallback = fn(u16, &[u8]);
40 
41 /// NCI API object to manage static API data
42 pub struct NciApi {
43     /// Command Sender external interface
44     commands: Option<CommandSender>,
45     /// Interface to Logical Connections Registry
46     connections: Option<LogicalConnectionsRegistry>,
47     /// The NFC response callback
48     callback: Option<RespCallback>,
49     /// HalEventRegistry is used to register for HAL events
50     hal_events: Option<HalEventRegistry>,
51     nfc_data: NfcData,
52 }
53 
54 impl NciApi {
55     /// NciApi constructor
new() -> NciApi56     pub fn new() -> NciApi {
57         let nfc_data = NfcData { init_response: None, rf_callback: None, hci_callback: None };
58         NciApi { commands: None, connections: None, callback: None, hal_events: None, nfc_data }
59     }
60 
61     /** ****************************************************************************
62      **
63      ** Function         nfc_enable
64      **
65      ** Description      This function enables NFC. Prior to calling NFC_Enable:
66      **                  - the NFCC must be powered up, and ready to receive
67      **                    commands.
68      **
69      **                  This function opens the NCI transport (if applicable),
70      **                  resets the NFC controller, and initializes the NFC
71      **                  subsystems.
72      **
73      **                  When the NFC startup procedure is completed, an
74      **                  NFC_ENABLE_REVT is returned to the application using the
75      **                  tNFC_RESPONSE_CBACK.
76      **
77      ** Returns          tNFC_STATUS
78      **
79      *******************************************************************************/
80     /// extern tNFC_STATUS NFC_Enable(tNFC_RESPONSE_CBACK* p_cback);
nfc_enable(&mut self, callback: RespCallback)81     pub async fn nfc_enable(&mut self, callback: RespCallback) {
82         let nci = crate::init().await;
83 
84         self.commands = Some(nci.commands);
85         self.connections = Some(nci.connections);
86         self.callback = Some(callback);
87         self.hal_events = Some(nci.hal_events);
88     }
89     /** ****************************************************************************
90      **
91      ** Function         NFC_Disable
92      **
93      ** Description      This function performs clean up routines for shutting down
94      **                  NFC and closes the NCI transport (if using dedicated NCI
95      **                  transport).
96      **
97      **                  When the NFC shutdown procedure is completed, an
98      **                  NFC_DISABLED_REVT is returned to the application using the
99      **                  tNFC_RESPONSE_CBACK.
100      **
101      ** Returns          nothing
102      **
103      *******************************************************************************/
104     /// extern void NFC_Disable(void);
nfc_disable(&mut self)105     pub async fn nfc_disable(&mut self) {
106         let (tx, rx) = oneshot::channel::<HalEventStatus>();
107         if let Some(mut event) = self.hal_events.take() {
108             event.register(HalEvent::CloseComplete, tx).await;
109 
110             if let Some(cmd) = self.commands.take() {
111                 drop(cmd);
112             }
113             if let Some(conn) = self.connections.take() {
114                 drop(conn);
115             }
116             let status = rx.await.unwrap();
117             debug!("Shutdown complete {:?}.", status);
118 
119             if let Some(cb) = self.callback.take() {
120                 cb(1, &[]);
121             }
122         }
123     }
124 
125     /** ****************************************************************************
126      **
127      ** Function         NFC_Init
128      **
129      ** Description      This function initializes control blocks for NFC
130      **
131      ** Returns          nothing
132      **
133      *******************************************************************************/
134     /// extern void NFC_Init(tHAL_NFC_ENTRY* p_hal_entry_tbl);
nfc_init(&mut self) -> Result<()>135     pub async fn nfc_init(&mut self) -> Result<()> {
136         let pbf = PacketBoundaryFlag::CompleteOrFinal;
137         if let Some(cmd) = self.commands.as_mut() {
138             let reset = cmd
139                 .send_and_notify(
140                     ResetCommandBuilder { gid: 0, pbf, reset_type: ResetType::ResetConfig }
141                         .build()
142                         .into(),
143                 )
144                 .await?;
145             let _notification_packet = reset.notification.await?;
146             let init = cmd
147                 .send(
148                     InitCommandBuilder { gid: 0, pbf, feature_enable: FeatureEnable::Rfu }
149                         .build()
150                         .into(),
151                 )
152                 .await?;
153             if let ResponseChild::InitResponse(irp) = init.specialize() {
154                 if let Some(conn) = self.connections.as_mut() {
155                     // Open static RF connection
156                     // TODO: use channels instead of callcacks here
157                     // the data can be tranlated to c-callback at the shim level
158                     conn.open(0, self.nfc_data.rf_callback, 0, 0).await;
159                     // Open static HCI connection
160                     conn.open(
161                         1, /* TODO: link constants to the c header */
162                         self.nfc_data.hci_callback,
163                         irp.get_max_data_payload(),
164                         irp.get_num_of_credits(),
165                     )
166                     .await;
167                 }
168                 self.nfc_data.init_response = Some(irp);
169             }
170         }
171         Ok(())
172     }
173 
174     /** *****************************************************************************
175      **
176      ** Function         NFC_GetLmrtSize
177      **
178      ** Description      Called by application wto query the Listen Mode Routing
179      **                  Table size supported by NFCC
180      **
181      ** Returns          Listen Mode Routing Table size
182      **
183      *******************************************************************************/
184     /// extern uint16_t NFC_GetLmrtSize(void);
nfc_get_lmrt_size(&mut self) -> u16185     pub async fn nfc_get_lmrt_size(&mut self) -> u16 {
186         if let Some(ir) = &self.nfc_data.init_response {
187             ir.get_max_rout_tbls_size()
188         } else {
189             0
190         }
191     }
192 
193     /** *****************************************************************************
194      **
195      ** Function         NFC_SetConfig
196      **
197      ** Description      This function is called to send the configuration parameter
198      **                  TLV to NFCC. The response from NFCC is reported by
199      **                  tNFC_RESPONSE_CBACK as NFC_SET_CONFIG_REVT.
200      **
201      ** Parameters       tlv_size - the length of p_param_tlvs.
202      **                  p_param_tlvs - the parameter ID/Len/Value list
203      **
204      ** Returns          tNFC_STATUS
205      **
206      *******************************************************************************/
207     /// extern tNFC_STATUS NFC_SetConfig(uint8_t tlv_size, uint8_t* p_param_tlvs);
nfc_set_config(&mut self, param_tlvs: &[u8]) -> Result<u8>208     pub async fn nfc_set_config(&mut self, param_tlvs: &[u8]) -> Result<u8> {
209         let pbf = PacketBoundaryFlag::CompleteOrFinal;
210         if let Some(cmd) = self.commands.as_mut() {
211             let raw = cmd
212                 .send(
213                     CommandBuilder {
214                         gid: 0,
215                         pbf,
216                         op: Opcode::CoreSetConfig,
217                         payload: Some(Bytes::copy_from_slice(param_tlvs)),
218                     }
219                     .build(),
220                 )
221                 .await?
222                 .encode_to_bytes()?;
223             if let Some(cb) = self.callback {
224                 cb(2, &raw[3..]);
225             }
226             Ok(raw[3])
227         } else {
228             Ok(nci::Status::NotInitialized as u8)
229         }
230     }
231 
232     /** *****************************************************************************
233      **
234      ** Function         NFC_GetConfig
235      **
236      ** Description      This function is called to retrieve the parameter TLV from
237      **                  NFCC. The response from NFCC is reported by
238      **                  tNFC_RESPONSE_CBACK as NFC_GET_CONFIG_REVT.
239      **
240      ** Parameters       num_ids - the number of parameter IDs
241      **                  p_param_ids - the parameter ID list.
242      **
243      ** Returns          tNFC_STATUS
244      **
245      *******************************************************************************/
246     /// extern tNFC_STATUS NFC_GetConfig(uint8_t num_ids, uint8_t* p_param_ids);
nfc_get_config(&mut self, param_tlvs: &[u8]) -> Result<u8>247     pub async fn nfc_get_config(&mut self, param_tlvs: &[u8]) -> Result<u8> {
248         let pbf = PacketBoundaryFlag::CompleteOrFinal;
249         if let Some(cmd) = self.commands.as_mut() {
250             let raw = cmd
251                 .send(
252                     CommandBuilder {
253                         gid: 0,
254                         pbf,
255                         op: Opcode::CoreGetConfig,
256                         payload: Some(Bytes::copy_from_slice(param_tlvs)),
257                     }
258                     .build(),
259                 )
260                 .await?
261                 .encode_to_bytes()?;
262             if let Some(cb) = self.callback {
263                 cb(3, &raw[3..]);
264             }
265             Ok(raw[3])
266         } else {
267             Ok(nci::Status::NotInitialized as u8)
268         }
269     }
270     /** ****************************************************************************
271      **
272      ** Function         NFC_ConnCreate
273      **
274      ** Description      This function is called to create a logical connection with
275      **                  NFCC for data exchange.
276      **                  The response from NFCC is reported in tNFC_CONN_CBACK
277      **                  as NFC_CONN_CREATE_CEVT.
278      **
279      ** Parameters       dest_type - the destination type
280      **                  id   - the NFCEE ID or RF Discovery ID .
281      **                  protocol - the protocol
282      **                  p_cback - the data callback function to receive data from
283      **                  NFCC
284      **
285      ** Returns          tNFC_STATUS
286      **
287      *******************************************************************************/
288     //extern tNFC_STATUS NFC_ConnCreate(uint8_t dest_type, uint8_t id,
289     //                                  uint8_t protocol, tNFC_CONN_CBACK* p_cback);
nfc_conn_create( &mut self, dest_type: u8, id: u8, protocol: u8, callback: ConnCallback, ) -> Result<u8>290     pub async fn nfc_conn_create(
291         &mut self,
292         dest_type: u8,
293         id: u8,
294         protocol: u8,
295         callback: ConnCallback,
296     ) -> Result<u8> {
297         let pbf = PacketBoundaryFlag::CompleteOrFinal;
298         let mut destparams: Vec<DestParam> = vec![];
299         let dt = DestTypes::try_from(dest_type).unwrap();
300         match dt {
301             DestTypes::NfccLpbk => (),
302             DestTypes::Remote => {
303                 let parameter = vec![id, protocol];
304                 destparams.push(DestParam { ptype: DestParamTypes::RfDisc, parameter });
305             }
306             DestTypes::Nfcee => {
307                 let parameter: Vec<u8> = vec![id, protocol];
308                 destparams.push(DestParam { ptype: DestParamTypes::Nfcee, parameter });
309             }
310             _ => return Ok(nci::Status::InvalidParam as u8),
311         }
312         if let Some(cmd) = self.commands.as_mut() {
313             let rp = cmd
314                 .send(ConnCreateCommandBuilder { gid: 0, pbf, dt, destparams }.build().into())
315                 .await?;
316             if let ResponseChild::ConnCreateResponse(ccrp) = rp.specialize() {
317                 let status = ccrp.get_status();
318                 if status == nci::Status::Ok {
319                     if let Some(conn) = self.connections.as_mut() {
320                         conn.open(
321                             ccrp.get_conn_id(),
322                             Some(callback),
323                             ccrp.get_mpps(),
324                             ccrp.get_ncreds(),
325                         )
326                         .await;
327                         let conn_create_evt =
328                             [status as u8, dest_type, id, ccrp.get_mpps(), ccrp.get_ncreds()];
329                         callback(ccrp.get_conn_id(), 0, &conn_create_evt[..]);
330                     } else {
331                         return Ok(nci::Status::NotInitialized as u8);
332                     }
333                 }
334                 Ok(status as u8)
335             } else {
336                 Ok(nci::Status::Failed as u8)
337             }
338         } else {
339             Ok(nci::Status::NotInitialized as u8)
340         }
341     }
342 
343     /** ****************************************************************************
344      **
345      ** Function         NFC_ConnClose
346      **
347      ** Description      This function is called to close a logical connection with
348      **                  NFCC.
349      **                  The response from NFCC is reported in tNFC_CONN_CBACK
350      **                  as NFC_CONN_CLOSE_CEVT.
351      **
352      ** Parameters       conn_id - the connection id.
353      **
354      ** Returns          tNFC_STATUS
355      **
356      *******************************************************************************/
357     //extern tNFC_STATUS NFC_ConnClose(uint8_t conn_id);
nfc_conn_close(&mut self, conn_id: u8) -> Result<u8>358     pub async fn nfc_conn_close(&mut self, conn_id: u8) -> Result<u8> {
359         let pbf = PacketBoundaryFlag::CompleteOrFinal;
360         if let Some(conn) = self.connections.as_mut() {
361             if let Some(cb) = conn.close(conn_id).await {
362                 if let Some(cmd) = self.commands.as_mut() {
363                     let rp = cmd
364                         .send(ConnCloseCommandBuilder { gid: 0, pbf, conn_id }.build().into())
365                         .await?;
366                     if let ResponseChild::ConnCloseResponse(ccrp) = rp.specialize() {
367                         let status = ccrp.get_status() as u8;
368                         let conn_close_evt = [status];
369                         cb(conn_id, 1, &conn_close_evt[..]);
370                         return Ok(status);
371                     } else {
372                         return Ok(nci::Status::Failed as u8);
373                     }
374                 }
375             } else {
376                 return Ok(nci::Status::InvalidParam as u8);
377             }
378         }
379         Ok(nci::Status::NotInitialized as u8)
380     }
381 
382     /** *****************************************************************************
383      **
384      ** Function         NFC_SetStaticRfCback
385      **
386      ** Description      This function is called to update the data callback function
387      **                  to receive the data for the given connection id.
388      **
389      ** Parameters       p_cback - the connection callback function
390      **
391      ** Returns          Nothing
392      **
393      *******************************************************************************/
394     //extern void NFC_SetStaticRfCback(tNFC_CONN_CBACK* p_cback);
nfc_set_static_rf_callback(&mut self, callback: ConnCallback)395     pub async fn nfc_set_static_rf_callback(&mut self, callback: ConnCallback) {
396         self.nfc_data.rf_callback = Some(callback);
397         if let Some(conn) = self.connections.as_mut() {
398             conn.set_static_callback(0, Some(callback)).await;
399         }
400     }
401 
402     /** *****************************************************************************
403      **
404      ** Function         NFC_SetStaticHciCback
405      **
406      ** Description      This function to update the data callback function
407      **                  to receive the data for the static Hci connection id.
408      **
409      ** Parameters       p_cback - the connection callback function
410      **
411      ** Returns          Nothing
412      **
413      *******************************************************************************/
414     //extern void NFC_SetStaticHciCback(tNFC_CONN_CBACK* p_cback);
nfc_set_static_hci_callback(&mut self, callback: ConnCallback)415     pub async fn nfc_set_static_hci_callback(&mut self, callback: ConnCallback) {
416         self.nfc_data.hci_callback = Some(callback);
417         if let Some(conn) = self.connections.as_mut() {
418             conn.set_static_callback(1, Some(callback)).await;
419         }
420     }
421 
422     /*******************************************************************************
423      **
424      ** Function         NFC_SetReassemblyFlag
425      **
426      ** Description      This function is called to set if nfc will reassemble
427      **                  nci packet as much as its buffer can hold or it should not
428      **                  reassemble but forward the fragmented nci packet to layer
429      **                  above. If nci data pkt is fragmented, nfc may send multiple
430      **                  NFC_DATA_CEVT with status NFC_STATUS_CONTINUE before sending
431      **                  NFC_DATA_CEVT with status NFC_STATUS_OK based on reassembly
432      **                  configuration and reassembly buffer size
433      **
434      ** Parameters       reassembly - flag to indicate if nfc may reassemble or not
435      **
436      ** Returns          Nothing
437      **
438      *******************************************************************************/
439     //extern void NFC_SetReassemblyFlag(bool reassembly);
440 
441     /** ****************************************************************************
442      **
443      ** Function         NFC_SendData
444      **
445      ** Description      This function is called to send the given data packet
446      **                  to the connection identified by the given connection id.
447      **
448      ** Parameters       conn_id - the connection id.
449      **                  p_data - the data packet
450      **
451      ** Returns          tNFC_STATUS
452      **
453      *******************************************************************************/
454     //extern tNFC_STATUS NFC_SendData(uint8_t conn_id, NFC_HDR* p_data);
nfc_send_data(&mut self, conn_id: u8, data: &[u8]) -> Result<u8>455     pub async fn nfc_send_data(&mut self, conn_id: u8, data: &[u8]) -> Result<u8> {
456         if let Some(conn) = self.connections.as_mut() {
457             match DataPacket::parse(data) {
458                 Ok(pkt) => {
459                     conn.send_packet(conn_id, pkt).await;
460                     return Ok(nci::Status::Ok as u8);
461                 }
462                 Err(e) => {
463                     error!("Data packet is invalid:{:?}", e);
464                     return Ok(nci::Status::InvalidParam as u8);
465                 }
466             }
467         }
468         Ok(nci::Status::NotInitialized as u8)
469     }
470 
471     /** ****************************************************************************
472      **
473      ** Function         NFC_FlushData
474      **
475      ** Description      This function is called to discard the tx data queue of
476      **                  the given connection id.
477      **
478      ** Parameters       conn_id - the connection id.
479      **
480      ** Returns          tNFC_STATUS
481      **
482      *******************************************************************************/
483     //extern tNFC_STATUS NFC_FlushData(uint8_t conn_id);
nfc_flush_data(&mut self, conn_id: u8) -> Result<u8>484     pub async fn nfc_flush_data(&mut self, conn_id: u8) -> Result<u8> {
485         if let Some(conn) = self.connections.as_mut() {
486             if conn.flush_data(conn_id).await {
487                 Ok(nci::Status::Ok as u8)
488             } else {
489                 Ok(nci::Status::Failed as u8)
490             }
491         } else {
492             Ok(nci::Status::NotInitialized as u8)
493         }
494     }
495 
496     /** ****************************************************************************
497      **
498      ** Function         NFC_DiscoveryMap
499      **
500      ** Description      This function is called to set the discovery interface
501      **                  mapping. The response from NFCC is reported by
502      **                  tNFC_DISCOVER_CBACK as. NFC_MAP_DEVT.
503      **
504      ** Parameters       num - the number of items in p_params.
505      **                  p_maps - the discovery interface mappings
506      **                  p_cback - the discovery callback function
507      **
508      ** Returns          tNFC_STATUS
509      **
510      *******************************************************************************/
511     // extern tNFC_STATUS NFC_DiscoveryMap(uint8_t num, tNFC_DISCOVER_MAPS* p_maps,
512     //                                    tNFC_DISCOVER_CBACK* p_cback);
nfc_discovery_map(&mut self, _maps: Vec<RfMappingConfiguration>) -> Result<u8>513     pub async fn nfc_discovery_map(&mut self, _maps: Vec<RfMappingConfiguration>) -> Result<u8> {
514         Ok(0)
515     }
516 
517     /*******************************************************************************
518      **
519      ** Function         NFC_DiscoveryStart
520      **
521      ** Description      This function is called to start Polling and/or Listening.
522      **                  The response from NFCC is reported by tNFC_DISCOVER_CBACK
523      **                  as NFC_START_DEVT. The notification from NFCC is reported by
524      **                  tNFC_DISCOVER_CBACK as NFC_RESULT_DEVT.
525      **
526      ** Parameters       num_params - the number of items in p_params.
527      **                  p_params - the discovery parameters
528      **                  p_cback - the discovery callback function
529      **
530      ** Returns          tNFC_STATUS
531      **
532      *******************************************************************************/
533     // extern tNFC_STATUS NFC_DiscoveryStart(uint8_t num_params,
534     //                                       tNFC_DISCOVER_PARAMS* p_params,
535     //                                       tNFC_DISCOVER_CBACK* p_cback);
536 
537     /*******************************************************************************
538      **
539      ** Function         NFC_DiscoverySelect
540      **
541      ** Description      If tNFC_DISCOVER_CBACK reports status=NFC_MULTIPLE_PROT,
542      **                  the application needs to use this function to select the
543      **                  the logical endpoint to continue. The response from NFCC is
544      **                  reported by tNFC_DISCOVER_CBACK as NFC_SELECT_DEVT.
545      **
546      ** Parameters       rf_disc_id - The ID identifies the remote device.
547      **                  protocol - the logical endpoint on the remote device
548      **                  rf_interface - the RF interface to communicate with NFCC
549      **
550      ** Returns          tNFC_STATUS
551      **
552      *******************************************************************************/
553     // extern tNFC_STATUS NFC_DiscoverySelect(uint8_t rf_disc_id, uint8_t protocol,
554     //                                        uint8_t rf_interface);
555 }
556 
557 impl Default for NciApi {
default() -> Self558     fn default() -> Self {
559         Self::new()
560     }
561 }
562