use core::ptr::addr_of_mut;
use procmacros::BuilderLite;
use super::*;
use crate::{
ble::InvalidConfigError,
common_adapter::*,
hal::{interrupt::Priority, peripherals::BT},
interrupt_dispatch::Handler,
sys::include::{
esp_bt_controller_config_t,
esp_bt_mode_t,
esp_bt_mode_t_ESP_BT_MODE_BLE,
esp_bt_mode_t_ESP_BT_MODE_BTDM,
esp_bt_mode_t_ESP_BT_MODE_CLASSIC_BT,
esp_bt_mode_t_ESP_BT_MODE_IDLE,
},
};
static ISR_INTERRUPT_5: Handler = Handler::new();
static ISR_INTERRUPT_7: Handler = Handler::new();
static ISR_INTERRUPT_8: Handler = Handler::new();
#[repr(C)]
pub(super) struct osi_funcs_s {
version: u32,
set_isr: Option<unsafe extern "C" fn(i32, unsafe extern "C" fn(), *const ()) -> i32>,
ints_on: Option<unsafe extern "C" fn(u32)>,
interrupt_disable: Option<unsafe extern "C" fn()>,
interrupt_restore: Option<unsafe extern "C" fn()>,
task_yield: Option<unsafe extern "C" fn()>,
task_yield_from_isr: Option<unsafe extern "C" fn()>,
semphr_create: Option<unsafe extern "C" fn(u32, u32) -> *mut c_void>,
semphr_delete: Option<unsafe extern "C" fn(*mut c_void)>,
semphr_take_from_isr: Option<unsafe extern "C" fn(*mut c_void, *mut bool) -> i32>,
semphr_give_from_isr: Option<unsafe extern "C" fn(*mut c_void, *mut bool) -> i32>,
semphr_take: Option<unsafe extern "C" fn(*mut c_void, u32) -> i32>,
semphr_give: Option<unsafe extern "C" fn(*mut c_void) -> i32>,
mutex_create: Option<unsafe extern "C" fn() -> *const ()>,
mutex_delete: Option<unsafe extern "C" fn(*const ())>,
mutex_lock: Option<unsafe extern "C" fn(*const ()) -> i32>,
mutex_unlock: Option<unsafe extern "C" fn(*const ()) -> i32>,
queue_create: Option<unsafe extern "C" fn(u32, u32) -> *mut c_void>,
queue_delete: Option<unsafe extern "C" fn(*mut c_void)>,
queue_send: Option<unsafe extern "C" fn(*mut c_void, *mut c_void, u32) -> i32>,
queue_send_from_isr: Option<unsafe extern "C" fn(*mut c_void, *mut c_void, *mut c_void) -> i32>,
queue_recv: Option<unsafe extern "C" fn(*mut c_void, *mut c_void, u32) -> i32>,
queue_recv_from_isr: Option<unsafe extern "C" fn(*mut c_void, *mut c_void, *mut c_void) -> i32>,
task_create: Option<
unsafe extern "C" fn(
*mut c_void,
*const c_char,
u32,
*mut c_void,
u32,
*mut c_void,
u32,
) -> i32,
>,
task_delete: Option<unsafe extern "C" fn(*mut ())>,
is_in_isr: Option<unsafe extern "C" fn() -> i32>,
cause_sw_intr_to_core: Option<unsafe extern "C" fn(i32, i32) -> i32>,
malloc: Option<unsafe extern "C" fn(u32) -> *mut c_void>,
malloc_internal: Option<unsafe extern "C" fn(u32) -> *mut c_void>,
free: Option<unsafe extern "C" fn(*mut c_void)>,
read_efuse_mac: Option<unsafe extern "C" fn(*const ()) -> i32>,
srand: Option<unsafe extern "C" fn(u32)>,
rand: Option<unsafe extern "C" fn() -> i32>,
btdm_lpcycles_2_hus: Option<unsafe extern "C" fn(u32, u32) -> u32>,
btdm_hus_2_lpcycles: Option<unsafe extern "C" fn(u32) -> u32>,
btdm_sleep_check_duration: Option<unsafe extern "C" fn(i32) -> i32>,
btdm_sleep_enter_phase1: Option<unsafe extern "C" fn(i32)>,
btdm_sleep_enter_phase2: Option<unsafe extern "C" fn()>,
btdm_sleep_exit_phase1: Option<unsafe extern "C" fn()>,
btdm_sleep_exit_phase2: Option<unsafe extern "C" fn()>,
btdm_sleep_exit_phase3: Option<unsafe extern "C" fn()>,
coex_bt_wakeup_request: Option<unsafe extern "C" fn() -> bool>,
coex_bt_wakeup_request_end: Option<unsafe extern "C" fn()>,
coex_bt_request: Option<unsafe extern "C" fn(u32, u32, u32) -> i32>,
coex_bt_release: Option<unsafe extern "C" fn(u32) -> i32>,
coex_register_bt_cb: Option<unsafe extern "C" fn(unsafe extern "C" fn()) -> i32>,
coex_bb_reset_lock: Option<unsafe extern "C" fn() -> u32>,
coex_bb_reset_unlock: Option<unsafe extern "C" fn(u32)>,
coex_schm_register_btdm_callback: Option<unsafe extern "C" fn(unsafe extern "C" fn()) -> i32>,
coex_schm_status_bit_clear: Option<unsafe extern "C" fn(i32, i32)>,
coex_schm_status_bit_set: Option<unsafe extern "C" fn(i32, i32)>,
coex_schm_interval_get: Option<unsafe extern "C" fn() -> u32>,
coex_schm_curr_period_get: Option<unsafe extern "C" fn() -> u8>,
coex_schm_curr_phase_get: Option<unsafe extern "C" fn() -> *mut c_void>,
coex_wifi_channel_get: Option<unsafe extern "C" fn(*mut u8, *mut u8) -> i32>,
coex_register_wifi_channel_change_callback:
Option<unsafe extern "C" fn(unsafe extern "C" fn()) -> i32>,
set_isr13: Option<unsafe extern "C" fn(i32, unsafe extern "C" fn(), *const ()) -> i32>,
interrupt_l3_disable: Option<unsafe extern "C" fn()>,
interrupt_l3_restore: Option<unsafe extern "C" fn()>,
custom_queue_create: Option<unsafe extern "C" fn(u32, u32) -> *mut c_void>,
coex_version_get: Option<unsafe extern "C" fn(*mut c_uint, *mut c_uint, *mut c_uint) -> c_int>,
patch_apply: Option<unsafe extern "C" fn()>,
magic: u32,
}
pub(super) static G_OSI_FUNCS: osi_funcs_s = osi_funcs_s {
version: 0x00010005,
set_isr: Some(set_isr),
ints_on: Some(ints_on),
interrupt_disable: Some(interrupt_disable),
interrupt_restore: Some(interrupt_enable),
task_yield: Some(task_yield),
task_yield_from_isr: Some(task_yield_from_isr),
semphr_create: Some(semphr_create),
semphr_delete: Some(semphr_delete),
semphr_take_from_isr: Some(semphr_take_from_isr),
semphr_give_from_isr: Some(semphr_give_from_isr),
semphr_take: Some(semphr_take),
semphr_give: Some(semphr_give),
mutex_create: Some(mutex_create),
mutex_delete: Some(mutex_delete),
mutex_lock: Some(mutex_lock),
mutex_unlock: Some(mutex_unlock),
queue_create: Some(queue_create),
queue_delete: Some(queue_delete),
queue_send: Some(queue_send),
queue_send_from_isr: Some(queue_send_from_isr),
queue_recv: Some(queue_recv),
queue_recv_from_isr: Some(queue_recv_from_isr),
task_create: Some(task_create),
task_delete: Some(task_delete),
is_in_isr: Some(is_in_isr),
cause_sw_intr_to_core: Some(cause_sw_intr_to_core),
malloc: Some(crate::ble::malloc),
malloc_internal: Some(crate::ble::malloc_internal),
free: Some(crate::ble::free),
read_efuse_mac: Some(read_efuse_mac),
srand: Some(crate::ble::btdm::srand),
rand: Some(crate::ble::btdm::rand),
btdm_lpcycles_2_hus: Some(btdm_lpcycles_2_hus),
btdm_hus_2_lpcycles: Some(btdm_hus_2_lpcycles),
btdm_sleep_check_duration: Some(btdm_sleep_check_duration),
btdm_sleep_enter_phase1: Some(btdm_sleep_enter_phase1),
btdm_sleep_enter_phase2: Some(btdm_sleep_enter_phase2),
btdm_sleep_exit_phase1: Some(btdm_sleep_exit_phase1),
btdm_sleep_exit_phase2: Some(btdm_sleep_exit_phase2),
btdm_sleep_exit_phase3: Some(btdm_sleep_exit_phase3),
coex_bt_wakeup_request: Some(coex_bt_wakeup_request),
coex_bt_wakeup_request_end: Some(coex_bt_wakeup_request_end),
coex_bt_request: Some(coex_bt_request),
coex_bt_release: Some(coex_bt_release),
coex_register_bt_cb: Some(coex_register_bt_cb),
coex_bb_reset_lock: Some(coex_bb_reset_lock),
coex_bb_reset_unlock: Some(coex_bb_reset_unlock),
coex_schm_register_btdm_callback: Some(coex_schm_register_btdm_callback_wrapper),
coex_schm_status_bit_clear: Some(coex_schm_status_bit_clear),
coex_schm_status_bit_set: Some(coex_schm_status_bit_set),
coex_schm_interval_get: Some(coex_schm_interval_get),
coex_schm_curr_period_get: Some(coex_schm_curr_period_get),
coex_schm_curr_phase_get: Some(coex_schm_curr_phase_get),
coex_wifi_channel_get: Some(coex_wifi_channel_get),
coex_register_wifi_channel_change_callback: Some(coex_register_wifi_channel_change_callback),
set_isr13: Some(set_isr13),
interrupt_l3_disable: Some(interrupt_l3_disable),
interrupt_l3_restore: Some(interrupt_l3_restore),
custom_queue_create: Some(custom_queue_create),
coex_version_get: Some(coex_version_get_wrapper),
patch_apply: Some(patch_apply),
magic: 0xfadebead,
};
extern "C" fn patch_apply() {
trace!("patch apply");
unsafe extern "C" {
fn config_ble_funcs_reset();
fn config_btdm_funcs_reset();
}
unsafe {
config_btdm_funcs_reset();
config_ble_funcs_reset();
}
}
extern "C" fn coex_version_get_wrapper(major: *mut u32, minor: *mut u32, patch: *mut u32) -> i32 {
unsafe {
let mut version = crate::sys::include::coex_version_t {
major: 0,
minor: 0,
patch: 0,
};
if coex_version_get_value(&mut version) == 0 {
info!(
"COEX Version {}.{}.{}",
version.major, version.minor, version.patch
);
major.write_volatile(version.major as u32);
minor.write_volatile(version.minor as u32);
patch.write_volatile(version.patch as u32);
} else {
error!("Unable to get COEX version");
}
}
0
}
#[repr(C)]
struct btdm_dram_available_region_t {
mode: esp_bt_mode_t,
start: u32, end: u32, }
const SOC_MEM_BT_DATA_START: u32 = 0x3ffae6e0;
const SOC_MEM_BT_DATA_END: u32 = 0x3ffaff10;
const SOC_MEM_BT_EM_BTDM0_START: u32 = 0x3ffb0000;
const SOC_MEM_BT_EM_BTDM0_END: u32 = 0x3ffb09a8;
const SOC_MEM_BT_EM_BLE_START: u32 = 0x3ffb09a8;
const SOC_MEM_BT_EM_BLE_END: u32 = 0x3ffb1ddc;
const SOC_MEM_BT_EM_BTDM1_START: u32 = 0x3ffb1ddc;
const SOC_MEM_BT_EM_BTDM1_END: u32 = 0x3ffb2730;
const SOC_MEM_BT_EM_BREDR_START: u32 = 0x3ffb2730;
const SOC_MEM_BT_BSS_START: u32 = 0x3ffb8000;
const SOC_MEM_BT_BSS_END: u32 = 0x3ffb9a20;
const SOC_MEM_BT_MISC_START: u32 = 0x3ffbdb28;
const SOC_MEM_BT_MISC_END: u32 = 0x3ffbdb5c;
const SOC_MEM_BT_EM_BREDR_REAL_END: u32 = 0x3ffb6388;
static BTDM_DRAM_AVAILABLE_REGION: [btdm_dram_available_region_t; 7] = [
btdm_dram_available_region_t {
mode: esp_bt_mode_t_ESP_BT_MODE_BTDM,
start: SOC_MEM_BT_DATA_START,
end: SOC_MEM_BT_DATA_END,
},
btdm_dram_available_region_t {
mode: esp_bt_mode_t_ESP_BT_MODE_BTDM,
start: SOC_MEM_BT_EM_BTDM0_START,
end: SOC_MEM_BT_EM_BTDM0_END,
},
btdm_dram_available_region_t {
mode: esp_bt_mode_t_ESP_BT_MODE_BLE,
start: SOC_MEM_BT_EM_BLE_START,
end: SOC_MEM_BT_EM_BLE_END,
},
btdm_dram_available_region_t {
mode: esp_bt_mode_t_ESP_BT_MODE_BTDM,
start: SOC_MEM_BT_EM_BTDM1_START,
end: SOC_MEM_BT_EM_BTDM1_END,
},
btdm_dram_available_region_t {
mode: esp_bt_mode_t_ESP_BT_MODE_CLASSIC_BT,
start: SOC_MEM_BT_EM_BREDR_START,
end: SOC_MEM_BT_EM_BREDR_REAL_END,
},
btdm_dram_available_region_t {
mode: esp_bt_mode_t_ESP_BT_MODE_BTDM,
start: SOC_MEM_BT_BSS_START,
end: SOC_MEM_BT_BSS_END,
},
btdm_dram_available_region_t {
mode: esp_bt_mode_t_ESP_BT_MODE_BTDM,
start: SOC_MEM_BT_MISC_START,
end: SOC_MEM_BT_MISC_END,
},
];
#[derive(BuilderLite, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Config {
task_priority: u8,
task_stack_size: u16,
max_connections: u8,
scan_duplicate_list_count: u16,
scan_duplicate_refresh_period: u16,
ble_scan_backoff: bool,
enc_key_sz_min: u8,
verify_access_address: bool,
channel_assessment: bool,
ping: bool,
disconnect_llcp_conn_update: bool,
disconnect_llcp_chan_map_update: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
task_priority: crate::preempt::max_task_priority()
.saturating_sub(2)
.min(255) as u8,
task_stack_size: 4096,
max_connections: CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF as _,
scan_duplicate_list_count: CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE as _,
scan_duplicate_refresh_period: SCAN_DUPL_CACHE_REFRESH_PERIOD as _,
ble_scan_backoff: false,
enc_key_sz_min: 7,
verify_access_address: false,
channel_assessment: false,
ping: false,
disconnect_llcp_conn_update: false,
disconnect_llcp_chan_map_update: false,
}
}
}
impl Config {
pub(crate) fn validate(&self) -> Result<(), InvalidConfigError> {
crate::ble::validate_range!(
self,
task_priority,
0,
crate::preempt::max_task_priority().min(255) as u8
);
crate::ble::validate_range!(self, max_connections, 1, 9);
crate::ble::validate_range!(self, scan_duplicate_list_count, 10, 1000);
crate::ble::validate_range!(self, scan_duplicate_refresh_period, 0, 1000);
crate::ble::validate_range!(self, enc_key_sz_min, 7, 16);
Ok(())
}
}
pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t {
esp_bt_controller_config_t {
controller_task_stack_size: config.task_stack_size,
controller_task_prio: config.task_priority,
hci_uart_no: 1,
hci_uart_baudrate: 921600,
scan_duplicate_mode: 0, scan_duplicate_type: 0,
mesh_adv_size: 0,
normal_adv_size: config.scan_duplicate_list_count,
send_adv_reserved_size: SCAN_SEND_ADV_RESERVED_SIZE as _,
controller_debug_flag: BTDM_CTRL_CONTROLLER_DEBUG_FLAG as _,
mode: esp_bt_mode_t_ESP_BT_MODE_BLE as _,
ble_max_conn: config.max_connections,
bt_max_acl_conn: 0,
bt_sco_datapath: 0,
auto_latency: false,
bt_legacy_auth_vs_evt: false,
bt_max_sync_conn: 1,
ble_sca: 1,
pcm_role: 0,
pcm_polar: 0,
hli: false,
dup_list_refresh_period: config.scan_duplicate_refresh_period,
ble_scan_backoff: config.ble_scan_backoff,
pcm_fsyncshp: 0,
enc_key_sz_min: config.enc_key_sz_min,
ble_llcp_disc_flag: config.disconnect_llcp_conn_update as u8
| ((config.disconnect_llcp_chan_map_update as u8) << 1),
ble_aa_check: config.verify_access_address,
ble_chan_ass_en: config.channel_assessment as u8,
ble_ping_en: config.ping as u8,
magic: ESP_BT_CONTROLLER_CONFIG_MAGIC_VAL,
}
}
pub(crate) fn btdm_controller_mem_init() {
unsafe extern "C" {
static mut _data_start_btdm: u32;
static mut _data_end_btdm: u32;
static _data_start_btdm_rom: u32;
}
unsafe {
let data_start = addr_of_mut!(_data_start_btdm).cast::<u8>();
let data_end = addr_of_mut!(_data_end_btdm).cast::<u8>();
let data_start_rom = _data_start_btdm_rom as *const u8;
let len = data_end as usize - data_start as usize;
core::ptr::copy_nonoverlapping(data_start_rom, data_start, len);
debug!(
"btdm_controller_mem_init {:?} {:?} {}",
data_start_rom, data_start, len,
);
}
let btdm_dram_regions = BTDM_DRAM_AVAILABLE_REGION.len();
#[allow(clippy::needless_range_loop)] for i in 1..btdm_dram_regions {
if BTDM_DRAM_AVAILABLE_REGION[i].mode != esp_bt_mode_t_ESP_BT_MODE_IDLE {
unsafe {
core::ptr::write_bytes(
BTDM_DRAM_AVAILABLE_REGION[i].start as *mut u8,
0x0,
(BTDM_DRAM_AVAILABLE_REGION[i].end - BTDM_DRAM_AVAILABLE_REGION[i].start)
as usize,
);
}
debug!(
".bss initialise {:x} - {:x}\n",
BTDM_DRAM_AVAILABLE_REGION[i].start, BTDM_DRAM_AVAILABLE_REGION[i].end
);
}
}
debug!("btdm_controller_mem_init done");
}
pub(crate) fn bt_periph_module_enable() {
}
pub(crate) fn disable_sleep_mode() {
unsafe extern "C" {
fn btdm_controller_set_sleep_mode(mode: u8);
}
const BTDM_MODEM_SLEEP_MODE_NONE: u8 = 0;
unsafe {
btdm_controller_set_sleep_mode(BTDM_MODEM_SLEEP_MODE_NONE);
}
}
pub(crate) unsafe extern "C" fn coex_bt_wakeup_request() -> bool {
trace!("coex_bt_wakeup_request");
true
}
pub(crate) unsafe extern "C" fn coex_bt_wakeup_request_end() {
trace!("coex_bt_wakeup_request_end");
}
extern_coex_fns! {
fn coex_bt_request(event: u32, latency: u32, duration: u32) -> i32;
fn coex_bt_release(event: u32) -> i32;
fn coex_register_bt_cb(callback: unsafe extern "C" fn()) -> i32;
fn coex_bb_reset_lock() -> u32;
fn coex_bb_reset_unlock(event: u32);
fn coex_register_wifi_channel_change_callback(callback: unsafe extern "C" fn()) -> i32;
}
coex_fns! {
fn coex_schm_interval_get() -> u32;
fn coex_schm_curr_period_get() -> u8;
fn coex_schm_curr_phase_get() -> *mut c_void;
}
pub(crate) unsafe extern "C" fn coex_schm_register_btdm_callback_wrapper(
callback: unsafe extern "C" fn(),
) -> i32 {
trace!("coex_schm_register_btdm_callback {:?}", callback);
cfg_if::cfg_if! {
if #[cfg(feature = "coex")] {
unsafe extern "C" {
fn coex_schm_register_callback(kind: u32, callback: unsafe extern "C" fn()) -> i32;
}
const COEX_SCHM_CALLBACK_TYPE_BT: u32 = 1;
unsafe { coex_schm_register_callback(COEX_SCHM_CALLBACK_TYPE_BT, callback) }
} else {
0
}
}
}
#[allow(unused_variables)]
pub(crate) unsafe extern "C" fn coex_wifi_channel_get(
_primary: *mut u8,
_secondary: *mut u8,
) -> i32 {
trace!("coex_wifi_channel_get");
cfg_if::cfg_if! {
if #[cfg(feature = "coex")] {
unsafe extern "C" {
fn coex_wifi_channel_get(_primary: *mut u8, _secondary: *mut u8) -> i32;
}
unsafe { coex_wifi_channel_get(_primary, _secondary) }
} else {
-1
}
}
}
pub(crate) unsafe extern "C" fn set_isr(n: i32, f: unsafe extern "C" fn(), arg: *const ()) -> i32 {
trace!("set_isr called {} {:?} {:?}", n, f, arg);
match n {
5 => unsafe {
ISR_INTERRUPT_5.set(f as *const c_void, arg.cast());
BT::steal().enable_rwbt_interrupt(Priority::Priority1);
BT::steal().enable_rwble_interrupt(Priority::Priority1);
},
7 => ISR_INTERRUPT_7.set(f as *const c_void, arg.cast()),
8 => unsafe {
ISR_INTERRUPT_8.set(f as *const c_void, arg.cast());
BT::steal().enable_bb_interrupt(Priority::Priority1);
},
_ => panic!("set_isr - unsupported interrupt number {}", n),
}
0
}
pub(crate) unsafe extern "C" fn ints_on(mask: u32) {
trace!("chip_ints_on esp32 {:b}", mask);
unsafe {
crate::hal::xtensa_lx::interrupt::enable_mask(mask);
}
}
#[cfg(feature = "coex")]
pub(crate) const BTDM_ASYNC_WAKEUP_REQ_HCI: i32 = 0;
#[cfg(feature = "coex")]
pub(crate) const BTDM_ASYNC_WAKEUP_REQ_COEX: i32 = 1;
#[cfg(feature = "coex")]
pub(crate) fn async_wakeup_request(event: i32) -> bool {
trace!("async_wakeup_request {}", event);
unsafe extern "C" {
fn btdm_in_wakeup_requesting_set(set: bool);
fn btdm_power_state_active() -> bool;
fn btdm_wakeup_request();
}
match event {
e if e == BTDM_ASYNC_WAKEUP_REQ_HCI => {
unsafe {
btdm_in_wakeup_requesting_set(true);
}
false
}
e if e == BTDM_ASYNC_WAKEUP_REQ_COEX => {
unsafe {
btdm_in_wakeup_requesting_set(true);
}
if !unsafe { btdm_power_state_active() } {
unsafe {
btdm_wakeup_request();
}
true
} else {
false
}
}
_ => false,
}
}
#[cfg(feature = "coex")]
pub(crate) fn async_wakeup_request_end(event: i32) {
trace!("async_wakeup_request_end {}", event);
let request_lock = match event {
e if e == BTDM_ASYNC_WAKEUP_REQ_HCI => true,
e if e == BTDM_ASYNC_WAKEUP_REQ_COEX => false,
_ => return,
};
unsafe extern "C" {
fn btdm_in_wakeup_requesting_set(set: bool);
}
if request_lock {
unsafe { btdm_in_wakeup_requesting_set(false) };
}
}
#[allow(non_snake_case)]
#[unsafe(no_mangle)]
#[crate::hal::ram]
fn Software0() {
ISR_INTERRUPT_7.dispatch();
}
#[unsafe(no_mangle)]
#[crate::hal::ram]
extern "C" fn RWBT() {
ISR_INTERRUPT_5.dispatch();
}
#[unsafe(no_mangle)]
#[crate::hal::ram]
extern "C" fn RWBLE() {
ISR_INTERRUPT_5.dispatch();
}
#[unsafe(no_mangle)]
#[crate::hal::ram]
extern "C" fn BT_BB() {
ISR_INTERRUPT_8.dispatch();
}
pub(crate) fn shutdown_ble_isr() {
unsafe {
BT::steal().disable_rwbt_interrupt_on_all_cores();
BT::steal().disable_rwble_interrupt_on_all_cores();
BT::steal().disable_bb_interrupt_on_all_cores();
}
}