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};
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 = send(&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 = send(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 = send(connection, AttServer::Response(rsp))?;
567    Ok(Reply::new(connection.clone(), Some(pdu)))
568}
569
570fn send<'stack, P: PacketPool>(conn: &Connection<'stack, P>, att: AttServer<'_>) -> Result<Pdu<P::Packet>, Error> {
571    let mut tx = P::allocate().ok_or(Error::OutOfMemory)?;
572    let mut w = WriteCursor::new(tx.as_mut());
573    let (mut header, mut data) = w.split(4)?;
574    data.write(Att::Server(att))?;
575
576    let mtu = conn.get_att_mtu();
577    data.truncate(mtu as usize);
578    header.write(data.len() as u16)?;
579    header.write(4_u16)?;
580    let len = header.len() + data.len();
581    Ok(Pdu::new(tx, len))
582}
583
584/// A reply to a gatt request.
585///
586/// The reply may be sent immediately or queued for sending later. To guarantee delivery of a reply
587/// in case of a full outbound queue, the async send() should be used rather than relying on the Drop implementation.
588pub struct Reply<'stack, P: PacketPool> {
589    connection: Connection<'stack, P>,
590    pdu: Option<Pdu<P::Packet>>,
591}
592
593impl<'stack, P: PacketPool> Reply<'stack, P> {
594    fn new(connection: Connection<'stack, P>, pdu: Option<Pdu<P::Packet>>) -> Self {
595        Self { connection, pdu }
596    }
597
598    /// Send the reply.
599    ///
600    /// May fail if the outbound queue is full.
601    pub fn try_send(mut self) -> Result<(), Error> {
602        if let Some(pdu) = self.pdu.take() {
603            self.connection.try_send(pdu)
604        } else {
605            Ok(())
606        }
607    }
608
609    /// Send the reply.
610    pub async fn send(mut self) {
611        if let Some(pdu) = self.pdu.take() {
612            self.connection.send(pdu).await
613        }
614    }
615}
616
617impl<P: PacketPool> Drop for Reply<'_, P> {
618    fn drop(&mut self) {
619        if let Some(pdu) = self.pdu.take() {
620            if self.connection.try_send(pdu).is_err() {
621                warn!("[gatt] error sending reply (outbound buffer full)");
622            }
623        }
624    }
625}
626
627/// Notification listener for GATT client.
628pub struct NotificationListener<'lst, const MTU: usize> {
629    handle: u16,
630    listener: pubsub::DynSubscriber<'lst, Notification<MTU>>,
631}
632
633impl<'lst, const MTU: usize> NotificationListener<'lst, MTU> {
634    #[allow(clippy::should_implement_trait)]
635    /// Get the next (len: u16, Packet) tuple from the rx queue
636    pub async fn next(&mut self) -> Notification<MTU> {
637        loop {
638            if let WaitResult::Message(m) = self.listener.next_message().await {
639                if m.handle == self.handle {
640                    return m;
641                }
642            }
643        }
644    }
645}
646
647const MAX_NOTIF: usize = config::GATT_CLIENT_NOTIFICATION_MAX_SUBSCRIBERS;
648const NOTIF_QSIZE: usize = config::GATT_CLIENT_NOTIFICATION_QUEUE_SIZE;
649
650/// A GATT client capable of using the GATT protocol.
651pub struct GattClient<'reference, T: Controller, P: PacketPool, const MAX_SERVICES: usize> {
652    known_services: RefCell<Vec<ServiceHandle, MAX_SERVICES>>,
653    rx: DynamicReceiver<'reference, (ConnHandle, Pdu<P::Packet>)>,
654    stack: &'reference Stack<'reference, T, P>,
655    connection: Connection<'reference, P>,
656    response_channel: Channel<NoopRawMutex, (ConnHandle, Pdu<P::Packet>), 1>,
657
658    // TODO: Wait for something like https://github.com/rust-lang/rust/issues/132980 (min_generic_const_args) to allow using P::MTU
659    notifications: PubSubChannel<NoopRawMutex, Notification<512>, NOTIF_QSIZE, MAX_NOTIF, 1>,
660}
661
662/// A notification payload.
663#[derive(Debug, PartialEq, Clone)]
664#[cfg_attr(feature = "defmt", derive(defmt::Format))]
665pub struct Notification<const MTU: usize> {
666    handle: u16,
667    data: [u8; MTU],
668    len: usize,
669}
670
671impl<const MTU: usize> AsRef<[u8]> for Notification<MTU> {
672    fn as_ref(&self) -> &[u8] {
673        &self.data[..self.len]
674    }
675}
676
677/// Handle for a GATT service.
678#[cfg_attr(feature = "defmt", derive(defmt::Format))]
679#[derive(Debug, PartialEq, Clone)]
680pub struct ServiceHandle {
681    start: u16,
682    end: u16,
683    uuid: Uuid,
684}
685
686pub(crate) struct Response<P> {
687    pdu: Pdu<P>,
688    handle: ConnHandle,
689}
690
691/// Trait with behavior for a gatt client.
692pub(crate) trait Client<'d, E, P: PacketPool> {
693    /// Perform a gatt request and return the response.
694    fn request(&self, req: AttReq<'_>) -> impl Future<Output = Result<Response<P::Packet>, BleHostError<E>>>;
695    fn command(&self, cmd: AttCmd<'_>) -> impl Future<Output = Result<(), BleHostError<E>>>;
696}
697
698impl<'reference, T: Controller, P: PacketPool, const MAX_SERVICES: usize> Client<'reference, T::Error, P>
699    for GattClient<'reference, T, P, MAX_SERVICES>
700{
701    async fn request(&self, req: AttReq<'_>) -> Result<Response<P::Packet>, BleHostError<T::Error>> {
702        let data = Att::Client(AttClient::Request(req));
703
704        self.send_att_data(data).await?;
705
706        let (h, pdu) = self.response_channel.receive().await;
707
708        assert_eq!(h, self.connection.handle());
709        Ok(Response { handle: h, pdu })
710    }
711
712    async fn command(&self, cmd: AttCmd<'_>) -> Result<(), BleHostError<T::Error>> {
713        let data = Att::Client(AttClient::Command(cmd));
714
715        self.send_att_data(data).await?;
716
717        Ok(())
718    }
719}
720
721impl<'reference, T: Controller, P: PacketPool, const MAX_SERVICES: usize> GattClient<'reference, T, P, MAX_SERVICES> {
722    async fn send_att_data(&self, data: Att<'_>) -> Result<(), BleHostError<T::Error>> {
723        let header = L2capHeader {
724            channel: crate::types::l2cap::L2CAP_CID_ATT,
725            length: data.size() as u16,
726        };
727
728        let mut buf = P::allocate().ok_or(Error::OutOfMemory)?;
729        let mut w = WriteCursor::new(buf.as_mut());
730        w.write_hci(&header)?;
731        w.write(data)?;
732        let len = w.len();
733
734        self.connection.send(Pdu::new(buf, len)).await;
735        Ok(())
736    }
737}
738
739impl<'reference, C: Controller, P: PacketPool, const MAX_SERVICES: usize> GattClient<'reference, C, P, MAX_SERVICES> {
740    /// Creates a GATT client capable of processing the GATT protocol using the provided table of attributes.
741    pub async fn new(
742        stack: &'reference Stack<'reference, C, P>,
743        connection: &Connection<'reference, P>,
744    ) -> Result<GattClient<'reference, C, P, MAX_SERVICES>, BleHostError<C::Error>> {
745        let l2cap = L2capHeader { channel: 4, length: 3 };
746        let mut buf = P::allocate().ok_or(Error::OutOfMemory)?;
747        let mut w = WriteCursor::new(buf.as_mut());
748        w.write_hci(&l2cap)?;
749        w.write(att::Att::Client(att::AttClient::Request(att::AttReq::ExchangeMtu {
750            mtu: P::MTU as u16 - 4,
751        })))?;
752
753        let len = w.len();
754        connection.send(Pdu::new(buf, len)).await;
755        Ok(Self {
756            known_services: RefCell::new(heapless::Vec::new()),
757            rx: stack.host.att_client.receiver().into(),
758            stack,
759            connection: connection.clone(),
760
761            response_channel: Channel::new(),
762
763            notifications: PubSubChannel::new(),
764        })
765    }
766
767    /// Discover primary services associated with a UUID.
768    pub async fn services_by_uuid(
769        &self,
770        uuid: &Uuid,
771    ) -> Result<Vec<ServiceHandle, MAX_SERVICES>, BleHostError<C::Error>> {
772        let mut start: u16 = 0x0001;
773        let mut result = Vec::new();
774
775        loop {
776            let data = att::AttReq::FindByTypeValue {
777                start_handle: start,
778                end_handle: 0xffff,
779                att_type: PRIMARY_SERVICE.into(),
780                att_value: uuid.as_raw(),
781            };
782
783            let response = self.request(data).await?;
784            let res = Self::response(response.pdu.as_ref())?;
785            match res {
786                AttRsp::Error { request, handle, code } => {
787                    if code == att::AttErrorCode::ATTRIBUTE_NOT_FOUND {
788                        break;
789                    }
790                    return Err(Error::Att(code).into());
791                }
792                AttRsp::FindByTypeValue { mut it } => {
793                    let mut end: u16 = 0;
794                    while let Some(res) = it.next() {
795                        let (handle, e) = res?;
796                        end = e;
797                        let svc = ServiceHandle {
798                            start: handle,
799                            end,
800                            uuid: uuid.clone(),
801                        };
802                        result.push(svc.clone()).map_err(|_| Error::InsufficientSpace)?;
803                        self.known_services
804                            .borrow_mut()
805                            .push(svc)
806                            .map_err(|_| Error::InsufficientSpace)?;
807                    }
808                    if end == 0xFFFF {
809                        break;
810                    }
811                    start = end + 1;
812                }
813                res => {
814                    trace!("[gatt client] response: {:?}", res);
815                    return Err(Error::UnexpectedGattResponse.into());
816                }
817            }
818        }
819
820        Ok(result)
821    }
822
823    /// Discover characteristics in a given service using a UUID.
824    pub async fn characteristic_by_uuid<T: AsGatt>(
825        &self,
826        service: &ServiceHandle,
827        uuid: &Uuid,
828    ) -> Result<Characteristic<T>, BleHostError<C::Error>> {
829        let mut start: u16 = service.start;
830        let mut found_indicate_or_notify_uuid = Option::None;
831
832        loop {
833            let data = att::AttReq::ReadByType {
834                start,
835                end: service.end,
836                attribute_type: CHARACTERISTIC.into(),
837            };
838            let response = self.request(data).await?;
839
840            match Self::response(response.pdu.as_ref())? {
841                AttRsp::ReadByType { mut it } => {
842                    while let Some(Ok((handle, item))) = it.next() {
843                        let expected_items_len = 5;
844                        let item_len = item.len();
845
846                        if item_len < expected_items_len {
847                            return Err(Error::MalformedCharacteristicDeclaration {
848                                expected: expected_items_len,
849                                actual: item_len,
850                            }
851                            .into());
852                        }
853                        if let AttributeData::Declaration {
854                            props,
855                            handle,
856                            uuid: decl_uuid,
857                        } = AttributeData::decode_declaration(item)?
858                        {
859                            if let Some(start_handle) = found_indicate_or_notify_uuid {
860                                return Ok(Characteristic {
861                                    handle: start_handle,
862                                    cccd_handle: Some(self.get_characteristic_cccd(start_handle, handle).await?),
863                                    phantom: PhantomData,
864                                });
865                            }
866
867                            if *uuid == decl_uuid {
868                                // If there are "notify" and "indicate" characteristic properties we need to find the
869                                // next characteristic so we can determine the search space for the CCCD
870                                if !props.any(&[CharacteristicProp::Indicate, CharacteristicProp::Notify]) {
871                                    return Ok(Characteristic {
872                                        handle,
873                                        cccd_handle: None,
874                                        phantom: PhantomData,
875                                    });
876                                }
877                                found_indicate_or_notify_uuid = Some(handle);
878                            }
879
880                            if handle == 0xFFFF {
881                                return Err(Error::NotFound.into());
882                            }
883                            start = handle + 1;
884                        } else {
885                            return Err(Error::InvalidCharacteristicDeclarationData.into());
886                        }
887                    }
888                }
889                AttRsp::Error { request, handle, code } => match code {
890                    att::AttErrorCode::ATTRIBUTE_NOT_FOUND => match found_indicate_or_notify_uuid {
891                        Some(handle) => {
892                            return Ok(Characteristic {
893                                handle,
894                                cccd_handle: Some(self.get_characteristic_cccd(handle, service.end).await?),
895                                phantom: PhantomData,
896                            });
897                        }
898                        None => return Err(Error::NotFound.into()),
899                    },
900                    _ => return Err(Error::Att(code).into()),
901                },
902                _ => return Err(Error::UnexpectedGattResponse.into()),
903            }
904        }
905    }
906
907    async fn get_characteristic_cccd(
908        &self,
909        char_start_handle: u16,
910        char_end_handle: u16,
911    ) -> Result<u16, BleHostError<C::Error>> {
912        let mut start_handle = char_start_handle;
913
914        while start_handle <= char_end_handle {
915            let data = att::AttReq::FindInformation {
916                start_handle,
917                end_handle: char_end_handle,
918            };
919
920            let response = self.request(data).await?;
921
922            match Self::response(response.pdu.as_ref())? {
923                AttRsp::FindInformation { mut it } => {
924                    while let Some(Ok((handle, uuid))) = it.next() {
925                        if uuid == CLIENT_CHARACTERISTIC_CONFIGURATION.into() {
926                            return Ok(handle);
927                        }
928                        start_handle = handle + 1;
929                    }
930                }
931                AttRsp::Error { request, handle, code } => return Err(Error::Att(code).into()),
932                _ => return Err(Error::UnexpectedGattResponse.into()),
933            }
934        }
935        Err(Error::NotFound.into())
936    }
937
938    /// Read a characteristic described by a handle.
939    ///
940    /// The number of bytes copied into the provided buffer is returned.
941    pub async fn read_characteristic<T: AsGatt>(
942        &self,
943        characteristic: &Characteristic<T>,
944        dest: &mut [u8],
945    ) -> Result<usize, BleHostError<C::Error>> {
946        let data = att::AttReq::Read {
947            handle: characteristic.handle,
948        };
949
950        let response = self.request(data).await?;
951
952        match Self::response(response.pdu.as_ref())? {
953            AttRsp::Read { data } => {
954                let to_copy = data.len().min(dest.len());
955                dest[..to_copy].copy_from_slice(&data[..to_copy]);
956                Ok(to_copy)
957            }
958            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
959            _ => Err(Error::UnexpectedGattResponse.into()),
960        }
961    }
962
963    /// Read a characteristic described by a UUID.
964    ///
965    /// The number of bytes copied into the provided buffer is returned.
966    pub async fn read_characteristic_by_uuid(
967        &self,
968        service: &ServiceHandle,
969        uuid: &Uuid,
970        dest: &mut [u8],
971    ) -> Result<usize, BleHostError<C::Error>> {
972        let data = att::AttReq::ReadByType {
973            start: service.start,
974            end: service.end,
975            attribute_type: uuid.clone(),
976        };
977
978        let response = self.request(data).await?;
979
980        match Self::response(response.pdu.as_ref())? {
981            AttRsp::ReadByType { mut it } => {
982                let mut to_copy = 0;
983                if let Some(item) = it.next() {
984                    let (_handle, data) = item?;
985                    to_copy = data.len().min(dest.len());
986                    dest[..to_copy].copy_from_slice(&data[..to_copy]);
987                }
988                Ok(to_copy)
989            }
990            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
991            _ => Err(Error::UnexpectedGattResponse.into()),
992        }
993    }
994
995    /// Write to a characteristic described by a handle.
996    pub async fn write_characteristic<T: FromGatt>(
997        &self,
998        handle: &Characteristic<T>,
999        buf: &[u8],
1000    ) -> Result<(), BleHostError<C::Error>> {
1001        let data = att::AttReq::Write {
1002            handle: handle.handle,
1003            data: buf,
1004        };
1005
1006        let response = self.request(data).await?;
1007        match Self::response(response.pdu.as_ref())? {
1008            AttRsp::Write => Ok(()),
1009            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
1010            _ => Err(Error::UnexpectedGattResponse.into()),
1011        }
1012    }
1013
1014    /// Write without waiting for a response to a characteristic described by a handle.
1015    pub async fn write_characteristic_without_response<T: FromGatt>(
1016        &self,
1017        handle: &Characteristic<T>,
1018        buf: &[u8],
1019    ) -> Result<(), BleHostError<C::Error>> {
1020        let data = att::AttCmd::Write {
1021            handle: handle.handle,
1022            data: buf,
1023        };
1024
1025        self.command(data).await?;
1026
1027        Ok(())
1028    }
1029
1030    /// Subscribe to indication/notification of a given Characteristic
1031    ///
1032    /// A listener is returned, which has a `next()` method
1033    pub async fn subscribe<T: AsGatt>(
1034        &self,
1035        characteristic: &Characteristic<T>,
1036        indication: bool,
1037    ) -> Result<NotificationListener<'_, 512>, BleHostError<C::Error>> {
1038        let properties = u16::to_le_bytes(if indication { 0x02 } else { 0x01 });
1039
1040        let data = att::AttReq::Write {
1041            handle: characteristic.cccd_handle.ok_or(Error::NotSupported)?,
1042            data: &properties,
1043        };
1044
1045        // set the CCCD
1046        let response = self.request(data).await?;
1047
1048        match Self::response(response.pdu.as_ref())? {
1049            AttRsp::Write => match self.notifications.dyn_subscriber() {
1050                Ok(listener) => Ok(NotificationListener {
1051                    listener,
1052                    handle: characteristic.handle,
1053                }),
1054                Err(embassy_sync::pubsub::Error::MaximumSubscribersReached) => {
1055                    Err(Error::GattSubscriberLimitReached.into())
1056                }
1057                Err(_) => Err(Error::Other.into()),
1058            },
1059            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
1060            _ => Err(Error::UnexpectedGattResponse.into()),
1061        }
1062    }
1063
1064    /// Unsubscribe from a given Characteristic
1065    pub async fn unsubscribe<T: AsGatt>(
1066        &self,
1067        characteristic: &Characteristic<T>,
1068    ) -> Result<(), BleHostError<C::Error>> {
1069        let properties = u16::to_le_bytes(0);
1070        let data = att::AttReq::Write {
1071            handle: characteristic.cccd_handle.ok_or(Error::NotSupported)?,
1072            data: &[0, 0],
1073        };
1074
1075        // set the CCCD
1076        let response = self.request(data).await?;
1077
1078        match Self::response(response.pdu.as_ref())? {
1079            AttRsp::Write => Ok(()),
1080            AttRsp::Error { request, handle, code } => Err(Error::Att(code).into()),
1081            _ => Err(Error::UnexpectedGattResponse.into()),
1082        }
1083    }
1084
1085    /// Handle a notification that was received.
1086    async fn handle_notification_packet(&self, data: &[u8]) -> Result<(), BleHostError<C::Error>> {
1087        let mut r = ReadCursor::new(data);
1088        let value_handle: u16 = r.read()?;
1089        let value_attr = r.remaining();
1090
1091        let handle = value_handle;
1092
1093        // TODO
1094        let mut data = [0u8; 512];
1095        let to_copy = data.len().min(value_attr.len());
1096        data[..to_copy].copy_from_slice(&value_attr[..to_copy]);
1097        let n = Notification {
1098            handle,
1099            data,
1100            len: to_copy,
1101        };
1102        self.notifications.immediate_publisher().publish_immediate(n);
1103        Ok(())
1104    }
1105
1106    /// Task which handles GATT rx data (needed for notifications to work)
1107    pub async fn task(&self) -> Result<(), BleHostError<C::Error>> {
1108        loop {
1109            let (handle, pdu) = self.rx.receive().await;
1110            let data = pdu.as_ref();
1111            // handle notifications
1112            if pdu.as_ref()[0] == ATT_HANDLE_VALUE_NTF {
1113                self.handle_notification_packet(&pdu.as_ref()[1..]).await?;
1114            } else {
1115                self.response_channel.send((handle, pdu)).await;
1116            }
1117        }
1118    }
1119
1120    fn response<'a>(data: &'a [u8]) -> Result<AttRsp<'a>, BleHostError<C::Error>> {
1121        let att = Att::decode(data)?;
1122        match att {
1123            Att::Server(AttServer::Response(rsp)) => Ok(rsp),
1124            _ => Err(Error::UnexpectedGattResponse.into()),
1125        }
1126    }
1127}