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 }