trouble_host/
gatt.rs

1//! GATT server and client implementation.
2use core::cell::RefCell;
3use core::future::Future;
4use core::marker::PhantomData;
5
6use bt_hci::controller::Controller;
7use bt_hci::param::{ConnHandle, PhyKind, Status};
8use bt_hci::uuid::declarations::{CHARACTERISTIC, PRIMARY_SERVICE};
9use bt_hci::uuid::descriptors::CLIENT_CHARACTERISTIC_CONFIGURATION;
10use embassy_futures::select::{select, Either};
11use embassy_sync::blocking_mutex::raw::{NoopRawMutex, RawMutex};
12use embassy_sync::channel::{Channel, DynamicReceiver};
13use embassy_sync::pubsub::{self, PubSubChannel, WaitResult};
14use embassy_time::Duration;
15use heapless::Vec;
16
17use crate::att::{self, Att, AttClient, AttCmd, AttErrorCode, AttReq, AttRsp, AttServer, AttUns, ATT_HANDLE_VALUE_NTF};
18use crate::attribute::{AttributeData, Characteristic, CharacteristicProp, Uuid, CCCD};
19use crate::attribute_server::{AttributeServer, DynamicAttributeServer};
20use crate::connection::Connection;
21use crate::cursor::{ReadCursor, WriteCursor};
22use crate::pdu::Pdu;
23use crate::prelude::ConnectionEvent;
24#[cfg(feature = "security")]
25use crate::security_manager::BondInformation;
26use crate::types::gatt_traits::{AsGatt, FromGatt, FromGattError};
27use crate::types::l2cap::L2capHeader;
28use crate::{config, BleHostError, Error, PacketPool, Stack};
29
30/// A GATT connection event.
31pub enum GattConnectionEvent<'stack, 'server, P: PacketPool> {
32    /// Connection disconnected.
33    Disconnected {
34        /// The reason (status code) for the disconnect.
35        reason: Status,
36    },
37    /// The phy settings was updated for this connection.
38    PhyUpdated {
39        /// The TX phy.
40        tx_phy: PhyKind,
41        /// The RX phy.
42        rx_phy: PhyKind,
43    },
44    /// The phy settings was updated for this connection.
45    ConnectionParamsUpdated {
46        /// Connection interval.
47        conn_interval: Duration,
48        /// Peripheral latency.
49        peripheral_latency: u16,
50        /// Supervision timeout.
51        supervision_timeout: Duration,
52    },
53    #[cfg(feature = "security")]
54    /// Bonded event.
55    Bonded {
56        /// Bond info for this connection
57        bond_info: BondInformation,
58    },
59    /// GATT event.
60    Gatt {
61        /// The event that was returned
62        event: GattEvent<'stack, 'server, P>,
63    },
64}
65
66/// Used to manage a GATT connection with a client.
67pub struct GattConnection<'stack, 'server, P: PacketPool> {
68    connection: Connection<'stack, P>,
69    pub(crate) server: &'server dyn DynamicAttributeServer<P>,
70}
71
72impl<P: PacketPool> Drop for GattConnection<'_, '_, P> {
73    fn drop(&mut self) {
74        trace!("[gatt {}] disconnecting from server", self.connection.handle().raw());
75        self.server.disconnect(&self.connection);
76    }
77}
78
79impl<'stack, 'server, P: PacketPool> GattConnection<'stack, 'server, P> {
80    /// Creates a GATT connection from the given BLE connection and `AttributeServer`:
81    /// this will register the client within the server's CCCD table.
82    pub(crate) fn try_new<'values, M: RawMutex, const AT: usize, const CT: usize, const CN: usize>(
83        connection: Connection<'stack, P>,
84        server: &'server AttributeServer<'values, M, P, AT, CT, CN>,
85    ) -> Result<Self, Error> {
86        trace!("[gatt {}] connecting to server", connection.handle().raw());
87        server.connect(&connection)?;
88        Ok(Self { connection, server })
89    }
90
91    /// Wait for the next GATT connection event.
92    ///
93    /// Uses the attribute server to handle the protocol.
94    pub async fn next(&self) -> GattConnectionEvent<'stack, 'server, P> {
95        match select(self.connection.next(), self.connection.next_gatt()).await {
96            Either::First(event) => match event {
97                ConnectionEvent::Disconnected { reason } => GattConnectionEvent::Disconnected { reason },
98                ConnectionEvent::ConnectionParamsUpdated {
99                    conn_interval,
100                    peripheral_latency,
101                    supervision_timeout,
102                } => GattConnectionEvent::ConnectionParamsUpdated {
103                    conn_interval,
104                    peripheral_latency,
105                    supervision_timeout,
106                },
107                ConnectionEvent::PhyUpdated { tx_phy, rx_phy } => GattConnectionEvent::PhyUpdated { tx_phy, rx_phy },
108                #[cfg(feature = "security")]
109                ConnectionEvent::Bonded { bond_info } => {
110                    // Update the identity of the connection
111                    if let Err(e) = self.server.update_identity(bond_info.identity) {
112                        error!("Failed to update identity in att server: {:?}", e);
113                    }
114                    GattConnectionEvent::Bonded { bond_info }
115                }
116            },
117            Either::Second(data) => GattConnectionEvent::Gatt {
118                event: GattEvent::new(GattData::new(data, self.connection.clone()), self.server),
119            },
120        }
121    }
122
123    /// Get a reference to the underlying BLE connection.
124    pub fn raw(&self) -> &Connection<'stack, P> {
125        &self.connection
126    }
127}
128
129/// A GATT payload ready for processing.
130pub struct GattData<'stack, P: PacketPool> {
131    pdu: Option<Pdu<P::Packet>>,
132    connection: Connection<'stack, P>,
133}
134
135impl<'stack, P: PacketPool> GattData<'stack, P> {
136    pub(crate) const fn new(pdu: Pdu<P::Packet>, connection: Connection<'stack, P>) -> Self {
137        Self {
138            pdu: Some(pdu),
139            connection,
140        }
141    }
142
143    /// Return the characteristic handle that this GATT request is related to, if applicable.
144    ///
145    /// Returns `None` if the request is not related to a characteristic handle (e.g. a service discovery request).
146    pub fn handle(&self) -> Option<u16> {
147        match self.incoming() {
148            AttClient::Request(AttReq::Write { handle, .. }) => Some(handle),
149            AttClient::Command(AttCmd::Write { handle, .. }) => Some(handle),
150            AttClient::Request(AttReq::Read { handle }) => Some(handle),
151            AttClient::Request(AttReq::ReadBlob { handle, .. }) => Some(handle),
152            _ => None,
153        }
154    }
155
156    /// Get the raw incoming ATT PDU.
157    pub fn incoming(&self) -> AttClient<'_> {
158        // We know that:
159        // - The PDU is decodable, as it was already decoded once before adding it to the connection queue
160        // - The PDU is of type `Att::Client` because only those types of PDUs are added to the connection queue
161        let att = unwrap!(Att::decode(self.pdu.as_ref().unwrap().as_ref()));
162        let Att::Client(client) = att else {
163            unreachable!("Expected Att::Client, got {:?}", att)
164        };
165
166        client
167    }
168
169    /// Respond directly to request.
170    pub async fn reply(self, rsp: AttRsp<'_>) -> Result<(), Error> {
171        let pdu = send(&self.connection, AttServer::Response(rsp))?;
172        self.connection.send(pdu).await;
173        Ok(())
174    }
175
176    /// Send an unsolicited ATT PDU without having a request (e.g. notification or indication)
177    pub async fn send_unsolicited(connection: &Connection<'_, P>, uns: AttUns<'_>) -> Result<(), Error> {
178        let pdu = send(connection, AttServer::Unsolicited(uns))?;
179        connection.send(pdu).await;
180        Ok(())
181    }
182}
183
184/// An event returned while processing GATT requests.
185pub enum GattEvent<'stack, 'server, P: PacketPool> {
186    /// A characteristic was read.
187    Read(ReadEvent<'stack, 'server, P>),
188    /// A characteristic was written.
189    Write(WriteEvent<'stack, 'server, P>),
190    /// Other event.
191    Other(OtherEvent<'stack, 'server, P>),
192}
193
194impl<'stack, 'server, P: PacketPool> GattEvent<'stack, 'server, P> {
195    /// Create a new GATT event from the provided `GattData` and `DynamicAttributeServer`.
196    pub fn new(data: GattData<'stack, P>, server: &'server dyn DynamicAttributeServer<P>) -> Self {
197        let att = data.incoming();
198        match att {
199            AttClient::Request(AttReq::Write { .. }) | AttClient::Command(AttCmd::Write { .. }) => {
200                GattEvent::Write(WriteEvent { data, server })
201            }
202            AttClient::Request(AttReq::Read { .. }) | AttClient::Request(AttReq::ReadBlob { .. }) => {
203                GattEvent::Read(ReadEvent { data, server })
204            }
205            _ => GattEvent::Other(OtherEvent { data, server }),
206        }
207    }
208
209    /// Accept the event, making it processed by the server.
210    pub fn accept(self) -> Result<Reply<'stack, P>, Error> {
211        match self {
212            Self::Read(e) => e.accept(),
213            Self::Write(e) => e.accept(),
214            Self::Other(e) => e.accept(),
215        }
216    }
217
218    /// Reject the event with the provided error code, it will not be processed by the attribute server.
219    pub fn reject(self, err: AttErrorCode) -> Result<Reply<'stack, P>, Error> {
220        match self {
221            Self::Read(e) => e.reject(err),
222            Self::Write(e) => e.reject(err),
223            Self::Other(e) => e.reject(err),
224        }
225    }
226
227    /// Get a reference to the underlying `GattData` payload that this event is enclosing
228    pub fn payload(&self) -> &GattData<'stack, P> {
229        match self {
230            Self::Read(e) => e.payload(),
231            Self::Write(e) => e.payload(),
232            Self::Other(e) => e.payload(),
233        }
234    }
235
236    /// Convert the event back into the `GattData` payload it is enclosing
237    ///
238    /// Allows for custom processing of the enclosed data, as in handling payloads
239    /// which are not supported yet by the enclosed attribute server.
240    /// Note that this will consume the event, so it would be up to the caller to respond
241    /// to the incoming payload if needed and however they see fit.
242    pub fn into_payload(self) -> GattData<'stack, P> {
243        match self {
244            Self::Read(e) => e.into_payload(),
245            Self::Write(e) => e.into_payload(),
246            Self::Other(e) => e.into_payload(),
247        }
248    }
249}
250
251/// A characteristic read event returned while processing GATT requests.
252pub struct ReadEvent<'stack, 'server, P: PacketPool> {
253    data: GattData<'stack, P>,
254    server: &'server dyn DynamicAttributeServer<P>,
255}
256
257impl<'stack, P: PacketPool> ReadEvent<'stack, '_, P> {
258    /// Characteristic handle that was read
259    pub fn handle(&self) -> u16 {
260        // We know that the unwrap cannot fail, because `ReadEvent` wraps
261        // ATT payloads that always do have a handle
262        unwrap!(self.data.handle())
263    }
264
265    /// Accept the event, making it processed by the server.
266    ///
267    /// Automatically called if drop() is invoked.
268    pub fn accept(mut self) -> Result<Reply<'stack, P>, Error> {
269        process(&mut self.data, self.server, Ok(()))
270    }
271
272    /// Reject the event with the provided error code, it will not be processed by the attribute server.
273    pub fn reject(mut self, err: AttErrorCode) -> Result<Reply<'stack, P>, Error> {
274        process(&mut self.data, self.server, Err(err))
275    }
276
277    /// Get a reference to the underlying `GattData` payload that this event is enclosing
278    pub fn payload(&self) -> &GattData<'stack, P> {
279        &self.data
280    }
281
282    /// Convert the event back into the `GattData` payload it is enclosing
283    ///
284    /// Allows for custom processing of the enclosed data, as in handling payloads
285    /// which are not supported yet by the enclosed attribute server.
286    /// Note that this will consume the event, so it would be up to the caller to respond
287    /// to the incoming payload if needed and however they see fit.
288    pub fn into_payload(mut self) -> GattData<'stack, P> {
289        GattData {
290            pdu: self.data.pdu.take(),
291            connection: self.data.connection.clone(),
292        }
293    }
294}
295
296impl<P: PacketPool> Drop for ReadEvent<'_, '_, P> {
297    fn drop(&mut self) {
298        let _ = process(&mut self.data, self.server, Ok(()));
299    }
300}
301
302/// A characteristic write event returned while processing GATT requests.
303pub struct WriteEvent<'stack, 'server, P: PacketPool> {
304    data: GattData<'stack, P>,
305    server: &'server dyn DynamicAttributeServer<P>,
306}
307
308impl<'stack, P: PacketPool> WriteEvent<'stack, '_, P> {
309    /// Characteristic handle that was written
310    pub fn handle(&self) -> u16 {
311        // We know that the unwrap cannot fail, because `ReadEvent` wraps
312        // ATT payloads that always do have a handle
313        unwrap!(self.data.handle())
314    }
315
316    /// Raw data to be written
317    pub fn data(&self) -> &[u8] {
318        // Note: write event data is always at offset 3, right?
319        &self.data.pdu.as_ref().unwrap().as_ref()[3..]
320    }
321
322    /// Characteristic data to be written
323    pub fn value<T: FromGatt>(&self, _c: &Characteristic<T>) -> Result<T, FromGattError> {
324        T::from_gatt(self.data())
325    }
326
327    /// Accept the event, making it processed by the server.
328    ///
329    /// Automatically called if drop() is invoked.
330    pub fn accept(mut self) -> Result<Reply<'stack, P>, Error> {
331        process(&mut self.data, self.server, Ok(()))
332    }
333
334    /// Reject the event with the provided error code, it will not be processed by the attribute server.
335    pub fn reject(mut self, err: AttErrorCode) -> Result<Reply<'stack, P>, Error> {
336        process(&mut self.data, self.server, Err(err))
337    }
338
339    /// Get a reference to the underlying `GattData` payload that this event is enclosing
340    pub fn payload(&self) -> &GattData<'stack, P> {
341        &self.data
342    }
343
344    /// Convert the event back into the `GattData` payload it is enclosing
345    ///
346    /// Allows for custom processing of the enclosed data, as in handling payloads
347    /// which are not supported yet by the enclosed attribute server.
348    /// Note that this will consume the event, so it would be up to the caller to respond
349    /// to the incoming payload if needed and however they see fit.
350    pub fn into_payload(mut self) -> GattData<'stack, P> {
351        GattData {
352            pdu: self.data.pdu.take(),
353            connection: self.data.connection.clone(),
354        }
355    }
356}
357
358impl<P: PacketPool> Drop for WriteEvent<'_, '_, P> {
359    fn drop(&mut self) {
360        let _ = process(&mut self.data, self.server, Ok(()));
361    }
362}
363
364/// Other event returned while processing GATT requests (neither read, nor write).
365pub struct OtherEvent<'stack, 'server, P: PacketPool> {
366    data: GattData<'stack, P>,
367    server: &'server dyn DynamicAttributeServer<P>,
368}
369
370impl<'stack, P: PacketPool> OtherEvent<'stack, '_, P> {
371    /// Accept the event, making it processed by the server.
372    ///
373    /// Automatically called if drop() is invoked.
374    pub fn accept(mut self) -> Result<Reply<'stack, P>, Error> {
375        process(&mut self.data, self.server, Ok(()))
376    }
377
378    /// Reject the event with the provided error code, it will not be processed by the attribute server.
379    pub fn reject(mut self, err: AttErrorCode) -> Result<Reply<'stack, P>, Error> {
380        process(&mut self.data, self.server, Err(err))
381    }
382
383    /// Get a reference to the underlying `GattData` payload that this event is enclosing
384    pub fn payload(&self) -> &GattData<'stack, P> {
385        &self.data
386    }
387
388    /// Convert the event back into the `GattData` payload it is enclosing
389    ///
390    /// Allows for custom processing of the enclosed data, as in handling payloads
391    /// which are not supported yet by the enclosed attribute server.
392    /// Note that this will consume the event, so it would be up to the caller to respond
393    /// to the incoming payload if needed and however they see fit.
394    pub fn into_payload(mut self) -> GattData<'stack, P> {
395        GattData {
396            pdu: self.data.pdu.take(),
397            connection: self.data.connection.clone(),
398        }
399    }
400}
401
402impl<P: PacketPool> Drop for OtherEvent<'_, '_, P> {
403    fn drop(&mut self) {
404        let _ = process(&mut self.data, self.server, Ok(()));
405    }
406}
407
408fn process<'stack, P>(
409    data: &mut GattData<'stack, P>,
410    server: &dyn DynamicAttributeServer<P>,
411    result: Result<(), AttErrorCode>,
412) -> Result<Reply<'stack, P>, Error>
413where
414    P: PacketPool,
415{
416    if let Some(pdu) = data.pdu.take() {
417        let res = match result {
418            Ok(_) => process_accept(&pdu, &data.connection, server),
419            Err(code) => process_reject(&pdu, &data.connection, code),
420        };
421        res
422    } else {
423        Ok(Reply::new(data.connection.clone(), None))
424    }
425}
426
427fn process_accept<'stack, P>(
428    pdu: &Pdu<P::Packet>,
429    connection: &Connection<'stack, P>,
430    server: &dyn DynamicAttributeServer<P>,
431) -> Result<Reply<'stack, P>, Error>
432where
433    P: PacketPool,
434{
435    // - The PDU is decodable, as it was already decoded once before adding it to the connection queue
436    // - The PDU is of type `Att::Client` because only those types of PDUs are added to the connection queue
437    let att = unwrap!(Att::decode(pdu.as_ref()));
438    let Att::Client(att) = att else {
439        unreachable!("Expected Att::Client, got {:?}", att)
440    };
441    let mut tx = P::allocate().ok_or(Error::OutOfMemory)?;
442    let mut w = WriteCursor::new(tx.as_mut());
443    let (mut header, mut data) = w.split(4)?;
444    if let Some(written) = server.process(connection, &att, data.write_buf())? {
445        let mtu = connection.get_att_mtu();
446        data.commit(written)?;
447        data.truncate(mtu as usize);
448        header.write(data.len() as u16)?;
449        header.write(4_u16)?;
450        let len = header.len() + data.len();
451        let pdu = Pdu::new(tx, len);
452        Ok(Reply::new(connection.clone(), Some(pdu)))
453    } else {
454        Ok(Reply::new(connection.clone(), None))
455    }
456}
457
458fn process_reject<'stack, P: PacketPool>(
459    pdu: &Pdu<P::Packet>,
460    connection: &Connection<'stack, P>,
461    code: AttErrorCode,
462) -> Result<Reply<'stack, P>, Error> {
463    // - The PDU is decodable, as it was already decoded once before adding it to the connection queue
464    // - The PDU is of type `Att::Client` because only those types of PDUs are added to the connection queue
465    let att = unwrap!(Att::decode(pdu.as_ref()));
466    let Att::Client(att) = att else {
467        unreachable!("Expected Att::Client, got {:?}", att)
468    };
469    let handle = match att {
470        AttClient::Request(AttReq::Write { handle, .. }) => handle,
471        AttClient::Command(AttCmd::Write { handle, .. }) => handle,
472        AttClient::Request(AttReq::Read { handle }) => handle,
473        AttClient::Request(AttReq::ReadBlob { handle, .. }) => handle,
474        _ => 0, // As per spec, if the incoming ATT does not have an ATT handle, we should report with handle 0
475    };
476    // We know it has been checked, therefore this cannot fail
477    let request = pdu.as_ref()[0];
478    let rsp = AttRsp::Error { request, handle, code };
479    let pdu = send(connection, AttServer::Response(rsp))?;
480    Ok(Reply::new(connection.clone(), Some(pdu)))
481}
482
483fn send<'stack, P: PacketPool>(conn: &Connection<'stack, P>, att: AttServer<'_>) -> Result<Pdu<P::Packet>, Error> {
484    let mut tx = P::allocate().ok_or(Error::OutOfMemory)?;
485    let mut w = WriteCursor::new(tx.as_mut());
486    let (mut header, mut data) = w.split(4)?;
487    data.write(Att::Server(att))?;
488
489    let mtu = conn.get_att_mtu();
490    data.truncate(mtu as usize);
491    header.write(data.len() as u16)?;
492    header.write(4_u16)?;
493    let len = header.len() + data.len();
494    Ok(Pdu::new(tx, len))
495}
496
497/// A reply to a gatt request.
498///
499/// The reply may be sent immediately or queued for sending later. To guarantee delivery of a reply
500/// in case of a full outbound queue, the async send() should be used rather than relying on the Drop implementation.
501pub struct Reply<'stack, P: PacketPool> {
502    connection: Connection<'stack, P>,
503    pdu: Option<Pdu<P::Packet>>,
504}
505
506impl<'stack, P: PacketPool> Reply<'stack, P> {
507    fn new(connection: Connection<'stack, P>, pdu: Option<Pdu<P::Packet>>) -> Self {
508        Self { connection, pdu }
509    }
510
511    /// Send the reply.
512    ///
513    /// May fail if the outbound queue is full.
514    pub fn try_send(mut self) -> Result<(), Error> {
515        if let Some(pdu) = self.pdu.take() {
516            self.connection.try_send(pdu)
517        } else {
518            Ok(())
519        }
520    }
521
522    /// Send the reply.
523    pub async fn send(mut self) {
524        if let Some(pdu) = self.pdu.take() {
525            self.connection.send(pdu).await
526        }
527    }
528}
529
530impl<P: PacketPool> Drop for Reply<'_, P> {
531    fn drop(&mut self) {
532        if let Some(pdu) = self.pdu.take() {
533            if self.connection.try_send(pdu).is_err() {
534                warn!("[gatt] error sending reply (outbound buffer full)");
535            }
536        }
537    }
538}
539
540/// Notification listener for GATT client.
541pub struct NotificationListener<'lst, const MTU: usize> {
542    handle: u16,
543    listener: pubsub::DynSubscriber<'lst, Notification<MTU>>,
544}
545
546impl<'lst, const MTU: usize> NotificationListener<'lst, MTU> {
547    #[allow(clippy::should_implement_trait)]
548    /// Get the next (len: u16, Packet) tuple from the rx queue
549    pub async fn next(&mut self) -> Notification<MTU> {
550        loop {
551            if let WaitResult::Message(m) = self.listener.next_message().await {
552                if m.handle == self.handle {
553                    return m;
554                }
555            }
556        }
557    }
558}
559
560const MAX_NOTIF: usize = config::GATT_CLIENT_NOTIFICATION_MAX_SUBSCRIBERS;
561const NOTIF_QSIZE: usize = config::GATT_CLIENT_NOTIFICATION_QUEUE_SIZE;
562
563/// A GATT client capable of using the GATT protocol.
564pub struct GattClient<'reference, T: Controller, P: PacketPool, const MAX_SERVICES: usize> {
565    known_services: RefCell<Vec<ServiceHandle, MAX_SERVICES>>,
566    rx: DynamicReceiver<'reference, (ConnHandle, Pdu<P::Packet>)>,
567    stack: &'reference Stack<'reference, T, P>,
568    connection: Connection<'reference, P>,
569    response_channel: Channel<NoopRawMutex, (ConnHandle, Pdu<P::Packet>), 1>,
570
571    // TODO: Wait for something like https://github.com/rust-lang/rust/issues/132980 (min_generic_const_args) to allow using P::MTU
572    notifications: PubSubChannel<NoopRawMutex, Notification<512>, NOTIF_QSIZE, MAX_NOTIF, 1>,
573}
574
575/// A notification payload.
576#[derive(Debug, PartialEq, Clone)]
577#[cfg_attr(feature = "defmt", derive(defmt::Format))]
578pub struct Notification<const MTU: usize> {
579    handle: u16,
580    data: [u8; MTU],
581    len: usize,
582}
583
584impl<const MTU: usize> AsRef<[u8]> for Notification<MTU> {
585    fn as_ref(&self) -> &[u8] {
586        &self.data[..self.len]
587    }
588}
589
590/// Handle for a GATT service.
591#[cfg_attr(feature = "defmt", derive(defmt::Format))]
592#[derive(Debug, PartialEq, Clone)]
593pub struct ServiceHandle {
594    start: u16,
595    end: u16,
596    uuid: Uuid,
597}
598
599pub(crate) struct Response<P> {
600    pdu: Pdu<P>,
601    handle: ConnHandle,
602}
603
604/// Trait with behavior for a gatt client.
605pub(crate) trait Client<'d, E, P: PacketPool> {
606    /// Perform a gatt request and return the response.
607    fn request(&self, req: AttReq<'_>) -> impl Future<Output = Result<Response<P::Packet>, BleHostError<E>>>;
608    fn command(&self, cmd: AttCmd<'_>) -> impl Future<Output = Result<(), BleHostError<E>>>;
609}
610
611impl<'reference, T: Controller, P: PacketPool, const MAX_SERVICES: usize> Client<'reference, T::Error, P>
612    for GattClient<'reference, T, P, MAX_SERVICES>
613{
614    async fn request(&self, req: AttReq<'_>) -> Result<Response<P::Packet>, BleHostError<T::Error>> {
615        let data = Att::Client(AttClient::Request(req));
616
617        self.send_att_data(data).await?;
618
619        let (h, pdu) = self.response_channel.receive().await;
620
621        assert_eq!(h, self.connection.handle());
622        Ok(Response { handle: h, pdu })
623    }
624
625    async fn command(&self, cmd: AttCmd<'_>) -> Result<(), BleHostError<T::Error>> {
626        let data = Att::Client(AttClient::Command(cmd));
627
628        self.send_att_data(data).await?;
629
630        Ok(())
631    }
632}
633
634impl<'reference, T: Controller, P: PacketPool, const MAX_SERVICES: usize> GattClient<'reference, T, P, MAX_SERVICES> {
635    async fn send_att_data(&self, data: Att<'_>) -> Result<(), BleHostError<T::Error>> {
636        let header = L2capHeader {
637            channel: crate::types::l2cap::L2CAP_CID_ATT,
638            length: data.size() as u16,
639        };
640
641        let mut buf = P::allocate().ok_or(Error::OutOfMemory)?;
642        let mut w = WriteCursor::new(buf.as_mut());
643        w.write_hci(&header)?;
644        w.write(data)?;
645        let len = w.len();
646
647        self.connection.send(Pdu::new(buf, len)).await;
648        Ok(())
649    }
650}
651
652impl<'reference, C: Controller, P: PacketPool, const MAX_SERVICES: usize> GattClient<'reference, C, P, MAX_SERVICES> {
653    /// Creates a GATT client capable of processing the GATT protocol using the provided table of attributes.
654    pub async fn new(
655        stack: &'reference Stack<'reference, C, P>,
656        connection: &Connection<'reference, P>,
657    ) -> Result<GattClient<'reference, C, P, MAX_SERVICES>, BleHostError<C::Error>> {
658        let l2cap = L2capHeader { channel: 4, length: 3 };
659        let mut buf = P::allocate().ok_or(Error::OutOfMemory)?;
660        let mut w = WriteCursor::new(buf.as_mut());
661        w.write_hci(&l2cap)?;
662        w.write(att::Att::Client(att::AttClient::Request(att::AttReq::ExchangeMtu {
663            mtu: P::MTU as u16 - 4,
664        })))?;
665
666        let len = w.len();
667        connection.send(Pdu::new(buf, len)).await;
668        Ok(Self {
669            known_services: RefCell::new(heapless::Vec::new()),
670            rx: stack.host.att_client.receiver().into(),
671            stack,
672            connection: connection.clone(),
673
674            response_channel: Channel::new(),
675
676            notifications: PubSubChannel::new(),
677        })
678    }
679
680    /// Discover primary services associated with a UUID.
681    pub async fn services_by_uuid(
682        &self,
683        uuid: &Uuid,
684    ) -> Result<Vec<ServiceHandle, MAX_SERVICES>, BleHostError<C::Error>> {
685        let mut start: u16 = 0x0001;
686        let mut result = Vec::new();
687
688        loop {
689            let data = att::AttReq::FindByTypeValue {
690                start_handle: start,
691                end_handle: 0xffff,
692                att_type: PRIMARY_SERVICE.into(),
693                att_value: uuid.as_raw(),
694            };
695
696            let response = self.request(data).await?;
697            let res = Self::response(response.pdu.as_ref())?;
698            match res {
699                AttRsp::Error { request, handle, code } => {
700                    if code == att::AttErrorCode::ATTRIBUTE_NOT_FOUND {
701                        break;
702                    }
703                    return Err(Error::Att(code).into());
704                }
705                AttRsp::FindByTypeValue { mut it } => {
706                    let mut end: u16 = 0;
707                    while let Some(res) = it.next() {
708                        let (handle, e) = res?;
709                        end = e;
710                        let svc = ServiceHandle {
711                            start: handle,
712                            end,
713                            uuid: uuid.clone(),
714                        };
715                        result.push(svc.clone()).map_err(|_| Error::InsufficientSpace)?;
716                        self.known_services
717                            .borrow_mut()
718                            .push(svc)
719                            .map_err(|_| Error::InsufficientSpace)?;
720                    }
721                    if end == 0xFFFF {
722                        break;
723                    }
724                    start = end + 1;
725                }
726                res => {
727                    trace!("[gatt client] response: {:?}", res);
728                    return Err(Error::UnexpectedGattResponse.into());
729                }
730            }
731        }
732
733        Ok(result)
734    }
735
736    /// Discover characteristics in a given service using a UUID.
737    pub async fn characteristic_by_uuid<T: AsGatt>(
738        &self,
739        service: &ServiceHandle,
740        uuid: &Uuid,
741    ) -> Result<Characteristic<T>, BleHostError<C::Error>> {
742        let mut start: u16 = service.start;
743        loop {
744            let data = att::AttReq::ReadByType {
745                start,
746                end: service.end,
747                attribute_type: CHARACTERISTIC.into(),
748            };
749            let response = self.request(data).await?;
750
751            match Self::response(response.pdu.as_ref())? {
752                AttRsp::ReadByType { mut it } => {
753                    while let Some(Ok((handle, item))) = it.next() {
754                        let expected_items_len = 5;
755                        let item_len = item.len();
756
757                        if item_len < expected_items_len {
758                            return Err(Error::MalformedCharacteristicDeclaration {
759                                expected: expected_items_len,
760                                actual: item_len,
761                            }
762                            .into());
763                        }
764                        if let AttributeData::Declaration {
765                            props,
766                            handle,
767                            uuid: decl_uuid,
768                        } = AttributeData::decode_declaration(item)?
769                        {
770                            if *uuid == decl_uuid {
771                                // "notify" and "indicate" characteristic properties
772                                let cccd_handle =
773                                    if props.any(&[CharacteristicProp::Indicate, CharacteristicProp::Notify]) {
774                                        Some(self.get_characteristic_cccd(handle).await?.0)
775                                    } else {
776                                        None
777                                    };
778
779                                return Ok(Characteristic {
780                                    handle,
781                                    cccd_handle,
782                                    phantom: PhantomData,
783                                });
784                            }
785
786                            if handle == 0xFFFF {
787                                return Err(Error::NotFound.into());
788                            }
789                            start = handle + 1;
790                        } else {
791                            return Err(Error::InvalidCharacteristicDeclarationData.into());
792                        }
793                    }
794                }
795                AttRsp::Error { request, handle, code } => return Err(Error::Att(code).into()),
796                _ => {
797                    return Err(Error::UnexpectedGattResponse.into());
798                }
799            }
800        }
801    }
802
803    async fn get_characteristic_cccd(&self, char_handle: u16) -> Result<(u16, CCCD), BleHostError<C::Error>> {
804        let data = att::AttReq::ReadByType {
805            start: char_handle,
806            end: char_handle + 1,
807            attribute_type: CLIENT_CHARACTERISTIC_CONFIGURATION.into(),
808        };
809
810        let response = self.request(data).await?;
811
812        match Self::response(response.pdu.as_ref())? {
813            AttRsp::ReadByType { mut it } => {
814                if let Some(Ok((handle, item))) = it.next() {
815                    // As defined in the bluetooth spec [3.3.3.3. Client Characteristic Configuration](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host/generic-attribute-profile--gatt-.html#UUID-09487be3-178b-eeca-f49f-f783e8d462f6)
816                    // "The Client Characteristic Configuration declaration is an optional characteristic descriptor" and
817                    // "The default value for the Client Characteristic Configuration descriptor value shall be 0x0000."
818                    if item.is_empty() {
819                        Ok((handle, CCCD(0)))
820                    } else {
821                        Ok((
822                            handle,
823                            CCCD(u16::from_le_bytes(
824                                item.try_into()
825                                    .map_err(|_| Error::InvalidCccdHandleLength(item.len()))?,
826                            )),
827                        ))
828                    }
829                } else {
830                    Err(Error::NotFound.into())
831                }
832            }
833            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
834            _ => Err(Error::UnexpectedGattResponse.into()),
835        }
836    }
837
838    /// Read a characteristic described by a handle.
839    ///
840    /// The number of bytes copied into the provided buffer is returned.
841    pub async fn read_characteristic<T: AsGatt>(
842        &self,
843        characteristic: &Characteristic<T>,
844        dest: &mut [u8],
845    ) -> Result<usize, BleHostError<C::Error>> {
846        let data = att::AttReq::Read {
847            handle: characteristic.handle,
848        };
849
850        let response = self.request(data).await?;
851
852        match Self::response(response.pdu.as_ref())? {
853            AttRsp::Read { data } => {
854                let to_copy = data.len().min(dest.len());
855                dest[..to_copy].copy_from_slice(&data[..to_copy]);
856                Ok(to_copy)
857            }
858            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
859            _ => Err(Error::UnexpectedGattResponse.into()),
860        }
861    }
862
863    /// Read a characteristic described by a UUID.
864    ///
865    /// The number of bytes copied into the provided buffer is returned.
866    pub async fn read_characteristic_by_uuid(
867        &self,
868        service: &ServiceHandle,
869        uuid: &Uuid,
870        dest: &mut [u8],
871    ) -> Result<usize, BleHostError<C::Error>> {
872        let data = att::AttReq::ReadByType {
873            start: service.start,
874            end: service.end,
875            attribute_type: uuid.clone(),
876        };
877
878        let response = self.request(data).await?;
879
880        match Self::response(response.pdu.as_ref())? {
881            AttRsp::ReadByType { mut it } => {
882                let mut to_copy = 0;
883                if let Some(item) = it.next() {
884                    let (_handle, data) = item?;
885                    to_copy = data.len().min(dest.len());
886                    dest[..to_copy].copy_from_slice(&data[..to_copy]);
887                }
888                Ok(to_copy)
889            }
890            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
891            _ => Err(Error::UnexpectedGattResponse.into()),
892        }
893    }
894
895    /// Write to a characteristic described by a handle.
896    pub async fn write_characteristic<T: FromGatt>(
897        &self,
898        handle: &Characteristic<T>,
899        buf: &[u8],
900    ) -> Result<(), BleHostError<C::Error>> {
901        let data = att::AttReq::Write {
902            handle: handle.handle,
903            data: buf,
904        };
905
906        let response = self.request(data).await?;
907        match Self::response(response.pdu.as_ref())? {
908            AttRsp::Write => Ok(()),
909            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
910            _ => Err(Error::UnexpectedGattResponse.into()),
911        }
912    }
913
914    /// Write without waiting for a response to a characteristic described by a handle.
915    pub async fn write_characteristic_without_response<T: FromGatt>(
916        &self,
917        handle: &Characteristic<T>,
918        buf: &[u8],
919    ) -> Result<(), BleHostError<C::Error>> {
920        let data = att::AttCmd::Write {
921            handle: handle.handle,
922            data: buf,
923        };
924
925        self.command(data).await?;
926
927        Ok(())
928    }
929
930    /// Subscribe to indication/notification of a given Characteristic
931    ///
932    /// A listener is returned, which has a `next()` method
933    pub async fn subscribe<T: AsGatt>(
934        &self,
935        characteristic: &Characteristic<T>,
936        indication: bool,
937    ) -> Result<NotificationListener<'_, 512>, BleHostError<C::Error>> {
938        let properties = u16::to_le_bytes(if indication { 0x02 } else { 0x01 });
939
940        let data = att::AttReq::Write {
941            handle: characteristic.cccd_handle.ok_or(Error::NotSupported)?,
942            data: &properties,
943        };
944
945        // set the CCCD
946        let response = self.request(data).await?;
947
948        match Self::response(response.pdu.as_ref())? {
949            AttRsp::Write => match self.notifications.dyn_subscriber() {
950                Ok(listener) => Ok(NotificationListener {
951                    listener,
952                    handle: characteristic.handle,
953                }),
954                Err(embassy_sync::pubsub::Error::MaximumSubscribersReached) => {
955                    Err(Error::GattSubscriberLimitReached.into())
956                }
957                Err(_) => Err(Error::Other.into()),
958            },
959            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
960            _ => Err(Error::UnexpectedGattResponse.into()),
961        }
962    }
963
964    /// Unsubscribe from a given Characteristic
965    pub async fn unsubscribe<T: AsGatt>(
966        &self,
967        characteristic: &Characteristic<T>,
968    ) -> Result<(), BleHostError<C::Error>> {
969        let properties = u16::to_le_bytes(0);
970        let data = att::AttReq::Write {
971            handle: characteristic.cccd_handle.ok_or(Error::NotSupported)?,
972            data: &[0, 0],
973        };
974
975        // set the CCCD
976        let response = self.request(data).await?;
977
978        match Self::response(response.pdu.as_ref())? {
979            AttRsp::Write => Ok(()),
980            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
981            _ => Err(Error::UnexpectedGattResponse.into()),
982        }
983    }
984
985    /// Handle a notification that was received.
986    async fn handle_notification_packet(&self, data: &[u8]) -> Result<(), BleHostError<C::Error>> {
987        let mut r = ReadCursor::new(data);
988        let value_handle: u16 = r.read()?;
989        let value_attr = r.remaining();
990
991        let handle = value_handle;
992
993        // TODO
994        let mut data = [0u8; 512];
995        let to_copy = data.len().min(value_attr.len());
996        data[..to_copy].copy_from_slice(&value_attr[..to_copy]);
997        let n = Notification {
998            handle,
999            data,
1000            len: to_copy,
1001        };
1002        self.notifications.immediate_publisher().publish_immediate(n);
1003        Ok(())
1004    }
1005
1006    /// Task which handles GATT rx data (needed for notifications to work)
1007    pub async fn task(&self) -> Result<(), BleHostError<C::Error>> {
1008        loop {
1009            let (handle, pdu) = self.rx.receive().await;
1010            let data = pdu.as_ref();
1011            // handle notifications
1012            if pdu.as_ref()[0] == ATT_HANDLE_VALUE_NTF {
1013                self.handle_notification_packet(&pdu.as_ref()[1..]).await?;
1014            } else {
1015                self.response_channel.send((handle, pdu)).await;
1016            }
1017        }
1018    }
1019
1020    fn response<'a>(data: &'a [u8]) -> Result<AttRsp<'a>, BleHostError<C::Error>> {
1021        let att = Att::decode(data)?;
1022        match att {
1023            Att::Server(AttServer::Response(rsp)) => Ok(rsp),
1024            _ => Err(Error::UnexpectedGattResponse.into()),
1025        }
1026    }
1027}