Skip to main content

trouble_host/
attribute.rs

1//! Attribute protocol implementation.
2use core::cell::RefCell;
3use core::fmt;
4use core::marker::PhantomData;
5
6use bt_hci::uuid::declarations::{CHARACTERISTIC, INCLUDE, PRIMARY_SERVICE, SECONDARY_SERVICE};
7use bt_hci::uuid::descriptors::CLIENT_CHARACTERISTIC_CONFIGURATION;
8use embassy_sync::blocking_mutex::raw::RawMutex;
9use embassy_sync::blocking_mutex::Mutex;
10use heapless::Vec;
11
12use crate::att::{AttErrorCode, AttUns};
13use crate::attribute_server::AttributeServer;
14use crate::cursor::ReadCursor;
15use crate::prelude::{AsGatt, FixedGattValue, FromGatt, GattConnection, SecurityLevel};
16use crate::types::gatt_traits::FromGattError;
17pub use crate::types::uuid::Uuid;
18use crate::{gatt, Error, PacketPool, MAX_INVALID_DATA_LEN};
19
20/// Characteristic properties
21#[derive(Debug, Clone, Copy)]
22#[repr(u8)]
23pub enum CharacteristicProp {
24    /// Broadcast
25    Broadcast = 0x01,
26    /// Read
27    Read = 0x02,
28    /// Write without response
29    WriteWithoutResponse = 0x04,
30    /// Write
31    Write = 0x08,
32    /// Notify
33    Notify = 0x10,
34    /// Indicate
35    Indicate = 0x20,
36    /// Authenticated writes
37    AuthenticatedWrite = 0x40,
38    /// Extended properties
39    Extended = 0x80,
40}
41
42/// Attribute permissions
43#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45pub enum PermissionLevel {
46    #[default]
47    /// Operation is allowed with no encryption or authentication
48    Allowed,
49    /// Encryption is required
50    EncryptionRequired,
51    /// Encryption and authentication are required
52    AuthenticationRequired,
53    /// Operation is not allowed
54    NotAllowed,
55}
56
57/// Attribute permissions
58#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
59#[cfg_attr(feature = "defmt", derive(defmt::Format))]
60pub struct AttPermissions {
61    /// Security required for read operations
62    pub read: PermissionLevel,
63    /// Security required for write operations
64    pub write: PermissionLevel,
65}
66
67impl AttPermissions {
68    pub(crate) fn can_read(&self, level: SecurityLevel) -> Result<(), AttErrorCode> {
69        match self.read {
70            PermissionLevel::NotAllowed => Err(AttErrorCode::READ_NOT_PERMITTED),
71            PermissionLevel::EncryptionRequired | PermissionLevel::AuthenticationRequired
72                if level < SecurityLevel::Encrypted =>
73            {
74                Err(AttErrorCode::INSUFFICIENT_ENCRYPTION)
75            }
76            PermissionLevel::AuthenticationRequired if level < SecurityLevel::EncryptedAuthenticated => {
77                Err(AttErrorCode::INSUFFICIENT_AUTHENTICATION)
78            }
79            _ => Ok(()),
80        }
81    }
82
83    pub(crate) fn can_write(&self, level: SecurityLevel) -> Result<(), AttErrorCode> {
84        match self.write {
85            PermissionLevel::NotAllowed => Err(AttErrorCode::WRITE_NOT_PERMITTED),
86            PermissionLevel::EncryptionRequired | PermissionLevel::AuthenticationRequired
87                if level < SecurityLevel::Encrypted =>
88            {
89                Err(AttErrorCode::INSUFFICIENT_ENCRYPTION)
90            }
91            PermissionLevel::AuthenticationRequired if level < SecurityLevel::EncryptedAuthenticated => {
92                Err(AttErrorCode::INSUFFICIENT_AUTHENTICATION)
93            }
94            _ => Ok(()),
95        }
96    }
97}
98
99/// Attribute metadata.
100pub struct Attribute<'a> {
101    pub(crate) uuid: Uuid,
102    pub(crate) data: AttributeData<'a>,
103}
104
105impl<'a> Attribute<'a> {
106    const EMPTY: Option<Attribute<'a>> = None;
107
108    pub(crate) fn read(&self, offset: usize, data: &mut [u8]) -> Result<usize, AttErrorCode> {
109        self.data.read(offset, data)
110    }
111
112    pub(crate) fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
113        self.data.write(offset, data)
114    }
115
116    pub(crate) fn permissions(&self) -> AttPermissions {
117        self.data.permissions()
118    }
119}
120
121#[derive(Debug, PartialEq, Eq)]
122pub(crate) enum AttributeData<'d> {
123    Service {
124        uuid: Uuid,
125        last_handle_in_group: u16,
126    },
127    IncludedService {
128        handle: u16,
129        last_handle_in_group: u16,
130        uuid: Option<[u8; 2]>,
131    },
132    ReadOnlyData {
133        permissions: AttPermissions,
134        value: &'d [u8],
135    },
136    Data {
137        permissions: AttPermissions,
138        variable_len: bool,
139        len: u16,
140        value: &'d mut [u8],
141    },
142    SmallData {
143        permissions: AttPermissions,
144        variable_len: bool,
145        capacity: u8,
146        len: u8,
147        value: [u8; 8],
148    },
149    Declaration {
150        props: CharacteristicProps,
151        handle: u16,
152        uuid: Uuid,
153    },
154    Cccd {
155        notifications: bool,
156        indications: bool,
157        write_permission: PermissionLevel,
158    },
159}
160
161impl AttributeData<'_> {
162    pub(crate) fn permissions(&self) -> AttPermissions {
163        match self {
164            AttributeData::Service { .. }
165            | AttributeData::IncludedService { .. }
166            | AttributeData::Declaration { .. } => AttPermissions {
167                read: PermissionLevel::Allowed,
168                write: PermissionLevel::NotAllowed,
169            },
170            AttributeData::ReadOnlyData { permissions, .. }
171            | AttributeData::Data { permissions, .. }
172            | AttributeData::SmallData { permissions, .. } => *permissions,
173            AttributeData::Cccd { write_permission, .. } => AttPermissions {
174                read: PermissionLevel::Allowed,
175                write: *write_permission,
176            },
177        }
178    }
179
180    pub(crate) fn readable(&self) -> bool {
181        self.permissions().read != PermissionLevel::NotAllowed
182    }
183
184    pub(crate) fn writable(&self) -> bool {
185        self.permissions().write != PermissionLevel::NotAllowed
186    }
187
188    fn read(&self, mut offset: usize, mut data: &mut [u8]) -> Result<usize, AttErrorCode> {
189        fn append(src: &[u8], offset: &mut usize, dest: &mut &mut [u8]) -> usize {
190            if *offset >= src.len() {
191                *offset -= src.len();
192                0
193            } else {
194                let d = core::mem::take(dest);
195                let n = d.len().min(src.len() - *offset);
196                d[..n].copy_from_slice(&src[*offset..][..n]);
197                *dest = &mut d[n..];
198                *offset = 0;
199                n
200            }
201        }
202
203        let written = match self {
204            Self::ReadOnlyData { value, .. } => append(value, &mut offset, &mut data),
205            Self::Data { len, value, .. } => {
206                let value = &value[..*len as usize];
207                append(value, &mut offset, &mut data)
208            }
209            Self::SmallData { len, value, .. } => {
210                let value = &value[..*len as usize];
211                append(value, &mut offset, &mut data)
212            }
213            Self::Service { uuid, .. } => {
214                let val = uuid.as_raw();
215                append(val, &mut offset, &mut data)
216            }
217            Self::IncludedService {
218                handle,
219                last_handle_in_group,
220                uuid,
221            } => {
222                let written = append(&handle.to_le_bytes(), &mut offset, &mut data)
223                    + append(&last_handle_in_group.to_le_bytes(), &mut offset, &mut data);
224                if let Some(uuid) = uuid {
225                    written + append(uuid, &mut offset, &mut data)
226                } else {
227                    written
228                }
229            }
230            Self::Cccd {
231                notifications,
232                indications,
233                ..
234            } => {
235                let mut v = 0u16;
236                if *notifications {
237                    v |= 0x01;
238                }
239
240                if *indications {
241                    v |= 0x02;
242                }
243                append(&v.to_le_bytes(), &mut offset, &mut data)
244            }
245            Self::Declaration { props, handle, uuid } => {
246                let val = uuid.as_raw();
247                append(&[props.0], &mut offset, &mut data)
248                    + append(&handle.to_le_bytes(), &mut offset, &mut data)
249                    + append(val, &mut offset, &mut data)
250            }
251        };
252
253        if offset > 0 {
254            Err(AttErrorCode::INVALID_OFFSET)
255        } else {
256            Ok(written)
257        }
258    }
259
260    fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
261        match self {
262            Self::Data {
263                value,
264                variable_len,
265                len,
266                ..
267            } => {
268                if offset + data.len() <= value.len() {
269                    value[offset..offset + data.len()].copy_from_slice(data);
270                    if *variable_len {
271                        *len = (offset + data.len()) as u16;
272                    }
273                    Ok(())
274                } else {
275                    Err(AttErrorCode::INVALID_OFFSET)
276                }
277            }
278            Self::SmallData {
279                variable_len,
280                capacity,
281                len,
282                value,
283                ..
284            } => {
285                if offset + data.len() <= *capacity as usize {
286                    value[offset..offset + data.len()].copy_from_slice(data);
287                    if *variable_len {
288                        *len = (offset + data.len()) as u8;
289                    }
290                    Ok(())
291                } else {
292                    Err(AttErrorCode::INVALID_OFFSET)
293                }
294            }
295            Self::Cccd {
296                notifications,
297                indications,
298                ..
299            } => {
300                if offset > 0 {
301                    return Err(AttErrorCode::INVALID_OFFSET);
302                }
303
304                if data.is_empty() {
305                    return Err(AttErrorCode::UNLIKELY_ERROR);
306                }
307
308                *notifications = data[0] & 0x01 != 0;
309                *indications = data[0] & 0x02 != 0;
310                Ok(())
311            }
312            _ => Err(AttErrorCode::WRITE_NOT_PERMITTED),
313        }
314    }
315
316    pub(crate) fn decode_declaration(data: &[u8]) -> Result<Self, Error> {
317        let mut r = ReadCursor::new(data);
318        Ok(Self::Declaration {
319            props: CharacteristicProps(r.read()?),
320            handle: r.read()?,
321            uuid: Uuid::try_from(r.remaining())?,
322        })
323    }
324}
325
326impl fmt::Debug for Attribute<'_> {
327    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328        f.debug_struct("Attribute")
329            .field("uuid", &self.uuid)
330            .field("readable", &self.data.readable())
331            .field("writable", &self.data.writable())
332            .finish()
333    }
334}
335
336#[cfg(feature = "defmt")]
337impl<'a> defmt::Format for Attribute<'a> {
338    fn format(&self, fmt: defmt::Formatter) {
339        defmt::write!(fmt, "{}", defmt::Debug2Format(self))
340    }
341}
342
343impl<'a> Attribute<'a> {
344    pub(crate) fn new(uuid: Uuid, data: AttributeData<'a>) -> Attribute<'a> {
345        Attribute { uuid, data }
346    }
347}
348
349/// A table of attributes.
350pub struct AttributeTable<'d, M: RawMutex, const MAX: usize> {
351    inner: Mutex<M, RefCell<InnerTable<'d, MAX>>>,
352}
353
354pub(crate) struct InnerTable<'d, const MAX: usize> {
355    attributes: Vec<Attribute<'d>, MAX>,
356}
357
358impl<'d, const MAX: usize> InnerTable<'d, MAX> {
359    fn push(&mut self, attribute: Attribute<'d>) -> u16 {
360        let handle = self.next_handle();
361        self.attributes.push(attribute).unwrap();
362        handle
363    }
364
365    fn next_handle(&self) -> u16 {
366        self.attributes.len() as u16 + 1
367    }
368}
369
370impl<M: RawMutex, const MAX: usize> Default for AttributeTable<'_, M, MAX> {
371    fn default() -> Self {
372        Self::new()
373    }
374}
375
376impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> {
377    /// Create a new GATT table.
378    pub fn new() -> Self {
379        Self {
380            inner: Mutex::new(RefCell::new(InnerTable { attributes: Vec::new() })),
381        }
382    }
383
384    pub(crate) fn with_inner<F: FnOnce(&mut InnerTable<'d, MAX>) -> R, R>(&self, f: F) -> R {
385        self.inner.lock(|inner| {
386            let mut table = inner.borrow_mut();
387            f(&mut table)
388        })
389    }
390
391    pub(crate) fn iterate<F: FnOnce(AttributeIterator<'_, 'd>) -> R, R>(&self, f: F) -> R {
392        self.with_inner(|table| {
393            let it = AttributeIterator {
394                attributes: table.attributes.as_mut_slice(),
395                pos: 0,
396            };
397            f(it)
398        })
399    }
400
401    pub(crate) fn with_attribute<F: FnOnce(&mut Attribute<'d>) -> R, R>(&self, handle: u16, f: F) -> Option<R> {
402        if handle == 0 {
403            return None;
404        }
405
406        self.with_inner(|table| {
407            let i = usize::from(handle) - 1;
408            table.attributes.get_mut(i).map(f)
409        })
410    }
411
412    pub(crate) fn iterate_from<F: FnOnce(AttributeIterator<'_, 'd>) -> R, R>(&self, start: u16, f: F) -> R {
413        self.with_inner(|table| {
414            let it = AttributeIterator {
415                attributes: &mut table.attributes[..],
416                pos: usize::from(start).saturating_sub(1),
417            };
418            f(it)
419        })
420    }
421
422    fn push(&mut self, attribute: Attribute<'d>) -> u16 {
423        self.with_inner(|table| table.push(attribute))
424    }
425
426    /// Add a service to the attribute table (group of characteristics)
427    pub fn add_service(&mut self, service: Service) -> ServiceBuilder<'_, 'd, M, MAX> {
428        let handle = self.push(Attribute {
429            uuid: PRIMARY_SERVICE.into(),
430            data: AttributeData::Service {
431                uuid: service.uuid,
432                last_handle_in_group: 0,
433            },
434        });
435        ServiceBuilder { handle, table: self }
436    }
437
438    /// Add a service to the attribute table (group of characteristics)
439    pub fn add_secondary_service(&mut self, service: Service) -> ServiceBuilder<'_, 'd, M, MAX> {
440        let handle = self.push(Attribute {
441            uuid: SECONDARY_SERVICE.into(),
442            data: AttributeData::Service {
443                uuid: service.uuid,
444                last_handle_in_group: 0,
445            },
446        });
447        ServiceBuilder { handle, table: self }
448    }
449
450    /// Get the permissions for the attribute
451    ///
452    /// Returns `None` if the attribute handle is invalid.
453    pub fn permissions(&self, attribute: u16) -> Option<AttPermissions> {
454        self.with_attribute(attribute, |att| att.data.permissions())
455    }
456
457    /// Get the UUID of the attribute type
458    ///
459    /// Returns `None` if the attribute handle is invalid.
460    pub fn uuid(&self, attribute: u16) -> Option<Uuid> {
461        self.with_attribute(attribute, |att| att.uuid.clone())
462    }
463
464    pub(crate) fn set_ro(&self, attribute: u16, new_value: &'d [u8]) -> Result<(), Error> {
465        self.with_attribute(attribute, |att| match &mut att.data {
466            AttributeData::ReadOnlyData { value, .. } => {
467                *value = new_value;
468                Ok(())
469            }
470            _ => Err(Error::NotSupported),
471        })
472        .unwrap_or(Err(Error::NotFound))
473    }
474
475    /// Read the raw value of the attribute
476    ///
477    /// If the attribute value is larger than the data buffer, data will be filled with
478    /// as many bytes as fit. Use additional reads with an offset to read the remaining data.
479    ///
480    /// The value of the attribute is undefined for connection-specific attributes (like CCCD).
481    pub fn read(&self, attribute: u16, offset: usize, data: &mut [u8]) -> Result<usize, Error> {
482        self.with_attribute(attribute, |att| att.read(offset, data).map_err(Into::into))
483            .unwrap_or(Err(Error::NotFound))
484    }
485
486    /// Write the raw value of the attribute
487    ///
488    /// If the attribute is variable length, its length will be set to `offset + data.len()`.
489    /// If the attribute is fixed length, the range `offset..(offset + data.len())` will be
490    /// overwritten.
491    pub fn write(&self, attribute: u16, offset: usize, data: &[u8]) -> Result<(), Error> {
492        self.with_attribute(attribute, |att| att.write(offset, data).map_err(Into::into))
493            .unwrap_or(Err(Error::NotFound))
494    }
495
496    pub(crate) fn set_raw(&self, attribute: u16, input: &[u8]) -> Result<(), Error> {
497        self.with_attribute(attribute, |att| match &mut att.data {
498            AttributeData::Data {
499                value,
500                variable_len,
501                len,
502                ..
503            } => {
504                let expected_len = value.len();
505                let actual_len = input.len();
506
507                if expected_len == actual_len {
508                    value.copy_from_slice(input);
509                    Ok(())
510                } else if *variable_len && actual_len <= expected_len {
511                    value[..input.len()].copy_from_slice(input);
512                    *len = input.len() as u16;
513                    Ok(())
514                } else {
515                    Err(Error::UnexpectedDataLength {
516                        expected: expected_len,
517                        actual: actual_len,
518                    })
519                }
520            }
521            AttributeData::SmallData {
522                variable_len,
523                capacity,
524                len,
525                value,
526                ..
527            } => {
528                let expected_len = usize::from(*capacity);
529                let actual_len = input.len();
530
531                if expected_len == actual_len {
532                    value[..expected_len].copy_from_slice(input);
533                    Ok(())
534                } else if *variable_len && actual_len <= expected_len {
535                    value[..input.len()].copy_from_slice(input);
536                    *len = input.len() as u8;
537                    Ok(())
538                } else {
539                    Err(Error::UnexpectedDataLength {
540                        expected: expected_len,
541                        actual: actual_len,
542                    })
543                }
544            }
545            _ => Err(Error::NotSupported),
546        })
547        .unwrap_or(Err(Error::NotFound))
548    }
549
550    /// Get the number of attributes in the table
551    pub fn len(&self) -> usize {
552        self.with_inner(|table| table.attributes.len())
553    }
554
555    /// Returns true if the table is empty
556    pub fn is_empty(&self) -> bool {
557        self.with_inner(|table| table.attributes.is_empty())
558    }
559
560    /// Set the value of a characteristic
561    ///
562    /// The provided data must exactly match the size of the storage for the characteristic,
563    /// otherwise this function will panic.
564    ///
565    /// If the characteristic for the handle cannot be found, or the shape of the data does not match the type of the characterstic,
566    /// an error is returned
567    pub fn set<T: AttributeHandle>(&self, attribute_handle: &T, input: &T::Value) -> Result<(), Error> {
568        let gatt_value = input.as_gatt();
569        self.set_raw(attribute_handle.handle(), gatt_value)
570    }
571
572    /// Read the value of the characteristic and pass the value to the provided closure.
573    ///
574    /// The return value of the closure is returned in this function and is assumed to be infallible.
575    ///
576    /// If the characteristic for the handle cannot be found, an error is returned.
577    pub fn get<T: AttributeHandle<Value = V>, V: FromGatt>(&self, attribute_handle: &T) -> Result<T::Value, Error> {
578        self.with_attribute(attribute_handle.handle(), |att| {
579            let value_slice = match &mut att.data {
580                AttributeData::Data { value, len, .. } => &value[..*len as usize],
581                AttributeData::ReadOnlyData { value, .. } => value,
582                AttributeData::SmallData { len, value, .. } => &value[..usize::from(*len)],
583                _ => return Err(Error::NotSupported),
584            };
585
586            T::Value::from_gatt(value_slice).map_err(|_| {
587                let mut invalid_data = [0u8; MAX_INVALID_DATA_LEN];
588                let len_to_copy = value_slice.len().min(MAX_INVALID_DATA_LEN);
589                invalid_data[..len_to_copy].copy_from_slice(&value_slice[..len_to_copy]);
590
591                Error::CannotConstructGattValue(invalid_data)
592            })
593        })
594        .unwrap_or(Err(Error::NotFound))
595    }
596
597    /// Return the characteristic which corresponds to the supplied value handle
598    ///
599    /// If no characteristic corresponding to the given value handle was found, returns an error
600    pub fn find_characteristic_by_value_handle<T: AsGatt>(&self, handle: u16) -> Result<Characteristic<T>, Error> {
601        if handle == 0 {
602            return Err(Error::NotFound);
603        }
604
605        self.iterate_from(handle - 1, |mut it| {
606            if let Some((_, att)) = it.next() {
607                if let AttributeData::Declaration { props, .. } = att.data {
608                    if it.next().is_some() {
609                        let cccd_handle = it
610                            .next()
611                            .and_then(|(handle, att)| matches!(att.data, AttributeData::Cccd { .. }).then_some(handle));
612
613                        return Ok(Characteristic {
614                            handle,
615                            cccd_handle,
616                            props,
617                            phantom: PhantomData,
618                        });
619                    }
620                }
621            }
622            Err(Error::NotFound)
623        })
624    }
625
626    #[cfg(feature = "security")]
627    /// Calculate the database hash for the attribute table.
628    ///
629    /// See Core Specification Vol 3, Part G, Section 7.3.1
630    pub fn hash(&self) -> u128 {
631        use bt_hci::uuid::*;
632
633        use crate::security_manager::crypto::AesCmac;
634
635        const PRIMARY_SERVICE: Uuid = Uuid::Uuid16(declarations::PRIMARY_SERVICE.to_le_bytes());
636        const SECONDARY_SERVICE: Uuid = Uuid::Uuid16(declarations::SECONDARY_SERVICE.to_le_bytes());
637        const INCLUDED_SERVICE: Uuid = Uuid::Uuid16(declarations::INCLUDE.to_le_bytes());
638        const CHARACTERISTIC: Uuid = Uuid::Uuid16(declarations::CHARACTERISTIC.to_le_bytes());
639        const CHARACTERISTIC_EXTENDED_PROPERTIES: Uuid =
640            Uuid::Uuid16(descriptors::CHARACTERISTIC_EXTENDED_PROPERTIES.to_le_bytes());
641
642        const CHARACTERISTIC_USER_DESCRIPTION: Uuid =
643            Uuid::Uuid16(descriptors::CHARACTERISTIC_USER_DESCRIPTION.to_le_bytes());
644        const CLIENT_CHARACTERISTIC_CONFIGURATION: Uuid =
645            Uuid::Uuid16(descriptors::CLIENT_CHARACTERISTIC_CONFIGURATION.to_le_bytes());
646        const SERVER_CHARACTERISTIC_CONFIGURATION: Uuid =
647            Uuid::Uuid16(descriptors::SERVER_CHARACTERISTIC_CONFIGURATION.to_le_bytes());
648        const CHARACTERISTIC_PRESENTATION_FORMAT: Uuid =
649            Uuid::Uuid16(descriptors::CHARACTERISTIC_PRESENTATION_FORMAT.to_le_bytes());
650        const CHARACTERISTIC_AGGREGATE_FORMAT: Uuid =
651            Uuid::Uuid16(descriptors::CHARACTERISTIC_AGGREGATE_FORMAT.to_le_bytes());
652
653        let mut mac = AesCmac::db_hash();
654
655        self.iterate(|mut it| {
656            while let Some((handle, att)) = it.next() {
657                match att.uuid {
658                    PRIMARY_SERVICE
659                    | SECONDARY_SERVICE
660                    | INCLUDED_SERVICE
661                    | CHARACTERISTIC
662                    | CHARACTERISTIC_EXTENDED_PROPERTIES => {
663                        mac.update(handle.to_le_bytes()).update(att.uuid.as_raw());
664                        match &att.data {
665                            AttributeData::ReadOnlyData { value, .. } => {
666                                mac.update(value);
667                            }
668                            AttributeData::Data { len, value, .. } => {
669                                mac.update(&value[..usize::from(*len)]);
670                            }
671                            AttributeData::Service { uuid, .. } => {
672                                mac.update(uuid.as_raw());
673                            }
674                            AttributeData::Declaration { props, handle, uuid } => {
675                                mac.update([props.0]).update(handle.to_le_bytes()).update(uuid.as_raw());
676                            }
677                            _ => unreachable!(),
678                        }
679                    }
680                    CHARACTERISTIC_USER_DESCRIPTION
681                    | CLIENT_CHARACTERISTIC_CONFIGURATION
682                    | SERVER_CHARACTERISTIC_CONFIGURATION
683                    | CHARACTERISTIC_PRESENTATION_FORMAT
684                    | CHARACTERISTIC_AGGREGATE_FORMAT => {
685                        mac.update(handle.to_le_bytes()).update(att.uuid.as_raw());
686                    }
687                    _ => {}
688                }
689            }
690        });
691
692        mac.finalize()
693    }
694}
695
696/// A type which holds a handle to an attribute in the attribute table
697pub trait AttributeHandle {
698    /// The data type which the attribute contains
699    type Value: AsGatt;
700
701    /// Returns the attribute's handle
702    fn handle(&self) -> u16;
703}
704
705impl<T: AsGatt> AttributeHandle for Characteristic<T> {
706    type Value = T;
707
708    fn handle(&self) -> u16 {
709        self.handle
710    }
711}
712
713/// Invalid handle value
714#[derive(Debug, Clone, PartialEq)]
715#[cfg_attr(feature = "defmt", derive(defmt::Format))]
716pub struct InvalidHandle;
717
718impl core::fmt::Display for InvalidHandle {
719    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
720        core::fmt::Debug::fmt(self, f)
721    }
722}
723
724impl core::error::Error for InvalidHandle {}
725
726impl From<InvalidHandle> for Error {
727    fn from(value: InvalidHandle) -> Self {
728        Error::InvalidValue
729    }
730}
731
732/// Builder for constructing GATT service definitions.
733pub struct ServiceBuilder<'r, 'd, M: RawMutex, const MAX: usize> {
734    handle: u16,
735    table: &'r mut AttributeTable<'d, M, MAX>,
736}
737
738impl<'d, M: RawMutex, const MAX: usize> ServiceBuilder<'_, 'd, M, MAX> {
739    fn add_characteristic_internal<T: AsGatt + ?Sized>(
740        &mut self,
741        uuid: Uuid,
742        props: CharacteristicProps,
743        data: AttributeData<'d>,
744    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
745        // First the characteristic declaration
746        let (handle, cccd_handle) = self.table.with_inner(|table| {
747            let value_handle = table.next_handle() + 1;
748            table.push(Attribute {
749                uuid: CHARACTERISTIC.into(),
750                data: AttributeData::Declaration {
751                    props,
752                    handle: value_handle,
753                    uuid: uuid.clone(),
754                },
755            });
756
757            // Then the value declaration
758            let h = table.push(Attribute { uuid, data });
759            debug_assert!(h == value_handle);
760
761            // Add optional CCCD handle
762            let cccd_handle = if props.has_cccd() {
763                let handle = table.push(Attribute {
764                    uuid: CLIENT_CHARACTERISTIC_CONFIGURATION.into(),
765                    data: AttributeData::Cccd {
766                        notifications: false,
767                        indications: false,
768                        write_permission: PermissionLevel::Allowed,
769                    },
770                });
771
772                Some(handle)
773            } else {
774                None
775            };
776
777            (value_handle, cccd_handle)
778        });
779
780        CharacteristicBuilder {
781            handle: Characteristic {
782                handle,
783                cccd_handle,
784                props,
785                phantom: PhantomData,
786            },
787            table: self.table,
788        }
789    }
790
791    /// Add a characteristic to this service with a refererence to a mutable storage buffer.
792    pub fn add_characteristic<T: AsGatt, U: Into<Uuid>, P: Into<CharacteristicProps>>(
793        &mut self,
794        uuid: U,
795        props: P,
796        value: T,
797        store: &'d mut [u8],
798    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
799        let props: CharacteristicProps = props.into();
800        let permissions = props.default_permissions();
801        let bytes = value.as_gatt();
802        store[..bytes.len()].copy_from_slice(bytes);
803        let variable_len = T::MAX_SIZE != T::MIN_SIZE;
804        let len = bytes.len() as u16;
805        self.add_characteristic_internal(
806            uuid.into(),
807            props,
808            AttributeData::Data {
809                permissions,
810                value: store,
811                variable_len,
812                len,
813            },
814        )
815    }
816
817    /// Add a characteristic to this service using inline storage. The characteristic value must be 8 bytes or less.
818    pub fn add_characteristic_small<T: AsGatt, U: Into<Uuid>, P: Into<CharacteristicProps>>(
819        &mut self,
820        uuid: U,
821        props: P,
822        value: T,
823    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
824        assert!(T::MIN_SIZE <= 8);
825
826        let props: CharacteristicProps = props.into();
827        let permissions = props.default_permissions();
828        let bytes = value.as_gatt();
829        assert!(bytes.len() <= 8);
830        let mut value = [0; 8];
831        value[..bytes.len()].copy_from_slice(bytes);
832        let variable_len = T::MAX_SIZE != T::MIN_SIZE;
833        let capacity = T::MAX_SIZE.min(8) as u8;
834        let len = bytes.len() as u8;
835        self.add_characteristic_internal(
836            uuid.into(),
837            props,
838            AttributeData::SmallData {
839                permissions,
840                variable_len,
841                capacity,
842                len,
843                value,
844            },
845        )
846    }
847
848    /// Add a characteristic to this service with a refererence to an immutable storage buffer.
849    pub fn add_characteristic_ro<T: AsGatt + ?Sized, U: Into<Uuid>>(
850        &mut self,
851        uuid: U,
852        value: &'d T,
853    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
854        let props: CharacteristicProps = [CharacteristicProp::Read].into();
855        let permissions = props.default_permissions();
856        self.add_characteristic_internal(
857            uuid.into(),
858            props,
859            AttributeData::ReadOnlyData {
860                permissions,
861                value: value.as_gatt(),
862            },
863        )
864    }
865
866    /// Add an included service to this service
867    pub fn add_included_service(&mut self, handle: u16) -> Result<u16, InvalidHandle> {
868        self.table.with_inner(|table| {
869            if handle > 0 && table.attributes.len() >= usize::from(handle) {
870                if let AttributeData::Service {
871                    uuid,
872                    last_handle_in_group,
873                } = &table.attributes[usize::from(handle) - 1].data
874                {
875                    // Included service values only include 16-bit UUIDs per the Bluetooth spec
876                    let uuid = match uuid {
877                        Uuid::Uuid16(uuid) => Some(*uuid),
878                        Uuid::Uuid128(_) => None,
879                    };
880
881                    Ok(table.push(Attribute {
882                        uuid: INCLUDE.into(),
883                        data: AttributeData::IncludedService {
884                            handle,
885                            last_handle_in_group: *last_handle_in_group,
886                            uuid,
887                        },
888                    }))
889                } else {
890                    Err(InvalidHandle)
891                }
892            } else {
893                Err(InvalidHandle)
894            }
895        })
896    }
897
898    /// Finish construction of the service and return a handle.
899    pub fn build(self) -> u16 {
900        self.handle
901    }
902}
903
904impl<M: RawMutex, const MAX: usize> Drop for ServiceBuilder<'_, '_, M, MAX> {
905    fn drop(&mut self) {
906        self.table.with_inner(|inner| {
907            let last_handle = inner.next_handle() - 1;
908
909            let i = usize::from(self.handle - 1);
910            let AttributeData::Service {
911                last_handle_in_group, ..
912            } = &mut inner.attributes[i].data
913            else {
914                unreachable!()
915            };
916
917            *last_handle_in_group = last_handle;
918        });
919    }
920}
921
922/// A characteristic in the attribute table.
923#[cfg_attr(feature = "defmt", derive(defmt::Format))]
924#[derive(Clone, Copy, Debug, PartialEq)]
925pub struct Characteristic<T: AsGatt + ?Sized> {
926    /// Handle value assigned to the Client Characteristic Configuration Descriptor (if any)
927    pub cccd_handle: Option<u16>,
928    /// Handle value assigned to this characteristic when it is added to the Gatt Attribute Table
929    pub handle: u16,
930    /// Properties of this characteristic
931    pub props: CharacteristicProps,
932    pub(crate) phantom: PhantomData<T>,
933}
934
935impl<T: AsGatt + ?Sized> Characteristic<T> {
936    /// Write a value to a characteristic, and notify a connection with the new value of the characteristic.
937    ///
938    /// If the provided connection has not subscribed for this characteristic, it will not be notified.
939    ///
940    /// If the characteristic does not support notifications, an error is returned.
941    pub async fn notify<P: PacketPool>(&self, connection: &GattConnection<'_, '_, P>, value: &T) -> Result<(), Error> {
942        let value = value.as_gatt();
943        let server = connection.server;
944        server.set(self.handle, value)?;
945
946        let cccd_handle = self.cccd_handle.ok_or(Error::NotFound)?;
947        let connection = connection.raw();
948        if !server.should_notify(connection, cccd_handle) {
949            // No reason to fail?
950            return Ok(());
951        }
952
953        let uns = AttUns::Notify {
954            handle: self.handle,
955            data: value,
956        };
957        let pdu = gatt::assemble(connection, crate::att::AttServer::Unsolicited(uns))?;
958        connection.send(pdu).await;
959        Ok(())
960    }
961
962    /// Write a value to a characteristic, and indicate a connection with the new value of the characteristic.
963    ///
964    /// If the provided connection has not subscribed for this characteristic, it will not be sent an indication.
965    ///
966    /// If the characteristic does not support indications, an error is returned.
967    ///
968    /// This function does not block for the confirmation to the indication message, if the client sends a confirmation
969    /// this will be seen on the [GattConnection] as a [crate::att::AttClient::Confirmation] event.
970    pub async fn indicate<P: PacketPool>(
971        &self,
972        connection: &GattConnection<'_, '_, P>,
973        value: &T,
974    ) -> Result<(), Error> {
975        let value = value.as_gatt();
976        let server = connection.server;
977        server.set(self.handle, value)?;
978
979        let cccd_handle = self.cccd_handle.ok_or(Error::NotFound)?;
980        let connection = connection.raw();
981        if !server.should_indicate(connection, cccd_handle) {
982            // No reason to fail?
983            return Ok(());
984        }
985
986        let uns = AttUns::Indicate {
987            handle: self.handle,
988            data: value,
989        };
990        let pdu = gatt::assemble(connection, crate::att::AttServer::Unsolicited(uns))?;
991        connection.send(pdu).await;
992        Ok(())
993    }
994
995    /// Set the value of the characteristic in the provided attribute server.
996    pub fn set<M: RawMutex, P: PacketPool, const AT: usize, const CT: usize, const CN: usize>(
997        &self,
998        server: &AttributeServer<'_, M, P, AT, CT, CN>,
999        value: &T,
1000    ) -> Result<(), Error> {
1001        let value = value.as_gatt();
1002        server.table().set_raw(self.handle, value)?;
1003        Ok(())
1004    }
1005
1006    /// Set the value of the characteristic in the provided attribute server.
1007    pub fn set_ro<'a, M: RawMutex, P: PacketPool, const AT: usize, const CT: usize, const CN: usize>(
1008        &self,
1009        server: &AttributeServer<'a, M, P, AT, CT, CN>,
1010        value: &'a T,
1011    ) -> Result<(), Error> {
1012        let value = value.as_gatt();
1013        server.table().set_ro(self.handle, value)?;
1014        Ok(())
1015    }
1016
1017    /// Read the value of the characteristic.
1018    ///
1019    /// If the characteristic for the handle cannot be found, an error is returned.
1020    ///
1021    pub fn get<M: RawMutex, P: PacketPool, const AT: usize, const CT: usize, const CN: usize>(
1022        &self,
1023        server: &AttributeServer<'_, M, P, AT, CT, CN>,
1024    ) -> Result<T, Error>
1025    where
1026        T: FromGatt,
1027    {
1028        server.table().get(self)
1029    }
1030
1031    /// Returns the attribute handle for the characteristic's client characteristic configuration descriptor (if available)
1032    pub fn cccd_handle(&self) -> Option<CharacteristicPropertiesHandle> {
1033        self.cccd_handle.map(CharacteristicPropertiesHandle)
1034    }
1035
1036    /// Convert this characteristic's type to raw bytes
1037    pub fn to_raw(self) -> Characteristic<[u8]> {
1038        Characteristic {
1039            cccd_handle: self.cccd_handle,
1040            handle: self.handle,
1041            props: self.props,
1042            phantom: PhantomData,
1043        }
1044    }
1045}
1046
1047/// Attribute handle for a characteristic's properties
1048pub struct CharacteristicPropertiesHandle(u16);
1049
1050impl AttributeHandle for CharacteristicPropertiesHandle {
1051    type Value = CharacteristicProps;
1052
1053    fn handle(&self) -> u16 {
1054        self.0
1055    }
1056}
1057
1058/// Builder for characteristics.
1059pub struct CharacteristicBuilder<'r, 'd, T: AsGatt + ?Sized, M: RawMutex, const MAX: usize> {
1060    handle: Characteristic<T>,
1061    table: &'r mut AttributeTable<'d, M, MAX>,
1062}
1063
1064impl<'r, 'd, T: AsGatt + ?Sized, M: RawMutex, const MAX: usize> CharacteristicBuilder<'r, 'd, T, M, MAX> {
1065    fn add_descriptor_internal<DT: AsGatt + ?Sized>(&mut self, uuid: Uuid, data: AttributeData<'d>) -> Descriptor<DT> {
1066        let handle = self.table.push(Attribute { uuid, data });
1067
1068        Descriptor {
1069            handle,
1070            phantom: PhantomData,
1071        }
1072    }
1073
1074    /// Add a characteristic descriptor for this characteristic.
1075    pub fn add_descriptor<DT: AsGatt, U: Into<Uuid>>(
1076        &mut self,
1077        uuid: U,
1078        permissions: AttPermissions,
1079        value: DT,
1080        store: &'d mut [u8],
1081    ) -> Descriptor<DT> {
1082        let bytes = value.as_gatt();
1083        store[..bytes.len()].copy_from_slice(bytes);
1084        let variable_len = DT::MAX_SIZE != DT::MIN_SIZE;
1085        let len = bytes.len() as u16;
1086        self.add_descriptor_internal(
1087            uuid.into(),
1088            AttributeData::Data {
1089                permissions,
1090                value: store,
1091                variable_len,
1092                len,
1093            },
1094        )
1095    }
1096
1097    /// Add a characteristic to this service using inline storage. The descriptor value must be 8 bytes or less.
1098    pub fn add_descriptor_small<DT: AsGatt, U: Into<Uuid>>(
1099        &mut self,
1100        uuid: U,
1101        permissions: AttPermissions,
1102        value: DT,
1103    ) -> Descriptor<DT> {
1104        assert!(DT::MIN_SIZE <= 8);
1105
1106        let bytes = value.as_gatt();
1107        assert!(bytes.len() <= 8);
1108        let mut value = [0; 8];
1109        value[..bytes.len()].copy_from_slice(bytes);
1110        let variable_len = T::MAX_SIZE != T::MIN_SIZE;
1111        let capacity = T::MAX_SIZE.min(8) as u8;
1112        let len = bytes.len() as u8;
1113        self.add_descriptor_internal(
1114            uuid.into(),
1115            AttributeData::SmallData {
1116                permissions,
1117                variable_len,
1118                capacity,
1119                len,
1120                value,
1121            },
1122        )
1123    }
1124
1125    /// Add a read only characteristic descriptor for this characteristic.
1126    pub fn add_descriptor_ro<DT: AsGatt + ?Sized, U: Into<Uuid>>(
1127        &mut self,
1128        uuid: U,
1129        read_permission: PermissionLevel,
1130        data: &'d DT,
1131    ) -> Descriptor<DT> {
1132        let permissions = AttPermissions {
1133            write: PermissionLevel::NotAllowed,
1134            read: read_permission,
1135        };
1136        self.add_descriptor_internal(
1137            uuid.into(),
1138            AttributeData::ReadOnlyData {
1139                permissions,
1140                value: data.as_gatt(),
1141            },
1142        )
1143    }
1144
1145    /// Set the read permission for this characteristic
1146    pub fn read_permission(self, read: PermissionLevel) -> Self {
1147        self.table.with_attribute(self.handle.handle, |att| {
1148            let permissions = match &mut att.data {
1149                AttributeData::Data { permissions, .. }
1150                | AttributeData::SmallData { permissions, .. }
1151                | AttributeData::ReadOnlyData { permissions, .. } => permissions,
1152                _ => unreachable!(),
1153            };
1154
1155            permissions.read = read;
1156        });
1157
1158        self
1159    }
1160
1161    /// Set the write permission for this characteristic
1162    pub fn write_permission(self, write: PermissionLevel) -> Self {
1163        self.table.with_attribute(self.handle.handle, |att| {
1164            let permissions = match &mut att.data {
1165                AttributeData::Data { permissions, .. }
1166                | AttributeData::SmallData { permissions, .. }
1167                | AttributeData::ReadOnlyData { permissions, .. } => permissions,
1168                _ => unreachable!(),
1169            };
1170
1171            permissions.write = write;
1172        });
1173
1174        self
1175    }
1176
1177    /// Set the write permission for the Client Characteristic Configuration Descriptor for this characteristic
1178    ///
1179    /// Panics if this characteristic does not have a Client Characteristic Configuration Descriptor.
1180    pub fn cccd_permission(self, write: PermissionLevel) -> Self {
1181        let Some(handle) = self.handle.cccd_handle else {
1182            panic!("Can't set CCCD permission on characteristics without notify or indicate properties.");
1183        };
1184
1185        self.table.with_attribute(handle, |att| {
1186            let permission = match &mut att.data {
1187                AttributeData::Cccd { write_permission, .. } => write_permission,
1188                _ => unreachable!(),
1189            };
1190
1191            *permission = write;
1192        });
1193
1194        self
1195    }
1196
1197    /// Convert this characteristic's type to raw bytes
1198    pub fn to_raw(self) -> CharacteristicBuilder<'r, 'd, [u8], M, MAX> {
1199        CharacteristicBuilder {
1200            handle: self.handle.to_raw(),
1201            table: self.table,
1202        }
1203    }
1204    /// Return the built characteristic.
1205    pub fn build(self) -> Characteristic<T> {
1206        self.handle
1207    }
1208}
1209
1210/// Characteristic descriptor handle.
1211#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1212#[derive(Clone, Copy, Debug)]
1213pub struct Descriptor<T: AsGatt + ?Sized> {
1214    pub(crate) handle: u16,
1215    phantom: PhantomData<T>,
1216}
1217
1218impl<T: AsGatt> AttributeHandle for Descriptor<T> {
1219    type Value = T;
1220
1221    fn handle(&self) -> u16 {
1222        self.handle
1223    }
1224}
1225
1226impl<T: AsGatt + ?Sized> Descriptor<T> {
1227    /// Set the value of the descriptor in the provided attribute server.
1228    pub fn set<M: RawMutex, P: PacketPool, const AT: usize, const CT: usize, const CN: usize>(
1229        &self,
1230        server: &AttributeServer<'_, M, P, AT, CT, CN>,
1231        value: &T,
1232    ) -> Result<(), Error> {
1233        let value = value.as_gatt();
1234        server.table().set_raw(self.handle, value)?;
1235        Ok(())
1236    }
1237
1238    /// Read the value of the descriptor.
1239    ///
1240    /// If the descriptor for the handle cannot be found, an error is returned.
1241    ///
1242    pub fn get<M: RawMutex, P: PacketPool, const AT: usize, const CT: usize, const CN: usize>(
1243        &self,
1244        server: &AttributeServer<'_, M, P, AT, CT, CN>,
1245    ) -> Result<T, Error>
1246    where
1247        T: FromGatt,
1248    {
1249        server.table().get(self)
1250    }
1251}
1252
1253/// Iterator over attributes.
1254pub struct AttributeIterator<'a, 'd> {
1255    attributes: &'a mut [Attribute<'d>],
1256    pos: usize,
1257}
1258
1259impl<'d> AttributeIterator<'_, 'd> {
1260    /// Return next attribute in iterator.
1261    pub fn next<'m>(&'m mut self) -> Option<(u16, &'m mut Attribute<'d>)> {
1262        if self.pos < self.attributes.len() {
1263            let att = &mut self.attributes[self.pos];
1264            self.pos += 1;
1265            let handle = self.pos as u16;
1266            Some((handle, att))
1267        } else {
1268            None
1269        }
1270    }
1271}
1272
1273/// A GATT service.
1274pub struct Service {
1275    /// UUID of the service.
1276    pub uuid: Uuid,
1277}
1278
1279impl Service {
1280    /// Create a new service with a uuid.
1281    pub fn new<U: Into<Uuid>>(uuid: U) -> Self {
1282        Self { uuid: uuid.into() }
1283    }
1284}
1285
1286/// Properties of a characteristic.
1287#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1288#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1289pub struct CharacteristicProps(u8);
1290
1291impl<'a> From<&'a [CharacteristicProp]> for CharacteristicProps {
1292    fn from(props: &'a [CharacteristicProp]) -> Self {
1293        let mut val: u8 = 0;
1294        for prop in props {
1295            val |= *prop as u8;
1296        }
1297        CharacteristicProps(val)
1298    }
1299}
1300
1301impl<'a, const N: usize> From<&'a [CharacteristicProp; N]> for CharacteristicProps {
1302    fn from(props: &'a [CharacteristicProp; N]) -> Self {
1303        let mut val: u8 = 0;
1304        for prop in props {
1305            val |= *prop as u8;
1306        }
1307        CharacteristicProps(val)
1308    }
1309}
1310
1311impl<const N: usize> From<[CharacteristicProp; N]> for CharacteristicProps {
1312    fn from(props: [CharacteristicProp; N]) -> Self {
1313        let mut val: u8 = 0;
1314        for prop in props {
1315            val |= prop as u8;
1316        }
1317        CharacteristicProps(val)
1318    }
1319}
1320
1321impl From<u8> for CharacteristicProps {
1322    fn from(value: u8) -> Self {
1323        Self(value)
1324    }
1325}
1326
1327impl CharacteristicProps {
1328    /// Check if any of the properties are set.
1329    pub fn any(&self, props: &[CharacteristicProp]) -> bool {
1330        for p in props {
1331            if (*p as u8) & self.0 != 0 {
1332                return true;
1333            }
1334        }
1335        false
1336    }
1337
1338    pub(crate) fn default_permissions(&self) -> AttPermissions {
1339        let read = if (self.0 & CharacteristicProp::Read as u8) != 0 {
1340            PermissionLevel::Allowed
1341        } else {
1342            PermissionLevel::NotAllowed
1343        };
1344
1345        let write = if (self.0
1346            & (CharacteristicProp::Write as u8
1347                | CharacteristicProp::WriteWithoutResponse as u8
1348                | CharacteristicProp::AuthenticatedWrite as u8))
1349            != 0
1350        {
1351            PermissionLevel::Allowed
1352        } else {
1353            PermissionLevel::NotAllowed
1354        };
1355
1356        AttPermissions { read, write }
1357    }
1358
1359    /// Check if the characteristic will have a Client Characteristic Configuration Descriptor
1360    pub fn has_cccd(&self) -> bool {
1361        (self.0 & (CharacteristicProp::Indicate as u8 | CharacteristicProp::Notify as u8)) != 0
1362    }
1363
1364    /// Get the raw value of the characteristic props
1365    pub fn to_raw(self) -> u8 {
1366        self.0
1367    }
1368}
1369
1370impl FixedGattValue for CharacteristicProps {
1371    const SIZE: usize = 1;
1372}
1373
1374impl FromGatt for CharacteristicProps {
1375    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
1376        if data.len() != Self::SIZE {
1377            return Err(FromGattError::InvalidLength);
1378        }
1379
1380        Ok(CharacteristicProps(data[0]))
1381    }
1382}
1383
1384impl AsGatt for CharacteristicProps {
1385    const MIN_SIZE: usize = Self::SIZE;
1386    const MAX_SIZE: usize = Self::SIZE;
1387
1388    fn as_gatt(&self) -> &[u8] {
1389        AsGatt::as_gatt(&self.0)
1390    }
1391}
1392
1393/// A value of an attribute.
1394pub struct AttributeValue<'d, M: RawMutex> {
1395    value: Mutex<M, &'d mut [u8]>,
1396}
1397
1398impl<M: RawMutex> AttributeValue<'_, M> {}
1399
1400/// CCCD flag values.
1401#[derive(Clone, Copy)]
1402pub enum CCCDFlag {
1403    /// Notifications enabled.
1404    Notify = 0x1,
1405    /// Indications enabled.
1406    Indicate = 0x2,
1407}
1408
1409/// CCCD flag.
1410#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1411#[derive(Clone, Copy, Default, Debug, PartialEq)]
1412pub struct CCCD(pub(crate) u16);
1413
1414impl<const T: usize> From<[CCCDFlag; T]> for CCCD {
1415    fn from(props: [CCCDFlag; T]) -> Self {
1416        let mut val: u16 = 0;
1417        for prop in props {
1418            val |= prop as u16;
1419        }
1420        CCCD(val)
1421    }
1422}
1423
1424impl From<u16> for CCCD {
1425    fn from(value: u16) -> Self {
1426        CCCD(value)
1427    }
1428}
1429
1430impl CCCD {
1431    /// Get raw value
1432    pub fn raw(&self) -> u16 {
1433        self.0
1434    }
1435
1436    /// Clear all properties
1437    pub fn disable(&mut self) {
1438        self.0 = 0;
1439    }
1440
1441    /// Check if any of the properties are set.
1442    pub fn any(&self, props: &[CCCDFlag]) -> bool {
1443        for p in props {
1444            if (*p as u16) & self.0 != 0 {
1445                return true;
1446            }
1447        }
1448        false
1449    }
1450
1451    /// Enable or disable notifications
1452    pub fn set_notify(&mut self, is_enabled: bool) {
1453        let mask: u16 = CCCDFlag::Notify as u16;
1454        self.0 = if is_enabled { self.0 | mask } else { self.0 & !mask };
1455    }
1456
1457    /// Check if notifications are enabled
1458    pub fn should_notify(&self) -> bool {
1459        (self.0 & (CCCDFlag::Notify as u16)) != 0
1460    }
1461
1462    /// Enable or disable indication
1463    pub fn set_indicate(&mut self, is_enabled: bool) {
1464        let mask: u16 = CCCDFlag::Indicate as u16;
1465        self.0 = if is_enabled { self.0 | mask } else { self.0 & !mask };
1466    }
1467
1468    /// Check if indications are enabled
1469    pub fn should_indicate(&self) -> bool {
1470        (self.0 & (CCCDFlag::Indicate as u16)) != 0
1471    }
1472}
1473
1474#[cfg(test)]
1475mod tests {
1476    extern crate std;
1477
1478    #[cfg(feature = "security")]
1479    #[test]
1480    fn database_hash() {
1481        use bt_hci::uuid::characteristic::{
1482            APPEARANCE, CLIENT_SUPPORTED_FEATURES, DATABASE_HASH, DEVICE_NAME, SERVICE_CHANGED,
1483        };
1484        use bt_hci::uuid::declarations::{CHARACTERISTIC, PRIMARY_SERVICE};
1485        use bt_hci::uuid::descriptors::{
1486            CHARACTERISTIC_PRESENTATION_FORMAT, CHARACTERISTIC_USER_DESCRIPTION, CLIENT_CHARACTERISTIC_CONFIGURATION,
1487        };
1488        use bt_hci::uuid::service::{GAP, GATT};
1489        use embassy_sync::blocking_mutex::raw::NoopRawMutex;
1490
1491        use super::*;
1492
1493        // The raw message data that should be hashed for this attribute table is:
1494        //
1495        // 0100 0028 0018
1496        // 0200 0328 020300002a
1497        // 0400 0328 020500012a
1498        //
1499        // 0600 0028 0118
1500        // 0700 0328 200800052a
1501        // 0900 0229
1502        // 0a00 0328 0a0b00292b
1503        // 0c00 0328 020d002a2b
1504        //
1505        // 0e00 0028 f0debc9a785634127856341278563412
1506        // 0f00 0328 121000f1debc9a785634127856341278563412
1507        // 1100 0229
1508        // 1200 0129
1509        // 1300 0429
1510        //
1511        // The message hash can be calculated on the command line with:
1512        // > xxd -plain -revert message.txt message.bin
1513        // > openssl mac -cipher AES-128-CBC -macopt hexkey:00000000000000000000000000000000 -in message.bin CMAC
1514
1515        let mut table: AttributeTable<'static, NoopRawMutex, 20> = AttributeTable::new();
1516
1517        // GAP service (handles 0x001 - 0x005)
1518        table.push(Attribute::new(
1519            PRIMARY_SERVICE.into(),
1520            AttributeData::Service {
1521                uuid: GAP.into(),
1522                last_handle_in_group: 0x05,
1523            },
1524        ));
1525
1526        let expected = 0xd4cdec10804db3f147b4d7d10baa0120;
1527        let actual = table.hash();
1528        assert_eq!(
1529            actual, expected,
1530            "\nexpected: {:#032x}\nactual: {:#032x}",
1531            expected, actual
1532        );
1533
1534        // Device name characteristic
1535        table.push(Attribute::new(
1536            CHARACTERISTIC.into(),
1537            AttributeData::Declaration {
1538                props: [CharacteristicProp::Read].as_slice().into(),
1539                handle: 0x0003,
1540                uuid: DEVICE_NAME.into(),
1541            },
1542        ));
1543
1544        table.push(Attribute::new(
1545            DEVICE_NAME.into(),
1546            AttributeData::ReadOnlyData {
1547                permissions: AttPermissions {
1548                    read: PermissionLevel::Allowed,
1549                    write: PermissionLevel::NotAllowed,
1550                },
1551                value: b"",
1552            },
1553        ));
1554
1555        // Appearance characteristic
1556        table.push(Attribute::new(
1557            CHARACTERISTIC.into(),
1558            AttributeData::Declaration {
1559                props: [CharacteristicProp::Read].as_slice().into(),
1560                handle: 0x0005,
1561                uuid: APPEARANCE.into(),
1562            },
1563        ));
1564
1565        table.push(Attribute::new(
1566            APPEARANCE.into(),
1567            AttributeData::ReadOnlyData {
1568                permissions: AttPermissions {
1569                    read: PermissionLevel::Allowed,
1570                    write: PermissionLevel::NotAllowed,
1571                },
1572                value: b"",
1573            },
1574        ));
1575
1576        let expected = 0x6c329e3f1d52c03f174980f6b4704875;
1577        let actual = table.hash();
1578        assert_eq!(
1579            actual, expected,
1580            "\nexpected: {:#032x}\n  actual: {:#032x}",
1581            expected, actual
1582        );
1583
1584        // GATT service (handles 0x006 - 0x000d)
1585        table.push(Attribute::new(
1586            PRIMARY_SERVICE.into(),
1587            AttributeData::Service {
1588                uuid: GATT.into(),
1589                last_handle_in_group: 0x0d,
1590            },
1591        ));
1592
1593        // Service changed characteristic
1594        table.push(Attribute::new(
1595            CHARACTERISTIC.into(),
1596            AttributeData::Declaration {
1597                props: [CharacteristicProp::Indicate].as_slice().into(),
1598                handle: 0x0008,
1599                uuid: SERVICE_CHANGED.into(),
1600            },
1601        ));
1602
1603        table.push(Attribute::new(
1604            SERVICE_CHANGED.into(),
1605            AttributeData::ReadOnlyData {
1606                permissions: AttPermissions {
1607                    read: PermissionLevel::Allowed,
1608                    write: PermissionLevel::NotAllowed,
1609                },
1610                value: b"",
1611            },
1612        ));
1613
1614        table.push(Attribute::new(
1615            CLIENT_CHARACTERISTIC_CONFIGURATION.into(),
1616            AttributeData::Cccd {
1617                notifications: false,
1618                indications: false,
1619                write_permission: PermissionLevel::Allowed,
1620            },
1621        ));
1622
1623        // Client supported features characteristic
1624        table.push(Attribute::new(
1625            CHARACTERISTIC.into(),
1626            AttributeData::Declaration {
1627                props: [CharacteristicProp::Read, CharacteristicProp::Write].as_slice().into(),
1628                handle: 0x000b,
1629                uuid: CLIENT_SUPPORTED_FEATURES.into(),
1630            },
1631        ));
1632
1633        table.push(Attribute::new(
1634            CLIENT_SUPPORTED_FEATURES.into(),
1635            AttributeData::ReadOnlyData {
1636                permissions: AttPermissions {
1637                    read: PermissionLevel::Allowed,
1638                    write: PermissionLevel::NotAllowed,
1639                },
1640                value: b"",
1641            },
1642        ));
1643
1644        // Database hash characteristic
1645        table.push(Attribute::new(
1646            CHARACTERISTIC.into(),
1647            AttributeData::Declaration {
1648                props: [CharacteristicProp::Read].as_slice().into(),
1649                handle: 0x000d,
1650                uuid: DATABASE_HASH.into(),
1651            },
1652        ));
1653
1654        table.push(Attribute::new(
1655            DATABASE_HASH.into(),
1656            AttributeData::ReadOnlyData {
1657                permissions: AttPermissions {
1658                    read: PermissionLevel::Allowed,
1659                    write: PermissionLevel::NotAllowed,
1660                },
1661                value: b"",
1662            },
1663        ));
1664
1665        let expected = 0x16ce756326c5062bf74022f845c2b21f;
1666        let actual = table.hash();
1667        assert_eq!(
1668            actual, expected,
1669            "\nexpected: {:#032x}\n  actual: {:#032x}",
1670            expected, actual
1671        );
1672
1673        const CUSTOM_SERVICE: u128 = 0x12345678_12345678_12345678_9abcdef0;
1674        const CUSTOM_CHARACTERISTIC: u128 = 0x12345678_12345678_12345678_9abcdef1;
1675
1676        // Custom service (handles 0x00e - 0x0013)
1677        table.push(Attribute::new(
1678            PRIMARY_SERVICE.into(),
1679            AttributeData::Service {
1680                uuid: CUSTOM_SERVICE.into(),
1681                last_handle_in_group: 0x13,
1682            },
1683        ));
1684
1685        // Custom characteristic
1686        table.push(Attribute::new(
1687            CHARACTERISTIC.into(),
1688            AttributeData::Declaration {
1689                props: [CharacteristicProp::Notify, CharacteristicProp::Read].as_slice().into(),
1690                handle: 0x0010,
1691                uuid: CUSTOM_CHARACTERISTIC.into(),
1692            },
1693        ));
1694
1695        table.push(Attribute::new(
1696            CUSTOM_CHARACTERISTIC.into(),
1697            AttributeData::ReadOnlyData {
1698                permissions: AttPermissions {
1699                    read: PermissionLevel::Allowed,
1700                    write: PermissionLevel::NotAllowed,
1701                },
1702                value: b"",
1703            },
1704        ));
1705
1706        table.push(Attribute::new(
1707            CLIENT_CHARACTERISTIC_CONFIGURATION.into(),
1708            AttributeData::Cccd {
1709                notifications: false,
1710                indications: false,
1711                write_permission: PermissionLevel::Allowed,
1712            },
1713        ));
1714
1715        table.push(Attribute::new(
1716            CHARACTERISTIC_USER_DESCRIPTION.into(),
1717            AttributeData::ReadOnlyData {
1718                permissions: AttPermissions {
1719                    read: PermissionLevel::Allowed,
1720                    write: PermissionLevel::NotAllowed,
1721                },
1722                value: b"Custom Characteristic",
1723            },
1724        ));
1725
1726        table.push(Attribute::new(
1727            CHARACTERISTIC_PRESENTATION_FORMAT.into(),
1728            AttributeData::ReadOnlyData {
1729                permissions: AttPermissions {
1730                    read: PermissionLevel::Allowed,
1731                    write: PermissionLevel::NotAllowed,
1732                },
1733                value: &[4, 0, 0, 0x27, 1, 0, 0],
1734            },
1735        ));
1736
1737        let expected = 0xc7352cced28d6608d4b057d247d8be76;
1738        let actual = table.hash();
1739        assert_eq!(
1740            actual, expected,
1741            "\nexpected: {:#032x}\n  actual: {:#032x}",
1742            expected, actual
1743        );
1744    }
1745}