1#[cfg(bt_controller = "btdm")]
4pub(crate) mod btdm;
5
6#[cfg(bt_controller = "npl")]
7pub(crate) mod npl;
8#[cfg(bt_controller = "npl")]
9mod os_mempool;
10
11use alloc::{boxed::Box, collections::vec_deque::VecDeque};
12use core::mem::MaybeUninit;
13
14pub(crate) use ble::{ble_deinit, ble_init, send_hci, send_hci_async};
15use docsplay::Display;
16use esp_sync::NonReentrantMutex;
17
18#[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
26pub 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
47#[cfg(any(esp32c3, esp32s3))]
48pub(crate) unsafe extern "C" fn malloc_retention(size: u32) -> *mut crate::sys::c_types::c_void {
49 unsafe { crate::compat::malloc::malloc_internal(size as usize).cast() }
52}
53
54pub(crate) unsafe extern "C" fn free(ptr: *mut crate::sys::c_types::c_void) {
55 unsafe { crate::compat::malloc::free(ptr.cast()) }
56}
57
58struct BleState {
59 pub rx_queue: VecDeque<ReceivedPacket>,
60 pub partial_read: Option<(Box<[u8]>, usize)>,
63}
64
65static BT_STATE: NonReentrantMutex<BleState> = NonReentrantMutex::new(BleState {
66 rx_queue: VecDeque::new(),
67 partial_read: None,
68});
69
70static mut HCI_OUT_COLLECTOR: MaybeUninit<HciOutCollector> = MaybeUninit::uninit();
71
72#[derive(PartialEq, Debug)]
73enum HciOutType {
74 Unknown,
75 Acl,
76 Command,
77}
78
79const MAX_HCI_PACKET_LEN: usize = 259;
81
82struct HciOutCollector {
88 data: [u8; MAX_HCI_PACKET_LEN],
89 index: usize,
90 ready: bool,
91 kind: HciOutType,
92}
93
94impl HciOutCollector {
95 fn new() -> HciOutCollector {
96 HciOutCollector {
97 data: [0u8; MAX_HCI_PACKET_LEN],
98 index: 0,
99 ready: false,
100 kind: HciOutType::Unknown,
101 }
102 }
103
104 fn is_ready(&self) -> bool {
105 self.ready
106 }
107
108 fn header_len(&self) -> usize {
113 match self.kind {
114 HciOutType::Unknown => 1,
115 HciOutType::Command => 4,
116 HciOutType::Acl => 5,
117 }
118 }
119
120 fn packet_len(&self) -> Option<usize> {
122 if self.index < self.header_len() {
123 return None;
124 }
125
126 match self.kind {
127 HciOutType::Unknown => None,
128 HciOutType::Command => Some(self.data[3] as usize + 4),
129 HciOutType::Acl => Some(u16::from_le_bytes([self.data[3], self.data[4]]) as usize + 5),
130 }
131 }
132
133 fn fill_to(&mut self, data: &[u8], upto: usize) -> usize {
137 let take = usize::min(data.len(), upto - self.index);
138 self.data[self.index..][..take].copy_from_slice(&data[..take]);
139 self.index += take;
140 take
141 }
142
143 fn push(&mut self, data: &[u8]) -> usize {
148 if data.is_empty() {
149 return 0;
150 }
151
152 if self.index == 0 {
153 self.kind = match data[0] {
154 1 => HciOutType::Command,
155 2 => HciOutType::Acl,
156 indicator => {
157 warn!(
158 "Dropping HCI byte with unknown packet type indicator {}",
159 indicator
160 );
161 return 1;
162 }
163 };
164 }
165
166 let mut taken = 0;
169 if self.packet_len().is_none() {
170 taken += self.fill_to(data, self.header_len());
171 }
172
173 if let Some(total) = self.packet_len() {
174 if total > self.data.len() {
175 warn!("Dropping HCI packet of {} bytes, which is too long", total);
176 self.reset();
177 return taken;
178 }
179
180 taken += self.fill_to(&data[taken..], total);
181 self.ready = self.index == total;
182 }
183
184 taken
185 }
186
187 fn reset(&mut self) {
188 self.index = 0;
189 self.ready = false;
190 self.kind = HciOutType::Unknown;
191 }
192
193 fn packet(&self) -> &[u8] {
194 &self.data[0..self.index]
195 }
196}
197
198pub(crate) fn collect_and_send(data: &[u8], send: impl FnOnce(&[u8])) -> usize {
204 let hci_out = unsafe { (*core::ptr::addr_of_mut!(HCI_OUT_COLLECTOR)).assume_init_mut() };
205
206 let taken = hci_out.push(data);
207
208 if hci_out.is_ready() {
209 send(hci_out.packet());
210 hci_out.reset();
211 }
212
213 taken
214}
215
216#[derive(Debug, Clone, PartialEq, Eq, Hash)]
217#[instability::unstable]
219pub struct ReceivedPacket {
220 pub data: Box<[u8]>,
222}
223
224#[cfg(feature = "defmt")]
225impl defmt::Format for ReceivedPacket {
226 fn format(&self, fmt: defmt::Formatter<'_>) {
227 defmt::write!(fmt, "ReceivedPacket {}", &self.data[..])
228 }
229}
230
231pub(crate) fn clear_bt_state() {
233 BT_STATE.with(|state| {
234 state.rx_queue.clear();
235 state.partial_read = None;
236 });
237}
238
239#[instability::unstable]
241pub fn have_hci_read_data() -> bool {
242 BT_STATE.with(|state| !state.rx_queue.is_empty() || state.partial_read.is_some())
243}
244
245pub(crate) fn have_hci_packet() -> bool {
247 BT_STATE.with(|state| !state.rx_queue.is_empty())
248}
249
250pub(crate) fn take_next() -> Option<Box<[u8]>> {
252 BT_STATE.with(|state| state.rx_queue.pop_front().map(|packet| packet.data))
253}
254
255pub(crate) fn read_next(data: &mut [u8]) -> usize {
256 if let Some(packet) = take_next() {
257 data[..packet.len()].copy_from_slice(&packet);
258 packet.len()
259 } else {
260 0
261 }
262}
263
264#[instability::unstable]
266pub fn read_hci(data: &mut [u8]) -> usize {
267 BT_STATE.with(|state| {
268 if state.partial_read.is_none()
269 && let Some(packet) = state.rx_queue.pop_front()
270 {
271 state.partial_read = Some((packet.data, 0));
272 }
273
274 let Some((packet, read)) = state.partial_read.as_mut() else {
275 return 0;
276 };
277
278 let remaining = &packet[*read..];
279 let l = usize::min(remaining.len(), data.len());
280 data[..l].copy_from_slice(&remaining[..l]);
281 *read += l;
282
283 let drained = *read == packet.len();
284 if drained {
285 state.partial_read = None;
286 }
287
288 l
289 })
290}
291
292fn dump_packet_info(_buffer: &[u8]) {
293 #[cfg(dump_packets)]
294 info!("@HCIFRAME {:?}", _buffer);
295}
296
297macro_rules! validate_range {
298 ($this:ident, $field:ident, $min:expr, $max:expr) => {
299 if !($min..=$max).contains(&$this.$field) {
300 error!(
301 "{} must be between {} and {}, current value is {}",
302 stringify!($field),
303 $min,
304 $max,
305 $this.$field
306 );
307 return Err(InvalidConfigError);
308 }
309 };
310}
311pub(crate) use validate_range;