Skip to content

Commit

Permalink
various improvements
Browse files Browse the repository at this point in the history
* Use random static address by default
* Use device address as source on nRF
* Add support for filter_accept_list when scanning
* Expose connection info on connect
* Add non-blocking variant of send
  • Loading branch information
lulf committed Apr 3, 2024
1 parent 8c698b8 commit e213f07
Show file tree
Hide file tree
Showing 13 changed files with 284 additions and 100 deletions.
14 changes: 7 additions & 7 deletions examples/nrf-sdc/src/bin/ble_bas_peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![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};
Expand All @@ -12,7 +13,6 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_time::{Duration, Timer};
use nrf_sdc::{self as sdc, mpsl, mpsl::MultiprotocolServiceLayer};
use sdc::rng_pool::RngPool;
use sdc::vendor::ZephyrWriteBdAddr;
use static_cell::StaticCell;
use trouble_host::{
adapter::{Adapter, HostResources},
Expand Down Expand Up @@ -40,8 +40,8 @@ async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! {
fn bd_addr() -> BdAddr {
unsafe {
let ficr = &*pac::FICR::ptr();
let high = u64::from((ficr.deviceid[1].read().bits() & 0x0000ffff) | 0x0000c000);
let addr = high << 32 | u64::from(ficr.deviceid[0].read().bits());
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()))
}
}
Expand Down Expand Up @@ -108,8 +108,8 @@ 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!("Advertising as {:02x}", bd_addr());
unwrap!(ZephyrWriteBdAddr::new(bd_addr()).exec(&sdc).await);
info!("Our address = {:02x}", bd_addr());
unwrap!(LeSetRandomAddr::new(bd_addr()).exec(&sdc).await);
Timer::after(Duration::from_millis(200)).await;

static HOST_RESOURCES: StaticCell<HostResources<NoopRawMutex, L2CAP_CHANNELS_MAX, PACKET_POOL_SIZE, L2CAP_MTU>> =
Expand All @@ -119,11 +119,11 @@ async fn main(spawner: Spawner) {
let adapter: Adapter<'_, NoopRawMutex, _, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> = Adapter::new(sdc, host_resources);
let config = AdvertiseConfig {
params: None,
data: &[
adv_data: &[
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]),
AdStructure::CompleteLocalName(b"Trouble"),
],
scan_data: &[AdStructure::CompleteLocalName(b"Trouble")],
};

let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new();
Expand Down
13 changes: 8 additions & 5 deletions examples/nrf-sdc/src/bin/ble_l2cap_central.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![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};
Expand All @@ -12,7 +13,6 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_time::{Duration, Timer};
use nrf_sdc::{self as sdc, mpsl, mpsl::MultiprotocolServiceLayer};
use sdc::rng_pool::RngPool;
use sdc::vendor::ZephyrWriteBdAddr;
use static_cell::StaticCell;
use trouble_host::{
adapter::{Adapter, HostResources},
Expand Down Expand Up @@ -41,8 +41,8 @@ async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! {
fn bd_addr() -> BdAddr {
unsafe {
let ficr = &*pac::FICR::ptr();
let high = u64::from((ficr.deviceid[1].read().bits() & 0x0000ffff) | 0x0000c000);
let addr = high << 32 | u64::from(ficr.deviceid[0].read().bits());
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()))
}
}
Expand Down Expand Up @@ -117,7 +117,7 @@ async fn main(spawner: Spawner) {
let sdc = unwrap!(build_sdc(sdc_p, &rng, mpsl, &mut sdc_mem));

info!("Our address = {:02x}", bd_addr());
unwrap!(ZephyrWriteBdAddr::new(bd_addr()).exec(&sdc).await);
unwrap!(LeSetRandomAddr::new(bd_addr()).exec(&sdc).await);
Timer::after(Duration::from_millis(200)).await;

static HOST_RESOURCES: StaticCell<HostResources<NoopRawMutex, L2CAP_CHANNELS_MAX, PACKET_POOL_SIZE, L2CAP_MTU>> =
Expand All @@ -126,7 +126,10 @@ async fn main(spawner: Spawner) {

let adapter: Adapter<'_, NoopRawMutex, _, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> = Adapter::new(sdc, host_resources);

let config = ScanConfig { params: None };
let config = ScanConfig {
params: None,
filter_accept_list: &[],
};

// 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]);
Expand Down
14 changes: 6 additions & 8 deletions examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![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};
Expand All @@ -12,7 +13,6 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_time::{Duration, Timer};
use nrf_sdc::{self as sdc, mpsl, mpsl::MultiprotocolServiceLayer};
use sdc::rng_pool::RngPool;
use sdc::vendor::ZephyrWriteBdAddr;
use static_cell::StaticCell;
use trouble_host::{
adapter::{Adapter, HostResources},
Expand Down Expand Up @@ -40,8 +40,8 @@ async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! {
fn bd_addr() -> BdAddr {
unsafe {
let ficr = &*pac::FICR::ptr();
let high = u64::from((ficr.deviceid[1].read().bits() & 0x0000ffff) | 0x0000c000);
let addr = high << 32 | u64::from(ficr.deviceid[0].read().bits());
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()))
}
}
Expand Down Expand Up @@ -116,7 +116,7 @@ async fn main(spawner: Spawner) {
let sdc = unwrap!(build_sdc(sdc_p, &rng, mpsl, &mut sdc_mem));

info!("Our address = {:02x}", bd_addr());
unwrap!(ZephyrWriteBdAddr::new(bd_addr()).exec(&sdc).await);
unwrap!(LeSetRandomAddr::new(bd_addr()).exec(&sdc).await);
Timer::after(Duration::from_millis(200)).await;

static HOST_RESOURCES: StaticCell<HostResources<NoopRawMutex, L2CAP_CHANNELS_MAX, PACKET_POOL_SIZE, L2CAP_MTU>> =
Expand All @@ -127,10 +127,8 @@ async fn main(spawner: Spawner) {

let config = AdvertiseConfig {
params: None,
data: &[
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
AdStructure::CompleteLocalName(b"Trouble"),
],
adv_data: &[AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED)],
scan_data: &[AdStructure::CompleteLocalName(b"Trouble")],
};

let _ = join(adapter.run(), async {
Expand Down
4 changes: 2 additions & 2 deletions examples/serial-hci/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ async fn main() {
let adapter: Adapter<'_, NoopRawMutex, _, 2, 4, 1, 1> = Adapter::new(controller, host_resources);
let config = AdvertiseConfig {
params: None,
data: &[
adv_data: &[
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]),
AdStructure::CompleteLocalName(b"Trouble HCI"),
],
scan_data: &[AdStructure::CompleteLocalName(b"Trouble HCI")],
};

let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new();
Expand Down
93 changes: 75 additions & 18 deletions host/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use crate::types::l2cap::L2capLeSignal;
use crate::{AdapterError, Error};
use bt_hci::cmd::controller_baseband::{Reset, SetEventMask};
use bt_hci::cmd::le::{
LeCreateConn, LeCreateConnParams, LeReadBufferSize, LeSetAdvData, LeSetAdvEnable, LeSetAdvParams, LeSetScanEnable,
LeSetScanParams,
LeAddDeviceToFilterAcceptList, LeClearFilterAcceptList, LeCreateConn, LeCreateConnParams, LeReadBufferSize,
LeSetAdvData, LeSetAdvEnable, LeSetAdvParams, LeSetScanEnable, LeSetScanParams, LeSetScanResponseData,
};
use bt_hci::cmd::link_control::{Disconnect, DisconnectParams};
use bt_hci::cmd::{AsyncCmd, SyncCmd};
Expand All @@ -26,6 +26,7 @@ use bt_hci::event::le::LeEvent;
use bt_hci::event::Event;
use bt_hci::param::{BdAddr, ConnHandle, DisconnectReason, EventMask};
use bt_hci::ControllerToHostPacket;
use core::task::Poll;
use embassy_futures::select::{select, Either};
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::channel::Channel;
Expand Down Expand Up @@ -104,16 +105,33 @@ where
/// 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.
pub async fn scan(&self, config: &ScanConfig) -> Result<ScanReport, AdapterError<T::Error>>
pub async fn scan(&self, config: &ScanConfig<'_>) -> Result<ScanReport, AdapterError<T::Error>>
where
T: ControllerCmdSync<LeSetScanEnable> + ControllerCmdSync<LeSetScanParams>,
T: ControllerCmdSync<LeSetScanEnable>
+ ControllerCmdSync<LeSetScanParams>
+ ControllerCmdSync<LeClearFilterAcceptList>
+ ControllerCmdSync<LeAddDeviceToFilterAcceptList>,
{
LeClearFilterAcceptList::new().exec(&self.controller).await?;

if !config.filter_accept_list.is_empty() {
for entry in config.filter_accept_list {
LeAddDeviceToFilterAcceptList::new(entry.0, *entry.1)
.exec(&self.controller)
.await?;
}
}

let params = config.params.unwrap_or(LeSetScanParams::new(
bt_hci::param::LeScanKind::Active,
bt_hci::param::Duration::from_millis(1_000),
bt_hci::param::Duration::from_millis(1_000),
bt_hci::param::AddrKind::PUBLIC,
bt_hci::param::ScanningFilterPolicy::BasicUnfiltered,
bt_hci::param::AddrKind::RANDOM,
if config.filter_accept_list.is_empty() {
bt_hci::param::ScanningFilterPolicy::BasicUnfiltered
} else {
bt_hci::param::ScanningFilterPolicy::BasicFiltered
},
));
params.exec(&self.controller).await?;

Expand All @@ -130,28 +148,46 @@ where
/// in which case a handle for the connection is returned.
pub async fn advertise<'m>(&'m self, config: &AdvertiseConfig<'_>) -> Result<Connection<'m>, AdapterError<T::Error>>
where
T: ControllerCmdSync<LeSetAdvData> + ControllerCmdSync<LeSetAdvEnable> + ControllerCmdSync<LeSetAdvParams>,
T: ControllerCmdSync<LeSetAdvData>
+ ControllerCmdSync<LeSetAdvEnable>
+ ControllerCmdSync<LeSetAdvParams>
+ ControllerCmdSync<LeSetScanResponseData>,
{
let params = &config.params.unwrap_or(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::PUBLIC,
bt_hci::param::AddrKind::PUBLIC,
bt_hci::param::AddrKind::RANDOM,
bt_hci::param::AddrKind::RANDOM,
BdAddr::default(),
bt_hci::param::AdvChannelMap::ALL,
bt_hci::param::AdvFilterPolicy::default(),
));

params.exec(&self.controller).await?;

let mut data = [0; 31];
let mut w = WriteCursor::new(&mut data[..]);
for item in config.data.iter() {
item.encode(&mut w)?;
if !config.adv_data.is_empty() {
let mut data = [0; 31];
let mut w = WriteCursor::new(&mut data[..]);
for item in config.adv_data.iter() {
item.encode(&mut w)?;
}
let len = w.len();
LeSetAdvData::new(len as u8, data).exec(&self.controller).await?;
}
let len = w.len();
LeSetAdvData::new(len as u8, data).exec(&self.controller).await?;

if !config.scan_data.is_empty() {
let mut data = [0; 31];
let mut w = WriteCursor::new(&mut data[..]);
for item in config.scan_data.iter() {
item.encode(&mut w)?;
}
let len = w.len();
LeSetScanResponseData::new(len as u8, data)
.exec(&self.controller)
.await?;
}

LeSetAdvEnable::new(true).exec(&self.controller).await?;
let conn = Connection::accept(self).await;
LeSetAdvEnable::new(false).exec(&self.controller).await?;
Expand Down Expand Up @@ -197,7 +233,9 @@ where
}

other if other >= L2CAP_CID_DYN_START => match self.channels.dispatch(packet).await {
Ok(_) => {}
Ok(_) => {
info!("L2CAP packet dispatched!");
}
Err(e) => {
warn!("Error dispatching l2cap packet to channel: {:?}", e);
}
Expand Down Expand Up @@ -235,6 +273,7 @@ where
Ok(ControllerToHostPacket::Event(event)) => match event {
Event::Le(event) => match event {
LeEvent::LeConnectionComplete(e) => {
warn!("CONNECTION COMPLET!");
if let Err(err) = self.connections.connect(
e.handle,
ConnectionInfo {
Expand Down Expand Up @@ -359,11 +398,29 @@ where
}

pub struct HciController<'d, T: Controller> {
controller: &'d T,
permits: &'d LocalSemaphore,
pub(crate) controller: &'d T,
pub(crate) permits: &'d LocalSemaphore,
}

impl<'d, T: Controller> HciController<'d, T> {
pub(crate) fn try_send(&self, handle: ConnHandle, pdu: &[u8]) -> Result<(), AdapterError<T::Error>> {
let permit = self
.permits
.try_acquire(1)
.ok_or::<AdapterError<T::Error>>(Error::Busy.into())?;
let acl = AclPacket::new(
handle,
AclPacketBoundary::FirstNonFlushable,
AclBroadcastFlag::PointToPoint,
pdu,
);
let fut = self.controller.write_acl_data(&acl);
match embassy_futures::poll_once(fut) {
Poll::Ready(result) => result.map_err(AdapterError::Controller),
Poll::Pending => Err(Error::Busy.into()),
}
}

pub(crate) async fn send(&self, handle: ConnHandle, pdu: &[u8]) -> Result<(), AdapterError<T::Error>> {
self.permits.acquire(1).await.disarm();
let acl = AclPacket::new(
Expand Down
3 changes: 2 additions & 1 deletion host/src/advertise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use bt_hci::cmd::le::LeSetAdvParams;

pub struct AdvertiseConfig<'d> {
pub params: Option<LeSetAdvParams>,
pub data: &'d [AdStructure<'d>],
pub adv_data: &'d [AdStructure<'d>],
pub scan_data: &'d [AdStructure<'d>],
}

pub const AD_FLAG_LE_LIMITED_DISCOVERABLE: u8 = 0b00000001;
Expand Down
14 changes: 9 additions & 5 deletions host/src/channel_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub struct ChannelManager<'d, M: RawMutex, const CHANNELS: usize, const L2CAP_TX
}

pub trait DynamicChannelManager<'d> {
fn poll_request_to_send(&self, cid: u16, credits: usize, cx: &mut Context<'_>) -> Poll<Result<(), Error>>;
fn poll_request_to_send(&self, cid: u16, credits: usize, cx: Option<&mut Context<'_>>) -> Poll<Result<(), Error>>;
fn confirm_received(&self, cid: u16, credits: usize) -> Result<(ConnHandle, L2capLeSignal), Error>;
fn confirm_disconnected(&self, cid: u16) -> Result<(), Error>;
}
Expand Down Expand Up @@ -227,6 +227,8 @@ impl<'d, M: RawMutex, const CHANNELS: usize, const L2CAP_TXQ: usize, const L2CAP
}),
);

info!("Responding to create: {:?}", response);

controller.signal(conn, response).await?;
Ok((state, self.inbound[idx].receiver().into()))
}
Expand Down Expand Up @@ -302,13 +304,13 @@ impl<'d, M: RawMutex, const CHANNELS: usize, const L2CAP_TXQ: usize, const L2CAP
self.inbound[chan].send(Some(Pdu::new(p, len))).await;
Ok(())
} else {
warn!("No memory for channel {}", packet.channel);
warn!("No memory for channel {} (id {})", packet.channel, chan);
Err(Error::OutOfMemory)
}
}

pub async fn control(&self, conn: ConnHandle, signal: L2capLeSignal) -> Result<(), Error> {
// info!("Inbound signal: {:?}", signal);
info!("Inbound signal: {:?}", signal);
match signal.data {
L2capLeSignalData::LeCreditConnReq(req) => {
self.connect(ConnectingState {
Expand Down Expand Up @@ -414,7 +416,7 @@ impl<'d, M: RawMutex, const CHANNELS: usize, const L2CAP_TXQ: usize, const L2CAP
})
}

fn poll_request_to_send(&self, cid: u16, credits: usize, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
fn poll_request_to_send(&self, cid: u16, credits: usize, cx: Option<&mut Context<'_>>) -> Poll<Result<(), Error>> {
self.state.lock(|state| {
let mut state = state.borrow_mut();
for (idx, storage) in state.channels.iter_mut().enumerate() {
Expand All @@ -424,7 +426,9 @@ impl<'d, M: RawMutex, const CHANNELS: usize, const L2CAP_TXQ: usize, const L2CAP
s.peer_credits -= credits as u16;
return Poll::Ready(Ok(()));
} else {
state.credit_wakers[idx].register(cx.waker());
if let Some(cx) = cx {
state.credit_wakers[idx].register(cx.waker());
}
return Poll::Pending;
}
}
Expand Down
Loading

0 comments on commit e213f07

Please sign in to comment.