blob: 594a328cda2256c758152029230844c55806a478 [file] [log] [blame]
// 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>) -> 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<u8>) -> 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<Box<dyn AsyncRead>>,
}
/// Write NCI Control and Data packets received to the NCI transport.
/// Performs segmentation of the packets.
pub struct Writer {
socket: Pin<Box<dyn AsyncWrite>>,
}
impl Reader {
/// Create an NCI reader from an NCI transport.
pub fn new<T: AsyncRead + 'static>(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<Vec<u8>> {
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<Item = anyhow::Result<Vec<u8>>> {
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<Item = anyhow::Result<Vec<u8>>>>;
impl Writer {
/// Create an NCI writer from an NCI transport.
pub fn new<T: AsyncWrite + 'static>(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<rf::Protocol> 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<nci::RfProtocolType> 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<nci::RfTechnologyAndMode> for rf::Technology {
type Error = nci::RfTechnologyAndMode;
fn try_from(protocol: nci::RfTechnologyAndMode) -> Result<Self, Self::Error> {
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<rf::DeactivateType> 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<nci::DeactivationType> 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<rf::DeactivateReason> 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,
}
}
}