• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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