From 144b281b3b5520a60a770e1bd5582cc9388e9fc8 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Mon, 13 Jan 2025 18:14:16 +0000 Subject: [PATCH 01/21] Implement generic attribute handle for get and set to accept attributes beyond characteristics --- host/src/attribute.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index e96ca56d..33ba9e95 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -400,9 +400,9 @@ impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> { /// /// If the characteristic for the handle cannot be found, or the shape of the data does not match the type of the characterstic, /// an error is returned - pub fn set(&self, characteristic: &Characteristic, input: &T) -> Result<(), Error> { + pub fn set(&self, attribute_handle: &T, input: &T::Value) -> Result<(), Error> { let gatt_value = input.to_gatt(); - self.set_raw(characteristic.handle, gatt_value) + self.set_raw(attribute_handle.handle(), gatt_value) } /// Read the value of the characteristic and pass the value to the provided closure. @@ -410,10 +410,10 @@ impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> { /// The return value of the closure is returned in this function and is assumed to be infallible. /// /// If the characteristic for the handle cannot be found, an error is returned. - pub fn get(&self, characteristic: &Characteristic) -> Result { + pub fn get, V: FromGatt>(&self, attribute_handle: &T) -> Result { self.iterate(|mut it| { while let Some(att) = it.next() { - if att.handle == characteristic.handle { + if att.handle == attribute_handle.handle() { if let AttributeData::Data { props, value, @@ -422,7 +422,7 @@ impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> { } = &mut att.data { let value = if *variable_len { &value[..*len as usize] } else { value }; - let v = T::from_gatt(value).map_err(|_| Error::InvalidValue)?; + let v = T::Value::from_gatt(value).map_err(|_| Error::InvalidValue)?; return Ok(v); } } @@ -471,6 +471,19 @@ impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> { } } +pub trait AttrHandle { + type Value: ToGatt; + fn handle(&self) -> u16; +} + +impl AttrHandle for Characteristic { + type Value = T; + + fn handle(&self) -> u16 { + self.handle + } +} + /// Handle to an attribute in the attribute table. #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Clone, Copy, Debug, PartialEq)] From b40b309e882f90c905468c3d8ae9cd84308013ae Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Tue, 14 Jan 2025 09:43:56 +0000 Subject: [PATCH 02/21] Remove AttributeHandle type --- host/src/attribute.rs | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index 33ba9e95..69b08577 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -359,7 +359,7 @@ impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> { data: AttributeData::Service { uuid: service.uuid }, }); ServiceBuilder { - handle: AttributeHandle { handle }, + handle, start: len, table: self, } @@ -484,22 +484,9 @@ impl AttrHandle for Characteristic { } } -/// Handle to an attribute in the attribute table. -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct AttributeHandle { - pub(crate) handle: u16, -} - -impl From for AttributeHandle { - fn from(handle: u16) -> Self { - Self { handle } - } -} - /// Builder for constructing GATT service definitions. pub struct ServiceBuilder<'r, 'd, M: RawMutex, const MAX: usize> { - handle: AttributeHandle, + handle: u16, start: usize, table: &'r mut AttributeTable<'d, M, MAX>, } @@ -602,7 +589,7 @@ impl<'d, M: RawMutex, const MAX: usize> ServiceBuilder<'_, 'd, M, MAX> { } /// Finish construction of the service and return a handle. - pub fn build(self) -> AttributeHandle { + pub fn build(self) -> u16 { self.handle } } From a75daa19c8c15cfc6da83a4f6d5325770861c526 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Tue, 14 Jan 2025 09:44:46 +0000 Subject: [PATCH 03/21] Rename AttrHandle to AttributeHandle --- host/src/attribute.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index 69b08577..3419347a 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -400,7 +400,7 @@ impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> { /// /// If the characteristic for the handle cannot be found, or the shape of the data does not match the type of the characterstic, /// an error is returned - pub fn set(&self, attribute_handle: &T, input: &T::Value) -> Result<(), Error> { + pub fn set(&self, attribute_handle: &T, input: &T::Value) -> Result<(), Error> { let gatt_value = input.to_gatt(); self.set_raw(attribute_handle.handle(), gatt_value) } @@ -410,7 +410,7 @@ impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> { /// The return value of the closure is returned in this function and is assumed to be infallible. /// /// If the characteristic for the handle cannot be found, an error is returned. - pub fn get, V: FromGatt>(&self, attribute_handle: &T) -> Result { + pub fn get, V: FromGatt>(&self, attribute_handle: &T) -> Result { self.iterate(|mut it| { while let Some(att) = it.next() { if att.handle == attribute_handle.handle() { @@ -471,12 +471,16 @@ impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> { } } -pub trait AttrHandle { +/// A type which holds a handle to an attribute in the attribute table +pub trait AttributeHandle { + /// The data type which the attribute contains type Value: ToGatt; + + /// Returns the attribute handle fn handle(&self) -> u16; } -impl AttrHandle for Characteristic { +impl AttributeHandle for Characteristic { type Value = T; fn handle(&self) -> u16 { From f018d82d5978f334f8596c9e037c37e047ff359d Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Tue, 14 Jan 2025 11:03:55 +0000 Subject: [PATCH 04/21] Fix handle type issue in service macro --- host-macros/src/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-macros/src/service.rs b/host-macros/src/service.rs index c57695fa..4c2af6cd 100644 --- a/host-macros/src/service.rs +++ b/host-macros/src/service.rs @@ -153,7 +153,7 @@ impl ServiceBuilder { quote! { #visibility struct #struct_name { #fields - handle: trouble_host::attribute::AttributeHandle, + handle: u16, } #[allow(unused)] From b2885248d4ba18c018266113f1a250a65b33f8b9 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Tue, 14 Jan 2025 11:05:07 +0000 Subject: [PATCH 05/21] Change server's get and set methods to accept a generic AttributeHandle --- host-macros/src/server.rs | 8 ++++---- host/src/attribute.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/host-macros/src/server.rs b/host-macros/src/server.rs index f5698404..ff1976a0 100644 --- a/host-macros/src/server.rs +++ b/host-macros/src/server.rs @@ -167,12 +167,12 @@ impl ServerBuilder { }) } - #visibility fn get(&self, characteristic: &trouble_host::attribute::Characteristic) -> Result { - self.server.table().get(characteristic) + #visibility fn get, V: FromGatt>(&self, attribute_handle: &T) -> Result { + self.server.table().get(attribute_handle) } - #visibility fn set(&self, characteristic: &trouble_host::attribute::Characteristic, input: &T) -> Result<(), trouble_host::Error> { - self.server.table().set(characteristic, input) + #visibility fn set(&self, attribute_handle: &T, input: &T::Value) -> Result<(), Error> { + self.server.table().set(attribute_handle, input) } } diff --git a/host/src/attribute.rs b/host/src/attribute.rs index 3419347a..020ea3f8 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -476,7 +476,7 @@ pub trait AttributeHandle { /// The data type which the attribute contains type Value: ToGatt; - /// Returns the attribute handle + /// Returns the attribute's handle fn handle(&self) -> u16; } From d83ab63f8ea4bc730f004aa8f0d24b813364d30f Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Tue, 14 Jan 2025 15:40:34 +0000 Subject: [PATCH 06/21] Implement GattValue on Uuid type --- host/src/types/gatt_traits.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/host/src/types/gatt_traits.rs b/host/src/types/gatt_traits.rs index 51c90622..0b0af897 100644 --- a/host/src/types/gatt_traits.rs +++ b/host/src/types/gatt_traits.rs @@ -178,3 +178,19 @@ impl ToGatt for &'static str { self.as_bytes() } } + +impl ToGatt for crate::types::uuid::Uuid { + const MIN_SIZE: usize = 2; + const MAX_SIZE: usize = 16; + + fn to_gatt(&self) -> &[u8] { + self.as_raw() + } +} + +impl FromGatt for crate::types::uuid::Uuid { + fn from_gatt(data: &[u8]) -> Result { + Self::try_from(data).map_err(|_| FromGattError::InvalidLength) + } +} + From 1db6181860b2af99238268003727a1c17d9c9398 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Tue, 14 Jan 2025 15:41:23 +0000 Subject: [PATCH 07/21] Implement Attribute handle in service builder macro --- host-macros/src/service.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/host-macros/src/service.rs b/host-macros/src/service.rs index 4c2af6cd..70c1384e 100644 --- a/host-macros/src/service.rs +++ b/host-macros/src/service.rs @@ -174,6 +174,14 @@ impl ServiceBuilder { } #code_impl } + + impl trouble_host::attribute::AttributeHandle for #struct_name { + type Value = trouble_host::types::uuid::Uuid; + + fn handle(&self) -> u16 { + self.handle + } + } } } From 2aebbabe7f857bb62b9c9342fac6a471659f08d7 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Mon, 20 Jan 2025 13:23:20 +0000 Subject: [PATCH 08/21] Revert "Implement Attribute handle in service builder macro" This reverts commit 2793596a227761d86935b509a2d75a93886feed0. --- host-macros/src/service.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/host-macros/src/service.rs b/host-macros/src/service.rs index 70c1384e..4c2af6cd 100644 --- a/host-macros/src/service.rs +++ b/host-macros/src/service.rs @@ -174,14 +174,6 @@ impl ServiceBuilder { } #code_impl } - - impl trouble_host::attribute::AttributeHandle for #struct_name { - type Value = trouble_host::types::uuid::Uuid; - - fn handle(&self) -> u16 { - self.handle - } - } } } From e71c3e4365ea0eeaa7509c9c3e2060107a6a4137 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Mon, 20 Jan 2025 13:57:03 +0000 Subject: [PATCH 09/21] Allow getting and setting characteristic CCCD properties --- host/src/attribute.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index 020ea3f8..15fa7782 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -679,6 +679,22 @@ impl Characteristic { pub fn get(&self, server: &AttributeServer<'_, M, MAX>) -> Result { server.table().get(self) } + + /// Returns the attribute handle for the characteristic's CCCD properties (if available) + pub fn cccd_handle(&self) -> Option { + self.cccd_handle.map(CccdHandle) + } +} + +/// Attribute handle for a characteristic's CCCD properties +pub struct CccdHandle(u16); + +impl AttributeHandle for CccdHandle { + type Value = CharacteristicProps; + + fn handle(&self) -> u16 { + self.0 + } } /// Builder for characteristics. @@ -813,6 +829,22 @@ impl CharacteristicProps { } } +impl FixedGattValue for CharacteristicProps { + const SIZE: usize = 1; + + fn from_gatt(data: &[u8]) -> Result { + if data.len() != Self::SIZE { + return Err(FromGattError::InvalidLength); + } + + Ok(CharacteristicProps(data[0])) + } + + fn to_gatt(&self) -> &[u8] { + FixedGattValue::to_gatt(&self.0) + } +} + /// A value of an attribute. pub struct AttributeValue<'d, M: RawMutex> { value: Mutex, From e13ec183951a4aec431c97481c6cdf740c69c8ec Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Mon, 20 Jan 2025 14:00:10 +0000 Subject: [PATCH 10/21] Swap mention of CCCD for characteristic properties in new attribute handle --- host/src/attribute.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index 15fa7782..60bc712b 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -680,16 +680,16 @@ impl Characteristic { server.table().get(self) } - /// Returns the attribute handle for the characteristic's CCCD properties (if available) - pub fn cccd_handle(&self) -> Option { - self.cccd_handle.map(CccdHandle) + /// Returns the attribute handle for the characteristic's properties (if available) + pub fn cccd_handle(&self) -> Option { + self.cccd_handle.map(CharacteristicPropertiesHandle) } } -/// Attribute handle for a characteristic's CCCD properties -pub struct CccdHandle(u16); +/// Attribute handle for a characteristic's properties +pub struct CharacteristicPropertiesHandle(u16); -impl AttributeHandle for CccdHandle { +impl AttributeHandle for CharacteristicPropertiesHandle { type Value = CharacteristicProps; fn handle(&self) -> u16 { From ba6837e528b55784f18f2d9720166a470daa6cda Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Thu, 23 Jan 2025 17:18:07 +0000 Subject: [PATCH 11/21] Add name as an argument for descriptor attribute --- examples/apps/src/ble_bas_peripheral.rs | 2 +- host-macros/src/characteristic.rs | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/apps/src/ble_bas_peripheral.rs b/examples/apps/src/ble_bas_peripheral.rs index 5e1aae01..51601675 100644 --- a/examples/apps/src/ble_bas_peripheral.rs +++ b/examples/apps/src/ble_bas_peripheral.rs @@ -20,7 +20,7 @@ struct Server { struct BatteryService { /// Battery Level #[descriptor(uuid = descriptors::VALID_RANGE, read, value = [0, 100])] - #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, read, value = "Battery Level")] + #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "hello", read, value = "Battery Level")] #[characteristic(uuid = characteristic::BATTERY_LEVEL, read, notify, value = 10)] level: u8, #[characteristic(uuid = "408813df-5dd4-1f87-ec11-cdb001100000", write, read, notify)] diff --git a/host-macros/src/characteristic.rs b/host-macros/src/characteristic.rs index 68c37448..f6e0ae78 100644 --- a/host-macros/src/characteristic.rs +++ b/host-macros/src/characteristic.rs @@ -55,6 +55,8 @@ pub struct AccessArgs { pub struct DescriptorArgs { /// The UUID of the descriptor. pub uuid: TokenStream, + /// The name which will be used to identify the descriptor when accessing its attribute handle + pub name: Option, /// The initial value of the descriptor (&str). /// This is optional and can be used to set the initial value of the descriptor. pub default_value: Option, @@ -166,6 +168,7 @@ impl CharacteristicArgs { impl DescriptorArgs { pub fn parse(attribute: &syn::Attribute) -> Result { let mut uuid: Option<_> = None; + let mut name: Option = None; let mut read: Option = None; // let mut write: Option = None; // let mut capacity: Option = None; @@ -180,6 +183,12 @@ impl DescriptorArgs { .as_str() { "uuid" => check_multi(&mut uuid, "uuid", &meta, parse_uuid(&meta)?)?, + "name" => { + let value = meta + .value() + .map_err(|_| meta.error("'name' must be followed by '= [name]'. i.e. name = \"units\""))?; + check_multi(&mut name, "name", &meta, value.parse()?)? + } "read" => check_multi(&mut read, "read", &meta, true)?, // "write" => check_multi(&mut write, "write", &meta, true)?, // "write_without_response" => check_multi(&mut write_without_response, "write_without_response", &meta, true)?, @@ -196,7 +205,7 @@ impl DescriptorArgs { "default_value" => return Err(meta.error("use 'value' for default value")), other => { return Err(meta.error(format!( - "Unsupported descriptor property: '{other}'.\nSupported properties are: uuid, read, value" + "Unsupported descriptor property: '{other}'.\nSupported properties are: uuid, name, read, value" ))); } }; @@ -205,6 +214,7 @@ impl DescriptorArgs { Ok(Self { uuid: uuid.ok_or(Error::custom("Descriptor must have a UUID"))?, + name, default_value, capacity: None, access: AccessArgs { From 1705aba2b0dbab16cbdf86f65a6d788211928775 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Thu, 23 Jan 2025 17:21:04 +0000 Subject: [PATCH 12/21] Implement AttributeHandle for Descriptor --- host/src/attribute.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index 60bc712b..e4c065bb 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -704,12 +704,12 @@ pub struct CharacteristicBuilder<'r, 'd, T: ToGatt, M: RawMutex, const MAX: usiz } impl<'d, T: ToGatt, M: RawMutex, const MAX: usize> CharacteristicBuilder<'_, 'd, T, M, MAX> { - fn add_descriptor_internal( + fn add_descriptor_internal( &mut self, uuid: Uuid, props: CharacteristicProps, data: AttributeData<'d>, - ) -> DescriptorHandle { + ) -> Descriptor
{ let handle = self.table.handle; self.table.push(Attribute { uuid, @@ -718,16 +718,19 @@ impl<'d, T: ToGatt, M: RawMutex, const MAX: usize> CharacteristicBuilder<'_, 'd, data, }); - DescriptorHandle { handle } + Descriptor { + handle, + phantom: PhantomData, + } } /// Add a characteristic descriptor for this characteristic. - pub fn add_descriptor>( + pub fn add_descriptor>( &mut self, uuid: U, props: &[CharacteristicProp], data: &'d mut [u8], - ) -> DescriptorHandle { + ) -> Descriptor
{ let props = props.into(); let len = data.len() as u16; self.add_descriptor_internal( @@ -743,7 +746,7 @@ impl<'d, T: ToGatt, M: RawMutex, const MAX: usize> CharacteristicBuilder<'_, 'd, } /// Add a read only characteristic descriptor for this characteristic. - pub fn add_descriptor_ro>(&mut self, uuid: U, data: &'d [u8]) -> DescriptorHandle { + pub fn add_descriptor_ro>(&mut self, uuid: U, data: &'d [u8]) -> Descriptor
{ let props = [CharacteristicProp::Read].into(); self.add_descriptor_internal(uuid.into(), props, AttributeData::ReadOnlyData { props, value: data }) } @@ -757,8 +760,17 @@ impl<'d, T: ToGatt, M: RawMutex, const MAX: usize> CharacteristicBuilder<'_, 'd, /// Characteristic descriptor handle. #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Clone, Copy, Debug)] -pub struct DescriptorHandle { +pub struct Descriptor { pub(crate) handle: u16, + phantom: PhantomData, +} + +impl AttributeHandle for Descriptor { + type Value = T; + + fn handle(&self) -> u16 { + self.handle + } } /// Iterator over attributes. From b699d7e083285914c6089826ad6112792c86593d Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Thu, 23 Jan 2025 17:22:28 +0000 Subject: [PATCH 13/21] Add named descriptor handles to service to make them accessible --- host-macros/src/service.rs | 45 +++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/host-macros/src/service.rs b/host-macros/src/service.rs index 4c2af6cd..4ee6d2cf 100644 --- a/host-macros/src/service.rs +++ b/host-macros/src/service.rs @@ -7,7 +7,7 @@ use darling::{Error, FromMeta}; use inflector::cases::screamingsnakecase::to_screaming_snake_case; use proc_macro2::TokenStream as TokenStream2; -use quote::{format_ident, quote, quote_spanned}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::parse::Result; use syn::spanned::Spanned; use syn::{Expr, Meta, Token}; @@ -179,7 +179,7 @@ impl ServiceBuilder { /// Construct instructions for adding a characteristic to the service, with static storage. fn construct_characteristic_static(&mut self, characteristic: Characteristic) { - let descriptors = self.build_descriptors(&characteristic); + let (code_descriptors, named_descriptors) = self.build_descriptors(&characteristic); let name_screaming = format_ident!("{}", to_screaming_snake_case(characteristic.name.as_str())); let char_name = format_ident!("{}", characteristic.name); let ty = characteristic.ty; @@ -192,16 +192,16 @@ impl ServiceBuilder { }; self.code_build_chars.extend(quote_spanned! {characteristic.span=> - let #char_name = { + let (#char_name, #(#named_descriptors),*) = { static #name_screaming: static_cell::StaticCell<[u8; <#ty as trouble_host::types::gatt_traits::ToGatt>::MAX_SIZE]> = static_cell::StaticCell::new(); let mut val = <#ty>::default(); // constrain the type of the value here val = #default_value; // update the temporary value with our new default let store = #name_screaming.init([0; <#ty as trouble_host::types::gatt_traits::ToGatt>::MAX_SIZE]); let mut builder = service .add_characteristic(#uuid, &[#(#properties),*], val, store); - #descriptors + #code_descriptors - builder.build() + (builder.build(), #(#named_descriptors),*) }; }); @@ -271,8 +271,9 @@ impl ServiceBuilder { } /// Generate token stream for any descriptors tagged against this characteristic. - fn build_descriptors(&mut self, characteristic: &Characteristic) -> TokenStream2 { - characteristic + fn build_descriptors(&mut self, characteristic: &Characteristic) -> (TokenStream2, Vec) { + let mut named_descriptors = Vec::::new(); + (characteristic .args .descriptors .iter() @@ -280,6 +281,7 @@ impl ServiceBuilder { .map(|(index, args)| { let name_screaming = format_ident!("DESC_{index}_{}", to_screaming_snake_case(characteristic.name.as_str())); + let identifier = args.name.as_ref().map(|name| format_ident!("{}_{}_descriptor", characteristic.name.as_str(), name.value())); let access = &args.access; let properties = set_access_properties(access); let uuid = &args.uuid; @@ -289,28 +291,41 @@ impl ServiceBuilder { }; let capacity = match &args.capacity { Some(cap) => quote!(#cap), - None => quote!(#default_value.len() as u8) + None => quote!(#default_value.len() as usize) + }; + + let mut identifier_assignment = None; + if let Some(name) = &identifier { + self.code_fields.extend(quote_spanned!{ identifier.span() => + #name: trouble_host::attribute::Descriptor<&'static [u8]>, + }); + self.code_struct_init.extend(quote_spanned! { identifier.span() => + #name, + }); + named_descriptors.push(name.to_token_stream()); + identifier_assignment = Some(quote! { let #name = }); }; self.attribute_count += 1; // descriptors should always only be one attribute. quote_spanned! {characteristic.span=> - { + #identifier_assignment { let value = #default_value; - const CAPACITY: u8 = if (#capacity) < 16 { 16 } else { #capacity }; // minimum capacity is 16 bytes - static #name_screaming: static_cell::StaticCell<[u8; CAPACITY as usize]> = static_cell::StaticCell::new(); - let store = #name_screaming.init([0; CAPACITY as usize]); + const CAPACITY: usize = if (#capacity) < 16 { 16 } else { #capacity }; // minimum capacity is 16 bytes + static #name_screaming: static_cell::StaticCell<[u8; CAPACITY]> = static_cell::StaticCell::new(); + let store = #name_screaming.init([0; CAPACITY]); let value = trouble_host::types::gatt_traits::ToGatt::to_gatt(&value); store[..value.len()].copy_from_slice(value); - builder.add_descriptor( + builder.add_descriptor::<&[u8], _>( #uuid, &[#(#properties),*], store, - ); + ) }; } }) - .collect() + .collect(), + named_descriptors) } } From 15e49de1da9be4e5637139949bf8d17e3fc8fd61 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Wed, 26 Feb 2025 15:06:11 +0000 Subject: [PATCH 14/21] Fix issues from rebase --- host/src/attribute.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index e4c065bb..7f05b850 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -11,8 +11,8 @@ use embassy_sync::blocking_mutex::Mutex; use crate::att::AttErrorCode; use crate::attribute_server::AttributeServer; use crate::cursor::{ReadCursor, WriteCursor}; -use crate::prelude::Connection; -use crate::types::gatt_traits::{FromGatt, ToGatt}; +use crate::prelude::{Connection, FixedGattValue, FromGatt, ToGatt}; +use crate::types::gatt_traits::FromGattError; pub use crate::types::uuid::Uuid; use crate::Error; use heapless::Vec; @@ -704,7 +704,7 @@ pub struct CharacteristicBuilder<'r, 'd, T: ToGatt, M: RawMutex, const MAX: usiz } impl<'d, T: ToGatt, M: RawMutex, const MAX: usize> CharacteristicBuilder<'_, 'd, T, M, MAX> { - fn add_descriptor_internal( + fn add_descriptor_internal( &mut self, uuid: Uuid, props: CharacteristicProps, From 762d7758eb0fc5da7ecd9640a8776be4fdf7a8f9 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Wed, 26 Feb 2025 15:06:49 +0000 Subject: [PATCH 15/21] Implement ToGatt for &'static [u8] --- host/src/types/gatt_traits.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/host/src/types/gatt_traits.rs b/host/src/types/gatt_traits.rs index 0b0af897..20b7c288 100644 --- a/host/src/types/gatt_traits.rs +++ b/host/src/types/gatt_traits.rs @@ -179,6 +179,15 @@ impl ToGatt for &'static str { } } +impl ToGatt for &'static [u8] { + const MIN_SIZE: usize = 0; + const MAX_SIZE: usize = usize::MAX; + + fn to_gatt(&self) -> &[u8] { + self + } +} + impl ToGatt for crate::types::uuid::Uuid { const MIN_SIZE: usize = 2; const MAX_SIZE: usize = 16; @@ -193,4 +202,3 @@ impl FromGatt for crate::types::uuid::Uuid { Self::try_from(data).map_err(|_| FromGattError::InvalidLength) } } - From 5be62cc1cfe5f92a7b22aea1fab860bb7ee3c314 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Wed, 26 Feb 2025 15:30:40 +0000 Subject: [PATCH 16/21] Replace ToGatt references with AsGatt --- host/src/attribute.rs | 12 ++++++------ host/src/types/gatt_traits.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index c9570fc6..c3391b2d 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -474,13 +474,13 @@ impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> { /// A type which holds a handle to an attribute in the attribute table pub trait AttributeHandle { /// The data type which the attribute contains - type Value: ToGatt; + type Value: AsGatt; /// Returns the attribute's handle fn handle(&self) -> u16; } -impl AttributeHandle for Characteristic { +impl AttributeHandle for Characteristic { type Value = T; fn handle(&self) -> u16 { @@ -725,7 +725,7 @@ impl<'d, T: AsGatt, M: RawMutex, const MAX: usize> CharacteristicBuilder<'_, 'd, } /// Add a characteristic descriptor for this characteristic. - pub fn add_descriptor>( + pub fn add_descriptor>( &mut self, uuid: U, props: &[CharacteristicProp], @@ -746,7 +746,7 @@ impl<'d, T: AsGatt, M: RawMutex, const MAX: usize> CharacteristicBuilder<'_, 'd, } /// Add a read only characteristic descriptor for this characteristic. - pub fn add_descriptor_ro>(&mut self, uuid: U, data: &'d [u8]) -> Descriptor
{ + pub fn add_descriptor_ro>(&mut self, uuid: U, data: &'d [u8]) -> Descriptor
{ let props = [CharacteristicProp::Read].into(); self.add_descriptor_internal(uuid.into(), props, AttributeData::ReadOnlyData { props, value: data }) } @@ -760,12 +760,12 @@ impl<'d, T: AsGatt, M: RawMutex, const MAX: usize> CharacteristicBuilder<'_, 'd, /// Characteristic descriptor handle. #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Clone, Copy, Debug)] -pub struct Descriptor { +pub struct Descriptor { pub(crate) handle: u16, phantom: PhantomData, } -impl AttributeHandle for Descriptor { +impl AttributeHandle for Descriptor { type Value = T; fn handle(&self) -> u16 { diff --git a/host/src/types/gatt_traits.rs b/host/src/types/gatt_traits.rs index c39ec554..658441f1 100644 --- a/host/src/types/gatt_traits.rs +++ b/host/src/types/gatt_traits.rs @@ -179,7 +179,7 @@ impl AsGatt for &'static str { } } -impl ToGatt for &'static [u8] { +impl AsGatt for &'static [u8] { const MIN_SIZE: usize = 0; const MAX_SIZE: usize = usize::MAX; @@ -188,7 +188,7 @@ impl ToGatt for &'static [u8] { } } -impl ToGatt for crate::types::uuid::Uuid { +impl AsGatt for crate::types::uuid::Uuid { const MIN_SIZE: usize = 2; const MAX_SIZE: usize = 16; From d275a0268beb1dc55d6da1e7fd67795d142a0444 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Wed, 26 Feb 2025 15:37:38 +0000 Subject: [PATCH 17/21] Replace to_gatt with as_gatt --- host/src/attribute.rs | 4 ++-- host/src/types/gatt_traits.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index c3391b2d..bfd7d591 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -852,8 +852,8 @@ impl FixedGattValue for CharacteristicProps { Ok(CharacteristicProps(data[0])) } - fn to_gatt(&self) -> &[u8] { - FixedGattValue::to_gatt(&self.0) + fn as_gatt(&self) -> &[u8] { + FixedGattValue::as_gatt(&self.0) } } diff --git a/host/src/types/gatt_traits.rs b/host/src/types/gatt_traits.rs index 658441f1..031a16cd 100644 --- a/host/src/types/gatt_traits.rs +++ b/host/src/types/gatt_traits.rs @@ -25,7 +25,7 @@ pub trait FixedGattValue: FromGatt { /// Converts to gatt bytes. /// Must return a slice of len Self::SIZE - fn to_gatt(&self) -> &[u8]; + fn as_gatt(&self) -> &[u8]; } /// Trait to allow conversion of a type to gatt bytes @@ -59,7 +59,7 @@ impl AsGatt for T { const MAX_SIZE: usize = Self::SIZE; fn as_gatt(&self) -> &[u8] { - ::to_gatt(self) + ::as_gatt(self) } } @@ -91,7 +91,7 @@ impl FixedGattValue for T { } } - fn to_gatt(&self) -> &[u8] { + fn as_gatt(&self) -> &[u8] { // SAFETY // - Slice is of type u8 so data is guaranteed valid for reads of any length // - Data and len are tied to the address and size of the type @@ -110,7 +110,7 @@ impl FixedGattValue for bool { } } - fn to_gatt(&self) -> &[u8] { + fn as_gatt(&self) -> &[u8] { match self { true => &[0x01], false => &[0x00], @@ -183,7 +183,7 @@ impl AsGatt for &'static [u8] { const MIN_SIZE: usize = 0; const MAX_SIZE: usize = usize::MAX; - fn to_gatt(&self) -> &[u8] { + fn as_gatt(&self) -> &[u8] { self } } @@ -192,7 +192,7 @@ impl AsGatt for crate::types::uuid::Uuid { const MIN_SIZE: usize = 2; const MAX_SIZE: usize = 16; - fn to_gatt(&self) -> &[u8] { + fn as_gatt(&self) -> &[u8] { self.as_raw() } } From 4deec225e0bdacf3d7f54251e3bc0e88bfa15bce Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Wed, 26 Feb 2025 15:55:45 +0000 Subject: [PATCH 18/21] Explicitly use nightly for cargo fmt in CI --- ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci.sh b/ci.sh index cb8d44de..17bff040 100755 --- a/ci.sh +++ b/ci.sh @@ -36,7 +36,7 @@ cargo batch \ --- build --release --manifest-path examples/rp-pico-2-w/Cargo.toml --target thumbv8m.main-none-eabihf --features skip-cyw43-firmware # --- build --release --manifest-path examples/apache-nimble/Cargo.toml --target thumbv7em-none-eabihf -cargo fmt --check --manifest-path ./host/Cargo.toml +cargo +nightly fmt --check --manifest-path ./host/Cargo.toml cargo clippy --manifest-path ./host/Cargo.toml --features gatt,peripheral,central cargo test --manifest-path ./host/Cargo.toml --lib -- --nocapture cargo test --manifest-path ./host/Cargo.toml --no-run -- --nocapture From 2ca0ca971a378eb8daa1911a99a3420c2481a0e8 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Wed, 26 Feb 2025 17:02:45 +0000 Subject: [PATCH 19/21] Revert "Explicitly use nightly for cargo fmt in CI" This reverts commit 4deec225e0bdacf3d7f54251e3bc0e88bfa15bce. --- ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci.sh b/ci.sh index 17bff040..cb8d44de 100755 --- a/ci.sh +++ b/ci.sh @@ -36,7 +36,7 @@ cargo batch \ --- build --release --manifest-path examples/rp-pico-2-w/Cargo.toml --target thumbv8m.main-none-eabihf --features skip-cyw43-firmware # --- build --release --manifest-path examples/apache-nimble/Cargo.toml --target thumbv7em-none-eabihf -cargo +nightly fmt --check --manifest-path ./host/Cargo.toml +cargo fmt --check --manifest-path ./host/Cargo.toml cargo clippy --manifest-path ./host/Cargo.toml --features gatt,peripheral,central cargo test --manifest-path ./host/Cargo.toml --lib -- --nocapture cargo test --manifest-path ./host/Cargo.toml --no-run -- --nocapture From 76bc1b2582f8e00f36d9777995a40e1d9633048b Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Thu, 27 Feb 2025 11:29:23 +0000 Subject: [PATCH 20/21] Run cargo fmt --- host/src/attribute.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index bfd7d591..5a127a5d 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -5,17 +5,17 @@ use core::marker::PhantomData; use bt_hci::uuid::declarations::{CHARACTERISTIC, PRIMARY_SERVICE}; use bt_hci::uuid::descriptors::CLIENT_CHARACTERISTIC_CONFIGURATION; -use embassy_sync::blocking_mutex::Mutex; use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::blocking_mutex::Mutex; use heapless::Vec; -use crate::Error; use crate::att::AttErrorCode; use crate::attribute_server::AttributeServer; use crate::cursor::{ReadCursor, WriteCursor}; -use crate::prelude::{Connection, FixedGattValue, FromGatt, AsGatt}; +use crate::prelude::{AsGatt, Connection, FixedGattValue, FromGatt}; use crate::types::gatt_traits::FromGattError; pub use crate::types::uuid::Uuid; +use crate::Error; /// Characteristic properties #[derive(Debug, Clone, Copy)] From 55819aa5839ce954d9b14b895e1f1d8149b16232 Mon Sep 17 00:00:00 2001 From: Pete Kubiak Date: Thu, 27 Feb 2025 11:41:00 +0000 Subject: [PATCH 21/21] Format with the correct version of rustfmt --- host/src/attribute.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/host/src/attribute.rs b/host/src/attribute.rs index 5a127a5d..c41ceb36 100644 --- a/host/src/attribute.rs +++ b/host/src/attribute.rs @@ -5,17 +5,17 @@ use core::marker::PhantomData; use bt_hci::uuid::declarations::{CHARACTERISTIC, PRIMARY_SERVICE}; use bt_hci::uuid::descriptors::CLIENT_CHARACTERISTIC_CONFIGURATION; -use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::RawMutex; use heapless::Vec; +use crate::Error; use crate::att::AttErrorCode; use crate::attribute_server::AttributeServer; use crate::cursor::{ReadCursor, WriteCursor}; use crate::prelude::{AsGatt, Connection, FixedGattValue, FromGatt}; use crate::types::gatt_traits::FromGattError; pub use crate::types::uuid::Uuid; -use crate::Error; /// Characteristic properties #[derive(Debug, Clone, Copy)]