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, 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#[derive(Debug, Clone, Copy)]
24#[repr(u8)]
25pub enum CharacteristicProp {
26 Broadcast = 0x01,
28 Read = 0x02,
30 WriteWithoutResponse = 0x04,
32 Write = 0x08,
34 Notify = 0x10,
36 Indicate = 0x20,
38 AuthenticatedWrite = 0x40,
40 Extended = 0x80,
42}
43
44pub 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
291pub 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 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 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 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 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 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 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
489pub trait AttributeHandle {
491 type Value: AsGatt;
493
494 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
506pub 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 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 self.table.push(Attribute {
536 uuid,
537 handle: 0,
538 last_handle_in_group: 0,
539 data,
540 });
541
542 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 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 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 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 self.table.handle = self.table.handle + (0x10 - (self.table.handle % 0x10));
627 }
628}
629
630#[cfg_attr(feature = "defmt", derive(defmt::Format))]
632#[derive(Clone, Copy, Debug, PartialEq)]
633pub struct Characteristic<T: AsGatt> {
634 pub cccd_handle: Option<u16>,
636 pub handle: u16,
638 pub(crate) phantom: PhantomData<T>,
639}
640
641impl<T: FromGatt> Characteristic<T> {
642 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 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 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 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 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 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 pub fn cccd_handle(&self) -> Option<CharacteristicPropertiesHandle> {
725 self.cccd_handle.map(CharacteristicPropertiesHandle)
726 }
727}
728
729pub struct CharacteristicPropertiesHandle(u16);
731
732impl AttributeHandle for CharacteristicPropertiesHandle {
733 type Value = CharacteristicProps;
734
735 fn handle(&self) -> u16 {
736 self.0
737 }
738}
739
740pub 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 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 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 pub fn build(self) -> Characteristic<T> {
796 self.handle
797 }
798}
799
800#[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
816pub struct AttributeIterator<'a, 'd> {
818 attributes: &'a mut [Attribute<'d>],
819 pos: usize,
820}
821
822impl<'d> AttributeIterator<'_, 'd> {
823 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
835pub struct Service {
837 pub uuid: Uuid,
839}
840
841impl Service {
842 pub fn new<U: Into<Uuid>>(uuid: U) -> Self {
844 Self { uuid: uuid.into() }
845 }
846}
847
848#[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 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
900pub struct AttributeValue<'d, M: RawMutex> {
902 value: Mutex<M, &'d mut [u8]>,
903}
904
905impl<M: RawMutex> AttributeValue<'_, M> {}
906
907#[derive(Clone, Copy)]
909pub enum CCCDFlag {
910 Notify = 0x1,
912 Indicate = 0x2,
914}
915
916#[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 pub fn raw(&self) -> u16 {
940 self.0
941 }
942
943 pub fn disable(&mut self) {
945 self.0 = 0;
946 }
947
948 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 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 pub fn should_notify(&self) -> bool {
966 (self.0 & (CCCDFlag::Notify as u16)) != 0
967 }
968
969 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 pub fn should_indicate(&self) -> bool {
977 (self.0 & (CCCDFlag::Indicate as u16)) != 0
978 }
979}