Skip to content

Commit

Permalink
refactor: change api and support varying scan buffer sizes
Browse files Browse the repository at this point in the history
* Add Scanner type which wraps the central to provide scan features
* Make scanner buffer configurable size in order to handle large number
  of reports.
* Only add valid advertising reports to scan buffer.
  • Loading branch information
lulf committed Jan 16, 2025
1 parent 3f63c23 commit 3085912
Show file tree
Hide file tree
Showing 6 changed files with 381 additions and 290 deletions.
2 changes: 1 addition & 1 deletion examples/serial-hci/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"
env_logger = "0.10.0"
log = "0.4"
embedded-io-adapters = { version = "0.6.1", features = ["tokio-1"] }
embassy-sync = { version = "0.6.0", features = ["log"] }
embassy-sync = { version = "0.6", features = ["log"] }
embassy-time = { version = "0.4", features = ["log", "std", "generic-queue-8"] }
critical-section = { version = "1.1", features = ["std"] }
tokio = { version = "1", features = ["full"] }
Expand Down
3 changes: 3 additions & 0 deletions host/src/advertise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,9 @@ pub struct AdStructureIter<'d> {
impl<'d> AdStructureIter<'d> {
fn read(&mut self) -> Result<AdStructure<'d>, codec::Error> {
let len: u8 = self.cursor.read()?;
if len < 2 {
return Err(codec::Error::InvalidValue);
}
let code: u8 = self.cursor.read()?;
let data = self.cursor.slice(len as usize - 1)?;
match code {
Expand Down
191 changes: 25 additions & 166 deletions host/src/central.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
//! Functionality for the BLE central role.
#[cfg(feature = "scan")]
use bt_hci::cmd::le::LeSetScanParams;
use bt_hci::cmd::le::{
LeAddDeviceToFilterAcceptList, LeClearFilterAcceptList, LeCreateConn, LeExtCreateConn, LeSetExtScanEnable,
LeSetExtScanParams, LeSetScanEnable,
};
use crate::connection::{ConnectConfig, Connection};
use crate::scan::PhySet;
use crate::{BleHostError, Error, Stack};
use bt_hci::cmd::le::{LeAddDeviceToFilterAcceptList, LeClearFilterAcceptList, LeCreateConn, LeExtCreateConn};
use bt_hci::controller::{Controller, ControllerCmdAsync, ControllerCmdSync};
use bt_hci::param::{AddrKind, BdAddr, FilterDuplicates, InitiatingPhy, LeConnRole, PhyParams, ScanningPhy};
use bt_hci::param::{AddrKind, BdAddr, InitiatingPhy, LeConnRole, PhyParams};
#[cfg(feature = "controller-host-flow-control")]
use bt_hci::param::{ConnHandleCompletedPackets, ControllerToHostFlowControl};
use embassy_futures::select::{select, Either};

use crate::connection::{ConnectConfig, Connection};
#[cfg(feature = "scan")]
use crate::scan::ScanReport;
use crate::scan::{PhySet, ScanConfig};
use crate::{BleHostError, Error, Stack};

/// A type implementing the BLE central role.
pub struct Central<'d, C: Controller> {
stack: Stack<'d, C>,
pub(crate) stack: Stack<'d, C>,
}

impl<'d, C: Controller> Central<'d, C> {
Expand Down Expand Up @@ -82,9 +74,7 @@ impl<'d, C: Controller> Central<'d, C> {
where
C: ControllerCmdSync<LeClearFilterAcceptList>
+ ControllerCmdSync<LeAddDeviceToFilterAcceptList>
+ ControllerCmdAsync<LeExtCreateConn>
+ ControllerCmdSync<LeSetExtScanEnable>
+ ControllerCmdSync<LeSetExtScanParams>,
+ ControllerCmdAsync<LeExtCreateConn>,
{
if config.scan_config.filter_accept_list.is_empty() {
return Err(Error::InvalidValue.into());
Expand All @@ -109,7 +99,7 @@ impl<'d, C: Controller> Central<'d, C> {
min_ce_len: config.connect_params.event_length.into(),
max_ce_len: config.connect_params.event_length.into(),
};
let phy_params = Self::create_phy_params(initiating, config.scan_config.phys);
let phy_params = create_phy_params(initiating, config.scan_config.phys);

host.async_command(LeExtCreateConn::new(
true,
Expand All @@ -136,24 +126,6 @@ impl<'d, C: Controller> Central<'d, C> {
}
}

fn create_phy_params<P: Copy>(phy: P, phys: PhySet) -> PhyParams<P> {
let phy_params: PhyParams<P> = PhyParams {
le_1m_phy: match phys {
PhySet::M1 | PhySet::M1M2 | PhySet::M1Coded | PhySet::M1M2Coded => Some(phy),
_ => None,
},
le_2m_phy: match phys {
PhySet::M2 | PhySet::M1M2 | PhySet::M2Coded | PhySet::M1M2Coded => Some(phy),
_ => None,
},
le_coded_phy: match phys {
PhySet::M2Coded | PhySet::Coded | PhySet::M1Coded | PhySet::M1M2Coded => Some(phy),
_ => None,
},
};
phy_params
}

pub(crate) async fn set_accept_filter(
&mut self,
filter_accept_list: &[(AddrKind, &BdAddr)],
Expand All @@ -169,135 +141,22 @@ impl<'d, C: Controller> Central<'d, C> {
}
Ok(())
}
}

#[cfg(feature = "scan")]
async fn start_scan(&mut self, config: &ScanConfig<'_>) -> Result<(), BleHostError<C::Error>>
where
C: ControllerCmdSync<LeSetScanParams>
+ ControllerCmdSync<LeSetScanEnable>
+ ControllerCmdSync<LeClearFilterAcceptList>
+ ControllerCmdSync<LeAddDeviceToFilterAcceptList>,
{
let host = self.stack.host;
self.set_accept_filter(config.filter_accept_list).await?;

let params = LeSetScanParams::new(
if config.active {
bt_hci::param::LeScanKind::Active
} else {
bt_hci::param::LeScanKind::Passive
},
config.interval.into(),
config.interval.into(),
bt_hci::param::AddrKind::PUBLIC,
if config.filter_accept_list.is_empty() {
bt_hci::param::ScanningFilterPolicy::BasicUnfiltered
} else {
bt_hci::param::ScanningFilterPolicy::BasicFiltered
},
);
host.command(params).await?;
host.command(LeSetScanEnable::new(true, true)).await?;
Ok(())
}

async fn start_scan_ext(&mut self, config: &ScanConfig<'_>) -> Result<(), BleHostError<C::Error>>
where
C: ControllerCmdSync<LeSetExtScanEnable>
+ ControllerCmdSync<LeSetExtScanParams>
+ ControllerCmdSync<LeClearFilterAcceptList>
+ ControllerCmdSync<LeAddDeviceToFilterAcceptList>,
{
self.set_accept_filter(config.filter_accept_list).await?;

let scanning = ScanningPhy {
active_scan: config.active,
scan_interval: config.interval.into(),
scan_window: config.window.into(),
};
let phy_params = Self::create_phy_params(scanning, config.phys);
let host = self.stack.host;
host.command(LeSetExtScanParams::new(
host.address.map(|s| s.kind).unwrap_or(AddrKind::PUBLIC),
if config.filter_accept_list.is_empty() {
bt_hci::param::ScanningFilterPolicy::BasicUnfiltered
} else {
bt_hci::param::ScanningFilterPolicy::BasicFiltered
},
phy_params,
))
.await?;
host.command(LeSetExtScanEnable::new(
true,
FilterDuplicates::Disabled,
config.timeout.into(),
bt_hci::param::Duration::from_secs(0),
))
.await?;
Ok(())
}

async fn stop_scan(&mut self) -> Result<(), BleHostError<C::Error>>
where
C: ControllerCmdSync<LeSetScanEnable>,
{
let host = self.stack.host;
host.command(LeSetScanEnable::new(false, false)).await?;
Ok(())
}

async fn stop_scan_ext(&mut self) -> Result<(), BleHostError<C::Error>>
where
C: ControllerCmdSync<LeSetExtScanEnable>,
{
let host = self.stack.host;
host.command(LeSetExtScanEnable::new(
false,
FilterDuplicates::Disabled,
bt_hci::param::Duration::from_secs(0),
bt_hci::param::Duration::from_secs(0),
))
.await?;
Ok(())
}

/// Performs an extended BLE scan, return a report for discovering peripherals.
///
/// Scan is stopped when a report is received. Call this method repeatedly to continue scanning.
#[cfg(feature = "scan")]
pub async fn scan_ext(&mut self, config: &ScanConfig<'_>) -> Result<ScanReport, BleHostError<C::Error>>
where
C: ControllerCmdSync<LeSetExtScanEnable>
+ ControllerCmdSync<LeSetExtScanParams>
+ ControllerCmdSync<LeClearFilterAcceptList>
+ ControllerCmdSync<LeAddDeviceToFilterAcceptList>,
{
let host = self.stack.host;
self.start_scan_ext(config).await?;
let Some(report) = host.scanner.receive().await else {
return Err(Error::Timeout.into());
};
self.stop_scan_ext().await?;
Ok(report)
}

/// Performs a BLE scan, return a report for discovering peripherals.
///
/// Scan is stopped when a report is received. Call this method repeatedly to continue scanning.
#[cfg(feature = "scan")]
pub async fn scan(&mut self, config: &ScanConfig<'_>) -> Result<ScanReport, BleHostError<C::Error>>
where
C: ControllerCmdSync<LeSetScanParams>
+ ControllerCmdSync<LeSetScanEnable>
+ ControllerCmdSync<LeClearFilterAcceptList>
+ ControllerCmdSync<LeAddDeviceToFilterAcceptList>,
{
let host = self.stack.host;
self.start_scan(config).await?;
let Some(report) = host.scanner.receive().await else {
return Err(Error::Timeout.into());
};
self.stop_scan().await?;
Ok(report)
}
pub(crate) fn create_phy_params<P: Copy>(phy: P, phys: PhySet) -> PhyParams<P> {
let phy_params: PhyParams<P> = PhyParams {
le_1m_phy: match phys {
PhySet::M1 | PhySet::M1M2 | PhySet::M1Coded | PhySet::M1M2Coded => Some(phy),
_ => None,
},
le_2m_phy: match phys {
PhySet::M2 | PhySet::M1M2 | PhySet::M2Coded | PhySet::M1M2Coded => Some(phy),
_ => None,
},
le_coded_phy: match phys {
PhySet::M2Coded | PhySet::Coded | PhySet::M1Coded | PhySet::M1M2Coded => Some(phy),
_ => None,
},
};
phy_params
}
Loading

0 comments on commit 3085912

Please sign in to comment.