bt_hci/
controller.rs

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