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, 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                            self.slots.complete(
108                                e.cmd_opcode,
109                                e.status,
110                                e.num_hci_cmd_pkts as usize,
111                                e.return_param_bytes.as_ref(),
112                            );
113                            continue;
114                        }
115                        EventKind::CommandStatus => {
116                            let e = CommandStatus::from_hci_bytes_complete(event.data)?;
117                            self.slots
118                                .complete(e.cmd_opcode, e.status, e.num_hci_cmd_pkts as usize, &[]);
119                            continue;
120                        }
121                        _ => return Ok(value),
122                    },
123                    _ => return Ok(value),
124                }
125            }
126        }
127    }
128}
129
130impl<T, const SLOTS: usize> blocking::Controller for ExternalController<T, SLOTS>
131where
132    T: crate::transport::blocking::Transport,
133    T::Error: From<FromHciBytesError>,
134{
135    fn write_acl_data(&self, packet: &data::AclPacket<'_>) -> Result<(), Self::Error> {
136        loop {
137            match self.try_write_acl_data(packet) {
138                Err(blocking::TryError::Busy) => {}
139                Err(blocking::TryError::Error(e)) => return Err(e),
140                Ok(r) => return Ok(r),
141            }
142        }
143    }
144
145    fn write_sync_data(&self, packet: &data::SyncPacket<'_>) -> Result<(), Self::Error> {
146        loop {
147            match self.try_write_sync_data(packet) {
148                Err(blocking::TryError::Busy) => {}
149                Err(blocking::TryError::Error(e)) => return Err(e),
150                Ok(r) => return Ok(r),
151            }
152        }
153    }
154
155    fn write_iso_data(&self, packet: &data::IsoPacket<'_>) -> Result<(), Self::Error> {
156        loop {
157            match self.try_write_iso_data(packet) {
158                Err(blocking::TryError::Busy) => {}
159                Err(blocking::TryError::Error(e)) => return Err(e),
160                Ok(r) => return Ok(r),
161            }
162        }
163    }
164
165    fn read<'a>(&self, buf: &'a mut [u8]) -> Result<ControllerToHostPacket<'a>, Self::Error> {
166        loop {
167            // Safety: we will not hold references across loop iterations.
168            let buf = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) };
169            match self.try_read(buf) {
170                Err(blocking::TryError::Busy) => {}
171                Err(blocking::TryError::Error(e)) => return Err(e),
172                Ok(r) => return Ok(r),
173            }
174        }
175    }
176
177    fn try_write_acl_data(&self, packet: &data::AclPacket<'_>) -> Result<(), blocking::TryError<Self::Error>> {
178        self.transport.write(packet)?;
179        Ok(())
180    }
181
182    fn try_write_sync_data(&self, packet: &data::SyncPacket<'_>) -> Result<(), blocking::TryError<Self::Error>> {
183        self.transport.write(packet)?;
184        Ok(())
185    }
186
187    fn try_write_iso_data(&self, packet: &data::IsoPacket<'_>) -> Result<(), blocking::TryError<Self::Error>> {
188        self.transport.write(packet)?;
189        Ok(())
190    }
191
192    fn try_read<'a>(&self, buf: &'a mut [u8]) -> Result<ControllerToHostPacket<'a>, blocking::TryError<Self::Error>> {
193        loop {
194            {
195                // Safety: we will not hold references across loop iterations.
196                let buf = unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()) };
197                let value = self.transport.read(&mut buf[..])?;
198                match value {
199                    ControllerToHostPacket::Event(ref event) => match event.kind {
200                        EventKind::CommandComplete => {
201                            let e = CommandComplete::from_hci_bytes_complete(event.data)?;
202                            self.slots.complete(
203                                e.cmd_opcode,
204                                e.status,
205                                e.num_hci_cmd_pkts as usize,
206                                e.return_param_bytes.as_ref(),
207                            );
208                            continue;
209                        }
210                        EventKind::CommandStatus => {
211                            let e = CommandStatus::from_hci_bytes_complete(event.data)?;
212                            self.slots
213                                .complete(e.cmd_opcode, e.status, e.num_hci_cmd_pkts as usize, &[]);
214                            continue;
215                        }
216                        _ => return Ok(value),
217                    },
218                    _ => return Ok(value),
219                }
220            }
221        }
222    }
223}
224
225impl<T, C, const SLOTS: usize> ControllerCmdSync<C> for ExternalController<T, SLOTS>
226where
227    T: Transport,
228    C: cmd::SyncCmd,
229    C::Return: FixedSizeValue,
230    T::Error: From<FromHciBytesError>,
231{
232    async fn exec(&self, cmd: &C) -> Result<C::Return, cmd::Error<Self::Error>> {
233        let mut retval: C::ReturnBuf = C::ReturnBuf::new();
234
235        //info!("Executing command with opcode {}", C::OPCODE);
236        let (slot, idx) = self.slots.acquire(C::OPCODE, retval.as_mut()).await;
237        let _d = OnDrop::new(|| {
238            self.slots.release_slot(idx);
239        });
240
241        self.transport.write(cmd).await.map_err(cmd::Error::Io)?;
242
243        let result = slot.wait().await;
244        let return_param_bytes = RemainingBytes::from_hci_bytes_complete(&retval.as_ref()[..result.len]).unwrap();
245        let e = CommandComplete {
246            num_hci_cmd_pkts: 0,
247            status: result.status,
248            cmd_opcode: C::OPCODE,
249            return_param_bytes,
250        };
251        let r = e.to_result::<C>().map_err(cmd::Error::Hci)?;
252        // info!("Done executing command with opcode {}", C::OPCODE);
253        Ok(r)
254    }
255}
256
257impl<T, C, const SLOTS: usize> ControllerCmdAsync<C> for ExternalController<T, SLOTS>
258where
259    T: Transport,
260    C: cmd::AsyncCmd,
261    T::Error: From<FromHciBytesError>,
262{
263    async fn exec(&self, cmd: &C) -> Result<(), cmd::Error<Self::Error>> {
264        let (slot, idx) = self.slots.acquire(C::OPCODE, &mut []).await;
265        let _d = OnDrop::new(|| {
266            self.slots.release_slot(idx);
267        });
268
269        self.transport.write(cmd).await.map_err(cmd::Error::Io)?;
270
271        let result = slot.wait().await;
272        result.status.to_result()?;
273        Ok(())
274    }
275}
276
277struct ControllerState<const SLOTS: usize> {
278    permits: LocalSemaphore,
279    slots: RefCell<[CommandSlot; SLOTS]>,
280    signals: [Signal<NoopRawMutex, CommandResponse>; SLOTS],
281    waker: AtomicWaker,
282}
283
284struct CommandResponse {
285    status: Status,
286    len: usize,
287}
288
289enum CommandSlot {
290    Empty,
291    Pending { opcode: u16, event: *mut [u8] },
292}
293
294impl<const SLOTS: usize> Default for ControllerState<SLOTS> {
295    fn default() -> Self {
296        Self::new()
297    }
298}
299
300impl<const SLOTS: usize> ControllerState<SLOTS> {
301    const EMPTY_SLOT: CommandSlot = CommandSlot::Empty;
302    #[allow(clippy::declare_interior_mutable_const)]
303    const EMPTY_SIGNAL: Signal<NoopRawMutex, CommandResponse> = Signal::new();
304
305    fn new() -> Self {
306        Self {
307            permits: LocalSemaphore::new(true, 1),
308            slots: RefCell::new([Self::EMPTY_SLOT; SLOTS]),
309            signals: [Self::EMPTY_SIGNAL; SLOTS],
310            waker: AtomicWaker::new(),
311        }
312    }
313
314    fn complete(&self, op: cmd::Opcode, status: Status, num_hci_command_packets: usize, data: &[u8]) {
315        let mut slots = self.slots.borrow_mut();
316        for (idx, slot) in slots.iter_mut().enumerate() {
317            match slot {
318                CommandSlot::Pending { opcode, event } if *opcode == op.to_raw() => {
319                    if !data.is_empty() {
320                        assert!(!event.is_null());
321                        // Safety: since the slot is in pending, the caller stack will be valid.
322                        unsafe { (&mut (**event))[..data.len()].copy_from_slice(data) };
323                    }
324                    self.signals[idx].signal(CommandResponse {
325                        status,
326                        len: data.len(),
327                    });
328                    if op != Reset::OPCODE {
329                        break;
330                    }
331                }
332                CommandSlot::Pending { opcode: _, event: _ } if op == Reset::OPCODE => {
333                    // Signal other commands
334                    self.signals[idx].signal(CommandResponse {
335                        status: Status::CONTROLLER_BUSY,
336                        len: 0,
337                    });
338                }
339                _ => {}
340            }
341        }
342
343        // Adjust the semaphore permits ensuring we don't grant more than num_hci_cmd_pkts
344        self.permits
345            .release(num_hci_command_packets.saturating_sub(self.permits.permits()));
346    }
347
348    fn release_slot(&self, idx: usize) {
349        let mut slots = self.slots.borrow_mut();
350        slots[idx] = CommandSlot::Empty;
351    }
352
353    async fn acquire(&self, op: cmd::Opcode, event: *mut [u8]) -> (&Signal<NoopRawMutex, CommandResponse>, usize) {
354        let to_acquire = if op == Reset::OPCODE { self.permits.permits() } else { 1 };
355        let mut permit = self.permits.acquire(to_acquire).await;
356        permit.disarm();
357        poll_fn(|cx| match self.acquire_slot(op, event) {
358            Some(ret) => Poll::Ready(ret),
359            None => {
360                self.waker.register(cx.waker());
361                Poll::Pending
362            }
363        })
364        .await
365    }
366
367    fn acquire_slot(
368        &self,
369        op: cmd::Opcode,
370        event: *mut [u8],
371    ) -> Option<(&Signal<NoopRawMutex, CommandResponse>, usize)> {
372        let mut slots = self.slots.borrow_mut();
373        // Make sure there are no existing command with this opcode
374        for slot in slots.iter() {
375            match slot {
376                CommandSlot::Pending { opcode, event: _ } if *opcode == op.to_raw() => {
377                    return None;
378                }
379                _ => {}
380            }
381        }
382        // Reserve our slot
383        for (idx, slot) in slots.iter_mut().enumerate() {
384            if matches!(slot, CommandSlot::Empty) {
385                *slot = CommandSlot::Pending {
386                    opcode: op.to_raw(),
387                    event,
388                };
389                self.signals[idx].reset();
390                return Some((&self.signals[idx], idx));
391            }
392        }
393        None
394    }
395}
396
397/// A type to delay the drop handler invocation.
398#[must_use = "to delay the drop handler invocation to the end of the scope"]
399struct OnDrop<F: FnOnce()> {
400    f: MaybeUninit<F>,
401}
402
403impl<F: FnOnce()> OnDrop<F> {
404    /// Create a new instance.
405    pub(crate) fn new(f: F) -> Self {
406        Self { f: MaybeUninit::new(f) }
407    }
408}
409
410impl<F: FnOnce()> Drop for OnDrop<F> {
411    fn drop(&mut self) {
412        unsafe { self.f.as_ptr().read()() }
413    }
414}