Merge "API for libnfc_nci is added"
diff --git a/src/nci_packets.pdl b/src/nci_packets.pdl
index f94a35d..6fea6e0 100644
--- a/src/nci_packets.pdl
+++ b/src/nci_packets.pdl
@@ -230,7 +230,7 @@
}
packet InitCommand : Command (op = CORE_INIT) {
- feature_eneble : FeatureEnable,
+ feature_enable : FeatureEnable,
}
test InitCommand {
diff --git a/src/rust/hal/hal.rs b/src/rust/hal/hal.rs
index a1689b3..fd4ae32 100644
--- a/src/rust/hal/hal.rs
+++ b/src/rust/hal/hal.rs
@@ -1,10 +1,27 @@
+// 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 Hardware Abstraction Layer
//! Supports sending NCI commands to the HAL and receiving
//! NCI events from the HAL
use nfc_packets::nci::{DataPacket, NciPacket};
+use std::collections::HashMap;
+use std::sync::Arc;
use thiserror::Error;
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
+use tokio::sync::{oneshot, Mutex};
#[cfg(target_os = "android")]
#[path = "hidl_hal.rs"]
@@ -16,6 +33,8 @@
/// HAL module interface
pub struct Hal {
+ /// HAL events
+ pub hal_events: HalEventRegistry,
/// HAL outbound channel for Command messages
pub out_cmd_tx: UnboundedSender<NciPacket>,
/// HAL inbound channel for Response and Notification messages
@@ -31,10 +50,57 @@
ihal::init().await
}
+/// NFC HAL specific events
+#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
+pub enum HalEvent {
+ /// HAL CLOSE_CPLT event
+ CloseComplete,
+}
+
+/// Status of a NFC HAL event
+#[derive(Debug)]
+pub enum HalEventStatus {
+ /// HAL OK status
+ Success,
+ /// HAL FAILED status
+ Failed,
+ /// HAL ERR_TRANSPORT status
+ TransportError,
+ /// HAL ERR_CMD_TIMEOUT status
+ Timeout,
+ /// HAL REFUSED status
+ Refused,
+}
+
+/// Provides ability to register and unregister for HAL event notifications
+#[derive(Clone)]
+pub struct HalEventRegistry {
+ handlers: Arc<Mutex<HashMap<HalEvent, oneshot::Sender<HalEventStatus>>>>,
+}
+
+impl HalEventRegistry {
+ /// Indicate interest in specific HAL event
+ pub async fn register(&mut self, event: HalEvent, sender: oneshot::Sender<HalEventStatus>) {
+ assert!(
+ self.handlers.lock().await.insert(event, sender).is_none(),
+ "A handler for {:?} is already registered",
+ event
+ );
+ }
+
+ /// Remove interest in specific HAL event
+ pub async fn unregister(&mut self, event: HalEvent) -> Option<oneshot::Sender<HalEventStatus>> {
+ self.handlers.lock().await.remove(&event)
+ }
+}
+
mod internal {
- use crate::Hal;
+ use crate::{Hal, HalEventRegistry};
use nfc_packets::nci::{DataPacket, NciPacket};
+ use std::collections::HashMap;
+ use std::sync::Arc;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
+ use tokio::sync::Mutex;
pub struct InnerHal {
pub out_cmd_rx: UnboundedReceiver<NciPacket>,
@@ -49,8 +115,10 @@
let (in_cmd_tx, in_cmd_rx) = unbounded_channel();
let (out_data_tx, out_data_rx) = unbounded_channel();
let (in_data_tx, in_data_rx) = unbounded_channel();
+ let handlers = Arc::new(Mutex::new(HashMap::new()));
+ let hal_events = HalEventRegistry { handlers };
(
- Hal { out_cmd_tx, in_cmd_rx, out_data_tx, in_data_rx },
+ Hal { hal_events, out_cmd_tx, in_cmd_rx, out_data_tx, in_data_rx },
Self { out_cmd_rx, in_cmd_tx, out_data_rx, in_data_tx },
)
}
diff --git a/src/rust/hal/hidl_hal.rs b/src/rust/hal/hidl_hal.rs
index feb3913..91e67ef 100644
--- a/src/rust/hal/hidl_hal.rs
+++ b/src/rust/hal/hidl_hal.rs
@@ -1,27 +1,49 @@
+// 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.
+
//! Implementation of the HAl that talks to NFC controller over Android's HIDL
use crate::internal::InnerHal;
#[allow(unused)]
-use crate::{is_control_packet, Hal, Result};
+use crate::{is_control_packet, Hal, HalEvent, HalEventRegistry, HalEventStatus, Result};
use lazy_static::lazy_static;
-use log::error;
+use log::{debug, error};
use nfc_packets::nci::{DataPacket, NciPacket, Packet};
use std::sync::Mutex;
use tokio::select;
-use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
+use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
+use tokio::sync::oneshot;
/// Initialize the module
pub async fn init() -> Hal {
let (raw_hal, inner_hal) = InnerHal::new();
- let (hal_open_evt_tx, mut hal_open_evt_rx) = unbounded_channel();
+ let (hal_open_evt_tx, hal_open_evt_rx) = oneshot::channel::<ffi::NfcStatus>();
+ let (hal_close_evt_tx, hal_close_evt_rx) = oneshot::channel::<ffi::NfcStatus>();
*CALLBACKS.lock().unwrap() = Some(Callbacks {
- hal_open_evt_tx,
+ hal_open_evt_tx: Some(hal_open_evt_tx),
+ hal_close_evt_tx: Some(hal_close_evt_tx),
in_cmd_tx: inner_hal.in_cmd_tx,
in_data_tx: inner_hal.in_data_tx,
});
ffi::start_hal();
- hal_open_evt_rx.recv().await.unwrap();
+ hal_open_evt_rx.await.unwrap();
- tokio::spawn(dispatch_outgoing(inner_hal.out_cmd_rx, inner_hal.out_data_rx));
+ tokio::spawn(dispatch_outgoing(
+ raw_hal.hal_events.clone(),
+ inner_hal.out_cmd_rx,
+ inner_hal.out_data_rx,
+ hal_close_evt_rx,
+ ));
raw_hal
}
@@ -73,8 +95,22 @@
}
}
+impl From<ffi::NfcStatus> for HalEventStatus {
+ fn from(ffi_nfc_status: ffi::NfcStatus) -> Self {
+ match ffi_nfc_status {
+ ffi::NfcStatus::OK => HalEventStatus::Success,
+ ffi::NfcStatus::FAILED => HalEventStatus::Failed,
+ ffi::NfcStatus::ERR_TRANSPORT => HalEventStatus::TransportError,
+ ffi::NfcStatus::ERR_CMD_TIMEOUT => HalEventStatus::Timeout,
+ ffi::NfcStatus::REFUSED => HalEventStatus::Refused,
+ _ => HalEventStatus::Failed,
+ }
+ }
+}
+
struct Callbacks {
- hal_open_evt_tx: UnboundedSender<()>,
+ hal_open_evt_tx: Option<oneshot::Sender<ffi::NfcStatus>>,
+ hal_close_evt_tx: Option<oneshot::Sender<ffi::NfcStatus>>,
in_cmd_tx: UnboundedSender<NciPacket>,
in_data_tx: UnboundedSender<DataPacket>,
}
@@ -84,15 +120,25 @@
}
fn on_event(evt: ffi::NfcEvent, status: ffi::NfcStatus) {
- error!("got event: {:?} with status {:?}", evt, status);
- let callbacks = CALLBACKS.lock().unwrap();
- if evt == ffi::NfcEvent::OPEN_CPLT {
- callbacks.as_ref().unwrap().hal_open_evt_tx.send(()).unwrap();
+ debug!("got event: {:?} with status {:?}", evt, status);
+ let mut callbacks = CALLBACKS.lock().unwrap();
+ match evt {
+ ffi::NfcEvent::OPEN_CPLT => {
+ if let Some(evt_tx) = callbacks.as_mut().unwrap().hal_open_evt_tx.take() {
+ evt_tx.send(status).unwrap();
+ }
+ }
+ ffi::NfcEvent::CLOSE_CPLT => {
+ if let Some(evt_tx) = callbacks.as_mut().unwrap().hal_close_evt_tx.take() {
+ evt_tx.send(status).unwrap();
+ }
+ }
+ _ => error!("Unhandled HAL event {:?}", evt),
}
}
fn on_data(data: &[u8]) {
- error!("got packet: {:02x?}", data);
+ debug!("got packet: {:02x?}", data);
let callbacks = CALLBACKS.lock().unwrap();
if is_control_packet(data) {
match NciPacket::parse(data) {
@@ -108,8 +154,10 @@
}
async fn dispatch_outgoing(
+ mut hal_events: HalEventRegistry,
mut out_cmd_rx: UnboundedReceiver<NciPacket>,
mut out_data_rx: UnboundedReceiver<DataPacket>,
+ hal_close_evt_rx: oneshot::Receiver<ffi::NfcStatus>,
) {
loop {
select! {
@@ -118,4 +166,9 @@
else => break,
}
}
+ ffi::stop_hal();
+ let status = hal_close_evt_rx.await.unwrap();
+ if let Some(evt) = hal_events.unregister(HalEvent::CloseComplete).await {
+ evt.send(HalEventStatus::from(status)).unwrap();
+ }
}
diff --git a/src/rust/hal/rootcanal_hal.rs b/src/rust/hal/rootcanal_hal.rs
index ec4abbf..b52154d 100644
--- a/src/rust/hal/rootcanal_hal.rs
+++ b/src/rust/hal/rootcanal_hal.rs
@@ -1,9 +1,23 @@
+// 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.
+
//! Rootcanal HAL
//! This connects to "rootcanal" which provides a simulated
//! Nfc chip as well as a simulated environment.
use crate::internal::InnerHal;
-use crate::{is_control_packet, Hal, Result};
+use crate::{is_control_packet, Hal, HalEvent, HalEventRegistry, HalEventStatus, Result};
use bytes::{BufMut, BytesMut};
use log::{debug, error};
use nfc_packets::nci::{DataPacket, NciPacket, Packet};
@@ -23,7 +37,12 @@
let reader = BufReader::new(reader);
tokio::spawn(dispatch_incoming(inner_hal.in_cmd_tx, inner_hal.in_data_tx, reader));
- tokio::spawn(dispatch_outgoing(inner_hal.out_cmd_rx, inner_hal.out_data_rx, writer));
+ tokio::spawn(dispatch_outgoing(
+ raw_hal.hal_events.clone(),
+ inner_hal.out_cmd_rx,
+ inner_hal.out_data_rx,
+ writer,
+ ));
raw_hal
}
@@ -46,20 +65,31 @@
debug!("{:?}", &frozen);
if is_control_packet(&frozen[..]) {
match NciPacket::parse(&frozen) {
- Ok(p) => in_cmd_tx.send(p)?,
+ Ok(p) => {
+ if in_cmd_tx.send(p).is_err() {
+ break;
+ }
+ }
Err(e) => error!("dropping invalid cmd event packet: {}: {:02x}", e, frozen),
}
} else {
match DataPacket::parse(&frozen) {
- Ok(p) => in_data_tx.send(p)?,
+ Ok(p) => {
+ if in_data_tx.send(p).is_err() {
+ break;
+ }
+ }
Err(e) => error!("dropping invalid data event packet: {}: {:02x}", e, frozen),
}
}
}
+ debug!("Dispatch incoming finished.");
+ Ok(())
}
/// Send commands received from the NCI later to rootcanal
async fn dispatch_outgoing<W>(
+ mut hal_events: HalEventRegistry,
mut out_cmd_rx: UnboundedReceiver<NciPacket>,
mut out_data_rx: UnboundedReceiver<DataPacket>,
mut writer: W,
@@ -75,6 +105,11 @@
}
}
+ writer.shutdown().await?;
+ if let Some(evt) = hal_events.unregister(HalEvent::CloseComplete).await {
+ evt.send(HalEventStatus::Success).unwrap();
+ }
+ debug!("Dispatch outgoing finished.");
Ok(())
}
diff --git a/src/rust/nci/api.rs b/src/rust/nci/api.rs
new file mode 100644
index 0000000..2bd4ae6
--- /dev/null
+++ b/src/rust/nci/api.rs
@@ -0,0 +1,137 @@
+// 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, Result};
+use log::debug;
+use nfc_hal::{HalEvent, HalEventRegistry, HalEventStatus};
+use nfc_packets::nci::{FeatureEnable, PacketBoundaryFlag, ResetType};
+use nfc_packets::nci::{InitCommandBuilder, ResetCommandBuilder};
+use tokio::sync::oneshot;
+
+/// NCI API object to manage static API data
+pub struct NciApi {
+ /// Command Sender external interface
+ commands: Option<CommandSender>,
+ /// The NFC response callback
+ callback: Option<fn(u16, &[u8])>,
+ /// HalEventRegistry is used to register for HAL events
+ hal_events: Option<HalEventRegistry>,
+}
+
+impl NciApi {
+ /// NciApi constructor
+ pub fn new() -> NciApi {
+ NciApi { commands: None, callback: None, hal_events: None }
+ }
+
+ /** ****************************************************************************
+ **
+ ** 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: fn(u16, &[u8])) {
+ let nci = crate::init().await;
+
+ self.commands = Some(nci.commands);
+ 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 hr) = self.hal_events.take() {
+ hr.register(HalEvent::CloseComplete, tx).await;
+
+ if let Some(cmd) = self.commands.take() {
+ drop(cmd);
+ }
+ 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?;
+ }
+ Ok(())
+ }
+}
+
+impl Default for NciApi {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/src/rust/nci/nci.rs b/src/rust/nci/nci.rs
index 47685e4..d0743f1 100644
--- a/src/rust/nci/nci.rs
+++ b/src/rust/nci/nci.rs
@@ -1,9 +1,23 @@
+// 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 Protocol Abstraction Layer
//! Supports sending NCI commands to the HAL and receiving
//! NCI messages back
-use log::error;
-use nfc_hal::Hal;
+use log::{debug, error};
+use nfc_hal::{Hal, HalEventRegistry};
use nfc_packets::nci::NciChild::{Notification, Response};
use nfc_packets::nci::{CommandPacket, DataPacket, NotificationPacket, Opcode, ResponsePacket};
use std::collections::HashMap;
@@ -13,6 +27,8 @@
use tokio::sync::{oneshot, Mutex};
use tokio::time::{sleep, Duration, Instant};
+pub mod api;
+
/// Result type
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
@@ -28,15 +44,18 @@
let (cmd_tx, cmd_rx) = channel::<QueuedCommand>(10);
let commands = CommandSender { cmd_tx };
+ let hal_events = hc.hal_events.clone();
let notifications = EventRegistry { handlers: Arc::new(Mutex::new(HashMap::new())) };
tokio::spawn(dispatch(notifications, hc, ic, cmd_rx));
- Nci { commands, out_data_ext, in_data_ext }
+ Nci { hal_events, commands, out_data_ext, in_data_ext }
}
/// NCI module external interface
pub struct Nci {
+ /// HAL events
+ pub hal_events: HalEventRegistry,
/// NCI command communication interface
pub commands: CommandSender,
/// NCI outbound channel for Data messages
@@ -63,7 +82,7 @@
}
/// Sends raw commands. Only useful for facades & shims, or wrapped as a CommandSender.
-#[derive(Clone)]
+// #[derive(Clone)]
pub struct CommandSender {
cmd_tx: Sender<QueuedCommand>,
}
@@ -107,6 +126,12 @@
}
}
+impl Drop for CommandSender {
+ fn drop(&mut self) {
+ debug!("CommandSender is dropped");
+ }
+}
+
/// Provides ability to register and unregister for NCI notifications
#[derive(Clone)]
pub struct EventRegistry {
@@ -140,6 +165,8 @@
) -> Result<()> {
let mut pending: Option<PendingCommand> = None;
let timeout = sleep(Duration::MAX);
+ // The max_deadline is used to set the sleep() deadline to a very distant moment in
+ // the future, when the notification from the timer is not required.
let max_deadline = timeout.deadline();
tokio::pin!(timeout);
loop {
@@ -172,8 +199,9 @@
}
_ => error!("Unexpected NCI data received {:?}", cmd),
}
- }
- Some(queued) = cmd_rx.recv(), if pending.is_none() => {
+ },
+ qc = cmd_rx.recv(), if pending.is_none() => if let Some(queued) = qc {
+ debug!("cmd_rx got a q");
if let Some(nsender) = queued.notification {
ntfs.register(queued.pending.cmd.get_op(), nsender).await;
}
@@ -182,16 +210,22 @@
}
timeout.as_mut().reset(Instant::now() + Duration::from_millis(20));
pending = Some(queued.pending);
+ } else {
+ break;
},
() = &mut timeout => {
error!("Command processing timeout");
timeout.as_mut().reset(max_deadline);
pending = None;
- }
- Some(data) = hc.in_data_rx.recv() => ic.in_data_int.send(data).await?,
- Some(data) = ic.out_data_int.recv() => hc.out_data_tx.send(data)?,
- else => break,
+ },
+ Some(data) = hc.in_data_rx.recv() => ic.in_data_int.send(data).await.unwrap(),
+ Some(data) = ic.out_data_int.recv() => hc.out_data_tx.send(data).unwrap(),
+ else => {
+ debug!("Select is done");
+ break;
+ },
}
}
+ debug!("NCI dispatch is terminated.");
Ok(())
}
diff --git a/src/rust/rootcanal/main.rs b/src/rust/rootcanal/main.rs
index c023a11..e7d20a4 100644
--- a/src/rust/rootcanal/main.rs
+++ b/src/rust/rootcanal/main.rs
@@ -1,3 +1,17 @@
+// 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.
+
//! This connects to "rootcanal" and provides a simulated
//! Nfc chip as well as a simulated environment.
@@ -15,7 +29,7 @@
use std::convert::TryInto;
use thiserror::Error;
use tokio::io;
-use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
+use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader, ErrorKind};
use tokio::net::TcpListener;
/// Result type
@@ -43,21 +57,28 @@
let listener = TcpListener::bind("127.0.0.1:54323").await?;
- let (mut sock, _) = listener.accept().await?;
+ for _ in 0..2 {
+ let (mut sock, _) = listener.accept().await?;
- tokio::spawn(async move {
- let (rd, mut wr) = sock.split();
- let mut rd = BufReader::new(rd);
- loop {
- if let Err(e) = process(&mut rd, &mut wr).await {
- match e {
- RootcanalError::TerminateTask => break,
- _ => panic!("Communication error: {:?}", e),
+ tokio::spawn(async move {
+ let (rd, mut wr) = sock.split();
+ let mut rd = BufReader::new(rd);
+ loop {
+ if let Err(e) = process(&mut rd, &mut wr).await {
+ match e {
+ RootcanalError::TerminateTask => break,
+ RootcanalError::IoError(e) => {
+ if e.kind() == ErrorKind::UnexpectedEof {
+ break;
+ }
+ }
+ _ => panic!("Communication error: {:?}", e),
+ }
}
}
- }
- })
- .await?;
+ })
+ .await?;
+ }
Ok(())
}
diff --git a/src/rust/test/main.rs b/src/rust/test/main.rs
index aa66dc4..aa01629 100644
--- a/src/rust/test/main.rs
+++ b/src/rust/test/main.rs
@@ -1,44 +1,43 @@
+// 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.
+
//! Rootcanal HAL
//! This connects to "rootcanal" which provides a simulated
//! Nfc chip as well as a simulated environment.
-use log::{debug, error, Level};
+use log::{debug, Level};
use logger::{self, Config};
-use nfc_packets::nci::CommandPacket;
-use nfc_packets::nci::Opcode::{self, CoreInit, CoreReset};
-use nfc_packets::nci::{FeatureEnable, PacketBoundaryFlag, ResetType};
-use nfc_packets::nci::{InitCommandBuilder, ResetCommandBuilder};
+use nfc_rnci::api::NciApi;
/// Result type
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
+/// The NFC response callback
+pub fn nfc_callback(kind: u16, _val: &[u8]) {
+ debug!("Callback {} received", kind);
+}
+
#[tokio::main]
async fn main() -> Result<()> {
logger::init(Config::default().with_tag_on_device("lnfc").with_min_level(Level::Trace));
- let mut nci = nfc_rnci::init().await;
- let reset = nci.commands.send_and_notify(build_cmd(CoreReset).unwrap()).await?;
- let init = nci.commands.send(build_cmd(CoreInit).unwrap()).await?;
- let reset_response_packet = reset.response.specialize();
- debug!("Received {:?}", reset_response_packet);
- let init_response_packet = init.specialize();
- debug!("Received {:?}", init_response_packet);
- let notification_packet = reset.notification.await?;
- debug!("Received {:?}", notification_packet.specialize());
- Ok(())
-}
-fn build_cmd(cmd_op_code: Opcode) -> Option<CommandPacket> {
- let pbf = PacketBoundaryFlag::CompleteOrFinal;
- match cmd_op_code {
- CoreReset => Some(
- ResetCommandBuilder { gid: 0, pbf, reset_type: ResetType::ResetConfig }.build().into(),
- ),
- CoreInit => Some(
- InitCommandBuilder { gid: 0, pbf, feature_eneble: FeatureEnable::Rfu }.build().into(),
- ),
- _ => {
- error!("Unsupported command: {}", cmd_op_code);
- None
- }
- }
+ let mut nci = NciApi::new();
+ nci.nfc_enable(nfc_callback).await;
+ nci.nfc_init().await?;
+ nci.nfc_disable().await;
+ nci.nfc_enable(nfc_callback).await;
+ nci.nfc_init().await?;
+ nci.nfc_disable().await;
+ Ok(())
}