diff --git a/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs b/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs index c728257a..3433632a 100644 --- a/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs +++ b/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs @@ -2,9 +2,6 @@ #![no_main] #![feature(impl_trait_in_assoc_type)] -use bt_hci::cmd::le::LeSetRandomAddr; -use bt_hci::cmd::SyncCmd; -use bt_hci::param::BdAddr; use defmt::{error, info, unwrap}; use embassy_executor::Spawner; use embassy_futures::join::join3; @@ -16,9 +13,9 @@ use sdc::rng_pool::RngPool; use static_cell::StaticCell; use trouble_host::{ adapter::{Adapter, HostResources}, - advertise::{AdStructure, AdvertiseConfig, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, + advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, attribute::{AttributeTable, CharacteristicProp, Service, Uuid}, - PacketQos, + Address, PacketQos, }; use {defmt_rtt as _, panic_probe as _}; @@ -37,12 +34,12 @@ async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! { mpsl.run().await } -fn bd_addr() -> BdAddr { +fn my_addr() -> Address { unsafe { let ficr = &*pac::FICR::ptr(); let high = u64::from((ficr.deviceaddr[1].read().bits() & 0x0000ffff) | 0x0000c000); let addr = high << 32 | u64::from(ficr.deviceaddr[0].read().bits()); - BdAddr::new(unwrap!(addr.to_le_bytes()[..6].try_into())) + Address::random(unwrap!(addr.to_le_bytes()[..6].try_into())) } } @@ -98,8 +95,8 @@ async fn main(spawner: Spawner) { spawner.must_spawn(mpsl_task(&*mpsl)); let sdc_p = sdc::Peripherals::new( - pac_p.ECB, pac_p.AAR, p.NVMC, 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, + pac_p.ECB, pac_p.AAR, 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 pool = [0; 256]; @@ -108,8 +105,7 @@ async fn main(spawner: Spawner) { let mut sdc_mem = sdc::Mem::<3312>::new(); let sdc = unwrap!(build_sdc(sdc_p, &rng, mpsl, &mut sdc_mem)); - info!("Our address = {:02x}", bd_addr()); - unwrap!(LeSetRandomAddr::new(bd_addr()).exec(&sdc).await); + info!("Our address = {:02x}", my_addr()); Timer::after(Duration::from_millis(200)).await; static HOST_RESOURCES: StaticCell> = @@ -117,14 +113,6 @@ async fn main(spawner: Spawner) { let host_resources = HOST_RESOURCES.init(HostResources::new(PacketQos::None)); let adapter: Adapter<'_, NoopRawMutex, _, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> = Adapter::new(sdc, host_resources); - let config = AdvertiseConfig { - params: None, - adv_data: &[ - AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), - AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]), - ], - scan_data: &[AdStructure::CompleteLocalName(b"Trouble")], - }; let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new(); @@ -152,6 +140,7 @@ async fn main(spawner: Spawner) { }; let server = adapter.gatt_server(&table); + unwrap!(adapter.set_random_address(my_addr()).await); info!("Starting advertising and GATT service"); let _ = join3( @@ -169,7 +158,20 @@ async fn main(spawner: Spawner) { } }, async { - let conn = unwrap!(adapter.advertise(&config).await); + let conn = unwrap!( + adapter + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &[ + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]), + ], + scan_data: &[AdStructure::CompleteLocalName(b"Trouble")], + } + ) + .await + ); // Keep connection alive let mut tick: u8 = 0; loop { diff --git a/examples/nrf-sdc/src/bin/ble_l2cap_central.rs b/examples/nrf-sdc/src/bin/ble_l2cap_central.rs index fe3b4716..c9502602 100644 --- a/examples/nrf-sdc/src/bin/ble_l2cap_central.rs +++ b/examples/nrf-sdc/src/bin/ble_l2cap_central.rs @@ -2,9 +2,6 @@ #![no_main] #![feature(impl_trait_in_assoc_type)] -use bt_hci::cmd::le::LeSetRandomAddr; -use bt_hci::cmd::SyncCmd; -use bt_hci::param::BdAddr; use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_futures::join::join; @@ -19,7 +16,7 @@ use trouble_host::{ connection::Connection, l2cap::L2capChannel, scan::ScanConfig, - PacketQos, + Address, PacketQos, }; use {defmt_rtt as _, panic_probe as _}; @@ -38,12 +35,12 @@ async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! { mpsl.run().await } -fn bd_addr() -> BdAddr { +fn my_addr() -> Address { unsafe { let ficr = &*pac::FICR::ptr(); let high = u64::from((ficr.deviceaddr[1].read().bits() & 0x0000ffff) | 0x0000c000); let addr = high << 32 | u64::from(ficr.deviceaddr[0].read().bits()); - BdAddr::new(unwrap!(addr.to_le_bytes()[..6].try_into())) + Address::random(unwrap!(addr.to_le_bytes()[..6].try_into())) } } @@ -106,8 +103,8 @@ async fn main(spawner: Spawner) { spawner.must_spawn(mpsl_task(&*mpsl)); let sdc_p = sdc::Peripherals::new( - pac_p.ECB, pac_p.AAR, p.NVMC, 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, + pac_p.ECB, pac_p.AAR, 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 pool = [0; 256]; @@ -116,8 +113,7 @@ async fn main(spawner: Spawner) { let mut sdc_mem = sdc::Mem::<6544>::new(); let sdc = unwrap!(build_sdc(sdc_p, &rng, mpsl, &mut sdc_mem)); - info!("Our address = {:02x}", bd_addr()); - unwrap!(LeSetRandomAddr::new(bd_addr()).exec(&sdc).await); + info!("Our address = {:02x}", my_addr()); Timer::after(Duration::from_millis(200)).await; static HOST_RESOURCES: StaticCell> = @@ -132,7 +128,8 @@ async fn main(spawner: Spawner) { }; // NOTE: Modify this to match the address of the peripheral you want to connect to - let target: BdAddr = BdAddr::new([0xf5, 0x9f, 0x1a, 0x05, 0xe4, 0xee]); + let target: Address = Address::random([0xf5, 0x9f, 0x1a, 0x05, 0xe4, 0xee]); + unwrap!(adapter.set_random_address(my_addr()).await); info!("Scanning for peripheral..."); let _ = join(adapter.run(), async { @@ -140,7 +137,7 @@ async fn main(spawner: Spawner) { let reports = unwrap!(adapter.scan(&config).await); for report in reports.iter() { let report = report.unwrap(); - if report.addr == target { + if report.addr == target.addr { let conn = Connection::connect(&adapter, report.addr).await; info!("Connected, creating l2cap channel"); const PAYLOAD_LEN: usize = 27; diff --git a/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs b/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs index c19bd9f1..4f28574b 100644 --- a/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs +++ b/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs @@ -2,9 +2,6 @@ #![no_main] #![feature(impl_trait_in_assoc_type)] -use bt_hci::cmd::le::LeSetRandomAddr; -use bt_hci::cmd::SyncCmd; -use bt_hci::param::BdAddr; use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_futures::join::join; @@ -16,9 +13,9 @@ use sdc::rng_pool::RngPool; use static_cell::StaticCell; use trouble_host::{ adapter::{Adapter, HostResources}, - advertise::{AdStructure, AdvertiseConfig, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, + advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, l2cap::L2capChannel, - PacketQos, + Address, PacketQos, }; use {defmt_rtt as _, panic_probe as _}; @@ -37,12 +34,12 @@ async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! { mpsl.run().await } -fn bd_addr() -> BdAddr { +fn my_addr() -> Address { unsafe { let ficr = &*pac::FICR::ptr(); let high = u64::from((ficr.deviceaddr[1].read().bits() & 0x0000ffff) | 0x0000c000); let addr = high << 32 | u64::from(ficr.deviceaddr[0].read().bits()); - BdAddr::new(unwrap!(addr.to_le_bytes()[..6].try_into())) + Address::random(unwrap!(addr.to_le_bytes()[..6].try_into())) } } @@ -105,8 +102,8 @@ async fn main(spawner: Spawner) { spawner.must_spawn(mpsl_task(&*mpsl)); let sdc_p = sdc::Peripherals::new( - pac_p.ECB, pac_p.AAR, p.NVMC, 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, + pac_p.ECB, pac_p.AAR, 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 pool = [0; 256]; @@ -115,8 +112,7 @@ async fn main(spawner: Spawner) { let mut sdc_mem = sdc::Mem::<6224>::new(); let sdc = unwrap!(build_sdc(sdc_p, &rng, mpsl, &mut sdc_mem)); - info!("Our address = {:02x}", bd_addr()); - unwrap!(LeSetRandomAddr::new(bd_addr()).exec(&sdc).await); + info!("Our address = {:02x}", my_addr()); Timer::after(Duration::from_millis(200)).await; static HOST_RESOURCES: StaticCell> = @@ -125,16 +121,22 @@ async fn main(spawner: Spawner) { let adapter: Adapter<'_, NoopRawMutex, _, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> = Adapter::new(sdc, host_resources); - let config = AdvertiseConfig { - params: None, - adv_data: &[AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED)], - scan_data: &[AdStructure::CompleteLocalName(b"Trouble")], - }; + unwrap!(adapter.set_random_address(my_addr()).await); let _ = join(adapter.run(), async { loop { info!("Advertising, waiting for connection..."); - let conn = unwrap!(adapter.advertise(&config).await); + let conn = unwrap!( + adapter + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &[AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),], + scan_data: &[AdStructure::CompleteLocalName(b"Trouble")], + } + ) + .await + ); info!("Connection established"); diff --git a/examples/serial-hci/src/main.rs b/examples/serial-hci/src/main.rs index 0a70584c..010f4b07 100644 --- a/examples/serial-hci/src/main.rs +++ b/examples/serial-hci/src/main.rs @@ -10,7 +10,7 @@ use tokio_serial::SerialStream; use tokio_serial::{DataBits, Parity, StopBits}; use trouble_host::{ adapter::{Adapter, HostResources}, - advertise::{AdStructure, AdvertiseConfig, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, + advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, attribute::{AttributeTable, CharacteristicProp, Service, Uuid}, PacketQos, }; @@ -62,14 +62,6 @@ async fn main() { let host_resources = HOST_RESOURCES.init(HostResources::new(PacketQos::None)); let adapter: Adapter<'_, NoopRawMutex, _, 2, 4, 1, 1> = Adapter::new(controller, host_resources); - let config = AdvertiseConfig { - params: None, - adv_data: &[ - AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), - AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]), - ], - scan_data: &[AdStructure::CompleteLocalName(b"Trouble HCI")], - }; let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new(); @@ -114,7 +106,19 @@ async fn main() { } }, async { - let conn = adapter.advertise(&config).await.unwrap(); + let conn = adapter + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &[ + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]), + ], + scan_data: &[AdStructure::CompleteLocalName(b"Trouble HCI")], + }, + ) + .await + .unwrap(); // Keep connection alive let mut tick: u8 = 0; loop { diff --git a/host/src/adapter.rs b/host/src/adapter.rs index 74f1bd03..dd78eef4 100644 --- a/host/src/adapter.rs +++ b/host/src/adapter.rs @@ -1,4 +1,4 @@ -use crate::advertise::{AdvParams, AdvertiseConfig}; +use crate::advertise::{AdvertisementConfig, AdvertisementKind, RawAdvertisement}; use crate::channel_manager::ChannelManager; use crate::connection::Connection; use crate::connection_manager::{ConnectionInfo, ConnectionManager}; @@ -8,12 +8,13 @@ use crate::packet_pool::{DynamicPacketPool, PacketPool, Qos}; use crate::pdu::Pdu; use crate::scan::{ScanConfig, ScanReport}; use crate::types::l2cap::L2capLeSignal; +use crate::Address; use crate::{AdapterError, Error}; use bt_hci::cmd::controller_baseband::{Reset, SetEventMask}; use bt_hci::cmd::le::{ LeAddDeviceToFilterAcceptList, LeClearAdvSets, LeClearFilterAcceptList, LeCreateConn, LeCreateConnParams, LeReadBufferSize, LeSetAdvData, LeSetAdvEnable, LeSetAdvParams, LeSetExtAdvEnable, LeSetExtAdvParams, - LeSetScanEnable, LeSetScanParams, LeSetScanResponseData, + LeSetRandomAddr, LeSetScanEnable, LeSetScanParams, LeSetScanResponseData, }; use bt_hci::cmd::link_control::{Disconnect, DisconnectParams}; use bt_hci::cmd::{AsyncCmd, SyncCmd}; @@ -22,7 +23,7 @@ use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync}; use bt_hci::data::{AclBroadcastFlag, AclPacket, AclPacketBoundary}; use bt_hci::event::le::LeEvent; use bt_hci::event::Event; -use bt_hci::param::{BdAddr, ConnHandle, DisconnectReason, EventMask}; +use bt_hci::param::{AddrKind, AdvHandle, BdAddr, ConnHandle, DisconnectReason, EventMask}; use bt_hci::ControllerToHostPacket; use core::task::Poll; use embassy_futures::select::{select, Either}; @@ -58,6 +59,7 @@ pub struct Adapter< > where M: RawMutex, { + pub(crate) address: Option
, pub(crate) controller: T, pub(crate) connections: ConnectionManager, pub(crate) channels: ChannelManager<'d, M, CHANNELS, L2CAP_TXQ, L2CAP_RXQ>, @@ -92,6 +94,7 @@ where host_resources: &'d mut HostResources, ) -> Self { Self { + address: None, controller, connections: ConnectionManager::new(), channels: ChannelManager::new(&host_resources.pool), @@ -103,6 +106,14 @@ where } } + pub async fn set_random_address(&self, address: Address) -> Result<(), AdapterError> + where + T: ControllerCmdSync, + { + LeSetRandomAddr::new(address.addr).exec(&self.controller).await?; + Ok(()) + } + /// 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. @@ -149,7 +160,8 @@ where /// in which case a handle for the connection is returned. pub async fn advertise<'m, 'k>( &'m self, - config: &AdvertiseConfig<'k>, + config: &AdvertisementConfig, + params: impl Into>, ) -> Result, AdapterError> where T: ControllerCmdSync @@ -160,51 +172,72 @@ where + ControllerCmdSync> + ControllerCmdSync, { - match config.params.as_ref() { - Some(AdvParams::Standard(params)) => { - // May fail if already disabled - let _ = LeSetAdvEnable::new(false).exec(&self.controller).await; - - params.exec(&self.controller).await?; - } - Some(AdvParams::Extended(params)) => { - let _ = LeSetExtAdvEnable::new(false, &[]).exec(&self.controller).await; - let _ = LeClearAdvSets::new().exec(&self.controller).await; - params.exec(&self.controller).await?; - } - None => { - // May fail if already disabled - let _ = LeSetAdvEnable::new(false).exec(&self.controller).await; - + // May fail if already disabled + let _ = LeSetAdvEnable::new(false).exec(&self.controller).await; + let _ = LeSetExtAdvEnable::new(false, &[]).exec(&self.controller).await; + let _ = LeClearAdvSets::new().exec(&self.controller).await; + + let params = params.into(); + match params.kind { + AdvertisementKind::Legacy(kind) => { + let peer = params.peer.unwrap_or(Address { + kind: AddrKind::RANDOM, + addr: BdAddr::default(), + }); LeSetAdvParams::new( - bt_hci::param::Duration::from_millis(400), - bt_hci::param::Duration::from_millis(400), - bt_hci::param::AdvKind::AdvInd, - bt_hci::param::AddrKind::RANDOM, - bt_hci::param::AddrKind::RANDOM, - BdAddr::default(), - bt_hci::param::AdvChannelMap::ALL, - bt_hci::param::AdvFilterPolicy::default(), + bt_hci::param::Duration::from_micros(config.interval_min.as_micros()), + bt_hci::param::Duration::from_micros(config.interval_min.as_micros()), + kind, + self.address.map(|a| a.kind).unwrap_or(AddrKind::RANDOM), + peer.kind, + peer.addr, + config.channel_map, + config.filter_policy, + ) + .exec(&self.controller) + .await?; + } + AdvertisementKind::Extended(props) => { + let peer = params.peer.unwrap_or(Address { + kind: AddrKind::RANDOM, + addr: BdAddr::default(), + }); + LeSetExtAdvParams::new( + AdvHandle::new(0), + props, + bt_hci::param::ExtDuration::from_micros(config.interval_min.as_micros()), + bt_hci::param::ExtDuration::from_micros(config.interval_min.as_micros()), + config.channel_map, + self.address.map(|a| a.kind).unwrap_or(AddrKind::RANDOM), + peer.kind, + peer.addr, + config.filter_policy, + config.tx_power as i8, + config.primary_phy, + 0, + config.secondary_phy, + params.set_id, + params.anonymous, ) .exec(&self.controller) .await?; } } - if !config.adv_data.is_empty() { + if !params.adv_data.is_empty() { let mut data = [0; 31]; let mut w = WriteCursor::new(&mut data[..]); - for item in config.adv_data.iter() { + for item in params.adv_data.iter() { item.encode(&mut w)?; } let len = w.len(); LeSetAdvData::new(len as u8, data).exec(&self.controller).await?; } - if !config.scan_data.is_empty() { + if !params.scan_data.is_empty() { let mut data = [0; 31]; let mut w = WriteCursor::new(&mut data[..]); - for item in config.scan_data.iter() { + for item in params.scan_data.iter() { item.encode(&mut w)?; } let len = w.len(); @@ -213,16 +246,19 @@ where .await?; } - if let Some(AdvParams::Extended(_)) = &config.params { - LeSetExtAdvEnable::new(true, &[]).exec(&self.controller).await?; - let conn = Connection::accept(self).await; - LeSetExtAdvEnable::new(false, &[]).exec(&self.controller).await?; - Ok(conn) - } else { - LeSetAdvEnable::new(true).exec(&self.controller).await?; - let conn = Connection::accept(self).await; - LeSetAdvEnable::new(false).exec(&self.controller).await?; - Ok(conn) + match params.kind { + AdvertisementKind::Legacy(_) => { + LeSetAdvEnable::new(true).exec(&self.controller).await?; + let conn = Connection::accept(self).await; + LeSetAdvEnable::new(false).exec(&self.controller).await?; + Ok(conn) + } + AdvertisementKind::Extended(_) => { + LeSetExtAdvEnable::new(true, &[]).exec(&self.controller).await?; + let conn = Connection::accept(self).await; + LeSetExtAdvEnable::new(false, &[]).exec(&self.controller).await?; + Ok(conn) + } } } diff --git a/host/src/advertise.rs b/host/src/advertise.rs index deb3606d..f4a582c8 100644 --- a/host/src/advertise.rs +++ b/host/src/advertise.rs @@ -2,32 +2,290 @@ use crate::{ codec, cursor::{ReadCursor, WriteCursor}, types::uuid::Uuid, + Address, }; -use bt_hci::cmd::le::{LeSetAdvParams, LeSetExtAdvParams}; +use bt_hci::param::{AdvChannelMap, AdvEventProps, AdvFilterPolicy, AdvKind, PhyKind}; +use embassy_time::Duration; + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Eq, PartialEq, Copy, Clone)] +#[repr(i8)] +pub enum TxPower { + Minus40dBm = -40, + Minus20dBm = -20, + Minus16dBm = -16, + Minus12dBm = -12, + Minus8dBm = -8, + Minus4dBm = -4, + ZerodBm = 0, + #[cfg(feature = "s140")] + Plus2dBm = 2, + Plus3dBm = 3, + Plus4dBm = 4, + #[cfg(feature = "s140")] + Plus5dBm = 5, + #[cfg(feature = "s140")] + Plus6dBm = 6, + #[cfg(feature = "s140")] + Plus7dBm = 7, + #[cfg(feature = "s140")] + Plus8dBm = 8, +} + +#[derive(Copy, Clone)] +pub struct AdvertisementConfig { + pub primary_phy: PhyKind, + pub secondary_phy: PhyKind, + pub tx_power: TxPower, + + /// Timeout duration + pub timeout: Option, + pub max_events: Option, + + /// Advertising interval + pub interval_min: Duration, + pub interval_max: Duration, + + pub channel_map: AdvChannelMap, + pub filter_policy: AdvFilterPolicy, +} + +impl Default for AdvertisementConfig { + fn default() -> Self { + Self { + primary_phy: PhyKind::Le1M, + secondary_phy: PhyKind::Le1M, + tx_power: TxPower::ZerodBm, + timeout: None, + max_events: None, + interval_min: Duration::from_millis(250), + interval_max: Duration::from_millis(250), + filter_policy: AdvFilterPolicy::default(), + channel_map: AdvChannelMap::ALL, + } + } +} + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub(crate) enum AdvertisementKind { + Legacy(AdvKind), + Extended(AdvEventProps), +} #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct AdvertiseConfig<'d> { - pub params: Option, - pub adv_data: &'d [AdStructure<'d>], - pub scan_data: &'d [AdStructure<'d>], +pub struct RawAdvertisement<'d> { + pub(crate) kind: AdvertisementKind, + pub(crate) adv_data: &'d [AdStructure<'d>], + pub(crate) scan_data: &'d [AdStructure<'d>], + pub(crate) peer: Option
, + pub(crate) anonymous: bool, + pub(crate) set_id: u8, } -impl<'d> Default for AdvertiseConfig<'d> { +impl<'d> Default for RawAdvertisement<'d> { fn default() -> Self { Self { - params: None, + kind: AdvertisementKind::Legacy(AdvKind::AdvInd), adv_data: &[], scan_data: &[], + peer: None, + anonymous: false, + set_id: 0, } } } -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum AdvParams { - Standard(LeSetAdvParams), - Extended(LeSetExtAdvParams), +/// Legacy advertisement types, which works with BLE 4.0 and newer +pub enum Advertisement<'d> { + ConnectableScannableUndirected { + adv_data: &'d [AdStructure<'d>], + scan_data: &'d [AdStructure<'d>], + }, + ConnectableNonscannableDirected { + peer: Address, + }, + ConnectableNonscannableDirectedHighDuty { + peer: Address, + }, + NonconnectableScannableUndirected { + adv_data: &'d [AdStructure<'d>], + scan_data: &'d [AdStructure<'d>], + }, + NonconnectableNonscannableUndirected { + adv_data: &'d [AdStructure<'d>], + }, +} + +/// Extended advertisement types, which works with BLE 5.0 and newer +pub enum ExtendedAdvertisement<'d> { + ConnectableNonscannableUndirected { + set_id: u8, + adv_data: &'d [AdStructure<'d>], + }, + ConnectableNonscannableDirected { + set_id: u8, + peer: Address, + adv_data: &'d [AdStructure<'d>], + }, + NonconnectableScannableUndirected { + set_id: u8, + scan_data: &'d [AdStructure<'d>], + }, + NonconnectableScannableDirected { + set_id: u8, + peer: Address, + scan_data: &'d [AdStructure<'d>], + }, + NonconnectableNonscannableUndirected { + set_id: u8, + anonymous: bool, + adv_data: &'d [AdStructure<'d>], + }, + NonconnectableNonscannableDirected { + set_id: u8, + anonymous: bool, + peer: Address, + adv_data: &'d [AdStructure<'d>], + }, +} + +impl<'d> From> for RawAdvertisement<'d> { + fn from(val: Advertisement<'d>) -> RawAdvertisement<'d> { + match val { + Advertisement::ConnectableScannableUndirected { adv_data, scan_data } => RawAdvertisement { + kind: AdvertisementKind::Legacy(AdvKind::AdvInd), + adv_data, + scan_data, + peer: None, + anonymous: false, + set_id: 0, + }, + Advertisement::ConnectableNonscannableDirected { peer } => RawAdvertisement { + kind: AdvertisementKind::Legacy(AdvKind::AdvDirectIndLow), + adv_data: &[], + scan_data: &[], + peer: Some(peer), + anonymous: false, + set_id: 0, + }, + Advertisement::ConnectableNonscannableDirectedHighDuty { peer } => RawAdvertisement { + kind: AdvertisementKind::Legacy(AdvKind::AdvDirectIndHigh), + adv_data: &[], + scan_data: &[], + peer: Some(peer), + anonymous: false, + set_id: 0, + }, + Advertisement::NonconnectableScannableUndirected { adv_data, scan_data } => RawAdvertisement { + kind: AdvertisementKind::Legacy(AdvKind::AdvScanInd), + adv_data, + scan_data, + peer: None, + anonymous: false, + set_id: 0, + }, + Advertisement::NonconnectableNonscannableUndirected { adv_data } => RawAdvertisement { + kind: AdvertisementKind::Legacy(AdvKind::AdvNonconnInd), + adv_data, + scan_data: &[], + peer: None, + anonymous: false, + set_id: 0, + }, + } + } +} + +impl<'d> From> for RawAdvertisement<'d> { + fn from(val: ExtendedAdvertisement<'d>) -> RawAdvertisement<'d> { + match val { + ExtendedAdvertisement::ConnectableNonscannableUndirected { adv_data, set_id } => RawAdvertisement { + kind: AdvertisementKind::Extended( + AdvEventProps::new().set_connectable_adv(true).set_scannable_adv(false), + ), + adv_data, + scan_data: &[], + peer: None, + anonymous: false, + set_id, + }, + ExtendedAdvertisement::ConnectableNonscannableDirected { adv_data, peer, set_id } => RawAdvertisement { + kind: AdvertisementKind::Extended( + AdvEventProps::new().set_connectable_adv(true).set_scannable_adv(false), + ), + adv_data, + scan_data: &[], + peer: Some(peer), + anonymous: false, + set_id, + }, + + ExtendedAdvertisement::NonconnectableScannableUndirected { scan_data, set_id } => RawAdvertisement { + kind: AdvertisementKind::Extended( + AdvEventProps::new().set_connectable_adv(false).set_scannable_adv(false), + ), + adv_data: &[], + scan_data, + peer: None, + anonymous: false, + set_id, + }, + ExtendedAdvertisement::NonconnectableScannableDirected { + scan_data, + peer, + set_id, + } => RawAdvertisement { + kind: AdvertisementKind::Extended( + AdvEventProps::new() + .set_connectable_adv(false) + .set_scannable_adv(true) + .set_directed_adv(true), + ), + adv_data: &[], + scan_data, + peer: Some(peer), + anonymous: false, + set_id, + }, + ExtendedAdvertisement::NonconnectableNonscannableUndirected { + adv_data, + anonymous, + set_id, + } => RawAdvertisement { + kind: AdvertisementKind::Extended( + AdvEventProps::new() + .set_connectable_adv(false) + .set_scannable_adv(false) + .set_directed_adv(false), + ), + adv_data, + scan_data: &[], + peer: None, + anonymous, + set_id, + }, + ExtendedAdvertisement::NonconnectableNonscannableDirected { + adv_data, + peer, + anonymous, + set_id, + } => RawAdvertisement { + kind: AdvertisementKind::Extended( + AdvEventProps::new() + .set_connectable_adv(false) + .set_scannable_adv(false) + .set_directed_adv(true), + ), + adv_data, + scan_data: &[], + peer: Some(peer), + anonymous, + set_id, + }, + } + } } pub const AD_FLAG_LE_LIMITED_DISCOVERABLE: u8 = 0b00000001; diff --git a/host/src/lib.rs b/host/src/lib.rs index 1b165d80..f0d7b708 100644 --- a/host/src/lib.rs +++ b/host/src/lib.rs @@ -4,7 +4,10 @@ #![allow(unused_variables)] use advertise::AdvertisementDataError; -use bt_hci::FromHciBytesError; +use bt_hci::{ + param::{AddrKind, BdAddr}, + FromHciBytesError, +}; mod fmt; @@ -32,6 +35,22 @@ mod attribute_server; #[cfg(feature = "gatt")] pub mod gatt; +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +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), + } + } +} + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AdapterError { diff --git a/host/tests/l2cap.rs b/host/tests/l2cap.rs index fed30199..5c6c03ad 100644 --- a/host/tests/l2cap.rs +++ b/host/tests/l2cap.rs @@ -11,7 +11,7 @@ use tokio_serial::SerialStream; use tokio_serial::{DataBits, Parity, StopBits}; use trouble_host::{ adapter::{Adapter, HostResources}, - advertise::{AdStructure, AdvertiseConfig, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, + advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, connection::Connection, l2cap::L2capChannel, scan::ScanConfig, @@ -75,15 +75,6 @@ async fn l2cap_connection_oriented_channels() { let adapter: Adapter<'_, NoopRawMutex, _, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> = Adapter::new(controller_peripheral, &mut host_resources); - let config = AdvertiseConfig { - params: None, - adv_data: &[ - AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), - AdStructure::CompleteLocalName(b"trouble-l2cap-int"), - ], - scan_data: &[], - }; - select! { r = adapter.run() => { r @@ -91,7 +82,13 @@ async fn l2cap_connection_oriented_channels() { r = async { loop { println!("[peripheral] advertising"); - let conn = adapter.advertise(&config).await?; + let conn = adapter.advertise(&Default::default(), Advertisement::ConnectableScannableUndirected { + adv_data: &[ + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::CompleteLocalName(b"trouble-l2cap-int"), + ], + scan_data: &[], + }).await?; println!("[peripheral] connected"); let mut ch1: L2capChannel<'_, '_, _, 27> = @@ -131,7 +128,7 @@ async fn l2cap_connection_oriented_channels() { HostResources::new(PacketQos::Guaranteed(4)); let adapter: Adapter<'_, NoopRawMutex, _, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> = - Adapter::new(controller_central, &mut host_resources); + Adapter::new(controller_central, &mut host_resources, None); let config = ScanConfig { params: None,