esp32_nimble/server/
ble_advertising.rs

1use core::ffi::{c_int, c_void};
2use esp_idf_svc::sys as esp_idf_sys;
3
4use crate::{
5    BLEAdvertisementData, BLEError, BLEServer, ble,
6    enums::*,
7    utilities::{as_void_ptr, voidp_to_ref},
8};
9use alloc::boxed::Box;
10use once_cell::sync::Lazy;
11
12const BLE_HS_ADV_MAX_SZ: usize = esp_idf_sys::BLE_HS_ADV_MAX_SZ as usize;
13
14// Copied from ble_hs.h, for some reason esp_idf_sys didn't pick this up.
15const BLE_HS_FOREVER: i32 = i32::MAX;
16
17pub struct BLEAdvertising {
18    adv_params: esp_idf_sys::ble_gap_adv_params,
19    scan_response: bool,
20    on_complete: Option<Box<dyn FnMut(c_int) + Send + Sync>>,
21}
22
23impl BLEAdvertising {
24    #[allow(dead_code)]
25    pub(crate) fn new() -> Self {
26        let mut ret = Self {
27            adv_params: esp_idf_sys::ble_gap_adv_params::default(),
28            scan_response: true,
29            on_complete: None,
30        };
31
32        ret.reset().unwrap();
33        ret
34    }
35
36    pub fn reset(&mut self) -> Result<(), BLEError> {
37        if self.is_advertising() {
38            self.stop()?;
39        }
40
41        self.adv_params.conn_mode = esp_idf_sys::BLE_GAP_CONN_MODE_UND as _;
42        self.adv_params.disc_mode = esp_idf_sys::BLE_GAP_DISC_MODE_GEN as _;
43        self.scan_response = true;
44
45        Ok(())
46    }
47
48    pub fn set_data(&mut self, data: &mut BLEAdvertisementData) -> Result<(), BLEError> {
49        if self.adv_params.conn_mode == (ConnMode::Non as _) && !self.scan_response {
50            data.flags = 0;
51        } else {
52            data.flags =
53                (esp_idf_sys::BLE_HS_ADV_F_DISC_GEN | esp_idf_sys::BLE_HS_ADV_F_BREDR_UNSUP) as _;
54        }
55
56        let mut adv_data = data.as_ble_hs_adv_fields();
57        let mut scan_data = esp_idf_sys::ble_hs_adv_fields::default();
58
59        let mut payload_len = data.payload_len();
60
61        if payload_len > BLE_HS_ADV_MAX_SZ {
62            if self.scan_response {
63                scan_data.name = adv_data.name;
64                scan_data.name_len = adv_data.name_len;
65                if scan_data.name_len > (BLE_HS_ADV_MAX_SZ - 2) as _ {
66                    scan_data.name_len = (BLE_HS_ADV_MAX_SZ - 2) as _;
67                    scan_data.set_name_is_complete(0);
68                } else {
69                    scan_data.set_name_is_complete(1);
70                }
71
72                adv_data.name = core::ptr::null();
73                adv_data.name_len = 0;
74                adv_data.set_name_is_complete(0);
75            } else {
76                if adv_data.tx_pwr_lvl_is_present() > 0 {
77                    adv_data.set_tx_pwr_lvl_is_present(0);
78                    payload_len -= 2 + 1;
79                }
80
81                if payload_len > BLE_HS_ADV_MAX_SZ {
82                    adv_data.name_len -= (payload_len - BLE_HS_ADV_MAX_SZ) as u8;
83                    adv_data.set_name_is_complete(0);
84                }
85            }
86        }
87
88        unsafe {
89            if self.scan_response {
90                ble!(esp_idf_sys::ble_gap_adv_rsp_set_fields(&scan_data))?;
91            }
92
93            ble!(esp_idf_sys::ble_gap_adv_set_fields(&adv_data))
94        }
95    }
96
97    pub fn set_raw_data(&mut self, data: &[u8]) -> Result<(), BLEError> {
98        let rc =
99            unsafe { esp_idf_sys::ble_gap_adv_set_data(data.as_ptr(), data.len() as i32) } as u32;
100
101        // convert BLE_ERR_INV_HCI_CMD_PARMS to BLE_HS_EINVAL
102        // https://github.com/taks/esp32-nimble/issues/104
103        // https://github.com/apache/mynewt-nimble/issues/1717
104        ble!(match rc {
105            esp_idf_sys::ble_error_codes_BLE_ERR_INV_HCI_CMD_PARMS => esp_idf_sys::BLE_HS_EINVAL,
106            rc => rc,
107        })
108    }
109
110    pub fn set_raw_scan_response_data(&mut self, data: &[u8]) -> Result<(), BLEError> {
111        unsafe {
112            ble!(esp_idf_sys::ble_gap_adv_rsp_set_data(
113                data.as_ptr(),
114                data.len() as i32
115            ))
116        }
117    }
118
119    /// Set the type of advertisment to use.
120    pub fn advertisement_type(&mut self, adv_type: ConnMode) -> &mut Self {
121        self.adv_params.conn_mode = adv_type as _;
122        self
123    }
124
125    /// Set discoverable mode.
126    pub fn disc_mode(&mut self, mode: DiscMode) -> &mut Self {
127        self.adv_params.disc_mode = mode as _;
128        self
129    }
130
131    /// Set the duty cycle for advertisement_type.
132    ///
133    /// Valid only if advertisement_type is directed-connectable.
134    pub fn high_duty_cycle(&mut self, val: bool) -> &mut Self {
135        self.adv_params.set_high_duty_cycle(val as _);
136        self
137    }
138
139    /// Set the minimum advertising interval.
140    ///
141    /// * `interval`: advertising interval in 0.625ms units, 0 = use default.
142    pub fn min_interval(&mut self, interval: u16) -> &mut Self {
143        self.adv_params.itvl_min = interval;
144        self
145    }
146
147    /// Set the maximum advertising interval.
148    ///
149    /// * `interval`: advertising interval in 0.625ms units, 0 = use default.
150    pub fn max_interval(&mut self, interval: u16) -> &mut Self {
151        self.adv_params.itvl_max = interval;
152        self
153    }
154
155    /// Set if scan response is available.
156    pub fn scan_response(&mut self, value: bool) -> &mut Self {
157        self.scan_response = value;
158        self
159    }
160
161    /// Set the filtering for the scan filter.
162    pub fn filter_policy(&mut self, value: AdvFilterPolicy) -> &mut Self {
163        self.adv_params.filter_policy = value.into();
164        self
165    }
166
167    /// Start advertising.
168    /// Advertising not stop until it is manually stopped.
169    pub fn start(&mut self) -> Result<(), BLEError> {
170        self.start_with_duration(BLE_HS_FOREVER)
171    }
172
173    /// Start advertising.
174    pub fn start_with_duration(&mut self, duration_ms: i32) -> Result<(), BLEError> {
175        let mut server = unsafe { Lazy::get_mut(&mut crate::ble_device::BLE_SERVER) };
176        if let Some(server) = server.as_mut()
177            && !server.started
178        {
179            server.start()?;
180        }
181
182        if self.adv_params.conn_mode == (ConnMode::Non as _) && !self.scan_response {
183            self.adv_params.disc_mode = esp_idf_sys::BLE_GAP_DISC_MODE_NON as _;
184        } else {
185            self.adv_params.disc_mode = esp_idf_sys::BLE_GAP_DISC_MODE_GEN as _;
186        }
187
188        let handle_gap_event = if server.is_some() {
189            BLEServer::handle_gap_event
190        } else {
191            Self::handle_gap_event
192        };
193        unsafe {
194            ble!(esp_idf_sys::ble_gap_adv_start(
195                crate::ble_device::OWN_ADDR_TYPE as _,
196                core::ptr::null(),
197                duration_ms,
198                &self.adv_params,
199                Some(handle_gap_event),
200                as_void_ptr(self),
201            ))?;
202        }
203
204        Ok(())
205    }
206
207    pub fn stop(&self) -> Result<(), BLEError> {
208        unsafe { ble!(esp_idf_sys::ble_gap_adv_stop()) }
209    }
210
211    pub fn is_advertising(&self) -> bool {
212        unsafe { esp_idf_sys::ble_gap_adv_active() != 0 }
213    }
214
215    pub fn on_complete(
216        &mut self,
217        callback: impl FnMut(c_int) + Send + Sync + 'static,
218    ) -> &mut Self {
219        self.on_complete = Some(Box::new(callback));
220        self
221    }
222
223    pub(crate) extern "C" fn handle_gap_event(
224        event: *mut esp_idf_sys::ble_gap_event,
225        arg: *mut c_void,
226    ) -> i32 {
227        let event = unsafe { &*event };
228        let adv = unsafe { voidp_to_ref::<Self>(arg) };
229
230        if event.type_ == esp_idf_sys::BLE_GAP_EVENT_ADV_COMPLETE as _
231            && let Some(callback) = adv.on_complete.as_mut()
232        {
233            callback(unsafe { event.__bindgen_anon_1.adv_complete.reason });
234        }
235
236        0
237    }
238}
239
240unsafe impl Send for BLEAdvertising {}