1use core::cell::RefCell;
3use core::fmt;
4use core::marker::PhantomData;
5
6use bt_hci::uuid::declarations::{CHARACTERISTIC, INCLUDE, PRIMARY_SERVICE, SECONDARY_SERVICE};
7use bt_hci::uuid::descriptors::CLIENT_CHARACTERISTIC_CONFIGURATION;
8use embassy_sync::blocking_mutex::raw::RawMutex;
9use embassy_sync::blocking_mutex::Mutex;
10use embassy_time::with_timeout;
11use heapless::Vec;
12
13use crate::att::{AttErrorCode, AttUns};
14use crate::attribute_server::AttributeServer;
15use crate::cursor::{ReadCursor, WriteCursor};
16use crate::gatt::ATT_TRANSACTION_TIMEOUT;
17use crate::prelude::{AsGatt, FixedGattValue, FromGatt, GattConnection, SecurityLevel};
18use crate::types::gatt_traits::FromGattError;
19pub use crate::types::uuid::Uuid;
20use crate::{gatt, Error, PacketPool, MAX_INVALID_DATA_LEN};
21
22pub const MAX_SMALL_DATA_SIZE: usize = 20;
24
25#[derive(Debug, Clone, Copy)]
27#[repr(u8)]
28pub enum CharacteristicProp {
29 Broadcast = 0x01,
31 Read = 0x02,
33 WriteWithoutResponse = 0x04,
35 Write = 0x08,
37 Notify = 0x10,
39 Indicate = 0x20,
41 AuthenticatedWrite = 0x40,
43 Extended = 0x80,
45}
46
47#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
49#[cfg_attr(feature = "defmt", derive(defmt::Format))]
50pub enum PermissionLevel {
51 #[default]
52 Allowed,
54 EncryptionRequired,
56 AuthenticationRequired,
58 NotAllowed,
60}
61
62#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
64#[cfg_attr(feature = "defmt", derive(defmt::Format))]
65pub struct AttPermissions {
66 pub read: PermissionLevel,
68 pub write: PermissionLevel,
70 #[cfg(feature = "legacy-pairing")]
72 pub min_key_len: u8,
73}
74
75impl AttPermissions {
76 pub(crate) fn read_only() -> Self {
77 Self {
78 read: PermissionLevel::Allowed,
79 write: PermissionLevel::NotAllowed,
80 #[cfg(feature = "legacy-pairing")]
81 min_key_len: 0,
82 }
83 }
84
85 pub(crate) fn can_read(
86 &self,
87 level: SecurityLevel,
88 #[cfg(feature = "legacy-pairing")] encryption_key_len: u8,
89 ) -> Result<(), AttErrorCode> {
90 match self.read {
91 PermissionLevel::NotAllowed => Err(AttErrorCode::READ_NOT_PERMITTED),
92 PermissionLevel::EncryptionRequired | PermissionLevel::AuthenticationRequired
93 if level < SecurityLevel::Encrypted =>
94 {
95 Err(AttErrorCode::INSUFFICIENT_AUTHENTICATION)
96 }
97 PermissionLevel::AuthenticationRequired if level < SecurityLevel::EncryptedAuthenticated => {
98 Err(AttErrorCode::INSUFFICIENT_AUTHENTICATION)
99 }
100 _ => {
101 #[cfg(feature = "legacy-pairing")]
102 if self.min_key_len > 0 && level.encrypted() && encryption_key_len < self.min_key_len {
103 return Err(AttErrorCode::INSUFFICIENT_ENCRYPTION_KEY_SIZE);
104 }
105 Ok(())
106 }
107 }
108 }
109
110 pub(crate) fn can_write(
111 &self,
112 level: SecurityLevel,
113 #[cfg(feature = "legacy-pairing")] encryption_key_len: u8,
114 ) -> Result<(), AttErrorCode> {
115 match self.write {
116 PermissionLevel::NotAllowed => Err(AttErrorCode::WRITE_NOT_PERMITTED),
117 PermissionLevel::EncryptionRequired | PermissionLevel::AuthenticationRequired
118 if level < SecurityLevel::Encrypted =>
119 {
120 Err(AttErrorCode::INSUFFICIENT_AUTHENTICATION)
121 }
122 PermissionLevel::AuthenticationRequired if level < SecurityLevel::EncryptedAuthenticated => {
123 Err(AttErrorCode::INSUFFICIENT_AUTHENTICATION)
124 }
125 _ => {
126 #[cfg(feature = "legacy-pairing")]
127 if self.min_key_len > 0 && level.encrypted() && encryption_key_len < self.min_key_len {
128 return Err(AttErrorCode::INSUFFICIENT_ENCRYPTION_KEY_SIZE);
129 }
130 Ok(())
131 }
132 }
133 }
134}
135
136pub(crate) struct CharacteristicDeclaration {
138 pub props: CharacteristicProps,
139 pub value_handle: u16,
140 pub uuid: Uuid,
141}
142
143impl CharacteristicDeclaration {
144 pub(crate) fn new<U: Into<Uuid>, P: Into<CharacteristicProps>>(props: P, value_handle: u16, uuid: U) -> Self {
145 Self {
146 props: props.into(),
147 value_handle,
148 uuid: uuid.into(),
149 }
150 }
151}
152
153impl TryFrom<&[u8]> for CharacteristicDeclaration {
154 type Error = Error;
155
156 fn try_from(data: &'_ [u8]) -> Result<Self, Self::Error> {
157 let mut r = ReadCursor::new(data);
158 Ok(CharacteristicDeclaration {
159 props: CharacteristicProps(r.read()?),
160 value_handle: r.read()?,
161 uuid: Uuid::try_from(r.remaining())?,
162 })
163 }
164}
165
166pub struct Attribute<'a> {
168 pub(crate) uuid: Uuid,
169 pub(crate) permissions: AttPermissions,
170 pub(crate) data: AttributeData<'a>,
171}
172
173impl<'a> Attribute<'a> {
174 const EMPTY: Option<Attribute<'a>> = None;
175
176 pub(crate) fn read(&self, offset: usize, data: &mut [u8]) -> Result<usize, AttErrorCode> {
177 self.data.read(offset, data)
178 }
179
180 pub(crate) fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
181 self.data.write(offset, data)
182 }
183
184 pub(crate) fn permissions(&self) -> AttPermissions {
185 self.permissions
186 }
187
188 pub(crate) fn readable(&self) -> bool {
189 self.permissions.read != PermissionLevel::NotAllowed
190 }
191
192 pub(crate) fn writable(&self) -> bool {
193 self.permissions.write != PermissionLevel::NotAllowed
194 }
195}
196
197#[derive(Debug, PartialEq, Eq)]
198pub(crate) enum AttributeData<'d> {
199 ReadOnlyData {
200 value: &'d [u8],
201 },
202 Data {
203 variable_len: bool,
204 len: u16,
205 value: &'d mut [u8],
206 },
207 SmallData {
208 variable_len: bool,
209 capacity: u8,
210 len: u8,
211 value: [u8; MAX_SMALL_DATA_SIZE],
212 },
213 ClientSpecific {
214 variable_len: bool,
215 capacity: u16,
216 },
217}
218
219impl From<Uuid> for AttributeData<'_> {
220 fn from(uuid: Uuid) -> Self {
221 let raw = uuid.as_raw();
222 let len = raw.len() as u8;
223 let mut value = [0u8; MAX_SMALL_DATA_SIZE];
224 value[..raw.len()].copy_from_slice(raw);
225 AttributeData::SmallData {
226 variable_len: false,
227 capacity: len,
228 len,
229 value,
230 }
231 }
232}
233
234impl From<bt_hci::uuid::BluetoothUuid16> for AttributeData<'_> {
235 fn from(uuid: bt_hci::uuid::BluetoothUuid16) -> Self {
236 let raw = uuid.to_le_bytes();
237 let len = raw.len() as u8;
238 let mut value = [0u8; MAX_SMALL_DATA_SIZE];
239 value[..raw.len()].copy_from_slice(&raw);
240 AttributeData::SmallData {
241 variable_len: false,
242 capacity: len,
243 len,
244 value,
245 }
246 }
247}
248
249impl From<bt_hci::uuid::BluetoothUuid128> for AttributeData<'_> {
250 fn from(uuid: bt_hci::uuid::BluetoothUuid128) -> Self {
251 let raw = uuid.to_le_bytes();
252 let len = raw.len() as u8;
253 let mut value = [0u8; MAX_SMALL_DATA_SIZE];
254 value[..raw.len()].copy_from_slice(&raw);
255 AttributeData::SmallData {
256 variable_len: false,
257 capacity: len,
258 len,
259 value,
260 }
261 }
262}
263
264impl From<CharacteristicDeclaration> for AttributeData<'_> {
265 fn from(decl: CharacteristicDeclaration) -> Self {
266 let uuid_raw = decl.uuid.as_raw();
267 let total_len = 1 + 2 + uuid_raw.len(); debug_assert!(total_len <= MAX_SMALL_DATA_SIZE);
269 let mut value = [0u8; MAX_SMALL_DATA_SIZE];
270 value[0] = decl.props.0;
271 value[1..3].copy_from_slice(&decl.value_handle.to_le_bytes());
272 value[3..3 + uuid_raw.len()].copy_from_slice(uuid_raw);
273 let len = total_len as u8;
274 AttributeData::SmallData {
275 variable_len: false,
276 capacity: len,
277 len,
278 value,
279 }
280 }
281}
282
283impl AttributeData<'_> {
284 pub(crate) fn value(&self) -> Option<&[u8]> {
286 match self {
287 AttributeData::ReadOnlyData { value } => Some(value),
288 AttributeData::Data { len, value, .. } => Some(&value[..*len as usize]),
289 AttributeData::SmallData { len, value, .. } => Some(&value[..*len as usize]),
290 AttributeData::ClientSpecific { .. } => None,
291 }
292 }
293
294 pub(crate) fn is_variable_len(&self) -> bool {
295 match self {
296 AttributeData::Data { variable_len, .. }
297 | AttributeData::SmallData { variable_len, .. }
298 | AttributeData::ClientSpecific { variable_len, .. } => *variable_len,
299 _ => false,
300 }
301 }
302
303 pub(crate) fn capacity(&self) -> usize {
304 match self {
305 AttributeData::ReadOnlyData { value, .. } => value.len(),
306 AttributeData::Data { value, .. } => value.len(),
307 AttributeData::SmallData { capacity, .. } => usize::from(*capacity),
308 AttributeData::ClientSpecific { capacity, .. } => usize::from(*capacity),
309 }
310 }
311
312 pub(crate) fn read(&self, mut offset: usize, mut data: &mut [u8]) -> Result<usize, AttErrorCode> {
313 fn append(src: &[u8], offset: &mut usize, dest: &mut &mut [u8]) -> usize {
314 if *offset >= src.len() {
315 *offset -= src.len();
316 0
317 } else {
318 let d = core::mem::take(dest);
319 let n = d.len().min(src.len() - *offset);
320 d[..n].copy_from_slice(&src[*offset..][..n]);
321 *dest = &mut d[n..];
322 *offset = 0;
323 n
324 }
325 }
326
327 let written = match self {
328 Self::ReadOnlyData { value } => append(value, &mut offset, &mut data),
329 Self::Data { len, value, .. } => {
330 let value = &value[..*len as usize];
331 append(value, &mut offset, &mut data)
332 }
333 Self::SmallData { len, value, .. } => {
334 let value = &value[..*len as usize];
335 append(value, &mut offset, &mut data)
336 }
337 Self::ClientSpecific { .. } => return Err(AttErrorCode::UNLIKELY_ERROR),
338 };
339
340 if offset > 0 {
341 Err(AttErrorCode::INVALID_OFFSET)
342 } else {
343 Ok(written)
344 }
345 }
346
347 pub(crate) fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
348 match self {
349 Self::Data {
350 value,
351 variable_len,
352 len,
353 } => {
354 if offset > usize::from(*len) {
355 Err(AttErrorCode::INVALID_OFFSET)
356 } else if offset + data.len() > value.len() {
357 Err(AttErrorCode::INVALID_ATTRIBUTE_VALUE_LENGTH)
358 } else {
359 value[offset..offset + data.len()].copy_from_slice(data);
360 if *variable_len {
361 *len = (offset + data.len()) as u16;
362 }
363 Ok(())
364 }
365 }
366 Self::SmallData {
367 variable_len,
368 capacity,
369 len,
370 value,
371 } => {
372 if offset > usize::from(*len) {
373 Err(AttErrorCode::INVALID_OFFSET)
374 } else if offset + data.len() > usize::from(*capacity) {
375 Err(AttErrorCode::INVALID_ATTRIBUTE_VALUE_LENGTH)
376 } else {
377 value[offset..offset + data.len()].copy_from_slice(data);
378 if *variable_len {
379 *len = (offset + data.len()) as u8;
380 }
381 Ok(())
382 }
383 }
384 _ => Err(AttErrorCode::WRITE_NOT_PERMITTED),
385 }
386 }
387}
388
389impl fmt::Debug for Attribute<'_> {
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 f.debug_struct("Attribute")
392 .field("uuid", &self.uuid)
393 .field("readable", &self.readable())
394 .field("writable", &self.writable())
395 .finish()
396 }
397}
398
399#[cfg(feature = "defmt")]
400impl<'a> defmt::Format for Attribute<'a> {
401 fn format(&self, fmt: defmt::Formatter) {
402 defmt::write!(fmt, "{}", defmt::Debug2Format(self))
403 }
404}
405
406impl<'a> Attribute<'a> {
407 pub(crate) fn new<U: Into<Uuid>, T: Into<AttributeData<'a>>>(
408 uuid: U,
409 permissions: AttPermissions,
410 data: T,
411 ) -> Attribute<'a> {
412 Attribute {
413 uuid: uuid.into(),
414 permissions,
415 data: data.into(),
416 }
417 }
418}
419
420pub struct AttributeTable<'d, M: RawMutex, const MAX: usize> {
422 inner: Mutex<M, RefCell<InnerTable<'d, MAX>>>,
423}
424
425pub(crate) struct InnerTable<'d, const MAX: usize> {
426 attributes: Vec<Attribute<'d>, MAX>,
427}
428
429impl<'d, const MAX: usize> InnerTable<'d, MAX> {
430 fn push(&mut self, attribute: Attribute<'d>) -> u16 {
431 let handle = self.next_handle();
432 self.attributes.push(attribute).unwrap();
433 handle
434 }
435
436 fn iterate_from(&self, pos: usize) -> AttributeIterator<'_, 'd> {
437 AttributeIterator {
438 attributes: &self.attributes,
439 pos,
440 }
441 }
442
443 fn service_group_end(&self, service_handle: u16) -> u16 {
444 self.iterate_from(usize::from(service_handle)).service_group_end()
445 }
446
447 fn next_handle(&self) -> u16 {
448 self.attributes.len() as u16 + 1
449 }
450}
451
452impl<M: RawMutex, const MAX: usize> Default for AttributeTable<'_, M, MAX> {
453 fn default() -> Self {
454 Self::new()
455 }
456}
457
458impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> {
459 pub fn new() -> Self {
461 Self {
462 inner: Mutex::new(RefCell::new(InnerTable { attributes: Vec::new() })),
463 }
464 }
465
466 pub(crate) fn with_inner<F: FnOnce(&InnerTable<'d, MAX>) -> R, R>(&self, f: F) -> R {
467 self.inner.lock(|inner| {
468 let table = inner.borrow();
469 f(&table)
470 })
471 }
472
473 pub(crate) fn with_inner_mut<F: FnOnce(&mut InnerTable<'d, MAX>) -> R, R>(&self, f: F) -> R {
474 self.inner.lock(|inner| {
475 let mut table = inner.borrow_mut();
476 f(&mut table)
477 })
478 }
479
480 pub(crate) fn iterate<F: FnOnce(AttributeIterator<'_, 'd>) -> R, R>(&self, f: F) -> R {
481 self.with_inner(|table| f(table.iterate_from(0)))
482 }
483
484 pub(crate) fn with_attribute<F: FnOnce(&Attribute<'d>) -> R, R>(&self, handle: u16, f: F) -> Option<R> {
485 if handle == 0 {
486 return None;
487 }
488
489 self.with_inner(|table| {
490 let i = usize::from(handle) - 1;
491 table.attributes.get(i).map(f)
492 })
493 }
494
495 pub(crate) fn with_attribute_mut<F: FnOnce(&mut Attribute<'d>) -> R, R>(&self, handle: u16, f: F) -> Option<R> {
496 if handle == 0 {
497 return None;
498 }
499
500 self.with_inner_mut(|table| {
501 let i = usize::from(handle) - 1;
502 table.attributes.get_mut(i).map(f)
503 })
504 }
505
506 pub(crate) fn iterate_from<F: FnOnce(AttributeIterator<'_, 'd>) -> R, R>(&self, start: u16, f: F) -> R {
507 let i = usize::from(start).saturating_sub(1);
508 self.with_inner(|table| f(table.iterate_from(i)))
509 }
510
511 fn push(&mut self, attribute: Attribute<'d>) -> u16 {
512 self.with_inner_mut(|table| table.push(attribute))
513 }
514
515 pub fn add_service(&mut self, service: Service) -> ServiceBuilder<'_, 'd, M, MAX> {
517 let handle = self.push(Attribute::new(
518 PRIMARY_SERVICE,
519 AttPermissions::read_only(),
520 service.uuid,
521 ));
522 ServiceBuilder { handle, table: self }
523 }
524
525 pub fn add_secondary_service(&mut self, service: Service) -> ServiceBuilder<'_, 'd, M, MAX> {
527 let handle = self.push(Attribute::new(
528 SECONDARY_SERVICE,
529 AttPermissions::read_only(),
530 service.uuid,
531 ));
532 ServiceBuilder { handle, table: self }
533 }
534
535 pub fn permissions(&self, attribute: u16) -> Option<AttPermissions> {
539 self.with_attribute(attribute, |att| att.permissions())
540 }
541
542 pub fn uuid(&self, attribute: u16) -> Option<Uuid> {
546 self.with_attribute(attribute, |att| att.uuid)
547 }
548
549 pub(crate) fn set_ro(&self, attribute: u16, new_value: &'d [u8]) -> Result<(), Error> {
550 self.with_attribute_mut(attribute, |att| match &mut att.data {
551 AttributeData::ReadOnlyData { value, .. } => {
552 *value = new_value;
553 Ok(())
554 }
555 _ => Err(Error::NotSupported),
556 })
557 .unwrap_or(Err(Error::NotFound))
558 }
559
560 pub fn read(&self, attribute: u16, offset: usize, data: &mut [u8]) -> Result<usize, Error> {
567 self.with_attribute(attribute, |att| att.read(offset, data).map_err(Into::into))
568 .unwrap_or(Err(Error::NotFound))
569 }
570
571 pub fn write(&self, attribute: u16, offset: usize, data: &[u8]) -> Result<(), Error> {
577 self.with_attribute_mut(attribute, |att| att.write(offset, data).map_err(Into::into))
578 .unwrap_or(Err(Error::NotFound))
579 }
580
581 pub(crate) fn set_raw(&self, attribute: u16, input: &[u8]) -> Result<(), Error> {
582 self.with_attribute_mut(attribute, |att| {
583 if !att.data.is_variable_len() && att.data.capacity() != input.len() {
584 Err(Error::UnexpectedDataLength {
585 expected: att.data.capacity(),
586 actual: input.len(),
587 })
588 } else {
589 att.write(0, input).map_err(Into::into)
590 }
591 })
592 .unwrap_or(Err(Error::NotFound))
593 }
594
595 pub fn len(&self) -> usize {
597 self.with_inner(|table| table.attributes.len())
598 }
599
600 pub fn is_empty(&self) -> bool {
602 self.with_inner(|table| table.attributes.is_empty())
603 }
604
605 pub fn set<T: AttributeHandle>(&self, attribute_handle: &T, input: &T::Value) -> Result<(), Error> {
614 let gatt_value = input.as_gatt();
615 self.set_raw(attribute_handle.handle(), gatt_value)
616 }
617
618 pub fn get<T: AttributeHandle<Value = V>, V: FromGatt>(&self, attribute_handle: &T) -> Result<T::Value, Error> {
624 self.with_attribute(attribute_handle.handle(), |att| {
625 let value_slice = att.data.value().ok_or(Error::NotSupported)?;
626 T::Value::from_gatt(value_slice).map_err(|_| {
627 let mut invalid_data = [0u8; MAX_INVALID_DATA_LEN];
628 let len_to_copy = value_slice.len().min(MAX_INVALID_DATA_LEN);
629 invalid_data[..len_to_copy].copy_from_slice(&value_slice[..len_to_copy]);
630
631 Error::CannotConstructGattValue(invalid_data)
632 })
633 })
634 .unwrap_or(Err(Error::NotFound))
635 }
636
637 pub fn find_characteristic_by_value_handle<T: AsGatt>(&self, handle: u16) -> Result<Characteristic<T>, Error> {
641 if handle == 0 {
642 return Err(Error::NotFound);
643 }
644
645 self.iterate_from(handle - 1, |mut it| {
646 if let Some((_, att)) = it.next() {
647 if att.uuid == CHARACTERISTIC.into() {
648 let decl = CharacteristicDeclaration::try_from(att.data.value().unwrap())?;
649 let props = decl.props;
650 let uuid = decl.uuid;
651 if it.next().is_some() {
652 let end_handle = it.characteristic_group_end();
653 let cccd_handle = it.next().and_then(|(handle, att)| {
654 (att.uuid == CLIENT_CHARACTERISTIC_CONFIGURATION.into()).then_some(handle)
655 });
656
657 return Ok(Characteristic {
658 handle,
659 cccd_handle,
660 end_handle,
661 props,
662 uuid,
663 phantom: PhantomData,
664 });
665 }
666 }
667 }
668 Err(Error::NotFound)
669 })
670 }
671
672 #[cfg(feature = "security")]
673 pub fn hash(&self) -> u128 {
677 use bt_hci::uuid::*;
678
679 use crate::security_manager::crypto::AesCmac;
680
681 const PRIMARY_SERVICE: Uuid = Uuid::Uuid16(declarations::PRIMARY_SERVICE.to_le_bytes());
682 const SECONDARY_SERVICE: Uuid = Uuid::Uuid16(declarations::SECONDARY_SERVICE.to_le_bytes());
683 const INCLUDED_SERVICE: Uuid = Uuid::Uuid16(declarations::INCLUDE.to_le_bytes());
684 const CHARACTERISTIC: Uuid = Uuid::Uuid16(declarations::CHARACTERISTIC.to_le_bytes());
685 const CHARACTERISTIC_EXTENDED_PROPERTIES: Uuid =
686 Uuid::Uuid16(descriptors::CHARACTERISTIC_EXTENDED_PROPERTIES.to_le_bytes());
687
688 const CHARACTERISTIC_USER_DESCRIPTION: Uuid =
689 Uuid::Uuid16(descriptors::CHARACTERISTIC_USER_DESCRIPTION.to_le_bytes());
690 const CLIENT_CHARACTERISTIC_CONFIGURATION: Uuid =
691 Uuid::Uuid16(descriptors::CLIENT_CHARACTERISTIC_CONFIGURATION.to_le_bytes());
692 const SERVER_CHARACTERISTIC_CONFIGURATION: Uuid =
693 Uuid::Uuid16(descriptors::SERVER_CHARACTERISTIC_CONFIGURATION.to_le_bytes());
694 const CHARACTERISTIC_PRESENTATION_FORMAT: Uuid =
695 Uuid::Uuid16(descriptors::CHARACTERISTIC_PRESENTATION_FORMAT.to_le_bytes());
696 const CHARACTERISTIC_AGGREGATE_FORMAT: Uuid =
697 Uuid::Uuid16(descriptors::CHARACTERISTIC_AGGREGATE_FORMAT.to_le_bytes());
698
699 let mut mac = AesCmac::db_hash();
700
701 self.iterate(|it| {
702 for (handle, att) in it {
703 match att.uuid {
704 PRIMARY_SERVICE
705 | SECONDARY_SERVICE
706 | INCLUDED_SERVICE
707 | CHARACTERISTIC
708 | CHARACTERISTIC_EXTENDED_PROPERTIES => {
709 mac.update(handle.to_le_bytes()).update(att.uuid.as_raw());
710 let value = att.data.value().unwrap_or(&[]);
711 mac.update(value);
712 }
713 CHARACTERISTIC_USER_DESCRIPTION
714 | CLIENT_CHARACTERISTIC_CONFIGURATION
715 | SERVER_CHARACTERISTIC_CONFIGURATION
716 | CHARACTERISTIC_PRESENTATION_FORMAT
717 | CHARACTERISTIC_AGGREGATE_FORMAT => {
718 mac.update(handle.to_le_bytes()).update(att.uuid.as_raw());
719 }
720 _ => {}
721 }
722 }
723 });
724
725 mac.finalize()
726 }
727}
728
729pub trait AttributeHandle {
731 type Value: AsGatt + ?Sized;
733
734 fn handle(&self) -> u16;
736}
737
738impl<T: AsGatt + ?Sized> AttributeHandle for Characteristic<T> {
739 type Value = T;
740
741 fn handle(&self) -> u16 {
742 self.handle
743 }
744}
745
746#[derive(Debug, Clone, PartialEq)]
748#[cfg_attr(feature = "defmt", derive(defmt::Format))]
749pub struct InvalidHandle;
750
751impl core::fmt::Display for InvalidHandle {
752 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
753 core::fmt::Debug::fmt(self, f)
754 }
755}
756
757impl core::error::Error for InvalidHandle {}
758
759impl From<InvalidHandle> for Error {
760 fn from(value: InvalidHandle) -> Self {
761 Error::InvalidValue
762 }
763}
764
765pub struct ServiceBuilder<'r, 'd, M: RawMutex, const MAX: usize> {
767 handle: u16,
768 table: &'r mut AttributeTable<'d, M, MAX>,
769}
770
771impl<'d, M: RawMutex, const MAX: usize> ServiceBuilder<'_, 'd, M, MAX> {
772 fn add_characteristic_internal<T: AsGatt + ?Sized>(
773 &mut self,
774 uuid: Uuid,
775 props: CharacteristicProps,
776 permissions: AttPermissions,
777 data: AttributeData<'d>,
778 ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
779 let chrc_uuid = uuid;
780 let (handle, cccd_handle) = self.table.with_inner_mut(|table| {
782 let value_handle = table.next_handle() + 1;
783 let declaration = CharacteristicDeclaration::new(props, value_handle, uuid);
784 table.push(Attribute::new(CHARACTERISTIC, AttPermissions::read_only(), declaration));
785
786 let h = table.push(Attribute::new(uuid, permissions, data));
788 debug_assert!(h == value_handle);
789
790 let cccd_handle = if props.has_cccd() {
792 let handle = table.push(Attribute::new(
793 CLIENT_CHARACTERISTIC_CONFIGURATION,
794 AttPermissions {
795 read: PermissionLevel::Allowed,
796 write: PermissionLevel::Allowed,
797 #[cfg(feature = "legacy-pairing")]
798 min_key_len: 0,
799 },
800 AttributeData::ClientSpecific {
801 variable_len: false,
802 capacity: 2,
803 },
804 ));
805
806 Some(handle)
807 } else {
808 None
809 };
810
811 (value_handle, cccd_handle)
812 });
813
814 CharacteristicBuilder {
815 handle: Characteristic {
816 handle,
817 cccd_handle,
818 end_handle: cccd_handle.unwrap_or(handle),
820 props,
821 uuid: chrc_uuid,
822 phantom: PhantomData,
823 },
824 table: self.table,
825 }
826 }
827
828 pub fn add_characteristic<T: AsGatt, U: Into<Uuid>, P: Into<CharacteristicProps>>(
830 &mut self,
831 uuid: U,
832 props: P,
833 value: T,
834 store: &'d mut [u8],
835 ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
836 let props: CharacteristicProps = props.into();
837 let permissions = props.default_permissions();
838 let bytes = value.as_gatt();
839 store[..bytes.len()].copy_from_slice(bytes);
840 let variable_len = T::MAX_SIZE != T::MIN_SIZE;
841 let len = bytes.len() as u16;
842 self.add_characteristic_internal(
843 uuid.into(),
844 props,
845 permissions,
846 AttributeData::Data {
847 value: store,
848 variable_len,
849 len,
850 },
851 )
852 }
853
854 pub fn add_characteristic_small<T: AsGatt, U: Into<Uuid>, P: Into<CharacteristicProps>>(
856 &mut self,
857 uuid: U,
858 props: P,
859 value: T,
860 ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
861 assert!(T::MIN_SIZE <= MAX_SMALL_DATA_SIZE);
862
863 let props: CharacteristicProps = props.into();
864 let permissions = props.default_permissions();
865 let bytes = value.as_gatt();
866 assert!(bytes.len() <= MAX_SMALL_DATA_SIZE);
867 let mut value = [0; MAX_SMALL_DATA_SIZE];
868 value[..bytes.len()].copy_from_slice(bytes);
869 let variable_len = T::MAX_SIZE != T::MIN_SIZE;
870 let capacity = T::MAX_SIZE.min(MAX_SMALL_DATA_SIZE) as u8;
871 let len = bytes.len() as u8;
872 self.add_characteristic_internal(
873 uuid.into(),
874 props,
875 permissions,
876 AttributeData::SmallData {
877 variable_len,
878 capacity,
879 len,
880 value,
881 },
882 )
883 }
884
885 pub fn add_characteristic_ro<T: AsGatt + ?Sized, U: Into<Uuid>>(
887 &mut self,
888 uuid: U,
889 value: &'d T,
890 ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
891 let props: CharacteristicProps = [CharacteristicProp::Read].into();
892 let permissions = props.default_permissions();
893 self.add_characteristic_internal(
894 uuid.into(),
895 props,
896 permissions,
897 AttributeData::ReadOnlyData { value: value.as_gatt() },
898 )
899 }
900
901 pub fn add_characteristic_client<T: AsGatt, U: Into<Uuid>, P: Into<CharacteristicProps>>(
903 &mut self,
904 uuid: U,
905 props: P,
906 ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
907 assert!(T::MAX_SIZE <= 512);
908 let props: CharacteristicProps = props.into();
909 let permissions = props.default_permissions();
910 let variable_len = T::MAX_SIZE != T::MIN_SIZE;
911 self.add_characteristic_internal(
912 uuid.into(),
913 props,
914 permissions,
915 AttributeData::ClientSpecific {
916 variable_len,
917 capacity: T::MAX_SIZE as u16,
918 },
919 )
920 }
921
922 pub fn add_included_service(&mut self, handle: u16) -> Result<u16, InvalidHandle> {
924 self.table.with_inner_mut(|table| {
925 if handle > 0 && table.attributes.len() >= usize::from(handle) {
926 let i = usize::from(handle - 1);
927 let att = &table.attributes[i];
928 let service_uuid = att.uuid;
929 if service_uuid == Uuid::from(PRIMARY_SERVICE) || service_uuid == Uuid::from(SECONDARY_SERVICE) {
930 let last_handle_in_group = table.service_group_end(handle);
931
932 let uuid = att.data.value().and_then(|val| (val.len() == 2).then_some(val));
934
935 let mut value = [0u8; MAX_SMALL_DATA_SIZE];
937 let mut w = WriteCursor::new(&mut value);
938 w.write(handle).unwrap();
939 w.write(last_handle_in_group).unwrap();
940 if let Some(uuid) = uuid {
941 w.append(uuid).unwrap();
942 }
943 let len = w.len() as u8;
944
945 Ok(table.push(Attribute::new(
946 INCLUDE,
947 AttPermissions::read_only(),
948 AttributeData::SmallData {
949 variable_len: false,
950 capacity: len,
951 len,
952 value,
953 },
954 )))
955 } else {
956 Err(InvalidHandle)
957 }
958 } else {
959 Err(InvalidHandle)
960 }
961 })
962 }
963
964 pub fn build(self) -> u16 {
966 self.handle
967 }
968}
969
970#[cfg_attr(feature = "defmt", derive(defmt::Format))]
972#[derive(Clone, Copy, Debug, PartialEq)]
973pub struct Characteristic<T: AsGatt + ?Sized> {
974 pub cccd_handle: Option<u16>,
976 pub handle: u16,
978 pub end_handle: u16,
980 pub props: CharacteristicProps,
982 pub uuid: Uuid,
984 pub(crate) phantom: PhantomData<T>,
985}
986
987impl<T: AsGatt + ?Sized> Characteristic<T> {
988 pub fn should_notify<P: PacketPool>(&self, connection: &GattConnection<'_, '_, P>) -> bool {
990 let Some(cccd_handle) = self.cccd_handle else {
991 return false;
992 };
993 connection.server.should_notify(connection.raw(), cccd_handle)
994 }
995
996 pub async fn notify<P: PacketPool>(
1004 &self,
1005 connection: &GattConnection<'_, '_, P>,
1006 value: &T,
1007 store: bool,
1008 ) -> Result<(), Error> {
1009 self.notify_raw(connection, value.as_gatt(), store).await
1010 }
1011
1012 pub async fn notify_raw<P: PacketPool>(
1020 &self,
1021 connection: &GattConnection<'_, '_, P>,
1022 value: &[u8],
1023 store: bool,
1024 ) -> Result<(), Error> {
1025 let server = connection.server;
1026 if store {
1027 server.set(connection.raw(), self.handle, value)?;
1028 }
1029
1030 let cccd_handle = self.cccd_handle.ok_or(Error::NotFound)?;
1031 let conn = connection.raw();
1032 if !server.should_notify(conn, cccd_handle) {
1033 return Ok(());
1035 }
1036
1037 self.authorize_unsolicited(connection, cccd_handle).await?;
1038
1039 let uns = AttUns::Notify {
1040 handle: self.handle,
1041 data: value,
1042 };
1043 let pdu = gatt::assemble(conn, crate::att::AttServer::Unsolicited(uns))?;
1044 conn.send(pdu).await;
1045 Ok(())
1046 }
1047
1048 pub fn should_indicate<P: PacketPool>(&self, connection: &GattConnection<'_, '_, P>) -> bool {
1050 let Some(cccd_handle) = self.cccd_handle else {
1051 return false;
1052 };
1053 connection.server.should_notify(connection.raw(), cccd_handle)
1054 }
1055
1056 pub async fn indicate<P: PacketPool>(
1070 &self,
1071 connection: &GattConnection<'_, '_, P>,
1072 value: &T,
1073 store: bool,
1074 ) -> Result<(), Error> {
1075 self.indicate_raw(connection, value.as_gatt(), store).await
1076 }
1077
1078 pub async fn indicate_raw<P: PacketPool>(
1092 &self,
1093 connection: &GattConnection<'_, '_, P>,
1094 value: &[u8],
1095 store: bool,
1096 ) -> Result<(), Error> {
1097 let server = connection.server;
1098 if store {
1099 server.set(connection.raw(), self.handle, value)?;
1100 }
1101
1102 let cccd_handle = self.cccd_handle.ok_or(Error::NotFound)?;
1103 let conn = connection.raw();
1104 if !server.should_indicate(conn, cccd_handle) {
1105 return Ok(());
1107 }
1108
1109 self.authorize_unsolicited(connection, cccd_handle).await?;
1110
1111 conn.acquire_indication_slot().await?;
1112 let _drop = crate::host::OnDrop::new(|| conn.release_indication_slot());
1113
1114 let uns = AttUns::Indicate {
1115 handle: self.handle,
1116 data: value,
1117 };
1118 let pdu = gatt::assemble(conn, crate::att::AttServer::Unsolicited(uns))?;
1119 conn.send(pdu).await;
1120
1121 match with_timeout(ATT_TRANSACTION_TIMEOUT, conn.wait_indication_confirmation()).await {
1122 Ok(Ok(())) => Ok(()),
1123 Ok(Err(e)) => Err(e),
1124 Err(_) => {
1125 warn!("[gatt] ATT transaction timeout (30s) waiting for indication confirmation, disconnecting");
1126 conn.disconnect();
1127 Err(Error::Timeout)
1128 }
1129 }
1130 }
1131
1132 pub fn set<M: RawMutex, P: PacketPool, const AT: usize, const CN: usize>(
1134 &self,
1135 server: &AttributeServer<'_, M, P, AT, CN>,
1136 value: &T,
1137 ) -> Result<(), Error> {
1138 let value = value.as_gatt();
1139 server.table().set_raw(self.handle, value)?;
1140 Ok(())
1141 }
1142
1143 pub fn set_ro<'a, M: RawMutex, P: PacketPool, const AT: usize, const CN: usize>(
1145 &self,
1146 server: &AttributeServer<'a, M, P, AT, CN>,
1147 value: &'a T,
1148 ) -> Result<(), Error> {
1149 let value = value.as_gatt();
1150 server.table().set_ro(self.handle, value)?;
1151 Ok(())
1152 }
1153
1154 pub fn get<M: RawMutex, P: PacketPool, const AT: usize, const CN: usize>(
1159 &self,
1160 server: &AttributeServer<'_, M, P, AT, CN>,
1161 ) -> Result<T, Error>
1162 where
1163 T: FromGatt,
1164 {
1165 server.table().get(self)
1166 }
1167
1168 pub fn with_raw_value<M: RawMutex, P: PacketPool, R, const AT: usize, const CN: usize>(
1170 &self,
1171 server: &AttributeServer<'_, M, P, AT, CN>,
1172 f: impl FnOnce(&[u8]) -> R,
1173 ) -> Option<R> {
1174 server
1175 .table()
1176 .with_attribute(self.handle, |att| att.data.value().map(f))
1177 .flatten()
1178 }
1179
1180 pub fn cccd_handle(&self) -> Option<CharacteristicPropertiesHandle> {
1182 self.cccd_handle.map(CharacteristicPropertiesHandle)
1183 }
1184
1185 pub fn to_raw(self) -> Characteristic<[u8]> {
1187 Characteristic {
1188 cccd_handle: self.cccd_handle,
1189 handle: self.handle,
1190 end_handle: self.end_handle,
1191 props: self.props,
1192 uuid: self.uuid,
1193 phantom: PhantomData,
1194 }
1195 }
1196
1197 async fn authorize_unsolicited<P: PacketPool>(
1198 &self,
1199 connection: &GattConnection<'_, '_, P>,
1200 cccd_handle: u16,
1201 ) -> Result<(), Error> {
1202 let server = connection.server;
1203 let conn = connection.raw();
1204
1205 match server.can_write(conn, cccd_handle) {
1209 Ok(()) => Ok(()),
1210 Err(err) => {
1211 #[cfg(feature = "security")]
1212 if conn.is_bonded_peer() && !conn.security_level().is_ok_and(|l| l.encrypted()) {
1213 conn.try_enable_encryption().await?;
1214 server.can_write(conn, cccd_handle)?;
1215 return Ok(());
1216 }
1217 Err(err.into())
1218 }
1219 }
1220 }
1221}
1222
1223pub struct CharacteristicPropertiesHandle(u16);
1225
1226impl AttributeHandle for CharacteristicPropertiesHandle {
1227 type Value = CharacteristicProps;
1228
1229 fn handle(&self) -> u16 {
1230 self.0
1231 }
1232}
1233
1234pub struct CharacteristicBuilder<'r, 'd, T: AsGatt + ?Sized, M: RawMutex, const MAX: usize> {
1236 handle: Characteristic<T>,
1237 table: &'r mut AttributeTable<'d, M, MAX>,
1238}
1239
1240impl<'r, 'd, T: AsGatt + ?Sized, M: RawMutex, const MAX: usize> CharacteristicBuilder<'r, 'd, T, M, MAX> {
1241 fn add_descriptor_internal<DT: AsGatt + ?Sized>(
1242 &mut self,
1243 uuid: Uuid,
1244 permissions: AttPermissions,
1245 data: AttributeData<'d>,
1246 ) -> Descriptor<DT> {
1247 let handle = self.table.push(Attribute::new(uuid, permissions, data));
1248
1249 Descriptor {
1250 handle,
1251 uuid,
1252 phantom: PhantomData,
1253 }
1254 }
1255
1256 pub fn add_descriptor<DT: AsGatt, U: Into<Uuid>>(
1258 &mut self,
1259 uuid: U,
1260 permissions: AttPermissions,
1261 value: DT,
1262 store: &'d mut [u8],
1263 ) -> Descriptor<DT> {
1264 let bytes = value.as_gatt();
1265 store[..bytes.len()].copy_from_slice(bytes);
1266 let variable_len = DT::MAX_SIZE != DT::MIN_SIZE;
1267 let len = bytes.len() as u16;
1268 self.add_descriptor_internal(
1269 uuid.into(),
1270 permissions,
1271 AttributeData::Data {
1272 value: store,
1273 variable_len,
1274 len,
1275 },
1276 )
1277 }
1278
1279 pub fn add_descriptor_small<DT: AsGatt, U: Into<Uuid>>(
1281 &mut self,
1282 uuid: U,
1283 permissions: AttPermissions,
1284 value: DT,
1285 ) -> Descriptor<DT> {
1286 assert!(DT::MIN_SIZE <= MAX_SMALL_DATA_SIZE);
1287
1288 let bytes = value.as_gatt();
1289 assert!(bytes.len() <= MAX_SMALL_DATA_SIZE);
1290 let mut value = [0; MAX_SMALL_DATA_SIZE];
1291 value[..bytes.len()].copy_from_slice(bytes);
1292 let variable_len = DT::MAX_SIZE != DT::MIN_SIZE;
1293 let capacity = DT::MAX_SIZE.min(MAX_SMALL_DATA_SIZE) as u8;
1294 let len = bytes.len() as u8;
1295 self.add_descriptor_internal(
1296 uuid.into(),
1297 permissions,
1298 AttributeData::SmallData {
1299 variable_len,
1300 capacity,
1301 len,
1302 value,
1303 },
1304 )
1305 }
1306
1307 pub fn add_descriptor_ro<DT: AsGatt + ?Sized, U: Into<Uuid>>(
1309 &mut self,
1310 uuid: U,
1311 read_permission: PermissionLevel,
1312 data: &'d DT,
1313 ) -> Descriptor<DT> {
1314 let permissions = AttPermissions {
1315 write: PermissionLevel::NotAllowed,
1316 read: read_permission,
1317 #[cfg(feature = "legacy-pairing")]
1318 min_key_len: 0,
1319 };
1320 self.add_descriptor_internal(
1321 uuid.into(),
1322 permissions,
1323 AttributeData::ReadOnlyData { value: data.as_gatt() },
1324 )
1325 }
1326
1327 pub fn add_descriptor_client<DT: AsGatt, U: Into<Uuid>>(
1329 &mut self,
1330 uuid: U,
1331 permissions: AttPermissions,
1332 ) -> Descriptor<DT> {
1333 assert!(DT::MAX_SIZE <= 512);
1334 let variable_len = DT::MAX_SIZE != DT::MIN_SIZE;
1335 self.add_descriptor_internal(
1336 uuid.into(),
1337 permissions,
1338 AttributeData::ClientSpecific {
1339 variable_len,
1340 capacity: DT::MAX_SIZE as u16,
1341 },
1342 )
1343 }
1344
1345 pub fn read_permission(self, read: PermissionLevel) -> Self {
1347 self.table
1348 .with_attribute_mut(self.handle.handle, |att| att.permissions.read = read);
1349 self
1350 }
1351
1352 pub fn write_permission(self, write: PermissionLevel) -> Self {
1354 self.table
1355 .with_attribute_mut(self.handle.handle, |att| att.permissions.write = write);
1356 self
1357 }
1358
1359 pub fn cccd_permission(self, write: PermissionLevel) -> Self {
1363 let Some(handle) = self.handle.cccd_handle else {
1364 panic!("Can't set CCCD permission on characteristics without notify or indicate properties.");
1365 };
1366
1367 self.table
1368 .with_attribute_mut(handle, |att| att.permissions.write = write);
1369 self
1370 }
1371
1372 #[cfg(feature = "legacy-pairing")]
1374 pub fn min_key_len(self, len: u8) -> Self {
1375 self.table
1376 .with_attribute_mut(self.handle.handle, |att| att.permissions.min_key_len = len);
1377 self
1378 }
1379
1380 pub fn to_raw(self) -> CharacteristicBuilder<'r, 'd, [u8], M, MAX> {
1382 CharacteristicBuilder {
1383 handle: self.handle.to_raw(),
1384 table: self.table,
1385 }
1386 }
1387 pub fn build(mut self) -> Characteristic<T> {
1389 self.handle.end_handle = self.table.with_inner(|t| t.next_handle() - 1);
1390 self.handle
1391 }
1392}
1393
1394#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1396#[derive(Clone, Copy, Debug)]
1397pub struct Descriptor<T: AsGatt + ?Sized> {
1398 pub(crate) handle: u16,
1399 pub(crate) uuid: Uuid,
1400 pub(crate) phantom: PhantomData<T>,
1401}
1402
1403impl<T: AsGatt + ?Sized> AttributeHandle for Descriptor<T> {
1404 type Value = T;
1405
1406 fn handle(&self) -> u16 {
1407 self.handle
1408 }
1409}
1410
1411impl<T: AsGatt + ?Sized> Descriptor<T> {
1412 pub fn handle(&self) -> u16 {
1414 self.handle
1415 }
1416
1417 pub fn uuid(&self) -> &Uuid {
1419 &self.uuid
1420 }
1421
1422 pub fn set<M: RawMutex, P: PacketPool, const AT: usize, const CN: usize>(
1424 &self,
1425 server: &AttributeServer<'_, M, P, AT, CN>,
1426 value: &T,
1427 ) -> Result<(), Error> {
1428 let value = value.as_gatt();
1429 server.table().set_raw(self.handle, value)?;
1430 Ok(())
1431 }
1432
1433 pub fn get<M: RawMutex, P: PacketPool, const AT: usize, const CN: usize>(
1438 &self,
1439 server: &AttributeServer<'_, M, P, AT, CN>,
1440 ) -> Result<T, Error>
1441 where
1442 T: FromGatt,
1443 {
1444 server.table().get(self)
1445 }
1446}
1447
1448pub struct AttributeIterator<'a, 'd> {
1450 attributes: &'a [Attribute<'d>],
1451 pos: usize,
1452}
1453
1454impl<'a, 'd> Iterator for AttributeIterator<'a, 'd> {
1455 type Item = (u16, &'a Attribute<'d>);
1456
1457 fn next(&mut self) -> Option<Self::Item> {
1458 if self.pos < self.attributes.len() {
1459 let att = &self.attributes[self.pos];
1460 self.pos += 1;
1461 let handle = self.pos as u16;
1462 Some((handle, att))
1463 } else {
1464 None
1465 }
1466 }
1467}
1468
1469impl<'a, 'd> AttributeIterator<'a, 'd> {
1470 pub fn service_group_end(&self) -> u16 {
1474 self.attributes
1477 .iter()
1478 .enumerate()
1479 .skip(self.pos)
1480 .find(|(_, attr)| attr.uuid == PRIMARY_SERVICE.into() || attr.uuid == SECONDARY_SERVICE.into())
1481 .map(|(i, _)| i as u16)
1482 .unwrap_or(u16::MAX)
1483 }
1484
1485 pub fn characteristic_group_end(&self) -> u16 {
1487 self.attributes
1490 .iter()
1491 .enumerate()
1492 .skip(self.pos)
1493 .find(|(_, attr)| {
1494 attr.uuid == PRIMARY_SERVICE.into()
1495 || attr.uuid == SECONDARY_SERVICE.into()
1496 || attr.uuid == CHARACTERISTIC.into()
1497 })
1498 .map(|(i, _)| i as u16)
1499 .unwrap_or(self.attributes.len() as u16)
1500 }
1501}
1502
1503pub struct Service {
1505 pub uuid: Uuid,
1507}
1508
1509impl Service {
1510 pub fn new<U: Into<Uuid>>(uuid: U) -> Self {
1512 Self { uuid: uuid.into() }
1513 }
1514}
1515
1516#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1518#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1519pub struct CharacteristicProps(u8);
1520
1521impl<'a> From<&'a [CharacteristicProp]> for CharacteristicProps {
1522 fn from(props: &'a [CharacteristicProp]) -> Self {
1523 let mut val: u8 = 0;
1524 for prop in props {
1525 val |= *prop as u8;
1526 }
1527 CharacteristicProps(val)
1528 }
1529}
1530
1531impl<'a, const N: usize> From<&'a [CharacteristicProp; N]> for CharacteristicProps {
1532 fn from(props: &'a [CharacteristicProp; N]) -> Self {
1533 let mut val: u8 = 0;
1534 for prop in props {
1535 val |= *prop as u8;
1536 }
1537 CharacteristicProps(val)
1538 }
1539}
1540
1541impl<const N: usize> From<[CharacteristicProp; N]> for CharacteristicProps {
1542 fn from(props: [CharacteristicProp; N]) -> Self {
1543 let mut val: u8 = 0;
1544 for prop in props {
1545 val |= prop as u8;
1546 }
1547 CharacteristicProps(val)
1548 }
1549}
1550
1551impl From<u8> for CharacteristicProps {
1552 fn from(value: u8) -> Self {
1553 Self(value)
1554 }
1555}
1556
1557impl CharacteristicProps {
1558 pub fn any(&self, props: &[CharacteristicProp]) -> bool {
1560 for p in props {
1561 if (*p as u8) & self.0 != 0 {
1562 return true;
1563 }
1564 }
1565 false
1566 }
1567
1568 pub(crate) fn default_permissions(&self) -> AttPermissions {
1569 let read = if (self.0 & CharacteristicProp::Read as u8) != 0 {
1570 PermissionLevel::Allowed
1571 } else {
1572 PermissionLevel::NotAllowed
1573 };
1574
1575 let write = if (self.0
1576 & (CharacteristicProp::Write as u8
1577 | CharacteristicProp::WriteWithoutResponse as u8
1578 | CharacteristicProp::AuthenticatedWrite as u8))
1579 != 0
1580 {
1581 PermissionLevel::Allowed
1582 } else {
1583 PermissionLevel::NotAllowed
1584 };
1585
1586 AttPermissions {
1587 read,
1588 write,
1589 #[cfg(feature = "legacy-pairing")]
1590 min_key_len: 0,
1591 }
1592 }
1593
1594 pub fn has_cccd(&self) -> bool {
1596 (self.0 & (CharacteristicProp::Indicate as u8 | CharacteristicProp::Notify as u8)) != 0
1597 }
1598
1599 pub fn to_raw(self) -> u8 {
1601 self.0
1602 }
1603}
1604
1605impl FixedGattValue for CharacteristicProps {
1606 const SIZE: usize = 1;
1607}
1608
1609impl FromGatt for CharacteristicProps {
1610 fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
1611 if data.len() != Self::SIZE {
1612 return Err(FromGattError::InvalidLength);
1613 }
1614
1615 Ok(CharacteristicProps(data[0]))
1616 }
1617}
1618
1619impl AsGatt for CharacteristicProps {
1620 const MIN_SIZE: usize = Self::SIZE;
1621 const MAX_SIZE: usize = Self::SIZE;
1622
1623 fn as_gatt(&self) -> &[u8] {
1624 AsGatt::as_gatt(&self.0)
1625 }
1626}
1627
1628pub struct AttributeValue<'d, M: RawMutex> {
1630 value: Mutex<M, &'d mut [u8]>,
1631}
1632
1633impl<M: RawMutex> AttributeValue<'_, M> {}
1634
1635#[derive(Clone, Copy)]
1637pub enum CCCDFlag {
1638 Notify = 0x1,
1640 Indicate = 0x2,
1642}
1643
1644#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1646#[derive(Clone, Copy, Default, Debug, PartialEq)]
1647pub struct CCCD(pub(crate) u16);
1648
1649impl<const T: usize> From<[CCCDFlag; T]> for CCCD {
1650 fn from(props: [CCCDFlag; T]) -> Self {
1651 let mut val: u16 = 0;
1652 for prop in props {
1653 val |= prop as u16;
1654 }
1655 CCCD(val)
1656 }
1657}
1658
1659impl From<u16> for CCCD {
1660 fn from(value: u16) -> Self {
1661 CCCD(value)
1662 }
1663}
1664
1665impl CCCD {
1666 pub fn raw(&self) -> u16 {
1668 self.0
1669 }
1670
1671 pub fn disable(&mut self) {
1673 self.0 = 0;
1674 }
1675
1676 pub fn any(&self, props: &[CCCDFlag]) -> bool {
1678 for p in props {
1679 if (*p as u16) & self.0 != 0 {
1680 return true;
1681 }
1682 }
1683 false
1684 }
1685
1686 pub fn set_notify(&mut self, is_enabled: bool) {
1688 let mask: u16 = CCCDFlag::Notify as u16;
1689 self.0 = if is_enabled { self.0 | mask } else { self.0 & !mask };
1690 }
1691
1692 pub fn should_notify(&self) -> bool {
1694 (self.0 & (CCCDFlag::Notify as u16)) != 0
1695 }
1696
1697 pub fn set_indicate(&mut self, is_enabled: bool) {
1699 let mask: u16 = CCCDFlag::Indicate as u16;
1700 self.0 = if is_enabled { self.0 | mask } else { self.0 & !mask };
1701 }
1702
1703 pub fn should_indicate(&self) -> bool {
1705 (self.0 & (CCCDFlag::Indicate as u16)) != 0
1706 }
1707}
1708
1709#[cfg(test)]
1710mod tests {
1711 extern crate std;
1712
1713 #[cfg(feature = "security")]
1714 #[test]
1715 fn database_hash() {
1716 use bt_hci::uuid::characteristic::{
1717 APPEARANCE, CLIENT_SUPPORTED_FEATURES, DATABASE_HASH, DEVICE_NAME, SERVICE_CHANGED,
1718 };
1719 use bt_hci::uuid::declarations::{CHARACTERISTIC, PRIMARY_SERVICE};
1720 use bt_hci::uuid::descriptors::{
1721 CHARACTERISTIC_PRESENTATION_FORMAT, CHARACTERISTIC_USER_DESCRIPTION, CLIENT_CHARACTERISTIC_CONFIGURATION,
1722 };
1723 use bt_hci::uuid::service::{GAP, GATT};
1724 use embassy_sync::blocking_mutex::raw::NoopRawMutex;
1725
1726 use super::*;
1727
1728 let mut table: AttributeTable<'static, NoopRawMutex, 20> = AttributeTable::new();
1751
1752 let ro = AttPermissions::read_only();
1753 let cccd_perms = AttPermissions {
1754 read: PermissionLevel::Allowed,
1755 write: PermissionLevel::Allowed,
1756 #[cfg(feature = "legacy-pairing")]
1757 min_key_len: 0,
1758 };
1759
1760 table.push(Attribute::new(PRIMARY_SERVICE, ro, GAP));
1762
1763 let expected = 0xd4cdec10804db3f147b4d7d10baa0120;
1764 let actual = table.hash();
1765 assert_eq!(
1766 actual, expected,
1767 "\nexpected: {:#032x}\nactual: {:#032x}",
1768 expected, actual
1769 );
1770
1771 table.push(Attribute::new(
1773 CHARACTERISTIC,
1774 ro,
1775 CharacteristicDeclaration::new([CharacteristicProp::Read], 0x0003, DEVICE_NAME),
1776 ));
1777
1778 table.push(Attribute::new(
1779 DEVICE_NAME,
1780 ro,
1781 AttributeData::ReadOnlyData { value: b"" },
1782 ));
1783
1784 table.push(Attribute::new(
1786 CHARACTERISTIC,
1787 ro,
1788 CharacteristicDeclaration::new([CharacteristicProp::Read], 0x0005, APPEARANCE),
1789 ));
1790
1791 table.push(Attribute::new(
1792 APPEARANCE,
1793 ro,
1794 AttributeData::ReadOnlyData { value: b"" },
1795 ));
1796
1797 let expected = 0x6c329e3f1d52c03f174980f6b4704875;
1798 let actual = table.hash();
1799 assert_eq!(
1800 actual, expected,
1801 "\nexpected: {:#032x}\n actual: {:#032x}",
1802 expected, actual
1803 );
1804
1805 table.push(Attribute::new(PRIMARY_SERVICE, ro, GATT));
1807
1808 table.push(Attribute::new(
1810 CHARACTERISTIC,
1811 ro,
1812 CharacteristicDeclaration::new([CharacteristicProp::Indicate], 0x0008, SERVICE_CHANGED),
1813 ));
1814
1815 table.push(Attribute::new(
1816 SERVICE_CHANGED,
1817 ro,
1818 AttributeData::ReadOnlyData { value: b"" },
1819 ));
1820
1821 table.push(Attribute::new(
1822 CLIENT_CHARACTERISTIC_CONFIGURATION,
1823 cccd_perms,
1824 AttributeData::ClientSpecific {
1825 variable_len: false,
1826 capacity: 2,
1827 },
1828 ));
1829
1830 table.push(Attribute::new(
1832 CHARACTERISTIC,
1833 ro,
1834 CharacteristicDeclaration::new(
1835 [CharacteristicProp::Read, CharacteristicProp::Write],
1836 0x000b,
1837 CLIENT_SUPPORTED_FEATURES,
1838 ),
1839 ));
1840
1841 table.push(Attribute::new(
1842 CLIENT_SUPPORTED_FEATURES,
1843 ro,
1844 AttributeData::ReadOnlyData { value: b"" },
1845 ));
1846
1847 table.push(Attribute::new(
1849 CHARACTERISTIC,
1850 ro,
1851 CharacteristicDeclaration::new([CharacteristicProp::Read], 0x000d, DATABASE_HASH),
1852 ));
1853
1854 table.push(Attribute::new(
1855 DATABASE_HASH,
1856 ro,
1857 AttributeData::ReadOnlyData { value: b"" },
1858 ));
1859
1860 let expected = 0x16ce756326c5062bf74022f845c2b21f;
1861 let actual = table.hash();
1862 assert_eq!(
1863 actual, expected,
1864 "\nexpected: {:#032x}\n actual: {:#032x}",
1865 expected, actual
1866 );
1867
1868 const CUSTOM_SERVICE: u128 = 0x12345678_12345678_12345678_9abcdef0;
1869 const CUSTOM_CHARACTERISTIC: u128 = 0x12345678_12345678_12345678_9abcdef1;
1870
1871 table.push(Attribute::new(PRIMARY_SERVICE, ro, Uuid::from(CUSTOM_SERVICE)));
1873
1874 table.push(Attribute::new(
1876 CHARACTERISTIC,
1877 ro,
1878 CharacteristicDeclaration::new(
1879 [CharacteristicProp::Notify, CharacteristicProp::Read],
1880 0x0010,
1881 CUSTOM_CHARACTERISTIC,
1882 ),
1883 ));
1884
1885 table.push(Attribute::new(
1886 CUSTOM_CHARACTERISTIC,
1887 ro,
1888 AttributeData::ReadOnlyData { value: b"" },
1889 ));
1890
1891 table.push(Attribute::new(
1892 CLIENT_CHARACTERISTIC_CONFIGURATION,
1893 cccd_perms,
1894 AttributeData::ClientSpecific {
1895 variable_len: false,
1896 capacity: 2,
1897 },
1898 ));
1899
1900 table.push(Attribute::new(
1901 CHARACTERISTIC_USER_DESCRIPTION,
1902 ro,
1903 AttributeData::ReadOnlyData {
1904 value: b"Custom Characteristic",
1905 },
1906 ));
1907
1908 table.push(Attribute::new(
1909 CHARACTERISTIC_PRESENTATION_FORMAT,
1910 ro,
1911 AttributeData::ReadOnlyData {
1912 value: &[4, 0, 0, 0x27, 1, 0, 0],
1913 },
1914 ));
1915
1916 let expected = 0xc7352cced28d6608d4b057d247d8be76;
1917 let actual = table.hash();
1918 assert_eq!(
1919 actual, expected,
1920 "\nexpected: {:#032x}\n actual: {:#032x}",
1921 expected, actual
1922 );
1923 }
1924
1925 #[test]
1926 fn set_updates_variable_length_when_value_fills_backing_storage() {
1927 use embassy_sync::blocking_mutex::raw::NoopRawMutex;
1928 use heapless::Vec;
1929
1930 use super::*;
1931
1932 let mut storage = [0u8; 4];
1933 let mut table: AttributeTable<'_, NoopRawMutex, 4> = AttributeTable::new();
1934 let initial = Vec::<u8, 4>::from_slice(b"ab").unwrap();
1935 let characteristic = table
1936 .add_service(Service {
1937 uuid: Uuid::new_long([0x10; 16]),
1938 })
1939 .add_characteristic(
1940 Uuid::new_long([0x11; 16]),
1941 [CharacteristicProp::Read, CharacteristicProp::Write],
1942 initial,
1943 &mut storage,
1944 )
1945 .build();
1946
1947 let replacement = Vec::<u8, 4>::from_slice(b"wxyz").unwrap();
1948 table.set(&characteristic, &replacement).unwrap();
1949
1950 let stored: Vec<u8, 4> = table.get(&characteristic).unwrap();
1951 assert_eq!(stored.as_slice(), b"wxyz");
1952 }
1953
1954 #[test]
1955 fn set_updates_small_variable_length_when_value_reaches_capacity() {
1956 use embassy_sync::blocking_mutex::raw::NoopRawMutex;
1957 use heapless::String;
1958
1959 use super::*;
1960
1961 let mut table: AttributeTable<'_, NoopRawMutex, 4> = AttributeTable::new();
1962 let initial = String::<8>::try_from("hi").unwrap();
1963 let characteristic = table
1964 .add_service(Service {
1965 uuid: Uuid::new_long([0x12; 16]),
1966 })
1967 .add_characteristic_small(
1968 Uuid::new_long([0x13; 16]),
1969 [CharacteristicProp::Read, CharacteristicProp::Write],
1970 initial,
1971 )
1972 .build();
1973
1974 let replacement = String::<8>::try_from("12345678").unwrap();
1975 table.set(&characteristic, &replacement).unwrap();
1976
1977 let stored: String<8> = table.get(&characteristic).unwrap();
1978 assert_eq!(stored.as_str(), "12345678");
1979 }
1980}