Skip to content

Commit

Permalink
Add ability to act on notification enables/disables.
Browse files Browse the repository at this point in the history
Being able to take action when a characteristic's notifications
are enabled are disabled is useful when the data source needs
some action to be taken to start or stop the data source like
enabling interrupts or powering on a sensor.
  • Loading branch information
konkers committed Mar 9, 2024
1 parent 92d5213 commit f359fef
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 27 deletions.
32 changes: 29 additions & 3 deletions bleps-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ pub fn gatt(input: TokenStream) -> TokenStream {
return quote!{ compile_error!("Unexpected"); }.into();
}
}
"notify_cb" => {
if let Expr::Path(p) = field.expr {
let name = path_to_string(p.path);
charact.notify_cb = Some(name);
} else {
return quote!{ compile_error!("Unexpected"); }.into();
}
}
"name" => {
if let Expr::Lit(value) = field.expr {
if let Lit::Str(s) = value.lit {
Expand Down Expand Up @@ -344,7 +352,14 @@ pub fn gatt(input: TokenStream) -> TokenStream {
quote!(())
};

quote!(let mut #gen_attr_att_data_ident = (#rfunction, #wfunction);)
let nfunction = if let Some(name) = &characteristic.notify_cb {
let fname = format_ident!("{}", name);
quote!(&mut #fname)
} else {
quote!(())
};

quote!(let mut #gen_attr_att_data_ident = (#rfunction, #wfunction, #nfunction);)
} else if let Some(name) = &characteristic.value {
let vname = format_ident!("{}", name);
quote!(let mut #gen_attr_att_data_ident = #vname;)
Expand Down Expand Up @@ -391,10 +406,17 @@ pub fn gatt(input: TokenStream) -> TokenStream {
let rfunction = format_ident!("_attr_read{}", current_handle);
let wfunction = format_ident!("_attr_write{}", current_handle);
decls.push(
quote!(let mut #char_ccd_data_attr = (&mut #rfunction, &mut #wfunction);),
quote!(let mut #char_ccd_data_attr = (&mut #rfunction, &mut #wfunction, ());),
);

let backing_data = format_ident!("_attr_data{}", current_handle);
let notify_cb_call = if let Some(cb) = &characteristic.notify_cb {
let cb = format_ident!("{}", cb);
quote!(#cb(unsafe{#backing_data[0] & 0x1 == 0x1}))
} else {
quote!()
};

pre.push(quote!(
#[allow(non_upper_case_globals)]
static mut #backing_data: [u8; 2] = [0u8; 2];
Expand Down Expand Up @@ -424,6 +446,7 @@ pub fn gatt(input: TokenStream) -> TokenStream {
}
}
}
#notify_cb_call;
};
));

Expand Down Expand Up @@ -497,7 +520,9 @@ pub fn gatt(input: TokenStream) -> TokenStream {
quote!(())
};

decls.push(quote!(let mut #char_desc_data_ident = (#rfunction, #wfunction);));
decls.push(
quote!(let mut #char_desc_data_ident = (#rfunction, #wfunction, ());),
);
} else if let Some(name) = &descriptor.value {
let vname = format_ident!("{}", name);
decls.push(quote!(let mut #char_desc_data_ident = #vname;));
Expand Down Expand Up @@ -572,6 +597,7 @@ struct Characteristic {
write: Option<String>,
description: Option<String>,
notify: bool,
notify_cb: Option<String>,
name: Option<String>,
descriptors: Vec<Descriptor>,
}
Expand Down
78 changes: 75 additions & 3 deletions bleps/src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ pub trait AttData {
fn write(&mut self, _offset: usize, _data: &[u8]) -> Result<(), AttErrorCode> {
Ok(())
}

fn enable_notification(&mut self, _enabled: bool) -> Result<(), AttErrorCode> {
Ok(())
}
}

impl<'a, const N: usize> AttData for &'a [u8; N] {
Expand Down Expand Up @@ -193,7 +197,7 @@ impl<T> IntoResult<T> for Result<T, AttErrorCode> {
}
}

impl<T, R> AttData for (R, ())
impl<T, R> AttData for (R, (), ())
where
T: IntoResult<usize>,
R: FnMut(usize, &mut [u8]) -> T,
Expand All @@ -207,7 +211,7 @@ where
}
}

impl<U, W> AttData for ((), W)
impl<U, W> AttData for ((), W, ())
where
U: IntoResult<()>,
W: FnMut(usize, &[u8]) -> U,
Expand All @@ -221,7 +225,7 @@ where
}
}

impl<T, U, R, W> AttData for (R, W)
impl<T, U, R, W> AttData for (R, W, ())
where
T: IntoResult<usize>,
U: IntoResult<()>,
Expand All @@ -245,6 +249,74 @@ where
}
}

impl<T, U, R, N> AttData for (R, (), N)
where
T: IntoResult<usize>,
U: IntoResult<()>,
R: FnMut(usize, &mut [u8]) -> T,
N: FnMut(bool) -> U,
{
fn readable(&self) -> bool {
true
}

fn read(&mut self, offset: usize, data: &mut [u8]) -> Result<usize, AttErrorCode> {
self.0(offset, data).into_result()
}

fn enable_notification(&mut self, enabled: bool) -> Result<(), AttErrorCode> {
self.2(enabled).into_result()
}
}

impl<T, U, R, W, N> AttData for (R, W, N)
where
T: IntoResult<usize>,
U: IntoResult<()>,
R: FnMut(usize, &mut [u8]) -> T,
W: FnMut(usize, &[u8]) -> U,
N: FnMut(bool) -> U,
{
fn readable(&self) -> bool {
true
}

fn read(&mut self, offset: usize, data: &mut [u8]) -> Result<usize, AttErrorCode> {
self.0(offset, data).into_result()
}

fn writable(&self) -> bool {
true
}

fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
self.1(offset, data).into_result()
}

fn enable_notification(&mut self, enabled: bool) -> Result<(), AttErrorCode> {
self.2(enabled).into_result()
}
}

impl<U, W, N> AttData for ((), W, N)
where
U: IntoResult<()>,
W: FnMut(usize, &[u8]) -> U,
N: FnMut(bool) -> U,
{
fn writable(&self) -> bool {
true
}

fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
self.1(offset, data).into_result()
}

fn enable_notification(&mut self, enabled: bool) -> Result<(), AttErrorCode> {
self.2(enabled).into_result()
}
}

pub const ATT_READABLE: u8 = 0x02;
pub const ATT_WRITEABLE: u8 = 0x08;

Expand Down
50 changes: 29 additions & 21 deletions bleps/src/attribute_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,31 +396,39 @@ bleps_dedup::dedup! {
self.write_att(src_handle, response).await;
}

async fn handle_write_cmd(&mut self, _src_handle: u16, handle: u16, data: Data) {
for att in self.attributes.iter_mut() {
if att.handle == handle {
if att.data.writable() {
// Write commands can't respond with an error.
let err = att.data.write(0, data.as_slice());
if let Err(e) = err {
log::debug!("write error: {e:?}");
}
}
break;
}
fn handle_write(&mut self, handle: u16, data: Data) -> Result<(), AttErrorCode> {
let Some((index, att)) = self.attributes.iter_mut().enumerate().find(|(_, att)| att.handle == handle) else {
return Err(AttErrorCode::InvalidHandle);
};
if !att.data.writable() {
return Err(AttErrorCode::WriteNotPermitted);
}

let err = att.data.write(0, data.as_slice());
if let Err(e) = err {
log::debug!("write error: {e:?}");
return Err(e);
}

// If this is a Client Characteristic Configuration descriptor, notify the parent of a change
// otherwise return immediatly.
if att.uuid != Uuid::Uuid16(0x2902) {
return Ok(());
}

// Here we make the same assumption made in async_atribute_server that the CCCD directly follows
// the charactaristic attribute.
let parrent_att = &mut self.attributes[index-1];
parrent_att.data.enable_notification(data.as_slice()[0] & 0x1 == 0x1)
}

async fn handle_write_cmd(&mut self, _src_handle: u16, handle: u16, data: Data) {
// Write commands can't respond with an error.
let _ = self.handle_write(handle, data);
}

async fn handle_write_req(&mut self, src_handle: u16, handle: u16, data: Data) {
let mut err = Err(AttErrorCode::AttributeNotFound);
for att in self.attributes.iter_mut() {
if att.handle == handle {
if att.data.writable() {
err = att.data.write(0, data.as_slice());
}
break;
}
}
let err = self.handle_write(handle, data);

let response = match err {
Ok(()) => Data::new_att_write_response(),
Expand Down

0 comments on commit f359fef

Please sign in to comment.