#![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::mem::MaybeUninit;
use advertise::AdvertisementDataError;
use bt_hci::cmd::status::ReadRssi;
use bt_hci::cmd::{AsyncCmd, SyncCmd};
use bt_hci::param::{AddrKind, BdAddr};
use bt_hci::FromHciBytesError;
use embassy_time::Duration;
#[cfg(feature = "security")]
use heapless::Vec;
use rand_core::{CryptoRng, RngCore};
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};
pub use crate::types::capabilities::IoCapabilities;
pub(crate) const BI_COUNT: usize = 10;
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, Host, HostResources, Packet, PacketPool, Stack};
#[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::*;
#[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};
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, PartialEq)]
pub struct Address {
pub kind: AddrKind,
pub addr: BdAddr,
}
impl Address {
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]
)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Identity {
pub bd_addr: BdAddr,
#[cfg(feature = "security")]
pub irk: Option<IdentityResolvingKey>,
}
#[cfg(feature = "defmt")]
impl defmt::Format for Identity {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "BdAddr({:X}) ", self.bd_addr);
#[cfg(feature = "security")]
defmt::write!(fmt, "Irk({:X})", self.irk);
}
}
impl Identity {
pub fn match_address(&self, address: &BdAddr) -> bool {
if self.bd_addr == *address {
return true;
}
#[cfg(feature = "security")]
if let Some(irk) = self.irk {
return irk.resolve_address(address);
}
false
}
pub fn match_identity(&self, identity: &Identity) -> bool {
if self.match_address(&identity.bd_addr) {
return true;
}
#[cfg(feature = "security")]
if let Some(irk) = identity.irk {
if let Some(current_irk) = self.irk {
return irk == current_irk;
} else {
return irk.resolve_address(&self.bd_addr);
}
}
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(crate::security_manager::Reason),
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),
InvalidChannelId,
NoChannelAvailable,
NotFound,
InvalidState,
OutOfMemory,
NotSupported,
ChannelClosed,
Timeout,
Busy,
NoPermits,
Disconnected,
ConnectionLimitReached,
GattSubscriberLimitReached,
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};
pub trait Controller:
bt_hci::controller::Controller
+ embedded_io::ErrorType
+ 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<LeLongTermKeyRequestReply>
+ ControllerCmdAsync<LeEnableEncryption>
+ ControllerCmdSync<ReadBdAddr>
{
}
impl<
C: bt_hci::controller::Controller
+ embedded_io::ErrorType
+ 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<LeLongTermKeyRequestReply>
+ ControllerCmdAsync<LeEnableEncryption>
+ ControllerCmdSync<ReadBdAddr>,
> 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<P: PacketPool, const CONNS: usize, const CHANNELS: usize, const ADV_SETS: usize = 1> {
connections: MaybeUninit<[ConnectionStorage<P::Packet>; CONNS]>,
channels: MaybeUninit<[ChannelStorage<P::Packet>; CHANNELS]>,
advertise_handles: MaybeUninit<[AdvHandleState; ADV_SETS]>,
}
impl<P: PacketPool, const CONNS: usize, const CHANNELS: usize, const ADV_SETS: usize> Default
for HostResources<P, CONNS, CHANNELS, ADV_SETS>
{
fn default() -> Self {
Self::new()
}
}
impl<P: PacketPool, const CONNS: usize, const CHANNELS: usize, const ADV_SETS: usize>
HostResources<P, CONNS, CHANNELS, ADV_SETS>
{
pub const fn new() -> Self {
Self {
connections: MaybeUninit::uninit(),
channels: MaybeUninit::uninit(),
advertise_handles: MaybeUninit::uninit(),
}
}
}
pub fn new<
'resources,
C: Controller,
P: PacketPool,
const CONNS: usize,
const CHANNELS: usize,
const ADV_SETS: usize,
>(
controller: C,
resources: &'resources mut HostResources<P, CONNS, CHANNELS, ADV_SETS>,
) -> Stack<'resources, C, P> {
unsafe fn transmute_slice<T>(x: &mut [T]) -> &'static mut [T] {
unsafe { core::mem::transmute(x) }
}
let connections: &mut [ConnectionStorage<P::Packet>] =
&mut *resources.connections.write([const { ConnectionStorage::new() }; CONNS]);
let connections: &'resources mut [ConnectionStorage<P::Packet>] = unsafe { transmute_slice(connections) };
let channels = &mut *resources.channels.write([const { ChannelStorage::new() }; CHANNELS]);
let channels: &'static mut [ChannelStorage<P::Packet>] = unsafe { transmute_slice(channels) };
let advertise_handles = &mut *resources.advertise_handles.write([AdvHandleState::None; ADV_SETS]);
let advertise_handles: &'static mut [AdvHandleState] = unsafe { transmute_slice(advertise_handles) };
let host: BleHost<'_, C, P> = BleHost::new(controller, connections, channels, advertise_handles);
Stack { host }
}
pub struct Stack<'stack, C, P: PacketPool> {
host: BleHost<'stack, C, P>,
}
#[non_exhaustive]
pub struct Host<'stack, C, P: PacketPool> {
#[cfg(feature = "central")]
pub central: Central<'stack, C, P>,
#[cfg(feature = "peripheral")]
pub peripheral: Peripheral<'stack, C, P>,
pub runner: Runner<'stack, C, P>,
}
impl<'stack, C: Controller, P: PacketPool> Stack<'stack, C, P> {
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
}
pub fn set_random_generator_seed<RNG: RngCore + CryptoRng>(self, _random_generator: &mut RNG) -> Self {
#[cfg(feature = "security")]
{
let mut random_seed = [0u8; 32];
_random_generator.fill_bytes(&mut random_seed);
self.host
.connections
.security_manager
.set_random_generator_seed(random_seed);
}
self
}
pub fn set_io_capabilities(&self, io_capabilities: IoCapabilities) {
#[cfg(feature = "security")]
{
self.host
.connections
.security_manager
.set_io_capabilities(io_capabilities);
}
}
pub fn build(&'stack self) -> Host<'stack, C, P> {
#[cfg(all(feature = "security", not(feature = "dev-disable-csprng-seed-requirement")))]
{
if !self.host.connections.security_manager.get_random_generator_seeded() {
panic!(
"The security manager random number generator has not been seeded from a cryptographically secure random number generator"
)
}
}
Host {
#[cfg(feature = "central")]
central: Central::new(self),
#[cfg(feature = "peripheral")]
peripheral: Peripheral::new(self),
runner: Runner::new(self),
}
}
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 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 add_bond_information(&self, bond_information: BondInformation) -> Result<(), Error> {
self.host
.connections
.security_manager
.add_bond_information(bond_information)
}
#[cfg(feature = "security")]
pub fn remove_bond_information(&self, identity: Identity) -> Result<(), Error> {
self.host.connections.security_manager.remove_bond_information(identity)
}
#[cfg(feature = "security")]
pub fn get_bond_information(&self) -> Vec<BondInformation, BI_COUNT> {
self.host.connections.security_manager.get_bond_information()
}
pub fn get_connection_by_peer_address(&'stack self, peer_address: Address) -> Option<Connection<'stack, P>> {
self.host.connections.get_connection_by_peer_address(peer_address)
}
}
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())
}