summaryrefslogtreecommitdiff
path: root/nrf-softdevice
diff options
context:
space:
mode:
Diffstat (limited to 'nrf-softdevice')
-rw-r--r--nrf-softdevice/src/ble/gatt_server.rs119
-rw-r--r--nrf-softdevice/src/ble/gatt_server/builder.rs208
-rw-r--r--nrf-softdevice/src/ble/gatt_server/characteristic.rs264
-rw-r--r--nrf-softdevice/src/ble/types.rs49
-rw-r--r--nrf-softdevice/src/softdevice.rs5
5 files changed, 557 insertions, 88 deletions
diff --git a/nrf-softdevice/src/ble/gatt_server.rs b/nrf-softdevice/src/ble/gatt_server.rs
index 99d22dd..ad12702 100644
--- a/nrf-softdevice/src/ble/gatt_server.rs
+++ b/nrf-softdevice/src/ble/gatt_server.rs
@@ -3,14 +3,15 @@
//! Typically the peripheral device is the GATT server, but it is not necessary.
//! In a connection any device can be server and client, and even both can be both at the same time.
-use core::mem;
-
use crate::ble::*;
use crate::raw;
use crate::util::{get_flexarray, get_union_field, Portal};
use crate::RawError;
use crate::Softdevice;
+pub mod builder;
+pub mod characteristic;
+
pub struct Characteristic {
pub uuid: Uuid,
pub can_read: bool,
@@ -29,21 +30,44 @@ pub struct CharacteristicHandles {
pub sccd_handle: u16,
}
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct ServiceHandle(u16);
+
+impl ServiceHandle {
+ pub fn handle(&self) -> u16 {
+ self.0
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct IncludedServiceHandle(u16);
+
+impl IncludedServiceHandle {
+ pub fn handle(&self) -> u16 {
+ self.0
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct DescriptorHandle(u16);
+
+impl DescriptorHandle {
+ pub fn handle(&self) -> u16 {
+ self.0
+ }
+}
+
pub trait Server: Sized {
type Event;
- fn register(sd: &Softdevice) -> Result<Self, RegisterError>;
fn on_write(&self, handle: u16, data: &[u8]) -> Option<Self::Event>;
}
pub trait Service: Sized {
type Event;
- fn uuid() -> Uuid;
-
- fn register<F>(service_handle: u16, register_char: F) -> Result<Self, RegisterError>
- where
- F: FnMut(Characteristic, &[u8]) -> Result<CharacteristicHandles, RegisterError>;
-
fn on_write(&self, handle: u16, data: &[u8]) -> Option<Self::Event>;
}
@@ -59,83 +83,6 @@ impl From<RawError> for RegisterError {
}
}
-pub fn register<S: Server>(sd: &Softdevice) -> Result<S, RegisterError> {
- S::register(sd)
-}
-
-pub fn register_service<S: Service>(_sd: &Softdevice) -> Result<S, RegisterError> {
- let uuid = S::uuid();
- let mut service_handle: u16 = 0;
- let ret = unsafe {
- raw::sd_ble_gatts_service_add(
- raw::BLE_GATTS_SRVC_TYPE_PRIMARY as u8,
- uuid.as_raw_ptr(),
- &mut service_handle as _,
- )
- };
- RawError::convert(ret)?;
-
- S::register(service_handle, |char, initial_value| {
- let mut cccd_attr_md: raw::ble_gatts_attr_md_t = unsafe { mem::zeroed() };
- cccd_attr_md.read_perm = raw::ble_gap_conn_sec_mode_t {
- _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
- };
- cccd_attr_md.write_perm = raw::ble_gap_conn_sec_mode_t {
- _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
- };
- cccd_attr_md.set_vloc(raw::BLE_GATTS_VLOC_STACK as u8);
-
- let mut attr_md: raw::ble_gatts_attr_md_t = unsafe { mem::zeroed() };
- attr_md.read_perm = raw::ble_gap_conn_sec_mode_t {
- _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
- };
- attr_md.write_perm = raw::ble_gap_conn_sec_mode_t {
- _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
- };
- attr_md.set_vloc(raw::BLE_GATTS_VLOC_STACK as u8);
- if char.vlen {
- attr_md.set_vlen(1);
- }
-
- let mut attr: raw::ble_gatts_attr_t = unsafe { mem::zeroed() };
- attr.p_uuid = unsafe { char.uuid.as_raw_ptr() };
- attr.p_attr_md = &attr_md as _;
- attr.max_len = char.max_len as _;
-
- attr.p_value = initial_value.as_ptr() as *mut u8;
- attr.init_len = initial_value.len() as _;
-
- let mut char_md: raw::ble_gatts_char_md_t = unsafe { mem::zeroed() };
- char_md.char_props.set_read(char.can_read as u8);
- char_md.char_props.set_write(char.can_write as u8);
- char_md
- .char_props
- .set_write_wo_resp(char.can_write_without_response as u8);
- char_md.char_props.set_notify(char.can_notify as u8);
- char_md.char_props.set_indicate(char.can_indicate as u8);
- char_md.p_cccd_md = &mut cccd_attr_md;
-
- let mut handles: raw::ble_gatts_char_handles_t = unsafe { mem::zeroed() };
-
- let ret = unsafe {
- raw::sd_ble_gatts_characteristic_add(
- service_handle,
- &mut char_md as _,
- &mut attr as _,
- &mut handles as _,
- )
- };
- RawError::convert(ret)?;
-
- Ok(CharacteristicHandles {
- value_handle: handles.value_handle,
- user_desc_handle: handles.user_desc_handle,
- cccd_handle: handles.cccd_handle,
- sccd_handle: handles.sccd_handle,
- })
- })
-}
-
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RunError {
diff --git a/nrf-softdevice/src/ble/gatt_server/builder.rs b/nrf-softdevice/src/ble/gatt_server/builder.rs
new file mode 100644
index 0000000..c0bff21
--- /dev/null
+++ b/nrf-softdevice/src/ble/gatt_server/builder.rs
@@ -0,0 +1,208 @@
+#![allow(dead_code)]
+
+use core::{convert::TryInto, marker::PhantomData, mem, ptr::null};
+
+#[cfg(feature = "alloc")]
+extern crate alloc;
+
+#[cfg(feature = "alloc")]
+use alloc::boxed::Box;
+
+use crate::{ble::Uuid, raw, RawError, Softdevice};
+
+use super::{
+ characteristic::{self, AttributeMetadata},
+ CharacteristicHandles, DescriptorHandle, IncludedServiceHandle, RegisterError, ServiceHandle,
+};
+
+pub struct ServiceBuilder<'a> {
+ handle: u16,
+ sd: PhantomData<&'a mut Softdevice>,
+}
+
+pub struct CharacteristicBuilder<'a> {
+ handles: CharacteristicHandles,
+ sb: PhantomData<&'a ServiceBuilder<'a>>,
+}
+
+impl<'a> ServiceBuilder<'a> {
+ pub fn new(_sd: &'a mut Softdevice, uuid: Uuid) -> Result<Self, RegisterError> {
+ let mut service_handle: u16 = 0;
+ let ret = unsafe {
+ raw::sd_ble_gatts_service_add(
+ raw::BLE_GATTS_SRVC_TYPE_PRIMARY as u8,
+ uuid.as_raw_ptr(),
+ &mut service_handle as _,
+ )
+ };
+ RawError::convert(ret)?;
+
+ Ok(ServiceBuilder {
+ handle: service_handle,
+ sd: PhantomData,
+ })
+ }
+
+ pub fn add_characteristic<T: AsRef<[u8]>>(
+ &mut self,
+ uuid: Uuid,
+ attr: characteristic::Attribute<T>,
+ md: characteristic::Metadata,
+ ) -> Result<CharacteristicBuilder<'_>, RegisterError> {
+ let value = attr.value.as_ref();
+ let attr_md = attr.metadata.into_raw();
+ self.add_characteristic_inner(uuid, value, attr.max_len, &attr_md, md)
+ }
+
+ #[cfg(feature = "alloc")]
+ pub fn add_characteristic_app(
+ &mut self,
+ uuid: Uuid,
+ attr: characteristic::Attribute<Box<[u8]>>,
+ md: characteristic::Metadata,
+ ) -> Result<CharacteristicBuilder<'_>, RegisterError> {
+ let value = Box::leak(attr.value);
+ let attr_md = attr.metadata.into_raw_user();
+ self.add_characteristic_inner(uuid, value, attr.max_len, &attr_md, md)
+ }
+
+ fn add_characteristic_inner(
+ &mut self,
+ uuid: Uuid,
+ value: &[u8],
+ max_len: u16,
+ attr_md: &raw::ble_gatts_attr_md_t,
+ char_md: characteristic::Metadata,
+ ) -> Result<CharacteristicBuilder<'_>, RegisterError> {
+ assert!(value.len() <= usize::from(max_len));
+ assert!(char_md
+ .user_description
+ .map_or(true, |x| x.value.len() <= usize::from(x.max_len)));
+
+ let (char_props, char_ext_props) = char_md.properties.into_raw();
+ let user_desc_md = char_md
+ .user_description
+ .and_then(|x| x.metadata.map(AttributeMetadata::into_raw));
+ let cccd_md = char_md.cccd.map(AttributeMetadata::into_raw);
+ let sccd_md = char_md.sccd.map(AttributeMetadata::into_raw);
+
+ let mut char_md = raw::ble_gatts_char_md_t {
+ char_props,
+ char_ext_props,
+ p_char_user_desc: char_md
+ .user_description
+ .map_or(null(), |x| x.value.as_ptr()),
+ char_user_desc_max_size: char_md.user_description.map_or(0, |x| x.max_len),
+ char_user_desc_size: char_md.user_description.map_or(0, |x| x.value.len() as u16),
+ p_char_pf: null(),
+ p_user_desc_md: user_desc_md.as_ref().map_or(null(), |x| x as _),
+ p_cccd_md: cccd_md.as_ref().map_or(null(), |x| x as _),
+ p_sccd_md: sccd_md.as_ref().map_or(null(), |x| x as _),
+ };
+
+ let mut attr = raw::ble_gatts_attr_t {
+ p_uuid: unsafe { uuid.as_raw_ptr() },
+ p_attr_md: attr_md as _,
+ init_len: unwrap!(value.len().try_into()),
+ init_offs: 0,
+ max_len,
+ p_value: value.as_ptr() as *mut _,
+ };
+
+ let mut handles: raw::ble_gatts_char_handles_t = unsafe { mem::zeroed() };
+
+ let ret = unsafe {
+ raw::sd_ble_gatts_characteristic_add(
+ self.handle,
+ &mut char_md as _,
+ &mut attr as _,
+ &mut handles as _,
+ )
+ };
+ RawError::convert(ret)?;
+
+ let handles = CharacteristicHandles {
+ value_handle: handles.value_handle,
+ user_desc_handle: handles.user_desc_handle,
+ cccd_handle: handles.cccd_handle,
+ sccd_handle: handles.sccd_handle,
+ };
+
+ Ok(CharacteristicBuilder {
+ handles,
+ sb: PhantomData,
+ })
+ }
+
+ pub fn include_service(
+ &mut self,
+ service: &ServiceHandle,
+ ) -> Result<IncludedServiceHandle, RegisterError> {
+ let mut handle = 0;
+ let ret =
+ unsafe { raw::sd_ble_gatts_include_add(self.handle, service.0, &mut handle as _) };
+ RawError::convert(ret)?;
+
+ Ok(IncludedServiceHandle(handle))
+ }
+
+ pub fn build(self) -> ServiceHandle {
+ ServiceHandle(self.handle)
+ }
+}
+
+impl<'a> CharacteristicBuilder<'a> {
+ pub fn add_descriptor<T: AsRef<[u8]>>(
+ &mut self,
+ uuid: Uuid,
+ attr: characteristic::Attribute<T>,
+ ) -> Result<DescriptorHandle, RegisterError> {
+ let value = attr.value.as_ref();
+ let attr_md = attr.metadata.into_raw();
+ self.add_descriptor_inner(uuid, value, attr.max_len, &attr_md)
+ }
+
+ #[cfg(feature = "alloc")]
+ pub fn add_descriptor_app(
+ &mut self,
+ uuid: Uuid,
+ attr: characteristic::Attribute<Box<[u8]>>,
+ ) -> Result<DescriptorHandle, RegisterError> {
+ let value = Box::leak(attr.value);
+ let attr_md = attr.metadata.into_raw_user();
+ self.add_descriptor_inner(uuid, value, attr.max_len, &attr_md)
+ }
+
+ fn add_descriptor_inner(
+ &mut self,
+ uuid: Uuid,
+ value: &[u8],
+ max_len: u16,
+ attr_md: &raw::ble_gatts_attr_md_t,
+ ) -> Result<DescriptorHandle, RegisterError> {
+ let attr = raw::ble_gatts_attr_t {
+ p_uuid: unsafe { uuid.as_raw_ptr() },
+ p_attr_md: attr_md as _,
+ init_len: unwrap!(value.len().try_into()),
+ init_offs: 0,
+ max_len,
+ p_value: value.as_ptr() as *mut _,
+ };
+
+ let mut handle = 0;
+ let ret = unsafe {
+ raw::sd_ble_gatts_descriptor_add(
+ self.handles.value_handle,
+ &attr as _,
+ &mut handle as _,
+ )
+ };
+ RawError::convert(ret)?;
+
+ Ok(DescriptorHandle(handle))
+ }
+
+ pub fn build(self) -> CharacteristicHandles {
+ self.handles
+ }
+}
diff --git a/nrf-softdevice/src/ble/gatt_server/characteristic.rs b/nrf-softdevice/src/ble/gatt_server/characteristic.rs
new file mode 100644
index 0000000..6bc33c2
--- /dev/null
+++ b/nrf-softdevice/src/ble/gatt_server/characteristic.rs
@@ -0,0 +1,264 @@
+use core::convert::TryInto;
+
+use crate::ble::SecurityMode;
+use crate::raw;
+
+// Missing:
+// - Characteristic presentation format
+
+#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct AttributeMetadata {
+ pub read: SecurityMode,
+ pub write: SecurityMode,
+ pub variable_len: bool,
+ pub deferred_read: bool,
+ pub deferred_write: bool,
+}
+
+impl AttributeMetadata {
+ pub fn with_security(security: SecurityMode) -> Self {
+ AttributeMetadata {
+ read: security,
+ write: security,
+ ..Default::default()
+ }
+ }
+
+ pub fn read_security(mut self, security: SecurityMode) -> Self {
+ self.read = security;
+ self
+ }
+
+ pub fn write_security(mut self, security: SecurityMode) -> Self {
+ self.write = security;
+ self
+ }
+
+ pub(crate) fn into_raw(self) -> raw::ble_gatts_attr_md_t {
+ self.into_raw_inner(raw::BLE_GATTS_VLOC_STACK as u8)
+ }
+
+ #[cfg(feature = "alloc")]
+ pub(crate) fn into_raw_user(self) -> raw::ble_gatts_attr_md_t {
+ self.into_raw_inner(raw::BLE_GATTS_VLOC_USER as u8)
+ }
+
+ fn into_raw_inner(self, vloc: u8) -> raw::ble_gatts_attr_md_t {
+ raw::ble_gatts_attr_md_t {
+ read_perm: self.read.into_raw(),
+ write_perm: self.write.into_raw(),
+ _bitfield_1: raw::ble_gatts_attr_md_t::new_bitfield_1(
+ self.variable_len.into(),
+ vloc,
+ self.deferred_read.into(),
+ self.deferred_write.into(),
+ ),
+ }
+ }
+}
+
+#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct Attribute<T: AsRef<[u8]>> {
+ pub metadata: AttributeMetadata,
+ pub value: T,
+ pub max_len: u16,
+}
+
+impl<T: AsRef<[u8]>> Attribute<T> {
+ pub fn new(value: T) -> Self {
+ let max_len = unwrap!(value.as_ref().len().try_into());
+ Attribute {
+ max_len,
+ value,
+ metadata: Default::default(),
+ }
+ }
+
+ pub fn security(mut self, security: SecurityMode) -> Self {
+ self.metadata.read = security;
+ self.metadata.write = security;
+ self
+ }
+
+ pub fn read_security(mut self, security: SecurityMode) -> Self {
+ self.metadata.read = security;
+ self
+ }
+
+ pub fn write_security(mut self, security: SecurityMode) -> Self {
+ self.metadata.write = security;
+ self
+ }
+
+ pub fn variable_len(mut self, max_len: u16) -> Self {
+ self.max_len = max_len;
+ self.metadata.variable_len = true;
+ self
+ }
+
+ pub fn deferred(mut self) -> Self {
+ self.metadata.deferred_read = true;
+ self.metadata.deferred_write = true;
+ self
+ }
+
+ pub fn deferred_read(mut self) -> Self {
+ self.metadata.deferred_read = true;
+ self
+ }
+
+ pub fn deferred_write(mut self) -> Self {
+ self.metadata.deferred_write = true;
+ self
+ }
+}
+
+#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct UserDescription {
+ pub metadata: Option<AttributeMetadata>,
+ pub value: &'static [u8],
+ pub max_len: u16,
+}
+
+#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct Properties {
+ pub broadcast: bool,
+ pub read: bool,
+ pub write_without_response: bool,
+ pub write: bool,
+ pub notify: bool,
+ pub indicate: bool,
+ pub signed_write: bool,
+ pub queued_write: bool,
+ pub write_user_description: bool,
+}
+
+impl Properties {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn broadcast(mut self) -> Self {
+ self.broadcast = true;
+ self
+ }
+
+ pub fn read(mut self) -> Self {
+ self.read = true;
+ self
+ }
+
+ pub fn write_without_response(mut self) -> Self {
+ self.write_without_response = true;
+ self
+ }
+
+ pub fn write(mut self) -> Self {
+ self.write = true;
+ self
+ }
+
+ pub fn notify(mut self) -> Self {
+ self.notify = true;
+ self
+ }
+
+ pub fn indicate(mut self) -> Self {
+ self.indicate = true;
+ self
+ }
+
+ pub fn signed_write(mut self) -> Self {
+ self.signed_write = true;
+ self
+ }
+
+ pub fn queued_write(mut self) -> Self {
+ self.queued_write = true;
+ self
+ }
+
+ pub fn write_user_description(mut self) -> Self {
+ self.write_user_description = true;
+ self
+ }
+
+ pub(crate) fn into_raw(self) -> (raw::ble_gatt_char_props_t, raw::ble_gatt_char_ext_props_t) {
+ (
+ raw::ble_gatt_char_props_t {
+ _bitfield_1: raw::ble_gatt_char_props_t::new_bitfield_1(
+ self.broadcast.into(),
+ self.read.into(),
+ self.write_without_response.into(),
+ self.write.into(),
+ self.notify.into(),
+ self.indicate.into(),
+ self.signed_write.into(),
+ ),
+ },
+ raw::ble_gatt_char_ext_props_t {
+ _bitfield_1: raw::ble_gatt_char_ext_props_t::new_bitfield_1(
+ self.queued_write.into(),
+ self.write_user_description.into(),
+ ),
+ },
+ )
+ }
+}
+
+#[derive(Default, Debug, PartialEq, Eq, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct Metadata {
+ pub properties: Properties,
+ pub user_description: Option<UserDescription>,
+ pub cccd: Option<AttributeMetadata>,
+ pub sccd: Option<AttributeMetadata>,
+}
+
+impl Metadata {
+ pub fn new(properties: Properties) -> Self {
+ let cccd = if properties.indicate || properties.notify {
+ Some(AttributeMetadata::default())
+ } else {
+ None
+ };
+
+ let sccd = if properties.broadcast {
+ Some(AttributeMetadata::default())
+ } else {
+ None
+ };
+
+ Metadata {
+ properties,
+ cccd,
+ sccd,
+ ..Default::default()
+ }
+ }
+
+ pub fn with_security(properties: Properties, write_security: SecurityMode) -> Self {
+ let cccd = if properties.indicate || properties.notify {
+ Some(AttributeMetadata::default().write_security(write_security))
+ } else {
+ None
+ };
+
+ let sccd = if properties.broadcast {
+ Some(AttributeMetadata::default().write_security(write_security))
+ } else {
+ None
+ };
+
+ Metadata {
+ properties,
+ cccd,
+ sccd,
+ ..Default::default()
+ }
+ }
+}
diff --git a/nrf-softdevice/src/ble/types.rs b/nrf-softdevice/src/ble/types.rs
index 7763ebc..6011808 100644
--- a/nrf-softdevice/src/ble/types.rs
+++ b/nrf-softdevice/src/ble/types.rs
@@ -84,6 +84,55 @@ impl Role {
}
}
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum SecurityMode {
+ NoAccess,
+ Open,
+ JustWorks,
+ Mitm,
+ LescMitm,
+ Signed,
+ SignedMitm,
+}
+
+impl Default for SecurityMode {
+ fn default() -> Self {
+ Self::Open
+ }
+}
+
+impl SecurityMode {
+ pub fn try_from_raw(raw: raw::ble_gap_conn_sec_mode_t) -> Option<Self> {
+ match (raw.sm(), raw.lv()) {
+ (0, 0) => Some(SecurityMode::NoAccess),
+ (1, 1) => Some(SecurityMode::Open),
+ (1, 2) => Some(SecurityMode::JustWorks),
+ (1, 3) => Some(SecurityMode::Mitm),
+ (1, 4) => Some(SecurityMode::LescMitm),
+ (2, 1) => Some(SecurityMode::Signed),
+ (2, 2) => Some(SecurityMode::SignedMitm),
+ _ => None,
+ }
+ }
+
+ pub fn into_raw(self) -> raw::ble_gap_conn_sec_mode_t {
+ let (sm, lv) = match self {
+ SecurityMode::NoAccess => (0, 0),
+ SecurityMode::Open => (1, 1),
+ SecurityMode::JustWorks => (1, 2),
+ SecurityMode::Mitm => (1, 3),
+ SecurityMode::LescMitm => (1, 4),
+ SecurityMode::Signed => (2, 1),
+ SecurityMode::SignedMitm => (2, 2),
+ };
+
+ raw::ble_gap_conn_sec_mode_t {
+ _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(sm, lv),
+ }
+ }
+}
+
#[repr(u8)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
diff --git a/nrf-softdevice/src/softdevice.rs b/nrf-softdevice/src/softdevice.rs
index d697cbe..282ba04 100644
--- a/nrf-softdevice/src/softdevice.rs
+++ b/nrf-softdevice/src/softdevice.rs
@@ -97,7 +97,7 @@ impl Softdevice {
/// - Panics if the requested configuration requires more memory than reserved for the softdevice. In that case, you can give more memory to the softdevice by editing the RAM start address in `memory.x`. The required start address is logged prior to panic.
/// - Panics if the requested configuration has too high memory requirements for the softdevice. The softdevice supports a maximum dynamic memory size of 64kb.
/// - Panics if called multiple times. Must be called at most once.
- pub fn enable(config: &Config) -> &'static Softdevice {
+ pub fn enable(config: &Config) -> &'static mut Softdevice {
if ENABLED
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_err()
@@ -310,7 +310,8 @@ impl Softdevice {
/// Return an instance to the softdevice without checking whether
/// it is enabled or not. This is only safe if the softdevice is enabled
- /// (a call to [`enable`] has returned without error)
+ /// (a call to [`enable`] has returned without error) and no `&mut` references
+ /// to the softdevice are active
pub unsafe fn steal() -> &'static Softdevice {
SOFTDEVICE.steal()
}