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