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 Protocol Abstraction Layer
16 //! Supports sending NCI commands to the HAL and receiving
17 //! NCI messages back
18
19 use log::{debug, error};
20 use nfc_hal::{Hal, HalEventRegistry};
21 use nfc_packets::nci::NciChild::{Notification, Response};
22 use nfc_packets::nci::{CommandPacket, DataPacket, NotificationPacket, Opcode, ResponsePacket};
23 use std::collections::HashMap;
24 use std::sync::Arc;
25 use tokio::select;
26 use tokio::sync::mpsc::{channel, Receiver, Sender};
27 use tokio::sync::{oneshot, Mutex};
28 use tokio::time::{sleep, Duration, Instant};
29
30 pub mod api;
31
32 /// Result type
33 type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
34
35 /// Initialize the module and connect the channels
init() -> Nci36 pub async fn init() -> Nci {
37 let hc = nfc_hal::init().await;
38 // Channel to handle data downstream messages
39 let (out_data_ext, out_data_int) = channel::<DataPacket>(10);
40 // Channel to handle data upstream messages
41 let (in_data_int, in_data_ext) = channel::<DataPacket>(10);
42 // Internal data channels
43 let ic = InternalChannels { out_data_int, in_data_int };
44
45 let (cmd_tx, cmd_rx) = channel::<QueuedCommand>(10);
46 let commands = CommandSender { cmd_tx };
47 let hal_events = hc.hal_events.clone();
48
49 let notifications = EventRegistry { handlers: Arc::new(Mutex::new(HashMap::new())) };
50
51 tokio::spawn(dispatch(notifications, hc, ic, cmd_rx));
52 Nci { hal_events, commands, out_data_ext, in_data_ext }
53 }
54
55 /// NCI module external interface
56 pub struct Nci {
57 /// HAL events
58 pub hal_events: HalEventRegistry,
59 /// NCI command communication interface
60 pub commands: CommandSender,
61 /// NCI outbound channel for Data messages
62 pub out_data_ext: Sender<DataPacket>,
63 /// NCI inbound channel for Data messages
64 pub in_data_ext: Receiver<DataPacket>,
65 }
66
67 struct InternalChannels {
68 out_data_int: Receiver<DataPacket>,
69 in_data_int: Sender<DataPacket>,
70 }
71
72 #[derive(Debug)]
73 struct PendingCommand {
74 cmd: CommandPacket,
75 response: oneshot::Sender<ResponsePacket>,
76 }
77
78 #[derive(Debug)]
79 struct QueuedCommand {
80 pending: PendingCommand,
81 notification: Option<oneshot::Sender<NotificationPacket>>,
82 }
83
84 /// Sends raw commands. Only useful for facades & shims, or wrapped as a CommandSender.
85 // #[derive(Clone)]
86 pub struct CommandSender {
87 cmd_tx: Sender<QueuedCommand>,
88 }
89
90 /// The data returned by send_notify() method.
91 pub struct ResponsePendingNotification {
92 /// Command response
93 pub response: ResponsePacket,
94 /// Pending notification receiver
95 pub notification: oneshot::Receiver<NotificationPacket>,
96 }
97
98 impl CommandSender {
99 /// Send a command, but do not expect notification to be returned
send(&mut self, cmd: CommandPacket) -> Result<ResponsePacket>100 pub async fn send(&mut self, cmd: CommandPacket) -> Result<ResponsePacket> {
101 let (tx, rx) = oneshot::channel::<ResponsePacket>();
102 self.cmd_tx
103 .send(QueuedCommand {
104 pending: PendingCommand { cmd, response: tx },
105 notification: None,
106 })
107 .await?;
108 let event = rx.await?;
109 Ok(event)
110 }
111 /// Send a command which expects notification as a result
send_and_notify( &mut self, cmd: CommandPacket, ) -> Result<ResponsePendingNotification>112 pub async fn send_and_notify(
113 &mut self,
114 cmd: CommandPacket,
115 ) -> Result<ResponsePendingNotification> {
116 let (tx, rx) = oneshot::channel::<ResponsePacket>();
117 let (ntx, nrx) = oneshot::channel::<NotificationPacket>();
118 self.cmd_tx
119 .send(QueuedCommand {
120 pending: PendingCommand { cmd, response: tx },
121 notification: Some(ntx),
122 })
123 .await?;
124 let event = rx.await?;
125 Ok(ResponsePendingNotification { response: event, notification: nrx })
126 }
127 }
128
129 impl Drop for CommandSender {
drop(&mut self)130 fn drop(&mut self) {
131 debug!("CommandSender is dropped");
132 }
133 }
134
135 /// Provides ability to register and unregister for NCI notifications
136 #[derive(Clone)]
137 pub struct EventRegistry {
138 handlers: Arc<Mutex<HashMap<Opcode, oneshot::Sender<NotificationPacket>>>>,
139 }
140
141 impl EventRegistry {
142 /// Indicate interest in specific NCI notification
register(&mut self, code: Opcode, sender: oneshot::Sender<NotificationPacket>)143 pub async fn register(&mut self, code: Opcode, sender: oneshot::Sender<NotificationPacket>) {
144 assert!(
145 self.handlers.lock().await.insert(code, sender).is_none(),
146 "A handler for {:?} is already registered",
147 code
148 );
149 }
150
151 /// Remove interest in specific NCI notification
unregister( &mut self, code: Opcode, ) -> Option<oneshot::Sender<NotificationPacket>>152 pub async fn unregister(
153 &mut self,
154 code: Opcode,
155 ) -> Option<oneshot::Sender<NotificationPacket>> {
156 self.handlers.lock().await.remove(&code)
157 }
158 }
159
dispatch( mut ntfs: EventRegistry, mut hc: Hal, mut ic: InternalChannels, mut cmd_rx: Receiver<QueuedCommand>, ) -> Result<()>160 async fn dispatch(
161 mut ntfs: EventRegistry,
162 mut hc: Hal,
163 mut ic: InternalChannels,
164 mut cmd_rx: Receiver<QueuedCommand>,
165 ) -> Result<()> {
166 let mut pending: Option<PendingCommand> = None;
167 let timeout = sleep(Duration::MAX);
168 // The max_deadline is used to set the sleep() deadline to a very distant moment in
169 // the future, when the notification from the timer is not required.
170 let max_deadline = timeout.deadline();
171 tokio::pin!(timeout);
172 loop {
173 select! {
174 Some(cmd) = hc.in_cmd_rx.recv() => {
175 match cmd.specialize() {
176 Response(rsp) => {
177 timeout.as_mut().reset(max_deadline);
178 let this_opcode = rsp.get_cmd_op();
179 match pending.take() {
180 Some(PendingCommand{cmd, response}) if cmd.get_op() == this_opcode => {
181 if let Err(e) = response.send(rsp) {
182 error!("failure dispatching command status {:?}", e);
183 }
184 },
185 Some(PendingCommand{cmd, ..}) => panic!("Waiting for {}, got {}", cmd.get_op(), this_opcode),
186 None => panic!("Unexpected status event with opcode {}", this_opcode),
187 }
188 }
189 Notification(ntfy) => {
190 let code = ntfy.get_cmd_op();
191 match ntfs.unregister(code).await {
192 Some(sender) => {
193 if let Err(e) = sender.send(ntfy) {
194 error!("notification channel closed {:?}", e);
195 }
196 },
197 None => panic!("Unhandled notification {:?}", code),
198 }
199 }
200 _ => error!("Unexpected NCI data received {:?}", cmd),
201 }
202 },
203 qc = cmd_rx.recv(), if pending.is_none() => if let Some(queued) = qc {
204 debug!("cmd_rx got a q");
205 if let Some(nsender) = queued.notification {
206 ntfs.register(queued.pending.cmd.get_op(), nsender).await;
207 }
208 if let Err(e) = hc.out_cmd_tx.send(queued.pending.cmd.clone().into()) {
209 error!("command queue closed: {:?}", e);
210 }
211 timeout.as_mut().reset(Instant::now() + Duration::from_millis(20));
212 pending = Some(queued.pending);
213 } else {
214 break;
215 },
216 () = &mut timeout => {
217 error!("Command processing timeout");
218 timeout.as_mut().reset(max_deadline);
219 pending = None;
220 },
221 Some(data) = hc.in_data_rx.recv() => ic.in_data_int.send(data).await.unwrap(),
222 Some(data) = ic.out_data_int.recv() => hc.out_data_tx.send(data).unwrap(),
223 else => {
224 debug!("Select is done");
225 break;
226 },
227 }
228 }
229 debug!("NCI dispatch is terminated.");
230 Ok(())
231 }
232