1#[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#[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#[instability::unstable]
126pub struct ReceivedPacket {
127 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#[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#[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;