summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <dirbaio@dirbaio.net>2021-06-19 03:21:36 +0200
committerDario Nieuwenhuis <dirbaio@dirbaio.net>2021-06-19 03:59:51 +0200
commited4d42f88622c31003e5594afecbdb31441657f4 (patch)
tree2890a9d77aa29fb89cbe678be1dfcf4194a85f30
parentf679f23dfba4e8b8c76fe5577498bb6f9434846f (diff)
downloadnrf-softdevice-ed4d42f88622c31003e5594afecbdb31441657f4.zip
Add non-connectable advertisements.
-rw-r--r--examples/src/bin/ble_advertise.rs98
-rw-r--r--examples/src/bin/ble_bas_peripheral.rs2
-rw-r--r--examples/src/bin/ble_l2cap_peripheral.rs2
-rw-r--r--examples/src/bin/ble_peripheral_onoff.rs4
-rw-r--r--nrf-softdevice/src/ble/peripheral.rs222
5 files changed, 271 insertions, 57 deletions
diff --git a/examples/src/bin/ble_advertise.rs b/examples/src/bin/ble_advertise.rs
new file mode 100644
index 0000000..b65ae79
--- /dev/null
+++ b/examples/src/bin/ble_advertise.rs
@@ -0,0 +1,98 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+#![feature(min_type_alias_impl_trait)]
+#![feature(impl_trait_in_bindings)]
+#![feature(alloc_error_handler)]
+#![allow(incomplete_features)]
+
+#[path = "../example_common.rs"]
+mod example_common;
+
+use core::mem;
+use cortex_m_rt::entry;
+use defmt::{info, unreachable, *};
+use embassy::executor::Executor;
+use embassy::util::Forever;
+
+use nrf_softdevice::ble::peripheral;
+use nrf_softdevice::{raw, Softdevice};
+
+static EXECUTOR: Forever<Executor> = Forever::new();
+
+#[embassy::task]
+async fn softdevice_task(sd: &'static Softdevice) {
+ sd.run().await;
+}
+
+#[embassy::task]
+async fn bluetooth_task(sd: &'static Softdevice) {
+ #[rustfmt::skip]
+ let adv_data = &[
+ 0x02, 0x01, raw::BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE as u8,
+ 0x03, 0x03, 0x09, 0x18,
+ 0x0a, 0x09, b'H', b'e', b'l', b'l', b'o', b'R', b'u', b's', b't',
+ ];
+ #[rustfmt::skip]
+ let scan_data = &[
+ 0x03, 0x03, 0x09, 0x18,
+ ];
+
+ let mut config = peripheral::Config::default();
+ config.interval = 50;
+ let adv = peripheral::NonconnectableAdvertisement::ScannableUndirected {
+ adv_data,
+ scan_data,
+ };
+ unwrap!(peripheral::advertise(sd, adv, &config).await);
+
+ // advertise never returns
+ unreachable!();
+}
+
+#[entry]
+fn main() -> ! {
+ info!("Hello World!");
+
+ let config = nrf_softdevice::Config {
+ clock: Some(raw::nrf_clock_lf_cfg_t {
+ source: raw::NRF_CLOCK_LF_SRC_XTAL as u8,
+ rc_ctiv: 0,
+ rc_temp_ctiv: 0,
+ accuracy: 7,
+ }),
+ conn_gap: Some(raw::ble_gap_conn_cfg_t {
+ conn_count: 6,
+ event_length: 24,
+ }),
+ conn_gatt: Some(raw::ble_gatt_conn_cfg_t { att_mtu: 256 }),
+ gatts_attr_tab_size: Some(raw::ble_gatts_cfg_attr_tab_size_t {
+ attr_tab_size: 32768,
+ }),
+ gap_role_count: Some(raw::ble_gap_cfg_role_count_t {
+ adv_set_count: 1,
+ periph_role_count: 3,
+ central_role_count: 3,
+ central_sec_count: 0,
+ _bitfield_1: raw::ble_gap_cfg_role_count_t::new_bitfield_1(0),
+ }),
+ gap_device_name: Some(raw::ble_gap_cfg_device_name_t {
+ p_value: b"HelloRust" as *const u8 as _,
+ current_len: 9,
+ max_len: 9,
+ write_perm: unsafe { mem::zeroed() },
+ _bitfield_1: raw::ble_gap_cfg_device_name_t::new_bitfield_1(
+ raw::BLE_GATTS_VLOC_STACK as u8,
+ ),
+ }),
+ ..Default::default()
+ };
+
+ let sd = Softdevice::enable(&config);
+
+ let executor = EXECUTOR.put(Executor::new());
+ executor.run(|spawner| {
+ unwrap!(spawner.spawn(softdevice_task(sd)));
+ unwrap!(spawner.spawn(bluetooth_task(sd)));
+ });
+}
diff --git a/examples/src/bin/ble_bas_peripheral.rs b/examples/src/bin/ble_bas_peripheral.rs
index 949a5cc..b9b1221 100644
--- a/examples/src/bin/ble_bas_peripheral.rs
+++ b/examples/src/bin/ble_bas_peripheral.rs
@@ -54,7 +54,7 @@ async fn bluetooth_task(sd: &'static Softdevice) {
adv_data,
scan_data,
};
- let conn = unwrap!(peripheral::advertise(sd, adv, &config).await);
+ let conn = unwrap!(peripheral::advertise_connectable(sd, adv, &config).await);
info!("advertising done!");
diff --git a/examples/src/bin/ble_l2cap_peripheral.rs b/examples/src/bin/ble_l2cap_peripheral.rs
index 0ab7369..d752ef7 100644
--- a/examples/src/bin/ble_l2cap_peripheral.rs
+++ b/examples/src/bin/ble_l2cap_peripheral.rs
@@ -51,7 +51,7 @@ async fn bluetooth_task(sd: &'static Softdevice) {
adv_data,
scan_data,
};
- let conn = unwrap!(peripheral::advertise(sd, adv, &config).await);
+ let conn = unwrap!(peripheral::advertise_connectable(sd, adv, &config).await);
info!("advertising done!");
diff --git a/examples/src/bin/ble_peripheral_onoff.rs b/examples/src/bin/ble_peripheral_onoff.rs
index f61d1b3..eb74814 100644
--- a/examples/src/bin/ble_peripheral_onoff.rs
+++ b/examples/src/bin/ble_peripheral_onoff.rs
@@ -54,7 +54,7 @@ async fn run_bluetooth(sd: &'static Softdevice, server: &FooService) {
adv_data,
scan_data,
};
- let conn = unwrap!(peripheral::advertise(sd, adv, &config).await);
+ let conn = unwrap!(peripheral::advertise_connectable(sd, adv, &config).await);
info!("advertising done!");
@@ -111,7 +111,7 @@ async fn bluetooth_task(sd: &'static Softdevice, button1: AnyPin, button2: AnyPi
// Since the bluetooth future never finishes, this can only happen when the Off button is pressed.
// This will cause the bluetooth future to be dropped.
//
- // If it was advertising, the nested `peripheral::advertise` future will be dropped, which will cause
+ // If it was advertising, the nested `peripheral::advertise_connectable` future will be dropped, which will cause
// the softdevice to stop advertising.
// If it was connected, it will drop everything including the `Connection` instance, which
// will tell the softdevice to disconnect it.
diff --git a/nrf-softdevice/src/ble/peripheral.rs b/nrf-softdevice/src/ble/peripheral.rs
index 5b30d35..a232e14 100644
--- a/nrf-softdevice/src/ble/peripheral.rs
+++ b/nrf-softdevice/src/ble/peripheral.rs
@@ -9,6 +9,12 @@ use crate::util::get_union_field;
use crate::util::{OnDrop, Portal};
use crate::{RawError, Softdevice};
+struct RawAdvertisement<'a> {
+ kind: u8,
+ adv_data: Option<&'a [u8]>,
+ scan_data: Option<&'a [u8]>,
+}
+
/// Connectable advertisement types, which can accept connections from interested Central devices.
pub enum ConnectableAdvertisement<'a> {
ScannableUndirected {
@@ -31,56 +37,133 @@ pub enum ConnectableAdvertisement<'a> {
},
}
-impl<'a> ConnectableAdvertisement<'a> {
- fn convert(&self) -> (u8, Option<&[u8]>, Option<&[u8]>) {
- match self {
+impl<'a> From<ConnectableAdvertisement<'a>> for RawAdvertisement<'a> {
+ fn from(val: ConnectableAdvertisement<'a>) -> RawAdvertisement<'a> {
+ match val {
ConnectableAdvertisement::ScannableUndirected {
adv_data,
scan_data,
- } => (
- raw::BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED as u8,
- Some(adv_data),
- Some(scan_data),
- ),
- ConnectableAdvertisement::NonscannableDirected { scan_data } => (
- raw::BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED as u8,
- None,
- Some(scan_data),
- ),
- ConnectableAdvertisement::NonscannableDirectedHighDuty { scan_data } => (
- raw::BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED_HIGH_DUTY_CYCLE as u8,
- None,
- Some(scan_data),
- ),
+ } => RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED as u8,
+ adv_data: Some(adv_data),
+ scan_data: Some(scan_data),
+ },
+ ConnectableAdvertisement::NonscannableDirected { scan_data } => RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED as u8,
+ adv_data: None,
+ scan_data: Some(scan_data),
+ },
+ ConnectableAdvertisement::NonscannableDirectedHighDuty { scan_data } => {
+ RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED_HIGH_DUTY_CYCLE
+ as u8,
+ adv_data: None,
+ scan_data: Some(scan_data),
+ }
+ }
#[cfg(any(feature = "s132", feature = "s140"))]
- ConnectableAdvertisement::ExtendedNonscannableUndirected { adv_data } => (
- raw::BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED as u8,
- Some(adv_data),
- None,
- ),
+ ConnectableAdvertisement::ExtendedNonscannableUndirected { adv_data } => {
+ RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED as u8,
+ adv_data: Some(adv_data),
+ scan_data: None,
+ }
+ }
#[cfg(any(feature = "s132", feature = "s140"))]
- ConnectableAdvertisement::ExtendedNonscannableDirected { adv_data } => (
- raw::BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_DIRECTED as u8,
- Some(adv_data),
- None,
- ),
+ ConnectableAdvertisement::ExtendedNonscannableDirected { adv_data } => {
+ RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_DIRECTED as u8,
+ adv_data: Some(adv_data),
+ scan_data: None,
+ }
+ }
}
}
}
/// Non-Connectable advertisement types. They cannot accept connections, they can be
/// only used to broadcast information in the air.
-pub enum NonconnectableAdvertisement {
- ScannableUndirected,
- NonscannableUndirected,
+pub enum NonconnectableAdvertisement<'a> {
+ ScannableUndirected {
+ adv_data: &'a [u8],
+ scan_data: &'a [u8],
+ },
+ NonscannableUndirected {
+ adv_data: &'a [u8],
+ },
#[cfg(any(feature = "s132", feature = "s140"))]
- ExtendedScannableUndirected,
+ ExtendedScannableUndirected {
+ adv_data: &'a [u8],
+ scan_data: &'a [u8],
+ },
#[cfg(any(feature = "s132", feature = "s140"))]
- ExtendedScannableDirected,
+ ExtendedScannableDirected {
+ adv_data: &'a [u8],
+ scan_data: &'a [u8],
+ },
#[cfg(any(feature = "s132", feature = "s140"))]
- ExtendedNonscannableUndirected,
+ ExtendedNonscannableUndirected {
+ adv_data: &'a [u8],
+ },
#[cfg(any(feature = "s132", feature = "s140"))]
- ExtendedNonscannableDirected,
+ ExtendedNonscannableDirected {
+ adv_data: &'a [u8],
+ },
+}
+
+impl<'a> From<NonconnectableAdvertisement<'a>> for RawAdvertisement<'a> {
+ fn from(val: NonconnectableAdvertisement<'a>) -> RawAdvertisement<'a> {
+ match val {
+ NonconnectableAdvertisement::ScannableUndirected {
+ adv_data,
+ scan_data,
+ } => RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_NONCONNECTABLE_SCANNABLE_UNDIRECTED as _,
+ adv_data: Some(adv_data),
+ scan_data: Some(scan_data),
+ },
+ NonconnectableAdvertisement::NonscannableUndirected { adv_data } => RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED as _,
+ adv_data: Some(adv_data),
+ scan_data: None,
+ },
+ #[cfg(any(feature = "s132", feature = "s140"))]
+ NonconnectableAdvertisement::ExtendedScannableUndirected {
+ adv_data,
+ scan_data,
+ } => RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_UNDIRECTED as _,
+ adv_data: Some(adv_data),
+ scan_data: Some(scan_data),
+ },
+ #[cfg(any(feature = "s132", feature = "s140"))]
+ NonconnectableAdvertisement::ExtendedScannableDirected {
+ adv_data,
+ scan_data,
+ } => RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_DIRECTED as _,
+ adv_data: Some(adv_data),
+ scan_data: Some(scan_data),
+ },
+ #[cfg(any(feature = "s132", feature = "s140"))]
+ NonconnectableAdvertisement::ExtendedNonscannableUndirected { adv_data } => {
+ RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED
+ as _,
+ adv_data: Some(adv_data),
+ scan_data: None,
+ }
+ }
+ #[cfg(any(feature = "s132", feature = "s140"))]
+ NonconnectableAdvertisement::ExtendedNonscannableDirected { adv_data } => {
+ RawAdvertisement {
+ kind: raw::BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_NONSCANNABLE_DIRECTED as _,
+ adv_data: Some(adv_data),
+ scan_data: None,
+ }
+ }
+ }
+ }
}
/// Error for [`advertise_start`]
@@ -101,17 +184,10 @@ impl From<RawError> for AdvertiseError {
static mut ADV_HANDLE: u8 = raw::BLE_GAP_ADV_SET_HANDLE_NOT_SET as u8;
pub(crate) static ADV_PORTAL: Portal<*const raw::ble_evt_t> = Portal::new();
-// Begins an ATT MTU exchange procedure, followed by a data length update request as necessary.
-pub async fn advertise(
- _sd: &Softdevice,
- adv: ConnectableAdvertisement<'_>,
- config: &Config,
-) -> Result<Connection, AdvertiseError> {
- let (adv_type, adv_data, scan_data) = adv.convert();
-
+fn start_adv(adv: RawAdvertisement<'_>, config: &Config) -> Result<(), AdvertiseError> {
// TODO make these configurable, only the right params based on type?
let mut adv_params: raw::ble_gap_adv_params_t = unsafe { mem::zeroed() };
- adv_params.properties.type_ = adv_type;
+ adv_params.properties.type_ = adv.kind;
adv_params.primary_phy = config.primary_phy as u8;
adv_params.secondary_phy = config.secondary_phy as u8;
adv_params.duration = config.timeout.map(|t| t.max(1)).unwrap_or(0);
@@ -134,17 +210,10 @@ pub async fn advertise(
};
let datas = raw::ble_gap_adv_data_t {
- adv_data: map_data(adv_data),
- scan_rsp_data: map_data(scan_data),
+ adv_data: map_data(adv.adv_data),
+ scan_rsp_data: map_data(adv.scan_data),
};
- let d = OnDrop::new(|| {
- let ret = unsafe { raw::sd_ble_gap_adv_stop(ADV_HANDLE) };
- if let Err(_e) = RawError::convert(ret) {
- warn!("sd_ble_gap_adv_stop: {:?}", _e);
- }
- });
-
let ret = unsafe {
raw::sd_ble_gap_adv_set_configure(&mut ADV_HANDLE as _, &datas as _, &adv_params as _)
};
@@ -171,8 +240,55 @@ pub async fn advertise(
err
})?;
+ Ok(())
+}
+
+/// Perform connectable advertising, returning the connection that's established as a result.
+pub async fn advertise(
+ _sd: &Softdevice,
+ adv: NonconnectableAdvertisement<'_>,
+ config: &Config,
+) -> Result<(), AdvertiseError> {
+ let d = OnDrop::new(|| {
+ let ret = unsafe { raw::sd_ble_gap_adv_stop(ADV_HANDLE) };
+ if let Err(_e) = RawError::convert(ret) {
+ warn!("sd_ble_gap_adv_stop: {:?}", _e);
+ }
+ });
+
+ start_adv(adv.into(), config)?;
+
// The advertising data needs to be kept alive for the entire duration of the advertising procedure.
+ let res = ADV_PORTAL
+ .wait_once(|ble_evt| unsafe {
+ match (*ble_evt).header.evt_id as u32 {
+ raw::BLE_GAP_EVTS_BLE_GAP_EVT_TIMEOUT => Err(AdvertiseError::Timeout),
+ raw::BLE_GAP_EVTS_BLE_GAP_EVT_ADV_SET_TERMINATED => Err(AdvertiseError::Timeout),
+ _ => unreachable!(),
+ }
+ })
+ .await;
+
+ d.defuse();
+ res
+}
+
+/// Perform connectable advertising, returning the connection that's established as a result.
+pub async fn advertise_connectable(
+ _sd: &Softdevice,
+ adv: ConnectableAdvertisement<'_>,
+ config: &Config,
+) -> Result<Connection, AdvertiseError> {
+ let d = OnDrop::new(|| {
+ let ret = unsafe { raw::sd_ble_gap_adv_stop(ADV_HANDLE) };
+ if let Err(_e) = RawError::convert(ret) {
+ warn!("sd_ble_gap_adv_stop: {:?}", _e);
+ }
+ });
+
+ start_adv(adv.into(), config)?;
+ // The advertising data needs to be kept alive for the entire duration of the advertising procedure.
let res = ADV_PORTAL
.wait_once(|ble_evt| unsafe {
match (*ble_evt).header.evt_id as u32 {