// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! ## Specifications //! - [MAC] FiRa Consortium UWB MAC Technical Requirements //! - [UCI] FiRa Consortium UWB Command Interface Generic Technical specification use crate::packets::uci::{self, *}; use crate::{AppConfig, MacAddress}; use bytes::BytesMut; use pdl_runtime::Packet; use std::time::Duration; use tokio::sync::mpsc; use tokio::task::JoinHandle; use tokio::time; use super::UciPacket; pub struct Session { /// cf. [UCI] 7.1 pub state: SessionState, /// cf. [UCI] 7.2 Table 13: 4 octets unique random number generated by application id: u32, device_handle: usize, data: BytesMut, pub session_type: SessionType, pub sequence_number: u32, pub app_config: AppConfig, pub ranging_task: Option>, tx: mpsc::UnboundedSender, } impl Session { pub fn new( id: u32, session_type: SessionType, device_handle: usize, tx: mpsc::UnboundedSender, ) -> Self { Self { state: SessionState::SessionStateDeinit, id, device_handle, data: BytesMut::new(), session_type, sequence_number: 0, app_config: AppConfig::default(), ranging_task: None, tx, } } pub fn set_state(&mut self, session_state: SessionState, reason_code: ReasonCode) { // No transition: ignore if session_state == self.state { return; } // Send status notification self.state = session_state; let tx = self.tx.clone(); let session_id = self.id; tokio::spawn(async move { time::sleep(Duration::from_millis(1)).await; tx.send( SessionStatusNtfBuilder { session_token: session_id, session_state, reason_code: reason_code.into(), } .build() .encode_to_vec() .unwrap(), ) .unwrap() }); } pub fn get_dst_mac_address(&self) -> &[MacAddress] { &self.app_config.dst_mac_address } pub fn is_session_info_ntf_enabled(&self) -> bool { self.app_config.session_info_ntf_config != uci::SessionInfoNtfConfig::Disable } #[allow(unused)] pub fn is_session_data_transfer_status_ntf_enabled(&self) -> bool { self.app_config.session_data_transfer_status_ntf_config != uci::SessionDataTransferStatusNtfConfig::Disable } pub fn data(&self) -> &BytesMut { &self.data } pub fn clear_data(&mut self) { self.data.clear() } pub fn session_type(&self) -> SessionType { self.session_type } pub fn session_state(&self) -> SessionState { self.state } pub fn init(&mut self) { self.set_state( SessionState::SessionStateInit, ReasonCode::StateChangeWithSessionManagementCommands, ); } pub fn stop_ranging_task(&mut self) { if let Some(handle) = &self.ranging_task { handle.abort(); self.ranging_task = None; } } pub fn data_message_snd(&mut self, data: DataMessageSnd) -> ControlPacket { log::debug!("[{}] data_message_snd", self.device_handle); let session_token = data.get_session_handle(); let uci_sequence_number = data.get_data_sequence_number() as u8; if self.session_type != SessionType::FiraRangingAndInBandDataSession { return SessionDataTransferStatusNtfBuilder { session_token, status: DataTransferNtfStatusCode::UciDataTransferStatusSessionTypeNotSupported, tx_count: 1, // TODO: support for retries? uci_sequence_number, } .build() .into(); } assert_eq!(self.id, session_token); self.data.extend_from_slice(data.get_application_data()); SessionDataCreditNtfBuilder { credit_availability: CreditAvailability::CreditAvailable, session_token, } .build() .into() } } impl Drop for Session { fn drop(&mut self) { // Make sure to abort the ranging task when dropping the session, // the default behaviour when dropping a task handle is to detach // the task, which is undesirable. self.stop_ranging_task(); self.set_state( SessionState::SessionStateDeinit, ReasonCode::StateChangeWithSessionManagementCommands, ); } }