1use crate::{
2 BLEAddress, BLEConnDesc, BLEDevice, BLEError, BLERemoteService, Signal, ble,
3 ble_device::OWN_ADDR_TYPE,
4 utilities::{ArcUnsafeCell, BleUuid, as_void_ptr, voidp_to_ref},
5};
6use alloc::{boxed::Box, vec::Vec};
7use core::{cell::UnsafeCell, ffi::c_void, ptr};
8use esp_idf_svc::sys as esp_idf_sys;
9use esp_idf_sys::*;
10
11#[allow(clippy::type_complexity)]
12pub(crate) struct BLEClientState {
13 address: Option<BLEAddress>,
14 pub(crate) conn_handle: u16,
15 services: Option<Vec<BLERemoteService>>,
16 signal: Signal<u32>,
17 connect_timeout_ms: u32,
18 ble_gap_conn_params: ble_gap_conn_params,
19 on_passkey_request: Option<Box<dyn Fn() -> u32 + Send + Sync>>,
20 on_confirm_pin: Option<Box<dyn Fn(u32) -> bool + Send + Sync>>,
21 on_connect: Option<Box<dyn Fn(&mut BLEClient) + Send + Sync>>,
22 on_disconnect: Option<Box<dyn Fn(i32) + Send + Sync>>,
23}
24
25pub struct BLEClient {
26 state: ArcUnsafeCell<BLEClientState>,
27}
28
29impl BLEClient {
30 pub(crate) fn new() -> Self {
31 Self {
32 state: ArcUnsafeCell::new(BLEClientState {
33 address: None,
34 conn_handle: esp_idf_sys::BLE_HS_CONN_HANDLE_NONE as _,
35 services: None,
36 connect_timeout_ms: 30000,
37 ble_gap_conn_params: ble_gap_conn_params {
38 scan_itvl: 16,
39 scan_window: 16,
40 itvl_min: (30 * 1000 / BLE_HCI_CONN_ITVL) as _,
41 itvl_max: (50 * 1000 / BLE_HCI_CONN_ITVL) as _,
42 latency: BLE_GAP_INITIAL_CONN_LATENCY as _,
43 supervision_timeout: BLE_GAP_INITIAL_SUPERVISION_TIMEOUT as _,
44 min_ce_len: BLE_GAP_INITIAL_CONN_MIN_CE_LEN as _,
45 max_ce_len: BLE_GAP_INITIAL_CONN_MAX_CE_LEN as _,
46 },
47 signal: Signal::new(),
48 on_passkey_request: None,
49 on_confirm_pin: None,
50 on_disconnect: None,
51 on_connect: None,
52 }),
53 }
54 }
55
56 pub(crate) fn conn_handle(&self) -> u16 {
57 self.state.conn_handle
58 }
59
60 pub fn on_passkey_request(
61 &mut self,
62 callback: impl Fn() -> u32 + Send + Sync + 'static,
63 ) -> &mut Self {
64 self.state.on_passkey_request = Some(Box::new(callback));
65 self
66 }
67
68 pub fn on_confirm_pin(
69 &mut self,
70 callback: impl Fn(u32) -> bool + Send + Sync + 'static,
71 ) -> &mut Self {
72 self.state.on_confirm_pin = Some(Box::new(callback));
73 self
74 }
75
76 pub fn on_connect(
77 &mut self,
78 callback: impl Fn(&mut Self) + Send + Sync + 'static,
79 ) -> &mut Self {
80 self.state.on_connect = Some(Box::new(callback));
81 self
82 }
83
84 pub fn on_disconnect(&mut self, callback: impl Fn(i32) + Send + Sync + 'static) -> &mut Self {
85 self.state.on_disconnect = Some(Box::new(callback));
86 self
87 }
88
89 pub async fn connect(&mut self, addr: &BLEAddress) -> Result<(), BLEError> {
90 unsafe {
91 if esp_idf_sys::ble_gap_conn_find_by_addr(&addr.value, core::ptr::null_mut()) == 0 {
92 ::log::warn!("A connection to {addr:?} already exists");
93 return BLEError::fail();
94 }
95
96 ble!(esp_idf_sys::ble_gap_connect(
97 OWN_ADDR_TYPE as _,
98 &addr.value,
99 self.state.connect_timeout_ms as _,
100 &self.state.ble_gap_conn_params,
101 Some(Self::handle_gap_event),
102 as_void_ptr(self),
103 ))?;
104 }
105
106 ble!(self.state.signal.wait().await)?;
107 self.state.address = Some(*addr);
108
109 let mut client = UnsafeCell::new(self);
110 unsafe {
111 if let Some(callback) = &(&(*client.get())).state.on_connect {
112 callback(client.get_mut());
113 }
114 }
115
116 Ok(())
117 }
118
119 pub async fn secure_connection(&mut self) -> Result<(), BLEError> {
120 unsafe {
121 ble!(esp_idf_sys::ble_gap_security_initiate(
122 self.state.conn_handle
123 ))?;
124 }
125 ble!(self.state.signal.wait().await)?;
126
127 ::log::info!("secure_connection: success");
128
129 Ok(())
130 }
131
132 pub fn disconnect(&mut self) -> Result<(), BLEError> {
134 self.disconnect_with_reason(esp_idf_sys::ble_error_codes_BLE_ERR_REM_USER_CONN_TERM as _)
135 }
136
137 pub fn disconnect_with_reason(&mut self, reason: u8) -> Result<(), BLEError> {
139 if !self.connected() {
140 return Ok(());
141 }
142
143 unsafe {
144 ble!(esp_idf_sys::ble_gap_terminate(
145 self.state.conn_handle,
146 reason
147 ))
148 }
149 }
150
151 pub fn connected(&self) -> bool {
152 self.state.conn_handle != (esp_idf_sys::BLE_HS_CONN_HANDLE_NONE as _)
153 }
154
155 pub fn set_connection_params(
164 &mut self,
165 min_interval: u16,
166 max_interval: u16,
167 latency: u16,
168 timeout: u16,
169 scan_interval: u16,
170 scan_window: u16,
171 ) {
172 self.state.ble_gap_conn_params.scan_itvl = scan_interval;
173 self.state.ble_gap_conn_params.scan_window = scan_window;
174 self.state.ble_gap_conn_params.itvl_min = min_interval;
175 self.state.ble_gap_conn_params.itvl_max = max_interval;
176 self.state.ble_gap_conn_params.latency = latency;
177 self.state.ble_gap_conn_params.supervision_timeout = timeout;
178 }
179
180 pub fn update_conn_params(
188 &mut self,
189 min_interval: u16,
190 max_interval: u16,
191 latency: u16,
192 timeout: u16,
193 ) -> Result<(), BLEError> {
194 let params = esp_idf_sys::ble_gap_upd_params {
195 itvl_min: min_interval,
196 itvl_max: max_interval,
197 latency,
198 supervision_timeout: timeout,
199 min_ce_len: esp_idf_sys::BLE_GAP_INITIAL_CONN_MIN_CE_LEN as _,
200 max_ce_len: esp_idf_sys::BLE_GAP_INITIAL_CONN_MAX_CE_LEN as _,
201 };
202
203 unsafe {
204 ble!(esp_idf_sys::ble_gap_update_params(
205 self.state.conn_handle,
206 ¶ms
207 ))
208 }
209 }
210
211 pub fn desc(&self) -> Result<BLEConnDesc, crate::BLEError> {
212 crate::utilities::ble_gap_conn_find(self.conn_handle())
213 }
214
215 pub fn get_rssi(&self) -> Result<i8, BLEError> {
218 let mut rssi: i8 = 0;
219 unsafe {
220 ble!(esp_idf_sys::ble_gap_conn_rssi(
221 self.conn_handle(),
222 &mut rssi
223 ))?;
224 }
225 Ok(rssi)
226 }
227
228 pub async fn get_services(
229 &mut self,
230 ) -> Result<core::slice::IterMut<'_, BLERemoteService>, BLEError> {
231 if self.state.services.is_none() {
232 self.state.services = Some(Vec::new());
233 unsafe {
234 esp_idf_sys::ble_gattc_disc_all_svcs(
235 self.state.conn_handle,
236 Some(Self::service_discovered_cb),
237 as_void_ptr(self),
238 );
239 }
240 ble!(self.state.signal.wait().await)?;
241 }
242
243 Ok(self.state.services.as_mut().unwrap().iter_mut())
244 }
245
246 pub async fn get_service(&mut self, uuid: BleUuid) -> Result<&mut BLERemoteService, BLEError> {
247 let mut iter = self.get_services().await?;
248 iter.find(|x| x.uuid() == uuid)
249 .ok_or_else(|| BLEError::fail().unwrap_err())
250 }
251
252 extern "C" fn handle_gap_event(
253 event: *mut esp_idf_sys::ble_gap_event,
254 arg: *mut c_void,
255 ) -> i32 {
256 let event = unsafe { &*event };
257 let client = unsafe { voidp_to_ref::<Self>(arg) };
258
259 match event.type_ as _ {
260 BLE_GAP_EVENT_CONNECT => {
261 let connect = unsafe { &event.__bindgen_anon_1.connect };
262
263 if connect.status == 0 {
264 client.state.conn_handle = connect.conn_handle;
265
266 let rc = unsafe {
267 ble_gattc_exchange_mtu(connect.conn_handle, None, core::ptr::null_mut())
268 };
269
270 if rc != 0 {
271 client.state.signal.signal(rc as _);
272 }
273 } else {
274 ::log::info!("connect_status {}", connect.status);
275 client.state.conn_handle = esp_idf_sys::BLE_HS_CONN_HANDLE_NONE as _;
276 client.state.signal.signal(connect.status as _);
277 }
278 }
279 BLE_GAP_EVENT_DISCONNECT => {
280 let disconnect = unsafe { &event.__bindgen_anon_1.disconnect };
281 if client.state.conn_handle != disconnect.conn.conn_handle {
282 return 0;
283 }
284 client.state.conn_handle = esp_idf_sys::BLE_HS_CONN_HANDLE_NONE as _;
285
286 ::log::info!(
287 "Disconnected: {:?}",
288 BLEError::convert(disconnect.reason as _)
289 );
290
291 if let Some(callback) = &client.state.on_disconnect {
292 callback(disconnect.reason);
293 }
294 }
295 BLE_GAP_EVENT_ENC_CHANGE => {
296 let enc_change = unsafe { &event.__bindgen_anon_1.enc_change };
297 if client.state.conn_handle != enc_change.conn_handle {
298 return 0;
299 }
300
301 if enc_change.status
302 == ((BLE_HS_ERR_HCI_BASE + ble_error_codes_BLE_ERR_PINKEY_MISSING) as _)
303 {
304 let desc = crate::utilities::ble_gap_conn_find(enc_change.conn_handle).unwrap();
305 unsafe { esp_idf_sys::ble_store_util_delete_peer(&desc.0.peer_id_addr) };
306 }
307
308 client.state.signal.signal(enc_change.status as _);
309 }
310 BLE_GAP_EVENT_MTU => {
311 let mtu = unsafe { &event.__bindgen_anon_1.mtu };
312 if client.state.conn_handle != mtu.conn_handle {
313 return 0;
314 }
315 ::log::info!(
316 "mtu update event; conn_handle={} mtu={}",
317 mtu.conn_handle,
318 mtu.value
319 );
320 client.state.signal.signal(0);
321 }
322 BLE_GAP_EVENT_NOTIFY_RX => {
323 let notify_rx = unsafe { &event.__bindgen_anon_1.notify_rx };
324 if client.state.conn_handle != notify_rx.conn_handle {
325 return 0;
326 }
327
328 if let Some(services) = &mut client.state.services {
329 for service in services {
330 if service.state.end_handle < notify_rx.attr_handle {
331 continue;
332 }
333
334 if let Some(characteristics) = &mut service.state.characteristics {
335 for characteristic in characteristics {
336 if characteristic.state().handle == notify_rx.attr_handle {
337 unsafe {
338 characteristic.notify(notify_rx.om);
339 }
340 return 0;
341 }
342 }
343 }
344 }
345 }
346 }
347 BLE_GAP_EVENT_CONN_UPDATE_REQ | BLE_GAP_EVENT_L2CAP_UPDATE_REQ => {
348 let conn_update_req = unsafe { &event.__bindgen_anon_1.conn_update_req };
349 if client.state.conn_handle != conn_update_req.conn_handle {
350 return 0;
351 }
352 unsafe {
353 ::log::debug!("Peer requesting to update connection parameters");
354 ::log::debug!(
355 "MinInterval: {}, MaxInterval: {}, Latency: {}, Timeout: {}",
356 (*conn_update_req.peer_params).itvl_min,
357 (*conn_update_req.peer_params).itvl_max,
358 (*conn_update_req.peer_params).latency,
359 (*conn_update_req.peer_params).supervision_timeout
360 );
361 }
362 }
363 BLE_GAP_EVENT_PASSKEY_ACTION => {
364 let passkey = unsafe { &event.__bindgen_anon_1.passkey };
365 if client.state.conn_handle != passkey.conn_handle {
366 return 0;
367 }
368 let mut pkey = esp_idf_sys::ble_sm_io {
369 action: passkey.params.action,
370 ..Default::default()
371 };
372 match passkey.params.action as _ {
373 esp_idf_sys::BLE_SM_IOACT_DISP => {
374 pkey.__bindgen_anon_1.passkey = BLEDevice::take().security().get_passkey();
375 let rc = unsafe {
376 esp_idf_sys::ble_sm_inject_io(passkey.conn_handle, &mut pkey)
377 };
378 ::log::debug!("BLE_SM_IOACT_DISP; ble_sm_inject_io result: {rc}");
379 }
380 esp_idf_sys::BLE_SM_IOACT_NUMCMP => {
381 if let Some(callback) = &client.state.on_confirm_pin {
382 pkey.__bindgen_anon_1.numcmp_accept =
383 callback(passkey.params.numcmp) as _;
384 } else {
385 ::log::warn!("on_passkey_request is not setted");
386 }
387 let rc = unsafe {
388 esp_idf_sys::ble_sm_inject_io(passkey.conn_handle, &mut pkey)
389 };
390 ::log::debug!("BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: {rc}");
391 }
392 esp_idf_sys::BLE_SM_IOACT_INPUT => {
393 if let Some(callback) = &client.state.on_passkey_request {
394 pkey.__bindgen_anon_1.passkey = callback();
395 } else {
396 ::log::warn!("on_passkey_request is not setted");
397 }
398 let rc = unsafe {
399 esp_idf_sys::ble_sm_inject_io(passkey.conn_handle, &mut pkey)
400 };
401 ::log::debug!("BLE_SM_IOACT_INPUT; ble_sm_inject_io result: {rc}");
402 }
403 esp_idf_sys::BLE_SM_IOACT_NONE => {
404 ::log::debug!("BLE_SM_IOACT_NONE; No passkey action required");
405 }
406 action => {
407 todo!("implementation required: {}", action);
408 }
409 }
410 }
411 _ => {
412 ::log::warn!("unhandled event: {}", event.type_);
413 }
414 }
415 0
416 }
417
418 extern "C" fn service_discovered_cb(
419 conn_handle: u16,
420 error: *const esp_idf_sys::ble_gatt_error,
421 service: *const esp_idf_sys::ble_gatt_svc,
422 arg: *mut c_void,
423 ) -> i32 {
424 let client = unsafe { voidp_to_ref::<Self>(arg) };
425 if client.state.conn_handle != conn_handle {
426 return 0;
427 }
428
429 let error = unsafe { &*error };
430
431 if error.status == 0 {
432 let service = unsafe { &*service };
433 let service = BLERemoteService::new(ArcUnsafeCell::downgrade(&client.state), service);
435 client.state.services.as_mut().unwrap().push(service);
436 return 0;
437 }
438
439 let ret = if error.status == (esp_idf_sys::BLE_HS_EDONE as _) {
440 0
441 } else {
442 error.status.into()
443 };
444
445 client.state.signal.signal(ret);
446 ret as _
447 }
448}
449
450impl Drop for BLEClient {
451 fn drop(&mut self) {
452 if self.connected() {
453 unsafe {
454 esp_idf_sys::ble_gap_set_event_cb(self.conn_handle(), None, ptr::null_mut());
455 }
456 }
457 }
458}