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 ret = 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 let new_ret = self.read_attribute_data(connection, 0, att, body.write_buf());
401 match (new_ret, ret) {
402 (Ok(first_length), Err(_)) => {
403 ret = new_ret;
406 body.commit(first_length)?;
407 }
408 (Ok(new_length), Ok(old_length)) => {
409 if new_length == old_length {
411 body.commit(new_length)?;
413 } else {
414 body.truncate(body.len() - 2);
417 break;
419 }
420 }
421 (Err(error_code), Ok(_old_length)) => {
422 body.truncate(body.len() - 2);
425 break;
428 }
429 (Err(_), Err(_)) => {
430 ret = new_ret;
432 break;
433 }
434 }
435 if let Ok(expected_length) = ret {
438 if body.available() < expected_length + 2 {
439 break;
440 }
441 }
442 }
443 }
444 ret
445 });
446
447 match err {
448 Ok(len) => {
449 header.write(att::ATT_READ_BY_TYPE_RSP)?;
450 header.write(2 + len as u8)?;
451 Ok(header.len() + body.len())
452 }
453 Err(e) => Ok(Self::error_response(data, att::ATT_READ_BY_TYPE_REQ, handle, e)?),
454 }
455 }
456
457 fn handle_read_by_group_type_req(
458 &self,
459 connection: &Connection<'_, P>,
460 buf: &mut [u8],
461 start: u16,
462 end: u16,
463 group_type: &Uuid,
464 ) -> Result<usize, codec::Error> {
465 let mut handle = start;
466 let mut data = WriteCursor::new(buf);
467 let (mut header, mut body) = data.split(2)?;
468 let err = self.att_table.iterate(|mut it| {
470 let mut ret: Result<usize, AttErrorCode> = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
472 while let Some(att) = it.next() {
473 if &att.uuid == group_type && att.handle >= start && att.handle <= end {
475 handle = att.handle;
477
478 body.write(att.handle)?;
479 body.write(att.last_handle_in_group)?;
480 let new_ret = self.read_attribute_data(connection, 0, att, body.write_buf());
481 match (new_ret, ret) {
482 (Ok(first_length), Err(_)) => {
483 ret = new_ret;
486 body.commit(first_length)?;
487 }
488 (Ok(new_length), Ok(old_length)) => {
489 if new_length == old_length {
491 body.commit(new_length)?;
493 } else {
494 body.truncate(body.len() - 4);
497 break;
499 }
500 }
501 (Err(error_code), Ok(_old_length)) => {
502 body.truncate(body.len() - 4);
505 break;
508 }
509 (Err(_), Err(_)) => {
510 ret = new_ret;
512 break;
513 }
514 }
515 if let Ok(expected_length) = ret {
518 if body.available() < expected_length + 4 {
519 break;
520 }
521 }
522 }
523 }
524 ret
525 });
526
527 match err {
528 Ok(len) => {
529 header.write(att::ATT_READ_BY_GROUP_TYPE_RSP)?;
530 header.write(4 + len as u8)?;
531 Ok(header.len() + body.len())
532 }
533 Err(e) => Ok(Self::error_response(data, att::ATT_READ_BY_GROUP_TYPE_REQ, handle, e)?),
534 }
535 }
536
537 fn handle_read_req(
538 &self,
539 connection: &Connection<'_, P>,
540 buf: &mut [u8],
541 handle: u16,
542 ) -> Result<usize, codec::Error> {
543 let mut data = WriteCursor::new(buf);
544
545 data.write(att::ATT_READ_RSP)?;
546
547 let err = self.att_table.iterate(|mut it| {
548 let mut err = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
549 while let Some(att) = it.next() {
550 if att.handle == handle {
551 err = self.read_attribute_data(connection, 0, att, data.write_buf());
552 if let Ok(len) = err {
553 data.commit(len)?;
554 }
555 break;
556 }
557 }
558 err
559 });
560
561 match err {
562 Ok(_) => Ok(data.len()),
563 Err(e) => Ok(Self::error_response(data, att::ATT_READ_REQ, handle, e)?),
564 }
565 }
566
567 fn handle_write_cmd(
568 &self,
569 connection: &Connection<'_, P>,
570 buf: &mut [u8],
571 handle: u16,
572 data: &[u8],
573 ) -> Result<usize, codec::Error> {
574 self.att_table.iterate(|mut it| {
575 while let Some(att) = it.next() {
576 if att.handle == handle {
577 let _ = self.write_attribute_data(connection, 0, att, data);
579 break;
580 }
581 }
582 });
583 Ok(0)
584 }
585
586 fn handle_write_req(
587 &self,
588 connection: &Connection<'_, P>,
589 buf: &mut [u8],
590 handle: u16,
591 data: &[u8],
592 ) -> Result<usize, codec::Error> {
593 let err = self.att_table.iterate(|mut it| {
594 let mut err = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
595 while let Some(att) = it.next() {
596 if att.handle == handle {
597 err = self.write_attribute_data(connection, 0, att, data);
598 break;
599 }
600 }
601 err
602 });
603
604 let mut w = WriteCursor::new(buf);
605 match err {
606 Ok(()) => {
607 w.write(att::ATT_WRITE_RSP)?;
608 Ok(w.len())
609 }
610 Err(e) => Ok(Self::error_response(w, att::ATT_WRITE_REQ, handle, e)?),
611 }
612 }
613
614 fn handle_find_type_value(
615 &self,
616 buf: &mut [u8],
617 start: u16,
618 end: u16,
619 attr_type: u16,
620 attr_value: &[u8],
621 ) -> Result<usize, codec::Error> {
622 let mut w = WriteCursor::new(buf);
623 let attr_type = Uuid::new_short(attr_type);
624
625 w.write(att::ATT_FIND_BY_TYPE_VALUE_RSP)?;
626 self.att_table.iterate(|mut it| {
627 while let Some(att) = it.next() {
628 if att.handle >= start && att.handle <= end && att.uuid == attr_type {
629 if let AttributeData::Service { uuid } = &att.data {
630 if uuid.as_raw() == attr_value {
631 if w.available() < 4 + uuid.as_raw().len() {
632 break;
633 }
634 w.write(att.handle)?;
635 w.write(att.last_handle_in_group)?;
636 }
637 }
638 }
639 }
640 Ok::<(), codec::Error>(())
641 })?;
642 if w.len() > 1 {
643 Ok(w.len())
644 } else {
645 Ok(Self::error_response(
646 w,
647 att::ATT_FIND_BY_TYPE_VALUE_REQ,
648 start,
649 AttErrorCode::ATTRIBUTE_NOT_FOUND,
650 )?)
651 }
652 }
653
654 fn handle_find_information(&self, buf: &mut [u8], start: u16, end: u16) -> Result<usize, codec::Error> {
655 let mut w = WriteCursor::new(buf);
656
657 let (mut header, mut body) = w.split(2)?;
658
659 header.write(att::ATT_FIND_INFORMATION_RSP)?;
660 let mut t = 0;
661
662 self.att_table.iterate(|mut it| {
663 while let Some(att) = it.next() {
664 if att.handle >= start && att.handle <= end {
665 if t == 0 {
666 t = att.uuid.get_type();
667 } else if t != att.uuid.get_type() {
668 break;
669 }
670 body.write(att.handle)?;
671 body.append(att.uuid.as_raw())?;
672 }
673 }
674 Ok::<(), codec::Error>(())
675 })?;
676 header.write(t)?;
677
678 if body.len() > 2 {
679 Ok(header.len() + body.len())
680 } else {
681 Ok(Self::error_response(
682 w,
683 att::ATT_FIND_INFORMATION_REQ,
684 start,
685 AttErrorCode::ATTRIBUTE_NOT_FOUND,
686 )?)
687 }
688 }
689
690 fn error_response(
691 mut w: WriteCursor<'_>,
692 opcode: u8,
693 handle: u16,
694 code: AttErrorCode,
695 ) -> Result<usize, codec::Error> {
696 w.reset();
697 w.write(att::ATT_ERROR_RSP)?;
698 w.write(opcode)?;
699 w.write(handle)?;
700 w.write(code)?;
701 Ok(w.len())
702 }
703
704 fn handle_prepare_write(
705 &self,
706 connection: &Connection<'_, P>,
707 buf: &mut [u8],
708 handle: u16,
709 offset: u16,
710 value: &[u8],
711 ) -> Result<usize, codec::Error> {
712 let mut w = WriteCursor::new(buf);
713 w.write(att::ATT_PREPARE_WRITE_RSP)?;
714 w.write(handle)?;
715 w.write(offset)?;
716
717 let err = self.att_table.iterate(|mut it| {
718 let mut err = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
719 while let Some(att) = it.next() {
720 if att.handle == handle {
721 err = self.write_attribute_data(connection, offset as usize, att, value);
722 w.append(value)?;
723 break;
724 }
725 }
726 err
727 });
728
729 match err {
730 Ok(()) => Ok(w.len()),
731 Err(e) => Ok(Self::error_response(w, att::ATT_PREPARE_WRITE_REQ, handle, e)?),
732 }
733 }
734
735 fn handle_execute_write(&self, buf: &mut [u8], _flags: u8) -> Result<usize, codec::Error> {
736 let mut w = WriteCursor::new(buf);
737 w.write(att::ATT_EXECUTE_WRITE_RSP)?;
738 Ok(w.len())
739 }
740
741 fn handle_read_blob(
742 &self,
743 connection: &Connection<'_, P>,
744 buf: &mut [u8],
745 handle: u16,
746 offset: u16,
747 ) -> Result<usize, codec::Error> {
748 let mut w = WriteCursor::new(buf);
749 w.write(att::ATT_READ_BLOB_RSP)?;
750
751 let err = self.att_table.iterate(|mut it| {
752 let mut err = Err(AttErrorCode::ATTRIBUTE_NOT_FOUND);
753 while let Some(att) = it.next() {
754 if att.handle == handle {
755 err = self.read_attribute_data(connection, offset as usize, att, w.write_buf());
756 if let Ok(n) = err {
757 w.commit(n)?;
758 }
759 break;
760 }
761 }
762 err
763 });
764
765 match err {
766 Ok(_) => Ok(w.len()),
767 Err(e) => Ok(Self::error_response(w, att::ATT_READ_BLOB_REQ, handle, e)?),
768 }
769 }
770
771 fn handle_read_multiple(&self, buf: &mut [u8], handles: &[u8]) -> Result<usize, codec::Error> {
772 let w = WriteCursor::new(buf);
773 Self::error_response(
774 w,
775 att::ATT_READ_MULTIPLE_REQ,
776 u16::from_le_bytes([handles[0], handles[1]]),
777 AttErrorCode::ATTRIBUTE_NOT_FOUND,
778 )
779 }
780
781 pub fn process(
783 &self,
784 connection: &Connection<'_, P>,
785 packet: &AttClient,
786 rx: &mut [u8],
787 ) -> Result<Option<usize>, codec::Error> {
788 let len = match packet {
789 AttClient::Request(AttReq::ReadByType {
790 start,
791 end,
792 attribute_type,
793 }) => self.handle_read_by_type_req(connection, rx, *start, *end, attribute_type)?,
794
795 AttClient::Request(AttReq::ReadByGroupType { start, end, group_type }) => {
796 self.handle_read_by_group_type_req(connection, rx, *start, *end, group_type)?
797 }
798 AttClient::Request(AttReq::FindInformation {
799 start_handle,
800 end_handle,
801 }) => self.handle_find_information(rx, *start_handle, *end_handle)?,
802
803 AttClient::Request(AttReq::Read { handle }) => self.handle_read_req(connection, rx, *handle)?,
804
805 AttClient::Command(AttCmd::Write { handle, data }) => {
806 self.handle_write_cmd(connection, rx, *handle, data)?;
807 0
808 }
809
810 AttClient::Request(AttReq::Write { handle, data }) => {
811 self.handle_write_req(connection, rx, *handle, data)?
812 }
813
814 AttClient::Request(AttReq::ExchangeMtu { mtu }) => 0, AttClient::Request(AttReq::FindByTypeValue {
817 start_handle,
818 end_handle,
819 att_type,
820 att_value,
821 }) => self.handle_find_type_value(rx, *start_handle, *end_handle, *att_type, att_value)?,
822
823 AttClient::Request(AttReq::PrepareWrite { handle, offset, value }) => {
824 self.handle_prepare_write(connection, rx, *handle, *offset, value)?
825 }
826
827 AttClient::Request(AttReq::ExecuteWrite { flags }) => self.handle_execute_write(rx, *flags)?,
828
829 AttClient::Request(AttReq::ReadBlob { handle, offset }) => {
830 self.handle_read_blob(connection, rx, *handle, *offset)?
831 }
832
833 AttClient::Request(AttReq::ReadMultiple { handles }) => self.handle_read_multiple(rx, handles)?,
834
835 AttClient::Confirmation(_) => 0,
836 };
837 if len > 0 {
838 Ok(Some(len))
839 } else {
840 Ok(None)
841 }
842 }
843
844 pub fn table(&self) -> &AttributeTable<'values, M, ATT_MAX> {
846 &self.att_table
847 }
848
849 pub fn get_cccd_table(&self, connection: &Connection<'_, P>) -> Option<CccdTable<CCCD_MAX>> {
851 self.cccd_tables.get_cccd_table(&connection.peer_identity())
852 }
853
854 pub fn set_cccd_table(&self, connection: &Connection<'_, P>, table: CccdTable<CCCD_MAX>) {
856 self.cccd_tables.set_cccd_table(&connection.peer_identity(), table);
857 }
858}
859
860#[cfg(test)]
861mod tests {
862 use core::task::Poll;
863
864 use bt_hci::param::{AddrKind, BdAddr, ConnHandle, LeConnRole};
865 use embassy_sync::blocking_mutex::raw::NoopRawMutex;
866
867 use super::*;
868 use crate::connection_manager::tests::{setup, ADDR_1};
869 use crate::prelude::*;
870
871 #[test]
872 fn test_attribute_server_last_handle_of_group() {
873 let primary_service_group_type = Uuid::new_short(0x2800);
895
896 let _ = env_logger::try_init();
897 const MAX_ATTRIBUTES: usize = 1024;
898 const CONNECTIONS_MAX: usize = 3;
899 const CCCD_MAX: usize = 1024;
900 const L2CAP_CHANNELS_MAX: usize = 5;
901 type FacadeDummyType = [u8; 0];
902
903 for interior_handle_count in 0..=64u8 {
906 debug!("Testing with interior handle count of {}", interior_handle_count);
907
908 let mut table: AttributeTable<'_, NoopRawMutex, { MAX_ATTRIBUTES }> = AttributeTable::new();
910
911 {
913 let svc = table.add_service(Service {
914 uuid: Uuid::new_long([10; 16]).into(),
915 });
916 }
917
918 {
920 let mut svc = table.add_service(Service {
921 uuid: Uuid::new_long([0; 16]).into(),
922 });
923
924 for c in 0..interior_handle_count {
925 let _service_instance = svc
926 .add_characteristic_ro::<[u8; 2], _>(Uuid::new_long([c; 16]), &[0, 0])
927 .build();
928 }
929 }
930 {
932 table.add_service(Service {
933 uuid: Uuid::new_long([8; 16]).into(),
934 });
935 }
936
937 table.iterate(|mut it| {
939 while let Some(att) = it.next() {
940 let handle = att.handle;
941 let uuid = &att.uuid;
942 trace!(
943 "last_handle_in_group for 0x{:0>4x?}, 0x{:0>2x?} 0x{:0>2x?}",
944 handle,
945 uuid,
946 att.last_handle_in_group
947 );
948 }
949 });
950
951 let server = AttributeServer::<_, DefaultPacketPool, MAX_ATTRIBUTES, CCCD_MAX, CONNECTIONS_MAX>::new(table);
953
954 let mgr = setup();
956
957 assert!(mgr.poll_accept(LeConnRole::Peripheral, &[], None).is_pending());
959 unwrap!(mgr.connect(
960 ConnHandle::new(0),
961 AddrKind::RANDOM,
962 BdAddr::new(ADDR_1),
963 LeConnRole::Peripheral
964 ));
965
966 if let Poll::Ready(conn_handle) = mgr.poll_accept(LeConnRole::Peripheral, &[], None) {
967 let mut buffer = [0u8; 64];
969
970 let mut start = 0;
971 let end = u16::MAX;
972 for _ in 0..3 {
974 let length = server
975 .handle_read_by_group_type_req(
976 &conn_handle,
977 &mut buffer,
978 start,
979 end,
980 &primary_service_group_type,
981 )
982 .unwrap();
983 let response = &buffer[0..length];
984 trace!(" 0x{:0>2x?}", response);
985 assert_eq!(response[0], att::ATT_READ_BY_GROUP_TYPE_RSP);
988 let last_handle = u16::from_le_bytes([response[4], response[5]]);
992 start = last_handle + 1;
993 }
994 } else {
995 panic!("expected connection to be accepted");
996 };
997 }
998 }
999}