1use 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#[derive(Debug, Clone, Copy)]
22#[repr(u8)]
23pub enum CharacteristicProp {
24 Broadcast = 0x01,
26 Read = 0x02,
28 WriteWithoutResponse = 0x04,
30 Write = 0x08,
32 Notify = 0x10,
34 Indicate = 0x20,
36 AuthenticatedWrite = 0x40,
38 Extended = 0x80,
40}
41
42pub 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
289pub 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 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 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 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 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 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 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
487pub trait AttributeHandle {
489 type Value: AsGatt;
491
492 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
504pub 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 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 self.table.push(Attribute {
534 uuid,
535 handle: 0,
536 last_handle_in_group: 0,
537 data,
538 });
539
540 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 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 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 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 self.table.handle = self.table.handle + (0x10 - (self.table.handle % 0x10));
625 }
626}
627
628#[cfg_attr(feature = "defmt", derive(defmt::Format))]
630#[derive(Clone, Copy, Debug, PartialEq)]
631pub struct Characteristic<T: AsGatt> {
632 pub cccd_handle: Option<u16>,
634 pub handle: u16,
636 pub(crate) phantom: PhantomData<T>,
637}
638
639impl<T: FromGatt> Characteristic<T> {
640 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 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 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 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 pub fn cccd_handle(&self) -> Option<CharacteristicPropertiesHandle> {
697 self.cccd_handle.map(CharacteristicPropertiesHandle)
698 }
699}
700
701pub struct CharacteristicPropertiesHandle(u16);
703
704impl AttributeHandle for CharacteristicPropertiesHandle {
705 type Value = CharacteristicProps;
706
707 fn handle(&self) -> u16 {
708 self.0
709 }
710}
711
712pub 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 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 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 pub fn build(self) -> Characteristic<T> {
768 self.handle
769 }
770}
771
772#[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
788pub struct AttributeIterator<'a, 'd> {
790 attributes: &'a mut [Attribute<'d>],
791 pos: usize,
792}
793
794impl<'d> AttributeIterator<'_, 'd> {
795 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
807pub struct Service {
809 pub uuid: Uuid,
811}
812
813impl Service {
814 pub fn new<U: Into<Uuid>>(uuid: U) -> Self {
816 Self { uuid: uuid.into() }
817 }
818}
819
820#[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 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
872pub struct AttributeValue<'d, M: RawMutex> {
874 value: Mutex<M, &'d mut [u8]>,
875}
876
877impl<M: RawMutex> AttributeValue<'_, M> {}
878
879#[derive(Clone, Copy)]
881pub enum CCCDFlag {
882 Notify = 0x1,
884 Indicate = 0x2,
886}
887
888#[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 pub fn raw(&self) -> u16 {
912 self.0
913 }
914
915 pub fn disable(&mut self) {
917 self.0 = 0;
918 }
919
920 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 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 pub fn should_notify(&self) -> bool {
938 (self.0 & (CCCDFlag::Notify as u16)) != 0
939 }
940}