nrf-softdevice 0.1.0

Rust interface to nRF SoftDevice
Documentation
#![allow(dead_code)]

use core::marker::PhantomData;
use core::mem;
use core::ptr::null;

#[cfg(feature = "alloc")]
extern crate alloc;

#[cfg(feature = "alloc")]
use alloc::boxed::Box;

use super::characteristic::{self, AttributeMetadata};
use super::{CharacteristicHandles, DescriptorHandle, IncludedServiceHandle, RegisterError, ServiceHandle};
use crate::ble::Uuid;
use crate::{raw, RawError, Softdevice};

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: 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: 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
    }
}