use procmacros::BuilderLite;
use super::*;
use crate::{
ble::InvalidConfigError,
hal::{interrupt, peripherals::BT},
interrupt_dispatch::Handler,
sys::include::esp_bt_controller_config_t,
};
static ISR_INTERRUPT_4: Handler = Handler::new();
static ISR_INTERRUPT_7: Handler = Handler::new();
#[derive(Default, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TxPower {
N24,
N21,
N18,
N15,
N12,
N9,
N6,
N3,
N0,
P3,
P6,
#[default]
P9,
P12,
P15,
P18,
P20,
}
#[allow(dead_code)]
impl TxPower {
fn idx(self) -> esp_power_level_t {
match self {
Self::N24 => esp_power_level_t_ESP_PWR_LVL_N24,
Self::N21 => esp_power_level_t_ESP_PWR_LVL_N21,
Self::N18 => esp_power_level_t_ESP_PWR_LVL_N18,
Self::N15 => esp_power_level_t_ESP_PWR_LVL_N15,
Self::N12 => esp_power_level_t_ESP_PWR_LVL_N12,
Self::N9 => esp_power_level_t_ESP_PWR_LVL_N9,
Self::N6 => esp_power_level_t_ESP_PWR_LVL_N6,
Self::N3 => esp_power_level_t_ESP_PWR_LVL_N3,
Self::N0 => esp_power_level_t_ESP_PWR_LVL_N0,
Self::P3 => esp_power_level_t_ESP_PWR_LVL_P3,
Self::P6 => esp_power_level_t_ESP_PWR_LVL_P6,
Self::P9 => esp_power_level_t_ESP_PWR_LVL_P9,
Self::P12 => esp_power_level_t_ESP_PWR_LVL_P12,
Self::P15 => esp_power_level_t_ESP_PWR_LVL_P15,
Self::P18 => esp_power_level_t_ESP_PWR_LVL_P18,
Self::P20 => esp_power_level_t_ESP_PWR_LVL_P20,
}
}
fn dbm(self) -> i8 {
match self {
Self::N24 => -24,
Self::N21 => -21,
Self::N18 => -18,
Self::N15 => -15,
Self::N12 => -12,
Self::N9 => -9,
Self::N6 => -6,
Self::N3 => -3,
Self::N0 => 0,
Self::P3 => 3,
Self::P6 => 6,
Self::P9 => 9,
Self::P12 => 12,
Self::P15 => 15,
Self::P18 => 18,
Self::P20 => 20,
}
}
}
#[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: u16,
qa_test_mode: bool,
bqb_test: bool,
ll_resolv_list_size: u16,
ll_sync_list_cnt: u8,
ll_sync_cnt: u8,
ll_rsp_dup_list_count: u16,
ll_adv_dup_list_count: u16,
default_tx_power: TxPower,
hci_high_buffer_count: u16,
hci_low_buffer_count: u16,
whitelist_size: u8,
acl_buf_size: u16,
acl_buf_count: u16,
hci_evt_buf_size: u16,
multi_adv_instances: u16,
ext_adv_max_size: u16,
dis_scan_backoff: bool,
scan_backoff_max: u16,
verify_access_address: bool,
cca: bool,
cca_threshold: u8,
disconnect_llcp_conn_update: bool,
disconnect_llcp_chan_map_update: bool,
disconnect_llcp_phy_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: CONFIG_BT_LE_CONTROLLER_TASK_STACK_SIZE as _,
max_connections: CONFIG_BT_LE_MAX_CONNECTIONS as _,
qa_test_mode: false,
bqb_test: false,
ll_resolv_list_size: CONFIG_BT_LE_LL_RESOLV_LIST_SIZE as _,
ll_sync_list_cnt: CONFIG_BT_LE_MAX_PERIODIC_ADVERTISER_LIST as _,
ll_sync_cnt: CONFIG_BT_LE_MAX_PERIODIC_SYNCS as _,
ll_rsp_dup_list_count: CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT as _,
ll_adv_dup_list_count: CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT as _,
default_tx_power: TxPower::default(),
hci_high_buffer_count: CONFIG_BT_LE_HCI_EVT_HI_BUF_COUNT as _,
hci_low_buffer_count: CONFIG_BT_LE_HCI_EVT_LO_BUF_COUNT as _,
whitelist_size: CONFIG_BT_LE_WHITELIST_SIZE as _,
acl_buf_size: CONFIG_BT_LE_ACL_BUF_SIZE as _,
acl_buf_count: CONFIG_BT_LE_ACL_BUF_COUNT as _,
hci_evt_buf_size: CONFIG_BT_LE_HCI_EVT_BUF_SIZE as _,
multi_adv_instances: CONFIG_BT_LE_MAX_EXT_ADV_INSTANCES as _,
ext_adv_max_size: CONFIG_BT_LE_EXT_ADV_MAX_SIZE as _,
dis_scan_backoff: false,
scan_backoff_max: CONFIG_BT_CTRL_SCAN_BACKOFF_UPPERLIMITMAX as _,
verify_access_address: false,
disconnect_llcp_conn_update: false,
disconnect_llcp_chan_map_update: false,
disconnect_llcp_phy_update: false,
cca_threshold: 65,
cca: 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, 2);
crate::ble::validate_range!(self, ll_sync_cnt, 0, 3);
crate::ble::validate_range!(self, ll_sync_list_cnt, 1, 5);
crate::ble::validate_range!(self, ll_rsp_dup_list_count, 1, 100);
crate::ble::validate_range!(self, ll_adv_dup_list_count, 1, 100);
crate::ble::validate_range!(self, whitelist_size, 1, 31);
crate::ble::validate_range!(self, multi_adv_instances, 0, 4);
crate::ble::validate_range!(self, ext_adv_max_size, 0, 1650);
crate::ble::validate_range!(self, scan_backoff_max, 1, 256);
crate::ble::validate_range!(self, cca_threshold, 20, 100);
Ok(())
}
}
pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t {
let main_xtal_freq = esp_hal::clock::xtal_clock().as_mhz() as u8;
let rtc_freq = if main_xtal_freq == 26 { 40000 } else { 32000 };
esp_bt_controller_config_t {
config_version: CONFIG_VERSION,
ble_ll_resolv_list_size: config.ll_resolv_list_size,
ble_hci_evt_hi_buf_count: config.hci_high_buffer_count,
ble_hci_evt_lo_buf_count: config.hci_low_buffer_count,
ble_ll_sync_list_cnt: config.ll_sync_list_cnt,
ble_ll_sync_cnt: config.ll_sync_cnt,
ble_ll_rsp_dup_list_count: config.ll_rsp_dup_list_count,
ble_ll_adv_dup_list_count: config.ll_adv_dup_list_count,
ble_ll_tx_pwr_dbm: config.default_tx_power.dbm() as u8,
rtc_freq,
ble_ll_sca: CONFIG_BT_LE_LL_SCA as _,
ble_ll_scan_phy_number: if CONFIG_BT_LE_LL_CFG_FEAT_LE_CODED_PHY != 0 {
2
} else {
1
},
ble_ll_conn_def_auth_pyld_tmo: 3000,
ble_ll_jitter_usecs: 16,
ble_ll_sched_max_adv_pdu_usecs: 376,
ble_ll_sched_direct_adv_max_usecs: 502,
ble_ll_sched_adv_max_usecs: 852,
ble_scan_rsp_data_max_len: 31,
ble_ll_cfg_num_hci_cmd_pkts: 1,
ble_ll_ctrl_proc_timeout_ms: 40000,
nimble_max_connections: config.max_connections,
ble_whitelist_size: config.whitelist_size,
ble_acl_buf_size: config.acl_buf_size,
ble_acl_buf_count: config.acl_buf_count,
ble_hci_evt_buf_size: config.hci_evt_buf_size,
ble_multi_adv_instances: config.multi_adv_instances,
ble_ext_adv_max_size: config.ext_adv_max_size,
controller_task_stack_size: config.task_stack_size,
controller_task_prio: config.task_priority,
controller_run_cpu: 0,
enable_qa_test: config.qa_test_mode as u8,
enable_bqb_test: config.bqb_test as u8,
enable_uart_hci: 0,
ble_hci_uart_port: 0,
ble_hci_uart_baud: 0,
ble_hci_uart_data_bits: 0,
ble_hci_uart_stop_bits: 0,
ble_hci_uart_flow_ctrl: 0,
ble_hci_uart_uart_parity: 0,
enable_tx_cca: config.cca as u8,
cca_rssi_thresh: (256 - config.cca_threshold as u32) as u8,
sleep_en: 0, coex_phy_coded_tx_rx_time_limit: CONFIG_BT_LE_COEX_PHY_CODED_TX_RX_TLIM_EFF as _,
dis_scan_backoff: config.dis_scan_backoff as u8,
ble_scan_classify_filter_enable: 0,
cca_drop_mode: 0, cca_low_tx_pwr: 0, main_xtal_freq,
version_num: crate::hal::efuse::minor_chip_version(),
ignore_wl_for_direct_adv: 0,
csa2_select: CONFIG_BT_LE_50_FEATURE_SUPPORT as _,
ble_aa_check: config.verify_access_address as u8,
ble_llcp_disc_flag: config.disconnect_llcp_conn_update as u8
| ((config.disconnect_llcp_chan_map_update as u8) << 1)
| ((config.disconnect_llcp_phy_update as u8) << 2),
scan_backoff_upperlimitmax: config.scan_backoff_max,
vhci_enabled: CONFIG_BT_LE_HCI_INTERFACE_USE_RAM as _,
config_magic: CONFIG_MAGIC,
}
}
pub(crate) fn bt_periph_module_enable() {
}
pub(crate) fn disable_sleep_mode() {
}
pub(super) unsafe extern "C" fn esp_intr_alloc(
source: u32,
flags: u32,
handler: *mut c_void,
arg: *mut c_void,
ret_handle: *mut *mut c_void,
) -> i32 {
trace!(
"esp_intr_alloc {} {} {:?} {:?} {:?}",
source, flags, handler, arg, ret_handle
);
match source {
4 => unsafe {
ISR_INTERRUPT_4.set(handler, arg);
BT::steal().enable_mac_interrupt(interrupt::Priority::Priority1);
},
7 => unsafe {
ISR_INTERRUPT_7.set(handler, arg);
BT::steal().enable_lp_timer_interrupt(interrupt::Priority::Priority1);
},
_ => panic!("Unexpected interrupt source {}", source),
}
0
}
pub(super) fn ble_rtc_clk_init() {
crate::radio_clocks::clocks_ll::ble_rtc_clk_init();
}
pub(super) unsafe extern "C" fn esp_reset_rpa_moudle() {
trace!("esp_reset_rpa_moudle");
crate::radio_clocks::clocks_ll::reset_rpa();
}
#[unsafe(no_mangle)]
unsafe fn g_ble_lll_rfmgmt_env_p() -> *mut c_void {
unreachable!()
}
pub(crate) fn shutdown_ble_isr() {
unsafe {
BT::steal().disable_lp_timer_interrupt_on_all_cores();
BT::steal().disable_mac_interrupt_on_all_cores();
}
}
#[unsafe(no_mangle)]
#[crate::hal::ram]
extern "C" fn LP_TIMER() {
ISR_INTERRUPT_7.dispatch();
}
#[unsafe(no_mangle)]
#[crate::hal::ram]
extern "C" fn BT_MAC() {
ISR_INTERRUPT_4.dispatch();
}