From a78093e4327cf18055b4b00388afb9509c582afb Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Mon, 4 Dec 2023 12:48:20 +0100 Subject: [PATCH 1/3] Re-align sync/async attribute server --- bleps/src/async_attribute_server.rs | 13 ++++++------- bleps/src/attribute_server.rs | 8 +++----- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/bleps/src/async_attribute_server.rs b/bleps/src/async_attribute_server.rs index e5afcc9..135a169 100644 --- a/bleps/src/async_attribute_server.rs +++ b/bleps/src/async_attribute_server.rs @@ -14,7 +14,7 @@ use crate::{ ATT_READ_REQUEST_OPCODE, ATT_WRITE_REQUEST_OPCODE, }, attribute::Attribute, - attribute_server::{AttributeServerError, NotificationData, WorkResult, MTU}, + attribute_server::{AttributeServerError, NotificationData, WorkResult, BASE_MTU, MTU}, command::{Command, LE_OGF, SET_ADVERTISING_DATA_OCF}, event::EventType, l2cap::L2capPacket, @@ -182,7 +182,7 @@ where ) -> Result { if let Some(notification_data) = notification_data { let mut answer = notification_data.data; - answer.limit_len(MTU as usize - 3); + answer.limit_len(BASE_MTU as usize - 3); let mut data = Data::new_att_value_ntf(notification_data.handle); data.append(&answer.as_slice()); self.write_att(self.src_handle, data).await; @@ -322,7 +322,6 @@ where &Uuid::from(val), ); } - break; } } @@ -333,7 +332,6 @@ where Data::new_att_error_response(ATT_READ_BY_GROUP_TYPE_REQUEST_OPCODE, handle, e) } }; - self.write_att(src_handle, response).await; } @@ -361,6 +359,7 @@ where data.append_att_read_by_type_response(); } } + log::debug!("found! {:x?} {}", att.uuid, att.handle); break; } @@ -379,7 +378,7 @@ where for att in self.attributes.iter_mut() { if att.handle == handle { - if att.handle == handle && att.data.readable() { + if att.data.readable() { err = att.data.read(0, data.as_slice_mut()); if let Ok(len) = err { data.append_len(len); @@ -391,7 +390,7 @@ where let response = match err { Ok(_) => { - data.limit_len(MTU as usize); + data.limit_len(BASE_MTU as usize); data } Err(e) => Data::new_att_error_response(ATT_READ_REQUEST_OPCODE, handle, e), @@ -545,7 +544,7 @@ where let response = match err { Ok(_) => { - data.limit_len(MTU as usize - 1); + data.limit_len(BASE_MTU as usize - 1); data } Err(e) => Data::new_att_error_response(ATT_READ_BLOB_REQ_OPCODE, handle, e), diff --git a/bleps/src/attribute_server.rs b/bleps/src/attribute_server.rs index 32655df..e6078ba 100644 --- a/bleps/src/attribute_server.rs +++ b/bleps/src/attribute_server.rs @@ -411,12 +411,10 @@ impl<'a> AttributeServer<'a> { for att in self.attributes.iter_mut() { log::trace!("Check attribute {:x?} {}", att.uuid, att.handle); if att.handle >= start && att.handle <= end { - if att.handle >= start && att.handle <= end { - if !data.append_att_find_information_response(att.handle, &att.uuid) { - break; - } - log::debug!("found! {:x?} {}", att.uuid, att.handle); + if !data.append_att_find_information_response(att.handle, &att.uuid) { + break; } + log::debug!("found! {:x?} {}", att.uuid, att.handle); } } From 071cb98428cac91de04a5ab0e6347783d5089938 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Mon, 4 Dec 2023 12:59:16 +0100 Subject: [PATCH 2/3] Pull out `async run` --- bleps/src/async_attribute_server.rs | 157 ++++++++++++++-------------- 1 file changed, 81 insertions(+), 76 deletions(-) diff --git a/bleps/src/async_attribute_server.rs b/bleps/src/async_attribute_server.rs index 135a169..79f3eb3 100644 --- a/bleps/src/async_attribute_server.rs +++ b/bleps/src/async_attribute_server.rs @@ -30,6 +30,87 @@ where attributes: &'a mut [Attribute<'a>], } +impl<'a, T> AttributeServer<'a, T> +where + T: embedded_io_async::Read + embedded_io_async::Write, +{ + /// Run the GATT server until disconnect + pub async fn run(&mut self, notifier: &'a mut F) -> Result<(), AttributeServerError> + where + F: FnMut() -> R, + R: core::future::Future, + { + let notification_to_send = Mutex::new(RefCell::new(None)); + loop { + let notifier_future = async { notifier().await }; + let worker_future = async { + let notification: Option = + critical_section::with(|cs| notification_to_send.borrow_ref_mut(cs).take()); + + // check if notifications are enabled for the characteristic handle + let notification = if let Some(notification) = notification { + let attr = self + .attributes + .iter() + .enumerate() + .find(|(_idx, attr)| attr.handle == notification.handle); + let enabled = if let Some((idx, _)) = attr { + // assume the next descriptor is the "Client Characteristic Configuration" Descriptor + // which is always true when using the macro + if self.attributes.len() > idx + 1 + && self.attributes[idx + 1].uuid == Uuid::Uuid16(0x2902) + { + let mut cccd = [0u8; 1]; + let cccd_len = + self.get_characteristic_value((idx + 1) as u16, 0, &mut cccd[..]); + if let Some(1) = cccd_len { + cccd[0] == 1 + } else { + false + } + } else { + false + } + } else { + false + }; + if enabled { + Some(notification) + } else { + None + } + } else { + None + }; + + self.do_work_with_notification(notification).await + }; + pin_mut!(notifier_future); + pin_mut!(worker_future); + + let notification = match futures::future::select(notifier_future, worker_future).await { + Either::Left((notification, _)) => Some(notification), + Either::Right((value, _)) => { + if value? == WorkResult::GotDisconnected { + break; + } + None + } + }; + + if let Some(notification) = notification { + critical_section::with(|cs| { + notification_to_send + .borrow_ref_mut(cs) + .replace(notification); + }); + } + } + + Ok(()) + } +} + impl<'a, T> AttributeServer<'a, T> where T: embedded_io_async::Read + embedded_io_async::Write, @@ -96,82 +177,6 @@ where Ok(EventType::Unknown) } - /// Run the GATT server until disconnect - pub async fn run(&mut self, notifier: &'a mut F) -> Result<(), AttributeServerError> - where - F: FnMut() -> R, - R: core::future::Future, - { - let notification_to_send = Mutex::new(RefCell::new(None)); - loop { - let notifier_future = async { notifier().await }; - let worker_future = async { - let notification: Option = - critical_section::with(|cs| notification_to_send.borrow_ref_mut(cs).take()); - - // check if notifications are enabled for the characteristic handle - let notification = if let Some(notification) = notification { - let attr = self - .attributes - .iter() - .enumerate() - .find(|(_idx, attr)| attr.handle == notification.handle); - let enabled = if let Some((idx, _)) = attr { - // assume the next descriptor is the "Client Characteristic Configuration" Descriptor - // which is always true when using the macro - if self.attributes.len() > idx + 1 - && self.attributes[idx + 1].uuid == Uuid::Uuid16(0x2902) - { - let mut cccd = [0u8; 1]; - let cccd_len = - self.get_characteristic_value((idx + 1) as u16, 0, &mut cccd[..]); - if let Some(1) = cccd_len { - cccd[0] == 1 - } else { - false - } - } else { - false - } - } else { - false - }; - if enabled { - Some(notification) - } else { - None - } - } else { - None - }; - - self.do_work_with_notification(notification).await - }; - pin_mut!(notifier_future); - pin_mut!(worker_future); - - let notification = match futures::future::select(notifier_future, worker_future).await { - Either::Left((notification, _)) => Some(notification), - Either::Right((value, _)) => { - if value? == WorkResult::GotDisconnected { - break; - } - None - } - }; - - if let Some(notification) = notification { - critical_section::with(|cs| { - notification_to_send - .borrow_ref_mut(cs) - .replace(notification); - }); - } - } - - Ok(()) - } - pub async fn do_work(&mut self) -> Result { self.do_work_with_notification(None).await } From 6ded28753a515860c2656db638993062f583302b Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Mon, 4 Dec 2023 15:57:28 +0100 Subject: [PATCH 3/3] De-duplicate the attribute server --- bleps-dedup/Cargo.toml | 17 + bleps-dedup/src/lib.rs | 139 +++++ bleps/Cargo.toml | 4 +- bleps/src/async_attribute_server.rs | 592 +++------------------ bleps/src/attribute_server.rs | 765 +++++++++++++++------------- example/Cargo.toml | 2 +- 6 files changed, 627 insertions(+), 892 deletions(-) create mode 100644 bleps-dedup/Cargo.toml create mode 100644 bleps-dedup/src/lib.rs diff --git a/bleps-dedup/Cargo.toml b/bleps-dedup/Cargo.toml new file mode 100644 index 0000000..80b2f1b --- /dev/null +++ b/bleps-dedup/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "bleps-dedup" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0" +proc-macro2 = "1.0" +proc-macro-error = "1.0.4" +syn = { version = "2.0.39", features = ["full"] } +darling = "0.20.3" + +[features] +generate-async = [] diff --git a/bleps-dedup/src/lib.rs b/bleps-dedup/src/lib.rs new file mode 100644 index 0000000..c3a3b26 --- /dev/null +++ b/bleps-dedup/src/lib.rs @@ -0,0 +1,139 @@ +use proc_macro::TokenStream; +use proc_macro_error::proc_macro_error; + +#[derive(Debug)] +struct Implementation { + is_async: bool, + tokens: TokenStream, +} + +/// A macro to de-duplicate SYNC / ASYNC code loosely inspired by maybe-async +#[proc_macro] +#[proc_macro_error] +pub fn dedup(input: TokenStream) -> TokenStream { + let mut impls: Vec = Vec::new(); + let mut body = None; + let mut impl_is_async = false; + + let mut current = TokenStream::new(); + for token in input.into_iter() { + let tok = token.clone(); + match token { + proc_macro::TokenTree::Ident(ident) => match ident.to_string().as_str() { + "impl" => { + if !current.is_empty() { + impls.push(Implementation { + is_async: impl_is_async, + tokens: current, + }) + } + current = TokenStream::new(); + current.extend([tok]); + } + "SYNC" => { + impl_is_async = false; + } + "ASYNC" => { + impl_is_async = true; + } + _ => { + current.extend([tok]); + } + }, + proc_macro::TokenTree::Group(_group) => { + if !current.is_empty() { + impls.push(Implementation { + is_async: impl_is_async, + tokens: current.clone(), + }) + } + + let mut stream = TokenStream::new(); + stream.extend([tok]); + body = Some(stream); + } + _ => { + current.extend([tok]); + } + } + } + + let mut generated = Vec::new(); + for imp in impls { + #[cfg(not(feature = "generate-async"))] + if imp.is_async { + continue; + } + + let decl: proc_macro2::TokenStream = imp.tokens.into(); + let block: proc_macro2::TokenStream = if !imp.is_async { + de_async(body.clone().unwrap().into()) + } else { + body.clone().unwrap().into() + }; + + generated.push(quote::quote!( + #decl + #block + )); + } + + quote::quote!( + #(#generated)* + ) + .into() +} + +fn de_async(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream { + let mut output = proc_macro2::TokenStream::new(); + + let mut prev = None; + for token in input.into_iter() { + let tok = token.clone(); + match token { + proc_macro2::TokenTree::Ident(ident) => { + if match ident.to_string().as_str() { + "await" => { + prev = None; + false + } + "async" => false, + _ => true, + } { + if let Some(prev) = prev.clone() { + output.extend([prev]); + } + prev = None; + output.extend([tok]); + } + } + proc_macro2::TokenTree::Punct(p) => { + if p.as_char() == '.' { + prev = Some(tok); + } else { + output.extend([tok]); + } + } + proc_macro2::TokenTree::Group(group) => { + if let Some(prev) = prev.clone() { + output.extend([prev]); + } + prev = None; + + let converted = de_async(group.stream()); + let group = proc_macro2::Group::new(group.delimiter(), converted); + let group = proc_macro2::TokenTree::Group(group); + output.extend([group]); + } + _ => { + if let Some(prev) = prev.clone() { + output.extend([prev]); + } + prev = None; + output.extend([tok]); + } + } + } + + output +} diff --git a/bleps/Cargo.toml b/bleps/Cargo.toml index 7d2a96b..34bcc8c 100644 --- a/bleps/Cargo.toml +++ b/bleps/Cargo.toml @@ -17,6 +17,8 @@ categories = [ ] [dependencies] +bleps-dedup = { path = "../bleps-dedup" } + log = "0.4.16" embedded-io-blocking = { package = "embedded-io", version = "0.6.1" } embedded-io-async = { version = "0.6.0", optional = true } @@ -29,7 +31,7 @@ bleps-macros = { path = "../bleps-macros", optional = true } env_logger = "0.10.0" [features] -async = [ "dep:embedded-io-async", "dep:futures", "dep:critical-section" ] +async = [ "dep:embedded-io-async", "dep:futures", "dep:critical-section", "bleps-dedup/generate-async" ] macros = [ "bleps-macros" ] defmt = [ "dep:defmt" ] mtu128 = [] diff --git a/bleps/src/async_attribute_server.rs b/bleps/src/async_attribute_server.rs index 79f3eb3..09852c0 100644 --- a/bleps/src/async_attribute_server.rs +++ b/bleps/src/async_attribute_server.rs @@ -5,110 +5,19 @@ use futures::future::Either; use futures::pin_mut; use crate::{ - acl::{AclPacket, BoundaryFlag, HostBroadcastFlag}, asynch::Ble, - att::{ - Att, AttErrorCode, Uuid, ATT_FIND_BY_TYPE_VALUE_REQUEST_OPCODE, - ATT_FIND_INFORMATION_REQ_OPCODE, ATT_PREPARE_WRITE_REQ_OPCODE, ATT_READ_BLOB_REQ_OPCODE, - ATT_READ_BY_GROUP_TYPE_REQUEST_OPCODE, ATT_READ_BY_TYPE_REQUEST_OPCODE, - ATT_READ_REQUEST_OPCODE, ATT_WRITE_REQUEST_OPCODE, - }, + att::Uuid, attribute::Attribute, - attribute_server::{AttributeServerError, NotificationData, WorkResult, BASE_MTU, MTU}, - command::{Command, LE_OGF, SET_ADVERTISING_DATA_OCF}, - event::EventType, - l2cap::L2capPacket, - Data, Error, + attribute_server::{AttributeServerError, NotificationData, WorkResult}, }; pub struct AttributeServer<'a, T> where T: embedded_io_async::Read + embedded_io_async::Write, { - ble: &'a mut Ble, - src_handle: u16, - attributes: &'a mut [Attribute<'a>], -} - -impl<'a, T> AttributeServer<'a, T> -where - T: embedded_io_async::Read + embedded_io_async::Write, -{ - /// Run the GATT server until disconnect - pub async fn run(&mut self, notifier: &'a mut F) -> Result<(), AttributeServerError> - where - F: FnMut() -> R, - R: core::future::Future, - { - let notification_to_send = Mutex::new(RefCell::new(None)); - loop { - let notifier_future = async { notifier().await }; - let worker_future = async { - let notification: Option = - critical_section::with(|cs| notification_to_send.borrow_ref_mut(cs).take()); - - // check if notifications are enabled for the characteristic handle - let notification = if let Some(notification) = notification { - let attr = self - .attributes - .iter() - .enumerate() - .find(|(_idx, attr)| attr.handle == notification.handle); - let enabled = if let Some((idx, _)) = attr { - // assume the next descriptor is the "Client Characteristic Configuration" Descriptor - // which is always true when using the macro - if self.attributes.len() > idx + 1 - && self.attributes[idx + 1].uuid == Uuid::Uuid16(0x2902) - { - let mut cccd = [0u8; 1]; - let cccd_len = - self.get_characteristic_value((idx + 1) as u16, 0, &mut cccd[..]); - if let Some(1) = cccd_len { - cccd[0] == 1 - } else { - false - } - } else { - false - } - } else { - false - }; - if enabled { - Some(notification) - } else { - None - } - } else { - None - }; - - self.do_work_with_notification(notification).await - }; - pin_mut!(notifier_future); - pin_mut!(worker_future); - - let notification = match futures::future::select(notifier_future, worker_future).await { - Either::Left((notification, _)) => Some(notification), - Either::Right((value, _)) => { - if value? == WorkResult::GotDisconnected { - break; - } - None - } - }; - - if let Some(notification) = notification { - critical_section::with(|cs| { - notification_to_send - .borrow_ref_mut(cs) - .replace(notification); - }); - } - } - - Ok(()) - } + pub(crate) ble: &'a mut Ble, + pub(crate) src_handle: u16, + pub(crate) attributes: &'a mut [Attribute<'a>], } impl<'a, T> AttributeServer<'a, T> @@ -138,440 +47,79 @@ where } } - pub fn get_characteristic_value( - &mut self, - handle: u16, - offset: u16, - buffer: &mut [u8], - ) -> Option { - let att = &mut self.attributes[handle as usize]; - - if att.data.readable() { - att.data.read(offset as usize, buffer).ok() - } else { - None - } - } - - pub async fn update_le_advertising_data(&mut self, data: Data) -> Result { - self.ble - .write_bytes(Command::LeSetAdvertisingData { data }.encode().as_slice()) - .await; - self.ble - .wait_for_command_complete(LE_OGF, SET_ADVERTISING_DATA_OCF) - .await? - .check_command_completed() - } - - pub async fn disconnect(&mut self, reason: u8) -> Result { - self.ble - .write_bytes( - Command::Disconnect { - connection_handle: 0, - reason, - } - .encode() - .as_slice(), - ) - .await; - Ok(EventType::Unknown) - } - - pub async fn do_work(&mut self) -> Result { - self.do_work_with_notification(None).await - } - - pub async fn do_work_with_notification( - &mut self, - notification_data: Option, - ) -> Result { - if let Some(notification_data) = notification_data { - let mut answer = notification_data.data; - answer.limit_len(BASE_MTU as usize - 3); - let mut data = Data::new_att_value_ntf(notification_data.handle); - data.append(&answer.as_slice()); - self.write_att(self.src_handle, data).await; - } - - let packet = self.ble.poll().await; - - if packet.is_some() { - log::trace!("polled: {:?}", packet); - } - - match packet { - None => Ok(WorkResult::DidWork), - Some(packet) => match packet { - crate::PollResult::Event(evt) => { - if let EventType::DisconnectComplete { - handle: _, - status: _, - reason: _, - } = evt - { - Ok(WorkResult::GotDisconnected) - } else { - Ok(WorkResult::DidWork) - } - } - crate::PollResult::AsyncData(packet) => { - let (src_handle, l2cap_packet) = L2capPacket::decode(packet)?; - let packet = Att::decode(l2cap_packet)?; - log::trace!("att: {:x?}", packet); - match packet { - Att::ReadByGroupTypeReq { - start, - end, - group_type, - } => { - self.handle_read_by_group_type_req(src_handle, start, end, group_type) - .await; - } - - Att::ReadByTypeReq { - start, - end, - attribute_type, - } => { - self.handle_read_by_type_req(src_handle, start, end, attribute_type) - .await; - } - - Att::ReadReq { handle } => { - self.handle_read_req(src_handle, handle).await; - } - - Att::WriteCmd { handle, data } => { - self.src_handle = handle; - self.handle_write_cmd(src_handle, handle, data).await; - } - - Att::WriteReq { handle, data } => { - self.src_handle = src_handle; - self.handle_write_req(src_handle, handle, data).await; - } - - Att::ExchangeMtu { mtu } => { - self.handle_exchange_mtu(src_handle, mtu).await; - } - - Att::FindByTypeValue { - start_handle, - end_handle, - att_type, - att_value, - } => { - self.handle_find_type_value( - src_handle, - start_handle, - end_handle, - att_type, - att_value, - ) - .await; - } - - Att::FindInformation { - start_handle, - end_handle, - } => { - self.handle_find_information(src_handle, start_handle, end_handle) - .await; - } - - Att::PrepareWriteReq { - handle, - offset, - value, - } => { - self.handle_prepare_write(src_handle, handle, offset, value) - .await; - } - - Att::ExecuteWriteReq { flags } => { - self.handle_execute_write(src_handle, flags).await; - } - - Att::ReadBlobReq { handle, offset } => { - self.handle_read_blob(src_handle, handle, offset).await; + /// Run the GATT server until disconnect + pub async fn run(&mut self, notifier: &'a mut F) -> Result<(), AttributeServerError> + where + F: FnMut() -> R, + R: core::future::Future, + { + let notification_to_send = Mutex::new(RefCell::new(None)); + loop { + let notifier_future = async { notifier().await }; + let worker_future = async { + let notification: Option = + critical_section::with(|cs| notification_to_send.borrow_ref_mut(cs).take()); + + // check if notifications are enabled for the characteristic handle + let notification = if let Some(notification) = notification { + let attr = self + .attributes + .iter() + .enumerate() + .find(|(_idx, attr)| attr.handle == notification.handle); + let enabled = if let Some((idx, _)) = attr { + // assume the next descriptor is the "Client Characteristic Configuration" Descriptor + // which is always true when using the macro + if self.attributes.len() > idx + 1 + && self.attributes[idx + 1].uuid == Uuid::Uuid16(0x2902) + { + let mut cccd = [0u8; 1]; + let cccd_len = + self.get_characteristic_value((idx + 1) as u16, 0, &mut cccd[..]); + if let Some(1) = cccd_len { + cccd[0] == 1 + } else { + false + } + } else { + false } + } else { + false + }; + if enabled { + Some(notification) + } else { + None } + } else { + None + }; - Ok(WorkResult::DidWork) - } - }, - } - } - - async fn handle_read_by_group_type_req( - &mut self, - src_handle: u16, - start: u16, - end: u16, - group_type: Uuid, - ) { - // TODO respond with all finds - not just one - let mut handle = start; - let mut data = Data::new_att_read_by_group_type_response(); - let mut val = Err(AttErrorCode::AttributeNotFound); - for att in self.attributes.iter_mut() { - log::trace!("Check attribute {:x?} {}", att.uuid, att.handle); - if att.uuid == group_type && att.handle >= start && att.handle <= end { - log::debug!("found! {:x?}", att.handle); - handle = att.handle; - val = att.value(); - if let Ok(val) = val { - data.append_att_read_by_group_type_response( - att.handle, - att.last_handle_in_group, - &Uuid::from(val), - ); - } - break; - } - } - - let response = match val { - Ok(_) => data, - Err(e) => { - Data::new_att_error_response(ATT_READ_BY_GROUP_TYPE_REQUEST_OPCODE, handle, e) - } - }; - self.write_att(src_handle, response).await; - } - - async fn handle_read_by_type_req( - &mut self, - src_handle: u16, - start: u16, - end: u16, - attribute_type: Uuid, - ) { - // TODO respond with all finds - not just one - let mut handle = start; - let mut data = Data::new_att_read_by_type_response(); - let mut err = Err(AttErrorCode::AttributeNotFound); - for att in self.attributes.iter_mut() { - log::trace!("Check attribute {:x?} {}", att.uuid, att.handle); - if att.uuid == attribute_type && att.handle >= start && att.handle <= end { - data.append_value(att.handle); - handle = att.handle; - - if att.data.readable() { - err = att.data.read(0, data.as_slice_mut()); - if let Ok(len) = err { - data.append_len(len); - data.append_att_read_by_type_response(); - } - } - - log::debug!("found! {:x?} {}", att.uuid, att.handle); - break; - } - } - - let response = match err { - Ok(_) => data, - Err(e) => Data::new_att_error_response(ATT_READ_BY_TYPE_REQUEST_OPCODE, handle, e), - }; - self.write_att(src_handle, response).await; - } - - async fn handle_read_req(&mut self, src_handle: u16, handle: u16) { - let mut data = Data::new_att_read_response(); - let mut err = Err(AttErrorCode::AttributeNotFound); - - for att in self.attributes.iter_mut() { - if att.handle == handle { - if att.data.readable() { - err = att.data.read(0, data.as_slice_mut()); - if let Ok(len) = err { - data.append_len(len); - } - } - break; - } - } - - let response = match err { - Ok(_) => { - data.limit_len(BASE_MTU as usize); - data - } - Err(e) => Data::new_att_error_response(ATT_READ_REQUEST_OPCODE, handle, e), - }; - - 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; - } - } - } - - 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 response = match err { - Ok(()) => Data::new_att_write_response(), - Err(e) => Data::new_att_error_response(ATT_WRITE_REQUEST_OPCODE, handle, e), - }; - self.write_att(src_handle, response).await; - } - - async fn handle_exchange_mtu(&mut self, src_handle: u16, mtu: u16) { - log::debug!("Requested MTU {mtu}, returning {MTU}"); - self.write_att(src_handle, Data::new_att_exchange_mtu_response(MTU)) - .await; - } - - async fn handle_find_type_value( - &mut self, - src_handle: u16, - start: u16, - _end: u16, - _attr_type: u16, - _attr_value: u16, - ) { - // TODO for now just return an error - - // respond with error - self.write_att( - src_handle, - Data::new_att_error_response( - ATT_FIND_BY_TYPE_VALUE_REQUEST_OPCODE, - start, - AttErrorCode::AttributeNotFound, - ), - ) - .await; - } - - async fn handle_find_information(&mut self, src_handle: u16, start: u16, end: u16) { - let mut data = Data::new_att_find_information_response(); - - for att in self.attributes.iter_mut() { - log::trace!("Check attribute {:x?} {}", att.uuid, att.handle); - if att.handle >= start && att.handle <= end { - if !data.append_att_find_information_response(att.handle, &att.uuid) { - break; - } - log::debug!("found! {:x?} {}", att.uuid, att.handle); - } - } - - if data.has_att_find_information_response_data() { - self.write_att(src_handle, data).await; - return; - } - - log::debug!("not found"); - - // respond with error - self.write_att( - src_handle, - Data::new_att_error_response( - ATT_FIND_INFORMATION_REQ_OPCODE, - start, - AttErrorCode::AttributeNotFound, - ), - ) - .await; - } - - async fn handle_prepare_write( - &mut self, - src_handle: u16, - handle: u16, - offset: u16, - value: Data, - ) { - let mut data = Data::new_att_prepare_write_response(handle, offset); - 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(offset as usize, value.as_slice()); - } - data.append(value.as_slice()); - break; - } - } - - let response = match err { - Ok(()) => data, - Err(e) => Data::new_att_error_response(ATT_PREPARE_WRITE_REQ_OPCODE, handle, e), - }; - - self.write_att(src_handle, response).await; - } - - async fn handle_execute_write(&mut self, src_handle: u16, _flags: u8) { - // for now we don't do anything here - self.write_att(src_handle, Data::new_att_execute_write_response()) - .await; - } - - async fn handle_read_blob(&mut self, src_handle: u16, handle: u16, offset: u16) { - let mut data = Data::new_att_read_blob_response(); - let mut err = Err(AttErrorCode::AttributeNotFound); + self.do_work_with_notification(notification).await + }; + pin_mut!(notifier_future); + pin_mut!(worker_future); - for att in self.attributes.iter_mut() { - if att.handle == handle { - if att.data.readable() { - err = att.data.read(offset as usize, data.as_slice_mut()); - if let Ok(len) = err { - data.append_len(len); + let notification = match futures::future::select(notifier_future, worker_future).await { + Either::Left((notification, _)) => Some(notification), + Either::Right((value, _)) => { + if value? == WorkResult::GotDisconnected { + break; } + None } - break; + }; + + if let Some(notification) = notification { + critical_section::with(|cs| { + notification_to_send + .borrow_ref_mut(cs) + .replace(notification); + }); } } - let response = match err { - Ok(_) => { - data.limit_len(BASE_MTU as usize - 1); - data - } - Err(e) => Data::new_att_error_response(ATT_READ_BLOB_REQ_OPCODE, handle, e), - }; - - self.write_att(src_handle, response).await; - } - - async fn write_att(&mut self, handle: u16, data: Data) { - log::debug!("src_handle {}", handle); - log::debug!("data {:x?}", data.as_slice()); - - let res = L2capPacket::encode(data); - log::trace!("encoded_l2cap {:x?}", res.as_slice()); - - let res = AclPacket::encode( - handle, - BoundaryFlag::FirstAutoFlushable, - HostBroadcastFlag::NoBroadcast, - res, - ); - log::trace!("writing {:x?}", res.as_slice()); - self.ble.write_bytes(res.as_slice()).await; + Ok(()) } } diff --git a/bleps/src/attribute_server.rs b/bleps/src/attribute_server.rs index e6078ba..c19d3bb 100644 --- a/bleps/src/attribute_server.rs +++ b/bleps/src/attribute_server.rs @@ -65,446 +65,475 @@ pub struct AttributeServer<'a> { attributes: &'a mut [Attribute<'a>], } -impl<'a> AttributeServer<'a> { - pub fn new(ble: &'a mut Ble<'a>, attributes: &'a mut [Attribute<'a>]) -> AttributeServer<'a> { - for (i, attr) in attributes.iter_mut().enumerate() { - attr.handle = i as u16 + 1; - } - - let mut last_in_group = attributes.last().unwrap().handle; - for i in (0..attributes.len()).rev() { - attributes[i].last_handle_in_group = last_in_group; - - if attributes[i].uuid == Uuid::Uuid16(0x2800) && i > 0 { - last_in_group = attributes[i - 1].handle; +// Using the bleps-dedup proc-macro to de-duplicate the async/sync code +// The macro will remove async/await for the SYNC implementation +bleps_dedup::dedup! { + impl<'a> SYNC AttributeServer<'a> + impl<'a, T> ASYNC crate::async_attribute_server::AttributeServer<'a, T> + where + T: embedded_io_async::Read + embedded_io_async::Write, + { + pub fn get_characteristic_value( + &mut self, + handle: u16, + offset: u16, + buffer: &mut [u8], + ) -> Option { + let att = &mut self.attributes[handle as usize]; + + if att.data.readable() { + att.data.read(offset as usize, buffer).ok() + } else { + None } } - log::trace!("{:#x?}", &attributes); - - AttributeServer { - ble, - src_handle: 0, - attributes, - } - } - - pub fn get_characteristic_value( - &mut self, - handle: u16, - offset: u16, - buffer: &mut [u8], - ) -> Option { - let att = &mut self.attributes[handle as usize]; - - if att.data.readable() { - att.data.read(offset as usize, buffer).ok() - } else { - None + pub async fn update_le_advertising_data(&mut self, data: Data) -> Result { + self.ble + .write_bytes(Command::LeSetAdvertisingData { data }.encode().as_slice()) + .await; + self.ble + .wait_for_command_complete(LE_OGF, SET_ADVERTISING_DATA_OCF) + .await? + .check_command_completed() } - } - pub fn update_le_advertising_data(&mut self, data: Data) -> Result { - self.ble - .write_bytes(Command::LeSetAdvertisingData { data }.encode().as_slice()); - self.ble - .wait_for_command_complete(LE_OGF, SET_ADVERTISING_DATA_OCF)? - .check_command_completed() - } - - pub fn disconnect(&mut self, reason: u8) -> Result { - self.ble.write_bytes( - Command::Disconnect { - connection_handle: 0, - reason, - } - .encode() - .as_slice(), - ); - Ok(EventType::Unknown) - } - - pub fn do_work(&mut self) -> Result { - self.do_work_with_notification(None) - } - - pub fn do_work_with_notification( - &mut self, - notification_data: Option, - ) -> Result { - if let Some(notification_data) = notification_data { - let mut answer = notification_data.data; - answer.limit_len(BASE_MTU as usize - 3); - let mut data = Data::new_att_value_ntf(notification_data.handle); - data.append(&answer.as_slice()); - self.write_att(self.src_handle, data); + pub async fn disconnect(&mut self, reason: u8) -> Result { + self.ble + .write_bytes( + Command::Disconnect { + connection_handle: 0, + reason, + } + .encode() + .as_slice(), + ) + .await; + Ok(EventType::Unknown) } - let packet = self.ble.poll(); - - if packet.is_some() { - log::trace!("polled: {:?}", packet); + pub async fn do_work(&mut self) -> Result { + self.do_work_with_notification(None).await } - match packet { - None => Ok(WorkResult::DidWork), - Some(packet) => match packet { - crate::PollResult::Event(evt) => { - if let EventType::DisconnectComplete { - handle: _, - status: _, - reason: _, - } = evt - { - Ok(WorkResult::GotDisconnected) - } else { - Ok(WorkResult::DidWork) - } - } - crate::PollResult::AsyncData(packet) => { - let (src_handle, l2cap_packet) = L2capPacket::decode(packet)?; - let packet = Att::decode(l2cap_packet)?; - log::trace!("att: {:x?}", packet); - match packet { - Att::ReadByGroupTypeReq { - start, - end, - group_type, - } => { - self.handle_read_by_group_type_req(src_handle, start, end, group_type); - } - - Att::ReadByTypeReq { - start, - end, - attribute_type, - } => { - self.handle_read_by_type_req(src_handle, start, end, attribute_type); - } - - Att::ReadReq { handle } => { - self.handle_read_req(src_handle, handle); - } + pub async fn do_work_with_notification( + &mut self, + notification_data: Option, + ) -> Result { + if let Some(notification_data) = notification_data { + let mut answer = notification_data.data; + answer.limit_len(BASE_MTU as usize - 3); + let mut data = Data::new_att_value_ntf(notification_data.handle); + data.append(&answer.as_slice()); + self.write_att(self.src_handle, data).await; + } - Att::WriteReq { handle, data } => { - self.src_handle = src_handle; - self.handle_write_req(src_handle, handle, data); - } + let packet = self.ble.poll().await; - Att::WriteCmd { handle, data } => { - self.src_handle = src_handle; - self.handle_write_cmd(src_handle, handle, data); - } + if packet.is_some() { + log::trace!("polled: {:?}", packet); + } - Att::ExchangeMtu { mtu } => { - self.handle_exchange_mtu(src_handle, mtu); + match packet { + None => Ok(WorkResult::DidWork), + Some(packet) => match packet { + crate::PollResult::Event(evt) => { + if let EventType::DisconnectComplete { + handle: _, + status: _, + reason: _, + } = evt + { + Ok(WorkResult::GotDisconnected) + } else { + Ok(WorkResult::DidWork) } - - Att::FindByTypeValue { - start_handle, - end_handle, - att_type, - att_value, - } => { - self.handle_find_type_value( - src_handle, + } + crate::PollResult::AsyncData(packet) => { + let (src_handle, l2cap_packet) = L2capPacket::decode(packet)?; + let packet = Att::decode(l2cap_packet)?; + log::trace!("att: {:x?}", packet); + match packet { + Att::ReadByGroupTypeReq { + start, + end, + group_type, + } => { + self.handle_read_by_group_type_req(src_handle, start, end, group_type) + .await; + } + + Att::ReadByTypeReq { + start, + end, + attribute_type, + } => { + self.handle_read_by_type_req(src_handle, start, end, attribute_type) + .await; + } + + Att::ReadReq { handle } => { + self.handle_read_req(src_handle, handle).await; + } + + Att::WriteCmd { handle, data } => { + self.src_handle = handle; + self.handle_write_cmd(src_handle, handle, data).await; + } + + Att::WriteReq { handle, data } => { + self.src_handle = src_handle; + self.handle_write_req(src_handle, handle, data).await; + } + + Att::ExchangeMtu { mtu } => { + self.handle_exchange_mtu(src_handle, mtu).await; + } + + Att::FindByTypeValue { start_handle, end_handle, att_type, att_value, - ); - } - - Att::FindInformation { - start_handle, - end_handle, - } => { - self.handle_find_information(src_handle, start_handle, end_handle); - } - - Att::PrepareWriteReq { - handle, - offset, - value, - } => { - self.handle_prepare_write(src_handle, handle, offset, value); + } => { + self.handle_find_type_value( + src_handle, + start_handle, + end_handle, + att_type, + att_value, + ) + .await; + } + + Att::FindInformation { + start_handle, + end_handle, + } => { + self.handle_find_information(src_handle, start_handle, end_handle) + .await; + } + + Att::PrepareWriteReq { + handle, + offset, + value, + } => { + self.handle_prepare_write(src_handle, handle, offset, value) + .await; + } + + Att::ExecuteWriteReq { flags } => { + self.handle_execute_write(src_handle, flags).await; + } + + Att::ReadBlobReq { handle, offset } => { + self.handle_read_blob(src_handle, handle, offset).await; + } } - Att::ExecuteWriteReq { flags } => { - self.handle_execute_write(src_handle, flags); - } + Ok(WorkResult::DidWork) + } + }, + } + } - Att::ReadBlobReq { handle, offset } => { - self.handle_read_blob(src_handle, handle, offset); - } + async fn handle_read_by_group_type_req( + &mut self, + src_handle: u16, + start: u16, + end: u16, + group_type: Uuid, + ) { + // TODO respond with all finds - not just one + let mut handle = start; + let mut data = Data::new_att_read_by_group_type_response(); + let mut val = Err(AttErrorCode::AttributeNotFound); + for att in self.attributes.iter_mut() { + log::trace!("Check attribute {:x?} {}", att.uuid, att.handle); + if att.uuid == group_type && att.handle >= start && att.handle <= end { + log::debug!("found! {:x?}", att.handle); + handle = att.handle; + val = att.value(); + if let Ok(val) = val { + data.append_att_read_by_group_type_response( + att.handle, + att.last_handle_in_group, + &Uuid::from(val), + ); } + break; + } + } - Ok(WorkResult::DidWork) + let response = match val { + Ok(_) => data, + Err(e) => { + Data::new_att_error_response(ATT_READ_BY_GROUP_TYPE_REQUEST_OPCODE, handle, e) } - }, + }; + self.write_att(src_handle, response).await; } - } - fn handle_read_by_group_type_req( - &mut self, - src_handle: u16, - start: u16, - end: u16, - group_type: Uuid, - ) { - // TODO respond with all finds - not just one - let mut handle = start; - let mut data = Data::new_att_read_by_group_type_response(); - let mut val = Err(AttErrorCode::AttributeNotFound); - for att in self.attributes.iter_mut() { - log::trace!("Check attribute {:x?} {}", att.uuid, att.handle); - if att.uuid == group_type && att.handle >= start && att.handle <= end { - log::debug!("found! {:x?}", att.handle); - handle = att.handle; - val = att.value(); - if let Ok(val) = val { - data.append_att_read_by_group_type_response( - att.handle, - att.last_handle_in_group, - &Uuid::from(val), - ); + async fn handle_read_by_type_req( + &mut self, + src_handle: u16, + start: u16, + end: u16, + attribute_type: Uuid, + ) { + // TODO respond with all finds - not just one + let mut handle = start; + let mut data = Data::new_att_read_by_type_response(); + let mut err = Err(AttErrorCode::AttributeNotFound); + for att in self.attributes.iter_mut() { + log::trace!("Check attribute {:x?} {}", att.uuid, att.handle); + if att.uuid == attribute_type && att.handle >= start && att.handle <= end { + data.append_value(att.handle); + handle = att.handle; + + if att.data.readable() { + err = att.data.read(0, data.as_slice_mut()); + if let Ok(len) = err { + data.append_len(len); + data.append_att_read_by_type_response(); + } + } + + log::debug!("found! {:x?} {}", att.uuid, att.handle); + break; } - break; } + + let response = match err { + Ok(_) => data, + Err(e) => Data::new_att_error_response(ATT_READ_BY_TYPE_REQUEST_OPCODE, handle, e), + }; + self.write_att(src_handle, response).await; } - let response = match val { - Ok(_) => data, - Err(e) => { - Data::new_att_error_response(ATT_READ_BY_GROUP_TYPE_REQUEST_OPCODE, handle, e) - } - }; - self.write_att(src_handle, response); - } + async fn handle_read_req(&mut self, src_handle: u16, handle: u16) { + let mut data = Data::new_att_read_response(); + let mut err = Err(AttErrorCode::AttributeNotFound); - fn handle_read_by_type_req( - &mut self, - src_handle: u16, - start: u16, - end: u16, - attribute_type: Uuid, - ) { - // TODO respond with all finds - not just one - let mut handle = start; - let mut data = Data::new_att_read_by_type_response(); - let mut err = Err(AttErrorCode::AttributeNotFound); - for att in self.attributes.iter_mut() { - log::trace!("Check attribute {:x?} {}", att.uuid, att.handle); - if att.uuid == attribute_type && att.handle >= start && att.handle <= end { - data.append_value(att.handle); - handle = att.handle; - - if att.data.readable() { - err = att.data.read(0, data.as_slice_mut()); - if let Ok(len) = err { - data.append_len(len); - data.append_att_read_by_type_response(); + for att in self.attributes.iter_mut() { + if att.handle == handle { + if att.data.readable() { + err = att.data.read(0, data.as_slice_mut()); + if let Ok(len) = err { + data.append_len(len); + } } + break; } - - log::debug!("found! {:x?} {}", att.uuid, att.handle); - break; } - } - let response = match err { - Ok(_) => data, - Err(e) => Data::new_att_error_response(ATT_READ_BY_TYPE_REQUEST_OPCODE, handle, e), - }; - self.write_att(src_handle, response); - } + let response = match err { + Ok(_) => { + data.limit_len(BASE_MTU as usize); + data + } + Err(e) => Data::new_att_error_response(ATT_READ_REQUEST_OPCODE, handle, e), + }; - fn handle_read_req(&mut self, src_handle: u16, handle: u16) { - let mut data = Data::new_att_read_response(); - let mut err = Err(AttErrorCode::AttributeNotFound); + self.write_att(src_handle, response).await; + } - for att in self.attributes.iter_mut() { - if att.handle == handle { - if att.data.readable() { - err = att.data.read(0, data.as_slice_mut()); - if let Ok(len) = err { - data.append_len(len); + 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; } - break; } } - let response = match err { - Ok(_) => { - data.limit_len(BASE_MTU as usize); - data - } - Err(e) => Data::new_att_error_response(ATT_READ_REQUEST_OPCODE, handle, e), - }; - - self.write_att(src_handle, response); - } - - 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:?}"); + 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; } - break; } + + let response = match err { + Ok(()) => Data::new_att_write_response(), + Err(e) => Data::new_att_error_response(ATT_WRITE_REQUEST_OPCODE, handle, e), + }; + self.write_att(src_handle, response).await; } - } - 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; - } + async fn handle_exchange_mtu(&mut self, src_handle: u16, mtu: u16) { + log::debug!("Requested MTU {mtu}, returning {MTU}"); + self.write_att(src_handle, Data::new_att_exchange_mtu_response(MTU)) + .await; } - let response = match err { - Ok(()) => Data::new_att_write_response(), - Err(e) => Data::new_att_error_response(ATT_WRITE_REQUEST_OPCODE, handle, e), - }; - self.write_att(src_handle, response); - } + async fn handle_find_type_value( + &mut self, + src_handle: u16, + start: u16, + _end: u16, + _attr_type: u16, + _attr_value: u16, + ) { + // TODO for now just return an error + + // respond with error + self.write_att( + src_handle, + Data::new_att_error_response( + ATT_FIND_BY_TYPE_VALUE_REQUEST_OPCODE, + start, + AttErrorCode::AttributeNotFound, + ), + ) + .await; + } - fn handle_exchange_mtu(&mut self, src_handle: u16, mtu: u16) { - log::debug!("Requested MTU {mtu}, returning {MTU}"); - self.write_att(src_handle, Data::new_att_exchange_mtu_response(MTU)); - } + async fn handle_find_information(&mut self, src_handle: u16, start: u16, end: u16) { + let mut data = Data::new_att_find_information_response(); - fn handle_find_type_value( - &mut self, - src_handle: u16, - start: u16, - _end: u16, - _attr_type: u16, - _attr_value: u16, - ) { - // TODO for now just return an error - - // respond with error - self.write_att( - src_handle, - Data::new_att_error_response( - ATT_FIND_BY_TYPE_VALUE_REQUEST_OPCODE, - start, - AttErrorCode::AttributeNotFound, - ), - ); - } + for att in self.attributes.iter_mut() { + log::trace!("Check attribute {:x?} {}", att.uuid, att.handle); + if att.handle >= start && att.handle <= end { + if !data.append_att_find_information_response(att.handle, &att.uuid) { + break; + } + log::debug!("found! {:x?} {}", att.uuid, att.handle); + } + } - fn handle_find_information(&mut self, src_handle: u16, start: u16, end: u16) { - let mut data = Data::new_att_find_information_response(); + if data.has_att_find_information_response_data() { + self.write_att(src_handle, data).await; + return; + } - for att in self.attributes.iter_mut() { - log::trace!("Check attribute {:x?} {}", att.uuid, att.handle); - if att.handle >= start && att.handle <= end { - if !data.append_att_find_information_response(att.handle, &att.uuid) { + log::debug!("not found"); + + // respond with error + self.write_att( + src_handle, + Data::new_att_error_response( + ATT_FIND_INFORMATION_REQ_OPCODE, + start, + AttErrorCode::AttributeNotFound, + ), + ) + .await; + } + + async fn handle_prepare_write( + &mut self, + src_handle: u16, + handle: u16, + offset: u16, + value: Data, + ) { + let mut data = Data::new_att_prepare_write_response(handle, offset); + 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(offset as usize, value.as_slice()); + } + data.append(value.as_slice()); break; } - log::debug!("found! {:x?} {}", att.uuid, att.handle); } - } - if data.has_att_find_information_response_data() { - self.write_att(src_handle, data); - return; + let response = match err { + Ok(()) => data, + Err(e) => Data::new_att_error_response(ATT_PREPARE_WRITE_REQ_OPCODE, handle, e), + }; + + self.write_att(src_handle, response).await; } - log::debug!("not found"); - - // respond with error - self.write_att( - src_handle, - Data::new_att_error_response( - ATT_FIND_INFORMATION_REQ_OPCODE, - start, - AttErrorCode::AttributeNotFound, - ), - ); - } + async fn handle_execute_write(&mut self, src_handle: u16, _flags: u8) { + // for now we don't do anything here + self.write_att(src_handle, Data::new_att_execute_write_response()) + .await; + } - fn handle_prepare_write(&mut self, src_handle: u16, handle: u16, offset: u16, value: Data) { - let mut data = Data::new_att_prepare_write_response(handle, offset); - let mut err = Err(AttErrorCode::AttributeNotFound); + async fn handle_read_blob(&mut self, src_handle: u16, handle: u16, offset: u16) { + let mut data = Data::new_att_read_blob_response(); + 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(offset as usize, value.as_slice()); + for att in self.attributes.iter_mut() { + if att.handle == handle { + if att.data.readable() { + err = att.data.read(offset as usize, data.as_slice_mut()); + if let Ok(len) = err { + data.append_len(len); + } + } + break; } - data.append(value.as_slice()); - break; } - } - let response = match err { - Ok(()) => data, - Err(e) => Data::new_att_error_response(ATT_PREPARE_WRITE_REQ_OPCODE, handle, e), - }; + let response = match err { + Ok(_) => { + data.limit_len(BASE_MTU as usize - 1); + data + } + Err(e) => Data::new_att_error_response(ATT_READ_BLOB_REQ_OPCODE, handle, e), + }; - self.write_att(src_handle, response); - } + self.write_att(src_handle, response).await; + } - fn handle_execute_write(&mut self, src_handle: u16, _flags: u8) { - // for now we don't do anything here - self.write_att(src_handle, Data::new_att_execute_write_response()); + async fn write_att(&mut self, handle: u16, data: Data) { + log::debug!("src_handle {}", handle); + log::debug!("data {:x?}", data.as_slice()); + + let res = L2capPacket::encode(data); + log::trace!("encoded_l2cap {:x?}", res.as_slice()); + + let res = AclPacket::encode( + handle, + BoundaryFlag::FirstAutoFlushable, + HostBroadcastFlag::NoBroadcast, + res, + ); + log::trace!("writing {:x?}", res.as_slice()); + self.ble.write_bytes(res.as_slice()).await; + } } +} - fn handle_read_blob(&mut self, src_handle: u16, handle: u16, offset: u16) { - let mut data = Data::new_att_read_blob_response(); - let mut err = Err(AttErrorCode::AttributeNotFound); - - for att in self.attributes.iter_mut() { - if att.handle == handle { - if att.data.readable() { - err = att.data.read(offset as usize, data.as_slice_mut()); - if let Ok(len) = err { - data.append_len(len); - } - } - break; - } +impl<'a> AttributeServer<'a> { + pub fn new(ble: &'a mut Ble<'a>, attributes: &'a mut [Attribute<'a>]) -> AttributeServer<'a> { + for (i, attr) in attributes.iter_mut().enumerate() { + attr.handle = i as u16 + 1; } - let response = match err { - Ok(_) => { - data.limit_len(BASE_MTU as usize - 1); - data - } - Err(e) => Data::new_att_error_response(ATT_READ_BLOB_REQ_OPCODE, handle, e), - }; - - self.write_att(src_handle, response); - } + let mut last_in_group = attributes.last().unwrap().handle; + for i in (0..attributes.len()).rev() { + attributes[i].last_handle_in_group = last_in_group; - fn write_att(&mut self, handle: u16, data: Data) { - log::debug!("src_handle {}", handle); - log::debug!("data {:x?}", data.as_slice()); + if attributes[i].uuid == Uuid::Uuid16(0x2800) && i > 0 { + last_in_group = attributes[i - 1].handle; + } + } - let res = L2capPacket::encode(data); - log::trace!("encoded_l2cap {:x?}", res.as_slice()); + log::trace!("{:#x?}", &attributes); - let res = AclPacket::encode( - handle, - BoundaryFlag::FirstAutoFlushable, - HostBroadcastFlag::NoBroadcast, - res, - ); - log::trace!("writing {:x?}", res.as_slice()); - self.ble.write_bytes(res.as_slice()); + AttributeServer { + ble, + src_handle: 0, + attributes, + } } } diff --git a/example/Cargo.toml b/example/Cargo.toml index e0d23fc..83816d6 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -8,6 +8,6 @@ serialport = "4.2.0" critical-section = "1.1.1" embedded-io-adapters = { version = "0.6.1", features = ["std"] } embedded-io-blocking = { package = "embedded-io", version = "0.6.1" } -bleps = { path = "../bleps", features = ["macros"] } +bleps = { path = "../bleps", features = ["macros", "async"] } env_logger = "0.10.0" crossterm = "0.25.0"