btle/
lib.rs

1//! Generic BLE driver targeting mostly Bluetooth Advertisements. Implements the HCI layer.
2
3// For development, allow dead_code
4#![warn(clippy::pedantic)]
5// Clippy complains about the mass enum matching functions
6#![allow(clippy::too_many_lines)]
7// #[must_use] doesn't need to be on absolutely everything even though it should.
8#![allow(clippy::must_use_candidate)]
9#![allow(
10    clippy::missing_errors_doc,
11    clippy::range_plus_one,
12    clippy::type_complexity,
13    clippy::doc_markdown
14)]
15#![deny(unconditional_recursion)]
16#![allow(dead_code)]
17#![cfg_attr(not(feature = "std"), no_std)]
18
19#[cfg(feature = "std")]
20#[macro_use]
21extern crate std;
22
23#[cfg_attr(not(feature = "std"), macro_use)]
24extern crate alloc;
25pub(crate) use futures_util::stream::Stream;
26/// Workaround for returning futures from async Traits.
27pub type BoxFuture<'a, T> = core::pin::Pin<Box<dyn core::future::Future<Output = T> + 'a>>;
28/// Workaround for returning streams from async Traits.
29pub type BoxStream<'a, T> = core::pin::Pin<Box<dyn Stream<Item = T> + 'a>>;
30extern crate core;
31pub mod bytes;
32pub mod channel;
33#[cfg(feature = "classic")]
34pub mod classic;
35pub mod error;
36#[cfg(feature = "hci")]
37pub mod hci;
38pub mod le;
39pub mod uri;
40pub mod uuid;
41#[cfg(feature = "winrt_drives")]
42pub mod windows;
43
44use core::convert::{TryFrom, TryInto};
45
46/// Byte Packing/Unpacking error. Usually used for packing/unpacking a struct/type into/from
47/// a byte buffer.
48#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
49pub enum PackError {
50    BadOpcode,
51    BadLength { expected: usize, got: usize },
52    BadBytes { index: Option<usize> },
53    InvalidFields,
54}
55impl PackError {
56    /// Ensure `buf.len() == expected`. Returns `Ok(())` if they are equal or
57    /// `Err(HCIPackError::BadLength)` not equal.
58    #[inline]
59    pub fn expect_length(expected: usize, buf: &[u8]) -> Result<(), PackError> {
60        if buf.len() == expected {
61            Ok(())
62        } else {
63            Err(PackError::BadLength {
64                expected,
65                got: buf.len(),
66            })
67        }
68    }
69    /// Ensure `buf.len() >= expected`. Returns `Ok(())` if they are or
70    /// `Err(HCIPackError::BadLength)` not.
71    #[inline]
72    pub fn atleast_length(expected: usize, buf: &[u8]) -> Result<(), PackError> {
73        if buf.len() == expected {
74            Ok(())
75        } else {
76            Err(PackError::BadLength {
77                expected,
78                got: buf.len(),
79            })
80        }
81    }
82    /// Returns `PackError::BadBytes { index: Some(index) }`.
83    #[inline]
84    pub fn bad_index(index: usize) -> PackError {
85        PackError::BadBytes { index: Some(index) }
86    }
87}
88impl crate::error::Error for PackError {}
89
90/// Basic `ConversionError` for when primitives can't be converted to/from bytes because of invalid
91/// states. Most modules use their own errors for when there is more information to report.
92#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
93pub struct ConversionError(pub ());
94/// Received Signal Strength Indicator (RSSI). Units: `dBm`. Range -127 dBm to +20 dBm. Defaults to
95/// 0 dBm.
96#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)]
97pub struct RSSI(i8);
98impl RSSI {
99    pub const MIN_RSSI_I8: i8 = -127;
100    pub const MAX_RSSI_I8: i8 = 20;
101    pub const MAX_RSSI: RSSI = RSSI(Self::MAX_RSSI_I8);
102    pub const MIN_RSSI: RSSI = RSSI(Self::MIN_RSSI_I8);
103    /// Creates a new RSSI from `dbm`.
104    /// # Panics
105    /// Panics if `dbm < MIN_RSSI || dbm > MAX_RSSI`.
106    pub fn new(dbm: i8) -> RSSI {
107        assert!(
108            dbm >= Self::MIN_RSSI_I8 && dbm <= Self::MAX_RSSI_I8,
109            "invalid rssi '{}'",
110            dbm
111        );
112        RSSI(dbm)
113    }
114    pub const UNSUPPORTED_RSSI: i8 = 127;
115    pub fn maybe_rssi(val: i8) -> Result<Option<RSSI>, ConversionError> {
116        match val {
117            -127..=20 => Ok(Some(RSSI(val))),
118            127 => Ok(None),
119            _ => Err(ConversionError(())),
120        }
121    }
122}
123impl From<RSSI> for i8 {
124    fn from(rssi: RSSI) -> Self {
125        rssi.0
126    }
127}
128
129impl From<RSSI> for u8 {
130    fn from(rssi: RSSI) -> Self {
131        rssi.0 as u8
132    }
133}
134impl TryFrom<i8> for RSSI {
135    type Error = ConversionError;
136
137    fn try_from(value: i8) -> Result<Self, Self::Error> {
138        if value > Self::MAX_RSSI_I8 || value < Self::MIN_RSSI_I8 {
139            Err(ConversionError(()))
140        } else {
141            Ok(RSSI(value))
142        }
143    }
144}
145impl TryFrom<u8> for RSSI {
146    type Error = ConversionError;
147
148    fn try_from(value: u8) -> Result<Self, Self::Error> {
149        (value as i8).try_into()
150    }
151}
152/// Stores milli-dBm.
153/// So -100 dBm is = `RSSI(-100_000)`
154/// 0 dBm = `RSSI(0)`
155/// 10.05 dBm = `RSSI(10_050)`
156#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
157pub struct MilliDBM(pub i32);
158impl MilliDBM {
159    pub fn new(milli_dbm: i32) -> MilliDBM {
160        MilliDBM(milli_dbm)
161    }
162}
163/// Bluetooth address length (6 bytes)
164pub const BT_ADDRESS_LEN: usize = 6;
165
166/// Bluetooth Address. 6 bytes long.
167#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
168pub struct BTAddress(pub [u8; BT_ADDRESS_LEN]);
169impl BTAddress {
170    pub const LEN: usize = BT_ADDRESS_LEN;
171    pub const ZEROED: BTAddress = BTAddress([0_u8; 6]);
172    /// Creates a new 'BTAddress' from a byte slice.
173    /// # Panics
174    /// Panics if `bytes.len() != BT_ADDRESS_LEN` (6 bytes).
175    pub fn new(bytes: &[u8]) -> BTAddress {
176        assert_eq!(bytes.len(), BT_ADDRESS_LEN, "address wrong length");
177        BTAddress(bytes.try_into().expect("length checked by assert_eq above"))
178    }
179    pub fn unpack_from(bytes: &[u8]) -> Result<Self, PackError> {
180        PackError::expect_length(BT_ADDRESS_LEN, bytes)?;
181        Ok(Self::new(bytes))
182    }
183    pub fn pack_into(self, bytes: &mut [u8]) -> Result<(), PackError> {
184        PackError::expect_length(BT_ADDRESS_LEN, bytes)?;
185        bytes.copy_from_slice(&self.0[..]);
186        Ok(())
187    }
188    pub fn address_type(self) -> AddressType {
189        let address_type_bits = (self.0[BT_ADDRESS_LEN - 1] & 0xC0) >> 6;
190        match address_type_bits {
191            0b00 => AddressType::NonResolvablePrivate,
192            0b01 => AddressType::ResolvablePrivateAddress,
193            0b11 => AddressType::StaticDevice,
194            // Because of the mask above, _ should only match 0b10 (RFU).
195            _ => AddressType::RFU,
196        }
197    }
198    /// Returns `hash` (24-bit) and `prand` (24-bit) of the resolvable private address.
199    /// `prand` includes the address type bits.
200    pub fn private_address_parts(self) -> Option<(u32, u32)> {
201        match self.address_type() {
202            AddressType::ResolvablePrivateAddress => Some((
203                u32::from_le_bytes([self.0[0], self.0[1], self.0[2], 0]),
204                u32::from_le_bytes([self.0[3], self.0[4], self.0[5], 0]),
205            )),
206            _ => None,
207        }
208    }
209}
210#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Debug, Hash)]
211pub enum AddressType {
212    NonResolvablePrivate = 0b00,
213    ResolvablePrivateAddress = 0b01,
214    RFU = 0b10,
215    StaticDevice = 0b11,
216}
217/// 16-bit Bluetooth Company Identifier. Companies are assigned unique Company Identifiers to
218/// Bluetooth SIG members requesting them. [See here for more](https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/)
219#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
220#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
221pub struct CompanyID(pub u16);
222impl CompanyID {
223    /// Return the length in bytes of `CompanyID` (2-bytes, 16-bits)
224    pub const fn byte_len() -> usize {
225        2
226    }
227}
228impl crate::bytes::ToFromBytesEndian for CompanyID {
229    type AsBytesType = [u8; 2];
230
231    #[must_use]
232    fn to_bytes_le(&self) -> Self::AsBytesType {
233        (self.0).to_bytes_le()
234    }
235
236    #[must_use]
237    fn to_bytes_be(&self) -> Self::AsBytesType {
238        (self.0).to_bytes_be()
239    }
240
241    #[must_use]
242    fn from_bytes_le(bytes: &[u8]) -> Option<Self> {
243        Some(CompanyID(u16::from_bytes_le(bytes)?))
244    }
245
246    #[must_use]
247    fn from_bytes_be(bytes: &[u8]) -> Option<Self> {
248        Some(CompanyID(u16::from_bytes_be(bytes)?))
249    }
250}