xref: /aosp_15_r20/system/nfc/src/rust/nci/nci.rs (revision 7eba2f3b06c51ae21384f6a4f14577b668a869b3)
1*7eba2f3bSAndroid Build Coastguard Worker // Copyright 2021, The Android Open Source Project
2*7eba2f3bSAndroid Build Coastguard Worker //
3*7eba2f3bSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*7eba2f3bSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*7eba2f3bSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*7eba2f3bSAndroid Build Coastguard Worker //
7*7eba2f3bSAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*7eba2f3bSAndroid Build Coastguard Worker //
9*7eba2f3bSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*7eba2f3bSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*7eba2f3bSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*7eba2f3bSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*7eba2f3bSAndroid Build Coastguard Worker // limitations under the License.
14*7eba2f3bSAndroid Build Coastguard Worker 
15*7eba2f3bSAndroid Build Coastguard Worker //! NCI Protocol Abstraction Layer
16*7eba2f3bSAndroid Build Coastguard Worker //! Supports sending NCI commands to the HAL and receiving
17*7eba2f3bSAndroid Build Coastguard Worker //! NCI messages back
18*7eba2f3bSAndroid Build Coastguard Worker 
19*7eba2f3bSAndroid Build Coastguard Worker use bytes::{BufMut, BytesMut};
20*7eba2f3bSAndroid Build Coastguard Worker use log::{debug, error};
21*7eba2f3bSAndroid Build Coastguard Worker use nfc_hal::{Hal, HalEventRegistry};
22*7eba2f3bSAndroid Build Coastguard Worker use nfc_packets::nci::DataPacketChild::Payload;
23*7eba2f3bSAndroid Build Coastguard Worker use nfc_packets::nci::NciPacketChild;
24*7eba2f3bSAndroid Build Coastguard Worker use nfc_packets::nci::NotificationChild::ConnCreditsNotification;
25*7eba2f3bSAndroid Build Coastguard Worker use nfc_packets::nci::{Command, DataPacket, DataPacketBuilder, Notification};
26*7eba2f3bSAndroid Build Coastguard Worker use nfc_packets::nci::{Opcode, PacketBoundaryFlag, Response};
27*7eba2f3bSAndroid Build Coastguard Worker use pdl_runtime::Packet;
28*7eba2f3bSAndroid Build Coastguard Worker use std::collections::HashMap;
29*7eba2f3bSAndroid Build Coastguard Worker use std::collections::VecDeque;
30*7eba2f3bSAndroid Build Coastguard Worker use std::sync::{Arc, Mutex};
31*7eba2f3bSAndroid Build Coastguard Worker use tokio::select;
32*7eba2f3bSAndroid Build Coastguard Worker use tokio::sync::mpsc::{channel, Receiver, Sender, UnboundedSender};
33*7eba2f3bSAndroid Build Coastguard Worker use tokio::sync::{oneshot, RwLock};
34*7eba2f3bSAndroid Build Coastguard Worker use tokio::time::{sleep, Duration, Instant};
35*7eba2f3bSAndroid Build Coastguard Worker 
36*7eba2f3bSAndroid Build Coastguard Worker pub mod api;
37*7eba2f3bSAndroid Build Coastguard Worker 
38*7eba2f3bSAndroid Build Coastguard Worker /// Result type
39*7eba2f3bSAndroid Build Coastguard Worker type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
40*7eba2f3bSAndroid Build Coastguard Worker 
41*7eba2f3bSAndroid Build Coastguard Worker /// Initialize the module and connect the channels
init() -> Nci42*7eba2f3bSAndroid Build Coastguard Worker pub async fn init() -> Nci {
43*7eba2f3bSAndroid Build Coastguard Worker     let hc = nfc_hal::init().await;
44*7eba2f3bSAndroid Build Coastguard Worker     // Channel to handle data upstream messages
45*7eba2f3bSAndroid Build Coastguard Worker     //    let (in_data_int, in_data_ext) = channel::<DataPacket>(10);
46*7eba2f3bSAndroid Build Coastguard Worker     // Internal data channels
47*7eba2f3bSAndroid Build Coastguard Worker     //    let ic = InternalChannels { in_data_int };
48*7eba2f3bSAndroid Build Coastguard Worker 
49*7eba2f3bSAndroid Build Coastguard Worker     let (cmd_tx, cmd_rx) = channel::<QueuedCommand>(10);
50*7eba2f3bSAndroid Build Coastguard Worker     let commands = CommandSender { cmd_tx };
51*7eba2f3bSAndroid Build Coastguard Worker     let hal_events = hc.hal_events.clone();
52*7eba2f3bSAndroid Build Coastguard Worker 
53*7eba2f3bSAndroid Build Coastguard Worker     let notifications = EventRegistry { handlers: Arc::new(Mutex::new(HashMap::new())) };
54*7eba2f3bSAndroid Build Coastguard Worker     let connections = LogicalConnectionsRegistry {
55*7eba2f3bSAndroid Build Coastguard Worker         conns: Arc::new(RwLock::new(HashMap::new())),
56*7eba2f3bSAndroid Build Coastguard Worker         sender: hc.out_data_tx.clone(),
57*7eba2f3bSAndroid Build Coastguard Worker     };
58*7eba2f3bSAndroid Build Coastguard Worker 
59*7eba2f3bSAndroid Build Coastguard Worker     tokio::spawn(dispatch(notifications, connections.clone(), hc, cmd_rx));
60*7eba2f3bSAndroid Build Coastguard Worker     Nci { hal_events, commands, connections }
61*7eba2f3bSAndroid Build Coastguard Worker }
62*7eba2f3bSAndroid Build Coastguard Worker 
63*7eba2f3bSAndroid Build Coastguard Worker /// NCI module external interface
64*7eba2f3bSAndroid Build Coastguard Worker pub struct Nci {
65*7eba2f3bSAndroid Build Coastguard Worker     /// HAL events
66*7eba2f3bSAndroid Build Coastguard Worker     pub hal_events: HalEventRegistry,
67*7eba2f3bSAndroid Build Coastguard Worker     /// NCI command communication interface
68*7eba2f3bSAndroid Build Coastguard Worker     pub commands: CommandSender,
69*7eba2f3bSAndroid Build Coastguard Worker     /// NCI logical connections
70*7eba2f3bSAndroid Build Coastguard Worker     pub connections: LogicalConnectionsRegistry,
71*7eba2f3bSAndroid Build Coastguard Worker }
72*7eba2f3bSAndroid Build Coastguard Worker 
73*7eba2f3bSAndroid Build Coastguard Worker #[derive(Debug)]
74*7eba2f3bSAndroid Build Coastguard Worker struct PendingCommand {
75*7eba2f3bSAndroid Build Coastguard Worker     cmd: Command,
76*7eba2f3bSAndroid Build Coastguard Worker     response: oneshot::Sender<Response>,
77*7eba2f3bSAndroid Build Coastguard Worker }
78*7eba2f3bSAndroid Build Coastguard Worker 
79*7eba2f3bSAndroid Build Coastguard Worker #[derive(Debug)]
80*7eba2f3bSAndroid Build Coastguard Worker struct QueuedCommand {
81*7eba2f3bSAndroid Build Coastguard Worker     pending: PendingCommand,
82*7eba2f3bSAndroid Build Coastguard Worker     notification: Option<oneshot::Sender<Notification>>,
83*7eba2f3bSAndroid Build Coastguard Worker }
84*7eba2f3bSAndroid Build Coastguard Worker 
85*7eba2f3bSAndroid Build Coastguard Worker /// Sends raw commands. Only useful for facades & shims, or wrapped as a CommandSender.
86*7eba2f3bSAndroid Build Coastguard Worker pub struct CommandSender {
87*7eba2f3bSAndroid Build Coastguard Worker     cmd_tx: Sender<QueuedCommand>,
88*7eba2f3bSAndroid Build Coastguard Worker }
89*7eba2f3bSAndroid Build Coastguard Worker 
90*7eba2f3bSAndroid Build Coastguard Worker /// The data returned by send_notify() method.
91*7eba2f3bSAndroid Build Coastguard Worker pub struct ResponsePendingNotification {
92*7eba2f3bSAndroid Build Coastguard Worker     /// Command response
93*7eba2f3bSAndroid Build Coastguard Worker     pub response: Response,
94*7eba2f3bSAndroid Build Coastguard Worker     /// Pending notification receiver
95*7eba2f3bSAndroid Build Coastguard Worker     pub notification: oneshot::Receiver<Notification>,
96*7eba2f3bSAndroid Build Coastguard Worker }
97*7eba2f3bSAndroid Build Coastguard Worker 
98*7eba2f3bSAndroid Build Coastguard Worker impl CommandSender {
99*7eba2f3bSAndroid Build Coastguard Worker     /// Send a command, but do not expect notification to be returned
send(&mut self, cmd: Command) -> Result<Response>100*7eba2f3bSAndroid Build Coastguard Worker     pub async fn send(&mut self, cmd: Command) -> Result<Response> {
101*7eba2f3bSAndroid Build Coastguard Worker         let (tx, rx) = oneshot::channel::<Response>();
102*7eba2f3bSAndroid Build Coastguard Worker         self.cmd_tx
103*7eba2f3bSAndroid Build Coastguard Worker             .send(QueuedCommand {
104*7eba2f3bSAndroid Build Coastguard Worker                 pending: PendingCommand { cmd, response: tx },
105*7eba2f3bSAndroid Build Coastguard Worker                 notification: None,
106*7eba2f3bSAndroid Build Coastguard Worker             })
107*7eba2f3bSAndroid Build Coastguard Worker             .await?;
108*7eba2f3bSAndroid Build Coastguard Worker         let event = rx.await?;
109*7eba2f3bSAndroid Build Coastguard Worker         Ok(event)
110*7eba2f3bSAndroid Build Coastguard Worker     }
111*7eba2f3bSAndroid Build Coastguard Worker     /// Send a command which expects notification as a result
send_and_notify(&mut self, cmd: Command) -> Result<ResponsePendingNotification>112*7eba2f3bSAndroid Build Coastguard Worker     pub async fn send_and_notify(&mut self, cmd: Command) -> Result<ResponsePendingNotification> {
113*7eba2f3bSAndroid Build Coastguard Worker         let (tx, rx) = oneshot::channel::<Response>();
114*7eba2f3bSAndroid Build Coastguard Worker         let (ntx, nrx) = oneshot::channel::<Notification>();
115*7eba2f3bSAndroid Build Coastguard Worker         self.cmd_tx
116*7eba2f3bSAndroid Build Coastguard Worker             .send(QueuedCommand {
117*7eba2f3bSAndroid Build Coastguard Worker                 pending: PendingCommand { cmd, response: tx },
118*7eba2f3bSAndroid Build Coastguard Worker                 notification: Some(ntx),
119*7eba2f3bSAndroid Build Coastguard Worker             })
120*7eba2f3bSAndroid Build Coastguard Worker             .await?;
121*7eba2f3bSAndroid Build Coastguard Worker         let event = rx.await?;
122*7eba2f3bSAndroid Build Coastguard Worker         Ok(ResponsePendingNotification { response: event, notification: nrx })
123*7eba2f3bSAndroid Build Coastguard Worker     }
124*7eba2f3bSAndroid Build Coastguard Worker }
125*7eba2f3bSAndroid Build Coastguard Worker 
126*7eba2f3bSAndroid Build Coastguard Worker impl Drop for CommandSender {
drop(&mut self)127*7eba2f3bSAndroid Build Coastguard Worker     fn drop(&mut self) {
128*7eba2f3bSAndroid Build Coastguard Worker         debug!("CommandSender is dropped");
129*7eba2f3bSAndroid Build Coastguard Worker     }
130*7eba2f3bSAndroid Build Coastguard Worker }
131*7eba2f3bSAndroid Build Coastguard Worker 
132*7eba2f3bSAndroid Build Coastguard Worker /// Parameters of a logical connection
133*7eba2f3bSAndroid Build Coastguard Worker struct ConnectionParameters {
134*7eba2f3bSAndroid Build Coastguard Worker     callback: Option<fn(u8, u16, &[u8])>,
135*7eba2f3bSAndroid Build Coastguard Worker     max_payload_size: u8,
136*7eba2f3bSAndroid Build Coastguard Worker     nfcc_credits_avail: u8,
137*7eba2f3bSAndroid Build Coastguard Worker     sendq: VecDeque<DataPacket>,
138*7eba2f3bSAndroid Build Coastguard Worker     recvq: VecDeque<DataPacket>,
139*7eba2f3bSAndroid Build Coastguard Worker }
140*7eba2f3bSAndroid Build Coastguard Worker 
141*7eba2f3bSAndroid Build Coastguard Worker impl ConnectionParameters {
142*7eba2f3bSAndroid Build Coastguard Worker     /// Flush TX queue
flush_tx(&mut self)143*7eba2f3bSAndroid Build Coastguard Worker     fn flush_tx(&mut self) {
144*7eba2f3bSAndroid Build Coastguard Worker         self.sendq.clear();
145*7eba2f3bSAndroid Build Coastguard Worker     }
146*7eba2f3bSAndroid Build Coastguard Worker }
147*7eba2f3bSAndroid Build Coastguard Worker 
148*7eba2f3bSAndroid Build Coastguard Worker /// To keep track of currentry open logical connections
149*7eba2f3bSAndroid Build Coastguard Worker #[derive(Clone)]
150*7eba2f3bSAndroid Build Coastguard Worker pub struct LogicalConnectionsRegistry {
151*7eba2f3bSAndroid Build Coastguard Worker     conns: Arc<RwLock<HashMap<u8, Mutex<ConnectionParameters>>>>,
152*7eba2f3bSAndroid Build Coastguard Worker     sender: UnboundedSender<DataPacket>,
153*7eba2f3bSAndroid Build Coastguard Worker }
154*7eba2f3bSAndroid Build Coastguard Worker 
155*7eba2f3bSAndroid Build Coastguard Worker impl LogicalConnectionsRegistry {
156*7eba2f3bSAndroid Build Coastguard Worker     /// Create a logical connection
open( &mut self, conn_id: u8, cb: Option<fn(u8, u16, &[u8])>, max_payload_size: u8, nfcc_credits_avail: u8, )157*7eba2f3bSAndroid Build Coastguard Worker     pub async fn open(
158*7eba2f3bSAndroid Build Coastguard Worker         &mut self,
159*7eba2f3bSAndroid Build Coastguard Worker         conn_id: u8,
160*7eba2f3bSAndroid Build Coastguard Worker         cb: Option<fn(u8, u16, &[u8])>,
161*7eba2f3bSAndroid Build Coastguard Worker         max_payload_size: u8,
162*7eba2f3bSAndroid Build Coastguard Worker         nfcc_credits_avail: u8,
163*7eba2f3bSAndroid Build Coastguard Worker     ) {
164*7eba2f3bSAndroid Build Coastguard Worker         let conn_params = ConnectionParameters {
165*7eba2f3bSAndroid Build Coastguard Worker             callback: cb,
166*7eba2f3bSAndroid Build Coastguard Worker             max_payload_size,
167*7eba2f3bSAndroid Build Coastguard Worker             nfcc_credits_avail,
168*7eba2f3bSAndroid Build Coastguard Worker             sendq: VecDeque::<DataPacket>::new(),
169*7eba2f3bSAndroid Build Coastguard Worker             recvq: VecDeque::<DataPacket>::new(),
170*7eba2f3bSAndroid Build Coastguard Worker         };
171*7eba2f3bSAndroid Build Coastguard Worker         assert!(
172*7eba2f3bSAndroid Build Coastguard Worker             self.conns.write().await.insert(conn_id, Mutex::new(conn_params)).is_none(),
173*7eba2f3bSAndroid Build Coastguard Worker             "A logical connection with id {:?} already exists",
174*7eba2f3bSAndroid Build Coastguard Worker             conn_id
175*7eba2f3bSAndroid Build Coastguard Worker         );
176*7eba2f3bSAndroid Build Coastguard Worker     }
177*7eba2f3bSAndroid Build Coastguard Worker     /// Set static callback
set_static_callback(&mut self, conn_id: u8, cb: Option<fn(u8, u16, &[u8])>)178*7eba2f3bSAndroid Build Coastguard Worker     pub async fn set_static_callback(&mut self, conn_id: u8, cb: Option<fn(u8, u16, &[u8])>) {
179*7eba2f3bSAndroid Build Coastguard Worker         if conn_id < 2 && cb.is_some() {
180*7eba2f3bSAndroid Build Coastguard Worker             // Static connections
181*7eba2f3bSAndroid Build Coastguard Worker             if let Some(conn_params) = self.conns.read().await.get(&conn_id) {
182*7eba2f3bSAndroid Build Coastguard Worker                 let mut conn_params = conn_params.lock().unwrap();
183*7eba2f3bSAndroid Build Coastguard Worker                 conn_params.callback = cb;
184*7eba2f3bSAndroid Build Coastguard Worker             }
185*7eba2f3bSAndroid Build Coastguard Worker         }
186*7eba2f3bSAndroid Build Coastguard Worker     }
187*7eba2f3bSAndroid Build Coastguard Worker     /// Close a logical connection
close(&mut self, conn_id: u8) -> Option<fn(u8, u16, &[u8])>188*7eba2f3bSAndroid Build Coastguard Worker     pub async fn close(&mut self, conn_id: u8) -> Option<fn(u8, u16, &[u8])> {
189*7eba2f3bSAndroid Build Coastguard Worker         if let Some(conn_params) = self.conns.write().await.remove(&conn_id) {
190*7eba2f3bSAndroid Build Coastguard Worker             conn_params.lock().unwrap().callback
191*7eba2f3bSAndroid Build Coastguard Worker         } else {
192*7eba2f3bSAndroid Build Coastguard Worker             None
193*7eba2f3bSAndroid Build Coastguard Worker         }
194*7eba2f3bSAndroid Build Coastguard Worker     }
195*7eba2f3bSAndroid Build Coastguard Worker     /// Add credits to a logical connection
add_credits(&self, conn_id: u8, ncreds: u8)196*7eba2f3bSAndroid Build Coastguard Worker     pub async fn add_credits(&self, conn_id: u8, ncreds: u8) {
197*7eba2f3bSAndroid Build Coastguard Worker         if let Some(conn_params) = self.conns.read().await.get(&conn_id) {
198*7eba2f3bSAndroid Build Coastguard Worker             let mut conn_params = conn_params.lock().unwrap();
199*7eba2f3bSAndroid Build Coastguard Worker             conn_params.nfcc_credits_avail += ncreds;
200*7eba2f3bSAndroid Build Coastguard Worker             while !conn_params.sendq.is_empty() && conn_params.nfcc_credits_avail > 0 {
201*7eba2f3bSAndroid Build Coastguard Worker                 self.sender.send(conn_params.sendq.pop_front().unwrap()).unwrap();
202*7eba2f3bSAndroid Build Coastguard Worker                 conn_params.nfcc_credits_avail -= 1;
203*7eba2f3bSAndroid Build Coastguard Worker             }
204*7eba2f3bSAndroid Build Coastguard Worker         }
205*7eba2f3bSAndroid Build Coastguard Worker     }
206*7eba2f3bSAndroid Build Coastguard Worker 
207*7eba2f3bSAndroid Build Coastguard Worker     /// Send a packet to a logical channel, splitting it if needed
send_packet(&mut self, conn_id: u8, pkt: DataPacket)208*7eba2f3bSAndroid Build Coastguard Worker     pub async fn send_packet(&mut self, conn_id: u8, pkt: DataPacket) {
209*7eba2f3bSAndroid Build Coastguard Worker         if let Some(conn_params) = self.conns.read().await.get(&conn_id) {
210*7eba2f3bSAndroid Build Coastguard Worker             let mut conn_params = conn_params.lock().unwrap();
211*7eba2f3bSAndroid Build Coastguard Worker             if let Payload(mut p) = pkt.specialize() {
212*7eba2f3bSAndroid Build Coastguard Worker                 if p.len() > conn_params.max_payload_size.into() {
213*7eba2f3bSAndroid Build Coastguard Worker                     let conn_id = pkt.get_conn_id();
214*7eba2f3bSAndroid Build Coastguard Worker                     while p.len() > conn_params.max_payload_size.into() {
215*7eba2f3bSAndroid Build Coastguard Worker                         let part = DataPacketBuilder {
216*7eba2f3bSAndroid Build Coastguard Worker                             conn_id,
217*7eba2f3bSAndroid Build Coastguard Worker                             pbf: PacketBoundaryFlag::Incomplete,
218*7eba2f3bSAndroid Build Coastguard Worker                             cr: 0,
219*7eba2f3bSAndroid Build Coastguard Worker                             payload: Some(p.split_to(conn_params.max_payload_size.into())),
220*7eba2f3bSAndroid Build Coastguard Worker                         }
221*7eba2f3bSAndroid Build Coastguard Worker                         .build();
222*7eba2f3bSAndroid Build Coastguard Worker                         conn_params.sendq.push_back(part);
223*7eba2f3bSAndroid Build Coastguard Worker                     }
224*7eba2f3bSAndroid Build Coastguard Worker                     if !p.is_empty() {
225*7eba2f3bSAndroid Build Coastguard Worker                         let end = DataPacketBuilder {
226*7eba2f3bSAndroid Build Coastguard Worker                             conn_id,
227*7eba2f3bSAndroid Build Coastguard Worker                             pbf: PacketBoundaryFlag::CompleteOrFinal,
228*7eba2f3bSAndroid Build Coastguard Worker                             cr: 0,
229*7eba2f3bSAndroid Build Coastguard Worker                             payload: Some(p),
230*7eba2f3bSAndroid Build Coastguard Worker                         }
231*7eba2f3bSAndroid Build Coastguard Worker                         .build();
232*7eba2f3bSAndroid Build Coastguard Worker                         conn_params.sendq.push_back(end);
233*7eba2f3bSAndroid Build Coastguard Worker                     }
234*7eba2f3bSAndroid Build Coastguard Worker                 } else {
235*7eba2f3bSAndroid Build Coastguard Worker                     conn_params.sendq.push_back(pkt);
236*7eba2f3bSAndroid Build Coastguard Worker                 }
237*7eba2f3bSAndroid Build Coastguard Worker             }
238*7eba2f3bSAndroid Build Coastguard Worker             while conn_params.nfcc_credits_avail > 0 && !conn_params.sendq.is_empty() {
239*7eba2f3bSAndroid Build Coastguard Worker                 self.sender.send(conn_params.sendq.pop_front().unwrap()).unwrap();
240*7eba2f3bSAndroid Build Coastguard Worker                 conn_params.nfcc_credits_avail -= 1;
241*7eba2f3bSAndroid Build Coastguard Worker             }
242*7eba2f3bSAndroid Build Coastguard Worker         }
243*7eba2f3bSAndroid Build Coastguard Worker     }
244*7eba2f3bSAndroid Build Coastguard Worker 
245*7eba2f3bSAndroid Build Coastguard Worker     /// Send data packet callback to the upper layers
send_callback(&self, pkt: DataPacket)246*7eba2f3bSAndroid Build Coastguard Worker     pub async fn send_callback(&self, pkt: DataPacket) {
247*7eba2f3bSAndroid Build Coastguard Worker         let conn_id = pkt.get_conn_id();
248*7eba2f3bSAndroid Build Coastguard Worker         let ncreds = pkt.get_cr();
249*7eba2f3bSAndroid Build Coastguard Worker         if ncreds > 0 {
250*7eba2f3bSAndroid Build Coastguard Worker             self.add_credits(conn_id, ncreds).await;
251*7eba2f3bSAndroid Build Coastguard Worker         }
252*7eba2f3bSAndroid Build Coastguard Worker         let done = pkt.get_pbf() == PacketBoundaryFlag::CompleteOrFinal;
253*7eba2f3bSAndroid Build Coastguard Worker         if let Some(conn_params) = self.conns.read().await.get(&conn_id) {
254*7eba2f3bSAndroid Build Coastguard Worker             let mut conn_params = conn_params.lock().unwrap();
255*7eba2f3bSAndroid Build Coastguard Worker             if !done && conn_params.recvq.is_empty() {
256*7eba2f3bSAndroid Build Coastguard Worker                 const NFC_DATA_START_CEVT: u16 = 5;
257*7eba2f3bSAndroid Build Coastguard Worker                 let cb = conn_params.callback.unwrap();
258*7eba2f3bSAndroid Build Coastguard Worker                 cb(conn_id, NFC_DATA_START_CEVT, &[]);
259*7eba2f3bSAndroid Build Coastguard Worker             }
260*7eba2f3bSAndroid Build Coastguard Worker             conn_params.recvq.push_back(pkt);
261*7eba2f3bSAndroid Build Coastguard Worker             if done {
262*7eba2f3bSAndroid Build Coastguard Worker                 const NFC_DATA_CEVT_SIZE: usize = 4; // 3 for header and 1 for status
263*7eba2f3bSAndroid Build Coastguard Worker                 let cap = conn_params.recvq.len() * conn_params.max_payload_size as usize
264*7eba2f3bSAndroid Build Coastguard Worker                     + NFC_DATA_CEVT_SIZE;
265*7eba2f3bSAndroid Build Coastguard Worker                 let mut buffer = BytesMut::with_capacity(cap);
266*7eba2f3bSAndroid Build Coastguard Worker                 buffer.put_u8(0u8); // status
267*7eba2f3bSAndroid Build Coastguard Worker                 let pkt = conn_params.recvq.pop_front().unwrap();
268*7eba2f3bSAndroid Build Coastguard Worker                 buffer.put(pkt.encode_to_bytes().unwrap());
269*7eba2f3bSAndroid Build Coastguard Worker                 while !conn_params.recvq.is_empty() {
270*7eba2f3bSAndroid Build Coastguard Worker                     let pkt = conn_params.recvq.pop_front().unwrap();
271*7eba2f3bSAndroid Build Coastguard Worker                     if let Payload(p) = pkt.specialize() {
272*7eba2f3bSAndroid Build Coastguard Worker                         buffer.put(p);
273*7eba2f3bSAndroid Build Coastguard Worker                     }
274*7eba2f3bSAndroid Build Coastguard Worker                 }
275*7eba2f3bSAndroid Build Coastguard Worker                 let data_cevt = buffer.freeze();
276*7eba2f3bSAndroid Build Coastguard Worker                 let cb = conn_params.callback.unwrap();
277*7eba2f3bSAndroid Build Coastguard Worker                 const NFC_DATA_CEVT: u16 = 3;
278*7eba2f3bSAndroid Build Coastguard Worker                 cb(conn_id, NFC_DATA_CEVT, data_cevt.as_ref());
279*7eba2f3bSAndroid Build Coastguard Worker             }
280*7eba2f3bSAndroid Build Coastguard Worker         }
281*7eba2f3bSAndroid Build Coastguard Worker     }
282*7eba2f3bSAndroid Build Coastguard Worker 
283*7eba2f3bSAndroid Build Coastguard Worker     /// Flush outgoing data queue
flush_data(&mut self, conn_id: u8) -> bool284*7eba2f3bSAndroid Build Coastguard Worker     pub async fn flush_data(&mut self, conn_id: u8) -> bool {
285*7eba2f3bSAndroid Build Coastguard Worker         if let Some(conn_params) = self.conns.read().await.get(&conn_id) {
286*7eba2f3bSAndroid Build Coastguard Worker             conn_params.lock().unwrap().flush_tx();
287*7eba2f3bSAndroid Build Coastguard Worker             true
288*7eba2f3bSAndroid Build Coastguard Worker         } else {
289*7eba2f3bSAndroid Build Coastguard Worker             false
290*7eba2f3bSAndroid Build Coastguard Worker         }
291*7eba2f3bSAndroid Build Coastguard Worker     }
292*7eba2f3bSAndroid Build Coastguard Worker }
293*7eba2f3bSAndroid Build Coastguard Worker 
294*7eba2f3bSAndroid Build Coastguard Worker /// Provides ability to register and unregister for NCI notifications
295*7eba2f3bSAndroid Build Coastguard Worker #[derive(Clone)]
296*7eba2f3bSAndroid Build Coastguard Worker pub struct EventRegistry {
297*7eba2f3bSAndroid Build Coastguard Worker     handlers: Arc<Mutex<HashMap<Opcode, oneshot::Sender<Notification>>>>,
298*7eba2f3bSAndroid Build Coastguard Worker }
299*7eba2f3bSAndroid Build Coastguard Worker 
300*7eba2f3bSAndroid Build Coastguard Worker impl EventRegistry {
301*7eba2f3bSAndroid Build Coastguard Worker     /// Indicate interest in specific NCI notification
register(&mut self, code: Opcode, sender: oneshot::Sender<Notification>)302*7eba2f3bSAndroid Build Coastguard Worker     pub async fn register(&mut self, code: Opcode, sender: oneshot::Sender<Notification>) {
303*7eba2f3bSAndroid Build Coastguard Worker         assert!(
304*7eba2f3bSAndroid Build Coastguard Worker             self.handlers.lock().unwrap().insert(code, sender).is_none(),
305*7eba2f3bSAndroid Build Coastguard Worker             "A handler for {:?} is already registered",
306*7eba2f3bSAndroid Build Coastguard Worker             code
307*7eba2f3bSAndroid Build Coastguard Worker         );
308*7eba2f3bSAndroid Build Coastguard Worker     }
309*7eba2f3bSAndroid Build Coastguard Worker 
310*7eba2f3bSAndroid Build Coastguard Worker     /// Remove interest in specific NCI notification
unregister(&mut self, code: Opcode) -> Option<oneshot::Sender<Notification>>311*7eba2f3bSAndroid Build Coastguard Worker     pub async fn unregister(&mut self, code: Opcode) -> Option<oneshot::Sender<Notification>> {
312*7eba2f3bSAndroid Build Coastguard Worker         self.handlers.lock().unwrap().remove(&code)
313*7eba2f3bSAndroid Build Coastguard Worker     }
314*7eba2f3bSAndroid Build Coastguard Worker }
315*7eba2f3bSAndroid Build Coastguard Worker 
dispatch( mut ntfs: EventRegistry, lcons: LogicalConnectionsRegistry, mut hc: Hal, mut cmd_rx: Receiver<QueuedCommand>, ) -> Result<()>316*7eba2f3bSAndroid Build Coastguard Worker async fn dispatch(
317*7eba2f3bSAndroid Build Coastguard Worker     mut ntfs: EventRegistry,
318*7eba2f3bSAndroid Build Coastguard Worker     lcons: LogicalConnectionsRegistry,
319*7eba2f3bSAndroid Build Coastguard Worker     mut hc: Hal,
320*7eba2f3bSAndroid Build Coastguard Worker     //    ic: InternalChannels,
321*7eba2f3bSAndroid Build Coastguard Worker     mut cmd_rx: Receiver<QueuedCommand>,
322*7eba2f3bSAndroid Build Coastguard Worker ) -> Result<()> {
323*7eba2f3bSAndroid Build Coastguard Worker     let mut pending: Option<PendingCommand> = None;
324*7eba2f3bSAndroid Build Coastguard Worker     let timeout = sleep(Duration::MAX);
325*7eba2f3bSAndroid Build Coastguard Worker     // The max_deadline is used to set  the sleep() deadline to a very distant moment in
326*7eba2f3bSAndroid Build Coastguard Worker     // the future, when the notification from the timer is not required.
327*7eba2f3bSAndroid Build Coastguard Worker     let max_deadline = timeout.deadline();
328*7eba2f3bSAndroid Build Coastguard Worker     tokio::pin!(timeout);
329*7eba2f3bSAndroid Build Coastguard Worker     loop {
330*7eba2f3bSAndroid Build Coastguard Worker         select! {
331*7eba2f3bSAndroid Build Coastguard Worker             Some(cmd) = hc.in_cmd_rx.recv() => {
332*7eba2f3bSAndroid Build Coastguard Worker                 match cmd.specialize() {
333*7eba2f3bSAndroid Build Coastguard Worker                     NciPacketChild::Response(rsp) => {
334*7eba2f3bSAndroid Build Coastguard Worker                         timeout.as_mut().reset(max_deadline);
335*7eba2f3bSAndroid Build Coastguard Worker                         let this_opcode = rsp.get_cmd_op();
336*7eba2f3bSAndroid Build Coastguard Worker                         match pending.take() {
337*7eba2f3bSAndroid Build Coastguard Worker                             Some(PendingCommand{cmd, response}) if cmd.get_op() == this_opcode => {
338*7eba2f3bSAndroid Build Coastguard Worker                                 if let Err(e) = response.send(rsp) {
339*7eba2f3bSAndroid Build Coastguard Worker                                     error!("failure dispatching command status {:?}", e);
340*7eba2f3bSAndroid Build Coastguard Worker                                 }
341*7eba2f3bSAndroid Build Coastguard Worker                             },
342*7eba2f3bSAndroid Build Coastguard Worker                             Some(PendingCommand{cmd, ..}) => panic!("Waiting for {:?}, got {:?}", cmd.get_op(), this_opcode),
343*7eba2f3bSAndroid Build Coastguard Worker                             None => panic!("Unexpected status event with opcode {:?}", this_opcode),
344*7eba2f3bSAndroid Build Coastguard Worker                         }
345*7eba2f3bSAndroid Build Coastguard Worker                     },
346*7eba2f3bSAndroid Build Coastguard Worker                     NciPacketChild::Notification(ntfy) => {
347*7eba2f3bSAndroid Build Coastguard Worker                         match ntfy.specialize() {
348*7eba2f3bSAndroid Build Coastguard Worker                             ConnCreditsNotification(ccnp) => {
349*7eba2f3bSAndroid Build Coastguard Worker                                 let conns = ccnp.get_conns();
350*7eba2f3bSAndroid Build Coastguard Worker                                 for conn in conns {
351*7eba2f3bSAndroid Build Coastguard Worker                                     lcons.add_credits(conn.conn_id, conn.ncredits).await;
352*7eba2f3bSAndroid Build Coastguard Worker                                 }
353*7eba2f3bSAndroid Build Coastguard Worker                             },
354*7eba2f3bSAndroid Build Coastguard Worker                             _ => {
355*7eba2f3bSAndroid Build Coastguard Worker                                 let code = ntfy.get_cmd_op();
356*7eba2f3bSAndroid Build Coastguard Worker                                 match ntfs.unregister(code).await {
357*7eba2f3bSAndroid Build Coastguard Worker                                     Some(sender) => {
358*7eba2f3bSAndroid Build Coastguard Worker                                         if let Err(e) = sender.send(ntfy) {
359*7eba2f3bSAndroid Build Coastguard Worker                                             error!("notification channel closed {:?}", e);
360*7eba2f3bSAndroid Build Coastguard Worker                                         }
361*7eba2f3bSAndroid Build Coastguard Worker                                     },
362*7eba2f3bSAndroid Build Coastguard Worker                                     None => panic!("Unhandled notification {:?}", code),
363*7eba2f3bSAndroid Build Coastguard Worker                                 }
364*7eba2f3bSAndroid Build Coastguard Worker                             },
365*7eba2f3bSAndroid Build Coastguard Worker                         }
366*7eba2f3bSAndroid Build Coastguard Worker                     },
367*7eba2f3bSAndroid Build Coastguard Worker                     _ => error!("Unexpected NCI data received {:?}", cmd),
368*7eba2f3bSAndroid Build Coastguard Worker                 }
369*7eba2f3bSAndroid Build Coastguard Worker             },
370*7eba2f3bSAndroid Build Coastguard Worker             qc = cmd_rx.recv(), if pending.is_none() => if let Some(queued) = qc {
371*7eba2f3bSAndroid Build Coastguard Worker                 debug!("cmd_rx got a q");
372*7eba2f3bSAndroid Build Coastguard Worker                 if let Some(nsender) = queued.notification {
373*7eba2f3bSAndroid Build Coastguard Worker                     ntfs.register(queued.pending.cmd.get_op(), nsender).await;
374*7eba2f3bSAndroid Build Coastguard Worker                 }
375*7eba2f3bSAndroid Build Coastguard Worker                 if let Err(e) = hc.out_cmd_tx.send(queued.pending.cmd.clone().into()) {
376*7eba2f3bSAndroid Build Coastguard Worker                     error!("command queue closed: {:?}", e);
377*7eba2f3bSAndroid Build Coastguard Worker                 }
378*7eba2f3bSAndroid Build Coastguard Worker                 timeout.as_mut().reset(Instant::now() + Duration::from_millis(20));
379*7eba2f3bSAndroid Build Coastguard Worker                 pending = Some(queued.pending);
380*7eba2f3bSAndroid Build Coastguard Worker             } else {
381*7eba2f3bSAndroid Build Coastguard Worker                 break;
382*7eba2f3bSAndroid Build Coastguard Worker             },
383*7eba2f3bSAndroid Build Coastguard Worker             () = &mut timeout => {
384*7eba2f3bSAndroid Build Coastguard Worker                 error!("Command processing timeout");
385*7eba2f3bSAndroid Build Coastguard Worker                 timeout.as_mut().reset(max_deadline);
386*7eba2f3bSAndroid Build Coastguard Worker                 pending = None;
387*7eba2f3bSAndroid Build Coastguard Worker             },
388*7eba2f3bSAndroid Build Coastguard Worker             Some(data) = hc.in_data_rx.recv() => lcons.send_callback(data).await,
389*7eba2f3bSAndroid Build Coastguard Worker             else => {
390*7eba2f3bSAndroid Build Coastguard Worker                 debug!("Select is done");
391*7eba2f3bSAndroid Build Coastguard Worker                 break;
392*7eba2f3bSAndroid Build Coastguard Worker             },
393*7eba2f3bSAndroid Build Coastguard Worker         }
394*7eba2f3bSAndroid Build Coastguard Worker     }
395*7eba2f3bSAndroid Build Coastguard Worker     debug!("NCI dispatch is terminated.");
396*7eba2f3bSAndroid Build Coastguard Worker     Ok(())
397*7eba2f3bSAndroid Build Coastguard Worker }
398