From 88a13037a152f66928611bd501d2df11d9ce4aa8 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 27 Jan 2025 11:22:06 +0100 Subject: [PATCH] feat: add scanner example * Refactor scanner again to avoid buffer copies * Add ble scanner example --- examples/apps/Cargo.toml | 3 +- examples/apps/src/ble_bas_peripheral.rs | 5 +- examples/apps/src/ble_scanner.rs | 65 +++++++++++++ examples/apps/src/lib.rs | 1 + examples/esp32/Cargo.lock | 43 +-------- examples/nrf-sdc/.cargo/config.toml | 2 +- examples/nrf-sdc/Cargo.lock | 23 +---- examples/nrf-sdc/src/bin/ble_scanner.rs | 72 ++++++++++++++ examples/rp-pico-w/Cargo.lock | 29 +----- examples/serial-hci/Cargo.lock | 47 +--------- host/src/host.rs | 102 ++++++-------------- host/src/lib.rs | 2 +- host/src/scan.rs | 119 ++---------------------- 13 files changed, 186 insertions(+), 327 deletions(-) create mode 100644 examples/apps/src/ble_scanner.rs create mode 100644 examples/nrf-sdc/src/bin/ble_scanner.rs diff --git a/examples/apps/Cargo.toml b/examples/apps/Cargo.toml index ea9ab42f..94196b7e 100644 --- a/examples/apps/Cargo.toml +++ b/examples/apps/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" license = "MIT OR Apache-2.0" [dependencies] -trouble-host = { path = "../../host", features = ["derive"] } +trouble-host = { path = "../../host", features = ["derive", "scan"] } bt-hci = { version = "0.2" } embassy-executor = { version = "0.7.0" } embassy-futures = "0.1.1" @@ -14,6 +14,7 @@ embassy-time = "0.4" embedded-hal = "1.0" static_cell = "2" embedded-io = "0.6" +heapless = "0.8" defmt = { version = "0.3", optional = true } log = { version = "0.4", optional = true } diff --git a/examples/apps/src/ble_bas_peripheral.rs b/examples/apps/src/ble_bas_peripheral.rs index c947a40a..b2d1282b 100644 --- a/examples/apps/src/ble_bas_peripheral.rs +++ b/examples/apps/src/ble_bas_peripheral.rs @@ -1,4 +1,5 @@ -use embassy_futures::{join::join, select::select}; +use embassy_futures::join::join; +use embassy_futures::select::select; use embassy_time::Timer; use trouble_host::prelude::*; @@ -77,7 +78,7 @@ where /// /// If you didn't require this to be generic for your application, you could statically spawn this with i.e. /// -/// ```rust [ignore] +/// ```rust,ignore /// /// #[embassy_executor::task] /// async fn ble_task(mut runner: Runner<'static, SoftdeviceController<'static>>) { diff --git a/examples/apps/src/ble_scanner.rs b/examples/apps/src/ble_scanner.rs new file mode 100644 index 00000000..75f00c37 --- /dev/null +++ b/examples/apps/src/ble_scanner.rs @@ -0,0 +1,65 @@ +use bt_hci::cmd::le::LeSetScanParams; +use bt_hci::controller::ControllerCmdSync; +use core::cell::RefCell; +use embassy_futures::join::join; +use embassy_time::{Duration, Timer}; +use heapless::Deque; +use trouble_host::prelude::*; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; +const L2CAP_CHANNELS_MAX: usize = 1; +const L2CAP_MTU: usize = 27; + +pub async fn run(controller: C) +where + C: Controller + ControllerCmdSync, +{ + // Using a fixed "random" address can be useful for testing. In real scenarios, one would + // use e.g. the MAC 6 byte array as the address (how to get that varies by the platform). + let address: Address = Address::random([0xff, 0x8f, 0x1b, 0x05, 0xe4, 0xff]); + + info!("Our address = {:?}", address); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + central, mut runner, .. + } = stack.build(); + + let printer = Printer { + seen: RefCell::new(Deque::new()), + }; + let mut scanner = Scanner::new(central); + let _ = join(runner.run_with_handler(&printer), async { + let mut config = ScanConfig::default(); + config.active = true; + config.phys = PhySet::M1; + config.interval = Duration::from_secs(1); + config.window = Duration::from_secs(1); + let mut _session = scanner.scan(&config).await.unwrap(); + // Scan forever + loop { + Timer::after(Duration::from_secs(1)).await; + } + }) + .await; +} + +struct Printer { + seen: RefCell>, +} + +impl EventHandler for Printer { + fn on_adv_reports(&self, mut it: LeAdvReportsIter<'_>) { + let mut seen = self.seen.borrow_mut(); + while let Some(Ok(report)) = it.next() { + if seen.iter().find(|b| b.raw() == report.addr.raw()).is_none() { + info!("discovered: {:?}", report.addr); + if seen.is_full() { + seen.pop_front(); + } + seen.push_back(report.addr).unwrap(); + } + } + } +} diff --git a/examples/apps/src/lib.rs b/examples/apps/src/lib.rs index 49862f8f..2b0191f4 100644 --- a/examples/apps/src/lib.rs +++ b/examples/apps/src/lib.rs @@ -8,3 +8,4 @@ pub mod ble_bas_central; pub mod ble_bas_peripheral; pub mod ble_l2cap_central; pub mod ble_l2cap_peripheral; +pub mod ble_scanner; diff --git a/examples/esp32/Cargo.lock b/examples/esp32/Cargo.lock index 8cc0456f..9e50c85c 100644 --- a/examples/esp32/Cargo.lock +++ b/examples/esp32/Cargo.lock @@ -909,26 +909,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "object" version = "0.36.7" @@ -1230,26 +1210,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "thiserror" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "toml" version = "0.8.19" @@ -1310,6 +1270,7 @@ dependencies = [ "embassy-time", "embedded-hal 1.0.0", "embedded-io", + "heapless", "log", "static_cell", "trouble-host", @@ -1327,9 +1288,7 @@ dependencies = [ "futures", "heapless", "log", - "num_enum", "static_cell", - "thiserror", "trouble-host-macros", ] diff --git a/examples/nrf-sdc/.cargo/config.toml b/examples/nrf-sdc/.cargo/config.toml index da2300bd..31442eb6 100644 --- a/examples/nrf-sdc/.cargo/config.toml +++ b/examples/nrf-sdc/.cargo/config.toml @@ -11,4 +11,4 @@ runner = "probe-rs run --chip nRF52840_xxAA" target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) [env] -DEFMT_LOG = "trace" +DEFMT_LOG = "trouble_host=trace,info" diff --git a/examples/nrf-sdc/Cargo.lock b/examples/nrf-sdc/Cargo.lock index 1b60c573..5883767f 100644 --- a/examples/nrf-sdc/Cargo.lock +++ b/examples/nrf-sdc/Cargo.lock @@ -798,26 +798,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "panic-probe" version = "0.3.2" @@ -1094,6 +1074,7 @@ dependencies = [ "embassy-time", "embedded-hal 1.0.0", "embedded-io", + "heapless", "static_cell", "trouble-host", ] @@ -1110,9 +1091,7 @@ dependencies = [ "embedded-io", "futures", "heapless", - "num_enum", "static_cell", - "thiserror", "trouble-host-macros", ] diff --git a/examples/nrf-sdc/src/bin/ble_scanner.rs b/examples/nrf-sdc/src/bin/ble_scanner.rs new file mode 100644 index 00000000..9ebea5e7 --- /dev/null +++ b/examples/nrf-sdc/src/bin/ble_scanner.rs @@ -0,0 +1,72 @@ +#![no_std] +#![no_main] + +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_nrf::peripherals::RNG; +use embassy_nrf::{bind_interrupts, rng}; +use embassy_time::{Duration, Timer}; +use nrf_sdc::mpsl::MultiprotocolServiceLayer; +use nrf_sdc::{self as sdc, mpsl}; +use static_cell::StaticCell; +use trouble_example_apps::ble_scanner; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; + EGU0_SWI0 => nrf_sdc::mpsl::LowPrioInterruptHandler; + CLOCK_POWER => nrf_sdc::mpsl::ClockInterruptHandler; + RADIO => nrf_sdc::mpsl::HighPrioInterruptHandler; + TIMER0 => nrf_sdc::mpsl::HighPrioInterruptHandler; + RTC0 => nrf_sdc::mpsl::HighPrioInterruptHandler; +}); + +#[embassy_executor::task] +async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! { + mpsl.run().await +} + +fn build_sdc<'d, const N: usize>( + p: nrf_sdc::Peripherals<'d>, + rng: &'d mut rng::Rng, + mpsl: &'d MultiprotocolServiceLayer, + mem: &'d mut sdc::Mem, +) -> Result, nrf_sdc::Error> { + sdc::Builder::new()? + .support_scan()? + .support_ext_scan()? + .support_central()? + .support_ext_central()? + .central_count(1)? + .build(p, rng, mpsl, mem) +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mpsl_p = mpsl::Peripherals::new(p.RTC0, p.TIMER0, p.TEMP, p.PPI_CH19, p.PPI_CH30, p.PPI_CH31); + let lfclk_cfg = mpsl::raw::mpsl_clock_lfclk_cfg_t { + source: mpsl::raw::MPSL_CLOCK_LF_SRC_RC as u8, + rc_ctiv: mpsl::raw::MPSL_RECOMMENDED_RC_CTIV as u8, + rc_temp_ctiv: mpsl::raw::MPSL_RECOMMENDED_RC_TEMP_CTIV as u8, + accuracy_ppm: mpsl::raw::MPSL_DEFAULT_CLOCK_ACCURACY_PPM as u16, + skip_wait_lfclk_started: mpsl::raw::MPSL_DEFAULT_SKIP_WAIT_LFCLK_STARTED != 0, + }; + static MPSL: StaticCell = StaticCell::new(); + let mpsl = MPSL.init(unwrap!(mpsl::MultiprotocolServiceLayer::new(mpsl_p, Irqs, lfclk_cfg))); + spawner.must_spawn(mpsl_task(&*mpsl)); + + let sdc_p = sdc::Peripherals::new( + p.PPI_CH17, p.PPI_CH18, p.PPI_CH20, p.PPI_CH21, p.PPI_CH22, p.PPI_CH23, p.PPI_CH24, p.PPI_CH25, p.PPI_CH26, + p.PPI_CH27, p.PPI_CH28, p.PPI_CH29, + ); + + let mut rng = rng::Rng::new(p.RNG, Irqs); + + let mut sdc_mem = sdc::Mem::<2712>::new(); + let sdc = unwrap!(build_sdc(sdc_p, &mut rng, mpsl, &mut sdc_mem)); + + Timer::after(Duration::from_millis(200)).await; + + ble_scanner::run(sdc).await; +} diff --git a/examples/rp-pico-w/Cargo.lock b/examples/rp-pico-w/Cargo.lock index bca8bced..86292e78 100644 --- a/examples/rp-pico-w/Cargo.lock +++ b/examples/rp-pico-w/Cargo.lock @@ -293,7 +293,7 @@ dependencies = [ "embedded-io-async", "futures", "heapless", - "num_enum 0.5.11", + "num_enum", ] [[package]] @@ -1428,16 +1428,7 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ - "num_enum_derive 0.5.11", -] - -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive 0.7.3", + "num_enum_derive", ] [[package]] @@ -1451,17 +1442,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.93", -] - [[package]] name = "object" version = "0.36.7" @@ -1604,7 +1584,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" dependencies = [ "arrayvec", - "num_enum 0.5.11", + "num_enum", "paste", ] @@ -2415,6 +2395,7 @@ dependencies = [ "embassy-time", "embedded-hal 1.0.0", "embedded-io", + "heapless", "static_cell", "trouble-host", ] @@ -2431,9 +2412,7 @@ dependencies = [ "embedded-io", "futures", "heapless", - "num_enum 0.7.3", "static_cell", - "thiserror 2.0.9", "trouble-host-macros", ] diff --git a/examples/serial-hci/Cargo.lock b/examples/serial-hci/Cargo.lock index 57457610..79283abc 100644 --- a/examples/serial-hci/Cargo.lock +++ b/examples/serial-hci/Cargo.lock @@ -614,26 +614,6 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "object" version = "0.36.7" @@ -867,16 +847,7 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" -dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl", ] [[package]] @@ -890,17 +861,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thiserror-impl" -version = "2.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tokio" version = "1.42.0" @@ -954,6 +914,7 @@ dependencies = [ "embassy-time", "embedded-hal 1.0.0", "embedded-io", + "heapless", "log", "static_cell", "trouble-host", @@ -971,9 +932,7 @@ dependencies = [ "futures", "heapless", "log", - "num_enum", "static_cell", - "thiserror 2.0.9", "trouble-host-macros", ] @@ -995,7 +954,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815" dependencies = [ - "thiserror 1.0.69", + "thiserror", ] [[package]] diff --git a/host/src/host.rs b/host/src/host.rs index 7a647d47..3a6e92e5 100644 --- a/host/src/host.rs +++ b/host/src/host.rs @@ -72,8 +72,6 @@ pub(crate) struct BleHost<'d, T> { pub(crate) advertise_command_state: CommandState, pub(crate) connect_command_state: CommandState, pub(crate) scan_command_state: CommandState, - #[cfg(feature = "scan")] - pub(crate) scan_state: ScanState, } #[derive(Clone, Copy)] @@ -174,34 +172,6 @@ impl<'d> AdvState<'d> { } } -pub(crate) struct ScanState { - writer: RefCell>>, -} - -impl ScanState { - pub(crate) fn new() -> Self { - Self { - writer: RefCell::new(None), - } - } - - pub(crate) fn reset(&self, writer: embassy_sync::pipe::DynamicWriter<'static>) { - self.writer.borrow_mut().replace(writer); - } - - pub(crate) fn try_push(&self, _n_reports: u8, data: &[u8]) -> Result<(), ()> { - let mut writer = self.writer.borrow_mut(); - if let Some(writer) = writer.as_mut() { - writer.try_write(data).map_err(|_| ())?; - } - Ok(()) - } - - pub(crate) fn stop(&self) { - let _ = self.writer.borrow_mut().take(); - } -} - /// Host metrics #[derive(Default, Clone)] pub struct HostMetrics { @@ -249,8 +219,6 @@ where tx_pool, #[cfg(feature = "gatt")] att_client: Channel::new(), - #[cfg(feature = "scan")] - scan_state: ScanState::new(), advertise_state: AdvState::new(advertise_handles), advertise_command_state: CommandState::new(), scan_command_state: CommandState::new(), @@ -510,6 +478,21 @@ pub struct TxRunner<'d, C> { stack: &'d Stack<'d, C>, } +/// Event handler. +pub trait EventHandler { + /// Handle vendor events + fn on_vendor(&self, vendor: &Vendor) {} + /// Handle advertising reports + #[cfg(feature = "scan")] + fn on_adv_reports(&self, reports: bt_hci::param::LeAdvReportsIter) {} + /// Handle extended advertising reports + #[cfg(feature = "scan")] + fn on_ext_adv_reports(&self, reports: bt_hci::param::LeExtAdvReportsIter) {} +} + +struct DummyHandler; +impl EventHandler for DummyHandler {} + impl<'d, C: Controller> Runner<'d, C> { pub(crate) fn new(stack: &'d Stack<'d, C>) -> Self { Self { @@ -544,11 +527,12 @@ impl<'d, C: Controller> Runner<'d, C> { + for<'t> ControllerCmdSync> + ControllerCmdSync, { - self.run_with_handler(|_| {}).await + let dummy = DummyHandler; + self.run_with_handler(&dummy).await } /// Run the host with a vendor event handler for custom events. - pub async fn run_with_handler(&mut self, vendor_handler: F) -> Result<(), BleHostError> + pub async fn run_with_handler(&mut self, event_handler: &E) -> Result<(), BleHostError> where C: ControllerCmdSync + ControllerCmdSync @@ -568,7 +552,7 @@ impl<'d, C: Controller> Runner<'d, C> { + ControllerCmdSync, { let control_fut = self.control.run(); - let rx_fut = self.rx.run_with_handler(vendor_handler); + let rx_fut = self.rx.run_with_handler(event_handler); let tx_fut = self.tx.run(); pin_mut!(control_fut, rx_fut, tx_fut); match select3(&mut tx_fut, &mut rx_fut, &mut control_fut).await { @@ -594,12 +578,13 @@ impl<'d, C: Controller> RxRunner<'d, C> { where C: ControllerCmdSync + for<'t> ControllerCmdSync>, { - self.run_with_handler(|_| {}).await + let dummy = DummyHandler; + self.run_with_handler(&dummy).await } /// Runs the receive loop that pools the controller for events, dispatching /// vendor events to the provided closure. - pub async fn run_with_handler(&mut self, vendor_handler: F) -> Result<(), BleHostError> + pub async fn run_with_handler(&mut self, event_handler: &E) -> Result<(), BleHostError> where C: ControllerCmdSync + for<'t> ControllerCmdSync>, { @@ -696,55 +681,20 @@ impl<'d, C: Controller> RxRunner<'d, C> { host.connect_command_state.canceled(); } } - LeEvent::LeScanTimeout(_) => { - #[cfg(feature = "scan")] - host.scan_state.stop(); - } + LeEvent::LeScanTimeout(_) => {} LeEvent::LeAdvertisingSetTerminated(set) => { host.advertise_state.terminate(set.adv_handle); } LeEvent::LeExtendedAdvertisingReport(data) => { #[cfg(feature = "scan")] { - let mut bytes = &data.reports.bytes[..]; - let mut n = 0; - while n < data.reports.num_reports { - match bt_hci::param::LeExtAdvReport::from_hci_bytes(bytes) { - Ok((_, remaining)) => { - n += 1; - bytes = remaining; - } - Err(_) => { - break; - } - } - } - let consumed = data.reports.bytes.len() - bytes.len(); - if let Err(_) = host.scan_state.try_push(n, &data.reports.bytes[..consumed]) { - warn!("[host] scan buffer overflow"); - } + event_handler.on_ext_adv_reports(data.reports.iter()); } } LeEvent::LeAdvertisingReport(data) => { #[cfg(feature = "scan")] { - let mut bytes = &data.reports.bytes[..]; - let mut n = 0; - while n < data.reports.num_reports { - match bt_hci::param::LeAdvReport::from_hci_bytes(bytes) { - Ok((_, remaining)) => { - n += 1; - bytes = remaining; - } - Err(_) => { - break; - } - } - } - let consumed = data.reports.bytes.len() - bytes.len(); - if let Err(_) = host.scan_state.try_push(n, &data.reports.bytes[..consumed]) { - warn!("[host] scan buffer overflow"); - } + event_handler.on_adv_reports(data.reports.iter()); } } _ => { @@ -789,7 +739,7 @@ impl<'d, C: Controller> RxRunner<'d, C> { } } Event::Vendor(vendor) => { - vendor_handler(&vendor); + event_handler.on_vendor(&vendor); } // Ignore _ => {} diff --git a/host/src/lib.rs b/host/src/lib.rs index 648312bd..67ec0cf4 100644 --- a/host/src/lib.rs +++ b/host/src/lib.rs @@ -89,7 +89,7 @@ pub mod prelude { pub use crate::gap::*; #[cfg(feature = "gatt")] pub use crate::gatt::*; - pub use crate::host::{ControlRunner, HostMetrics, Runner, RxRunner, TxRunner}; + pub use crate::host::{ControlRunner, EventHandler, HostMetrics, Runner, RxRunner, TxRunner}; pub use crate::l2cap::*; pub use crate::packet_pool::PacketPool; #[cfg(feature = "peripheral")] diff --git a/host/src/scan.rs b/host/src/scan.rs index a2e02b73..d516c329 100644 --- a/host/src/scan.rs +++ b/host/src/scan.rs @@ -1,21 +1,14 @@ //! Scan config. use crate::command::CommandState; use crate::connection::ScanConfig; -use crate::host::ScanState; use crate::BleHostError; -use crate::Error; use bt_hci::cmd::le::LeSetScanParams; use bt_hci::cmd::le::{ LeAddDeviceToFilterAcceptList, LeClearFilterAcceptList, LeSetExtScanEnable, LeSetExtScanParams, LeSetScanEnable, }; use bt_hci::controller::{Controller, ControllerCmdSync}; -use bt_hci::param::{AddrKind, FilterDuplicates, LeAdvReport, LeExtAdvReport, ScanningPhy}; -use bt_hci::FromHciBytes; -use embassy_futures::yield_now; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::pipe::DynamicWriter; -use embassy_sync::pipe::{DynamicReader, Pipe}; -use embassy_time::with_deadline; +use bt_hci::param::{AddrKind, FilterDuplicates, ScanningPhy}; +pub use bt_hci::param::{LeAdvReportsIter, LeExtAdvReportsIter}; use embassy_time::Instant; use crate::Central; @@ -25,18 +18,14 @@ use crate::Central; /// /// The buffer size can be tuned if in a noisy environment that /// returns a lot of results. -pub struct Scanner<'d, C: Controller, const BUFFER_SIZE: usize> { - buffer: Pipe, +pub struct Scanner<'d, C: Controller> { central: Central<'d, C>, } -impl<'d, C: Controller, const BUFFER_SIZE: usize> Scanner<'d, C, BUFFER_SIZE> { +impl<'d, C: Controller> Scanner<'d, C> { /// Create a new scanner with the provided central. pub fn new(central: Central<'d, C>) -> Self { - Self { - central, - buffer: Pipe::new(), - } + Self { central } } /// Retrieve the underlying central @@ -57,7 +46,6 @@ impl<'d, C: Controller, const BUFFER_SIZE: usize> Scanner<'d, C, BUFFER_SIZE> { let host = &self.central.stack.host; let drop = crate::host::OnDrop::new(|| { host.scan_command_state.cancel(false); - host.scan_state.stop(); }); host.scan_command_state.request().await; self.central.set_accept_filter(config.filter_accept_list).await?; @@ -80,13 +68,6 @@ impl<'d, C: Controller, const BUFFER_SIZE: usize> Scanner<'d, C, BUFFER_SIZE> { )) .await?; - self.buffer.clear(); - let (reader, writer) = self.buffer.split(); - let writer: DynamicWriter<'_> = writer.into(); - // Safety: writer and reader is dropped by this or scan session - let writer = unsafe { core::mem::transmute(writer) }; - self.central.stack.host.scan_state.reset(writer); - host.command(LeSetExtScanEnable::new( true, FilterDuplicates::Disabled, @@ -96,9 +77,7 @@ impl<'d, C: Controller, const BUFFER_SIZE: usize> Scanner<'d, C, BUFFER_SIZE> { .await?; drop.defuse(); Ok(ScanSession { - reader: reader.into(), command_state: &self.central.stack.host.scan_command_state, - scan_state: &self.central.stack.host.scan_state, deadline: if config.timeout.as_ticks() == 0 { None } else { @@ -121,7 +100,6 @@ impl<'d, C: Controller, const BUFFER_SIZE: usize> Scanner<'d, C, BUFFER_SIZE> { let host = &self.central.stack.host; let drop = crate::host::OnDrop::new(|| { host.scan_command_state.cancel(false); - host.scan_state.stop(); }); host.scan_command_state.request().await; @@ -144,19 +122,10 @@ impl<'d, C: Controller, const BUFFER_SIZE: usize> Scanner<'d, C, BUFFER_SIZE> { ); host.command(params).await?; - self.buffer.clear(); - let (reader, writer) = self.buffer.split(); - // Safety: writer and reader is dropped before we are permitted to create a reader again. - let writer: DynamicWriter<'_> = writer.into(); - let writer = unsafe { core::mem::transmute(writer) }; - self.central.stack.host.scan_state.reset(writer); - host.command(LeSetScanEnable::new(true, true)).await?; drop.defuse(); Ok(ScanSession { - reader: reader.into(), command_state: &self.central.stack.host.scan_command_state, - scan_state: &self.central.stack.host.scan_state, deadline: if config.timeout.as_ticks() == 0 { None } else { @@ -169,89 +138,13 @@ impl<'d, C: Controller, const BUFFER_SIZE: usize> Scanner<'d, C, BUFFER_SIZE> { /// Handle to an active advertiser which can accept connections. pub struct ScanSession<'d, const EXTENDED: bool> { - reader: DynamicReader<'d>, - scan_state: &'d ScanState, command_state: &'d CommandState, deadline: Option, done: bool, } -impl<'d> ScanSession<'d, false> { - /// Process the advertising reports in the provided closure. - pub async fn process(&mut self, mut f: impl FnMut(LeAdvReport)) -> Result<(), Error> { - self.do_process(|data| { - let mut remaining = data; - loop { - match LeAdvReport::from_hci_bytes(remaining) { - Ok((report, rest)) => { - f(report); - remaining = rest; - } - Err(err) => { - //warn!("[scan] error: {:?}, available {}", err, data.len()); - break; - } - } - } - remaining - }) - .await - } -} - -impl<'d> ScanSession<'d, true> { - /// Process the advertising reports in the provided closure. - pub async fn process(&mut self, mut f: impl FnMut(LeExtAdvReport)) -> Result<(), Error> { - self.do_process(|data| { - let mut remaining = data; - loop { - match LeExtAdvReport::from_hci_bytes(remaining) { - Ok((report, rest)) => { - f(report); - remaining = rest; - } - Err(err) => { - //warn!("[scan] error: {:?}, available {}", err, data.len()); - break; - } - } - } - remaining - }) - .await - } -} - -impl ScanSession<'_, EXTENDED> { - async fn do_process(&mut self, mut f: impl FnMut(&[u8]) -> &[u8]) -> Result<(), Error> { - let process_fut = async { - loop { - let data = self.reader.fill_buf().await; - let remaining = f(data); - let consumed = data.len() - remaining.len(); - self.reader.consume(consumed); - yield_now().await; - } - }; - if let Some(deadline) = self.deadline { - let r = with_deadline(deadline, process_fut).await.map_err(|_| Error::Timeout); - self.command_state.cancel(EXTENDED); - self.done = true; - r - } else { - process_fut.await; - self.command_state.cancel(EXTENDED); - self.done = true; - Ok(()) - } - } -} - impl Drop for ScanSession<'_, EXTENDED> { fn drop(&mut self) { - if !self.done { - self.command_state.cancel(EXTENDED); - } - self.scan_state.stop(); + self.command_state.cancel(EXTENDED); } }