config SW_COEXIST_ENABLE
bool "Software controls WiFi/Bluetooth coexistence"
depends on BT_ENABLED
- default n
+ default y
help
If enabled, WiFi & Bluetooth coexistence is controlled by software rather than hardware.
Recommended for heavy traffic scenarios. Both coexistence configuration options are
automatically managed, no user intervention is required.
+choice SW_COEXIST_PREFERENCE
+ prompt "WiFi/Bluetooth coexistence performance preference"
+ depends on SW_COEXIST_ENABLE
+ default SW_COEXIST_PREFERENCE_BALANCE
+ help
+ Choose Bluetooth/WiFi/Balance for different preference.
+ If choose WiFi, it will make WiFi performance better. Such, keep WiFi Audio more smooth.
+ If choose Bluetooth, it will make Bluetooth performance better. Such, keep Bluetooth(A2DP) Audio more smooth.
+ If choose Balance, the performance of WiFi and bluetooth will be balance. It's default.
+ Except config preference in menuconfig, you can also call esp_coex_preference_set() dynamically.
+
+config SW_COEXIST_PREFERENCE_WIFI
+ bool "WiFi"
+
+config SW_COEXIST_PREFERENCE_BT
+ bool "Bluetooth(include BR/EDR and BLE)"
+
+config SW_COEXIST_PREFERENCE_BALANCE
+ bool "Balance"
+
+endchoice
+
+config SW_COEXIST_PREFERENCE_VALUE
+ int
+ depends on SW_COEXIST_ENABLE
+ default 0 if SW_COEXIST_PREFERENCE_WIFI
+ default 1 if SW_COEXIST_PREFERENCE_BT
+ default 2 if SW_COEXIST_PREFERENCE_BALANCE
config ESP32_WIFI_STATIC_RX_BUFFER_NUM
int "Max number of WiFi static RX buffers"
config ESP32_WIFI_AMPDU_TX_ENABLED
bool "WiFi AMPDU TX"
+ default y if SW_COEXIST_ENABLE
default y
help
Select this option to enable AMPDU TX feature
+ Suggest enable it when SW_COEXIST_ENABLE is defined.
config ESP32_WIFI_TX_BA_WIN
int "WiFi AMPDU TX BA window size"
depends on ESP32_WIFI_AMPDU_TX_ENABLED
range 2 32
+ default 2 if SW_COEXIST_ENABLE
default 6
help
Set the size of WiFi Block Ack TX window. Generally a bigger value means higher throughput but
more memory. Most of time we should NOT change the default value unless special reason, e.g.
test the maximum UDP TX throughput with iperf etc. For iperf test in shieldbox, the recommended
- value is 9~12.
+ value is 9~12. If SW_COEXIST_ENABLE is defined, suggest value is 2.
config ESP32_WIFI_AMPDU_RX_ENABLED
bool "WiFi AMPDU RX"
+ depends on !SW_COEXIST_ENABLE
+ default y
default y
help
Select this option to enable AMPDU RX feature
+ Suggest disable this option when SW_COEXIST_ENABLE is enabled. It can improve WiFi RX performance normally.
config ESP32_WIFI_RX_BA_WIN
int "WiFi AMPDU RX BA window size"
PHY_RF_CAL_FULL = 0x00000002 /*!< Do full RF calibration. Produces best results, but also consumes a lot of time and current. Suggested to be used once. */
} esp_phy_calibration_mode_t;
+
+/**
+ * @brief Modules for modem sleep
+ */
+typedef enum{
+ MODEM_BLE_MODULE, //!< BLE controller used
+ MODEM_CLASSIC_BT_MODULE, //!< Classic BT controller used
+ MODEM_WIFI_STATION_MODULE, //!< Wi-Fi Station used
+ MODEM_WIFI_SOFTAP_MODULE, //!< Wi-Fi SoftAP used
+ MODEM_WIFI_SNIFFER_MODULE, //!< Wi-Fi Sniffer used
+ MODEM_USER_MODULE, //!< User used
+ MODEM_MODULE_COUNT //!< Number of items
+}modem_sleep_module_t;
+
+/**
+ * @brief Module WIFI mask for medem sleep
+ */
+#define MODEM_BT_MASK ((1<<MODEM_BLE_MODULE) | \
+ (1<<MODEM_CLASSIC_BT_MODULE))
+
+/**
+ * @brief Module WIFI mask for medem sleep
+ */
+#define MODEM_WIFI_MASK ((1<<MODEM_WIFI_STATION_MODULE) | \
+ (1<<MODEM_WIFI_SOFTAP_MODULE) | \
+ (1<<MODEM_WIFI_SNIFFER_MODULE))
+
+/**
+ * @brief Modules needing to call phy_rf_init
+ */
+typedef enum{
+ PHY_BT_MODULE, //!< Bluetooth used
+ PHY_WIFI_MODULE, //!< Wi-Fi used
+ PHY_MODEM_MODULE, //!< Modem sleep used
+ PHY_MODULE_COUNT //!< Number of items
+}phy_rf_module_t;
+
/**
* @brief Get PHY init data
*
* @return ESP_OK on success.
* @return ESP_FAIL on fail.
*/
-esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data,
- esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data);
+esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data,esp_phy_calibration_mode_t mode,
+ esp_phy_calibration_data_t* calibration_data, phy_rf_module_t module);
/**
* @brief De-initialize PHY and RF module
*
* @return ESP_OK on success.
*/
-esp_err_t esp_phy_rf_deinit(void);
+esp_err_t esp_phy_rf_deinit(phy_rf_module_t module);
/**
* @brief Load calibration data from NVS and initialize PHY and RF module
*/
-void esp_phy_load_cal_and_init(void);
+void esp_phy_load_cal_and_init(phy_rf_module_t module);
+
+/**
+ * @brief Module requires to enter modem sleep
+ */
+esp_err_t esp_modem_sleep_enter(modem_sleep_module_t module);
+
+/**
+ * @brief Module requires to exit modem sleep
+ */
+esp_err_t esp_modem_sleep_exit(modem_sleep_module_t module);
+
+/**
+ * @brief Register module to make it be able to require to enter/exit modem sleep
+ */
+esp_err_t esp_modem_sleep_register(modem_sleep_module_t module);
+
+/**
+ * @brief De-register module from modem sleep list
+ */
+esp_err_t esp_modem_sleep_deregister(modem_sleep_module_t module);
#ifdef __cplusplus
}
#include "esp_coexist.h"
#include "driver/periph_ctrl.h"
-static const char* TAG = "phy_init";
-/* Count value to indicate if there is peripheral that has initialized PHY and RF */
-static int s_phy_rf_init_count = 0;
+static const char* TAG = "phy_init";
static _lock_t s_phy_rf_init_lock;
+/* Bit mask of modules needing to call phy_rf_init */
+static uint32_t s_module_phy_rf_init = 0;
+
+/* Whether modern sleep in turned on */
+static volatile bool s_is_phy_rf_en = false;
+
+/* Bit mask of modules needing to enter modem sleep mode */
+static uint32_t s_modem_sleep_module_enter = 0;
+
+/* Bit mask of modules which might use RF, system can enter modem
+ * sleep mode only when all modules registered require to enter
+ * modem sleep*/
+static uint32_t s_modem_sleep_module_register = 0;
+
+/* Whether modern sleep is turned on */
+static volatile bool s_is_modem_sleep_en = false;
+
+static _lock_t s_modem_sleep_lock;
+
uint32_t IRAM_ATTR phy_enter_critical(void)
{
return portENTER_CRITICAL_NESTED();
portEXIT_CRITICAL_NESTED(level);
}
-esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data,
- esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data)
+esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data, esp_phy_calibration_mode_t mode,
+ esp_phy_calibration_data_t* calibration_data, phy_rf_module_t module)
{
- assert((s_phy_rf_init_count <= 1) && (s_phy_rf_init_count >= 0));
+ /* 3 modules may call phy_init: Wi-Fi, BT, Modem Sleep */
+ if (module >= PHY_MODULE_COUNT){
+ ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
+ module count(%d)", __func__, module, PHY_MODULE_COUNT);
+ return ESP_ERR_INVALID_ARG;
+ }
_lock_acquire(&s_phy_rf_init_lock);
- if (s_phy_rf_init_count == 0) {
- // Enable WiFi/BT common peripheral clock
- periph_module_enable(PERIPH_WIFI_BT_COMMON_MODULE);
- ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d",
- init_data, calibration_data, mode);
- phy_set_wifi_mode_only(0);
- if (calibration_data != NULL) {
- uint8_t mac[6];
- esp_efuse_mac_get_default(mac);
- memcpy(&calibration_data->opaque[4], mac, 6);
+ uint32_t s_module_phy_rf_init_old = s_module_phy_rf_init;
+ bool is_wifi_or_bt_enabled = !!(s_module_phy_rf_init_old & (BIT(PHY_BT_MODULE) | BIT(PHY_WIFI_MODULE)));
+ esp_err_t status = ESP_OK;
+ s_module_phy_rf_init |= BIT(module);
+
+ if ((is_wifi_or_bt_enabled == false) && (module == PHY_MODEM_MODULE)){
+ status = ESP_FAIL;
+ }
+ else if (s_is_phy_rf_en == true) {
+ }
+ else {
+ /* If Wi-Fi, BT all disabled, modem sleep should not take effect;
+ * If either Wi-Fi or BT is enabled, should allow modem sleep requires
+ * to enter sleep;
+ * If Wi-Fi, BT co-exist, it is disallowed that only one module
+ * support modem sleep, E,g. BT support modem sleep but Wi-Fi not
+ * support modem sleep;
+ */
+ if (is_wifi_or_bt_enabled == false){
+ if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)){
+ s_is_phy_rf_en = true;
+ }
}
- register_chipv7_phy(init_data, calibration_data, mode);
- coex_bt_high_prio();
- } else {
+ else {
+ if (module == PHY_MODEM_MODULE){
+ s_is_phy_rf_en = true;
+ }
+ else if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)){
+ /* New module (BT or Wi-Fi) can init RF according to modem_sleep_exit */
+ }
+ }
+ if (s_is_phy_rf_en == true){
+ // Enable WiFi/BT common peripheral clock
+ periph_module_enable(PERIPH_WIFI_BT_COMMON_MODULE);
+ phy_set_wifi_mode_only(0);
+ register_chipv7_phy(init_data, calibration_data, mode);
+ coex_bt_high_prio();
+ }
+ }
+
#if CONFIG_SW_COEXIST_ENABLE
- coex_init();
-#endif
+ if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)){
+ uint32_t phy_bt_wifi_mask = BIT(PHY_BT_MODULE) | BIT(PHY_WIFI_MODULE);
+ if ((s_module_phy_rf_init & phy_bt_wifi_mask) == phy_bt_wifi_mask) { //both wifi & bt enabled
+ coex_init();
+ coex_preference_set(CONFIG_SW_COEXIST_PREFERENCE_VALUE);
+ coex_resume();
+ }
}
- s_phy_rf_init_count++;
+#endif
+
_lock_release(&s_phy_rf_init_lock);
- return ESP_OK;
+ return status;
}
-esp_err_t esp_phy_rf_deinit(void)
+esp_err_t esp_phy_rf_deinit(phy_rf_module_t module)
{
- assert((s_phy_rf_init_count <= 2) && (s_phy_rf_init_count >= 1));
+ /* 3 modules may call phy_init: Wi-Fi, BT, Modem Sleep */
+ if (module >= PHY_MODULE_COUNT){
+ ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
+ module count(%d)", __func__, module, PHY_MODULE_COUNT);
+ return ESP_ERR_INVALID_ARG;
+ }
_lock_acquire(&s_phy_rf_init_lock);
- if (s_phy_rf_init_count == 1) {
- // Disable PHY and RF.
- phy_close_rf();
- // Disable WiFi/BT common peripheral clock. Do not disable clock for hardware RNG
- periph_module_disable(PERIPH_WIFI_BT_COMMON_MODULE);
- } else {
+ uint32_t s_module_phy_rf_init_old = s_module_phy_rf_init;
+ uint32_t phy_bt_wifi_mask = BIT(PHY_BT_MODULE) | BIT(PHY_WIFI_MODULE);
+ bool is_wifi_or_bt_enabled = !!(s_module_phy_rf_init_old & phy_bt_wifi_mask);
+ bool is_both_wifi_bt_enabled = ((s_module_phy_rf_init_old & phy_bt_wifi_mask) == phy_bt_wifi_mask);
+ s_module_phy_rf_init &= ~BIT(module);
+ esp_err_t status = ESP_OK;
+
#if CONFIG_SW_COEXIST_ENABLE
- coex_deinit();
+ if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)){
+ if (is_both_wifi_bt_enabled == true) {
+ coex_deinit();
+ }
+ }
#endif
+
+ if ((is_wifi_or_bt_enabled == false) && (module == PHY_MODEM_MODULE)){
+ /* Modem sleep should not take effect in this case */
+ status = ESP_FAIL;
+ }
+ else if (s_is_phy_rf_en == false) {
+ //do nothing
+ }
+ else {
+ if (is_wifi_or_bt_enabled == false){
+ if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)){
+ s_is_phy_rf_en = false;
+ ESP_LOGE(TAG, "%s, RF should not be in enabled state if both Wi-Fi and BT are disabled", __func__);
+ }
+ }
+ else {
+ if (module == PHY_MODEM_MODULE){
+ s_is_phy_rf_en = false;
+ }
+ else if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)){
+ s_is_phy_rf_en = is_both_wifi_bt_enabled ? true : false;
+ }
+ }
+
+ if (s_is_phy_rf_en == false) {
+ gpio_set_level(15, 0); //G1, 15
+ // Disable PHY and RF.
+ phy_close_rf();
+ // Disable WiFi/BT common peripheral clock. Do not disable clock for hardware RNG
+ periph_module_disable(PERIPH_WIFI_BT_COMMON_MODULE);
+ }
}
- s_phy_rf_init_count--;
+
_lock_release(&s_phy_rf_init_lock);
+ return status;
+}
+
+
+
+esp_err_t esp_modem_sleep_enter(modem_sleep_module_t module)
+{
+#if CONFIG_SW_COEXIST_ENABLE
+ uint32_t phy_bt_wifi_mask = BIT(PHY_BT_MODULE) | BIT(PHY_WIFI_MODULE);
+#endif
+
+ if (module >= MODEM_MODULE_COUNT){
+ ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
+ module count(%d)", __func__, module, MODEM_MODULE_COUNT);
+ return ESP_ERR_INVALID_ARG;
+ }
+ else if (!(s_modem_sleep_module_register & BIT(module))){
+ ESP_LOGW(TAG, "%s, module (%d) has not been registered", __func__, module);
+ return ESP_ERR_INVALID_ARG;
+ }
+ else {
+ _lock_acquire(&s_modem_sleep_lock);
+ s_modem_sleep_module_enter |= BIT(module);
+#if CONFIG_SW_COEXIST_ENABLE
+ _lock_acquire(&s_phy_rf_init_lock);
+ if (((s_module_phy_rf_init & phy_bt_wifi_mask) == phy_bt_wifi_mask) //both wifi & bt enabled
+ && (s_modem_sleep_module_enter & (MODEM_BT_MASK | MODEM_WIFI_MASK)) != 0){
+ coex_pause();
+ }
+ _lock_release(&s_phy_rf_init_lock);
+#endif
+ if (!s_is_modem_sleep_en && (s_modem_sleep_module_enter == s_modem_sleep_module_register)){
+ esp_err_t status = esp_phy_rf_deinit(PHY_MODEM_MODULE);
+ if (status == ESP_OK){
+ s_is_modem_sleep_en = true;
+ }
+ }
+ _lock_release(&s_modem_sleep_lock);
+ return ESP_OK;
+ }
+}
+
+esp_err_t esp_modem_sleep_exit(modem_sleep_module_t module)
+{
+#if CONFIG_SW_COEXIST_ENABLE
+ uint32_t phy_bt_wifi_mask = BIT(PHY_BT_MODULE) | BIT(PHY_WIFI_MODULE);
+#endif
+
+ if (module >= MODEM_MODULE_COUNT){
+ ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
+ module count(%d)", __func__, module, MODEM_MODULE_COUNT);
+ return ESP_ERR_INVALID_ARG;
+ }
+ else if (!(s_modem_sleep_module_register & BIT(module))){
+ ESP_LOGW(TAG, "%s, module (%d) has not been registered", __func__, module);
+ return ESP_ERR_INVALID_ARG;
+ }
+ else {
+ _lock_acquire(&s_modem_sleep_lock);
+ s_modem_sleep_module_enter &= ~BIT(module);
+ if (s_is_modem_sleep_en){
+ esp_err_t status = esp_phy_rf_init(NULL,PHY_RF_CAL_NONE,NULL, PHY_MODEM_MODULE);
+ if (status == ESP_OK){
+ s_is_modem_sleep_en = false;
+ }
+ }
+#if CONFIG_SW_COEXIST_ENABLE
+ _lock_acquire(&s_phy_rf_init_lock);
+ if (((s_module_phy_rf_init & phy_bt_wifi_mask) == phy_bt_wifi_mask) //both wifi & bt enabled
+ && (s_modem_sleep_module_enter & (MODEM_BT_MASK | MODEM_WIFI_MASK)) == 0){
+ coex_resume();
+ }
+ _lock_release(&s_phy_rf_init_lock);
+#endif
+ _lock_release(&s_modem_sleep_lock);
+ return ESP_OK;
+ }
return ESP_OK;
}
+esp_err_t esp_modem_sleep_register(modem_sleep_module_t module)
+{
+ if (module >= MODEM_MODULE_COUNT){
+ ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
+ module count(%d)", __func__, module, MODEM_MODULE_COUNT);
+ return ESP_ERR_INVALID_ARG;
+ }
+ else if (s_modem_sleep_module_register & BIT(module)){
+ ESP_LOGI(TAG, "%s, multiple registration of module (%d)", __func__, module);
+ return ESP_OK;
+ }
+ else{
+ _lock_acquire(&s_modem_sleep_lock);
+ s_modem_sleep_module_register |= BIT(module);
+ /* The module is set to enter modem sleep by default, otherwise will prevent
+ * other modules from entering sleep mode if this module never call enter sleep function
+ * in the future */
+ s_modem_sleep_module_enter |= BIT(module);
+ _lock_release(&s_modem_sleep_lock);
+ return ESP_OK;
+ }
+}
+
+esp_err_t esp_modem_sleep_deregister(modem_sleep_module_t module)
+{
+ if (module >= MODEM_MODULE_COUNT){
+ ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
+ module count(%d)", __func__, module, MODEM_MODULE_COUNT);
+ return ESP_ERR_INVALID_ARG;
+ }
+ else if (!(s_modem_sleep_module_register & BIT(module))){
+ ESP_LOGI(TAG, "%s, module (%d) has not been registered", __func__, module);
+ return ESP_OK;
+ }
+ else{
+ _lock_acquire(&s_modem_sleep_lock);
+ s_modem_sleep_module_enter &= ~BIT(module);
+ s_modem_sleep_module_register &= ~BIT(module);
+ if (s_modem_sleep_module_register == 0){
+ s_modem_sleep_module_enter = 0;
+ /* Once all module are de-registered and current state
+ * is modem sleep mode, we need to turn off modem sleep
+ */
+ if (s_is_modem_sleep_en == true){
+ s_is_modem_sleep_en = false;
+ esp_phy_rf_init(NULL,PHY_RF_CAL_NONE,NULL, PHY_MODEM_MODULE);
+ }
+ }
+ _lock_release(&s_modem_sleep_lock);
+ return ESP_OK;
+ }
+}
+
+
// PHY init data handling functions
#if CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION
#include "esp_partition.h"
return err;
}
-void esp_phy_load_cal_and_init(void)
+void esp_phy_load_cal_and_init(phy_rf_module_t module)
{
esp_phy_calibration_data_t* cal_data =
(esp_phy_calibration_data_t*) calloc(sizeof(esp_phy_calibration_data_t), 1);
calibration_mode = PHY_RF_CAL_FULL;
}
- esp_phy_rf_init(init_data, calibration_mode, cal_data);
+ esp_phy_rf_init(init_data, calibration_mode, cal_data, module);
if (calibration_mode != PHY_RF_CAL_NONE && err != ESP_OK) {
err = esp_phy_store_cal_data_to_nvs(cal_data);
err = ESP_OK;
}
#else
- esp_phy_rf_init(init_data, PHY_RF_CAL_FULL, cal_data);
+ esp_phy_rf_init(NULL, PHY_RF_CAL_FULL, cal_data, module);
#endif
esp_phy_release_init_data(init_data);