| // Copyright 2023 Google LLC |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use crate::types::*; |
| |
| use bt_common::{PeerId, Uuid}; |
| use futures::{Future, Stream}; |
| |
| pub enum ServiceKind { |
| Primary, |
| Secondary, |
| } |
| |
| // A short definition of a service that is either being published or has been discovered on a |
| // peer. |
| pub struct PeerServiceDefinition { |
| /// Service Handle |
| /// When publishing services, unique among all services published with [`Server::publish`]. |
| pub id: u64, |
| /// Whether the service is marked as Primary in the GATT server. |
| pub kind: ServiceKind, |
| /// The UUID identifying the type of service that this is. |
| pub uuid: Uuid, |
| } |
| |
| /// GATT Client connected to a particular peer. |
| /// Holding a struct that implements this should attempt to maintain a LE connection to the peer. |
| pub trait Client { |
| type PeerServiceHandleT: PeerServiceHandle; |
| type ServiceResultFut: Future<Output = Result<Vec<Self::PeerServiceHandleT>>> + 'static; |
| |
| /// The ID of the peer this is connected to. |
| fn peer_id(&self) -> PeerId; |
| |
| /// Find services by UUID on the peer. |
| /// This may cause as much as a full discovery of all services on the peer if the stack deems it |
| /// appropriate. |
| /// Service information should be up to date at the time returned. |
| fn find_service(&self, uuid: Uuid) -> Self::ServiceResultFut; |
| } |
| |
| pub trait PeerServiceHandle { |
| type PeerServiceT: PeerService; |
| type ConnectFut: Future<Output = Result<Self::PeerServiceT>>; |
| |
| fn uuid(&self) -> Uuid; |
| fn is_primary(&self) -> bool; |
| fn connect(&self) -> Self::ConnectFut; |
| } |
| |
| #[derive(Debug)] |
| pub struct CharacteristicNotification { |
| pub handle: Handle, |
| pub value: Vec<u8>, |
| pub maybe_truncated: bool, |
| } |
| |
| /// A connection to a GATT Service on a Peer. |
| /// All operations are done synchronously. |
| pub trait PeerService { |
| type CharacteristicsFut: Future<Output = Result<Vec<Characteristic>>>; |
| type NotificationStream: Stream<Item = Result<CharacteristicNotification>>; |
| type ReadFut<'a>: Future<Output = Result<(usize, bool)>> + 'a; |
| type WriteFut<'a>: Future<Output = Result<()>> + 'a; |
| |
| /// Discover characteristics on this service. |
| /// If `uuid` is provided, only the characteristics matching `uuid` will be returned. |
| /// This operation may use either the Discovery All Characteristics of a Service or |
| /// Discovery Characteristic by UUID procedures, regardless of `uuid`. |
| fn discover_characteristics(&self, uuid: Option<Uuid>) -> Self::CharacteristicsFut; |
| |
| /// Read a characteristic into a buffer, given the handle within the service. |
| /// On success, returns the size read and whether the value may have been truncated. |
| /// By default this will try to use a long read if the `buf` is larger than a normal read will |
| /// allow (22 bytes) or if the offset is non-zero. |
| fn read_characteristic<'a>( |
| &self, |
| handle: &Handle, |
| offset: u16, |
| buf: &'a mut [u8], |
| ) -> Self::ReadFut<'a>; |
| |
| fn write_characteristic<'a>( |
| &self, |
| handle: &Handle, |
| mode: WriteMode, |
| offset: u16, |
| buf: &'a [u8], |
| ) -> Self::WriteFut<'a>; |
| |
| fn read_descriptor<'a>( |
| &self, |
| handle: &Handle, |
| offset: u16, |
| buf: &'a mut [u8], |
| ) -> Self::ReadFut<'a>; |
| |
| fn write_descriptor<'a>( |
| &self, |
| handle: &Handle, |
| offset: u16, |
| buf: &'a [u8], |
| ) -> Self::WriteFut<'a>; |
| |
| /// Subscribe to updates on a Characteristic. |
| /// Either notifications or indications will be enabled depending on the properties available, |
| /// with indications preferred if they are supported. |
| /// Fails if the Characteristic doesn't support indications or notifications. |
| /// Errors are delivered through an Err item in the stream. |
| /// This will often write to the Client Characteristic Configuration descriptor for the |
| /// Characteristic subscribed to. |
| /// Updates sent from the peer wlil be delivered to the Stream returned. |
| fn subscribe(&self, handle: &Handle) -> Self::NotificationStream; |
| } |
| |
| /// Convenience class for communicating with characteristics on a remote peer. |
| pub struct ServiceCharacteristic<'a, PeerServiceT> { |
| service: &'a PeerServiceT, |
| characteristic: Characteristic, |
| uuid: Uuid, |
| } |
| |
| impl<'a, PeerServiceT: PeerService> ServiceCharacteristic<'a, PeerServiceT> { |
| pub async fn find( |
| service: &'a PeerServiceT, |
| uuid: Uuid, |
| ) -> Result<Vec<ServiceCharacteristic<'a, PeerServiceT>>> { |
| let chrs = service.discover_characteristics(Some(uuid)).await?; |
| Ok(chrs |
| .into_iter() |
| .map(|characteristic| Self { |
| service, |
| characteristic, |
| uuid, |
| }) |
| .collect()) |
| } |
| } |
| |
| impl<'a, PeerServiceT> ServiceCharacteristic<'a, PeerServiceT> { |
| pub fn uuid(&self) -> Uuid { |
| self.uuid |
| } |
| |
| pub fn handle(&self) -> &Handle { |
| &self.characteristic.handle |
| } |
| |
| pub fn characteristic(&self) -> &Characteristic { |
| &self.characteristic |
| } |
| } |
| |
| impl<'a, PeerServiceT: PeerService> ServiceCharacteristic<'a, PeerServiceT> { |
| pub async fn read(&self, buf: &mut [u8]) -> Result<usize> { |
| self.service |
| .read_characteristic(self.handle(), 0, buf) |
| .await |
| .map(|(bytes, _)| bytes) |
| } |
| } |