1use core::cell::RefCell;
2use core::marker::PhantomData;
3
4use embassy_sync::blocking_mutex::raw::RawMutex;
5use embassy_sync::blocking_mutex::Mutex;
6
7use crate::att::{self, AttClient, AttCmd, AttErrorCode, AttReq};
8use crate::attribute::{Attribute, AttributeData, AttributeTable, CCCD};
9use crate::cursor::WriteCursor;
10use crate::prelude::Connection;
11use crate::types::uuid::Uuid;
12use crate::{codec, Error, Identity, PacketPool};
13
14#[derive(Default)]
15struct Client {
16 identity: Identity,
17 is_connected: bool,
18}
19
20impl Client {
21 fn set_identity(&mut self, identity: Identity) {
22 self.identity = identity;
23 }
24}
25
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28#[derive(Clone, Debug)]
29pub struct CccdTable<const ENTRIES: usize> {
30 inner: [(u16, CCCD); ENTRIES],
31}
32
33impl<const ENTRIES: usize> Default for CccdTable<ENTRIES> {
34 fn default() -> Self {
35 Self {
36 inner: [(0, CCCD(0)); ENTRIES],
37 }
38 }
39}
40
41impl<const ENTRIES: usize> CccdTable<ENTRIES> {
42 pub fn new(cccd_values: [(u16, CCCD); ENTRIES]) -> Self {
44 Self { inner: cccd_values }
45 }
46
47 pub fn inner(&self) -> &[(u16, CCCD); ENTRIES] {
49 &self.inner
50 }
51
52 fn add_handle(&mut self, cccd_handle: u16) {
53 for (handle, _) in self.inner.iter_mut() {
54 if *handle == 0 {
55 *handle = cccd_handle;
56 break;
57 }
58 }
59 }
60
61 fn disable_all(&mut self) {
62 for (_, value) in self.inner.iter_mut() {
63 value.disable();
64 }
65 }
66
67 fn get_raw(&self, cccd_handle: u16) -> Option<[u8; 2]> {
68 for (handle, value) in self.inner.iter() {
69 if *handle == cccd_handle {
70 return Some(value.raw().to_le_bytes());
71 }
72 }
73 None
74 }
75
76 fn set_notify(&mut self, cccd_handle: u16, is_enabled: bool) {
77 for (handle, value) in self.inner.iter_mut() {
78 if *handle == cccd_handle {
79 trace!("[cccd] set_notify({}) = {}", cccd_handle, is_enabled);
80 value.set_notify(is_enabled);
81 break;
82 }
83 }
84 }
85
86 fn should_notify(&self, cccd_handle: u16) -> bool {
87 for (handle, value) in self.inner.iter() {
88 if *handle == cccd_handle {
89 return value.should_notify();
90 }
91 }
92 false
93 }
94}
95
96struct CccdTables<M: RawMutex, const CCCD_MAX: usize, const CONN_MAX: usize> {
98 state: Mutex<M, RefCell<[(Client, CccdTable<CCCD_MAX>); CONN_MAX]>>,
99}
100
101impl<M: RawMutex, const CCCD_MAX: usize, const CONN_MAX: usize> CccdTables<M, CCCD_MAX, CONN_MAX> {
102 fn new<const ATT_MAX: usize>(att_table: &AttributeTable<'_, M, ATT_MAX>) -> Self {
103 let mut values: [(Client, CccdTable<CCCD_MAX>); CONN_MAX] =
104 core::array::from_fn(|_| (Client::default(), CccdTable::default()));
105 let mut base_cccd_table = CccdTable::default();
106 att_table.iterate(|mut at| {
107 while let Some(att) = at.next() {
108 if let AttributeData::Cccd { .. } = att.data {
109 base_cccd_table.add_handle(att.handle);
110 }
111 }
112 });
113 for (_, table) in values.iter_mut() {
115 *table = base_cccd_table.clone();
116 }
117 Self {
118 state: Mutex::new(RefCell::new(values)),
119 }
120 }
121
122 fn connect(&self, peer_identity: &Identity) -> Result<(), Error> {
123 self.state.lock(|n| {
124 trace!("[server] searching for peer {:?}", peer_identity);
125 let mut n = n.borrow_mut();
126 let empty_slot = Identity::default();
127 for (client, table) in n.iter_mut() {
128 if client.identity.match_identity(peer_identity) {
129 client.is_connected = true;
131 return Ok(());
132 } else if client.identity == empty_slot {
133 client.is_connected = true;
135 client.set_identity(*peer_identity);
136 return Ok(());
137 }
138 }
139 trace!("[server] all slots full...");
140 for (client, table) in n.iter_mut() {
142 if !client.is_connected {
143 trace!("[server] booting disconnected peer {:?}", client.identity);
144 client.is_connected = true;
145 client.set_identity(*peer_identity);
146 table.disable_all();
148 return Ok(());
149 }
150 }
151 warn!("[server] unable to obtain CCCD slot");
154 Err(Error::ConnectionLimitReached)
155 })
156 }
157
158 fn disconnect(&self, peer_identity: &Identity) {
159 self.state.lock(|n| {
160 let mut n = n.borrow_mut();
161 for (client, _) in n.iter_mut() {
162 if client.identity.match_identity(peer_identity) {
163 client.is_connected = false;
164 break;
165 }
166 }
167 })
168 }
169
170 fn get_value(&self, peer_identity: &Identity, cccd_handle: u16) -> Option<[u8; 2]> {
171 self.state.lock(|n| {
172 let n = n.borrow();
173 for (client, table) in n.iter() {
174 if client.identity.match_identity(peer_identity) {
175 return table.get_raw(cccd_handle);
176 }
177 }
178 None
179 })
180 }
181
182 fn set_notify(&self, peer_identity: &Identity, cccd_handle: u16, is_enabled: bool) {
183 self.state.lock(|n| {
184 let mut n = n.borrow_mut();
185 for (client, table) in n.iter_mut() {
186 if client.identity.match_identity(peer_identity) {
187 table.set_notify(cccd_handle, is_enabled);
188 break;
189 }
190 }
191 })
192 }
193
194 fn should_notify(&self, peer_identity: &Identity, cccd_handle: u16) -> bool {
195 self.state.lock(|n| {
196 let n = n.borrow();
197 for (client, table) in n.iter() {
198 if client.identity.match_identity(peer_identity) {
199 return table.should_notify(cccd_handle);
200 }
201 }
202 false
203 })
204 }
205
206 fn get_cccd_table(&self, peer_identity: &Identity) -> Option<CccdTable<CCCD_MAX>> {
207 self.state.lock(|n| {
208 let n = n.borrow();
209 for (client, table) in n.iter() {
210 if client.identity.match_identity(peer_identity) {
211 return Some(table.clone());
212 }
213 }
214 None
215 })
216 }
217
218 fn set_cccd_table(&self, peer_identity: &Identity, table: CccdTable<CCCD_MAX>) {
219 self.state.lock(|n| {
220 let mut n = n.borrow_mut();
221 for (client, t) in n.iter_mut() {
222 if client.identity.match_identity(peer_identity) {
223 trace!("Setting cccd table {:?} for {:?}", table, peer_identity);
224 *t = table;
225 break;
226 }
227 }
228 })
229 }
230
231 fn update_identity(&self, identity: Identity) -> Result<(), Error> {
232 self.state.lock(|n| {
233 let mut n = n.borrow_mut();
234 for (client, _) in n.iter_mut() {
235 if identity.match_identity(&client.identity) {
236 client.set_identity(identity);
237 return Ok(());
238 }
239 }
240 Err(Error::NotFound)
241 })
242 }
243}
244
245pub struct AttributeServer<
247 'values,
248 M: RawMutex,
249 P: PacketPool,
250 const ATT_MAX: usize,
251 const CCCD_MAX: usize,
252 const CONN_MAX: usize,
253> {
254 att_table: AttributeTable<'values, M, ATT_MAX>,
255 cccd_tables: CccdTables<M, CCCD_MAX, CONN_MAX>,
256 _p: PhantomData<P>,
257}
258
259pub(crate) mod sealed {
260 use super::*;
261
262 pub trait DynamicAttributeServer<P: PacketPool> {
263 fn connect(&self, connection: &Connection<'_, P>) -> Result<(), Error>;
264 fn disconnect(&self, connection: &Connection<'_, P>);
265 fn process(
266 &self,
267 connection: &Connection<'_, P>,
268 packet: &AttClient,
269 rx: &mut [u8],
270 ) -> Result<Option<usize>, Error>;
271 fn should_notify(&self, connection: &Connection<'_, P>, cccd_handle: u16) -> bool;
272 fn set(&self, characteristic: u16, input: &[u8]) -> Result<(), Error>;
273 fn update_identity(&self, identity: Identity) -> Result<(), Error>;
274 }
275}
276
277pub trait DynamicAttributeServer<P: PacketPool>: sealed::DynamicAttributeServer<P> {}
279
280impl<M: RawMutex, P: PacketPool, const ATT_MAX: usize, const CCCD_MAX: usize, const CONN_MAX: usize>
281 DynamicAttributeServer<P> for AttributeServer<'_, M, P, ATT_MAX, CCCD_MAX, CONN_MAX>
282{
283}
284impl<M: RawMutex, P: PacketPool, const ATT_MAX: usize, const CCCD_MAX: usize, const CONN_MAX: usize>
285 sealed::DynamicAttributeServer<P> for AttributeServer<'_, M, P, ATT_MAX, CCCD_MAX, CONN_MAX>
286{
287 fn connect(&self, connection: &Connection<'_, P>) -> Result<(), Error> {
288 AttributeServer::connect(self, connection)
289 }
290
291 fn disconnect(&self, connection: &Connection<'_, P>) {
292 self.cccd_tables.disconnect(&connection.peer_identity());
293 }
294
295 fn process(
296 &self,
297 connection: &Connection<'_, P>,
298 packet: &AttClient,
299 rx: &mut [u8],
300 ) -> Result<Option<usize>, Error> {
301 let res = AttributeServer::process(self, connection, packet, rx)?;
302 Ok(res)
303 }
304
305 fn should_notify(&self, connection: &Connection<'_, P>, cccd_handle: u16) -> bool {
306 AttributeServer::should_notify(self, connection, cccd_handle)
307 }
308
309 fn set(&self, characteristic: u16, input: &[u8]) -> Result<(), Error> {
310 self.att_table.set_raw(characteristic, input)
311 }
312
313 fn update_identity(&self, identity: Identity) -> Result<(), Error> {
314 self.cccd_tables.update_identity(identity)
315 }
316}
317
318impl<'values, M: RawMutex, P: PacketPool, const ATT_MAX: usize, const CCCD_MAX: usize, const CONN_MAX: usize>
319 AttributeServer<'values, M, P, ATT_MAX, CCCD_MAX, CONN_MAX>
320{
321 pub fn new(
323 att_table: AttributeTable<'values, M, ATT_MAX>,
324 ) -> AttributeServer<'values, M, P, ATT_MAX, CCCD_MAX, CONN_MAX> {
325 let cccd_tables = CccdTables::new(&att_table);
326 AttributeServer {
327 att_table,
328 cccd_tables,
329 _p: PhantomData,
330 }
331 }
332
333 pub(crate) fn connect(&self, connection: &Connection<'_, P>) -> Result<(), Error> {
334 self.cccd_tables.connect(&connection.peer_identity())
335 }
336
337 pub(crate) fn should_notify(&self, connection: &Connection<'_, P>, cccd_handle: u16) -> bool {
338 self.cccd_tables.should_notify(&connection.peer_identity(), cccd_handle)
339 }
340
341 fn read_attribute_data(
342 &self,
343 connection: &Connection<'_, P>,
344 offset: usize,
345 att: &mut Attribute<'values>,
346 data: &mut [u8],
347 ) -> Result<usize, AttErrorCode> {
348 if let AttributeData::Cccd { .. } = att.data {
349 if let Some(value) = self.cccd_tables.get_value(&connection.peer_identity(), att.handle) {
353 let _ = att.write(0, value.as_slice());
354 }
355 }
356 att.read(offset, data)
357 }
358
359 fn write_attribute_data(
360 &self,
361 connection: &Connection<'_, P>,
362 offset: usize,
363 att: &mut Attribute<'values>,
364 data: &[u8],
365 ) -> Result<(), AttErrorCode> {
366 let err = att.write(offset, data);
367 if err.is_ok() {
368 if let AttributeData::Cccd {
369 notifications,
370 indications,
371 } = att.data
372 {
373 self.cccd_tables
374 .set_notify(&connection.peer_identity(), att.handle, notifications);
375 }
376 }
377 err
378 }
379
380 fn handle_read_by_type_req(
381 &self,
382 connection: &Connection<'_, P>,
383 buf: &mut [u8],
384 start: u16,
385 end: u16,
386 attribute_type: &Uuid,
387 ) -> Result<usize, codec::Error> {
388 let mut handle = start;
389 let mut data = WriteCursor::new(buf);
390
391 let (mut header, mut body) = data.split(2)?;
392 let err = self.att_table.iterate(|mut it| {
393 let mut err = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
394 while let Some(att) = it.next() {
395 if &att.uuid == attribute_type && att.handle >= start && att.handle <= end {
397 body.write(att.handle)?;
398 handle = att.handle;
399
400 err = self.read_attribute_data(connection, 0, att, body.write_buf());
401 if let Ok(len) = err {
402 body.commit(len)?;
403 }
404
405 break;
407 }
408 }
409 err
410 });
411
412 match err {
413 Ok(len) => {
414 header.write(att::ATT_READ_BY_TYPE_RSP)?;
415 header.write(2 + len as u8)?;
416 Ok(header.len() + body.len())
417 }
418 Err(e) => Ok(Self::error_response(data, att::ATT_READ_BY_TYPE_REQ, handle, e)?),
419 }
420 }
421
422 fn handle_read_by_group_type_req(
423 &self,
424 connection: &Connection<'_, P>,
425 buf: &mut [u8],
426 start: u16,
427 end: u16,
428 group_type: &Uuid,
429 ) -> Result<usize, codec::Error> {
430 let mut handle = start;
432 let mut data = WriteCursor::new(buf);
433
434 let (mut header, mut body) = data.split(2)?;
435 let err = self.att_table.iterate(|mut it| {
436 let mut err = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
437 while let Some(att) = it.next() {
438 if &att.uuid == group_type && att.handle >= start && att.handle <= end {
440 handle = att.handle;
442
443 body.write(att.handle)?;
444 body.write(att.last_handle_in_group)?;
445 err = self.read_attribute_data(connection, 0, att, body.write_buf());
446 if let Ok(len) = err {
447 body.commit(len)?;
448 }
449 break;
450 }
451 }
452 err
453 });
454
455 match err {
456 Ok(len) => {
457 header.write(att::ATT_READ_BY_GROUP_TYPE_RSP)?;
458 header.write(4 + len as u8)?;
459 Ok(header.len() + body.len())
460 }
461 Err(e) => Ok(Self::error_response(data, att::ATT_READ_BY_GROUP_TYPE_REQ, handle, e)?),
462 }
463 }
464
465 fn handle_read_req(
466 &self,
467 connection: &Connection<'_, P>,
468 buf: &mut [u8],
469 handle: u16,
470 ) -> Result<usize, codec::Error> {
471 let mut data = WriteCursor::new(buf);
472
473 data.write(att::ATT_READ_RSP)?;
474
475 let err = self.att_table.iterate(|mut it| {
476 let mut err = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
477 while let Some(att) = it.next() {
478 if att.handle == handle {
479 err = self.read_attribute_data(connection, 0, att, data.write_buf());
480 if let Ok(len) = err {
481 data.commit(len)?;
482 }
483 break;
484 }
485 }
486 err
487 });
488
489 match err {
490 Ok(_) => Ok(data.len()),
491 Err(e) => Ok(Self::error_response(data, att::ATT_READ_REQ, handle, e)?),
492 }
493 }
494
495 fn handle_write_cmd(
496 &self,
497 connection: &Connection<'_, P>,
498 buf: &mut [u8],
499 handle: u16,
500 data: &[u8],
501 ) -> Result<usize, codec::Error> {
502 self.att_table.iterate(|mut it| {
503 while let Some(att) = it.next() {
504 if att.handle == handle {
505 let _ = self.write_attribute_data(connection, 0, att, data);
507 break;
508 }
509 }
510 });
511 Ok(0)
512 }
513
514 fn handle_write_req(
515 &self,
516 connection: &Connection<'_, P>,
517 buf: &mut [u8],
518 handle: u16,
519 data: &[u8],
520 ) -> Result<usize, codec::Error> {
521 let err = self.att_table.iterate(|mut it| {
522 let mut err = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
523 while let Some(att) = it.next() {
524 if att.handle == handle {
525 err = self.write_attribute_data(connection, 0, att, data);
526 break;
527 }
528 }
529 err
530 });
531
532 let mut w = WriteCursor::new(buf);
533 match err {
534 Ok(()) => {
535 w.write(att::ATT_WRITE_RSP)?;
536 Ok(w.len())
537 }
538 Err(e) => Ok(Self::error_response(w, att::ATT_WRITE_REQ, handle, e)?),
539 }
540 }
541
542 fn handle_find_type_value(
543 &self,
544 buf: &mut [u8],
545 start: u16,
546 end: u16,
547 attr_type: u16,
548 attr_value: &[u8],
549 ) -> Result<usize, codec::Error> {
550 let mut w = WriteCursor::new(buf);
551 let attr_type = Uuid::new_short(attr_type);
552
553 w.write(att::ATT_FIND_BY_TYPE_VALUE_RSP)?;
554 self.att_table.iterate(|mut it| {
555 while let Some(att) = it.next() {
556 if att.handle >= start && att.handle <= end && att.uuid == attr_type {
557 if let AttributeData::Service { uuid } = &att.data {
558 if uuid.as_raw() == attr_value {
559 if w.available() < 4 + uuid.as_raw().len() {
560 break;
561 }
562 w.write(att.handle)?;
563 w.write(att.last_handle_in_group)?;
564 }
565 }
566 }
567 }
568 Ok::<(), codec::Error>(())
569 })?;
570 if w.len() > 1 {
571 Ok(w.len())
572 } else {
573 Ok(Self::error_response(
574 w,
575 att::ATT_FIND_BY_TYPE_VALUE_REQ,
576 start,
577 AttErrorCode::ATTRIBUTE_NOT_FOUND,
578 )?)
579 }
580 }
581
582 fn handle_find_information(&self, buf: &mut [u8], start: u16, end: u16) -> Result<usize, codec::Error> {
583 let mut w = WriteCursor::new(buf);
584
585 let (mut header, mut body) = w.split(2)?;
586
587 header.write(att::ATT_FIND_INFORMATION_RSP)?;
588 let mut t = 0;
589
590 self.att_table.iterate(|mut it| {
591 while let Some(att) = it.next() {
592 if att.handle >= start && att.handle <= end {
593 if t == 0 {
594 t = att.uuid.get_type();
595 } else if t != att.uuid.get_type() {
596 break;
597 }
598 body.write(att.handle)?;
599 body.append(att.uuid.as_raw())?;
600 }
601 }
602 Ok::<(), codec::Error>(())
603 })?;
604 header.write(t)?;
605
606 if body.len() > 2 {
607 Ok(header.len() + body.len())
608 } else {
609 Ok(Self::error_response(
610 w,
611 att::ATT_FIND_INFORMATION_REQ,
612 start,
613 AttErrorCode::ATTRIBUTE_NOT_FOUND,
614 )?)
615 }
616 }
617
618 fn error_response(
619 mut w: WriteCursor<'_>,
620 opcode: u8,
621 handle: u16,
622 code: AttErrorCode,
623 ) -> Result<usize, codec::Error> {
624 w.reset();
625 w.write(att::ATT_ERROR_RSP)?;
626 w.write(opcode)?;
627 w.write(handle)?;
628 w.write(code)?;
629 Ok(w.len())
630 }
631
632 fn handle_prepare_write(
633 &self,
634 connection: &Connection<'_, P>,
635 buf: &mut [u8],
636 handle: u16,
637 offset: u16,
638 value: &[u8],
639 ) -> Result<usize, codec::Error> {
640 let mut w = WriteCursor::new(buf);
641 w.write(att::ATT_PREPARE_WRITE_RSP)?;
642 w.write(handle)?;
643 w.write(offset)?;
644
645 let err = self.att_table.iterate(|mut it| {
646 let mut err = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
647 while let Some(att) = it.next() {
648 if att.handle == handle {
649 err = self.write_attribute_data(connection, offset as usize, att, value);
650 w.append(value)?;
651 break;
652 }
653 }
654 err
655 });
656
657 match err {
658 Ok(()) => Ok(w.len()),
659 Err(e) => Ok(Self::error_response(w, att::ATT_PREPARE_WRITE_REQ, handle, e)?),
660 }
661 }
662
663 fn handle_execute_write(&self, buf: &mut [u8], _flags: u8) -> Result<usize, codec::Error> {
664 let mut w = WriteCursor::new(buf);
665 w.write(att::ATT_EXECUTE_WRITE_RSP)?;
666 Ok(w.len())
667 }
668
669 fn handle_read_blob(
670 &self,
671 connection: &Connection<'_, P>,
672 buf: &mut [u8],
673 handle: u16,
674 offset: u16,
675 ) -> Result<usize, codec::Error> {
676 let mut w = WriteCursor::new(buf);
677 w.write(att::ATT_READ_BLOB_RSP)?;
678
679 let err = self.att_table.iterate(|mut it| {
680 let mut err = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
681 while let Some(att) = it.next() {
682 if att.handle == handle {
683 err = self.read_attribute_data(connection, offset as usize, att, w.write_buf());
684 if let Ok(n) = err {
685 w.commit(n)?;
686 }
687 break;
688 }
689 }
690 err
691 });
692
693 match err {
694 Ok(_) => Ok(w.len()),
695 Err(e) => Ok(Self::error_response(w, att::ATT_READ_BLOB_REQ, handle, e)?),
696 }
697 }
698
699 fn handle_read_multiple(&self, buf: &mut [u8], handles: &[u8]) -> Result<usize, codec::Error> {
700 let w = WriteCursor::new(buf);
701 Self::error_response(
702 w,
703 att::ATT_READ_MULTIPLE_REQ,
704 u16::from_le_bytes([handles[0], handles[1]]),
705 AttErrorCode::ATTRIBUTE_NOT_FOUND,
706 )
707 }
708
709 pub fn process(
711 &self,
712 connection: &Connection<'_, P>,
713 packet: &AttClient,
714 rx: &mut [u8],
715 ) -> Result<Option<usize>, codec::Error> {
716 let len = match packet {
717 AttClient::Request(AttReq::ReadByType {
718 start,
719 end,
720 attribute_type,
721 }) => self.handle_read_by_type_req(connection, rx, *start, *end, attribute_type)?,
722
723 AttClient::Request(AttReq::ReadByGroupType { start, end, group_type }) => {
724 self.handle_read_by_group_type_req(connection, rx, *start, *end, group_type)?
725 }
726 AttClient::Request(AttReq::FindInformation {
727 start_handle,
728 end_handle,
729 }) => self.handle_find_information(rx, *start_handle, *end_handle)?,
730
731 AttClient::Request(AttReq::Read { handle }) => self.handle_read_req(connection, rx, *handle)?,
732
733 AttClient::Command(AttCmd::Write { handle, data }) => {
734 self.handle_write_cmd(connection, rx, *handle, data)?;
735 0
736 }
737
738 AttClient::Request(AttReq::Write { handle, data }) => {
739 self.handle_write_req(connection, rx, *handle, data)?
740 }
741
742 AttClient::Request(AttReq::ExchangeMtu { mtu }) => 0, AttClient::Request(AttReq::FindByTypeValue {
745 start_handle,
746 end_handle,
747 att_type,
748 att_value,
749 }) => self.handle_find_type_value(rx, *start_handle, *end_handle, *att_type, att_value)?,
750
751 AttClient::Request(AttReq::PrepareWrite { handle, offset, value }) => {
752 self.handle_prepare_write(connection, rx, *handle, *offset, value)?
753 }
754
755 AttClient::Request(AttReq::ExecuteWrite { flags }) => self.handle_execute_write(rx, *flags)?,
756
757 AttClient::Request(AttReq::ReadBlob { handle, offset }) => {
758 self.handle_read_blob(connection, rx, *handle, *offset)?
759 }
760
761 AttClient::Request(AttReq::ReadMultiple { handles }) => self.handle_read_multiple(rx, handles)?,
762
763 AttClient::Confirmation(_) => 0,
764 };
765 if len > 0 {
766 Ok(Some(len))
767 } else {
768 Ok(None)
769 }
770 }
771
772 pub fn table(&self) -> &AttributeTable<'values, M, ATT_MAX> {
774 &self.att_table
775 }
776
777 pub fn get_cccd_table(&self, connection: &Connection<'_, P>) -> Option<CccdTable<CCCD_MAX>> {
779 self.cccd_tables.get_cccd_table(&connection.peer_identity())
780 }
781
782 pub fn set_cccd_table(&self, connection: &Connection<'_, P>, table: CccdTable<CCCD_MAX>) {
784 self.cccd_tables.set_cccd_table(&connection.peer_identity(), table);
785 }
786}