use alloc::{vec, vec::Vec};
use core::ffi::c_void;
use esp_idf_svc::sys as esp_idf_sys;
use once_cell::sync::Lazy;
use crate::{
BLEAddress, BLEError, BLEServer, ble,
enums::*,
utilities::{BleUuid, OsMBuf, as_void_ptr, voidp_to_ref},
};
#[cfg(not(esp_idf_soc_esp_nimble_controller))]
use esp_idf_sys::os_msys_get_pkthdr;
#[cfg(esp_idf_soc_esp_nimble_controller)]
use esp_idf_sys::r_os_msys_get_pkthdr as os_msys_get_pkthdr;
pub struct BLEExtAdvertisement {
payload: Vec<u8>,
params: esp_idf_sys::ble_gap_ext_adv_params,
adv_address: Option<BLEAddress>,
}
impl BLEExtAdvertisement {
pub fn new(primary_phy: PrimPhy, secondary_phy: SecPhy) -> Self {
Self {
payload: Vec::new(),
params: esp_idf_sys::ble_gap_ext_adv_params {
own_addr_type: unsafe { crate::ble_device::OWN_ADDR_TYPE as _ },
primary_phy: primary_phy.into(),
secondary_phy: secondary_phy.into(),
tx_power: 127,
..Default::default()
},
adv_address: None,
}
}
pub fn legacy_advertising(&mut self, val: bool) {
self.params.set_legacy_pdu(val as _);
}
pub fn scannable(&mut self, val: bool) {
self.params.set_scannable(val as _);
}
pub fn tx_power(&mut self, dbm: i8) {
self.params.tx_power = dbm;
}
pub fn connectable(&mut self, val: bool) {
self.params.set_connectable(val as _);
}
pub fn address(&mut self, addr: &BLEAddress) {
self.adv_address = Some(*addr);
self.params.own_addr_type = esp_idf_sys::BLE_OWN_ADDR_RANDOM as _;
}
pub fn primary_channels(&mut self, ch37: bool, ch38: bool, ch39: bool) {
self.params.channel_map = (ch37 as u8) | ((ch38 as u8) << 1) | ((ch39 as u8) << 2);
}
pub fn min_interval(&mut self, val: u32) {
self.params.itvl_min = val;
}
pub fn max_interval(&mut self, val: u32) {
self.params.itvl_max = val;
}
pub fn enable_scan_request_callback(&mut self, val: bool) {
self.params.set_scan_req_notif(val as _);
}
pub fn filter_policy(&mut self, value: AdvFilterPolicy) -> &mut Self {
self.params.filter_policy = value.into();
self
}
pub fn clear(&mut self) {
self.payload.clear();
}
pub fn size(&self) -> usize {
self.payload.len()
}
pub fn appearance(&mut self, appearance: u16) {
self.add_data(
esp_idf_sys::BLE_HS_ADV_TYPE_APPEARANCE as _,
&appearance.to_le_bytes(),
);
}
pub fn manufacturer_data(&mut self, data: &[u8]) {
self.add_data(esp_idf_sys::BLE_HS_ADV_TYPE_MFG_DATA as _, data);
}
pub fn name(&mut self, name: &str) {
self.add_data(esp_idf_sys::BLE_HS_ADV_TYPE_COMP_NAME as _, name.as_bytes());
}
pub fn complete_service(&mut self, uuid: &BleUuid) {
let bit_size = match uuid {
BleUuid::Uuid16(_) => 16,
BleUuid::Uuid32(_) => 32,
BleUuid::Uuid128(_) => 128,
};
self.set_services(true, bit_size, &[*uuid]);
}
fn set_services(&mut self, complete: bool, size: u8, uuids: &[BleUuid]) {
let data_type: u8 = match size {
16 => {
if complete {
esp_idf_sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID16 as _
} else {
esp_idf_sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS16 as _
}
}
32 => {
if complete {
esp_idf_sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID32 as _
} else {
esp_idf_sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS32 as _
}
}
128 => {
if complete {
esp_idf_sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID128 as _
} else {
esp_idf_sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS128 as _
}
}
_ => return,
};
self.payload.push((size / 8) * (uuids.len() as u8) + 1);
self.payload.push(data_type);
for uuid in uuids {
match uuid {
BleUuid::Uuid16(uuid) => {
self.payload.extend_from_slice(&uuid.to_ne_bytes());
}
BleUuid::Uuid32(uuid) => {
self.payload.extend_from_slice(&uuid.to_ne_bytes());
}
BleUuid::Uuid128(uuid) => {
self.payload.extend_from_slice(uuid);
}
}
}
}
pub fn service_data(&mut self, uuid: BleUuid, data: &[u8]) {
match uuid {
BleUuid::Uuid16(uuid) => {
self.add_data2(
esp_idf_sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID16 as _,
&uuid.to_ne_bytes(),
data,
);
}
BleUuid::Uuid32(uuid) => {
self.add_data2(
esp_idf_sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID32 as _,
&uuid.to_ne_bytes(),
data,
);
}
BleUuid::Uuid128(uuid) => {
self.add_data2(
esp_idf_sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID128 as _,
&uuid,
data,
);
}
}
}
fn add_data(&mut self, data_type: u8, data: &[u8]) {
self.payload.push((data.len() as u8) + 1);
self.payload.push(data_type);
self.payload.extend_from_slice(data);
}
fn add_data2(&mut self, data_type: u8, data0: &[u8], data1: &[u8]) {
self.payload.push((data0.len() + data1.len() + 1) as u8);
self.payload.push(data_type);
self.payload.extend_from_slice(data0);
self.payload.extend_from_slice(data1);
}
}
pub struct BLEExtAdvertising {
adv_status: Vec<bool>,
}
impl BLEExtAdvertising {
#[allow(dead_code)]
pub(crate) fn new() -> Self {
Self {
adv_status: vec![false; (esp_idf_sys::CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES + 1) as _],
}
}
pub fn set_instance_data(
&mut self,
inst_id: u8,
adv: &mut BLEExtAdvertisement,
) -> Result<(), BLEError> {
adv.params.sid = inst_id;
if adv.params.legacy_pdu() != 0 && adv.params.connectable() != 0 {
adv.params.set_scannable(1);
}
if adv.params.connectable() != 0 || adv.params.scannable() == 0 {
adv.params.set_scan_req_notif(0);
}
let mut server = unsafe { Lazy::get_mut(&mut crate::ble_device::BLE_SERVER) };
if let Some(server) = server.as_mut()
&& !server.started
{
server.start()?;
}
let handle_gap_event = if server.is_some() {
BLEServer::handle_gap_event
} else {
Self::handle_gap_event
};
unsafe {
ble!(esp_idf_sys::ble_gap_ext_adv_configure(
inst_id,
&adv.params,
core::ptr::null_mut(),
Some(handle_gap_event),
as_void_ptr(self),
))?;
let mut buf = OsMBuf(os_msys_get_pkthdr(adv.payload.len() as _, 0));
if buf.0.is_null() {
return BLEError::fail();
}
let rc = buf.append(&adv.payload);
assert_eq!(rc, 0);
if (adv.params.scannable() != 0) && (adv.params.legacy_pdu() == 0) {
ble!(esp_idf_sys::ble_gap_ext_adv_rsp_set_data(inst_id, buf.0))?;
} else {
ble!(esp_idf_sys::ble_gap_ext_adv_set_data(inst_id, buf.0))?;
}
if let Some(addr) = adv.adv_address {
ble!(esp_idf_sys::ble_gap_ext_adv_set_addr(inst_id, &addr.value))?;
}
}
Ok(())
}
pub fn set_scan_response_data(
&mut self,
inst_id: u8,
lsr: &BLEExtAdvertisement,
) -> Result<(), BLEError> {
unsafe {
let mut buf = OsMBuf(os_msys_get_pkthdr(lsr.payload.len() as _, 0));
if buf.0.is_null() {
return BLEError::fail();
}
let rc = buf.append(&lsr.payload);
assert_eq!(rc, 0);
ble!(esp_idf_sys::ble_gap_ext_adv_rsp_set_data(inst_id, buf.0))
}
}
pub fn start(&mut self, inst_id: u8) -> Result<(), BLEError> {
self.start_with_duration(inst_id, 0, 0)
}
pub fn start_with_duration(
&mut self,
inst_id: u8,
duration: i32,
max_event: i32,
) -> Result<(), BLEError> {
unsafe {
ble!(esp_idf_sys::ble_gap_ext_adv_start(
inst_id, duration, max_event
))
}
}
pub(crate) extern "C" fn handle_gap_event(
event: *mut esp_idf_sys::ble_gap_event,
arg: *mut c_void,
) -> i32 {
let event = unsafe { &*event };
let adv = unsafe { voidp_to_ref::<Self>(arg) };
match event.type_ as _ {
esp_idf_sys::BLE_GAP_EVENT_ADV_COMPLETE => {
let adv_complete = unsafe { event.__bindgen_anon_1.adv_complete };
adv.adv_status[adv_complete.instance as usize] = false;
}
_ => {}
}
0
}
}