From 686fefc049a239fd079b3481ce7fcf4d9bdb4a72 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 15 Mar 2024 12:24:15 +0100 Subject: [PATCH] add convenience for read-only characteristics --- .../nrf-sdc/src/bin/ble_bas_peripheral.rs | 18 ++--- host/src/attribute.rs | 68 +++++++++++-------- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs b/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs index 5df7316c..f4099713 100644 --- a/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs +++ b/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs @@ -17,7 +17,7 @@ use static_cell::StaticCell; use trouble_host::{ adapter::{Adapter, HostResources}, advertise::{AdStructure, AdvertiseConfig, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, - attribute::{AttributeTable, Characteristic, CharacteristicProp, Service, Uuid}, + attribute::{AttributeTable, CharacteristicProp, Service, Uuid}, PacketQos, }; @@ -129,17 +129,13 @@ async fn main(spawner: Spawner) { let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new(); // Generic Access Service (mandatory) - let mut id = [b'T', b'r', b'o', b'u', b'b', b'l', b'e']; - let mut appearance = [0x80, 0x07]; + let id = b"Trouble"; + let appearance = [0x80, 0x07]; let mut bat_level = [0; 1]; let handle = { let mut svc = table.add_service(Service::new(0x1800)); - let _ = svc.add_characteristic(Characteristic::new(0x2a00, &[CharacteristicProp::Read], &mut id[..])); - let _ = svc.add_characteristic(Characteristic::new( - 0x2a01, - &[CharacteristicProp::Read], - &mut appearance[..], - )); + let _ = svc.add_characteristic_ro(0x2a00, id); + let _ = svc.add_characteristic_ro(0x2a01, &appearance[..]); drop(svc); // Generic attribute service (mandatory) @@ -148,11 +144,11 @@ async fn main(spawner: Spawner) { // Battery service let mut svc = table.add_service(Service::new(0x180f)); - svc.add_characteristic(Characteristic::new( + svc.add_characteristic( 0x2a19, &[CharacteristicProp::Read, CharacteristicProp::Notify], &mut bat_level, - )) + ) }; let server = adapter.gatt_server(&table); diff --git a/host/src/attribute.rs b/host/src/attribute.rs index 49687742..8dae32ed 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -44,6 +44,10 @@ pub enum AttributeData<'d> { Service { uuid: Uuid, }, + ReadOnlyData { + props: CharacteristicProps, + value: &'d [u8], + }, Data { props: CharacteristicProps, value: &'d mut [u8], @@ -89,6 +93,16 @@ impl<'d> AttributeData<'d> { return Err(AttErrorCode::ReadNotPermitted); } match self { + Self::ReadOnlyData { props, value } => { + if offset > value.len() { + return Ok(0); + } + let len = data.len().min(value.len() - offset); + if len > 0 { + data[..len].copy_from_slice(&value[offset..offset + len]); + } + Ok(len) + } Self::Data { props, value } => { if offset > value.len() { return Ok(0); @@ -188,8 +202,7 @@ impl<'d> AttributeData<'d> { *indications = data[0] & 0x02 != 0; Ok(()) } - Self::Declaration { .. } => Err(AttErrorCode::WriteNotPermitted), - Self::Service { .. } => Err(AttErrorCode::WriteNotPermitted), + _ => Err(AttErrorCode::WriteNotPermitted), } } } @@ -347,7 +360,12 @@ pub struct ServiceBuilder<'r, 'd, M: RawMutex, const MAX: usize> { } impl<'r, 'd, M: RawMutex, const MAX: usize> ServiceBuilder<'r, 'd, M, MAX> { - pub fn add_characteristic(&mut self, c: Characteristic<'d>) -> CharacteristicHandle { + fn add_characteristic_internal( + &mut self, + uuid: Uuid, + props: CharacteristicProps, + data: AttributeData<'d>, + ) -> CharacteristicHandle { // First the characteristic declaration let next = self.table.handle + 1; let cccd = self.table.handle + 2; @@ -356,25 +374,22 @@ impl<'r, 'd, M: RawMutex, const MAX: usize> ServiceBuilder<'r, 'd, M, MAX> { handle: 0, last_handle_in_group: 0, data: AttributeData::Declaration { - props: c.props, + props: props, handle: next, - uuid: c.uuid, + uuid, }, }); // Then the value declaration self.table.push(Attribute { - uuid: c.uuid, + uuid, handle: 0, last_handle_in_group: 0, - data: AttributeData::Data { - props: c.props, - value: c.storage, - }, + data, }); // Add optional CCCD handle - let cccd_handle = if c.props.any(&[CharacteristicProp::Notify, CharacteristicProp::Indicate]) { + let cccd_handle = if props.any(&[CharacteristicProp::Notify, CharacteristicProp::Indicate]) { self.table.push(Attribute { uuid: CHARACTERISTIC_CCCD_UUID16, handle: 0, @@ -394,6 +409,21 @@ impl<'r, 'd, M: RawMutex, const MAX: usize> ServiceBuilder<'r, 'd, M, MAX> { cccd_handle, } } + + pub fn add_characteristic>( + &mut self, + uuid: U, + props: &[CharacteristicProp], + storage: &'d mut [u8], + ) -> CharacteristicHandle { + let props = props.into(); + self.add_characteristic_internal(uuid.into(), props, AttributeData::Data { props, value: storage }) + } + + pub fn add_characteristic_ro>(&mut self, uuid: U, value: &'d [u8]) -> CharacteristicHandle { + let props = [CharacteristicProp::Read].into(); + self.add_characteristic_internal(uuid.into(), props, AttributeData::ReadOnlyData { props, value }) + } } impl<'r, 'd, M: RawMutex, const MAX: usize> Drop for ServiceBuilder<'r, 'd, M, MAX> { @@ -445,22 +475,6 @@ impl Service { } } -impl<'d> Characteristic<'d> { - pub fn new>(uuid: U, props: &[CharacteristicProp], storage: &'d mut [u8]) -> Self { - Self { - uuid: uuid.into(), - props: props.into(), - storage, - } - } -} - -pub struct Characteristic<'d> { - pub uuid: Uuid, - pub props: CharacteristicProps, - pub storage: &'d mut [u8], -} - #[derive(Clone, Copy)] pub struct CharacteristicProps(u8);