esp_radio/ble/
mod.rs

1//! Bluetooth Low Energy HCI interface
2//!
3//! The usage of BLE is currently incompatible with the usage of IEEE 802.15.4.
4
5#[cfg(bt_controller = "btdm")]
6pub(crate) mod btdm;
7
8#[cfg(bt_controller = "npl")]
9pub(crate) mod npl;
10
11use alloc::{boxed::Box, collections::vec_deque::VecDeque, vec::Vec};
12use core::mem::MaybeUninit;
13
14pub(crate) use ble::{ble_deinit, ble_init, send_hci};
15use docsplay::Display;
16use esp_sync::NonReentrantMutex;
17
18/// An error that is returned when the configuration is invalid.
19#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, Hash)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21#[non_exhaustive]
22pub struct InvalidConfigError;
23
24impl core::error::Error for InvalidConfigError {}
25
26// Expose chip-specific configuration types
27pub use ble::ble_os_adapter_chip_specific::*;
28
29#[cfg(bt_controller = "btdm")]
30use self::btdm as ble;
31#[cfg(bt_controller = "npl")]
32use self::npl as ble;
33
34unstable_module! {
35    pub mod controller;
36}
37
38pub(crate) unsafe extern "C" fn malloc(size: u32) -> *mut crate::sys::c_types::c_void {
39    unsafe { crate::compat::malloc::malloc(size as usize).cast() }
40}
41
42#[cfg(any(esp32, esp32c3, esp32s3))]
43pub(crate) unsafe extern "C" fn malloc_internal(size: u32) -> *mut crate::sys::c_types::c_void {
44    unsafe { crate::compat::malloc::malloc_internal(size as usize).cast() }
45}
46
47pub(crate) unsafe extern "C" fn free(ptr: *mut crate::sys::c_types::c_void) {
48    unsafe { crate::compat::malloc::free(ptr.cast()) }
49}
50
51struct BleState {
52    pub rx_queue: VecDeque<ReceivedPacket>,
53    pub hci_read_data: Vec<u8>,
54}
55
56static BT_STATE: NonReentrantMutex<BleState> = NonReentrantMutex::new(BleState {
57    rx_queue: VecDeque::new(),
58    hci_read_data: Vec::new(),
59});
60
61static mut HCI_OUT_COLLECTOR: MaybeUninit<HciOutCollector> = MaybeUninit::uninit();
62
63#[derive(PartialEq, Debug)]
64enum HciOutType {
65    Unknown,
66    Acl,
67    Command,
68}
69
70struct HciOutCollector {
71    data: [u8; 256],
72    index: usize,
73    ready: bool,
74    kind: HciOutType,
75}
76
77impl HciOutCollector {
78    fn new() -> HciOutCollector {
79        HciOutCollector {
80            data: [0u8; 256],
81            index: 0,
82            ready: false,
83            kind: HciOutType::Unknown,
84        }
85    }
86
87    fn is_ready(&self) -> bool {
88        self.ready
89    }
90
91    fn push(&mut self, data: &[u8]) {
92        self.data[self.index..(self.index + data.len())].copy_from_slice(data);
93        self.index += data.len();
94
95        if self.kind == HciOutType::Unknown {
96            self.kind = match self.data[0] {
97                1 => HciOutType::Command,
98                2 => HciOutType::Acl,
99                _ => HciOutType::Unknown,
100            };
101        }
102
103        if !self.ready {
104            if self.kind == HciOutType::Command && self.index >= 4 {
105                if self.index == self.data[3] as usize + 4 {
106                    self.ready = true;
107                }
108            } else if self.kind == HciOutType::Acl
109                && self.index >= 5
110                && self.index == (self.data[3] as usize) + ((self.data[4] as usize) << 8) + 5
111            {
112                self.ready = true;
113            }
114        }
115    }
116
117    fn reset(&mut self) {
118        self.index = 0;
119        self.ready = false;
120        self.kind = HciOutType::Unknown;
121    }
122
123    fn packet(&self) -> &[u8] {
124        &self.data[0..self.index]
125    }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, Hash)]
129/// Represents a received BLE packet.
130#[instability::unstable]
131pub struct ReceivedPacket {
132    /// The data of the received packet.
133    pub data: Box<[u8]>,
134}
135
136#[cfg(feature = "defmt")]
137impl defmt::Format for ReceivedPacket {
138    fn format(&self, fmt: defmt::Formatter<'_>) {
139        defmt::write!(fmt, "ReceivedPacket {}", &self.data[..])
140    }
141}
142
143/// Checks if there is any HCI data available to read.
144#[instability::unstable]
145pub fn have_hci_read_data() -> bool {
146    BT_STATE.with(|state| !state.rx_queue.is_empty() || !state.hci_read_data.is_empty())
147}
148
149pub(crate) fn read_next(data: &mut [u8]) -> usize {
150    if let Some(packet) = BT_STATE.with(|state| state.rx_queue.pop_front()) {
151        data[..packet.data.len()].copy_from_slice(&packet.data[..packet.data.len()]);
152        packet.data.len()
153    } else {
154        0
155    }
156}
157
158/// Reads the next HCI packet from the BLE controller.
159#[instability::unstable]
160pub fn read_hci(data: &mut [u8]) -> usize {
161    BT_STATE.with(|state| {
162        if state.hci_read_data.is_empty()
163            && let Some(packet) = state.rx_queue.pop_front()
164        {
165            state.hci_read_data.extend_from_slice(&packet.data);
166        }
167
168        let l = usize::min(state.hci_read_data.len(), data.len());
169        data[..l].copy_from_slice(&state.hci_read_data[..l]);
170        state.hci_read_data.drain(..l);
171        l
172    })
173}
174
175fn dump_packet_info(_buffer: &[u8]) {
176    #[cfg(dump_packets)]
177    info!("@HCIFRAME {:?}", _buffer);
178}
179
180macro_rules! validate_range {
181    ($this:ident, $field:ident, $min:expr, $max:expr) => {
182        if !($min..=$max).contains(&$this.$field) {
183            error!(
184                "{} must be between {} and {}, current value is {}",
185                stringify!($field),
186                $min,
187                $max,
188                $this.$field
189            );
190            return Err(InvalidConfigError);
191        }
192    };
193}
194pub(crate) use validate_range;