Skip to main content

trouble_host/
lib.rs

1#![no_std]
2#![allow(dead_code)]
3#![allow(unused_variables)]
4#![allow(clippy::needless_lifetimes)]
5#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
6#![warn(missing_docs)]
7
8use core::cell::{Cell, RefCell};
9use core::mem::{ManuallyDrop, MaybeUninit};
10
11use advertise::AdvertisementDataError;
12use bt_hci::cmd::le::LeReadMinimumSupportedConnectionInterval;
13#[cfg(feature = "shorter-connection-intervals")]
14use bt_hci::cmd::le::LeSetHostFeatureV2;
15use bt_hci::cmd::status::ReadRssi;
16use bt_hci::cmd::{AsyncCmd, SyncCmd};
17use bt_hci::param::{AddrKind, BdAddr, ConnHandle};
18use bt_hci::FromHciBytesError;
19use embassy_time::Duration;
20#[cfg(feature = "security")]
21use heapless::{Vec, VecView};
22
23use crate::att::AttErrorCode;
24use crate::channel_manager::ChannelStorage;
25use crate::connection::Connection;
26use crate::connection_manager::ConnectionStorage;
27#[cfg(feature = "security")]
28pub use crate::security_manager::{
29    BondInformation, IdentityResolvingKey, LongTermKey, OobData, Reason as PairingFailedReason,
30};
31pub use crate::types::capabilities::IoCapabilities;
32
33mod fmt;
34
35#[cfg(not(any(feature = "central", feature = "peripheral")))]
36compile_error!("Must enable at least one of the `central` or `peripheral` features");
37
38pub mod att;
39#[cfg(feature = "central")]
40pub mod central;
41mod channel_manager;
42mod codec;
43mod command;
44pub mod config;
45mod connection_manager;
46mod cursor;
47#[cfg(feature = "iso")]
48pub mod iso;
49#[cfg(feature = "default-packet-pool")]
50mod packet_pool;
51mod pdu;
52#[cfg(feature = "peripheral")]
53pub mod peripheral;
54#[cfg(feature = "security")]
55mod security_manager;
56pub mod types;
57
58#[cfg(feature = "central")]
59use central::*;
60#[cfg(feature = "peripheral")]
61use peripheral::*;
62
63pub mod advertise;
64pub mod connection;
65#[cfg(feature = "gatt")]
66pub mod gap;
67pub mod l2cap;
68#[cfg(feature = "scan")]
69pub mod scan;
70
71#[cfg(test)]
72pub(crate) mod mock_controller;
73
74pub(crate) mod host;
75use host::{AdvHandleState, BleHost, HostMetrics, Runner};
76
77pub mod prelude {
78    //! Convenience include of most commonly used types.
79    pub use bt_hci::controller::ExternalController;
80    pub use bt_hci::param::{AddrKind, BdAddr, LeConnRole as Role, PhyKind, PhyMask};
81    pub use bt_hci::uuid::*;
82    #[cfg(feature = "derive")]
83    pub use heapless::String as HeaplessString;
84    #[cfg(feature = "derive")]
85    pub use trouble_host_macros::*;
86
87    pub use super::att::AttErrorCode;
88    pub use super::{BleHostError, Controller, Error, HostResources, Packet, PacketPool, Stack, StackBuilder};
89    #[cfg(feature = "peripheral")]
90    pub use crate::advertise::*;
91    #[cfg(feature = "gatt")]
92    pub use crate::attribute::*;
93    #[cfg(feature = "gatt")]
94    pub use crate::attribute_server::*;
95    #[cfg(feature = "central")]
96    pub use crate::central::*;
97    pub use crate::connection::{ConnectRateParams, *};
98    #[cfg(feature = "gatt")]
99    pub use crate::gap::*;
100    #[cfg(feature = "gatt")]
101    pub use crate::gatt::*;
102    pub use crate::host::{ControlRunner, EventHandler, HostMetrics, Runner, RxRunner, TxRunner};
103    #[cfg(feature = "iso")]
104    pub use crate::iso::Iso;
105    pub use crate::l2cap::*;
106    #[cfg(feature = "default-packet-pool")]
107    pub use crate::packet_pool::DefaultPacketPool;
108    pub use crate::pdu::Sdu;
109    #[cfg(feature = "peripheral")]
110    pub use crate::peripheral::*;
111    #[cfg(feature = "scan")]
112    pub use crate::scan::*;
113    #[cfg(feature = "security")]
114    pub use crate::security_manager::{
115        BondInformation, IdentityResolvingKey, LongTermKey, OobData, Reason as PairingFailedReason,
116    };
117    pub use crate::types::capabilities::IoCapabilities;
118    #[cfg(feature = "gatt")]
119    pub use crate::types::gatt_traits::{AsGatt, FixedGattValue, FromGatt};
120    pub use crate::{Address, Identity};
121}
122
123#[cfg(feature = "gatt")]
124pub mod attribute;
125#[cfg(feature = "gatt")]
126mod attribute_server;
127#[cfg(feature = "gatt")]
128pub mod gatt;
129
130/// A BLE address.
131/// Every BLE device is identified by a unique *Bluetooth Device Address*, which is a 48-bit identifier similar to a MAC address. BLE addresses are categorized into two main types: *Public* and *Random*.
132///
133/// A Public Address is globally unique and assigned by the IEEE. It remains constant and is typically used by devices requiring a stable identifier.
134///
135/// A Random Address can be *static* or *dynamic*:
136///
137/// - *Static Random Address*: Remains fixed until the device restarts or resets.
138/// - *Private Random Address*: Changes periodically for privacy purposes. It can be *Resolvable* (can be linked to the original device using an Identity Resolving Key) or *Non-Resolvable* (completely anonymous).
139///
140/// Random addresses enhance privacy by preventing device tracking.
141#[derive(Debug, Clone, Copy, Default, Eq)]
142#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
143pub struct Address {
144    /// Address type.
145    pub kind: AddrKind,
146    /// Address value.
147    pub addr: BdAddr,
148}
149
150impl PartialEq for Address {
151    /// Compare two addresses, normalizing HCI identity address types.
152    ///
153    /// In HCI events the controller may report a peer's address type as 0x02 (Public Identity) or
154    /// 0x03 (Random Static Identity) when it resolved the peer's RPA via the resolving list. These
155    /// are semantically equivalent to 0x00 (Public) and 0x01 (Random) respectively, so this
156    /// implementation treats them as equal when comparing.
157    fn eq(&self, other: &Self) -> bool {
158        self.addr == other.addr && self.kind.as_raw() & 1 == other.kind.as_raw() & 1
159    }
160}
161
162impl Address {
163    /// Create a new address with the given kind and value.
164    pub const fn new(kind: AddrKind, addr: BdAddr) -> Self {
165        Self { kind, addr }
166    }
167
168    /// Create a new random address.
169    pub fn random(val: [u8; 6]) -> Self {
170        Self {
171            kind: AddrKind::RANDOM,
172            addr: BdAddr::new(val),
173        }
174    }
175
176    /// To bytes
177    pub fn to_bytes(&self) -> [u8; 7] {
178        let mut bytes = [0; 7];
179        bytes[0] = self.kind.into_inner();
180        let mut addr_bytes = self.addr.into_inner();
181        addr_bytes.reverse();
182        bytes[1..].copy_from_slice(&addr_bytes);
183        bytes
184    }
185}
186
187impl core::fmt::Display for Address {
188    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
189        let a = self.addr.into_inner();
190        write!(
191            f,
192            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
193            a[5], a[4], a[3], a[2], a[1], a[0]
194        )
195    }
196}
197
198#[cfg(feature = "defmt")]
199impl defmt::Format for Address {
200    fn format(&self, fmt: defmt::Formatter) {
201        let a = self.addr.into_inner();
202        defmt::write!(
203            fmt,
204            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
205            a[5],
206            a[4],
207            a[3],
208            a[2],
209            a[1],
210            a[0]
211        )
212    }
213}
214
215/// Identity of a peer device
216///
217/// Sometimes we have to save both the address and the IRK.
218/// Because sometimes the peer uses the static or public address even though the IRK is sent.
219/// In this case, the IRK exists but the used address is not RPA.
220#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
222pub struct Identity {
223    /// Identity address (random static or public)
224    pub addr: Address,
225
226    /// Identity Resolving Key
227    #[cfg(feature = "security")]
228    pub irk: Option<IdentityResolvingKey>,
229}
230
231#[cfg(feature = "defmt")]
232impl defmt::Format for Identity {
233    fn format(&self, fmt: defmt::Formatter) {
234        defmt::write!(fmt, "Addr({}) ", self.addr);
235        #[cfg(feature = "security")]
236        defmt::write!(fmt, "Irk({:X})", self.irk);
237    }
238}
239
240impl From<Address> for Identity {
241    fn from(addr: Address) -> Self {
242        Self {
243            addr,
244            #[cfg(feature = "security")]
245            irk: None,
246        }
247    }
248}
249
250impl Identity {
251    /// Check whether the address matches the identity.
252    ///
253    /// Matches if the address is an exact match (kind + addr) or if the IRK can resolve it.
254    pub fn match_address(&self, address: &Address) -> bool {
255        if self.addr == *address {
256            return true;
257        }
258        #[cfg(feature = "security")]
259        if let Some(irk) = self.irk {
260            return irk.resolve_address(&address.addr);
261        }
262        false
263    }
264
265    /// Check whether the given identity matches current identity
266    pub fn match_identity(&self, identity: &Identity) -> bool {
267        if self.addr == identity.addr {
268            return true;
269        }
270        #[cfg(feature = "security")]
271        {
272            if let Some(irk) = self.irk {
273                if irk.resolve_address(&identity.addr.addr) {
274                    return true;
275                }
276            }
277            if let Some(irk) = identity.irk {
278                if let Some(current_irk) = self.irk {
279                    if irk == current_irk {
280                        return true;
281                    }
282                }
283                if irk.resolve_address(&self.addr.addr) {
284                    return true;
285                }
286            }
287        }
288        false
289    }
290}
291
292/// Errors returned by the host.
293#[derive(Debug)]
294#[cfg_attr(feature = "defmt", derive(defmt::Format))]
295pub enum BleHostError<E> {
296    /// Error from the controller.
297    Controller(E),
298    /// Error from the host.
299    BleHost(Error),
300}
301
302/// How many bytes of invalid data to capture in the error variants before truncating.
303pub const MAX_INVALID_DATA_LEN: usize = 16;
304
305/// Errors related to Host.
306#[derive(Debug, Clone, PartialEq)]
307#[cfg_attr(feature = "defmt", derive(defmt::Format))]
308pub enum Error {
309    /// Error encoding parameters for HCI commands.
310    Hci(bt_hci::param::Error),
311    /// Error decoding responses from HCI commands.
312    HciDecode(FromHciBytesError),
313    /// Error from the Attribute Protocol.
314    Att(AttErrorCode),
315    #[cfg(feature = "security")]
316    /// Error from the security manager
317    Security(PairingFailedReason),
318    /// Insufficient space in the buffer.
319    InsufficientSpace,
320    /// Invalid value.
321    InvalidValue,
322
323    /// Unexpected data length.
324    ///
325    /// This happens if the attribute data length doesn't match the input length size,
326    /// and the attribute is deemed as *not* having variable length due to the characteristic's
327    /// `MAX_SIZE` and `MIN_SIZE` being defined as equal.
328    UnexpectedDataLength {
329        /// Expected length.
330        expected: usize,
331        /// Actual length.
332        actual: usize,
333    },
334
335    /// Error converting from GATT value.
336    CannotConstructGattValue([u8; MAX_INVALID_DATA_LEN]),
337
338    /// Scan config filter accept list is empty.
339    ConfigFilterAcceptListIsEmpty,
340
341    /// Unexpected GATT response.
342    UnexpectedGattResponse,
343
344    /// Received characteristic declaration data shorter than the minimum required length (5 bytes).
345    MalformedCharacteristicDeclaration {
346        /// Expected length.
347        expected: usize,
348        /// Actual length.
349        actual: usize,
350    },
351
352    /// Failed to decode the data structure within a characteristic declaration attribute value.
353    InvalidCharacteristicDeclarationData,
354
355    /// Failed to finalize the packet.
356    FailedToFinalize {
357        /// Expected length.
358        expected: usize,
359        /// Actual length.
360        actual: usize,
361    },
362
363    /// Codec error.
364    CodecError(codec::Error),
365
366    /// Extended advertising not supported.
367    ExtendedAdvertisingNotSupported,
368
369    /// Invalid UUID length.
370    InvalidUuidLength(usize),
371
372    /// Error decoding advertisement data.
373    Advertisement(AdvertisementDataError),
374    /// L2CAP credit-based connection refused by the peer.
375    L2capConnectError(crate::types::l2cap::LeCreditConnResultCode),
376    /// Invalid l2cap channel id provided.
377    InvalidChannelId,
378    /// No l2cap channel available.
379    NoChannelAvailable,
380    /// Resource not found.
381    NotFound,
382    /// Invalid state.
383    InvalidState,
384    /// Out of memory.
385    OutOfMemory,
386    /// Unsupported operation.
387    NotSupported,
388    /// L2cap channel closed.
389    ChannelClosed,
390    /// Operation timed out.
391    Timeout,
392    /// Controller is busy.
393    Busy,
394    /// No send permits available.
395    NoPermits,
396    /// Connection is disconnected.
397    Disconnected,
398    /// Connection limit has been reached.
399    ConnectionLimitReached,
400    /// GATT subscriber limit has been reached.
401    ///
402    /// The limit can be modified using the `gatt-client-notification-max-subscribers-N` features.
403    GattSubscriberLimitReached,
404    /// Resource is already in use.
405    AlreadyInUse,
406    /// Other error.
407    Other,
408}
409
410impl<E> From<Error> for BleHostError<E> {
411    fn from(value: Error) -> Self {
412        Self::BleHost(value)
413    }
414}
415
416impl From<FromHciBytesError> for Error {
417    fn from(error: FromHciBytesError) -> Self {
418        Self::HciDecode(error)
419    }
420}
421
422impl From<AttErrorCode> for Error {
423    fn from(error: AttErrorCode) -> Self {
424        Self::Att(error)
425    }
426}
427
428impl<E> From<bt_hci::cmd::Error<E>> for BleHostError<E> {
429    fn from(error: bt_hci::cmd::Error<E>) -> Self {
430        match error {
431            bt_hci::cmd::Error::Hci(p) => Self::BleHost(Error::Hci(p)),
432            bt_hci::cmd::Error::Io(p) => Self::Controller(p),
433        }
434    }
435}
436
437impl<E> From<bt_hci::param::Error> for BleHostError<E> {
438    fn from(error: bt_hci::param::Error) -> Self {
439        Self::BleHost(Error::Hci(error))
440    }
441}
442
443impl From<codec::Error> for Error {
444    fn from(error: codec::Error) -> Self {
445        match error {
446            codec::Error::InsufficientSpace => Error::InsufficientSpace,
447            codec::Error::InvalidValue => Error::CodecError(error),
448        }
449    }
450}
451
452impl<E> From<codec::Error> for BleHostError<E> {
453    fn from(error: codec::Error) -> Self {
454        match error {
455            codec::Error::InsufficientSpace => BleHostError::BleHost(Error::InsufficientSpace),
456            codec::Error::InvalidValue => BleHostError::BleHost(Error::InvalidValue),
457        }
458    }
459}
460
461use bt_hci::cmd::controller_baseband::*;
462use bt_hci::cmd::info::*;
463use bt_hci::cmd::le::*;
464use bt_hci::cmd::link_control::*;
465use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync};
466
467/// Trait for security-related controller commands.
468///
469/// When the `security` feature is enabled, this requires the controller to support
470/// encryption, resolving list and address resolution HCI commands. When disabled, this is
471/// automatically implemented for all controllers.
472#[cfg(feature = "security")]
473pub trait SecurityCmds:
474    bt_hci::controller::Controller
475    + ControllerCmdSync<LeLongTermKeyRequestReply>
476    + ControllerCmdAsync<LeEnableEncryption>
477    + ControllerCmdSync<LeAddDeviceToResolvingList>
478    + ControllerCmdSync<LeRemoveDeviceFromResolvingList>
479    + ControllerCmdSync<LeClearResolvingList>
480    + ControllerCmdSync<LeSetAddrResolutionEnable>
481    + ControllerCmdSync<LeSetResolvablePrivateAddrTimeout>
482    + ControllerCmdSync<LeSetPrivacyMode>
483    + ControllerCmdSync<LeRand>
484{
485}
486
487#[cfg(feature = "security")]
488impl<
489        C: bt_hci::controller::Controller
490            + ControllerCmdSync<LeLongTermKeyRequestReply>
491            + ControllerCmdAsync<LeEnableEncryption>
492            + ControllerCmdSync<LeAddDeviceToResolvingList>
493            + ControllerCmdSync<LeRemoveDeviceFromResolvingList>
494            + ControllerCmdSync<LeClearResolvingList>
495            + ControllerCmdSync<LeSetAddrResolutionEnable>
496            + ControllerCmdSync<LeSetResolvablePrivateAddrTimeout>
497            + ControllerCmdSync<LeSetPrivacyMode>
498            + ControllerCmdSync<LeRand>,
499    > SecurityCmds for C
500{
501}
502
503/// Auto-implemented when security is not enabled.
504#[cfg(not(feature = "security"))]
505pub trait SecurityCmds: bt_hci::controller::Controller {}
506
507#[cfg(not(feature = "security"))]
508impl<C: bt_hci::controller::Controller> SecurityCmds for C {}
509
510/// Auto-implemented when the `iso` feature is enabled.
511#[cfg(feature = "iso")]
512pub trait IsoStreamCmds: bt_hci::controller::Controller + ControllerCmdSync<LeSetHostFeature> {}
513
514#[cfg(feature = "iso")]
515impl<C: bt_hci::controller::Controller + ControllerCmdSync<LeSetHostFeature>> IsoStreamCmds for C {}
516
517/// Auto-implemented when `iso` is not enabled.
518#[cfg(not(feature = "iso"))]
519pub trait IsoStreamCmds: bt_hci::controller::Controller {}
520
521#[cfg(not(feature = "iso"))]
522impl<C: bt_hci::controller::Controller> IsoStreamCmds for C {}
523
524/// Auto-implemented when the `subrating` feature is enabled.
525#[cfg(feature = "subrating")]
526pub trait SubratingCmds: bt_hci::controller::Controller + ControllerCmdSync<LeSetHostFeature> {}
527
528#[cfg(feature = "subrating")]
529impl<C: bt_hci::controller::Controller + ControllerCmdSync<LeSetHostFeature>> SubratingCmds for C {}
530
531/// Auto-implemented when subrating is not enabled.
532#[cfg(not(feature = "subrating"))]
533pub trait SubratingCmds: bt_hci::controller::Controller {}
534
535#[cfg(not(feature = "subrating"))]
536impl<C: bt_hci::controller::Controller> SubratingCmds for C {}
537
538/// Auto-implemented when `shorter-connection-intervals` isenabled.
539#[cfg(feature = "shorter-connection-intervals")]
540pub trait ShortConnIntervalCmds: bt_hci::controller::Controller + ControllerCmdSync<LeSetHostFeatureV2> {}
541
542#[cfg(feature = "shorter-connection-intervals")]
543impl<C: bt_hci::controller::Controller + ControllerCmdSync<LeSetHostFeatureV2>> ShortConnIntervalCmds for C {}
544
545/// Auto-implemented when `shorter-connection-intervals` is not enabled.
546#[cfg(not(feature = "shorter-connection-intervals"))]
547pub trait ShortConnIntervalCmds: bt_hci::controller::Controller {}
548
549#[cfg(not(feature = "shorter-connection-intervals"))]
550impl<C: bt_hci::controller::Controller> ShortConnIntervalCmds for C {}
551
552/// Trait that defines the controller implementation required by the host.
553///
554/// The controller must implement the required commands and events to be able to be used with Trouble.
555pub trait Controller:
556    bt_hci::controller::Controller
557    + embedded_io::ErrorType<Error: crate::fmt::Format>
558    + ControllerCmdSync<LeReadBufferSize>
559    + ControllerCmdSync<Disconnect>
560    + ControllerCmdSync<SetEventMask>
561    + ControllerCmdSync<SetEventMaskPage2>
562    + ControllerCmdSync<LeSetEventMask>
563    + ControllerCmdSync<LeSetRandomAddr>
564    + ControllerCmdSync<HostBufferSize>
565    + ControllerCmdAsync<LeConnUpdate>
566    + ControllerCmdSync<LeReadFilterAcceptListSize>
567    + ControllerCmdSync<SetControllerToHostFlowControl>
568    + ControllerCmdSync<Reset>
569    + ControllerCmdSync<ReadRssi>
570    + ControllerCmdSync<LeCreateConnCancel>
571    + ControllerCmdSync<LeSetScanEnable>
572    + ControllerCmdSync<LeSetExtScanEnable>
573    + ControllerCmdAsync<LeCreateConn>
574    + ControllerCmdSync<LeClearFilterAcceptList>
575    + ControllerCmdSync<LeAddDeviceToFilterAcceptList>
576    + for<'t> ControllerCmdSync<LeSetAdvEnable>
577    + for<'t> ControllerCmdSync<LeSetExtAdvEnable<'t>>
578    + for<'t> ControllerCmdSync<HostNumberOfCompletedPackets<'t>>
579    + ControllerCmdSync<LeReadBufferSize>
580    + for<'t> ControllerCmdSync<LeSetAdvData>
581    + ControllerCmdSync<LeSetAdvParams>
582    + for<'t> ControllerCmdSync<LeSetAdvEnable>
583    + for<'t> ControllerCmdSync<LeSetScanResponseData>
584    + ControllerCmdSync<ReadBdAddr>
585    + SecurityCmds
586    + SubratingCmds
587    + IsoStreamCmds
588    + ShortConnIntervalCmds
589{
590}
591
592impl<
593        C: bt_hci::controller::Controller
594            + embedded_io::ErrorType<Error: crate::fmt::Format>
595            + ControllerCmdSync<LeReadBufferSize>
596            + ControllerCmdSync<Disconnect>
597            + ControllerCmdSync<SetEventMask>
598            + ControllerCmdSync<SetEventMaskPage2>
599            + ControllerCmdSync<LeSetEventMask>
600            + ControllerCmdSync<LeSetRandomAddr>
601            + ControllerCmdSync<HostBufferSize>
602            + ControllerCmdAsync<LeConnUpdate>
603            + ControllerCmdSync<LeReadFilterAcceptListSize>
604            + ControllerCmdSync<LeClearFilterAcceptList>
605            + ControllerCmdSync<LeAddDeviceToFilterAcceptList>
606            + ControllerCmdSync<SetControllerToHostFlowControl>
607            + ControllerCmdSync<Reset>
608            + ControllerCmdSync<ReadRssi>
609            + ControllerCmdSync<LeSetScanEnable>
610            + ControllerCmdSync<LeSetExtScanEnable>
611            + ControllerCmdSync<LeCreateConnCancel>
612            + ControllerCmdAsync<LeCreateConn>
613            + for<'t> ControllerCmdSync<LeSetAdvEnable>
614            + for<'t> ControllerCmdSync<LeSetExtAdvEnable<'t>>
615            + for<'t> ControllerCmdSync<HostNumberOfCompletedPackets<'t>>
616            + ControllerCmdSync<LeReadBufferSize>
617            + for<'t> ControllerCmdSync<LeSetAdvData>
618            + ControllerCmdSync<LeSetAdvParams>
619            + for<'t> ControllerCmdSync<LeSetAdvEnable>
620            + for<'t> ControllerCmdSync<LeSetScanResponseData>
621            + ControllerCmdSync<ReadBdAddr>
622            + SecurityCmds
623            + SubratingCmds
624            + IsoStreamCmds
625            + ShortConnIntervalCmds,
626    > Controller for C
627{
628}
629
630/// A Packet is a byte buffer for packet data.
631/// Similar to a `Vec<u8>` it has a length and a capacity.
632pub trait Packet: Sized + AsRef<[u8]> + AsMut<[u8]> {}
633
634/// A Packet Pool that can allocate packets of the desired size.
635///
636/// The MTU is usually related to the MTU of l2cap payloads.
637pub trait PacketPool: 'static {
638    /// Packet type provided by this pool.
639    type Packet: Packet;
640
641    /// The maximum size a packet can have.
642    const MTU: usize;
643
644    /// Allocate a new buffer with space for `MTU` bytes.
645    /// Return `None` when the allocation can't be fulfilled.
646    ///
647    /// This function is called by the L2CAP driver when it needs
648    /// space to receive a packet into.
649    /// It will later call `from_raw_parts` with the buffer and the
650    /// amount of bytes it has received.
651    fn allocate() -> Option<Self::Packet>;
652
653    /// Capacity of this pool in the number of packets.
654    fn capacity() -> usize;
655}
656
657/// HostResources holds the resources used by the host.
658///
659/// The l2cap packet pool is used by the host to handle inbound data, by allocating space for
660/// incoming packets and dispatching to the appropriate connection and channel.
661pub struct HostResources<
662    P: PacketPool,
663    const CONNS: usize,
664    const CHANNELS: usize,
665    const ADV_SETS: usize = 1,
666    const BONDS: usize = 10,
667> {
668    state: MaybeUninit<ManuallyDrop<host::HostState<'static, P>>>,
669    connections: MaybeUninit<RefCell<[ConnectionStorage<P::Packet>; CONNS]>>,
670    channels: MaybeUninit<RefCell<[ChannelStorage<P::Packet>; CHANNELS]>>,
671    advertise_handles: MaybeUninit<RefCell<[AdvHandleState; ADV_SETS]>>,
672    #[cfg(feature = "security")]
673    bond_storage: MaybeUninit<RefCell<Vec<BondInformation, BONDS>>>,
674}
675
676impl<P: PacketPool, const CONNS: usize, const CHANNELS: usize, const ADV_SETS: usize, const BONDS: usize> Default
677    for HostResources<P, CONNS, CHANNELS, ADV_SETS, BONDS>
678{
679    fn default() -> Self {
680        Self::new()
681    }
682}
683
684impl<P: PacketPool, const CONNS: usize, const CHANNELS: usize, const ADV_SETS: usize, const BONDS: usize>
685    HostResources<P, CONNS, CHANNELS, ADV_SETS, BONDS>
686{
687    /// Create a new instance of host resources.
688    pub const fn new() -> Self {
689        Self {
690            state: MaybeUninit::uninit(),
691            connections: MaybeUninit::uninit(),
692            channels: MaybeUninit::uninit(),
693            advertise_handles: MaybeUninit::uninit(),
694            #[cfg(feature = "security")]
695            bond_storage: MaybeUninit::uninit(),
696        }
697    }
698}
699
700/// Create a new instance of the BLE host using the provided controller implementation and
701/// the resource configuration
702pub fn new<
703    'resources,
704    C: Controller,
705    P: PacketPool,
706    const CONNS: usize,
707    const CHANNELS: usize,
708    const ADV_SETS: usize,
709    const BONDS: usize,
710>(
711    controller: C,
712    resources: &'resources mut HostResources<P, CONNS, CHANNELS, ADV_SETS, BONDS>,
713) -> StackBuilder<'resources, C, P> {
714    let connections: &'resources RefCell<[ConnectionStorage<P::Packet>]> = resources
715        .connections
716        .write(RefCell::new([const { ConnectionStorage::new() }; CONNS]));
717
718    let channels: &'resources RefCell<[ChannelStorage<P::Packet>]> = resources
719        .channels
720        .write(RefCell::new([const { ChannelStorage::new() }; CHANNELS]));
721
722    let advertise_handles: &'resources RefCell<[AdvHandleState]> = resources
723        .advertise_handles
724        .write(RefCell::new([AdvHandleState::None; ADV_SETS]));
725
726    #[cfg(feature = "security")]
727    let bond_storage: &'resources RefCell<VecView<BondInformation>> =
728        resources.bond_storage.write(RefCell::new(Vec::new()));
729
730    // SAFETY: Narrows the host field's lifetime from `'static` to `'resources`. Sound because:
731    // - HostState is covariant in 'd so the types differ only in a lifetime (identical layout).
732    // - The returned StackBuilder/Stack exclusively borrows the HostResources for 'resources,
733    //   preventing re-entry into this function while the narrowed-lifetime data is live.
734    // - The `state` field is private, MaybeUninit (no auto-drop), and only ever accessed by this
735    //   function (which overwrites via write()), so the narrowed lifetime can never be observed
736    //   through the original `'static` type — even if the StackBuilder/Stack is forgotten.
737    let host_state: &'resources mut MaybeUninit<ManuallyDrop<host::HostState<'resources, P>>> =
738        unsafe { core::mem::transmute(&mut resources.state) };
739
740    let host_state: &'resources mut ManuallyDrop<host::HostState<'resources, P>> =
741        host_state.write(ManuallyDrop::new(host::HostState::new(
742            connections,
743            channels,
744            advertise_handles,
745            #[cfg(feature = "security")]
746            bond_storage,
747        )));
748
749    StackBuilder {
750        host_state: Some(host_state),
751        controller: Some(controller),
752    }
753}
754
755/// Contains the host stack
756pub struct Stack<'stack, C, P: PacketPool> {
757    host_state: &'stack mut ManuallyDrop<host::HostState<'stack, P>>,
758    controller: C,
759    runner_taken: Cell<bool>,
760}
761
762impl<'stack, C, P: PacketPool> Drop for Stack<'stack, C, P> {
763    fn drop(&mut self) {
764        // SAFETY: host was fully initialized in new() and has not been dropped.
765        // Stack is the sole owner responsible for dropping BleHost.
766        // All shared &HostState references (in Runner, Central, etc.) have already
767        // been dropped (reverse drop order), so no aliasing conflict.
768        unsafe { ManuallyDrop::drop(self.host_state) }
769    }
770}
771
772/// Builder for configuring the BLE stack before use.
773///
774/// Call [`build()`](StackBuilder::build) to finalize configuration and obtain the [`Stack`].
775pub struct StackBuilder<'stack, C, P: PacketPool> {
776    pub(crate) host_state: Option<&'stack mut ManuallyDrop<host::HostState<'stack, P>>>,
777    controller: Option<C>,
778}
779
780impl<'stack, C, P: PacketPool> Drop for StackBuilder<'stack, C, P> {
781    fn drop(&mut self) {
782        if let Some(host_state) = &mut self.host_state {
783            // SAFETY: host was fully initialized in new() and has not been dropped.
784            // `build()` was never called, leaving StackBuilder as the sole owner
785            // responsible for dropping BleHost.
786            unsafe { ManuallyDrop::drop(host_state) }
787        }
788    }
789}
790
791impl<'stack, C: Controller, P: PacketPool> StackBuilder<'stack, C, P> {
792    fn host_state(&mut self) -> &mut host::HostState<'stack, P> {
793        self.host_state.as_mut().unwrap()
794    }
795
796    /// Register an L2CAP SPSM (Simplified Protocol/Service Multiplexer) for accepting incoming connections.
797    pub fn register_l2cap_spsm(mut self, spsm: u16) -> Self {
798        self.host_state().channels.register_spsm(spsm);
799        self
800    }
801
802    /// Set the random address used by this host.
803    pub fn set_random_address(mut self, address: Address) -> Self {
804        self.host_state().address.replace(address);
805        #[cfg(feature = "security")]
806        self.host_state()
807            .connections
808            .security_manager
809            .set_local_address(address);
810        self
811    }
812
813    /// Enable BLE address privacy with the given Identity Resolving Key (IRK).
814    ///
815    /// When privacy is enabled, Resolvable Private Addresses (RPAs) are generated
816    /// that rotate periodically, preventing device tracking while allowing bonded peers to
817    /// resolve the device's identity.
818    ///
819    /// The IRK should be persisted across reboots so bonded peers can continue to resolve
820    /// our RPAs. Generate a new IRK using a CSPRNG for first-time setup.
821    ///
822    /// After bonds are added or removed (either directly or via pairing), the controller's
823    /// resolving list is updated automatically the next time advertising, scanning, and
824    /// connecting are all idle. Applications should ensure periodic idle windows to allow
825    /// resolving list updates to take effect.
826    #[cfg(feature = "security")]
827    pub fn enable_privacy(mut self, irk: IdentityResolvingKey) -> Self {
828        self.host_state().connections.security_manager.set_local_irk(irk);
829        self
830    }
831
832    /// Set the RPA (Resolvable Private Address) rotation timeout.
833    ///
834    /// New RPAs will be generated after this duration. Note that host generated RPAs
835    /// (used for active scanning of and connecting to unbonded devices) will only rotate
836    /// when scanning, connection initiation, and legacy advertising are all idle.
837    ///
838    /// Default is 900 seconds (15 minutes) per the BLE specification.
839    #[cfg(feature = "security")]
840    pub fn set_rpa_timeout(mut self, timeout: Duration) -> Self {
841        self.host_state().rpa_timeout.set(timeout);
842        self
843    }
844
845    /// Set the IO capabilities used by the security manager.
846    ///
847    /// Only relevant if the feature `security` is enabled.
848    #[cfg(feature = "security")]
849    pub fn set_io_capabilities(mut self, io_capabilities: IoCapabilities) -> Self {
850        self.host_state()
851            .connections
852            .security_manager
853            .set_io_capabilities(io_capabilities);
854        self
855    }
856
857    /// Set a fixed passkey to use for PassKey Entry pairing (DisplayOnly).
858    ///
859    /// When set, this passkey will be displayed to the user instead of a randomly generated one.
860    ///
861    /// Set to `None` to return to random passkey generation (the default).
862    #[cfg(feature = "security")]
863    pub fn set_passkey(mut self, passkey: Option<u32>) -> Self {
864        self.host_state().connections.security_manager.set_passkey(passkey);
865        self
866    }
867
868    /// Enable or disable secure connections only mode.
869    ///
870    /// When enabled, legacy pairing is rejected even if the `legacy-pairing` feature is compiled in.
871    /// This matches the BLE spec's "Secure Connections Only Mode" (Vol 3, Part C, Section 10.2.4).
872    ///
873    /// Only relevant if the feature `legacy-pairing` is enabled.
874    #[cfg(feature = "legacy-pairing")]
875    pub fn set_secure_connections_only(mut self, enabled: bool) -> Self {
876        self.host_state()
877            .connections
878            .security_manager
879            .set_secure_connections_only(enabled);
880        self
881    }
882
883    /// Finalize configuration and return the stack.
884    ///
885    /// Use the returned [`Stack`] for runtime operations: obtain a [`Runner`] via
886    /// [`Stack::runner()`], and [`Central`](central::Central) or
887    /// [`Peripheral`](peripheral::Peripheral) handles via [`Stack::central()`] and
888    /// [`Stack::peripheral()`].
889    pub fn build(mut self) -> Stack<'stack, C, P> {
890        Stack {
891            host_state: self.host_state.take().unwrap(),
892            controller: self.controller.take().unwrap(),
893            runner_taken: Cell::new(false),
894        }
895    }
896}
897
898impl<'stack, C, P: PacketPool> Stack<'stack, C, P> {
899    fn host(&self) -> BleHost<'_, C, P> {
900        BleHost::new(&self.controller, self.host_state)
901    }
902}
903
904impl<'stack, C: Controller, P: PacketPool> Stack<'stack, C, P> {
905    /// Obtain a [`Runner`] to drive the BLE host.
906    ///
907    /// The runner must be polled (e.g. via [`Runner::run()`]) to drive the BLE host.
908    pub fn runner(&self) -> Runner<'_, C, P> {
909        assert!(
910            !self.runner_taken.replace(true),
911            "runner() can only be called once per Stack"
912        );
913        Runner::new(self.host())
914    }
915
916    /// Obtain a [`Central`](central::Central) handle for the central BLE role.
917    ///
918    /// This is a lightweight handle that can be created multiple times.
919    /// Concurrent connect operations are serialized internally.
920    #[cfg(feature = "central")]
921    pub fn central(&self) -> Central<'_, C, P> {
922        Central::new(self.host())
923    }
924
925    /// Obtain a [`Peripheral`](peripheral::Peripheral) handle for the peripheral BLE role.
926    ///
927    /// This is a lightweight handle that can be created multiple times.
928    /// Concurrent advertise operations are serialized internally.
929    #[cfg(feature = "peripheral")]
930    pub fn peripheral(&self) -> Peripheral<'_, C, P> {
931        Peripheral::new(self.host())
932    }
933
934    /// Obtain an [`Iso`](iso::Iso) handle for isochronous-stream (CIS/BIS) HCI commands and data.
935    ///
936    /// This is a lightweight handle that can be created multiple times.
937    #[cfg(feature = "iso")]
938    pub fn iso(&self) -> iso::Iso<'_, C, P> {
939        iso::Iso::new(self.host())
940    }
941
942    /// Set the IO capabilities used by the security manager.
943    ///
944    /// Only relevant if the feature `security` is enabled.
945    #[cfg(feature = "security")]
946    pub fn set_io_capabilities(&self, io_capabilities: IoCapabilities) {
947        self.host_state
948            .connections
949            .security_manager
950            .set_io_capabilities(io_capabilities);
951    }
952
953    /// Set a fixed passkey to use for PassKey Entry pairing (DisplayOnly).
954    ///
955    /// When set, this passkey will be displayed to the user instead of a randomly generated one.
956    ///
957    /// Set to `None` to return to random passkey generation (the default).
958    #[cfg(feature = "security")]
959    pub fn set_passkey(&self, passkey: Option<u32>) {
960        self.host_state.connections.security_manager.set_passkey(passkey);
961    }
962
963    /// Enable or disable secure connections only mode.
964    ///
965    /// When enabled, legacy pairing is rejected even if the `legacy-pairing` feature is compiled in.
966    /// This matches the BLE spec's "Secure Connections Only Mode" (Vol 3, Part C, Section 10.2.4).
967    ///
968    /// Only relevant if the feature `legacy-pairing` is enabled.
969    #[cfg(feature = "legacy-pairing")]
970    pub fn set_secure_connections_only(&self, enabled: bool) {
971        self.host_state
972            .connections
973            .security_manager
974            .set_secure_connections_only(enabled);
975    }
976
977    /// Set the RPA (Resolvable Private Address) rotation timeout.
978    ///
979    /// Updates the stored timeout. If the host is already initialized, also sends
980    /// the `LeSetResolvablePrivateAddrTimeout` HCI command to the controller.
981    /// If called before initialization (e.g. during pre-server setup), the value
982    /// will be used when the controller is initialized.
983    ///
984    /// Valid range is 1s to 3600s.
985    #[cfg(feature = "security")]
986    pub async fn set_rpa_timeout(&self, timeout: Duration) -> Result<(), BleHostError<C::Error>>
987    where
988        C: ControllerCmdSync<LeSetResolvablePrivateAddrTimeout>,
989    {
990        self.host().rpa_timeout().set(timeout);
991        if self.host().is_initialized() {
992            self.host()
993                .command(LeSetResolvablePrivateAddrTimeout::new(
994                    bt_hci::param::Duration::from_secs(timeout.as_secs() as u32),
995                ))
996                .await?;
997        }
998        Ok(())
999    }
1000
1001    /// Run a HCI command and return the response.
1002    pub async fn command<T>(&self, cmd: T) -> Result<T::Return, BleHostError<C::Error>>
1003    where
1004        T: SyncCmd,
1005        C: ControllerCmdSync<T>,
1006    {
1007        self.host().command(cmd).await
1008    }
1009
1010    /// Run an async HCI command where the response will generate an event later.
1011    pub async fn async_command<T>(&self, cmd: T) -> Result<(), BleHostError<C::Error>>
1012    where
1013        T: AsyncCmd,
1014        C: ControllerCmdAsync<T>,
1015    {
1016        self.host().async_command(cmd).await
1017    }
1018
1019    /// Read the minimum supported connection interval from the controller.
1020    pub async fn read_minimum_supported_connection_interval(
1021        &self,
1022    ) -> Result<<LeReadMinimumSupportedConnectionInterval as SyncCmd>::Return, BleHostError<C::Error>>
1023    where
1024        C: ControllerCmdSync<LeReadMinimumSupportedConnectionInterval>,
1025    {
1026        self.host()
1027            .command(LeReadMinimumSupportedConnectionInterval::new())
1028            .await
1029    }
1030
1031    /// Read current host metrics
1032    pub fn metrics<F: FnOnce(&HostMetrics) -> R, R>(&self, f: F) -> R {
1033        self.host().metrics(f)
1034    }
1035
1036    /// Log status information of the host
1037    pub fn log_status(&self, verbose: bool) {
1038        self.host().log_status(verbose);
1039    }
1040
1041    #[cfg(feature = "security")]
1042    /// Generate local OOB data for LESC pairing.
1043    ///
1044    /// The returned data should be transferred to the peer device via an out-of-band
1045    /// channel (NFC, QR code, etc.) before pairing begins.
1046    pub fn get_local_oob_data(&self) -> OobData {
1047        self.host_state.connections.security_manager.get_local_oob_data()
1048    }
1049
1050    #[cfg(feature = "security")]
1051    /// Get the local address configured on the security manager.
1052    pub fn get_local_address(&self) -> Option<Address> {
1053        self.host_state.connections.security_manager.get_local_address()
1054    }
1055
1056    /// Check whether BLE address privacy is enabled.
1057    #[cfg(feature = "security")]
1058    pub fn is_privacy_enabled(&self) -> bool {
1059        self.host().is_privacy_enabled()
1060    }
1061
1062    #[cfg(feature = "security")]
1063    /// Add bond information for a peer device.
1064    ///
1065    /// After bonds are added or removed (either directly or via pairing), the controller's
1066    /// resolving list is updated automatically the next time advertising, scanning, and
1067    /// connecting are all idle. Applications should ensure periodic idle windows to allow
1068    /// resolving list updates to take effect.
1069    pub fn add_bond_information(&self, bond_information: BondInformation) -> Result<(), Error> {
1070        let identity = bond_information.identity;
1071        let result = self
1072            .host()
1073            .connections()
1074            .security_manager
1075            .add_bond_information(bond_information);
1076        #[cfg(feature = "security")]
1077        if result.is_ok() {
1078            self.host()
1079                .resolving_list_state()
1080                .borrow_mut()
1081                .push(crate::host::ResolvingListUpdate::Add(identity));
1082        }
1083        result
1084    }
1085
1086    #[cfg(feature = "security")]
1087    /// Remove a bonded device.
1088    ///
1089    /// After bonds are added or removed (either directly or via pairing), the controller's
1090    /// resolving list is updated automatically the next time advertising, scanning, and
1091    /// connecting are all idle. Applications should ensure periodic idle windows to allow
1092    /// resolving list updates to take effect.
1093    pub fn remove_bond_information(&self, identity: Identity) -> Result<(), Error> {
1094        let result = self
1095            .host()
1096            .connections()
1097            .security_manager
1098            .remove_bond_information(identity);
1099        #[cfg(feature = "security")]
1100        if result.is_ok() {
1101            self.host()
1102                .resolving_list_state()
1103                .borrow_mut()
1104                .push(crate::host::ResolvingListUpdate::Remove(identity));
1105        }
1106        result
1107    }
1108
1109    #[cfg(feature = "security")]
1110    /// Access bonded devices
1111    pub fn with_bond_information<R>(&self, f: impl FnOnce(&[BondInformation]) -> R) -> R {
1112        f(&self.host_state.connections.security_manager.get_bond_information())
1113    }
1114
1115    /// Get a connection by its peer address
1116    pub fn get_connection_by_peer_address(&self, peer_address: Address) -> Option<Connection<'_, P>> {
1117        self.host_state.connections.get_connection_by_peer_address(peer_address)
1118    }
1119
1120    /// Get a connection by its handle
1121    pub fn get_connected_handle(&self, handle: ConnHandle) -> Option<Connection<'_, P>> {
1122        self.host_state.connections.get_connected_handle(handle)
1123    }
1124
1125    /// Iterate over all currently connected connections.
1126    pub fn connections(&self) -> connection_manager::ConnectedIter<'_, P> {
1127        self.host_state.connections.connections()
1128    }
1129}
1130
1131pub(crate) fn bt_hci_duration<const US: u32>(d: Duration) -> bt_hci::param::Duration<US> {
1132    bt_hci::param::Duration::from_micros(d.as_micros())
1133}
1134
1135pub(crate) fn bt_hci_ext_duration<const US: u16>(d: Duration) -> bt_hci::param::ExtDuration<US> {
1136    bt_hci::param::ExtDuration::from_micros(d.as_micros())
1137}
1138
1139// Re-export our version of embassy-sync for the macros
1140#[doc(hidden)]
1141pub mod __export {
1142    pub use embassy_sync;
1143}