1 // Copyright 2022 Google LLC
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 //     https://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 //! ## Specifications
16 //! - [MAC] FiRa Consortium UWB MAC Technical Requirements
17 //! - [UCI] FiRa Consortium UWB Command Interface Generic Technical specification
18 
19 use crate::packets::uci::{self, *};
20 use crate::{AppConfig, MacAddress};
21 use bytes::BytesMut;
22 use pdl_runtime::Packet;
23 use std::time::Duration;
24 use tokio::sync::mpsc;
25 use tokio::task::JoinHandle;
26 use tokio::time;
27 
28 use super::UciPacket;
29 
30 pub struct Session {
31     /// cf. [UCI] 7.1
32     pub state: SessionState,
33     /// cf. [UCI] 7.2 Table 13: 4 octets unique random number generated by application
34     id: u32,
35     device_handle: usize,
36     data: BytesMut,
37 
38     pub session_type: SessionType,
39     pub sequence_number: u32,
40     pub app_config: AppConfig,
41     pub ranging_task: Option<JoinHandle<()>>,
42     tx: mpsc::UnboundedSender<UciPacket>,
43 }
44 
45 impl Session {
new( id: u32, session_type: SessionType, device_handle: usize, tx: mpsc::UnboundedSender<UciPacket>, ) -> Self46     pub fn new(
47         id: u32,
48         session_type: SessionType,
49         device_handle: usize,
50         tx: mpsc::UnboundedSender<UciPacket>,
51     ) -> Self {
52         Self {
53             state: SessionState::SessionStateDeinit,
54             id,
55             device_handle,
56             data: BytesMut::new(),
57             session_type,
58             sequence_number: 0,
59             app_config: AppConfig::default(),
60             ranging_task: None,
61             tx,
62         }
63     }
64 
set_state(&mut self, session_state: SessionState, reason_code: ReasonCode)65     pub fn set_state(&mut self, session_state: SessionState, reason_code: ReasonCode) {
66         // No transition: ignore
67         if session_state == self.state {
68             return;
69         }
70 
71         // Send status notification
72         self.state = session_state;
73         let tx = self.tx.clone();
74         let session_id = self.id;
75         tokio::spawn(async move {
76             time::sleep(Duration::from_millis(1)).await;
77             tx.send(
78                 SessionStatusNtfBuilder {
79                     session_token: session_id,
80                     session_state,
81                     reason_code: reason_code.into(),
82                 }
83                 .build()
84                 .encode_to_vec()
85                 .unwrap(),
86             )
87             .unwrap()
88         });
89     }
90 
get_dst_mac_address(&self) -> &[MacAddress]91     pub fn get_dst_mac_address(&self) -> &[MacAddress] {
92         &self.app_config.dst_mac_address
93     }
94 
is_session_info_ntf_enabled(&self) -> bool95     pub fn is_session_info_ntf_enabled(&self) -> bool {
96         self.app_config.session_info_ntf_config != uci::SessionInfoNtfConfig::Disable
97     }
98 
99     #[allow(unused)]
is_session_data_transfer_status_ntf_enabled(&self) -> bool100     pub fn is_session_data_transfer_status_ntf_enabled(&self) -> bool {
101         self.app_config.session_data_transfer_status_ntf_config
102             != uci::SessionDataTransferStatusNtfConfig::Disable
103     }
104 
data(&self) -> &BytesMut105     pub fn data(&self) -> &BytesMut {
106         &self.data
107     }
108 
clear_data(&mut self)109     pub fn clear_data(&mut self) {
110         self.data.clear()
111     }
112 
session_type(&self) -> SessionType113     pub fn session_type(&self) -> SessionType {
114         self.session_type
115     }
116 
session_state(&self) -> SessionState117     pub fn session_state(&self) -> SessionState {
118         self.state
119     }
120 
init(&mut self)121     pub fn init(&mut self) {
122         self.set_state(
123             SessionState::SessionStateInit,
124             ReasonCode::StateChangeWithSessionManagementCommands,
125         );
126     }
127 
stop_ranging_task(&mut self)128     pub fn stop_ranging_task(&mut self) {
129         if let Some(handle) = &self.ranging_task {
130             handle.abort();
131             self.ranging_task = None;
132         }
133     }
134 
data_message_snd(&mut self, data: DataMessageSnd) -> ControlPacket135     pub fn data_message_snd(&mut self, data: DataMessageSnd) -> ControlPacket {
136         log::debug!("[{}] data_message_snd", self.device_handle);
137         let session_token = data.get_session_handle();
138         let uci_sequence_number = data.get_data_sequence_number() as u8;
139 
140         if self.session_type != SessionType::FiraRangingAndInBandDataSession {
141             return SessionDataTransferStatusNtfBuilder {
142                 session_token,
143                 status: DataTransferNtfStatusCode::UciDataTransferStatusSessionTypeNotSupported,
144                 tx_count: 1, // TODO: support for retries?
145                 uci_sequence_number,
146             }
147             .build()
148             .into();
149         }
150 
151         assert_eq!(self.id, session_token);
152 
153         self.data.extend_from_slice(data.get_application_data());
154 
155         SessionDataCreditNtfBuilder {
156             credit_availability: CreditAvailability::CreditAvailable,
157             session_token,
158         }
159         .build()
160         .into()
161     }
162 }
163 
164 impl Drop for Session {
drop(&mut self)165     fn drop(&mut self) {
166         // Make sure to abort the ranging task when dropping the session,
167         // the default behaviour when dropping a task handle is to detach
168         // the task, which is undesirable.
169         self.stop_ranging_task();
170         self.set_state(
171             SessionState::SessionStateDeinit,
172             ReasonCode::StateChangeWithSessionManagementCommands,
173         );
174     }
175 }