From b829df0afe5512e7980b57ef2930536ae1155949 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Wed, 9 Oct 2024 08:16:54 +0000 Subject: [PATCH] Introduce a separate GattHandler trait type that is connection-aware --- host/src/gatt.rs | 100 +++++++++++++++++++++++++++++++++++++++-- host/src/gatt/split.rs | 19 ++++---- 2 files changed, 108 insertions(+), 11 deletions(-) diff --git a/host/src/gatt.rs b/host/src/gatt.rs index 07ab9d2d..7aa686c3 100644 --- a/host/src/gatt.rs +++ b/host/src/gatt.rs @@ -10,7 +10,7 @@ use embassy_sync::pubsub::{self, PubSubChannel, WaitResult}; use heapless::Vec; use split::{ExchangeArea, GattEvents, GattNotifier, GattRunner}; -use crate::att::{self, AttReq, AttRsp, ATT_HANDLE_VALUE_NTF}; +use crate::att::{self, AttErrorCode, AttReq, AttRsp, ATT_HANDLE_VALUE_NTF}; use crate::attribute::{ AttributeData, AttributeTable, Characteristic, CharacteristicProp, Uuid, CCCD, CHARACTERISTIC_CCCD_UUID16, CHARACTERISTIC_UUID16, PRIMARY_SERVICE_UUID16, @@ -25,6 +25,95 @@ use crate::{config, BleHostError, Error, Stack}; pub mod split; +/// A descriptor for an attribute handling by a `GattHandler` +pub struct GattAttrDesc<'a> { + /// The connection on behalf of which this attribute handling is coming + pub connection: &'a Connection<'a>, + /// The attribute UUID + pub uuid: &'a Uuid, + /// The attribute handle + /// TODO: Do we even want to expose handles in the user-facing API? + /// This is a general problem we have to decide on, i.e. shall we treat the handles as an internal + /// detail of the server or not? If not, we need to expose the connection handle as well, + /// either in addition to or instead of the `Connection` thing, which is giving us lifetime troubles + pub handle: u16, +} + +/// A callback trait invoked by the Gatt server on various operations +// TODO: As often happens with handlers, the error to be returned from `read`/`write` and potential future +// methods is a bit unclear. What should it be? +pub trait GattHandler { + /// Read data for an attribute + /// + /// # Arguments + /// - `attr`: The attribute descriptor + /// - `offset`: The offset to read from + /// - `data`: The buffer to write the data to + /// + /// Return the number of bytes read + async fn read(&mut self, attr: &GattAttrDesc<'_>, offset: usize, data: &mut [u8]) -> Result; + + /// Write data to an attribute + /// + /// # Arguments + /// - `attr`: The attribute descriptor + /// - `offset`: The offset to write to + /// - `data`: The data to write + async fn write(&mut self, attr: &GattAttrDesc<'_>, offset: usize, data: &[u8]) -> Result<(), AttErrorCode>; +} + +impl GattHandler for &mut T +where + T: GattHandler, +{ + async fn read(&mut self, attr: &GattAttrDesc<'_>, offset: usize, data: &mut [u8]) -> Result { + (**self).read(attr, offset, data).await + } + + async fn write(&mut self, attr: &GattAttrDesc<'_>, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> { + (**self).write(attr, offset, data).await + } +} + +// A type adapting the `GattHandler` trait to the `AttrHandler` trait +struct HandlerAdaptor<'a, T> { + handler: T, + connection: &'a Connection<'a>, +} + +impl<'a, T> AttrHandler for HandlerAdaptor<'a, T> +where + T: GattHandler, +{ + async fn read(&mut self, uuid: &Uuid, handle: u16, offset: usize, data: &mut [u8]) -> Result { + self.handler + .read( + &GattAttrDesc { + connection: self.connection, + uuid, + handle, + }, + offset, + data, + ) + .await + } + + async fn write(&mut self, uuid: &Uuid, handle: u16, offset: usize, data: &[u8]) -> Result<(), att::AttErrorCode> { + self.handler + .write( + &GattAttrDesc { + connection: self.connection, + uuid, + handle, + }, + offset, + data, + ) + .await + } +} + /// A GATT server capable of processing the GATT protocol using the provided table of attributes. pub struct GattServer<'reference, C: Controller, M: RawMutex, const MAX: usize, const L2CAP_MTU: usize> { stack: Stack<'reference, C>, @@ -76,7 +165,7 @@ impl<'reference, C: Controller, M: RawMutex, const MAX: usize, const L2CAP_MTU: /// read or write the actual attribute data. pub async fn process(&self, mut handler: T) -> Result<(), Error> where - T: AttrHandler, + T: GattHandler, { loop { let (handle, pdu) = self.rx.receive().await; @@ -87,7 +176,12 @@ impl<'reference, C: Controller, M: RawMutex, const MAX: usize, const L2CAP_MTU: let mut w = WriteCursor::new(&mut tx); let (mut header, mut data) = w.split(4)?; - match self.server.process(handle, &att, data.write_buf(), &mut handler).await { + let adaptor = HandlerAdaptor { + handler: &mut handler, + connection: &connection, + }; + + match self.server.process(handle, &att, data.write_buf(), adaptor).await { Ok(Some(written)) => { let mtu = self.connections.get_att_mtu(handle); data.commit(written)?; diff --git a/host/src/gatt/split.rs b/host/src/gatt/split.rs index 70a525d2..c17e1d08 100644 --- a/host/src/gatt/split.rs +++ b/host/src/gatt/split.rs @@ -7,12 +7,11 @@ use embassy_sync::blocking_mutex::{self, raw::RawMutex}; use embassy_sync::signal::Signal; use crate::att::AttErrorCode; -use crate::attribute::{Characteristic, Uuid}; -use crate::attribute_server::AttrHandler; +use crate::attribute::Characteristic; use crate::connection::Connection; use crate::{BleHostError, Error}; -use super::GattServer; +use super::{GattAttrDesc, GattHandler, GattServer}; /// Represents a GATT attribute read request that needs to be replied with the attribute data. pub struct GattReadRequest<'a, M: RawMutex, const L2CAP_MTU: usize>(&'a ExchangeArea); @@ -184,10 +183,13 @@ impl ExchangeArea { } } -impl AttrHandler for &ExchangeArea { - async fn read(&mut self, _uuid: &Uuid, handle: u16, offset: usize, data: &mut [u8]) -> Result { +impl GattHandler for &ExchangeArea { + async fn read(&mut self, attr: &GattAttrDesc<'_>, offset: usize, data: &mut [u8]) -> Result { self.request.signal(Request::Read { - handle, + // NOTE: We are a bit struggling with connections here as they are lifetimed + // Perhaps we should use a connection handle instead of a reference to a connection + // and then somehow restore the `Connection` ref from the handle when the `GattEvent` is created + handle: attr.handle, offset: offset as u16, }); @@ -204,7 +206,7 @@ impl AttrHandler for &ExchangeArea Result<(), AttErrorCode> { + async fn write(&mut self, attr: &GattAttrDesc<'_>, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> { self.buf.lock(|buf| { let mut buf = buf.borrow_mut(); @@ -213,7 +215,8 @@ impl AttrHandler for &ExchangeArea