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