blob: f7d94e52bcc17547cb344c76126fabdff3f37f5d [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// Traits and utilities to handle length-type-value structures.
pub mod ltv;
use crate::packet_encoding::{Encodable, Error as PacketError};
/// Bluetooth Device Address that uniquely identifies the device
/// to another Bluetooth device.
/// See Core spec v5.3 Vol 2, Part B section 1.2.
pub type Address = [u8; 6];
/// See Core spec v5.3 Vol 3, Part C section 15.1.1.
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AddressType {
Public = 0x00,
Random = 0x01,
}
impl AddressType {
pub const BYTE_SIZE: usize = 1;
}
impl TryFrom<u8> for AddressType {
type Error = PacketError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x00 => Ok(Self::Public),
0x01 => Ok(Self::Random),
_ => Err(PacketError::OutOfRange),
}
}
}
/// Advertising Set ID which is 1 byte long.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct AdvertisingSetId(pub u8);
impl AdvertisingSetId {
// Byte size if this is to be encoded.
pub const BYTE_SIZE: usize = 1;
}
/// SyncInfo Interval value which is 2 bytes long.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PaInterval(pub u16);
impl PaInterval {
pub const BYTE_SIZE: usize = 2;
pub const UNKNOWN_VALUE: u16 = 0xFFFF;
pub const fn unknown() -> Self {
Self(Self::UNKNOWN_VALUE)
}
}
impl Encodable for PaInterval {
type Error = PacketError;
/// Encodees the PaInterval to 2 byte value using little endian encoding.
fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
if buf.len() < Self::BYTE_SIZE {
return Err(PacketError::BufferTooSmall);
}
buf[0..Self::BYTE_SIZE].copy_from_slice(&self.0.to_le_bytes());
Ok(())
}
fn encoded_len(&self) -> core::primitive::usize {
Self::BYTE_SIZE
}
}
/// Coding Format as defined by the Assigned Numbers Document. Section 2.11.
/// Referenced in the Core Spec 5.3, Volume 4, Part E, Section 7 as well as
/// various other profile specifications.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CodingFormat {
MuLawLog,
ALawLog,
Cvsd,
Transparent,
LinearPcm,
Msbc,
Lc3,
G729a,
VendorSpecific,
Unrecognized(u8),
}
impl From<u8> for CodingFormat {
fn from(value: u8) -> Self {
match value {
0x00 => Self::MuLawLog,
0x01 => Self::ALawLog,
0x02 => Self::Cvsd,
0x03 => Self::Transparent,
0x04 => Self::LinearPcm,
0x05 => Self::Msbc,
0x06 => Self::Lc3,
0x07 => Self::G729a,
0xFF => Self::VendorSpecific,
x => Self::Unrecognized(x),
}
}
}
impl From<CodingFormat> for u8 {
fn from(value: CodingFormat) -> Self {
match value {
CodingFormat::MuLawLog => 0x00,
CodingFormat::ALawLog => 0x01,
CodingFormat::Cvsd => 0x02,
CodingFormat::Transparent => 0x03,
CodingFormat::LinearPcm => 0x04,
CodingFormat::Msbc => 0x05,
CodingFormat::Lc3 => 0x06,
CodingFormat::G729a => 0x07,
CodingFormat::VendorSpecific => 0xFF,
CodingFormat::Unrecognized(x) => x,
}
}
}
impl core::fmt::Display for CodingFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CodingFormat::MuLawLog => write!(f, "ยต-law log"),
CodingFormat::ALawLog => write!(f, "A-law log"),
CodingFormat::Cvsd => write!(f, "CVSD"),
CodingFormat::Transparent => write!(f, "Transparent"),
CodingFormat::LinearPcm => write!(f, "Linear PCM"),
CodingFormat::Msbc => write!(f, "mSBC"),
CodingFormat::Lc3 => write!(f, "LC3"),
CodingFormat::G729a => write!(f, "G.729A"),
CodingFormat::VendorSpecific => write!(f, "Vendor Specific"),
CodingFormat::Unrecognized(x) => write!(f, "Unrecognized ({x})"),
}
}
}
/// Codec_ID communicated by a basic audio profile service/role.
#[derive(Debug, Clone, PartialEq)]
pub enum CodecId {
/// From the Assigned Numbers. Format will not be
/// `CodingFormat::VendorSpecific`
Assigned(CodingFormat),
VendorSpecific {
company_id: crate::CompanyId,
vendor_specific_codec_id: u16,
},
}
impl CodecId {
pub const BYTE_SIZE: usize = 5;
}
impl crate::packet_encoding::Decodable for CodecId {
type Error = crate::packet_encoding::Error;
fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
if buf.len() < 5 {
return Err(crate::packet_encoding::Error::UnexpectedDataLength);
}
let format = buf[0].into();
if format != CodingFormat::VendorSpecific {
// Maybe don't ignore the company and vendor id, and check if they are wrong.
return Ok((Self::Assigned(format), 5));
}
let company_id = u16::from_le_bytes([buf[1], buf[2]]).into();
let vendor_specific_codec_id = u16::from_le_bytes([buf[3], buf[4]]);
Ok((Self::VendorSpecific { company_id, vendor_specific_codec_id }, 5))
}
}
impl Encodable for CodecId {
type Error = PacketError;
fn encoded_len(&self) -> core::primitive::usize {
Self::BYTE_SIZE
}
fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
if buf.len() < Self::BYTE_SIZE {
return Err(Self::Error::BufferTooSmall);
}
match self {
CodecId::Assigned(format) => {
buf[0] = (*format).into();
buf[1..5].fill(0);
}
CodecId::VendorSpecific { company_id, vendor_specific_codec_id } => {
buf[0] = 0xFF;
[buf[1], buf[2]] = u16::from(*company_id).to_le_bytes();
[buf[3], buf[4]] = vendor_specific_codec_id.to_le_bytes();
}
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Phy {
/// LE 1M PHY
Le1m,
/// LE 2M PHY
Le2m,
/// LE Coded PHY
LeCoded,
}
#[cfg(test)]
mod tests {
use crate::packet_encoding::Decodable;
use super::*;
#[test]
fn encode_pa_interval() {
let mut buf = [0; PaInterval::BYTE_SIZE];
let interval = PaInterval(0x1004);
interval.encode(&mut buf[..]).expect("should succeed");
assert_eq!(buf, [0x04, 0x10]);
}
#[test]
fn encode_pa_interval_fails() {
let mut buf = [0; 1]; // Not enough buffer space.
let interval = PaInterval(0x1004);
interval.encode(&mut buf[..]).expect_err("should fail");
}
#[test]
fn decode_codec_id() {
let assigned = [0x01, 0x00, 0x00, 0x00, 0x00];
let (codec_id, _) = CodecId::decode(&assigned[..]).expect("should succeed");
assert_eq!(codec_id, CodecId::Assigned(CodingFormat::ALawLog));
let vendor_specific = [0xFF, 0x36, 0xFD, 0x11, 0x22];
let (codec_id, _) = CodecId::decode(&vendor_specific[..]).expect("should succeed");
assert_eq!(
codec_id,
CodecId::VendorSpecific {
company_id: (0xFD36 as u16).into(),
vendor_specific_codec_id: 0x2211
}
);
}
#[test]
fn encode_codec_id() {
let assigned = [0x01, 0x00, 0x00, 0x00, 0x00];
let (codec_id, _) = CodecId::decode(&assigned[..]).expect("should succeed");
assert_eq!(codec_id, CodecId::Assigned(CodingFormat::ALawLog));
let vendor_specific = [0xFF, 0x36, 0xFD, 0x11, 0x22];
let (codec_id, _) = CodecId::decode(&vendor_specific[..]).expect("should succeed");
assert_eq!(
codec_id,
CodecId::VendorSpecific {
company_id: (0xFD36 as u16).into(),
vendor_specific_codec_id: 0x2211
}
);
}
}