Skip to main content

bt_hci/
controller.rs

1//! HCI controller
2
3use core::cell::RefCell;
4use core::convert::Infallible;
5use core::future::{poll_fn, Future};
6use core::mem::MaybeUninit;
7use core::slice;
8use core::task::Poll;
9
10use bt_hci_transport::ReadHciError;
11use cmd::controller_baseband::Reset;
12use embassy_sync::blocking_mutex::raw::NoopRawMutex;
13use embassy_sync::signal::Signal;
14use embassy_sync::waitqueue::AtomicWaker;
15use embedded_io::ErrorType;
16use futures_intrusive::sync::LocalSemaphore;
17
18use crate::cmd::{Cmd, CmdReturnBuf};
19use crate::event::{CommandComplete, CommandCompleteWithStatus, CommandStatus, EventKind};
20use crate::param::{RemainingBytes, Status};
21use crate::transport::Transport;
22use crate::{cmd, data, ControllerToHostPacket, FixedSizeValue, FromHciBytes};
23
24pub mod blocking;
25
26/// Trait representing a HCI controller which supports async operations.
27pub trait Controller: ErrorType {
28    /// Type of the buffer
29    type Buffer<'a>;
30
31    /// Allocate a buffer
32    fn alloc_buf(&self) -> Result<Self::Buffer<'_>, Self::Error>;
33
34    /// Write ACL data to the controller.
35    fn write_acl_data(&self, packet: &data::AclPacket) -> impl Future<Output = Result<(), Self::Error>>;
36    /// Write Sync data to the controller.
37    fn write_sync_data(&self, packet: &data::SyncPacket) -> impl Future<Output = Result<(), Self::Error>>;
38    /// Write Iso data to the controller.
39    fn write_iso_data(&self, packet: &data::IsoPacket) -> impl Future<Output = Result<(), Self::Error>>;
40
41    /// Read a valid HCI packet from the controller.
42    fn read<'a>(
43        &self,
44        buf: &'a mut Self::Buffer<'_>,
45    ) -> impl Future<Output = Result<ControllerToHostPacket<'a>, Self::Error>>;
46}
47
48/// Marker trait for declaring that a controller supports a given HCI command.
49pub trait ControllerCmdSync<C: cmd::SyncCmd + ?Sized>: Controller {
50    /// Note: Some implementations may require [`Controller::read()`] to be polled for this to return.
51    fn exec(&self, cmd: &C) -> impl Future<Output = Result<C::Return, cmd::Error<Self::Error>>>;
52}
53
54/// Marker trait for declaring that a controller supports a given async HCI command.
55pub trait ControllerCmdAsync<C: cmd::AsyncCmd + ?Sized>: Controller {
56    /// Note: Some implementations may require [`Controller::read()`] to be polled for this to return.
57    fn exec(&self, cmd: &C) -> impl Future<Output = Result<(), cmd::Error<Self::Error>>>;
58}
59
60/// An external Bluetooth controller with communication via [`Transport`] type `T`.
61///
62/// The controller state holds a number of command slots that can be used
63/// to issue commands and await responses from an underlying controller.
64///
65/// The contract is that before sending a command, a slot is reserved, which
66/// returns a signal handle that can be used to await a response.
67pub struct ExternalController<T, const SLOTS: usize> {
68    transport: T,
69    slots: ControllerState<SLOTS>,
70}
71
72impl<T, const SLOTS: usize> ExternalController<T, SLOTS> {
73    /// Create a new instance.
74    pub fn new(transport: T) -> Self {
75        Self {
76            slots: ControllerState::new(),
77            transport,
78        }
79    }
80}
81
82impl<T, const SLOTS: usize> ErrorType for ExternalController<T, SLOTS>
83where
84    T: ErrorType,
85{
86    type Error = T::Error;
87}
88
89impl<T, const SLOTS: usize> Controller for ExternalController<T, SLOTS>
90where
91    T: Transport,
92    T::Error: From<ReadHciError<Infallible>>,
93{
94    type Buffer<'a> = [u8; 259];
95
96    #[inline]
97    fn alloc_buf(&self) -> Result<Self::Buffer<'_>, Self::Error> {
98        Ok([0u8; 259])
99    }
100
101    async fn write_acl_data(&self, packet: &data::AclPacket<'_>) -> Result<(), Self::Error> {
102        self.transport.write(packet).await?;
103        Ok(())
104    }
105
106    async fn write_sync_data(&self, packet: &data::SyncPacket<'_>) -> Result<(), Self::Error> {
107        self.transport.write(packet).await?;
108        Ok(())
109    }
110
111    async fn write_iso_data(&self, packet: &data::IsoPacket<'_>) -> Result<(), Self::Error> {
112        self.transport.write(packet).await?;
113        Ok(())
114    }
115
116    async fn read<'a>(&self, buf: &'a mut Self::Buffer<'_>) -> Result<ControllerToHostPacket<'a>, Self::Error> {
117        loop {
118            {
119                // Safety: we will not hold references across loop iterations.
120                let buf = unsafe { slice::from_raw_parts_mut(buf as *mut u8, buf.len()) };
121                let value = self.transport.read(&mut buf[..]).await?;
122                match value {
123                    ControllerToHostPacket::Event(ref event) => match event.kind {
124                        EventKind::CommandComplete => {
125                            let e = CommandComplete::from_hci_bytes_complete(event.data).map_err(ReadHciError::from)?;
126                            if !e.has_status() {
127                                return Ok(value);
128                            }
129                            let e: CommandCompleteWithStatus = e.try_into().map_err(ReadHciError::from)?;
130                            self.slots.complete(
131                                e.cmd_opcode,
132                                e.status,
133                                e.num_hci_cmd_pkts as usize,
134                                e.return_param_bytes.as_ref(),
135                            );
136                            continue;
137                        }
138                        EventKind::CommandStatus => {
139                            let e = CommandStatus::from_hci_bytes_complete(event.data).map_err(ReadHciError::from)?;
140                            self.slots
141                                .complete(e.cmd_opcode, e.status, e.num_hci_cmd_pkts as usize, &[]);
142                            continue;
143                        }
144                        _ => return Ok(value),
145                    },
146                    _ => return Ok(value),
147                }
148            }
149        }
150    }
151}
152
153impl<T, const SLOTS: usize> blocking::Controller for ExternalController<T, SLOTS>
154where
155    T: crate::transport::blocking::Transport,
156    T::Error: From<ReadHciError<Infallible>>,
157{
158    type Buffer<'a> = [u8; 259];
159
160    #[inline]
161    fn alloc_buf(&self) -> Result<Self::Buffer<'_>, Self::Error> {
162        Ok([0u8; 259])
163    }
164
165    fn write_acl_data(&self, packet: &data::AclPacket<'_>) -> Result<(), Self::Error> {
166        loop {
167            match self.try_write_acl_data(packet) {
168                Err(blocking::TryError::Busy) => {}
169                Err(blocking::TryError::Error(e)) => return Err(e),
170                Ok(r) => return Ok(r),
171            }
172        }
173    }
174
175    fn write_sync_data(&self, packet: &data::SyncPacket<'_>) -> Result<(), Self::Error> {
176        loop {
177            match self.try_write_sync_data(packet) {
178                Err(blocking::TryError::Busy) => {}
179                Err(blocking::TryError::Error(e)) => return Err(e),
180                Ok(r) => return Ok(r),
181            }
182        }
183    }
184
185    fn write_iso_data(&self, packet: &data::IsoPacket<'_>) -> Result<(), Self::Error> {
186        loop {
187            match self.try_write_iso_data(packet) {
188                Err(blocking::TryError::Busy) => {}
189                Err(blocking::TryError::Error(e)) => return Err(e),
190                Ok(r) => return Ok(r),
191            }
192        }
193    }
194
195    fn read<'a>(&self, buf: &'a mut Self::Buffer<'_>) -> Result<ControllerToHostPacket<'a>, Self::Error> {
196        loop {
197            // Safety: we will not hold references across loop iterations.
198            let buf = unsafe { &mut *(buf as *mut _) };
199            match self.try_read(buf) {
200                Err(blocking::TryError::Busy) => {}
201                Err(blocking::TryError::Error(e)) => return Err(e),
202                Ok(r) => return Ok(r),
203            }
204        }
205    }
206
207    fn try_write_acl_data(&self, packet: &data::AclPacket<'_>) -> Result<(), blocking::TryError<Self::Error>> {
208        self.transport.write(packet)?;
209        Ok(())
210    }
211
212    fn try_write_sync_data(&self, packet: &data::SyncPacket<'_>) -> Result<(), blocking::TryError<Self::Error>> {
213        self.transport.write(packet)?;
214        Ok(())
215    }
216
217    fn try_write_iso_data(&self, packet: &data::IsoPacket<'_>) -> Result<(), blocking::TryError<Self::Error>> {
218        self.transport.write(packet)?;
219        Ok(())
220    }
221
222    fn try_read<'a>(
223        &self,
224        buf: &'a mut Self::Buffer<'_>,
225    ) -> Result<ControllerToHostPacket<'a>, blocking::TryError<Self::Error>> {
226        loop {
227            {
228                // Safety: we will not hold references across loop iterations.
229                let buf = unsafe { slice::from_raw_parts_mut(buf as *mut u8, buf.len()) };
230                let value = self.transport.read(&mut buf[..])?;
231                match value {
232                    ControllerToHostPacket::Event(ref event) => match event.kind {
233                        EventKind::CommandComplete => {
234                            let e = CommandComplete::from_hci_bytes_complete(event.data).map_err(ReadHciError::from)?;
235                            if !e.has_status() {
236                                return Ok(value);
237                            }
238                            let e: CommandCompleteWithStatus = e.try_into().map_err(ReadHciError::from)?;
239                            self.slots.complete(
240                                e.cmd_opcode,
241                                e.status,
242                                e.num_hci_cmd_pkts as usize,
243                                e.return_param_bytes.as_ref(),
244                            );
245                            continue;
246                        }
247                        EventKind::CommandStatus => {
248                            let e = CommandStatus::from_hci_bytes_complete(event.data).map_err(ReadHciError::from)?;
249                            self.slots
250                                .complete(e.cmd_opcode, e.status, e.num_hci_cmd_pkts as usize, &[]);
251                            continue;
252                        }
253                        _ => return Ok(value),
254                    },
255                    _ => return Ok(value),
256                }
257            }
258        }
259    }
260}
261
262impl<T, C, const SLOTS: usize> ControllerCmdSync<C> for ExternalController<T, SLOTS>
263where
264    T: Transport,
265    C: cmd::SyncCmd,
266    C::Return: FixedSizeValue,
267    T::Error: From<ReadHciError<Infallible>>,
268{
269    async fn exec(&self, cmd: &C) -> Result<C::Return, cmd::Error<Self::Error>> {
270        let mut retval: C::ReturnBuf = C::ReturnBuf::new();
271
272        //info!("Executing command with opcode {}", C::OPCODE);
273        let (slot, idx) = self.slots.acquire(C::OPCODE, retval.as_mut()).await;
274        let _d = OnDrop::new(|| {
275            self.slots.release_slot(idx);
276        });
277
278        self.transport.write(cmd).await.map_err(cmd::Error::Io)?;
279
280        let result = slot.wait().await;
281        let return_param_bytes = RemainingBytes::from_hci_bytes_complete(&retval.as_ref()[..result.len]).unwrap();
282        let e = CommandCompleteWithStatus {
283            num_hci_cmd_pkts: 0,
284            status: result.status,
285            cmd_opcode: C::OPCODE,
286            return_param_bytes,
287        };
288        let r = e.to_result::<C>().map_err(cmd::Error::Hci)?;
289        // info!("Done executing command with opcode {}", C::OPCODE);
290        Ok(r)
291    }
292}
293
294impl<T, C, const SLOTS: usize> ControllerCmdAsync<C> for ExternalController<T, SLOTS>
295where
296    T: Transport,
297    C: cmd::AsyncCmd,
298    T::Error: for<'a> From<ReadHciError<Infallible>>,
299{
300    async fn exec(&self, cmd: &C) -> Result<(), cmd::Error<Self::Error>> {
301        let (slot, idx) = self.slots.acquire(C::OPCODE, &mut []).await;
302        let _d = OnDrop::new(|| {
303            self.slots.release_slot(idx);
304        });
305
306        self.transport.write(cmd).await.map_err(cmd::Error::Io)?;
307
308        let result = slot.wait().await;
309        result.status.to_result()?;
310        Ok(())
311    }
312}
313
314struct ControllerState<const SLOTS: usize> {
315    permits: LocalSemaphore,
316    slots: RefCell<[CommandSlot; SLOTS]>,
317    signals: [Signal<NoopRawMutex, CommandResponse>; SLOTS],
318    waker: AtomicWaker,
319}
320
321struct CommandResponse {
322    status: Status,
323    len: usize,
324}
325
326enum CommandSlot {
327    Empty,
328    Pending { opcode: u16, event: *mut [u8] },
329}
330
331impl<const SLOTS: usize> Default for ControllerState<SLOTS> {
332    fn default() -> Self {
333        Self::new()
334    }
335}
336
337impl<const SLOTS: usize> ControllerState<SLOTS> {
338    const EMPTY_SLOT: CommandSlot = CommandSlot::Empty;
339    #[allow(clippy::declare_interior_mutable_const)]
340    const EMPTY_SIGNAL: Signal<NoopRawMutex, CommandResponse> = Signal::new();
341
342    fn new() -> Self {
343        Self {
344            permits: LocalSemaphore::new(true, 1),
345            slots: RefCell::new([Self::EMPTY_SLOT; SLOTS]),
346            signals: [Self::EMPTY_SIGNAL; SLOTS],
347            waker: AtomicWaker::new(),
348        }
349    }
350
351    fn complete(&self, op: cmd::Opcode, status: Status, num_hci_command_packets: usize, data: &[u8]) {
352        let mut slots = self.slots.borrow_mut();
353        for (idx, slot) in slots.iter_mut().enumerate() {
354            match slot {
355                CommandSlot::Pending { opcode, event } if *opcode == op.to_raw() => {
356                    if !data.is_empty() {
357                        assert!(!event.is_null());
358                        // Safety: since the slot is in pending, the caller stack will be valid.
359                        unsafe { (&mut (**event))[..data.len()].copy_from_slice(data) };
360                    }
361                    self.signals[idx].signal(CommandResponse {
362                        status,
363                        len: data.len(),
364                    });
365                    if op != Reset::OPCODE {
366                        break;
367                    }
368                }
369                CommandSlot::Pending { opcode: _, event: _ } if op == Reset::OPCODE => {
370                    // Signal other commands
371                    self.signals[idx].signal(CommandResponse {
372                        status: Status::CONTROLLER_BUSY,
373                        len: 0,
374                    });
375                }
376                _ => {}
377            }
378        }
379
380        // Adjust the semaphore permits ensuring we don't grant more than num_hci_cmd_pkts
381        self.permits
382            .release(num_hci_command_packets.saturating_sub(self.permits.permits()));
383    }
384
385    fn release_slot(&self, idx: usize) {
386        let mut slots = self.slots.borrow_mut();
387        slots[idx] = CommandSlot::Empty;
388    }
389
390    async fn acquire(&self, op: cmd::Opcode, event: *mut [u8]) -> (&Signal<NoopRawMutex, CommandResponse>, usize) {
391        let to_acquire = if op == Reset::OPCODE { self.permits.permits() } else { 1 };
392        let mut permit = self.permits.acquire(to_acquire).await;
393        permit.disarm();
394        poll_fn(|cx| match self.acquire_slot(op, event) {
395            Some(ret) => Poll::Ready(ret),
396            None => {
397                self.waker.register(cx.waker());
398                Poll::Pending
399            }
400        })
401        .await
402    }
403
404    fn acquire_slot(
405        &self,
406        op: cmd::Opcode,
407        event: *mut [u8],
408    ) -> Option<(&Signal<NoopRawMutex, CommandResponse>, usize)> {
409        let mut slots = self.slots.borrow_mut();
410        // Make sure there are no existing command with this opcode
411        for slot in slots.iter() {
412            match slot {
413                CommandSlot::Pending { opcode, event: _ } if *opcode == op.to_raw() => {
414                    return None;
415                }
416                _ => {}
417            }
418        }
419        // Reserve our slot
420        for (idx, slot) in slots.iter_mut().enumerate() {
421            if matches!(slot, CommandSlot::Empty) {
422                *slot = CommandSlot::Pending {
423                    opcode: op.to_raw(),
424                    event,
425                };
426                self.signals[idx].reset();
427                return Some((&self.signals[idx], idx));
428            }
429        }
430        None
431    }
432}
433
434/// A type to delay the drop handler invocation.
435#[must_use = "to delay the drop handler invocation to the end of the scope"]
436struct OnDrop<F: FnOnce()> {
437    f: MaybeUninit<F>,
438}
439
440impl<F: FnOnce()> OnDrop<F> {
441    /// Create a new instance.
442    pub(crate) fn new(f: F) -> Self {
443        Self { f: MaybeUninit::new(f) }
444    }
445}
446
447impl<F: FnOnce()> Drop for OnDrop<F> {
448    fn drop(&mut self) {
449        unsafe { self.f.as_ptr().read()() }
450    }
451}
452
453#[cfg(test)]
454mod tests {
455    use bt_hci_transport::{PacketKind, PacketToController, PacketToHost};
456
457    use super::*;
458
459    pub struct TestTransport<'d> {
460        pub rx: &'d [u8],
461    }
462
463    #[derive(Clone, Copy, Debug, PartialEq)]
464    pub struct Error;
465
466    impl core::fmt::Display for Error {
467        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
468            write!(f, "{:?}", self)
469        }
470    }
471
472    impl core::error::Error for Error {}
473
474    impl From<ReadHciError<Infallible>> for Error {
475        fn from(_: ReadHciError<Infallible>) -> Self {
476            Self
477        }
478    }
479
480    impl ErrorType for TestTransport<'_> {
481        type Error = Error;
482    }
483    impl embedded_io::Error for Error {
484        fn kind(&self) -> embedded_io::ErrorKind {
485            embedded_io::ErrorKind::Other
486        }
487    }
488    impl Transport for TestTransport<'_> {
489        fn read<'a, P: PacketToHost<'a>>(&self, rx: &'a mut [u8]) -> impl Future<Output = Result<P, Self::Error>> {
490            async {
491                let to_read = rx.len().min(self.rx.len());
492                let mut reader = &self.rx[..to_read];
493                let kind = PacketKind::read(&mut reader)?;
494                let pkt = P::read_hci(kind, &mut reader, rx)?;
495                if !reader.is_empty() {
496                    return Err(Error);
497                }
498                Ok(pkt)
499            }
500        }
501
502        fn write<T: PacketToController>(&self, _val: &T) -> impl Future<Output = Result<(), Self::Error>> {
503            async { todo!() }
504        }
505    }
506
507    #[futures_test::test]
508    pub async fn test_can_handle_unsolicited_command_complete() {
509        let t = TestTransport {
510            rx: &[
511                4, 0x0e, 3, // header
512                1, 0, 0, // special command
513            ],
514        };
515        let c: ExternalController<_, 10> = ExternalController::new(t);
516
517        let mut buf = c.alloc_buf().unwrap();
518        let pkt = c.read(&mut buf).await;
519        assert!(pkt.is_ok());
520    }
521}