1pub use bt_hci;
4use bt_hci::{
5 ControllerToHostPacket, FromHciBytes, WriteHci,
6 cmd::{self, AsyncCmd, Cmd, SyncCmd, le::*},
7 controller::{Controller, ControllerCmdAsync, ControllerCmdSync},
8 data::{AclPacket, AclPacketBoundary, IsoPacket, SyncPacket},
9 event::{EventKind, NumberOfCompletedPackets},
10};
11use core::cell::RefCell;
12use embassy_sync::blocking_mutex::{Mutex, raw::CriticalSectionRawMutex};
13use trouble_host::{
14 att::{AttClient, AttReq},
15 prelude::Uuid,
16};
17
18pub fn accesses_device_name(request: AttClient<'_>, name_handle: u16) -> bool {
20 let AttClient::Request(request) = request else {
21 return false;
22 };
23 let in_range = |start, end| start <= name_handle && name_handle <= end;
24 match request {
25 AttReq::Read { handle } | AttReq::ReadBlob { handle, .. } => handle == name_handle,
26 AttReq::ReadMultiple { handles } => handles
27 .chunks_exact(2)
28 .any(|h| u16::from_le_bytes([h[0], h[1]]) == name_handle),
29 AttReq::ReadByType {
30 start,
31 end,
32 attribute_type,
33 } => in_range(start, end) && attribute_type == Uuid::Uuid16([0x00, 0x2a]),
34 AttReq::ReadByGroupType {
35 start,
36 end,
37 group_type,
38 } => in_range(start, end) && group_type == Uuid::Uuid16([0x00, 0x2a]),
39 AttReq::FindByTypeValue {
40 start_handle,
41 end_handle,
42 att_type,
43 ..
44 } => in_range(start_handle, end_handle) && att_type == 0x2a00,
45 _ => false,
46 }
47}
48
49#[derive(Default)]
50struct State {
51 failed: bool,
52 runner_exited: bool,
53 local_irk: bool,
54 resolution: bool,
55 timeout: bool,
56 private_advertiser: bool,
57 advertising_invalidated: bool,
58 sent: u64,
59 completed: u64,
60 disconnected: bool,
61 tx_failed: bool,
62 notify_handle: u16,
63 queued_notifications: u64,
64 written_notifications: u64,
65 notification_remaining: usize,
66 notification_active: bool,
67 fence_notification: u64,
68 fence_packet: Option<u64>,
69 fence_transmitted: bool,
70}
71
72impl State {
73 fn ready(&self) -> bool {
74 !self.failed
75 && !self.runner_exited
76 && !self.advertising_invalidated
77 && self.local_irk
78 && self.resolution
79 && self.timeout
80 && self.private_advertiser
81 }
82 fn acl(&mut self, data: &[u8], continuing: bool) {
83 self.sent += 1;
84 if !continuing {
85 self.notification_active = data.len() >= 7
87 && data[2..4] == [4, 0]
88 && data[4] == 0x1b
89 && u16::from_le_bytes([data[5], data[6]]) == self.notify_handle;
90 self.notification_remaining = if self.notification_active {
91 usize::from(u16::from_le_bytes([data[0], data[1]])) + 4
92 } else {
93 0
94 };
95 }
96 if self.notification_active {
97 self.notification_remaining = self.notification_remaining.saturating_sub(data.len());
98 if self.notification_remaining == 0 {
99 self.notification_active = false;
100 self.written_notifications += 1;
101 if self.fence_notification == self.written_notifications {
102 self.fence_packet = Some(self.sent);
103 self.latch_fence();
104 }
105 }
106 }
107 }
108 fn latch_fence(&mut self) {
109 self.fence_transmitted |= !self.tx_failed
110 && !self.disconnected
111 && self
112 .fence_packet
113 .is_some_and(|packet| self.completed >= packet);
114 }
115 fn completed_packets(&mut self, count: u16) {
116 self.completed += u64::from(count);
117 self.latch_fence();
118 }
119 fn fence_done(&self) -> bool {
120 self.fence_transmitted
124 }
125}
126
127pub struct ControllerState(
128 Mutex<CriticalSectionRawMutex, RefCell<State>>,
129 embassy_sync::signal::Signal<CriticalSectionRawMutex, ()>,
130);
131impl Default for ControllerState {
132 fn default() -> Self {
133 Self::new()
134 }
135}
136impl ControllerState {
137 pub const fn new() -> Self {
138 Self(
139 Mutex::new(RefCell::new(State {
140 failed: false,
141 runner_exited: false,
142 local_irk: false,
143 resolution: false,
144 timeout: false,
145 private_advertiser: false,
146 advertising_invalidated: false,
147 sent: 0,
148 completed: 0,
149 disconnected: false,
150 tx_failed: false,
151 notify_handle: 0,
152 queued_notifications: 0,
153 written_notifications: 0,
154 notification_remaining: 0,
155 notification_active: false,
156 fence_notification: 0,
157 fence_packet: None,
158 fence_transmitted: false,
159 })),
160 embassy_sync::signal::Signal::new(),
161 )
162 }
163 fn with<R>(&self, f: impl FnOnce(&mut State) -> R) -> R {
164 self.0.lock(|s| f(&mut s.borrow_mut()))
165 }
166 pub fn reset(&self) {
168 self.with(|s| *s = State::default());
169 self.1.reset();
170 }
171 pub fn invalidate_advertising(&self) {
174 self.with(|s| s.advertising_invalidated = true);
175 self.1.signal(());
176 }
177 pub fn failed(&self) -> bool {
178 self.with(|s| s.failed)
179 }
180 pub fn fail(&self) {
181 self.with(|s| s.failed = true);
182 self.1.signal(());
183 }
184 pub fn runner_exited(&self) {
187 self.with(|s| s.runner_exited = true);
188 self.1.signal(());
189 }
190 pub fn needs_recovery(&self) -> bool {
191 self.with(|s| s.runner_exited && !s.failed)
192 }
193 pub async fn wait_for_privacy(&self) -> bool {
197 loop {
198 let status = self.with(|s| {
199 if s.failed || s.runner_exited || s.advertising_invalidated {
200 Some(false)
201 } else if s.local_irk && s.resolution && s.timeout {
202 Some(true)
203 } else {
204 None
205 }
206 });
207 if let Some(ready) = status {
208 return ready;
209 }
210 self.1.wait().await;
211 }
212 }
213 pub fn notification(&self, handle: u16, final_reply: bool) {
216 self.with(|s| {
217 s.notify_handle = handle;
218 s.queued_notifications += 1;
219 if final_reply {
220 s.fence_notification = s.queued_notifications;
221 s.fence_packet = None;
222 s.fence_transmitted = false;
223 }
224 });
225 }
226 pub fn fence_done(&self) -> bool {
227 self.with(|s| s.fence_done())
228 }
229 pub fn link_failed(&self) -> bool {
230 self.with(|s| s.tx_failed || s.disconnected)
231 }
232}
233
234pub struct Guarded<C> {
237 inner: C,
238 state: &'static ControllerState,
239}
240impl<C> Guarded<C> {
241 pub fn new(inner: C, state: &'static ControllerState) -> Self {
242 Self { inner, state }
243 }
244}
245impl<C: embedded_io::ErrorType> embedded_io::ErrorType for Guarded<C> {
246 type Error = C::Error;
247}
248impl<C: Controller> Controller for Guarded<C> {
249 async fn write_acl_data(&self, packet: &AclPacket<'_>) -> Result<(), Self::Error> {
250 self.state.with(|s| {
253 s.acl(
254 packet.data(),
255 packet.boundary_flag() == AclPacketBoundary::Continuing,
256 )
257 });
258 let result = self.inner.write_acl_data(packet).await;
259 if result.is_err() {
260 self.state.with(|s| s.tx_failed = true);
261 }
262 result
263 }
264 async fn write_sync_data(&self, packet: &SyncPacket<'_>) -> Result<(), Self::Error> {
265 self.inner.write_sync_data(packet).await
266 }
267 async fn write_iso_data(&self, packet: &IsoPacket<'_>) -> Result<(), Self::Error> {
268 self.inner.write_iso_data(packet).await
269 }
270 async fn read<'a>(&self, buf: &'a mut [u8]) -> Result<ControllerToHostPacket<'a>, Self::Error> {
271 let packet = self.inner.read(buf).await?;
272 if let ControllerToHostPacket::Event(event) = &packet {
273 match event.kind {
274 EventKind::NumberOfCompletedPackets => {
275 if let Ok(completed) =
276 NumberOfCompletedPackets::from_hci_bytes_complete(event.data)
277 {
278 for entry in completed.completed_packets {
279 if let Ok(count) = entry.num_completed_packets() {
280 self.state.with(|s| s.completed_packets(count));
281 }
282 }
283 }
284 }
285 EventKind::DisconnectionComplete => self.state.with(|s| s.disconnected = true),
286 _ => {}
287 }
288 }
289 Ok(packet)
290 }
291}
292
293impl<C, T> ControllerCmdSync<T> for Guarded<C>
294where
295 C: ControllerCmdSync<T> + ControllerCmdSync<LeReadResolvingListSize>,
296 T: SyncCmd,
297{
298 async fn exec(&self, command: &T) -> Result<T::Return, cmd::Error<Self::Error>> {
299 let opcode = T::OPCODE;
300 let tracked = opcode == LeSetAdvParams::OPCODE
301 || opcode == LeSetAdvEnable::OPCODE
302 || opcode == LeAddDeviceToResolvingList::OPCODE
303 || opcode == LeSetPrivacyMode::OPCODE
304 || opcode == LeClearResolvingList::OPCODE
305 || opcode == LeSetAddrResolutionEnable::OPCODE
306 || opcode == LeSetResolvablePrivateAddrTimeout::OPCODE;
307 if !tracked {
308 return self.inner.exec(command).await;
309 }
310 let mut bytes = [0; 40];
311 command
312 .params()
313 .write_hci(&mut bytes[..])
314 .map_err(|_| cmd::Error::Hci(bt_hci::param::Error::INVALID_HCI_PARAMETERS))?;
315 if opcode == LeSetAdvEnable::OPCODE && bytes[0] != 0 && !self.state.with(|s| s.ready()) {
316 return Err(cmd::Error::Hci(bt_hci::param::Error::CMD_DISALLOWED));
317 }
318 if opcode == LeClearResolvingList::OPCODE {
319 let capacity = self.inner.exec(&LeReadResolvingListSize::new()).await;
320 match capacity {
322 Ok(size) if size >= 5 => {}
323 result => {
324 crate::log::debug_log(format_args!(
325 "BLE privacy resolving-list capacity {result:?}; require at least 5"
326 ));
327 self.state.fail();
328 return Err(cmd::Error::Hci(bt_hci::param::Error::CMD_DISALLOWED));
329 }
330 }
331 self.state.with(|s| {
332 s.local_irk = false;
333 s.resolution = false;
334 });
335 }
336 let result = self.inner.exec(command).await;
337 if let Err(error) = &result {
338 crate::log::debug_log(format_args!(
339 "BLE privacy HCI command 0x{:04x} failed: {error:?}",
340 opcode.to_raw()
341 ));
342 }
343 self.state.with(|s| {
344 if result.is_err() {
345 s.failed = true;
346 return;
347 }
348 if opcode == LeAddDeviceToResolvingList::OPCODE && bytes[..7] == [0; 7] {
349 s.local_irk = bytes[23..39] != [0; 16];
350 if !s.local_irk {
351 s.failed = true;
352 }
353 }
354 if opcode == LeSetAddrResolutionEnable::OPCODE {
355 s.resolution = bytes[0] != 0;
356 }
357 if opcode == LeSetResolvablePrivateAddrTimeout::OPCODE {
358 s.timeout = u64::from(u16::from_le_bytes([bytes[0], bytes[1]]))
359 == crate::ble_privacy::RPA_TIMEOUT_SECS;
360 }
361 if opcode == LeSetAdvParams::OPCODE {
362 s.private_advertiser = matches!(bytes[5], 2 | 3);
363 }
364 });
365 self.state.1.signal(());
366 result
367 }
368}
369impl<C: ControllerCmdAsync<T>, T: AsyncCmd> ControllerCmdAsync<T> for Guarded<C> {
370 async fn exec(&self, command: &T) -> Result<(), cmd::Error<Self::Error>> {
371 self.inner.exec(command).await
372 }
373}
374
375pub struct Borrowed<'a, C>(pub &'a C);
376impl<C: embedded_io::ErrorType> embedded_io::ErrorType for Borrowed<'_, C> {
377 type Error = C::Error;
378}
379impl<C: Controller> Controller for Borrowed<'_, C> {
380 async fn write_acl_data(&self, p: &AclPacket<'_>) -> Result<(), Self::Error> {
381 self.0.write_acl_data(p).await
382 }
383 async fn write_sync_data(&self, p: &SyncPacket<'_>) -> Result<(), Self::Error> {
384 self.0.write_sync_data(p).await
385 }
386 async fn write_iso_data(&self, p: &IsoPacket<'_>) -> Result<(), Self::Error> {
387 self.0.write_iso_data(p).await
388 }
389 async fn read<'a>(&self, buf: &'a mut [u8]) -> Result<ControllerToHostPacket<'a>, Self::Error> {
390 self.0.read(buf).await
391 }
392}
393impl<C: ControllerCmdSync<T>, T: SyncCmd> ControllerCmdSync<T> for Borrowed<'_, C> {
394 async fn exec(&self, command: &T) -> Result<T::Return, cmd::Error<Self::Error>> {
395 self.0.exec(command).await
396 }
397}
398impl<C: ControllerCmdAsync<T>, T: AsyncCmd> ControllerCmdAsync<T> for Borrowed<'_, C> {
399 async fn exec(&self, command: &T) -> Result<(), cmd::Error<Self::Error>> {
400 self.0.exec(command).await
401 }
402}
403
404#[cfg(test)]
405mod tests {
406 use super::*;
407 #[test]
408 fn every_supported_name_read_and_equality_probe_is_guarded() {
409 let reads = [
410 AttReq::Read { handle: 3 },
411 AttReq::ReadBlob {
412 handle: 3,
413 offset: 12,
414 },
415 AttReq::ReadMultiple {
416 handles: &[2, 0, 3, 0],
417 },
418 AttReq::ReadByType {
419 start: 1,
420 end: 9,
421 attribute_type: Uuid::Uuid16([0x00, 0x2a]),
422 },
423 AttReq::ReadByGroupType {
424 start: 1,
425 end: 9,
426 group_type: Uuid::Uuid16([0x00, 0x2a]),
427 },
428 AttReq::FindByTypeValue {
429 start_handle: 1,
430 end_handle: 9,
431 att_type: 0x2a00,
432 att_value: b"Alice",
433 },
434 ];
435 for read in reads {
436 assert!(accesses_device_name(AttClient::Request(read), 3));
437 }
438 assert!(!accesses_device_name(
439 AttClient::Request(AttReq::Read { handle: 7 }),
440 3
441 ));
442 assert!(!accesses_device_name(
443 AttClient::Request(AttReq::ReadByType {
444 start: 4,
445 end: 9,
446 attribute_type: Uuid::Uuid16([0x00, 0x2a])
447 }),
448 3
449 ));
450 }
451 #[test]
452 fn fence_waits_for_the_right_notification_and_last_acl_fragment() {
453 let mut s = State {
454 notify_handle: 3,
455 fence_notification: 2,
456 ..State::default()
457 };
458 let earlier = [4, 0, 4, 0, 0x1b, 3, 0, 42];
459 s.acl(&earlier, false);
460 s.completed_packets(1);
461 assert!(!s.fence_done());
462 s.acl(&[6, 0, 4, 0, 0x1b, 3, 0, 42], false);
463 s.completed_packets(1);
464 assert!(!s.fence_done());
465 s.acl(&[43, 44], true);
466 assert!(!s.fence_done());
467 s.completed_packets(1);
468 assert!(s.fence_done());
469 s.disconnected = true;
470 assert!(
471 s.fence_done(),
472 "a subsequent disconnect cannot undo a transmitted response"
473 );
474 let mut canceled = State {
475 notify_handle: 3,
476 fence_notification: 1,
477 ..State::default()
478 };
479 canceled.acl(&earlier, false);
480 canceled.disconnected = true;
481 canceled.completed_packets(1);
482 assert!(
483 !canceled.fence_done(),
484 "disconnect credits cannot confirm transmission"
485 );
486 }
487 #[test]
488 fn privacy_is_fail_closed() {
489 let mut s = State::default();
490 assert!(!s.ready());
491 s.local_irk = true;
492 s.resolution = true;
493 s.timeout = true;
494 s.private_advertiser = true;
495 assert!(s.ready());
496 s.failed = true;
497 assert!(!s.ready());
498 }
499 struct Fake {
500 capacity: u8,
501 fail: Option<cmd::Opcode>,
502 calls: RefCell<std::vec::Vec<(cmd::Opcode, std::vec::Vec<u8>)>>,
503 }
504 impl embedded_io::ErrorType for Fake {
505 type Error = core::convert::Infallible;
506 }
507 impl Controller for Fake {
508 async fn write_acl_data(&self, _: &AclPacket<'_>) -> Result<(), Self::Error> {
509 Ok(())
510 }
511 async fn write_sync_data(&self, _: &SyncPacket<'_>) -> Result<(), Self::Error> {
512 Ok(())
513 }
514 async fn write_iso_data(&self, _: &IsoPacket<'_>) -> Result<(), Self::Error> {
515 Ok(())
516 }
517 async fn read<'a>(
518 &self,
519 _: &'a mut [u8],
520 ) -> Result<ControllerToHostPacket<'a>, Self::Error> {
521 core::future::pending().await
522 }
523 }
524 impl<T: SyncCmd> ControllerCmdSync<T> for Fake {
525 async fn exec(&self, command: &T) -> Result<T::Return, cmd::Error<Self::Error>> {
526 let mut data = [0; 40];
527 command.params().write_hci(&mut data[..]).unwrap();
528 self.calls
529 .borrow_mut()
530 .push((T::OPCODE, data[..command.params().size()].to_vec()));
531 if self.fail == Some(T::OPCODE) {
532 return Err(cmd::Error::Hci(bt_hci::param::Error::CMD_DISALLOWED));
533 }
534 let capacity = [self.capacity];
535 let result: &[u8] = if T::OPCODE == LeReadResolvingListSize::OPCODE {
536 &capacity
537 } else {
538 &[]
539 };
540 Ok(T::Return::from_hci_bytes_complete(result).unwrap())
541 }
542 }
543 fn controller(capacity: u8, fail: Option<cmd::Opcode>) -> Guarded<Fake> {
544 Guarded::new(
545 Fake {
546 capacity,
547 fail,
548 calls: RefCell::new(std::vec::Vec::new()),
549 },
550 Box::leak(Box::new(ControllerState::new())),
551 )
552 }
553 fn advertising_params(private: bool) -> LeSetAdvParams {
554 use bt_hci::param::*;
555 LeSetAdvParams::new(
556 Duration::from_micros(crate::ble_privacy::ADVERTISING_INTERVAL_US),
557 Duration::from_micros(crate::ble_privacy::ADVERTISING_INTERVAL_US),
558 AdvKind::AdvInd,
559 if private {
560 AddrKind::RESOLVABLE_PRIVATE_OR_RANDOM
561 } else {
562 AddrKind::RANDOM
563 },
564 AddrKind::PUBLIC,
565 BdAddr::default(),
566 AdvChannelMap::ALL,
567 AdvFilterPolicy::default(),
568 )
569 }
570 async fn setup(c: &Guarded<Fake>) {
571 use bt_hci::param::*;
572 c.exec(&LeSetResolvablePrivateAddrTimeout::new(
573 Duration::from_secs(crate::ble_privacy::RPA_TIMEOUT_SECS.try_into().unwrap()),
574 ))
575 .await
576 .unwrap();
577 c.exec(&LeClearResolvingList::new()).await.unwrap();
578 c.exec(&LeAddDeviceToResolvingList::new(
579 AddrKind::PUBLIC,
580 BdAddr::default(),
581 [0; 16],
582 [1; 16],
583 ))
584 .await
585 .unwrap();
586 c.exec(&LeSetAddrResolutionEnable::new(true)).await.unwrap();
587 c.exec(&advertising_params(true)).await.unwrap();
588 }
589 #[test]
590 fn hci_enable_requires_capacity_local_irk_timeout_and_private_address() {
591 embassy_futures::block_on(async {
592 let c = controller(5, None);
593 assert!(c.exec(&LeSetAdvEnable::new(true)).await.is_err());
594 setup(&c).await;
595 c.exec(&LeSetAdvEnable::new(true)).await.unwrap();
596 let calls = c.inner.calls.borrow();
597 let (_, bytes) = calls
598 .iter()
599 .find(|(op, _)| *op == LeSetAdvParams::OPCODE)
600 .unwrap();
601 assert_eq!(&bytes[..4], &[0x6a, 0x03, 0x6a, 0x03]); drop(calls);
603 c.exec(&advertising_params(false)).await.unwrap();
604 assert!(c.exec(&LeSetAdvEnable::new(true)).await.is_err());
605 c.exec(&advertising_params(true)).await.unwrap();
606 c.state.invalidate_advertising();
607 assert!(c.exec(&LeSetAdvEnable::new(true)).await.is_err());
608 assert!(!c.state.failed()); });
610 }
611 #[test]
612 fn controller_failures_cannot_fall_back_to_static_advertising() {
613 embassy_futures::block_on(async {
614 for capacity in [0, 4] {
615 let c = controller(capacity, None);
616 assert!(c.exec(&LeClearResolvingList::new()).await.is_err());
617 assert!(c.state.failed());
618 assert!(c.exec(&LeSetAdvEnable::new(true)).await.is_err());
619 }
620 let c = controller(5, Some(LeAddDeviceToResolvingList::OPCODE));
621 c.exec(&LeClearResolvingList::new()).await.unwrap();
622 use bt_hci::param::*;
623 assert!(
624 c.exec(&LeAddDeviceToResolvingList::new(
625 AddrKind::PUBLIC,
626 BdAddr::default(),
627 [0; 16],
628 [1; 16]
629 ))
630 .await
631 .is_err()
632 );
633 assert!(c.state.failed());
635 assert!(c.exec(&LeSetAdvEnable::new(true)).await.is_err());
636 });
637 }
638 #[test]
639 fn advertising_waits_for_privacy_initialization_or_explicit_failure() {
640 use core::{
641 future::Future,
642 task::{Context, Poll, Waker},
643 };
644 let c = controller(5, None);
645 let mut wait = core::pin::pin!(c.state.wait_for_privacy());
646 let mut cx = Context::from_waker(Waker::noop());
647 assert_eq!(wait.as_mut().poll(&mut cx), Poll::Pending);
648 embassy_futures::block_on(setup(&c));
649 assert_eq!(wait.as_mut().poll(&mut cx), Poll::Ready(true));
650 c.state.reset();
651 let mut wait = core::pin::pin!(c.state.wait_for_privacy());
652 assert_eq!(wait.as_mut().poll(&mut cx), Poll::Pending);
653 c.state.fail();
654 assert_eq!(wait.as_mut().poll(&mut cx), Poll::Ready(false));
655 c.state.reset();
656 c.state.invalidate_advertising();
657 assert!(!embassy_futures::block_on(c.state.wait_for_privacy()));
658 }
659
660 #[test]
661 fn runner_recovery_never_clears_a_privacy_failure() {
662 let c = controller(5, None);
663 embassy_futures::block_on(setup(&c));
664 c.state.runner_exited();
665 assert!(c.state.needs_recovery());
666 assert!(!c.state.failed());
667 assert!(!embassy_futures::block_on(c.state.wait_for_privacy()));
668 assert!(embassy_futures::block_on(c.exec(&LeSetAdvEnable::new(true))).is_err());
669 c.state.fail();
670 c.state.runner_exited();
671 assert!(c.state.failed());
672 assert!(!c.state.needs_recovery());
673 c.state.reset();
674 assert!(!c.state.needs_recovery());
675 embassy_futures::block_on(setup(&c));
676 assert!(embassy_futures::block_on(c.exec(&LeSetAdvEnable::new(true))).is_ok());
677 }
678}