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, PRIMARY_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::gatt;
14
15use crate::attribute_server::AttributeServer;
16use crate::cursor::{ReadCursor, WriteCursor};
17use crate::prelude::{AsGatt, FixedGattValue, FromGatt, GattConnection};
18use crate::types::gatt_traits::FromGattError;
19pub use crate::types::uuid::Uuid;
20use crate::{Error, PacketPool, MAX_INVALID_DATA_LEN};
21
22/// Characteristic properties
23#[derive(Debug, Clone, Copy)]
24#[repr(u8)]
25pub enum CharacteristicProp {
26    /// Broadcast
27    Broadcast = 0x01,
28    /// Read
29    Read = 0x02,
30    /// Write without response
31    WriteWithoutResponse = 0x04,
32    /// Write
33    Write = 0x08,
34    /// Notify
35    Notify = 0x10,
36    /// Indicate
37    Indicate = 0x20,
38    /// Authenticated writes
39    AuthenticatedWrite = 0x40,
40    /// Extended properties
41    Extended = 0x80,
42}
43
44/// Attribute metadata.
45pub struct Attribute<'a> {
46    pub(crate) uuid: Uuid,
47    pub(crate) handle: u16,
48    pub(crate) last_handle_in_group: u16,
49    pub(crate) data: AttributeData<'a>,
50}
51
52impl<'a> Attribute<'a> {
53    const EMPTY: Option<Attribute<'a>> = None;
54
55    pub(crate) fn read(&self, offset: usize, data: &mut [u8]) -> Result<usize, AttErrorCode> {
56        if !self.data.readable() {
57            return Err(AttErrorCode::READ_NOT_PERMITTED);
58        }
59        self.data.read(offset, data)
60    }
61
62    pub(crate) fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
63        if !self.data.writable() {
64            return Err(AttErrorCode::WRITE_NOT_PERMITTED);
65        }
66
67        self.data.write(offset, data)
68    }
69}
70
71pub(crate) enum AttributeData<'d> {
72    Service {
73        uuid: Uuid,
74    },
75    ReadOnlyData {
76        props: CharacteristicProps,
77        value: &'d [u8],
78    },
79    Data {
80        props: CharacteristicProps,
81        variable_len: bool,
82        len: u16,
83        value: &'d mut [u8],
84    },
85    Declaration {
86        props: CharacteristicProps,
87        handle: u16,
88        uuid: Uuid,
89    },
90    Cccd {
91        notifications: bool,
92        indications: bool,
93    },
94}
95
96impl AttributeData<'_> {
97    pub(crate) fn readable(&self) -> bool {
98        match self {
99            Self::Data { props, .. } => props.0 & (CharacteristicProp::Read as u8) != 0,
100            _ => true,
101        }
102    }
103
104    pub(crate) fn writable(&self) -> bool {
105        match self {
106            Self::Data { props, .. } => {
107                props.0
108                    & (CharacteristicProp::Write as u8
109                        | CharacteristicProp::WriteWithoutResponse as u8
110                        | CharacteristicProp::AuthenticatedWrite as u8)
111                    != 0
112            }
113            Self::Cccd {
114                notifications,
115                indications,
116            } => true,
117            _ => false,
118        }
119    }
120
121    fn read(&self, offset: usize, data: &mut [u8]) -> Result<usize, AttErrorCode> {
122        if !self.readable() {
123            return Err(AttErrorCode::READ_NOT_PERMITTED);
124        }
125        match self {
126            Self::ReadOnlyData { props, value } => {
127                if offset > value.len() {
128                    return Ok(0);
129                }
130                let len = data.len().min(value.len() - offset);
131                if len > 0 {
132                    data[..len].copy_from_slice(&value[offset..offset + len]);
133                }
134                Ok(len)
135            }
136            Self::Data {
137                props,
138                value,
139                variable_len,
140                len,
141            } => {
142                let value = &value[..*len as usize];
143                if offset > value.len() {
144                    return Ok(0);
145                }
146                let len = data.len().min(value.len() - offset);
147                if len > 0 {
148                    data[..len].copy_from_slice(&value[offset..offset + len]);
149                }
150                Ok(len)
151            }
152            Self::Service { uuid } => {
153                let val = uuid.as_raw();
154                if offset > val.len() {
155                    return Ok(0);
156                }
157                let len = data.len().min(val.len() - offset);
158                if len > 0 {
159                    data[..len].copy_from_slice(&val[offset..offset + len]);
160                }
161                Ok(len)
162            }
163            Self::Cccd {
164                notifications,
165                indications,
166            } => {
167                if offset > 0 {
168                    return Err(AttErrorCode::INVALID_OFFSET);
169                }
170                if data.len() < 2 {
171                    return Err(AttErrorCode::UNLIKELY_ERROR);
172                }
173                let mut v = 0;
174                if *notifications {
175                    v |= 0x01;
176                }
177
178                if *indications {
179                    v |= 0x02;
180                }
181                data[0] = v;
182                Ok(2)
183            }
184            Self::Declaration { props, handle, uuid } => {
185                let val = uuid.as_raw();
186                if offset > val.len() + 3 {
187                    return Ok(0);
188                }
189                let mut w = WriteCursor::new(data);
190                if offset == 0 {
191                    w.write(props.0)?;
192                    w.write(*handle)?;
193                } else if offset == 1 {
194                    w.write(*handle)?;
195                } else if offset == 2 {
196                    w.write(handle.to_le_bytes()[1])?;
197                }
198
199                let to_write = w.available().min(val.len());
200
201                if to_write > 0 {
202                    w.append(&val[..to_write])?;
203                }
204                Ok(w.len())
205            }
206        }
207    }
208
209    fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
210        let writable = self.writable();
211
212        match self {
213            Self::Data {
214                value,
215                props,
216                variable_len,
217                len,
218            } => {
219                if !writable {
220                    return Err(AttErrorCode::WRITE_NOT_PERMITTED);
221                }
222
223                if offset + data.len() <= value.len() {
224                    value[offset..offset + data.len()].copy_from_slice(data);
225                    *len = (offset + data.len()) as u16;
226                    Ok(())
227                } else {
228                    Err(AttErrorCode::INVALID_OFFSET)
229                }
230            }
231            Self::Cccd {
232                notifications,
233                indications,
234            } => {
235                if offset > 0 {
236                    return Err(AttErrorCode::INVALID_OFFSET);
237                }
238
239                if data.is_empty() {
240                    return Err(AttErrorCode::UNLIKELY_ERROR);
241                }
242
243                *notifications = data[0] & 0x01 != 0;
244                *indications = data[0] & 0x02 != 0;
245                Ok(())
246            }
247            _ => Err(AttErrorCode::WRITE_NOT_PERMITTED),
248        }
249    }
250
251    pub(crate) fn decode_declaration(data: &[u8]) -> Result<Self, Error> {
252        let mut r = ReadCursor::new(data);
253        Ok(Self::Declaration {
254            props: CharacteristicProps(r.read()?),
255            handle: r.read()?,
256            uuid: Uuid::try_from(r.remaining())?,
257        })
258    }
259}
260
261impl fmt::Debug for Attribute<'_> {
262    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263        f.debug_struct("Attribute")
264            .field("uuid", &self.uuid)
265            .field("handle", &self.handle)
266            .field("last_handle_in_group", &self.last_handle_in_group)
267            .field("readable", &self.data.readable())
268            .field("writable", &self.data.writable())
269            .finish()
270    }
271}
272
273#[cfg(feature = "defmt")]
274impl<'a> defmt::Format for Attribute<'a> {
275    fn format(&self, fmt: defmt::Formatter) {
276        defmt::write!(fmt, "{}", defmt::Debug2Format(self))
277    }
278}
279
280impl<'a> Attribute<'a> {
281    pub(crate) fn new(uuid: Uuid, data: AttributeData<'a>) -> Attribute<'a> {
282        Attribute {
283            uuid,
284            handle: 0,
285            data,
286            last_handle_in_group: 0xffff,
287        }
288    }
289}
290
291/// A table of attributes.
292pub struct AttributeTable<'d, M: RawMutex, const MAX: usize> {
293    inner: Mutex<M, RefCell<InnerTable<'d, MAX>>>,
294    handle: u16,
295}
296
297pub(crate) struct InnerTable<'d, const MAX: usize> {
298    attributes: Vec<Attribute<'d>, MAX>,
299}
300
301impl<'d, const MAX: usize> InnerTable<'d, MAX> {
302    fn push(&mut self, attribute: Attribute<'d>) {
303        self.attributes.push(attribute).unwrap();
304    }
305}
306
307impl<M: RawMutex, const MAX: usize> Default for AttributeTable<'_, M, MAX> {
308    fn default() -> Self {
309        Self::new()
310    }
311}
312
313impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> {
314    /// Create a new GATT table.
315    pub fn new() -> Self {
316        Self {
317            handle: 1,
318            inner: Mutex::new(RefCell::new(InnerTable { attributes: Vec::new() })),
319        }
320    }
321
322    pub(crate) fn with_inner<F: Fn(&mut InnerTable<'d, MAX>)>(&self, f: F) {
323        self.inner.lock(|inner| {
324            let mut table = inner.borrow_mut();
325            f(&mut table);
326        })
327    }
328
329    pub(crate) fn iterate<F: FnMut(AttributeIterator<'_, 'd>) -> R, R>(&self, mut f: F) -> R {
330        self.inner.lock(|inner| {
331            let mut table = inner.borrow_mut();
332            let it = AttributeIterator {
333                attributes: &mut table.attributes[..],
334                pos: 0,
335            };
336            f(it)
337        })
338    }
339
340    fn push(&mut self, mut attribute: Attribute<'d>) -> u16 {
341        let handle = self.handle;
342        attribute.handle = handle;
343        self.inner.lock(|inner| {
344            let mut inner = inner.borrow_mut();
345            inner.push(attribute);
346        });
347        self.handle += 1;
348        handle
349    }
350
351    /// Add a service to the attribute table (group of characteristics)
352    pub fn add_service(&mut self, service: Service) -> ServiceBuilder<'_, 'd, M, MAX> {
353        let len = self.inner.lock(|i| i.borrow().attributes.len());
354        let handle = self.handle;
355        self.push(Attribute {
356            uuid: PRIMARY_SERVICE.into(),
357            handle: 0,
358            last_handle_in_group: 0,
359            data: AttributeData::Service { uuid: service.uuid },
360        });
361        ServiceBuilder {
362            handle,
363            start: len,
364            table: self,
365        }
366    }
367
368    pub(crate) fn set_raw(&self, attribute: u16, input: &[u8]) -> Result<(), Error> {
369        self.iterate(|mut it| {
370            while let Some(att) = it.next() {
371                if att.handle == attribute {
372                    if let AttributeData::Data {
373                        props: _,
374                        value,
375                        variable_len,
376                        len,
377                    } = &mut att.data
378                    {
379                        let expected_len = value.len();
380                        let actual_len = input.len();
381
382                        if expected_len == actual_len {
383                            value.copy_from_slice(input);
384                            return Ok(());
385                        } else if *variable_len && actual_len <= expected_len {
386                            value[..input.len()].copy_from_slice(input);
387                            *len = input.len() as u16;
388                            return Ok(());
389                        } else {
390                            return Err(Error::UnexpectedDataLength {
391                                expected: expected_len,
392                                actual: actual_len,
393                            });
394                        }
395                    }
396                }
397            }
398            Err(Error::NotFound)
399        })
400    }
401
402    /// Set the value of a characteristic
403    ///
404    /// The provided data must exactly match the size of the storage for the characteristic,
405    /// otherwise this function will panic.
406    ///
407    /// If the characteristic for the handle cannot be found, or the shape of the data does not match the type of the characterstic,
408    /// an error is returned
409    pub fn set<T: AttributeHandle>(&self, attribute_handle: &T, input: &T::Value) -> Result<(), Error> {
410        let gatt_value = input.as_gatt();
411        self.set_raw(attribute_handle.handle(), gatt_value)
412    }
413
414    /// Read the value of the characteristic and pass the value to the provided closure.
415    ///
416    /// The return value of the closure is returned in this function and is assumed to be infallible.
417    ///
418    /// If the characteristic for the handle cannot be found, an error is returned.
419    pub fn get<T: AttributeHandle<Value = V>, V: FromGatt>(&self, attribute_handle: &T) -> Result<T::Value, Error> {
420        self.iterate(|mut it| {
421            while let Some(att) = it.next() {
422                if att.handle == attribute_handle.handle() {
423                    if let AttributeData::Data {
424                        props,
425                        value,
426                        variable_len,
427                        len,
428                    } = &mut att.data
429                    {
430                        let value_slice = if *variable_len { &value[..*len as usize] } else { value };
431
432                        match T::Value::from_gatt(value_slice) {
433                            Ok(v) => return Ok(v),
434                            Err(_) => {
435                                let mut invalid_data = [0u8; MAX_INVALID_DATA_LEN];
436                                let len_to_copy = value_slice.len().min(MAX_INVALID_DATA_LEN);
437                                invalid_data[..len_to_copy].copy_from_slice(&value_slice[..len_to_copy]);
438
439                                return Err(Error::CannotConstructGattValue(invalid_data));
440                            }
441                        }
442                    }
443                }
444            }
445            Err(Error::NotFound)
446        })
447    }
448
449    /// Return the characteristic which corresponds to the supplied value handle
450    ///
451    /// If no characteristic corresponding to the given value handle was found, returns an error
452    pub fn find_characteristic_by_value_handle<T: AsGatt>(&self, handle: u16) -> Result<Characteristic<T>, Error> {
453        self.iterate(|mut it| {
454            while let Some(att) = it.next() {
455                if att.handle == handle {
456                    // If next is CCCD
457                    if let Some(next) = it.next() {
458                        if let AttributeData::Cccd {
459                            notifications: _,
460                            indications: _,
461                        } = &next.data
462                        {
463                            return Ok(Characteristic {
464                                handle,
465                                cccd_handle: Some(next.handle),
466                                phantom: PhantomData,
467                            });
468                        } else {
469                            return Ok(Characteristic {
470                                handle,
471                                cccd_handle: None,
472                                phantom: PhantomData,
473                            });
474                        }
475                    } else {
476                        return Ok(Characteristic {
477                            handle,
478                            cccd_handle: None,
479                            phantom: PhantomData,
480                        });
481                    }
482                }
483            }
484            Err(Error::NotFound)
485        })
486    }
487}
488
489/// A type which holds a handle to an attribute in the attribute table
490pub trait AttributeHandle {
491    /// The data type which the attribute contains
492    type Value: AsGatt;
493
494    /// Returns the attribute's handle
495    fn handle(&self) -> u16;
496}
497
498impl<T: AsGatt> AttributeHandle for Characteristic<T> {
499    type Value = T;
500
501    fn handle(&self) -> u16 {
502        self.handle
503    }
504}
505
506/// Builder for constructing GATT service definitions.
507pub struct ServiceBuilder<'r, 'd, M: RawMutex, const MAX: usize> {
508    handle: u16,
509    start: usize,
510    table: &'r mut AttributeTable<'d, M, MAX>,
511}
512
513impl<'d, M: RawMutex, const MAX: usize> ServiceBuilder<'_, 'd, M, MAX> {
514    fn add_characteristic_internal<T: AsGatt>(
515        &mut self,
516        uuid: Uuid,
517        props: CharacteristicProps,
518        data: AttributeData<'d>,
519    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
520        // First the characteristic declaration
521        let next = self.table.handle + 1;
522        let cccd = self.table.handle + 2;
523        self.table.push(Attribute {
524            uuid: CHARACTERISTIC.into(),
525            handle: 0,
526            last_handle_in_group: 0,
527            data: AttributeData::Declaration {
528                props,
529                handle: next,
530                uuid: uuid.clone(),
531            },
532        });
533
534        // Then the value declaration
535        self.table.push(Attribute {
536            uuid,
537            handle: 0,
538            last_handle_in_group: 0,
539            data,
540        });
541
542        // Add optional CCCD handle
543        let cccd_handle = if props.any(&[CharacteristicProp::Notify, CharacteristicProp::Indicate]) {
544            self.table.push(Attribute {
545                uuid: CLIENT_CHARACTERISTIC_CONFIGURATION.into(),
546                handle: 0,
547                last_handle_in_group: 0,
548                data: AttributeData::Cccd {
549                    notifications: false,
550                    indications: false,
551                },
552            });
553            Some(cccd)
554        } else {
555            None
556        };
557
558        CharacteristicBuilder {
559            handle: Characteristic {
560                handle: next,
561                cccd_handle,
562                phantom: PhantomData,
563            },
564            table: self.table,
565        }
566    }
567
568    /// Add a characteristic to this service with a refererence to a mutable storage buffer.
569    pub fn add_characteristic<T: AsGatt, U: Into<Uuid>>(
570        &mut self,
571        uuid: U,
572        props: &[CharacteristicProp],
573        value: T,
574        store: &'d mut [u8],
575    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
576        let props = props.into();
577        let bytes = value.as_gatt();
578        store[..bytes.len()].copy_from_slice(bytes);
579        let variable_len = T::MAX_SIZE != T::MIN_SIZE;
580        let len = bytes.len() as u16;
581        self.add_characteristic_internal(
582            uuid.into(),
583            props,
584            AttributeData::Data {
585                props,
586                value: store,
587                variable_len,
588                len,
589            },
590        )
591    }
592
593    /// Add a characteristic to this service with a refererence to an immutable storage buffer.
594    pub fn add_characteristic_ro<T: AsGatt, U: Into<Uuid>>(
595        &mut self,
596        uuid: U,
597        value: &'d T,
598    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
599        let props = [CharacteristicProp::Read].into();
600        self.add_characteristic_internal(
601            uuid.into(),
602            props,
603            AttributeData::ReadOnlyData {
604                props,
605                value: value.as_gatt(),
606            },
607        )
608    }
609
610    /// Finish construction of the service and return a handle.
611    pub fn build(self) -> u16 {
612        self.handle
613    }
614}
615
616impl<M: RawMutex, const MAX: usize> Drop for ServiceBuilder<'_, '_, M, MAX> {
617    fn drop(&mut self) {
618        let last_handle = self.table.handle;
619        self.table.with_inner(|inner| {
620            for item in inner.attributes[self.start..].iter_mut() {
621                item.last_handle_in_group = last_handle;
622            }
623        });
624
625        // Jump to next 16-aligned
626        self.table.handle = self.table.handle + (0x10 - (self.table.handle % 0x10));
627    }
628}
629
630/// A characteristic in the attribute table.
631#[cfg_attr(feature = "defmt", derive(defmt::Format))]
632#[derive(Clone, Copy, Debug, PartialEq)]
633pub struct Characteristic<T: AsGatt> {
634    /// Handle value assigned to the Client Characteristic Configuration Descriptor (if any)
635    pub cccd_handle: Option<u16>,
636    /// Handle value assigned to this characteristic when it is added to the Gatt Attribute Table
637    pub handle: u16,
638    pub(crate) phantom: PhantomData<T>,
639}
640
641impl<T: FromGatt> Characteristic<T> {
642    /// Write a value to a characteristic, and notify a connection with the new value of the characteristic.
643    ///
644    /// If the provided connection has not subscribed for this characteristic, it will not be notified.
645    ///
646    /// If the characteristic does not support notifications, an error is returned.
647    pub async fn notify<P: PacketPool>(&self, connection: &GattConnection<'_, '_, P>, value: &T) -> Result<(), Error> {
648        let value = value.as_gatt();
649        let server = connection.server;
650        server.set(self.handle, value)?;
651
652        let cccd_handle = self.cccd_handle.ok_or(Error::NotFound)?;
653        let connection = connection.raw();
654        if !server.should_notify(connection, cccd_handle) {
655            // No reason to fail?
656            return Ok(());
657        }
658
659        let uns = AttUns::Notify {
660            handle: self.handle,
661            data: value,
662        };
663        let pdu = gatt::assemble(connection, crate::att::AttServer::Unsolicited(uns))?;
664        connection.send(pdu).await;
665        Ok(())
666    }
667
668    /// Write a value to a characteristic, and indicate a connection with the new value of the characteristic.
669    ///
670    /// If the provided connection has not subscribed for this characteristic, it will not be sent an indication.
671    ///
672    /// If the characteristic does not support indications, an error is returned.
673    ///
674    /// This function does not block for the confirmation to the indication message, if the client sends a confirmation
675    /// this will be seen on the [GattConnection] as a [crate::att::AttClient::Confirmation] event.
676    pub async fn indicate<P: PacketPool>(
677        &self,
678        connection: &GattConnection<'_, '_, P>,
679        value: &T,
680    ) -> Result<(), Error> {
681        let value = value.as_gatt();
682        let server = connection.server;
683        server.set(self.handle, value)?;
684
685        let cccd_handle = self.cccd_handle.ok_or(Error::NotFound)?;
686        let connection = connection.raw();
687        if !server.should_indicate(connection, cccd_handle) {
688            // No reason to fail?
689            return Ok(());
690        }
691
692        let uns = AttUns::Indicate {
693            handle: self.handle,
694            data: value,
695        };
696        let pdu = gatt::assemble(connection, crate::att::AttServer::Unsolicited(uns))?;
697        connection.send(pdu).await;
698        Ok(())
699    }
700
701    /// Set the value of the characteristic in the provided attribute server.
702    pub fn set<M: RawMutex, P: PacketPool, const AT: usize, const CT: usize, const CN: usize>(
703        &self,
704        server: &AttributeServer<'_, M, P, AT, CT, CN>,
705        value: &T,
706    ) -> Result<(), Error> {
707        let value = value.as_gatt();
708        server.table().set_raw(self.handle, value)?;
709        Ok(())
710    }
711
712    /// Read the value of the characteristic.
713    ///
714    /// If the characteristic for the handle cannot be found, an error is returned.
715    ///
716    pub fn get<M: RawMutex, P: PacketPool, const AT: usize, const CT: usize, const CN: usize>(
717        &self,
718        server: &AttributeServer<'_, M, P, AT, CT, CN>,
719    ) -> Result<T, Error> {
720        server.table().get(self)
721    }
722
723    /// Returns the attribute handle for the characteristic's properties (if available)
724    pub fn cccd_handle(&self) -> Option<CharacteristicPropertiesHandle> {
725        self.cccd_handle.map(CharacteristicPropertiesHandle)
726    }
727}
728
729/// Attribute handle for a characteristic's properties
730pub struct CharacteristicPropertiesHandle(u16);
731
732impl AttributeHandle for CharacteristicPropertiesHandle {
733    type Value = CharacteristicProps;
734
735    fn handle(&self) -> u16 {
736        self.0
737    }
738}
739
740/// Builder for characteristics.
741pub struct CharacteristicBuilder<'r, 'd, T: AsGatt, M: RawMutex, const MAX: usize> {
742    handle: Characteristic<T>,
743    table: &'r mut AttributeTable<'d, M, MAX>,
744}
745
746impl<'d, T: AsGatt, M: RawMutex, const MAX: usize> CharacteristicBuilder<'_, 'd, T, M, MAX> {
747    fn add_descriptor_internal<DT: AsGatt>(
748        &mut self,
749        uuid: Uuid,
750        props: CharacteristicProps,
751        data: AttributeData<'d>,
752    ) -> Descriptor<DT> {
753        let handle = self.table.handle;
754        self.table.push(Attribute {
755            uuid,
756            handle: 0,
757            last_handle_in_group: 0,
758            data,
759        });
760
761        Descriptor {
762            handle,
763            phantom: PhantomData,
764        }
765    }
766
767    /// Add a characteristic descriptor for this characteristic.
768    pub fn add_descriptor<DT: AsGatt, U: Into<Uuid>>(
769        &mut self,
770        uuid: U,
771        props: &[CharacteristicProp],
772        data: &'d mut [u8],
773    ) -> Descriptor<DT> {
774        let props = props.into();
775        let len = data.len() as u16;
776        self.add_descriptor_internal(
777            uuid.into(),
778            props,
779            AttributeData::Data {
780                props,
781                value: data,
782                variable_len: false,
783                len,
784            },
785        )
786    }
787
788    /// Add a read only characteristic descriptor for this characteristic.
789    pub fn add_descriptor_ro<DT: AsGatt, U: Into<Uuid>>(&mut self, uuid: U, data: &'d [u8]) -> Descriptor<DT> {
790        let props = [CharacteristicProp::Read].into();
791        self.add_descriptor_internal(uuid.into(), props, AttributeData::ReadOnlyData { props, value: data })
792    }
793
794    /// Return the built characteristic.
795    pub fn build(self) -> Characteristic<T> {
796        self.handle
797    }
798}
799
800/// Characteristic descriptor handle.
801#[cfg_attr(feature = "defmt", derive(defmt::Format))]
802#[derive(Clone, Copy, Debug)]
803pub struct Descriptor<T: AsGatt> {
804    pub(crate) handle: u16,
805    phantom: PhantomData<T>,
806}
807
808impl<T: AsGatt> AttributeHandle for Descriptor<T> {
809    type Value = T;
810
811    fn handle(&self) -> u16 {
812        self.handle
813    }
814}
815
816/// Iterator over attributes.
817pub struct AttributeIterator<'a, 'd> {
818    attributes: &'a mut [Attribute<'d>],
819    pos: usize,
820}
821
822impl<'d> AttributeIterator<'_, 'd> {
823    /// Return next attribute in iterator.
824    pub fn next<'m>(&'m mut self) -> Option<&'m mut Attribute<'d>> {
825        if self.pos < self.attributes.len() {
826            let i = &mut self.attributes[self.pos];
827            self.pos += 1;
828            Some(i)
829        } else {
830            None
831        }
832    }
833}
834
835/// A GATT service.
836pub struct Service {
837    /// UUID of the service.
838    pub uuid: Uuid,
839}
840
841impl Service {
842    /// Create a new service with a uuid.
843    pub fn new<U: Into<Uuid>>(uuid: U) -> Self {
844        Self { uuid: uuid.into() }
845    }
846}
847
848/// Properties of a characteristic.
849#[derive(Clone, Copy)]
850pub struct CharacteristicProps(u8);
851
852impl<'a> From<&'a [CharacteristicProp]> for CharacteristicProps {
853    fn from(props: &'a [CharacteristicProp]) -> Self {
854        let mut val: u8 = 0;
855        for prop in props {
856            val |= *prop as u8;
857        }
858        CharacteristicProps(val)
859    }
860}
861
862impl<const T: usize> From<[CharacteristicProp; T]> for CharacteristicProps {
863    fn from(props: [CharacteristicProp; T]) -> Self {
864        let mut val: u8 = 0;
865        for prop in props {
866            val |= prop as u8;
867        }
868        CharacteristicProps(val)
869    }
870}
871
872impl CharacteristicProps {
873    /// Check if any of the properties are set.
874    pub fn any(&self, props: &[CharacteristicProp]) -> bool {
875        for p in props {
876            if (*p as u8) & self.0 != 0 {
877                return true;
878            }
879        }
880        false
881    }
882}
883
884impl FixedGattValue for CharacteristicProps {
885    const SIZE: usize = 1;
886
887    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
888        if data.len() != Self::SIZE {
889            return Err(FromGattError::InvalidLength);
890        }
891
892        Ok(CharacteristicProps(data[0]))
893    }
894
895    fn as_gatt(&self) -> &[u8] {
896        FixedGattValue::as_gatt(&self.0)
897    }
898}
899
900/// A value of an attribute.
901pub struct AttributeValue<'d, M: RawMutex> {
902    value: Mutex<M, &'d mut [u8]>,
903}
904
905impl<M: RawMutex> AttributeValue<'_, M> {}
906
907/// CCCD flag values.
908#[derive(Clone, Copy)]
909pub enum CCCDFlag {
910    /// Notifications enabled.
911    Notify = 0x1,
912    /// Indications enabled.
913    Indicate = 0x2,
914}
915
916/// CCCD flag.
917#[cfg_attr(feature = "defmt", derive(defmt::Format))]
918#[derive(Clone, Copy, Default, Debug, PartialEq)]
919pub struct CCCD(pub(crate) u16);
920
921impl<const T: usize> From<[CCCDFlag; T]> for CCCD {
922    fn from(props: [CCCDFlag; T]) -> Self {
923        let mut val: u16 = 0;
924        for prop in props {
925            val |= prop as u16;
926        }
927        CCCD(val)
928    }
929}
930
931impl From<u16> for CCCD {
932    fn from(value: u16) -> Self {
933        CCCD(value)
934    }
935}
936
937impl CCCD {
938    /// Get raw value
939    pub fn raw(&self) -> u16 {
940        self.0
941    }
942
943    /// Clear all properties
944    pub fn disable(&mut self) {
945        self.0 = 0;
946    }
947
948    /// Check if any of the properties are set.
949    pub fn any(&self, props: &[CCCDFlag]) -> bool {
950        for p in props {
951            if (*p as u16) & self.0 != 0 {
952                return true;
953            }
954        }
955        false
956    }
957
958    /// Enable or disable notifications
959    pub fn set_notify(&mut self, is_enabled: bool) {
960        let mask: u16 = CCCDFlag::Notify as u16;
961        self.0 = if is_enabled { self.0 | mask } else { self.0 & !mask };
962    }
963
964    /// Check if notifications are enabled
965    pub fn should_notify(&self) -> bool {
966        (self.0 & (CCCDFlag::Notify as u16)) != 0
967    }
968
969    /// Enable or disable indication
970    pub fn set_indicate(&mut self, is_enabled: bool) {
971        let mask: u16 = CCCDFlag::Indicate as u16;
972        self.0 = if is_enabled { self.0 | mask } else { self.0 & !mask };
973    }
974
975    /// Check if indications are enabled
976    pub fn should_indicate(&self) -> bool {
977        (self.0 & (CCCDFlag::Indicate as u16)) != 0
978    }
979}