diff --git a/components/bt/controller/esp32c3/Kconfig.in b/components/bt/controller/esp32c3/Kconfig.in index fd1a2784e3..187d3886ab 100644 --- a/components/bt/controller/esp32c3/Kconfig.in +++ b/components/bt/controller/esp32c3/Kconfig.in @@ -310,6 +310,21 @@ menu "MODEM SLEEP Options" selected, bluetooth modem sleep can work under Dynamic Frequency Scaling(DFS) enabled, but cannot work when light sleep is enabled. Main crystal has a relatively better performance than other bluetooth low power clock sources. + config BT_CTRL_LPCLK_SEL_EXT_32K_XTAL + bool "External 32kHz crystal" + depends on ESP32C3_RTC_CLK_SRC_EXT_CRYS + help + External 32kHz crystal has a nominal frequency of 32.768kHz and provides good frequency + stability. If used as Bluetooth low power clock, External 32kHz can support Bluetooth + modem sleep to be used with both DFS and light sleep. + + config BT_CTRL_LPCLK_SEL_RTC_SLOW + bool "Internal 150kHz RC oscillator" + depends on ESP32C3_RTC_CLK_SRC_INT_RC + help + Internal 150kHz RC oscillator. + + endchoice endmenu @@ -321,6 +336,8 @@ config BT_CTRL_SLEEP_MODE_EFF config BT_CTRL_SLEEP_CLOCK_EFF int default 1 if BT_CTRL_LPCLK_SEL_MAIN_XTAL + default 2 if BT_CTRL_LPCLK_SEL_EXT_32K_XTAL + default 3 if BT_CTRL_LPCLK_SEL_RTC_SLOW default 0 diff --git a/components/bt/controller/esp32c3/bt.c b/components/bt/controller/esp32c3/bt.c index f274e27455..dc43b3b67c 100644 --- a/components/bt/controller/esp32c3/bt.c +++ b/components/bt/controller/esp32c3/bt.c @@ -44,6 +44,7 @@ #include "esp_coexist_internal.h" #include "esp32c3/rom/rom_layout.h" #include "esp_timer.h" +#include "esp_sleep.h" #if CONFIG_BT_ENABLED @@ -61,12 +62,43 @@ #define BTDM_LPCLK_SEL_RTC_SLOW (2) #define BTDM_LPCLK_SEL_8M (3) +// wakeup request sources +enum { + BTDM_ASYNC_WAKEUP_SRC_VHCI = 0, + BTDM_ASYNC_WAKEUP_SRC_DISA, + BTDM_ASYNC_WAKEUP_SRC_TMR, + BTDM_ASYNC_WAKEUP_SRC_MAX, +}; + +// low power control struct +typedef union { + struct { + uint32_t enable : 1; // whether low power mode is required + uint32_t lpclk_sel : 2; // low power clock source + uint32_t mac_bb_pd : 1; // whether hardware(MAC, BB) force-power-down is required during sleep + uint32_t wakeup_timer_required : 1; // whether system timer is needed + uint32_t no_light_sleep : 1; // do not allow system to enter light sleep after bluetooth is enabled + uint32_t reserved : 26; // reserved + }; + uint32_t val; +} btdm_lpcntl_t; + +// low power control status +typedef union { + struct { + uint32_t pm_lock_released : 1; // whether power management lock is released + uint32_t mac_bb_pd : 1; // whether hardware(MAC, BB) is powered down + uint32_t phy_enabled : 1; // whether phy is switched on + uint32_t wakeup_timer_started : 1; // whether wakeup timer is started + uint32_t reserved : 28; // reserved + }; + uint32_t val; +} btdm_lpstat_t; + /* Sleep and wakeup interval control */ #define BTDM_MIN_SLEEP_DURATION (24) // threshold of interval in half slots to allow to fall into modem sleep #define BTDM_MODEM_WAKE_UP_DELAY (8) // delay in half slots of modem wake up procedure, including re-enable PHY/RF -#define BTDM_MODEM_SLEEP_IN_EFFECT (1) - #define BT_DEBUG(...) #define BT_API_CALL_CHECK(info, api_call, ret) \ do{\ @@ -81,14 +113,17 @@ do{\ #define OSI_VERSION 0x00010006 #define OSI_MAGIC_VALUE 0xFADEBEAD -/* SPIRAM Configuration */ -#if CONFIG_SPIRAM_USE_MALLOC -#define BTDM_MAX_QUEUE_NUM (5) -#endif - /* Types definition ************************************************************************ */ +/* vendor dependent signals to be posted to controller task */ +typedef enum { + BTDM_VND_OL_SIG_WAKEUP_TMR = 0, + BTDM_VND_OL_SIG_NUM, +} btdm_vnd_ol_sig_t; + +/* prototype of function to handle vendor dependent signals */ +typedef void (* btdm_vnd_ol_task_func_t)(void *param); /* VHCI function interface */ typedef struct vhci_host_callback { @@ -103,15 +138,6 @@ typedef struct { intptr_t end; } btdm_dram_available_region_t; -/* PSRAM configuration */ -#if CONFIG_SPIRAM_USE_MALLOC -typedef struct { - QueueHandle_t handle; - void *storage; - void *buffer; -} btdm_queue_item_t; -#endif - typedef void (* osi_intr_handler)(void); static uint8_t own_bda[6]; @@ -192,8 +218,14 @@ extern void btdm_controller_enable_sleep(bool enable); extern uint8_t btdm_controller_get_sleep_mode(void); extern bool btdm_power_state_active(void); extern void btdm_wakeup_request(void); -extern void btdm_wakeup_request_start(void); -extern void btdm_wakeup_request_end(void); +extern void btdm_in_wakeup_requesting_set(bool in_wakeup_requesting); + +/* vendor dependent tasks to be posted and handled by controller task*/ +extern int btdm_vnd_offload_task_register(btdm_vnd_ol_sig_t sig, btdm_vnd_ol_task_func_t func); +extern int btdm_vnd_offload_task_deregister(btdm_vnd_ol_sig_t sig); +extern int btdm_vnd_offload_post_from_isr(btdm_vnd_ol_sig_t sig, void *param, bool need_yield); +extern int btdm_vnd_offload_post(btdm_vnd_ol_sig_t sig, void *param); + /* Low Power Clock */ extern bool btdm_lpclk_select_src(uint32_t sel); extern bool btdm_lpclk_set_div(uint32_t div); @@ -215,7 +247,7 @@ extern void btdm_deep_sleep_mem_deinit(void); extern void btdm_ble_power_down_dma_copy(bool copy); extern uint8_t btdm_sleep_clock_sync(void); -#if CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB +#if CONFIG_MAC_BB_PD extern void esp_mac_bb_power_down(void); extern void esp_mac_bb_power_up(void); extern void ets_backup_dma_copy(uint32_t reg, uint32_t mem_addr, uint32_t num, bool to_mem); @@ -243,10 +275,6 @@ extern char _bt_tmp_bss_end; /* Local Function Declare ********************************************************************* */ -#if CONFIG_SPIRAM_USE_MALLOC -static bool btdm_queue_generic_register(const btdm_queue_item_t *queue); -static bool btdm_queue_generic_deregister(btdm_queue_item_t *queue); -#endif /* CONFIG_SPIRAM_USE_MALLOC */ static void interrupt_set_wrapper(int cpu_no, int intr_source, int intr_num, int intr_prio); static void interrupt_clear_wrapper(int intr_source, int intr_num); static void interrupt_handler_set_wrapper(int n, intr_handler_t fn, void *arg); @@ -281,7 +309,6 @@ static uint32_t IRAM_ATTR btdm_hus_2_lpcycles(uint32_t hus); static bool IRAM_ATTR btdm_sleep_check_duration(int32_t *slot_cnt); static void btdm_sleep_enter_phase1_wrapper(uint32_t lpcycles); static void btdm_sleep_enter_phase2_wrapper(void); -static void IRAM_ATTR btdm_sleep_exit_phase1_wrapper(void); static void btdm_sleep_exit_phase3_wrapper(void); static void coex_wifi_sleep_set_hook(bool sleep); static void coex_schm_status_bit_set_wrapper(uint32_t type, uint32_t status); @@ -291,6 +318,8 @@ static void interrupt_off_wrapper(int intr_num); static void btdm_hw_mac_power_up_wrapper(void); static void btdm_hw_mac_power_down_wrapper(void); static void btdm_backup_dma_copy_wrapper(uint32_t reg, uint32_t mem_addr, uint32_t num, bool to_mem); + +static void btdm_slp_tmr_callback(void *arg); /* Local variable definition *************************************************************************** */ @@ -336,18 +365,13 @@ static const struct osi_funcs_t osi_funcs_ro = { ._btdm_sleep_check_duration = btdm_sleep_check_duration, ._btdm_sleep_enter_phase1 = btdm_sleep_enter_phase1_wrapper, ._btdm_sleep_enter_phase2 = btdm_sleep_enter_phase2_wrapper, - ._btdm_sleep_exit_phase1 = btdm_sleep_exit_phase1_wrapper, + ._btdm_sleep_exit_phase1 = NULL, ._btdm_sleep_exit_phase2 = NULL, ._btdm_sleep_exit_phase3 = btdm_sleep_exit_phase3_wrapper, - //._coex_bt_request = coex_bt_request_wrapper, ._coex_wifi_sleep_set = coex_wifi_sleep_set_hook, ._coex_core_ble_conn_dyn_prio_get = coex_core_ble_conn_dyn_prio_get, ._coex_schm_status_bit_set = coex_schm_status_bit_set_wrapper, ._coex_schm_status_bit_clear = coex_schm_status_bit_clear_wrapper, - //._coex_bt_release = coex_bt_release_wrapper, - // ._coex_register_bt_cb = coex_register_bt_cb_wrapper, - // ._coex_bb_reset_lock = coex_bb_reset_lock_wrapper, - //._coex_bb_reset_unlock = coex_bb_reset_unlock_wrapper, ._interrupt_on = interrupt_on_wrapper, ._interrupt_off = interrupt_off_wrapper, ._esp_hw_power_down = btdm_hw_mac_power_down_wrapper, @@ -357,111 +381,59 @@ static const struct osi_funcs_t osi_funcs_ro = { static DRAM_ATTR struct osi_funcs_t *osi_funcs_p; -#if CONFIG_SPIRAM_USE_MALLOC -static DRAM_ATTR btdm_queue_item_t btdm_queue_table[BTDM_MAX_QUEUE_NUM]; -static DRAM_ATTR SemaphoreHandle_t btdm_queue_table_mux = NULL; -#endif /* #if CONFIG_SPIRAM_USE_MALLOC */ - /* Static variable declare */ -// timestamp when PHY/RF was switched on -// static DRAM_ATTR int64_t s_time_phy_rf_just_enabled = 0; static DRAM_ATTR esp_bt_controller_status_t btdm_controller_status = ESP_BT_CONTROLLER_STATUS_IDLE; static DRAM_ATTR portMUX_TYPE global_int_mux = portMUX_INITIALIZER_UNLOCKED; +// low power control struct +static DRAM_ATTR btdm_lpcntl_t s_lp_cntl; +// low power status struct +static DRAM_ATTR btdm_lpstat_t s_lp_stat; // measured average low power clock period in micro seconds static DRAM_ATTR uint32_t btdm_lpcycle_us = 0; -static DRAM_ATTR uint8_t btdm_lpcycle_us_frac = 0; // number of fractional bit for btdm_lpcycle_us +// number of fractional bit for btdm_lpcycle_us +static DRAM_ATTR uint8_t btdm_lpcycle_us_frac = 0; +// semaphore used for blocking VHCI API to wait for controller to wake up +static DRAM_ATTR QueueHandle_t s_wakeup_req_sem = NULL; +// wakeup timer +static DRAM_ATTR esp_timer_handle_t s_btdm_slp_tmr; #ifdef CONFIG_PM_ENABLE static DRAM_ATTR esp_pm_lock_handle_t s_pm_lock; -static DRAM_ATTR esp_pm_lock_handle_t s_light_sleep_pm_lock; // pm_lock to prevent light sleep due to incompatibility currently -static DRAM_ATTR QueueHandle_t s_pm_lock_sem = NULL; +// pm_lock to prevent light sleep due to incompatibility currently +static DRAM_ATTR esp_pm_lock_handle_t s_light_sleep_pm_lock; #endif -#if (defined CONFIG_PM_ENABLE) || (defined CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) -static DRAM_ATTR esp_timer_handle_t s_btdm_slp_tmr; -static void btdm_slp_tmr_callback(void *arg); -#endif - -#ifdef CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB -static bool sleep_backup_en = false; -static bool sleep_backup_done = false; -#endif - -static inline void btdm_check_and_init_bb(void) +void IRAM_ATTR btdm_hw_mac_power_down_wrapper(void) { - // todo: - // btdm_rf_bb_init_phase2(); -} +#if CONFIG_MAC_BB_PD + // le module power down + SET_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_BT_FORCE_ISO); + SET_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_BT_FORCE_PD); -void btdm_hw_mac_power_down_wrapper(void) -{ -#if CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB esp_mac_bb_power_down(); #endif } -void btdm_hw_mac_power_up_wrapper(void) +void IRAM_ATTR btdm_hw_mac_power_up_wrapper(void) { -#if CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB +#if CONFIG_MAC_BB_PD + // le module power up + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_BT_FORCE_PD); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_BT_FORCE_ISO); + esp_mac_bb_power_up(); #endif } -void btdm_backup_dma_copy_wrapper(uint32_t reg, uint32_t mem_addr, uint32_t num, bool to_mem) +void IRAM_ATTR btdm_backup_dma_copy_wrapper(uint32_t reg, uint32_t mem_addr, uint32_t num, bool to_mem) { -#if CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB +#if CONFIG_MAC_BB_PD ets_backup_dma_copy(reg, mem_addr, num, to_mem); #endif } -#if CONFIG_SPIRAM_USE_MALLOC -static bool btdm_queue_generic_register(const btdm_queue_item_t *queue) -{ - if (!btdm_queue_table_mux || !queue) { - return NULL; - } - - bool ret = false; - btdm_queue_item_t *item; - xSemaphoreTake(btdm_queue_table_mux, portMAX_DELAY); - for (int i = 0; i < BTDM_MAX_QUEUE_NUM; ++i) { - item = &btdm_queue_table[i]; - if (item->handle == NULL) { - memcpy(item, queue, sizeof(btdm_queue_item_t)); - ret = true; - break; - } - } - xSemaphoreGive(btdm_queue_table_mux); - return ret; -} - -static bool btdm_queue_generic_deregister(btdm_queue_item_t *queue) -{ - if (!btdm_queue_table_mux || !queue) { - return false; - } - - bool ret = false; - btdm_queue_item_t *item; - xSemaphoreTake(btdm_queue_table_mux, portMAX_DELAY); - for (int i = 0; i < BTDM_MAX_QUEUE_NUM; ++i) { - item = &btdm_queue_table[i]; - if (item->handle == queue->handle) { - memcpy(queue, item, sizeof(btdm_queue_item_t)); - memset(item, 0, sizeof(btdm_queue_item_t)); - ret = true; - break; - } - } - xSemaphoreGive(btdm_queue_table_mux); - return ret; -} - -#endif /* CONFIG_SPIRAM_USE_MALLOC */ - static void interrupt_set_wrapper(int cpu_no, int intr_source, int intr_num, int intr_prio) { intr_matrix_route(intr_source, intr_num); @@ -514,63 +486,12 @@ static void IRAM_ATTR task_yield_from_isr(void) static void *semphr_create_wrapper(uint32_t max, uint32_t init) { -#if !CONFIG_SPIRAM_USE_MALLOC return (void *)xSemaphoreCreateCounting(max, init); -#else - StaticQueue_t *queue_buffer = NULL; - QueueHandle_t handle = NULL; - - queue_buffer = heap_caps_malloc(sizeof(StaticQueue_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); - if (!queue_buffer) { - goto error; - } - - handle = xSemaphoreCreateCountingStatic(max, init, queue_buffer); - if (!handle) { - goto error; - } - - btdm_queue_item_t item = { - .handle = handle, - .storage = NULL, - .buffer = queue_buffer, - }; - - if (!btdm_queue_generic_register(&item)) { - goto error; - } - return handle; - - error: - if (handle) { - vSemaphoreDelete(handle); - } - if (queue_buffer) { - free(queue_buffer); - } - - return NULL; -#endif } static void semphr_delete_wrapper(void *semphr) { -#if !CONFIG_SPIRAM_USE_MALLOC vSemaphoreDelete(semphr); -#else - btdm_queue_item_t item = { - .handle = semphr, - .storage = NULL, - .buffer = NULL, - }; - - if (btdm_queue_generic_deregister(&item)) { - vSemaphoreDelete(item.handle); - free(item.buffer); - } - - return; -#endif } static int IRAM_ATTR semphr_take_from_isr_wrapper(void *semphr, void *hptw) @@ -599,63 +520,12 @@ static int semphr_give_wrapper(void *semphr) static void *mutex_create_wrapper(void) { -#if CONFIG_SPIRAM_USE_MALLOC - StaticQueue_t *queue_buffer = NULL; - QueueHandle_t handle = NULL; - - queue_buffer = heap_caps_malloc(sizeof(StaticQueue_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); - if (!queue_buffer) { - goto error; - } - - handle = xSemaphoreCreateMutexStatic(queue_buffer); - if (!handle) { - goto error; - } - - btdm_queue_item_t item = { - .handle = handle, - .storage = NULL, - .buffer = queue_buffer, - }; - - if (!btdm_queue_generic_register(&item)) { - goto error; - } - return handle; - - error: - if (handle) { - vSemaphoreDelete(handle); - } - if (queue_buffer) { - free(queue_buffer); - } - - return NULL; -#else return (void *)xSemaphoreCreateMutex(); -#endif } static void mutex_delete_wrapper(void *mutex) { -#if !CONFIG_SPIRAM_USE_MALLOC vSemaphoreDelete(mutex); -#else - btdm_queue_item_t item = { - .handle = mutex, - .storage = NULL, - .buffer = NULL, - }; - - if (btdm_queue_generic_deregister(&item)) { - vSemaphoreDelete(item.handle); - free(item.buffer); - } - - return; -#endif } static int mutex_lock_wrapper(void *mutex) @@ -670,74 +540,12 @@ static int mutex_unlock_wrapper(void *mutex) static void *queue_create_wrapper(uint32_t queue_len, uint32_t item_size) { -#if CONFIG_SPIRAM_USE_MALLOC - StaticQueue_t *queue_buffer = NULL; - uint8_t *queue_storage = NULL; - QueueHandle_t handle = NULL; - - queue_buffer = heap_caps_malloc(sizeof(StaticQueue_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); - if (!queue_buffer) { - goto error; - } - - queue_storage = heap_caps_malloc((queue_len*item_size), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); - if (!queue_storage ) { - goto error; - } - - handle = xQueueCreateStatic(queue_len, item_size, queue_storage, queue_buffer); - if (!handle) { - goto error; - } - - btdm_queue_item_t item = { - .handle = handle, - .storage = queue_storage, - .buffer = queue_buffer, - }; - - if (!btdm_queue_generic_register(&item)) { - goto error; - } - - return handle; - - error: - if (handle) { - vQueueDelete(handle); - } - if (queue_storage) { - free(queue_storage); - } - if (queue_buffer) { - free(queue_buffer); - } - - return NULL; -#else return (void *)xQueueCreate(queue_len, item_size); -#endif } static void queue_delete_wrapper(void *queue) { -#if !CONFIG_SPIRAM_USE_MALLOC vQueueDelete(queue); -#else - btdm_queue_item_t item = { - .handle = queue, - .storage = NULL, - .buffer = NULL, - }; - - if (btdm_queue_generic_deregister(&item)) { - vQueueDelete(item.handle); - free(item.storage); - free(item.buffer); - } - - return; -#endif } static int queue_send_wrapper(void *queue, void *item, uint32_t block_time_ms) @@ -791,7 +599,7 @@ static void *malloc_internal_wrapper(size_t size) static int IRAM_ATTR read_mac_wrapper(uint8_t mac[6]) { int ret = esp_read_mac(mac, ESP_MAC_BT); - ESP_LOGI(BTDM_LOG_TAG, "Bluetooth MAC: 0x%02x:%02x:%02x:%02x:%02x:%02x\n", + ESP_LOGI(BTDM_LOG_TAG, "Bluetooth MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return ret; @@ -807,17 +615,6 @@ static int IRAM_ATTR rand_wrapper(void) return (int)esp_random(); } -#if (defined CONFIG_PM_ENABLE) || (defined CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) -static uint32_t IRAM_ATTR btdm_lpcycles_2_us(uint32_t cycles) -{ - // The number of lp cycles should not lead to overflow. Thrs: 100s - // clock measurement is conducted - uint64_t us = (uint64_t)btdm_lpcycle_us * cycles; - us = (us + (1 << (btdm_lpcycle_us_frac - 1))) >> btdm_lpcycle_us_frac; - return (uint32_t)us; -} -#endif - static uint32_t IRAM_ATTR btdm_lpcycles_2_hus(uint32_t cycles, uint32_t *error_corr) { uint64_t local_error_corr = (error_corr == NULL) ? 0 : (uint64_t)(*error_corr); @@ -857,9 +654,12 @@ static bool IRAM_ATTR btdm_sleep_check_duration(int32_t *half_slot_cnt) static void btdm_sleep_enter_phase1_wrapper(uint32_t lpcycles) { -#if (defined CONFIG_PM_ENABLE) || (defined CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) + if (s_lp_cntl.wakeup_timer_required == 0) { + return; + } + // start a timer to wake up and acquire the pm_lock before modem_sleep awakes - uint32_t us_to_sleep = btdm_lpcycles_2_us(lpcycles); + uint32_t us_to_sleep = btdm_lpcycles_2_hus(lpcycles, NULL) >> 1; #define BTDM_MIN_TIMER_UNCERTAINTY_US (1800) assert(us_to_sleep > BTDM_MIN_TIMER_UNCERTAINTY_US); @@ -870,77 +670,144 @@ static void btdm_sleep_enter_phase1_wrapper(uint32_t lpcycles) uncertainty = BTDM_MIN_TIMER_UNCERTAINTY_US; } - if (esp_timer_start_once(s_btdm_slp_tmr, us_to_sleep - uncertainty) != ESP_OK) { - ESP_LOGW(BTDM_LOG_TAG, "timer start failed"); + assert (s_lp_stat.wakeup_timer_started == 0); + if (esp_timer_start_once(s_btdm_slp_tmr, us_to_sleep - uncertainty) == ESP_OK) { + s_lp_stat.wakeup_timer_started = 1; + } else { + ESP_LOGE(BTDM_LOG_TAG, "timer start failed"); + assert(0); } -#endif } static void btdm_sleep_enter_phase2_wrapper(void) { if (btdm_controller_get_sleep_mode() == ESP_BT_SLEEP_MODE_1) { -#if BTDM_MODEM_SLEEP_IN_EFFECT - esp_phy_disable(); -#endif /* BTDM_MODEM_SLEEP_IN_EFFECT */ - -#if (CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) - if(sleep_backup_en) { - btdm_ble_power_down_dma_copy(true); - sleep_backup_done = true; - } -#endif + if (s_lp_stat.phy_enabled) { + esp_phy_disable(); + s_lp_stat.phy_enabled = 0; + } else { + assert(0); + } + if (s_lp_stat.pm_lock_released == 0) { #ifdef CONFIG_PM_ENABLE - esp_pm_lock_release(s_pm_lock); - semphr_give_wrapper(s_pm_lock_sem); + esp_pm_lock_release(s_pm_lock); #endif + s_lp_stat.pm_lock_released = 1; + } } } -static void IRAM_ATTR btdm_sleep_exit_phase1_wrapper(void) -{ -#ifdef CONFIG_PM_ENABLE - if (semphr_take_from_isr_wrapper(s_pm_lock_sem, NULL) == pdTRUE) { - esp_pm_lock_acquire(s_pm_lock); - } -#endif -} - static void btdm_sleep_exit_phase3_wrapper(void) { +#ifdef CONFIG_PM_ENABLE + // If BT wakeup before esp timer coming due to timer task have no chance to run. + // Then we will not run into `btdm_sleep_exit_phase0` and acquire PM lock, + // Do it again here to fix this issue. + if (s_lp_stat.pm_lock_released) { + esp_pm_lock_acquire(s_pm_lock); + s_lp_stat.pm_lock_released = 0; + } +#endif + if(btdm_sleep_clock_sync()) { ESP_LOGE(BTDM_LOG_TAG, "sleep eco state err\n"); assert(0); } if (btdm_controller_get_sleep_mode() == ESP_BT_SLEEP_MODE_1) { -#if BTDM_MODEM_SLEEP_IN_EFFECT - esp_phy_enable(); -#endif /* BTDM_MODEM_SLEEP_IN_EFFECT */ - btdm_check_and_init_bb(); -#if (defined CONFIG_PM_ENABLE) || (defined CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) + if (s_lp_stat.phy_enabled == 0) { + esp_phy_enable(); + s_lp_stat.phy_enabled = 1; + } + } + + // If BT wakeup before esp timer coming due to timer task have no chance to run. + // Then we will not run into `btdm_sleep_exit_phase0` and stop esp timer, + // Do it again here to fix this issue. + if (s_lp_cntl.wakeup_timer_required && s_lp_stat.wakeup_timer_started) { esp_timer_stop(s_btdm_slp_tmr); -#endif + s_lp_stat.wakeup_timer_started = 0; + } +} + +static void IRAM_ATTR btdm_sleep_exit_phase0(void *param) +{ + assert(s_lp_cntl.enable == 1); + +#ifdef CONFIG_PM_ENABLE + if (s_lp_stat.pm_lock_released) { + esp_pm_lock_acquire(s_pm_lock); + s_lp_stat.pm_lock_released = 0; + } +#endif + + btdm_wakeup_request(); + + if (s_lp_cntl.wakeup_timer_required && s_lp_stat.wakeup_timer_started) { + esp_timer_stop(s_btdm_slp_tmr); + s_lp_stat.wakeup_timer_started = 0; + } + int event = (int) param; + if (event == BTDM_ASYNC_WAKEUP_SRC_VHCI || event == BTDM_ASYNC_WAKEUP_SRC_DISA) { + semphr_give_wrapper(s_wakeup_req_sem); } } -#if (defined CONFIG_PM_ENABLE) || (defined CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) static void IRAM_ATTR btdm_slp_tmr_callback(void *arg) { #ifdef CONFIG_PM_ENABLE - if (semphr_take_wrapper(s_pm_lock_sem, 0) == pdTRUE) { - esp_pm_lock_acquire(s_pm_lock); - } + btdm_vnd_offload_post(BTDM_VND_OL_SIG_WAKEUP_TMR, (void *)BTDM_ASYNC_WAKEUP_SRC_TMR); #endif - -#ifdef CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB - if (sleep_backup_en && sleep_backup_done) { - btdm_ble_power_down_dma_copy(false); - sleep_backup_done = false; - } -#endif - } -#endif + + +static bool async_wakeup_request(int event) +{ + if (s_lp_cntl.enable == 0) { + return false; + } + + bool do_wakeup_request = false; + switch (event) { + case BTDM_ASYNC_WAKEUP_SRC_VHCI: + case BTDM_ASYNC_WAKEUP_SRC_DISA: + btdm_in_wakeup_requesting_set(true); + if (!btdm_power_state_active()) { + btdm_vnd_offload_post(BTDM_VND_OL_SIG_WAKEUP_TMR, (void *)event); + do_wakeup_request = true; + semphr_take_wrapper(s_wakeup_req_sem, OSI_FUNCS_TIME_BLOCKING); + } + break; + default: + break; + } + + return do_wakeup_request; +} + +static void async_wakeup_request_end(int event) +{ + if (s_lp_cntl.enable == 0) { + return; + } + + bool allow_to_sleep; + switch (event) { + case BTDM_ASYNC_WAKEUP_SRC_VHCI: + case BTDM_ASYNC_WAKEUP_SRC_DISA: + allow_to_sleep = true; + break; + default: + allow_to_sleep = true; + break; + } + + if (allow_to_sleep) { + btdm_in_wakeup_requesting_set(false); + } + + return; +} static void coex_schm_status_bit_set_wrapper(uint32_t type, uint32_t status) { @@ -958,38 +825,29 @@ static void coex_schm_status_bit_clear_wrapper(uint32_t type, uint32_t status) bool esp_vhci_host_check_send_available(void) { + if (btdm_controller_status != ESP_BT_CONTROLLER_STATUS_ENABLED) { + return false; + } return API_vhci_host_check_send_available(); } void esp_vhci_host_send_packet(uint8_t *data, uint16_t len) { - btdm_wakeup_request_start(); - - if (!btdm_power_state_active()) { -#if CONFIG_PM_ENABLE - if (semphr_take_wrapper(s_pm_lock_sem, 0)) { - esp_pm_lock_acquire(s_pm_lock); - } -#endif -#if (defined CONFIG_PM_ENABLE) || (defined CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) - esp_timer_stop(s_btdm_slp_tmr); -#ifdef CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB - if (sleep_backup_en && sleep_backup_done) { - btdm_ble_power_down_dma_copy(false); - sleep_backup_done = false; + if (btdm_controller_status != ESP_BT_CONTROLLER_STATUS_ENABLED) { + return; } -#endif // CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB + async_wakeup_request(BTDM_ASYNC_WAKEUP_SRC_VHCI); -#endif - btdm_wakeup_request(); - } API_vhci_host_send_packet(data, len); - btdm_wakeup_request_end(); + async_wakeup_request_end(BTDM_ASYNC_WAKEUP_SRC_VHCI); } esp_err_t esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback) { + if (btdm_controller_status != ESP_BT_CONTROLLER_STATUS_ENABLED) { + return ESP_FAIL; + } return API_vhci_host_register_callback((const vhci_host_callback_t *)callback) == 0 ? ESP_OK : ESP_FAIL; } @@ -1031,47 +889,31 @@ void esp_release_wifi_and_coex_mem(void) ESP_ERROR_CHECK(try_heap_caps_add_region((intptr_t)ets_rom_layout_p->data_start_interface_coexist,(intptr_t)ets_rom_layout_p->bss_end_interface_pp)); } -esp_err_t esp_bluetooth_stop(void) +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE +static void IRAM_ATTR btdm_mac_bb_power_down_cb(void) { -#ifdef CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB - if (sleep_backup_en && sleep_backup_done) { + if (s_lp_cntl.mac_bb_pd && s_lp_stat.mac_bb_pd == 0) { +#if (CONFIG_MAC_BB_PD) + btdm_ble_power_down_dma_copy(true); +#endif + s_lp_stat.mac_bb_pd = 1; + } +} - btdm_hw_mac_power_up_wrapper(); - // le module power up - CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_BT_FORCE_PD); - CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_BT_FORCE_ISO); - sleep_backup_done = false; +static void IRAM_ATTR btdm_mac_bb_power_up_cb(void) +{ +#if (CONFIG_MAC_BB_PD) + if (s_lp_cntl.mac_bb_pd && s_lp_stat.mac_bb_pd) { + btdm_ble_power_down_dma_copy(false); + s_lp_stat.mac_bb_pd = 0; } #endif - - return 0; } +#endif esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) { - esp_err_t err; - - btdm_controller_mem_init(); - if (esp_register_shutdown_handler((shutdown_handler_t)esp_bluetooth_stop) != 0) { - return ESP_ERR_INVALID_ARG; - } - - osi_funcs_p = (struct osi_funcs_t *)malloc_internal_wrapper(sizeof(struct osi_funcs_t)); - if (osi_funcs_p == NULL) { - return ESP_ERR_NO_MEM; - } - - memcpy(osi_funcs_p, &osi_funcs_ro, sizeof(struct osi_funcs_t)); - if (btdm_osi_funcs_register(osi_funcs_p) != 0) { - return ESP_ERR_INVALID_ARG; - } - -#if (CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) - bool try_alloc = btdm_deep_sleep_mem_init(); - if(try_alloc) { - sleep_backup_en = true; - } -#endif + esp_err_t err = ESP_FAIL; if (btdm_controller_status != ESP_BT_CONTROLLER_STATUS_IDLE) { return ESP_ERR_INVALID_STATE; @@ -1087,9 +929,6 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) return ESP_ERR_INVALID_ARG; } - //overwrite some parameters - cfg->magic = ESP_BT_CTRL_CONFIG_MAGIC_VAL; - if (cfg->bluetooth_mode != ESP_BT_MODE_BLE) { ESP_LOGE(BTDM_LOG_TAG, "%s controller only support BLE only mode", __func__); return ESP_ERR_NOT_SUPPORTED; @@ -1109,81 +948,161 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) } } + // overwrite some parameters + cfg->magic = ESP_BT_CTRL_CONFIG_MAGIC_VAL; + + btdm_controller_mem_init(); + +#if CONFIG_MAC_BB_PD + if (esp_register_mac_bb_pd_callback(btdm_mac_bb_power_down_cb) != 0) { + err = ESP_ERR_INVALID_ARG; + goto error; + } + + if (esp_register_mac_bb_pu_callback(btdm_mac_bb_power_up_cb) != 0) { + err = ESP_ERR_INVALID_ARG; + goto error; + } +#endif + + osi_funcs_p = (struct osi_funcs_t *)malloc_internal_wrapper(sizeof(struct osi_funcs_t)); + if (osi_funcs_p == NULL) { + return ESP_ERR_NO_MEM; + } + + memcpy(osi_funcs_p, &osi_funcs_ro, sizeof(struct osi_funcs_t)); + if (btdm_osi_funcs_register(osi_funcs_p) != 0) { + return ESP_ERR_INVALID_ARG; + } + read_mac_wrapper(own_bda); ESP_LOGI(BTDM_LOG_TAG, "BT controller compile version [%s]", btdm_controller_get_compile_version()); -#if CONFIG_SPIRAM_USE_MALLOC - btdm_queue_table_mux = xSemaphoreCreateMutex(); - if (btdm_queue_table_mux == NULL) { - return ESP_ERR_NO_MEM; - } - memset(btdm_queue_table, 0, sizeof(btdm_queue_item_t) * BTDM_MAX_QUEUE_NUM); + // init low-power control resources + do { + // set default values for global states or resources + s_lp_stat.val = 0; + s_lp_cntl.val = 0; + s_wakeup_req_sem = NULL; + s_btdm_slp_tmr = NULL; + + // configure and initialize resources + s_lp_cntl.enable = (cfg->sleep_mode == ESP_BT_SLEEP_MODE_1) ? 1 : 0; + s_lp_cntl.no_light_sleep = 0; + + if (s_lp_cntl.enable) { +#if (CONFIG_MAC_BB_PD) + if (!btdm_deep_sleep_mem_init()) { + err = ESP_ERR_NO_MEM; + goto error; + } + s_lp_cntl.mac_bb_pd = 1; +#endif +#ifdef CONFIG_PM_ENABLE + s_lp_cntl.wakeup_timer_required = 1; +#endif + // async wakeup semaphore for VHCI + s_wakeup_req_sem = semphr_create_wrapper(1, 0); + if (s_wakeup_req_sem == NULL) { + err = ESP_ERR_NO_MEM; + goto error; + } + btdm_vnd_offload_task_register(BTDM_VND_OL_SIG_WAKEUP_TMR, btdm_sleep_exit_phase0); + } + + if (s_lp_cntl.wakeup_timer_required) { + esp_timer_create_args_t create_args = { + .callback = btdm_slp_tmr_callback, + .arg = NULL, + .name = "btSlp", + }; + if ((err = esp_timer_create(&create_args, &s_btdm_slp_tmr)) != ESP_OK) { + goto error; + } + } + + // set default bluetooth sleep clock cycle and its fractional bits + btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; + btdm_lpcycle_us = 2 << (btdm_lpcycle_us_frac); + + // // set default bluetooth sleep clock source + // s_lp_cntl.lpclk_sel = BTDM_LPCLK_SEL_XTAL; +#if CONFIG_BT_CTRL_LPCLK_SEL_EXT_32K_XTAL + // check whether or not EXT_CRYS is working + if (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_32K_XTAL) { + s_lp_cntl.lpclk_sel = BTDM_LPCLK_SEL_XTAL32K; // set default value +// #ifdef CONFIG_PM_ENABLE +// s_btdm_allow_light_sleep = true; +// #endif + } else { + ESP_LOGW(BTDM_LOG_TAG, "32.768kHz XTAL not detected, fall back to main XTAL as Bluetooth sleep clock\n" + "light sleep mode will not be able to apply when bluetooth is enabled"); + s_lp_cntl.lpclk_sel = BTDM_LPCLK_SEL_XTAL; // set default value + } +#elif (CONFIG_BT_CTRL_LPCLK_SEL_RTC_SLOW) + // check whether or not EXT_CRYS is working + if (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) { + s_lp_cntl.lpclk_sel = BTDM_LPCLK_SEL_RTC_SLOW; // set default value + } else { + ESP_LOGW(BTDM_LOG_TAG, "Internal 150kHz RC oscillator not detected, fall back to main XTAL as Bluetooth sleep clock\n" + "light sleep mode will not be able to apply when bluetooth is enabled"); + s_lp_cntl.lpclk_sel = BTDM_LPCLK_SEL_XTAL; // set default value + } +#else + s_lp_cntl.lpclk_sel = BTDM_LPCLK_SEL_XTAL; // set default value #endif - if (cfg->sleep_mode == ESP_BT_SLEEP_MODE_1) { -#ifdef CONFIG_PM_ENABLE - if ((err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "btLS", &s_light_sleep_pm_lock)) != ESP_OK) { + bool select_src_ret, set_div_ret; + if (s_lp_cntl.lpclk_sel == BTDM_LPCLK_SEL_XTAL) { + select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_XTAL); + set_div_ret = btdm_lpclk_set_div(rtc_clk_xtal_freq_get() * 2); + assert(select_src_ret && set_div_ret); + btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; + btdm_lpcycle_us = 2 << (btdm_lpcycle_us_frac); + } else if (s_lp_cntl.lpclk_sel == BTDM_LPCLK_SEL_XTAL32K) { + select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_XTAL32K); + set_div_ret = btdm_lpclk_set_div(0); + assert(select_src_ret && set_div_ret); + btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; + btdm_lpcycle_us = (RTC_CLK_CAL_FRACT > 15) ? (1000000 << (RTC_CLK_CAL_FRACT - 15)) : + (1000000 >> (15 - RTC_CLK_CAL_FRACT)); + assert(btdm_lpcycle_us != 0); + } else if (s_lp_cntl.lpclk_sel == BTDM_LPCLK_SEL_RTC_SLOW) { + select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_RTC_SLOW); + set_div_ret = btdm_lpclk_set_div(0); + assert(select_src_ret && set_div_ret); + btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; + btdm_lpcycle_us = esp_clk_slowclk_cal_get(); + } else { + err = ESP_ERR_INVALID_ARG; goto error; } + +#ifdef CONFIG_PM_ENABLE + if (s_lp_cntl.no_light_sleep) { + if ((err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "btLS", &s_light_sleep_pm_lock)) != ESP_OK) { + err = ESP_ERR_NO_MEM; + goto error; + } + } if ((err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "bt", &s_pm_lock)) != ESP_OK) { - goto error; - } - s_pm_lock_sem = semphr_create_wrapper(1, 0); - if (s_pm_lock_sem == NULL) { err = ESP_ERR_NO_MEM; goto error; + } else { + s_lp_stat.pm_lock_released = 1; } #endif -#if (defined CONFIG_PM_ENABLE) || (defined CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) - esp_timer_create_args_t create_args = { - .callback = btdm_slp_tmr_callback, - .arg = NULL, - .name = "btSlp" - }; - if ((err = esp_timer_create(&create_args, &s_btdm_slp_tmr)) != ESP_OK) { - goto error; - } -#endif + } while (0); - do {// todo: rewrite this block of code for chip - #if CONFIG_IDF_ENV_FPGA - // overwrite the sleep clock for FPGA - cfg->sleep_clock = ESP_BT_SLEEP_CLOCK_FPGA_32K; - ESP_LOGW(BTDM_LOG_TAG, "%s sleep clock overwrite on FPGA", __func__); - #endif - bool select_src_ret = false; - bool set_div_ret = false; - if (cfg->sleep_clock == ESP_BT_SLEEP_CLOCK_MAIN_XTAL) { - select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_XTAL); - set_div_ret = btdm_lpclk_set_div(rtc_clk_xtal_freq_get() * 2); - assert(select_src_ret && set_div_ret); - btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; - btdm_lpcycle_us = 2 << (btdm_lpcycle_us_frac); - } else if (cfg->sleep_clock == ESP_BT_SLEEP_CLOCK_EXT_32K_XTAL) { - select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_XTAL32K); - set_div_ret = btdm_lpclk_set_div(0); - assert(select_src_ret && set_div_ret); - btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; - btdm_lpcycle_us = esp_clk_slowclk_cal_get(); - assert(btdm_lpcycle_us != 0); - } else if (cfg->sleep_clock == ESP_BT_SLEEP_CLOCK_FPGA_32K) { - // on FPGA, the low power clock is hard-wired to a 32kHz(clock cycle 31.25us) oscillator - btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; - btdm_lpcycle_us = 125 << (btdm_lpcycle_us_frac - 2); - } - } while (0); - } #if CONFIG_SW_COEXIST_ENABLE coex_init(); #endif periph_module_enable(PERIPH_BT_MODULE); -#ifdef CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB - btdm_hw_mac_power_up_wrapper(); -#endif - // must do fpga_init and phy init before controller init + esp_phy_enable(); + s_lp_stat.phy_enabled = 1; if (btdm_controller_init(cfg) != 0) { err = ESP_ERR_NO_MEM; @@ -1195,33 +1114,57 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) return ESP_OK; error: + if (s_lp_stat.phy_enabled) { + esp_phy_disable(); + s_lp_stat.phy_enabled = 0; + } + + do { + // deinit low power control resources #ifdef CONFIG_PM_ENABLE - if (s_light_sleep_pm_lock != NULL) { - esp_pm_lock_delete(s_light_sleep_pm_lock); - s_light_sleep_pm_lock = NULL; - } - if (s_pm_lock != NULL) { - esp_pm_lock_delete(s_pm_lock); - s_pm_lock = NULL; - } + if (s_lp_cntl.no_light_sleep) { + if (s_light_sleep_pm_lock != NULL) { + esp_pm_lock_delete(s_light_sleep_pm_lock); + s_light_sleep_pm_lock = NULL; + } + } + if (s_pm_lock != NULL) { + esp_pm_lock_delete(s_pm_lock); + s_pm_lock = NULL; + s_lp_stat.pm_lock_released = 0; + } - if (s_pm_lock_sem) { - semphr_delete_wrapper(s_pm_lock_sem); - s_pm_lock_sem = NULL; - } #endif -#if (defined CONFIG_PM_ENABLE) || (defined CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) - if (s_btdm_slp_tmr != NULL) { - esp_timer_delete(s_btdm_slp_tmr); - s_btdm_slp_tmr = NULL; - } + if (s_lp_cntl.wakeup_timer_required && s_btdm_slp_tmr != NULL) { + esp_timer_delete(s_btdm_slp_tmr); + s_btdm_slp_tmr = NULL; + } + +#if (CONFIG_MAC_BB_PD) + if (s_lp_cntl.mac_bb_pd) { + btdm_deep_sleep_mem_deinit(); + s_lp_cntl.mac_bb_pd = 0; + } +#endif + if (s_lp_cntl.enable) { + btdm_vnd_offload_task_deregister(BTDM_VND_OL_SIG_WAKEUP_TMR); + if (s_wakeup_req_sem != NULL) { + semphr_delete_wrapper(s_wakeup_req_sem); + s_wakeup_req_sem = NULL; + } + } + } while (0); + +#if CONFIG_MAC_BB_PD + esp_unregister_mac_bb_pd_callback(btdm_mac_bb_power_down_cb); + + esp_unregister_mac_bb_pu_callback(btdm_mac_bb_power_up_cb); #endif -#if (CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) - btdm_deep_sleep_mem_deinit(); - sleep_backup_en = false; -#endif - esp_unregister_shutdown_handler((shutdown_handler_t)esp_bluetooth_stop); + if (osi_funcs_p != NULL) { + free(osi_funcs_p); + osi_funcs_p = NULL; + } return err; } @@ -1232,35 +1175,52 @@ esp_err_t esp_bt_controller_deinit(void) } btdm_controller_deinit(); -#if (CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) - btdm_deep_sleep_mem_deinit(); -#endif - esp_unregister_shutdown_handler((shutdown_handler_t)esp_bluetooth_stop); periph_module_disable(PERIPH_BT_MODULE); - esp_phy_disable(); -#ifdef CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB - btdm_hw_mac_power_down_wrapper(); -#endif -#ifdef CONFIG_PM_ENABLE - esp_pm_lock_delete(s_light_sleep_pm_lock); - s_light_sleep_pm_lock = NULL; - esp_pm_lock_delete(s_pm_lock); - s_pm_lock = NULL; - semphr_delete_wrapper(s_pm_lock_sem); - s_pm_lock_sem = NULL; -#endif -#if ( defined CONFIG_PM_ENABLE) || (defined CONFIG_PM_POWER_DOWN_WIFI_BT_MAC_BB) - esp_timer_stop(s_btdm_slp_tmr); - esp_timer_delete(s_btdm_slp_tmr); - s_btdm_slp_tmr = NULL; -#endif -#if CONFIG_SPIRAM_USE_MALLOC - vSemaphoreDelete(btdm_queue_table_mux); - btdm_queue_table_mux = NULL; - memset(btdm_queue_table, 0, sizeof(btdm_queue_item_t) * BTDM_MAX_QUEUE_NUM); + if (s_lp_stat.phy_enabled) { + esp_phy_disable(); + s_lp_stat.phy_enabled = 0; + } else { + assert(0); + } + + // deinit low power control resources + do { +#if (CONFIG_MAC_BB_PD) + btdm_deep_sleep_mem_deinit(); #endif +#ifdef CONFIG_PM_ENABLE + if (s_lp_cntl.no_light_sleep) { + esp_pm_lock_delete(s_light_sleep_pm_lock); + s_light_sleep_pm_lock = NULL; + } + + esp_pm_lock_delete(s_pm_lock); + s_pm_lock = NULL; + s_lp_stat.pm_lock_released = 0; +#endif + if (s_lp_cntl.wakeup_timer_required) { + if (s_lp_stat.wakeup_timer_started) { + esp_timer_stop(s_btdm_slp_tmr); + } + s_lp_stat.wakeup_timer_started = 0; + esp_timer_delete(s_btdm_slp_tmr); + s_btdm_slp_tmr = NULL; + } + + if (s_lp_cntl.enable) { + btdm_vnd_offload_task_deregister(BTDM_VND_OL_SIG_WAKEUP_TMR); + + semphr_delete_wrapper(s_wakeup_req_sem); + s_wakeup_req_sem = NULL; + } + } while (0); + +#if CONFIG_MAC_BB_PD + esp_unregister_mac_bb_pd_callback(btdm_mac_bb_power_down_cb); + esp_unregister_mac_bb_pu_callback(btdm_mac_bb_power_up_cb); +#endif free(osi_funcs_p); osi_funcs_p = NULL; @@ -1271,7 +1231,7 @@ esp_err_t esp_bt_controller_deinit(void) esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode) { - int ret; + int ret = ESP_OK; if (btdm_controller_status != ESP_BT_CONTROLLER_STATUS_INITED) { return ESP_ERR_INVALID_STATE; @@ -1282,36 +1242,51 @@ esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode) ESP_LOGE(BTDM_LOG_TAG, "invalid mode %d, controller support mode is %d", mode, btdm_controller_get_mode()); return ESP_ERR_INVALID_ARG; } -#ifdef CONFIG_PM_ENABLE - esp_pm_lock_acquire(s_light_sleep_pm_lock); - esp_pm_lock_acquire(s_pm_lock); -#endif - - // esp_phy_enable(); #if CONFIG_SW_COEXIST_ENABLE coex_enable(); #endif - if (btdm_controller_get_sleep_mode() == ESP_BT_SLEEP_MODE_1) { - btdm_controller_enable_sleep(true); - } - // inititalize bluetooth baseband - btdm_check_and_init_bb(); - - ret = btdm_controller_enable(mode); - if (ret) { - // esp_phy_disable(); + // enable low power mode + do { #ifdef CONFIG_PM_ENABLE - esp_pm_lock_release(s_light_sleep_pm_lock); - esp_pm_lock_release(s_pm_lock); + if (s_lp_cntl.no_light_sleep) { + esp_pm_lock_acquire(s_light_sleep_pm_lock); + } + esp_pm_lock_acquire(s_pm_lock); + s_lp_stat.pm_lock_released = 0; #endif - return ESP_ERR_INVALID_STATE; + + if (s_lp_cntl.enable) { + btdm_controller_enable_sleep(true); + } + } while (0); + + if (btdm_controller_enable(mode) != 0) { + ret = ESP_ERR_INVALID_STATE; + goto error; } btdm_controller_status = ESP_BT_CONTROLLER_STATUS_ENABLED; - return ESP_OK; + return ret; + +error: + // disable low power mode + do { + btdm_controller_enable_sleep(false); +#ifdef CONFIG_PM_ENABLE + if (s_lp_cntl.no_light_sleep) { + esp_pm_lock_release(s_light_sleep_pm_lock); + } + if (s_lp_stat.pm_lock_released == 0) { + esp_pm_lock_release(s_pm_lock); + s_lp_stat.pm_lock_released = 1; + } +#endif + } while (0); + + return ret; } esp_err_t esp_bt_controller_disable(void) @@ -1319,28 +1294,35 @@ esp_err_t esp_bt_controller_disable(void) if (btdm_controller_status != ESP_BT_CONTROLLER_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } - // disable modem sleep and wake up from sleep mode - if (btdm_controller_get_sleep_mode() == ESP_BT_SLEEP_MODE_1) { - btdm_controller_enable_sleep(false); - if (!btdm_power_state_active()) { - btdm_wakeup_request(); - } - while (!btdm_power_state_active()) { - esp_rom_delay_us(1000); - } - } + + async_wakeup_request(BTDM_ASYNC_WAKEUP_SRC_DISA); + while (!btdm_power_state_active()){} btdm_controller_disable(); + async_wakeup_request_end(BTDM_ASYNC_WAKEUP_SRC_DISA); + #if CONFIG_SW_COEXIST_ENABLE coex_disable(); #endif - // esp_phy_disable(); btdm_controller_status = ESP_BT_CONTROLLER_STATUS_INITED; + + // disable low power mode + do { #ifdef CONFIG_PM_ENABLE - esp_pm_lock_release(s_light_sleep_pm_lock); - esp_pm_lock_release(s_pm_lock); + if (s_lp_cntl.no_light_sleep) { + esp_pm_lock_release(s_light_sleep_pm_lock); + } + + if (s_lp_stat.pm_lock_released == 0) { + esp_pm_lock_release(s_pm_lock); + s_lp_stat.pm_lock_released = 1; + } else { + assert(0); + } #endif + } while (0); + return ESP_OK; } @@ -1425,18 +1407,6 @@ int IRAM_ATTR esp_bt_h4tl_eif_io_event_notify(int event) return btdm_hci_tl_io_event_post(event); } -void force_wifi_mode(int arg) -{ -} - -void unforce_wifi_mode(void) -{ -} - -void bt_bb_init_cmplx_reg(void) -{ -} - uint16_t esp_bt_get_tx_buf_num(void) { return l2c_ble_link_get_tx_buf_num(); diff --git a/components/bt/controller/lib b/components/bt/controller/lib index cf348db2d2..272aaca1f8 160000 --- a/components/bt/controller/lib +++ b/components/bt/controller/lib @@ -1 +1 @@ -Subproject commit cf348db2d264019ac8c2a5c40147f9973f7cf52c +Subproject commit 272aaca1f859f87c9694cd441ae68cb3d7829664 diff --git a/components/bt/include/esp32c3/include/esp_bt.h b/components/bt/include/esp32c3/include/esp_bt.h index 45d0d9bc3e..543f80785e 100644 --- a/components/bt/include/esp32c3/include/esp_bt.h +++ b/components/bt/include/esp32c3/include/esp_bt.h @@ -73,7 +73,8 @@ typedef enum { ESP_BT_SLEEP_CLOCK_NONE = 0, /*!< Sleep clock not configured */ ESP_BT_SLEEP_CLOCK_MAIN_XTAL = 1, /*!< SoC main crystal */ ESP_BT_SLEEP_CLOCK_EXT_32K_XTAL = 2, /*!< External 32.768kHz crystal */ - ESP_BT_SLEEP_CLOCK_FPGA_32K = 3, /*!< Hardwired 32KHz clock temporarily used for FPGA */ + ESP_BT_SLEEP_CLOCK_RTC_SLOW = 3, /*!< Internal 150kHz RC oscillator */ + ESP_BT_SLEEP_CLOCK_FPGA_32K = 4, /*!< Hardwired 32KHz clock temporarily used for FPGA */ } esp_bt_sleep_clock_t; /** diff --git a/components/esp_rom/esp32c3/ld/esp32c3.rom.ld b/components/esp_rom/esp32c3/ld/esp32c3.rom.ld index 2c284c27bf..8909983fe2 100644 --- a/components/esp_rom/esp32c3/ld/esp32c3.rom.ld +++ b/components/esp_rom/esp32c3/ld/esp32c3.rom.ld @@ -1731,6 +1731,7 @@ ieee80211_set_tx_desc = 0x4000186c; rom_sta_input = 0x40001870; wifi_get_macaddr = 0x40001874; wifi_rf_phy_disable = 0x40001878; +wifi_rf_phy_enable = 0x4000187c; ic_ebuf_alloc = 0x40001880; ieee80211_classify = 0x40001884; ieee80211_copy_eb_header = 0x40001888; diff --git a/components/esp_system/include/esp_sleep.h b/components/esp_system/include/esp_sleep.h index 4c247ce706..900b1ff4e6 100644 --- a/components/esp_system/include/esp_sleep.h +++ b/components/esp_system/include/esp_sleep.h @@ -409,6 +409,51 @@ void esp_sleep_gpio_status_init(void); */ void esp_sleep_gpio_status_switch_configure(bool enable); #endif + +#if CONFIG_MAC_BB_PD +/** + * @brief Function type for stub to run mac bb power down. + */ +typedef void (* mac_bb_power_down_cb_t)(void); + +/** + * @brief Function type for stub to run mac bb power up. + */ +typedef void (* mac_bb_power_up_cb_t)(void); + +/** + * @brief Registet mac bb power down callback. + * @param cb mac bb power down callback. + * @return + * - ESP_OK on success + */ +esp_err_t esp_register_mac_bb_pd_callback(mac_bb_power_down_cb_t cb); + +/** + * @brief Unregistet mac bb power down callback. + * @param cb mac bb power down callback. + * @return + * - ESP_OK on success + */ +esp_err_t esp_unregister_mac_bb_pd_callback(mac_bb_power_down_cb_t cb); + +/** + * @brief Registet mac bb power up callback. + * @param cb mac bb power up callback. + * @return + * - ESP_OK on success + */ +esp_err_t esp_register_mac_bb_pu_callback(mac_bb_power_up_cb_t cb); + +/** + * @brief Unregistet mac bb power up callback. + * @param cb mac bb power up callback. + * @return + * - ESP_OK on success + */ +esp_err_t esp_unregister_mac_bb_pu_callback(mac_bb_power_up_cb_t cb); +#endif + #ifdef __cplusplus } #endif diff --git a/components/esp_system/sleep_modes.c b/components/esp_system/sleep_modes.c index 0add0788a5..cbcd82b0ad 100644 --- a/components/esp_system/sleep_modes.c +++ b/components/esp_system/sleep_modes.c @@ -176,6 +176,95 @@ static void timer_wakeup_prepare(void); static void touch_wakeup_prepare(void); #endif +#if CONFIG_MAC_BB_PD +#define MAC_BB_POWER_DOWN_CB_NO 2 +#define MAC_BB_POWER_UP_CB_NO 2 +static DRAM_ATTR mac_bb_power_down_cb_t s_mac_bb_power_down_cb[MAC_BB_POWER_DOWN_CB_NO]; +static DRAM_ATTR mac_bb_power_up_cb_t s_mac_bb_power_up_cb[MAC_BB_POWER_UP_CB_NO]; + +esp_err_t esp_register_mac_bb_pd_callback(mac_bb_power_down_cb_t cb) +{ + int index = MAC_BB_POWER_DOWN_CB_NO; + for (int i = MAC_BB_POWER_DOWN_CB_NO -1; i >= 0; i--) { + if (s_mac_bb_power_down_cb[i] == cb) { + return ESP_ERR_INVALID_STATE; + } + + if (s_mac_bb_power_down_cb[i] == NULL) { + index = i; + } + } + + if (index < MAC_BB_POWER_DOWN_CB_NO) { + s_mac_bb_power_down_cb[index] = cb; + return ESP_OK; + } + + return ESP_ERR_NO_MEM; +} + +esp_err_t esp_unregister_mac_bb_pd_callback(mac_bb_power_down_cb_t cb) +{ + for (int i = MAC_BB_POWER_DOWN_CB_NO -1; i >= 0; i--) { + if (s_mac_bb_power_down_cb[i] == cb) { + s_mac_bb_power_down_cb[i] = NULL; + return ESP_OK; + } + } + return ESP_ERR_INVALID_STATE; +} + +static IRAM_ATTR void mac_bb_power_down_cb_execute(void) +{ + for (int i = 0; i < MAC_BB_POWER_DOWN_CB_NO; i++) { + if (s_mac_bb_power_down_cb[i]) { + s_mac_bb_power_down_cb[i](); + } + } +} + +esp_err_t esp_register_mac_bb_pu_callback(mac_bb_power_up_cb_t cb) +{ + int index = MAC_BB_POWER_UP_CB_NO; + for (int i = MAC_BB_POWER_UP_CB_NO -1; i >= 0; i--) { + if (s_mac_bb_power_up_cb[i] == cb) { + return ESP_ERR_INVALID_STATE; + } + + if (s_mac_bb_power_up_cb[i] == NULL) { + index = i; + } + } + + if (index < MAC_BB_POWER_UP_CB_NO) { + s_mac_bb_power_up_cb[index] = cb; + return ESP_OK; + } + + return ESP_ERR_NO_MEM; +} + +esp_err_t esp_unregister_mac_bb_pu_callback(mac_bb_power_up_cb_t cb) +{ + for (int i = MAC_BB_POWER_UP_CB_NO -1; i >= 0; i--) { + if (s_mac_bb_power_up_cb[i] == cb) { + s_mac_bb_power_up_cb[i] = NULL; + return ESP_OK; + } + } + return ESP_ERR_INVALID_STATE; +} + +static IRAM_ATTR void mac_bb_power_up_cb_execute(void) +{ + for (int i = 0; i < MAC_BB_POWER_UP_CB_NO; i++) { + if (s_mac_bb_power_up_cb[i]) { + s_mac_bb_power_up_cb[i](); + } + } +} +#endif ///CONFIG_MAC_BB_PD + /* Wake from deep sleep stub See esp_deepsleep.h esp_wake_deep_sleep() comments for details. */ @@ -342,6 +431,7 @@ void esp_sleep_gpio_status_switch_configure(bool enable) } #endif // SOC_GPIO_SUPPORT_SLP_SWITCH + static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) { // Stop UART output so that output is not lost due to APB frequency change. @@ -364,6 +454,10 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) suspend_uarts(); } +#if CONFIG_MAC_BB_PD + mac_bb_power_down_cb_execute(); +#endif + // Save current frequency and switch to XTAL rtc_cpu_freq_config_t cpu_freq_config; rtc_clk_cpu_freq_get_config(&cpu_freq_config); @@ -474,6 +568,9 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) gpio_sleep_mode_config_unapply(); #endif +#if CONFIG_MAC_BB_PD + mac_bb_power_up_cb_execute(); +#endif // re-enable UART output resume_uarts(); diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index 1fc15fc180..56e8adac61 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -446,7 +446,7 @@ menu "PHY" config ESP32_PHY_MAC_BB_PD bool "Power down MAC and baseband of Wi-Fi and Bluetooth when PHY is disabled" - depends on IDF_TARGET_ESP32C3 + depends on (IDF_TARGET_ESP32C3 && FREERTOS_USE_TICKLESS_IDLE) default n help If enabled, the MAC and baseband of Wi-Fi and Bluetooth will be powered diff --git a/components/esp_wifi/include/esp_phy_init.h b/components/esp_wifi/include/esp_phy_init.h index 72dc673337..f85d8b38b1 100644 --- a/components/esp_wifi/include/esp_phy_init.h +++ b/components/esp_wifi/include/esp_phy_init.h @@ -219,6 +219,7 @@ int64_t esp_phy_rf_get_on_ts(void); */ esp_err_t esp_phy_update_country_info(const char *country); + #if CONFIG_ESP32_SUPPORT_MULTIPLE_PHY_INIT_DATA_BIN /** * @brief Apply PHY init bin to PHY diff --git a/components/esp_wifi/include/esp_private/wifi.h b/components/esp_wifi/include/esp_private/wifi.h index c0df0d2749..9ae3003994 100644 --- a/components/esp_wifi/include/esp_private/wifi.h +++ b/components/esp_wifi/include/esp_private/wifi.h @@ -526,6 +526,16 @@ void esp_wifi_internal_update_light_sleep_wake_ahead_time(uint32_t); * - ESP_OK: succeed */ esp_err_t esp_wifi_internal_set_mac_sleep(bool enable); + +/** + * @brief mac bb sleep. + */ +void pm_mac_sleep(void); + +/** + * @brief mac bb wakeup. + */ +void pm_mac_wakeup(void); #endif /** diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index 56d0911c8a..ba5b0ff41f 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit 56d0911c8a27bd97e7939057bf82c61e82a689e6 +Subproject commit ba5b0ff41f052c8e1e66c92be577a503fcd46674 diff --git a/components/esp_wifi/src/phy_init.c b/components/esp_wifi/src/phy_init.c index 9e836f8510..cf89bb9275 100644 --- a/components/esp_wifi/src/phy_init.c +++ b/components/esp_wifi/src/phy_init.c @@ -271,8 +271,6 @@ void esp_mac_bb_pd_mem_init(void) IRAM_ATTR void esp_mac_bb_power_up(void) { - uint32_t level = phy_enter_critical(); - if (s_mac_bb_pd_mem != NULL && s_mac_bb_pd_ref == 0) { esp_phy_common_clock_enable(); CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_WIFI_FORCE_PD); @@ -283,13 +281,13 @@ IRAM_ATTR void esp_mac_bb_power_up(void) esp_phy_common_clock_disable(); } s_mac_bb_pd_ref++; - - phy_exit_critical(level); } IRAM_ATTR void esp_mac_bb_power_down(void) { - uint32_t level = phy_enter_critical(); + if (s_mac_bb_pd_ref == 0) { + return; + } s_mac_bb_pd_ref--; if (s_mac_bb_pd_mem != NULL && s_mac_bb_pd_ref == 0) { @@ -299,8 +297,6 @@ IRAM_ATTR void esp_mac_bb_power_down(void) SET_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_WIFI_FORCE_PD); esp_phy_common_clock_disable(); } - - phy_exit_critical(level); } #endif diff --git a/components/esp_wifi/src/wifi_init.c b/components/esp_wifi/src/wifi_init.c index f01336665b..f60ebb87c2 100644 --- a/components/esp_wifi/src/wifi_init.c +++ b/components/esp_wifi/src/wifi_init.c @@ -152,7 +152,10 @@ esp_err_t esp_wifi_deinit(void) esp_pm_unregister_inform_out_light_sleep_overhead_callback(esp_wifi_internal_update_light_sleep_wake_ahead_time); #endif #endif - +#if CONFIG_MAC_BB_PD + esp_unregister_mac_bb_pd_callback(pm_mac_sleep); + esp_unregister_mac_bb_pu_callback(pm_mac_wakeup); +#endif return err; } @@ -207,11 +210,26 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config) } } #endif + #if CONFIG_FREERTOS_USE_TICKLESS_IDLE +#if CONFIG_MAC_BB_PD + if (esp_register_mac_bb_pd_callback(pm_mac_sleep) != ESP_OK + || esp_register_mac_bb_pu_callback(pm_mac_wakeup) != ESP_OK) { + + esp_unregister_mac_bb_pd_callback(pm_mac_sleep); + esp_unregister_mac_bb_pu_callback(pm_mac_wakeup); + return ESP_ERR_INVALID_ARG; + } +#endif + #if SOC_WIFI_HW_TSF esp_err_t ret = esp_pm_register_skip_light_sleep_callback(esp_wifi_internal_is_tsf_active); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to register skip light sleep callback (0x%x)", ret); +#if CONFIG_MAC_BB_PD + esp_unregister_mac_bb_pd_callback(pm_mac_sleep); + esp_unregister_mac_bb_pu_callback(pm_mac_wakeup); +#endif return ret; } ret = esp_pm_register_inform_out_light_sleep_overhead_callback(esp_wifi_internal_update_light_sleep_wake_ahead_time); @@ -222,10 +240,7 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config) esp_sleep_enable_wifi_wakeup(); #endif #endif -#if CONFIG_MAC_BB_PD - esp_mac_bb_pd_mem_init(); - esp_wifi_internal_set_mac_sleep(true); -#endif + #if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER esp_err_t err = tcpip_adapter_set_default_wifi_handlers(); if (err != ESP_OK) { @@ -237,6 +252,10 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config) #endif esp_err_t result = esp_wifi_init_internal(config); if (result == ESP_OK) { +#if CONFIG_MAC_BB_PD + esp_mac_bb_pd_mem_init(); + esp_wifi_internal_set_mac_sleep(true); +#endif esp_wifi_set_debug_log(); #if CONFIG_IDF_TARGET_ESP32 s_wifi_mac_time_update_cb = esp_wifi_internal_update_mac_time; diff --git a/components/hal/interrupt_controller_hal.c b/components/hal/interrupt_controller_hal.c index 0af5762725..9e00841fdb 100644 --- a/components/hal/interrupt_controller_hal.c +++ b/components/hal/interrupt_controller_hal.c @@ -20,8 +20,8 @@ static bool is_interrupt_number_reserved(int interrupt_number) { - //TODO. Workaround to reserve interrupt number 0 for Wi-Fi. - if (interrupt_number == 1) { + //TODO. Workaround to reserve interrupt number 1 for Wi-Fi and 5&8 for Bluetooth. + if (interrupt_number == 1 || interrupt_number == 5 || interrupt_number == 8) { return true; } diff --git a/components/soc/esp32c3/include/soc/soc.h b/components/soc/esp32c3/include/soc/soc.h index c85b764e43..0b4a7645cf 100644 --- a/components/soc/esp32c3/include/soc/soc.h +++ b/components/soc/esp32c3/include/soc/soc.h @@ -270,23 +270,23 @@ //interrupt cpu using table, Please see the core-isa.h /************************************************************************************************************* - * Intr num Level Type PRO CPU usage APP CPU uasge - * 0 1 extern level WMAC Reserved - * 1 1 extern level BT/BLE Host HCI DMA BT/BLE Host HCI DMA + * Intr num Level Type PRO CPU usage + * 0 1 extern level Panic + * 1 1 extern level WMAC * 2 1 extern level * 3 1 extern level * 4 1 extern level WBB - * 5 1 extern level BT/BLE Controller BT/BLE Controller - * 6 1 timer FreeRTOS Tick(L1) FreeRTOS Tick(L1) - * 7 1 software BT/BLE VHCI BT/BLE VHCI - * 8 1 extern level BT/BLE BB(RX/TX) BT/BLE BB(RX/TX) + * 5 1 extern level BT/BLE Controller + * 6 1 timer FreeRTOS Tick(L1) + * 7 1 software + * 8 1 extern level BT/BLE BB(RX/TX) * 9 1 extern level * 10 1 extern edge * 11 3 profiling * 12 1 extern level * 13 1 extern level - * 14 7 nmi Reserved Reserved - * 15 3 timer FreeRTOS Tick(L3) FreeRTOS Tick(L3) + * 14 7 nmi Reserved + * 15 3 timer FreeRTOS Tick(L3) * 16 5 timer * 17 1 extern level * 18 1 extern level @@ -298,10 +298,10 @@ * 24 4 extern level TG1_WDT * 25 4 extern level CACHEERR * 26 5 extern level - * 27 3 extern level Reserved Reserved - * 28 4 extern edge DPORT ACCESS DPORT ACCESS - * 29 3 software Reserved Reserved - * 30 4 extern edge Reserved Reserved + * 27 3 extern level Reserved + * 28 4 extern edge Reserved + * 29 3 software Reserved + * 30 4 extern edge Reserved * 31 5 extern level ************************************************************************************************************* */