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