#![no_std]
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(clippy::needless_lifetimes)]
#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
#![warn(missing_docs)]
use core::cell::{Cell, RefCell};
use core::mem::{ManuallyDrop, MaybeUninit};
use advertise::AdvertisementDataError;
use bt_hci::cmd::le::LeReadMinimumSupportedConnectionInterval;
use bt_hci::cmd::status::ReadRssi;
use bt_hci::cmd::{AsyncCmd, SyncCmd};
use bt_hci::param::{AddrKind, BdAddr, ConnHandle};
use bt_hci::FromHciBytesError;
use embassy_time::Duration;
#[cfg(feature = "security")]
use heapless::{Vec, VecView};
use crate::att::AttErrorCode;
use crate::channel_manager::ChannelStorage;
use crate::connection::Connection;
use crate::connection_manager::ConnectionStorage;
#[cfg(feature = "security")]
pub use crate::security_manager::{
BondInformation, IdentityResolvingKey, LongTermKey, OobData, Reason as PairingFailedReason,
};
pub use crate::types::capabilities::IoCapabilities;
mod fmt;
#[cfg(not(any(feature = "central", feature = "peripheral")))]
compile_error!("Must enable at least one of the `central` or `peripheral` features");
pub mod att;
#[cfg(feature = "central")]
pub mod central;
mod channel_manager;
mod codec;
mod command;
pub mod config;
mod connection_manager;
mod cursor;
#[cfg(feature = "default-packet-pool")]
mod packet_pool;
mod pdu;
#[cfg(feature = "peripheral")]
pub mod peripheral;
#[cfg(feature = "security")]
mod security_manager;
pub mod types;
#[cfg(feature = "central")]
use central::*;
#[cfg(feature = "peripheral")]
use peripheral::*;
pub mod advertise;
pub mod connection;
#[cfg(feature = "gatt")]
pub mod gap;
pub mod l2cap;
#[cfg(feature = "scan")]
pub mod scan;
#[cfg(test)]
pub(crate) mod mock_controller;
pub(crate) mod host;
use host::{AdvHandleState, BleHost, HostMetrics, Runner};
pub mod prelude {
pub use bt_hci::controller::ExternalController;
pub use bt_hci::param::{AddrKind, BdAddr, LeConnRole as Role, PhyKind, PhyMask};
pub use bt_hci::transport::SerialTransport;
pub use bt_hci::uuid::*;
#[cfg(feature = "derive")]
pub use heapless::String as HeaplessString;
#[cfg(feature = "derive")]
pub use trouble_host_macros::*;
pub use super::att::AttErrorCode;
pub use super::{BleHostError, Controller, Error, HostResources, Packet, PacketPool, Stack, StackBuilder};
#[cfg(feature = "peripheral")]
pub use crate::advertise::*;
#[cfg(feature = "gatt")]
pub use crate::attribute::*;
#[cfg(feature = "gatt")]
pub use crate::attribute_server::*;
#[cfg(feature = "central")]
pub use crate::central::*;
pub use crate::connection::{ConnectRateParams, *};
#[cfg(feature = "gatt")]
pub use crate::gap::*;
#[cfg(feature = "gatt")]
pub use crate::gatt::*;
pub use crate::host::{ControlRunner, EventHandler, HostMetrics, Runner, RxRunner, TxRunner};
pub use crate::l2cap::*;
#[cfg(feature = "default-packet-pool")]
pub use crate::packet_pool::DefaultPacketPool;
pub use crate::pdu::Sdu;
#[cfg(feature = "peripheral")]
pub use crate::peripheral::*;
#[cfg(feature = "scan")]
pub use crate::scan::*;
#[cfg(feature = "security")]
pub use crate::security_manager::{
BondInformation, IdentityResolvingKey, LongTermKey, OobData, Reason as PairingFailedReason,
};
pub use crate::types::capabilities::IoCapabilities;
#[cfg(feature = "gatt")]
pub use crate::types::gatt_traits::{AsGatt, FixedGattValue, FromGatt};
pub use crate::{Address, Identity};
}
#[cfg(feature = "gatt")]
pub mod attribute;
#[cfg(feature = "gatt")]
mod attribute_server;
#[cfg(feature = "gatt")]
pub mod gatt;
#[derive(Debug, Clone, Copy, Default, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Address {
pub kind: AddrKind,
pub addr: BdAddr,
}
impl PartialEq for Address {
fn eq(&self, other: &Self) -> bool {
self.addr == other.addr && self.kind.as_raw() & 1 == other.kind.as_raw() & 1
}
}
impl Address {
pub const fn new(kind: AddrKind, addr: BdAddr) -> Self {
Self { kind, addr }
}
pub fn random(val: [u8; 6]) -> Self {
Self {
kind: AddrKind::RANDOM,
addr: BdAddr::new(val),
}
}
pub fn to_bytes(&self) -> [u8; 7] {
let mut bytes = [0; 7];
bytes[0] = self.kind.into_inner();
let mut addr_bytes = self.addr.into_inner();
addr_bytes.reverse();
bytes[1..].copy_from_slice(&addr_bytes);
bytes
}
}
impl core::fmt::Display for Address {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let a = self.addr.into_inner();
write!(
f,
"{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
a[5], a[4], a[3], a[2], a[1], a[0]
)
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for Address {
fn format(&self, fmt: defmt::Formatter) {
let a = self.addr.into_inner();
defmt::write!(
fmt,
"{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
a[5],
a[4],
a[3],
a[2],
a[1],
a[0]
)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Identity {
pub addr: Address,
#[cfg(feature = "security")]
pub irk: Option<IdentityResolvingKey>,
}
#[cfg(feature = "defmt")]
impl defmt::Format for Identity {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "Addr({}) ", self.addr);
#[cfg(feature = "security")]
defmt::write!(fmt, "Irk({:X})", self.irk);
}
}
impl From<Address> for Identity {
fn from(addr: Address) -> Self {
Self {
addr,
#[cfg(feature = "security")]
irk: None,
}
}
}
impl Identity {
pub fn match_address(&self, address: &Address) -> bool {
if self.addr == *address {
return true;
}
#[cfg(feature = "security")]
if let Some(irk) = self.irk {
return irk.resolve_address(&address.addr);
}
false
}
pub fn match_identity(&self, identity: &Identity) -> bool {
if self.addr == identity.addr {
return true;
}
#[cfg(feature = "security")]
{
if let Some(irk) = self.irk {
if irk.resolve_address(&identity.addr.addr) {
return true;
}
}
if let Some(irk) = identity.irk {
if let Some(current_irk) = self.irk {
if irk == current_irk {
return true;
}
}
if irk.resolve_address(&self.addr.addr) {
return true;
}
}
}
false
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BleHostError<E> {
Controller(E),
BleHost(Error),
}
pub const MAX_INVALID_DATA_LEN: usize = 16;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
Hci(bt_hci::param::Error),
HciDecode(FromHciBytesError),
Att(AttErrorCode),
#[cfg(feature = "security")]
Security(PairingFailedReason),
InsufficientSpace,
InvalidValue,
UnexpectedDataLength {
expected: usize,
actual: usize,
},
CannotConstructGattValue([u8; MAX_INVALID_DATA_LEN]),
ConfigFilterAcceptListIsEmpty,
UnexpectedGattResponse,
MalformedCharacteristicDeclaration {
expected: usize,
actual: usize,
},
InvalidCharacteristicDeclarationData,
FailedToFinalize {
expected: usize,
actual: usize,
},
CodecError(codec::Error),
ExtendedAdvertisingNotSupported,
InvalidUuidLength(usize),
Advertisement(AdvertisementDataError),
L2capConnectError(crate::types::l2cap::LeCreditConnResultCode),
InvalidChannelId,
NoChannelAvailable,
NotFound,
InvalidState,
OutOfMemory,
NotSupported,
ChannelClosed,
Timeout,
Busy,
NoPermits,
Disconnected,
ConnectionLimitReached,
GattSubscriberLimitReached,
AlreadyInUse,
Other,
}
impl<E> From<Error> for BleHostError<E> {
fn from(value: Error) -> Self {
Self::BleHost(value)
}
}
impl From<FromHciBytesError> for Error {
fn from(error: FromHciBytesError) -> Self {
Self::HciDecode(error)
}
}
impl From<AttErrorCode> for Error {
fn from(error: AttErrorCode) -> Self {
Self::Att(error)
}
}
impl<E> From<bt_hci::cmd::Error<E>> for BleHostError<E> {
fn from(error: bt_hci::cmd::Error<E>) -> Self {
match error {
bt_hci::cmd::Error::Hci(p) => Self::BleHost(Error::Hci(p)),
bt_hci::cmd::Error::Io(p) => Self::Controller(p),
}
}
}
impl<E> From<bt_hci::param::Error> for BleHostError<E> {
fn from(error: bt_hci::param::Error) -> Self {
Self::BleHost(Error::Hci(error))
}
}
impl From<codec::Error> for Error {
fn from(error: codec::Error) -> Self {
match error {
codec::Error::InsufficientSpace => Error::InsufficientSpace,
codec::Error::InvalidValue => Error::CodecError(error),
}
}
}
impl<E> From<codec::Error> for BleHostError<E> {
fn from(error: codec::Error) -> Self {
match error {
codec::Error::InsufficientSpace => BleHostError::BleHost(Error::InsufficientSpace),
codec::Error::InvalidValue => BleHostError::BleHost(Error::InvalidValue),
}
}
}
use bt_hci::cmd::controller_baseband::*;
use bt_hci::cmd::info::*;
use bt_hci::cmd::le::*;
use bt_hci::cmd::link_control::*;
use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync};
#[cfg(feature = "security")]
pub trait SecurityCmds:
bt_hci::controller::Controller
+ ControllerCmdSync<LeLongTermKeyRequestReply>
+ ControllerCmdAsync<LeEnableEncryption>
+ ControllerCmdSync<LeAddDeviceToResolvingList>
+ ControllerCmdSync<LeRemoveDeviceFromResolvingList>
+ ControllerCmdSync<LeClearResolvingList>
+ ControllerCmdSync<LeSetAddrResolutionEnable>
+ ControllerCmdSync<LeSetResolvablePrivateAddrTimeout>
+ ControllerCmdSync<LeSetPrivacyMode>
+ ControllerCmdSync<LeRand>
{
}
#[cfg(feature = "security")]
impl<
C: bt_hci::controller::Controller
+ ControllerCmdSync<LeLongTermKeyRequestReply>
+ ControllerCmdAsync<LeEnableEncryption>
+ ControllerCmdSync<LeAddDeviceToResolvingList>
+ ControllerCmdSync<LeRemoveDeviceFromResolvingList>
+ ControllerCmdSync<LeClearResolvingList>
+ ControllerCmdSync<LeSetAddrResolutionEnable>
+ ControllerCmdSync<LeSetResolvablePrivateAddrTimeout>
+ ControllerCmdSync<LeSetPrivacyMode>
+ ControllerCmdSync<LeRand>,
> SecurityCmds for C
{
}
#[cfg(not(feature = "security"))]
pub trait SecurityCmds: bt_hci::controller::Controller {}
#[cfg(not(feature = "security"))]
impl<C: bt_hci::controller::Controller> SecurityCmds for C {}
pub trait Controller:
bt_hci::controller::Controller
+ embedded_io::ErrorType<Error: crate::fmt::Format>
+ ControllerCmdSync<LeReadBufferSize>
+ ControllerCmdSync<Disconnect>
+ ControllerCmdSync<SetEventMask>
+ ControllerCmdSync<SetEventMaskPage2>
+ ControllerCmdSync<LeSetEventMask>
+ ControllerCmdSync<LeSetRandomAddr>
+ ControllerCmdSync<HostBufferSize>
+ ControllerCmdAsync<LeConnUpdate>
+ ControllerCmdSync<LeReadFilterAcceptListSize>
+ ControllerCmdSync<SetControllerToHostFlowControl>
+ ControllerCmdSync<Reset>
+ ControllerCmdSync<ReadRssi>
+ ControllerCmdSync<LeCreateConnCancel>
+ ControllerCmdSync<LeSetScanEnable>
+ ControllerCmdSync<LeSetExtScanEnable>
+ ControllerCmdAsync<LeCreateConn>
+ ControllerCmdSync<LeClearFilterAcceptList>
+ ControllerCmdSync<LeAddDeviceToFilterAcceptList>
+ for<'t> ControllerCmdSync<LeSetAdvEnable>
+ for<'t> ControllerCmdSync<LeSetExtAdvEnable<'t>>
+ for<'t> ControllerCmdSync<HostNumberOfCompletedPackets<'t>>
+ ControllerCmdSync<LeReadBufferSize>
+ for<'t> ControllerCmdSync<LeSetAdvData>
+ ControllerCmdSync<LeSetAdvParams>
+ for<'t> ControllerCmdSync<LeSetAdvEnable>
+ for<'t> ControllerCmdSync<LeSetScanResponseData>
+ ControllerCmdSync<ReadBdAddr>
+ SecurityCmds
{
}
impl<
C: bt_hci::controller::Controller
+ embedded_io::ErrorType<Error: crate::fmt::Format>
+ ControllerCmdSync<LeReadBufferSize>
+ ControllerCmdSync<Disconnect>
+ ControllerCmdSync<SetEventMask>
+ ControllerCmdSync<SetEventMaskPage2>
+ ControllerCmdSync<LeSetEventMask>
+ ControllerCmdSync<LeSetRandomAddr>
+ ControllerCmdSync<HostBufferSize>
+ ControllerCmdAsync<LeConnUpdate>
+ ControllerCmdSync<LeReadFilterAcceptListSize>
+ ControllerCmdSync<LeClearFilterAcceptList>
+ ControllerCmdSync<LeAddDeviceToFilterAcceptList>
+ ControllerCmdSync<SetControllerToHostFlowControl>
+ ControllerCmdSync<Reset>
+ ControllerCmdSync<ReadRssi>
+ ControllerCmdSync<LeSetScanEnable>
+ ControllerCmdSync<LeSetExtScanEnable>
+ ControllerCmdSync<LeCreateConnCancel>
+ ControllerCmdAsync<LeCreateConn>
+ for<'t> ControllerCmdSync<LeSetAdvEnable>
+ for<'t> ControllerCmdSync<LeSetExtAdvEnable<'t>>
+ for<'t> ControllerCmdSync<HostNumberOfCompletedPackets<'t>>
+ ControllerCmdSync<LeReadBufferSize>
+ for<'t> ControllerCmdSync<LeSetAdvData>
+ ControllerCmdSync<LeSetAdvParams>
+ for<'t> ControllerCmdSync<LeSetAdvEnable>
+ for<'t> ControllerCmdSync<LeSetScanResponseData>
+ ControllerCmdSync<ReadBdAddr>
+ SecurityCmds,
> Controller for C
{
}
pub trait Packet: Sized + AsRef<[u8]> + AsMut<[u8]> {}
pub trait PacketPool: 'static {
type Packet: Packet;
const MTU: usize;
fn allocate() -> Option<Self::Packet>;
fn capacity() -> usize;
}
pub struct HostResources<
C: Controller,
P: PacketPool,
const CONNS: usize,
const CHANNELS: usize,
const ADV_SETS: usize = 1,
const BONDS: usize = 10,
> {
host: MaybeUninit<ManuallyDrop<BleHost<'static, C, P>>>,
connections: MaybeUninit<RefCell<[ConnectionStorage<P::Packet>; CONNS]>>,
channels: MaybeUninit<RefCell<[ChannelStorage<P::Packet>; CHANNELS]>>,
advertise_handles: MaybeUninit<RefCell<[AdvHandleState; ADV_SETS]>>,
#[cfg(feature = "security")]
bond_storage: MaybeUninit<RefCell<Vec<BondInformation, BONDS>>>,
}
impl<
C: Controller,
P: PacketPool,
const CONNS: usize,
const CHANNELS: usize,
const ADV_SETS: usize,
const BONDS: usize,
> Default for HostResources<C, P, CONNS, CHANNELS, ADV_SETS, BONDS>
{
fn default() -> Self {
Self::new()
}
}
impl<
C: Controller,
P: PacketPool,
const CONNS: usize,
const CHANNELS: usize,
const ADV_SETS: usize,
const BONDS: usize,
> HostResources<C, P, CONNS, CHANNELS, ADV_SETS, BONDS>
{
pub const fn new() -> Self {
Self {
host: MaybeUninit::uninit(),
connections: MaybeUninit::uninit(),
channels: MaybeUninit::uninit(),
advertise_handles: MaybeUninit::uninit(),
#[cfg(feature = "security")]
bond_storage: MaybeUninit::uninit(),
}
}
}
pub fn new<
'resources,
C: Controller,
P: PacketPool,
const CONNS: usize,
const CHANNELS: usize,
const ADV_SETS: usize,
const BONDS: usize,
>(
controller: C,
resources: &'resources mut HostResources<C, P, CONNS, CHANNELS, ADV_SETS, BONDS>,
) -> StackBuilder<'resources, C, P> {
let connections: &'resources RefCell<[ConnectionStorage<P::Packet>]> = resources
.connections
.write(RefCell::new([const { ConnectionStorage::new() }; CONNS]));
let channels: &'resources RefCell<[ChannelStorage<P::Packet>]> = resources
.channels
.write(RefCell::new([const { ChannelStorage::new() }; CHANNELS]));
let advertise_handles: &'resources RefCell<[AdvHandleState]> = resources
.advertise_handles
.write(RefCell::new([AdvHandleState::None; ADV_SETS]));
#[cfg(feature = "security")]
let bond_storage: &'resources RefCell<VecView<BondInformation>> =
resources.bond_storage.write(RefCell::new(Vec::new()));
let host: &'resources mut MaybeUninit<ManuallyDrop<BleHost<'resources, C, P>>> =
unsafe { core::mem::transmute(&mut resources.host) };
let host: &'resources mut ManuallyDrop<BleHost<'resources, C, P>> = host.write(ManuallyDrop::new(BleHost::new(
controller,
connections,
channels,
advertise_handles,
#[cfg(feature = "security")]
bond_storage,
)));
StackBuilder { host: Some(host) }
}
pub struct Stack<'stack, C, P: PacketPool> {
host: &'stack mut ManuallyDrop<BleHost<'stack, C, P>>,
runner_taken: Cell<bool>,
}
impl<'stack, C, P: PacketPool> Drop for Stack<'stack, C, P> {
fn drop(&mut self) {
unsafe { ManuallyDrop::drop(self.host) }
}
}
pub struct StackBuilder<'stack, C, P: PacketPool> {
host: Option<&'stack mut ManuallyDrop<BleHost<'stack, C, P>>>,
}
impl<'stack, C, P: PacketPool> Drop for StackBuilder<'stack, C, P> {
fn drop(&mut self) {
if let Some(host) = &mut self.host {
unsafe { ManuallyDrop::drop(host) }
}
}
}
impl<'stack, C: Controller, P: PacketPool> StackBuilder<'stack, C, P> {
fn host(&mut self) -> &mut BleHost<'stack, C, P> {
self.host.as_mut().unwrap()
}
pub fn register_l2cap_spsm(mut self, spsm: u16) -> Self {
self.host().channels.register_spsm(spsm);
self
}
pub fn set_random_address(mut self, address: Address) -> Self {
self.host().address.replace(address);
#[cfg(feature = "security")]
self.host().connections.security_manager.set_local_address(address);
self
}
#[cfg(feature = "security")]
pub fn enable_privacy(mut self, irk: IdentityResolvingKey) -> Self {
self.host().connections.security_manager.set_local_irk(irk);
self
}
#[cfg(feature = "security")]
pub fn set_rpa_timeout(mut self, timeout: Duration) -> Self {
self.host().rpa_timeout.set(timeout);
self
}
#[cfg(feature = "security")]
pub fn set_io_capabilities(mut self, io_capabilities: IoCapabilities) -> Self {
self.host()
.connections
.security_manager
.set_io_capabilities(io_capabilities);
self
}
#[cfg(feature = "legacy-pairing")]
pub fn set_secure_connections_only(mut self, enabled: bool) -> Self {
self.host()
.connections
.security_manager
.set_secure_connections_only(enabled);
self
}
pub fn build(mut self) -> Stack<'stack, C, P> {
Stack {
host: self.host.take().unwrap(),
runner_taken: Cell::new(false),
}
}
}
impl<'stack, C: Controller, P: PacketPool> Stack<'stack, C, P> {
pub fn runner(&self) -> Runner<'_, C, P> {
assert!(
!self.runner_taken.replace(true),
"runner() can only be called once per Stack"
);
Runner::new(self.host)
}
#[cfg(feature = "central")]
pub fn central(&self) -> Central<'_, C, P> {
Central::new(self.host)
}
#[cfg(feature = "peripheral")]
pub fn peripheral(&self) -> Peripheral<'_, C, P> {
Peripheral::new(self.host)
}
#[cfg(feature = "security")]
pub fn set_io_capabilities(&self, io_capabilities: IoCapabilities) {
self.host
.connections
.security_manager
.set_io_capabilities(io_capabilities);
}
#[cfg(feature = "legacy-pairing")]
pub fn set_secure_connections_only(&self, enabled: bool) {
self.host
.connections
.security_manager
.set_secure_connections_only(enabled);
}
#[cfg(feature = "security")]
pub async fn set_rpa_timeout(&self, timeout: Duration) -> Result<(), BleHostError<C::Error>>
where
C: ControllerCmdSync<LeSetResolvablePrivateAddrTimeout>,
{
self.host.rpa_timeout.set(timeout);
if self.host.is_initialized() {
self.host
.command(LeSetResolvablePrivateAddrTimeout::new(
bt_hci::param::Duration::from_secs(timeout.as_secs() as u32),
))
.await?;
}
Ok(())
}
pub async fn command<T>(&self, cmd: T) -> Result<T::Return, BleHostError<C::Error>>
where
T: SyncCmd,
C: ControllerCmdSync<T>,
{
self.host.command(cmd).await
}
pub async fn async_command<T>(&self, cmd: T) -> Result<(), BleHostError<C::Error>>
where
T: AsyncCmd,
C: ControllerCmdAsync<T>,
{
self.host.async_command(cmd).await
}
pub async fn read_minimum_supported_connection_interval(
&self,
) -> Result<<LeReadMinimumSupportedConnectionInterval as SyncCmd>::Return, BleHostError<C::Error>>
where
C: ControllerCmdSync<LeReadMinimumSupportedConnectionInterval>,
{
self.host.command(LeReadMinimumSupportedConnectionInterval::new()).await
}
pub fn metrics<F: FnOnce(&HostMetrics) -> R, R>(&self, f: F) -> R {
self.host.metrics(f)
}
pub fn log_status(&self, verbose: bool) {
self.host.log_status(verbose);
}
#[cfg(feature = "security")]
pub fn get_local_oob_data(&self) -> OobData {
self.host.connections.security_manager.get_local_oob_data()
}
#[cfg(feature = "security")]
pub fn get_local_address(&self) -> Option<Address> {
self.host.connections.security_manager.get_local_address()
}
#[cfg(feature = "security")]
pub fn is_privacy_enabled(&self) -> bool {
self.host.is_privacy_enabled()
}
#[cfg(feature = "security")]
pub fn add_bond_information(&self, bond_information: BondInformation) -> Result<(), Error> {
let identity = bond_information.identity;
let result = self
.host
.connections
.security_manager
.add_bond_information(bond_information);
#[cfg(feature = "security")]
if result.is_ok() {
self.host
.resolving_list_state
.borrow_mut()
.push(crate::host::ResolvingListUpdate::Add(identity));
}
result
}
#[cfg(feature = "security")]
pub fn remove_bond_information(&self, identity: Identity) -> Result<(), Error> {
let result = self.host.connections.security_manager.remove_bond_information(identity);
#[cfg(feature = "security")]
if result.is_ok() {
self.host
.resolving_list_state
.borrow_mut()
.push(crate::host::ResolvingListUpdate::Remove(identity));
}
result
}
#[cfg(feature = "security")]
pub fn with_bond_information<R>(&self, f: impl FnOnce(&[BondInformation]) -> R) -> R {
f(&self.host.connections.security_manager.get_bond_information())
}
pub fn get_connection_by_peer_address(&self, peer_address: Address) -> Option<Connection<'_, P>> {
self.host.connections.get_connection_by_peer_address(peer_address)
}
pub fn get_connected_handle(&self, handle: ConnHandle) -> Option<Connection<'_, P>> {
self.host.connections.get_connected_handle(handle)
}
pub fn connections(&self) -> connection_manager::ConnectedIter<'_, P> {
self.host.connections.connections()
}
}
pub(crate) fn bt_hci_duration<const US: u32>(d: Duration) -> bt_hci::param::Duration<US> {
bt_hci::param::Duration::from_micros(d.as_micros())
}
pub(crate) fn bt_hci_ext_duration<const US: u16>(d: Duration) -> bt_hci::param::ExtDuration<US> {
bt_hci::param::ExtDuration::from_micros(d.as_micros())
}
#[doc(hidden)]
pub mod __export {
pub use embassy_sync;
}