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