Skip to main content

bt_common/
core.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// Traits and utilities to handle length-type-value structures.
6pub mod ltv;
7
8use crate::packet_encoding::{Encodable, Error as PacketError};
9use std::str::FromStr;
10
11/// Bluetooth Device Address that uniquely identifies the device
12/// to another Bluetooth device.
13/// See Core spec v5.3 Vol 2, Part B section 1.2.
14pub type Address = [u8; 6];
15
16/// See Core spec v5.3 Vol 3, Part C section 15.1.1.
17#[repr(u8)]
18#[derive(Clone, Copy, Debug, PartialEq)]
19pub enum AddressType {
20    Public = 0x00,
21    Random = 0x01,
22}
23
24impl AddressType {
25    pub const BYTE_SIZE: usize = 1;
26}
27
28impl TryFrom<u8> for AddressType {
29    type Error = PacketError;
30
31    fn try_from(value: u8) -> Result<Self, Self::Error> {
32        match value {
33            0x00 => Ok(Self::Public),
34            0x01 => Ok(Self::Random),
35            _ => Err(PacketError::OutOfRange),
36        }
37    }
38}
39
40impl FromStr for AddressType {
41    type Err = PacketError;
42
43    fn from_str(s: &str) -> Result<Self, Self::Err> {
44        match s {
45            "Public" => Ok(AddressType::Public),
46            "Random" => Ok(AddressType::Random),
47            _ => Err(PacketError::InvalidParameter(format!("invalid address type: {s}"))),
48        }
49    }
50}
51
52/// Advertising Set ID (SID) which is 4 bits long (range 0x00 to 0x0F).
53/// See Bluetooth Core Specification Vol 6, Part B, Section 2.3.4 and BASS
54/// v1.0.1 Section 3.1.1.4 Table 3.5.
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub struct AdvertisingSetId(u8);
57
58impl AdvertisingSetId {
59    // Byte size if this is to be encoded.
60    pub const BYTE_SIZE: usize = 1;
61
62    /// Maximum valid ID. See BASS v1.0.1 Section 3.1.1.4 Table 3.5.
63    pub const MAX_VALUE: u8 = 0x0F;
64
65    pub fn value(&self) -> u8 {
66        self.0
67    }
68}
69
70impl TryFrom<u8> for AdvertisingSetId {
71    type Error = PacketError;
72
73    fn try_from(value: u8) -> Result<Self, Self::Error> {
74        if value > Self::MAX_VALUE {
75            Err(PacketError::OutOfRange)
76        } else {
77            Ok(Self(value))
78        }
79    }
80}
81
82impl From<AdvertisingSetId> for u8 {
83    fn from(sid: AdvertisingSetId) -> u8 {
84        sid.0
85    }
86}
87
88/// SyncInfo Interval value which is 2 bytes long.
89#[derive(Debug, Clone, Copy, PartialEq)]
90pub struct PeriodicAdvertisingInterval(pub u16);
91
92impl PeriodicAdvertisingInterval {
93    pub const BYTE_SIZE: usize = 2;
94    pub const UNKNOWN_VALUE: u16 = 0xFFFF;
95
96    pub const fn unknown() -> Self {
97        Self(Self::UNKNOWN_VALUE)
98    }
99}
100
101impl Encodable for PeriodicAdvertisingInterval {
102    type Error = PacketError;
103
104    /// Encodees the PaInterval to 2 byte value using little endian encoding.
105    fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
106        if buf.len() < Self::BYTE_SIZE {
107            return Err(PacketError::BufferTooSmall);
108        }
109        buf[0..Self::BYTE_SIZE].copy_from_slice(&self.0.to_le_bytes());
110        Ok(())
111    }
112
113    fn encoded_len(&self) -> core::primitive::usize {
114        Self::BYTE_SIZE
115    }
116}
117
118/// Coding Format as defined by the Assigned Numbers Document. Section 2.11.
119/// Referenced in the Core Spec 5.3, Volume 4, Part E, Section 7 as well as
120/// various other profile specifications.
121#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
122pub enum CodingFormat {
123    MuLawLog,
124    ALawLog,
125    Cvsd,
126    Transparent,
127    LinearPcm,
128    Msbc,
129    Lc3,
130    G729a,
131    VendorSpecific,
132    Unrecognized(u8),
133}
134
135impl From<u8> for CodingFormat {
136    fn from(value: u8) -> Self {
137        match value {
138            0x00 => Self::MuLawLog,
139            0x01 => Self::ALawLog,
140            0x02 => Self::Cvsd,
141            0x03 => Self::Transparent,
142            0x04 => Self::LinearPcm,
143            0x05 => Self::Msbc,
144            0x06 => Self::Lc3,
145            0x07 => Self::G729a,
146            0xFF => Self::VendorSpecific,
147            x => Self::Unrecognized(x),
148        }
149    }
150}
151
152impl From<CodingFormat> for u8 {
153    fn from(value: CodingFormat) -> Self {
154        match value {
155            CodingFormat::MuLawLog => 0x00,
156            CodingFormat::ALawLog => 0x01,
157            CodingFormat::Cvsd => 0x02,
158            CodingFormat::Transparent => 0x03,
159            CodingFormat::LinearPcm => 0x04,
160            CodingFormat::Msbc => 0x05,
161            CodingFormat::Lc3 => 0x06,
162            CodingFormat::G729a => 0x07,
163            CodingFormat::VendorSpecific => 0xFF,
164            CodingFormat::Unrecognized(x) => x,
165        }
166    }
167}
168
169impl core::fmt::Display for CodingFormat {
170    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171        match self {
172            CodingFormat::MuLawLog => write!(f, "ยต-law log"),
173            CodingFormat::ALawLog => write!(f, "A-law log"),
174            CodingFormat::Cvsd => write!(f, "CVSD"),
175            CodingFormat::Transparent => write!(f, "Transparent"),
176            CodingFormat::LinearPcm => write!(f, "Linear PCM"),
177            CodingFormat::Msbc => write!(f, "mSBC"),
178            CodingFormat::Lc3 => write!(f, "LC3"),
179            CodingFormat::G729a => write!(f, "G.729A"),
180            CodingFormat::VendorSpecific => write!(f, "Vendor Specific"),
181            CodingFormat::Unrecognized(x) => write!(f, "Unrecognized ({x})"),
182        }
183    }
184}
185
186/// Codec_ID communicated by a basic audio profile service/role.
187#[derive(Debug, Clone, PartialEq)]
188pub enum CodecId {
189    /// From the Assigned Numbers. Format will not be
190    /// `CodingFormat::VendorSpecific`
191    Assigned(CodingFormat),
192    VendorSpecific {
193        company_id: crate::CompanyId,
194        vendor_specific_codec_id: u16,
195    },
196}
197
198impl CodecId {
199    pub const BYTE_SIZE: usize = 5;
200}
201
202impl crate::packet_encoding::Decodable for CodecId {
203    type Error = crate::packet_encoding::Error;
204
205    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
206        if buf.len() < 5 {
207            return (Err(crate::packet_encoding::Error::UnexpectedDataLength), buf.len());
208        }
209        let format = buf[0].into();
210        if format != CodingFormat::VendorSpecific {
211            // Maybe don't ignore the company and vendor id, and check if they are wrong.
212            return (Ok(Self::Assigned(format)), 5);
213        }
214        let company_id = u16::from_le_bytes([buf[1], buf[2]]).into();
215        let vendor_specific_codec_id = u16::from_le_bytes([buf[3], buf[4]]);
216        (Ok(Self::VendorSpecific { company_id, vendor_specific_codec_id }), 5)
217    }
218}
219
220impl crate::packet_encoding::Encodable for CodecId {
221    type Error = crate::packet_encoding::Error;
222
223    fn encoded_len(&self) -> core::primitive::usize {
224        Self::BYTE_SIZE
225    }
226
227    fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
228        if buf.len() < Self::BYTE_SIZE {
229            return Err(Self::Error::BufferTooSmall);
230        }
231        match self {
232            CodecId::Assigned(format) => {
233                buf[0] = (*format).into();
234                buf[1..5].fill(0);
235            }
236            CodecId::VendorSpecific { company_id, vendor_specific_codec_id } => {
237                buf[0] = 0xFF;
238                [buf[1], buf[2]] = u16::from(*company_id).to_le_bytes();
239                [buf[3], buf[4]] = vendor_specific_codec_id.to_le_bytes();
240            }
241        }
242        Ok(())
243    }
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub enum Phy {
248    /// LE 1M PHY
249    Le1m,
250    /// LE 2M PHY
251    Le2m,
252    /// LE Coded PHY
253    LeCoded,
254}
255
256#[cfg(test)]
257mod tests {
258    use crate::packet_encoding::Decodable;
259
260    use super::*;
261    use std::str::FromStr;
262
263    #[test]
264    fn address_type_from_str() {
265        let addr_type = AddressType::from_str("Public").expect("should succeed");
266        assert_eq!(addr_type, AddressType::Public);
267        let addr_type = AddressType::from_str("Random").expect("should succeed");
268        assert_eq!(addr_type, AddressType::Random);
269        AddressType::from_str("invalid").expect_err("should fail");
270    }
271
272    #[test]
273    fn encode_pa_interval() {
274        let mut buf = [0; PeriodicAdvertisingInterval::BYTE_SIZE];
275        let interval = PeriodicAdvertisingInterval(0x1004);
276
277        interval.encode(&mut buf[..]).expect("should succeed");
278        assert_eq!(buf, [0x04, 0x10]);
279    }
280
281    #[test]
282    fn encode_pa_interval_fails() {
283        let mut buf = [0; 1]; // Not enough buffer space.
284        let interval = PeriodicAdvertisingInterval(0x1004);
285
286        interval.encode(&mut buf[..]).expect_err("should fail");
287    }
288
289    #[test]
290    fn decode_codec_id() {
291        let assigned = [0x01, 0x00, 0x00, 0x00, 0x00];
292        let (codec_id, _) = CodecId::decode(&assigned[..]);
293        assert_eq!(codec_id, Ok(CodecId::Assigned(CodingFormat::ALawLog)));
294
295        let vendor_specific = [0xFF, 0x36, 0xFD, 0x11, 0x22];
296        let (codec_id, _) = CodecId::decode(&vendor_specific[..]);
297        assert_eq!(
298            codec_id,
299            Ok(CodecId::VendorSpecific {
300                company_id: (0xFD36 as u16).into(),
301                vendor_specific_codec_id: 0x2211
302            })
303        );
304    }
305
306    #[test]
307    fn encode_codec_id() {
308        let assigned = [0x01, 0x00, 0x00, 0x00, 0x00];
309        let (codec_id, _) = CodecId::decode(&assigned[..]);
310        assert_eq!(codec_id, Ok(CodecId::Assigned(CodingFormat::ALawLog)));
311
312        let vendor_specific = [0xFF, 0x36, 0xFD, 0x11, 0x22];
313        let (codec_id, _) = CodecId::decode(&vendor_specific[..]);
314        assert_eq!(
315            codec_id,
316            Ok(CodecId::VendorSpecific {
317                company_id: (0xFD36 as u16).into(),
318                vendor_specific_codec_id: 0x2211
319            })
320        );
321    }
322
323    #[test]
324    fn advertising_set_id_success() {
325        assert_eq!(AdvertisingSetId::try_from(0x00).unwrap().value(), 0x00);
326        assert_eq!(AdvertisingSetId::try_from(0x0F).unwrap().value(), 0x0F);
327    }
328
329    #[test]
330    fn advertising_set_id_out_of_range() {
331        assert_eq!(AdvertisingSetId::try_from(0x10), Err(PacketError::OutOfRange));
332        assert_eq!(AdvertisingSetId::try_from(0xFF), Err(PacketError::OutOfRange));
333    }
334}