// Copyright 2023, The Android Open Source Project // // 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 // // http://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. //! Packet parsers and serializers. /// NCI packet parser and serializer. pub mod nci { #![allow(clippy::all)] #![allow(unused)] #![allow(missing_docs)] include!(concat!(env!("OUT_DIR"), "/nci_packets.rs")); impl ConnId { /// Create a Conn ID with `id` as an offset in the range of dynamic /// identifiers. pub fn from_dynamic(id: u8) -> Self { ConnId::try_from(id as u8 + 2).unwrap() } /// Return the index for a dynamic Conn ID. pub fn to_dynamic(id: Private) -> u8 { *id - 2 } } impl RfDiscoveryId { /// Create the default reserved RF Discovery ID. pub fn reserved() -> Self { RfDiscoveryId::try_from(0).unwrap() } /// Create an RF Discovery ID with `id` as an offset in the range of /// non-reserved identifiers. pub fn from_index(id: usize) -> Self { RfDiscoveryId::try_from(id as u8 + 1).unwrap() } /// Return the index for a valid RF Discovery ID. pub fn to_index(id: Private) -> usize { *id as usize - 1 } } impl NfceeId { pub fn nfcee(id: u8) -> Self { NfceeId::try_from(id).unwrap() } pub fn hci_nfcee(id: u8) -> Self { NfceeId::try_from(id).unwrap() } } use futures::stream::{self, Stream}; use std::pin::Pin; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::sync::Mutex; /// Read NCI Control and Data packets received on the NCI transport. /// Performs recombination of the segmented packets. pub struct Reader { socket: Pin>, } /// Write NCI Control and Data packets received to the NCI transport. /// Performs segmentation of the packets. pub struct Writer { socket: Pin>, } impl Reader { /// Create an NCI reader from an NCI transport. pub fn new(rx: T) -> Self { Reader { socket: Box::pin(rx) } } /// Read a single NCI packet from the reader. The packet is automatically /// re-assembled if segmented on the NCI transport. pub async fn read(&mut self) -> anyhow::Result> { use tokio::io::AsyncReadExt; const HEADER_SIZE: usize = 3; let mut complete_packet = vec![0; HEADER_SIZE]; // Note on reassembly: // - for each segment of a Control Message, the header of the // Control Packet SHALL contain the same MT, GID and OID values, // - for each segment of a Data Message the header of the Data // Packet SHALL contain the same MT and Conn ID. // Thus it is correct to keep only the last header of the segmented // packet. loop { // Read the common packet header. self.socket.read_exact(&mut complete_packet[0..HEADER_SIZE]).await?; let header = PacketHeader::parse(&complete_packet[0..HEADER_SIZE])?; // Read the packet payload. let payload_length = header.get_payload_length() as usize; let mut payload_bytes = vec![0; payload_length]; self.socket.read_exact(&mut payload_bytes).await?; complete_packet.extend(payload_bytes); // Check the Packet Boundary Flag. match header.get_pbf() { PacketBoundaryFlag::CompleteOrFinal => return Ok(complete_packet), PacketBoundaryFlag::Incomplete => (), } } } pub fn into_stream(self) -> impl Stream>> { stream::try_unfold(self, |mut reader| async move { Ok(Some((reader.read().await?, reader))) }) } } /// A mutable reference to the stream returned by into_stream pub type StreamRefMut<'a> = Pin<&'a mut dyn Stream>>>; impl Writer { /// Create an NCI writer from an NCI transport. pub fn new(rx: T) -> Self { Writer { socket: Box::pin(rx) } } /// Write a single NCI packet to the writer. The packet is automatically /// segmented if the payload exceeds the maximum size limit. pub async fn write(&mut self, mut packet: &[u8]) -> anyhow::Result<()> { use tokio::io::AsyncWriteExt; let mut header_bytes = [packet[0], packet[1], 0]; packet = &packet[3..]; loop { // Update header with framing information. let chunk_length = std::cmp::min(255, packet.len()); let pbf = if chunk_length < packet.len() { PacketBoundaryFlag::Incomplete } else { PacketBoundaryFlag::CompleteOrFinal }; const PBF_MASK: u8 = 0x10; header_bytes[0] &= !PBF_MASK; header_bytes[0] |= (pbf as u8) << 4; header_bytes[2] = chunk_length as u8; // Write the header and payload segment bytes. self.socket.write_all(&header_bytes).await?; self.socket.write_all(&packet[..chunk_length]).await?; packet = &packet[chunk_length..]; if packet.is_empty() { return Ok(()); } } } } } /// RF packet parser and serializer. pub mod rf { #![allow(clippy::all)] #![allow(unused)] #![allow(missing_docs)] include!(concat!(env!("OUT_DIR"), "/rf_packets.rs")); } impl From for nci::RfProtocolType { fn from(protocol: rf::Protocol) -> Self { match protocol { rf::Protocol::Undetermined => nci::RfProtocolType::Undetermined, rf::Protocol::T1t => nci::RfProtocolType::T1t, rf::Protocol::T2t => nci::RfProtocolType::T2t, rf::Protocol::T3t => nci::RfProtocolType::T3t, rf::Protocol::IsoDep => nci::RfProtocolType::IsoDep, rf::Protocol::NfcDep => nci::RfProtocolType::NfcDep, rf::Protocol::T5t => nci::RfProtocolType::T5t, rf::Protocol::Ndef => nci::RfProtocolType::Ndef, } } } impl From for rf::Protocol { fn from(protocol: nci::RfProtocolType) -> Self { match protocol { nci::RfProtocolType::Undetermined => rf::Protocol::Undetermined, nci::RfProtocolType::T1t => rf::Protocol::T1t, nci::RfProtocolType::T2t => rf::Protocol::T2t, nci::RfProtocolType::T3t => rf::Protocol::T3t, nci::RfProtocolType::IsoDep => rf::Protocol::IsoDep, nci::RfProtocolType::NfcDep => rf::Protocol::NfcDep, nci::RfProtocolType::T5t => rf::Protocol::T5t, nci::RfProtocolType::Ndef => rf::Protocol::Ndef, } } } impl TryFrom for rf::Technology { type Error = nci::RfTechnologyAndMode; fn try_from(protocol: nci::RfTechnologyAndMode) -> Result { Ok(match protocol { nci::RfTechnologyAndMode::NfcAPassivePollMode | nci::RfTechnologyAndMode::NfcAPassiveListenMode => rf::Technology::NfcA, nci::RfTechnologyAndMode::NfcBPassivePollMode | nci::RfTechnologyAndMode::NfcBPassiveListenMode => rf::Technology::NfcB, nci::RfTechnologyAndMode::NfcFPassivePollMode | nci::RfTechnologyAndMode::NfcFPassiveListenMode => rf::Technology::NfcF, nci::RfTechnologyAndMode::NfcVPassivePollMode => rf::Technology::NfcV, _ => return Err(protocol), }) } } impl From for nci::DeactivationType { fn from(type_: rf::DeactivateType) -> Self { match type_ { rf::DeactivateType::IdleMode => nci::DeactivationType::IdleMode, rf::DeactivateType::SleepMode => nci::DeactivationType::SleepMode, rf::DeactivateType::SleepAfMode => nci::DeactivationType::SleepAfMode, rf::DeactivateType::Discovery => nci::DeactivationType::Discovery, } } } impl From for rf::DeactivateType { fn from(type_: nci::DeactivationType) -> Self { match type_ { nci::DeactivationType::IdleMode => rf::DeactivateType::IdleMode, nci::DeactivationType::SleepMode => rf::DeactivateType::SleepMode, nci::DeactivationType::SleepAfMode => rf::DeactivateType::SleepAfMode, nci::DeactivationType::Discovery => rf::DeactivateType::Discovery, } } } impl From for nci::DeactivationReason { fn from(reason: rf::DeactivateReason) -> Self { match reason { rf::DeactivateReason::DhRequest => nci::DeactivationReason::DhRequest, rf::DeactivateReason::EndpointRequest => nci::DeactivationReason::EndpointRequest, rf::DeactivateReason::RfLinkLoss => nci::DeactivationReason::RfLinkLoss, rf::DeactivateReason::NfcBBadAfi => nci::DeactivationReason::NfcBBadAfi, rf::DeactivateReason::DhRequestFailed => nci::DeactivationReason::DhRequestFailed, } } }