summaryrefslogtreecommitdiff
path: root/nrf-softdevice/src/ble/gatt_server/builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'nrf-softdevice/src/ble/gatt_server/builder.rs')
-rw-r--r--nrf-softdevice/src/ble/gatt_server/builder.rs208
1 files changed, 208 insertions, 0 deletions
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
+ }
+}