]> granicus.if.org Git - esp-idf/commitdiff
support wifi&bt coexist (v0.9.1)
authorisland <island@espressif.com>
Fri, 10 Nov 2017 02:54:50 +0000 (10:54 +0800)
committerTian Hao <tianhao@espressif.com>
Mon, 2 Apr 2018 05:44:48 +0000 (13:44 +0800)
1. refactor wifi modem sleep
2. refactor wifi and bt phy enable/diable coexistence
3. support wifi&bt coexist (v0.9.1)
3. add coex pause resume
4. fix bt library interrupt reaction slowly
5. make a2dp more smooth when coex
6. add coexist preference option
7. Make CI do not check libcoexist.a printf/ets_printf
8. disable Wifi RX AMPDU when software coexistence enable && update wifi lib
9. bluetooth call modem sleep api

12 files changed:
.gitlab-ci.yml
components/bt/bt.c
components/bt/lib
components/esp32/Kconfig
components/esp32/include/esp_coexist.h
components/esp32/include/esp_phy_init.h
components/esp32/include/esp_wifi_types.h
components/esp32/lib
components/esp32/phy_init.c
docs/en/api-guides/wifi.rst
examples/wifi/power_save/main/Kconfig.projbuild
examples/wifi/power_save/main/power_save.c

index c3e188bdc5b792dd7dfa37fe8118587c94b7cd44..27e9e0bbcd16c3cbea2265806b670fa4471a0493 100644 (file)
@@ -98,8 +98,8 @@ build_template_app:
     - make all V=1
     # Check if there are any stray printf/ets_printf references in WiFi libs
     - cd ../components/esp32/lib
-    - test $(xtensa-esp32-elf-nm *.a | grep -w printf | wc -l) -eq 0
-    - test $(xtensa-esp32-elf-nm *.a | grep -w ets_printf | wc -l) -eq 0
+    - test $(ls *.a|awk '{if ($1 != "libcoexist.a") print $1}' | xargs xtensa-esp32-elf-nm | grep -w printf | wc -l) -eq 0
+    - test $(ls *.a|awk '{if ($1 != "libcoexist.a") print $1}' | xargs xtensa-esp32-elf-nm | grep -w ets_printf | wc -l) -eq 0
 
 
 .build_template: &build_template
index 8a42a71241c6f73c7c552d72f1c3419461ff87df..0d848e214304f0d90955a9c13f1f6bbb55ed35d7 100644 (file)
@@ -540,7 +540,13 @@ esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode)
     esp_pm_lock_acquire(s_pm_lock);
 #endif
 
-    esp_phy_load_cal_and_init();
+    esp_phy_load_cal_and_init(PHY_BT_MODULE);
+    esp_modem_sleep_register(MODEM_BLE_MODULE);
+
+    /* TODO: Classic BT should be registered once it supports 
+     * modem sleep */
+
+    esp_modem_sleep_exit(MODEM_BLE_MODULE);
 
     if (btdm_bb_init_flag == false) {
         btdm_bb_init_flag = true;
@@ -549,6 +555,8 @@ esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode)
 
     ret = btdm_controller_enable(mode);
     if (ret) {
+        esp_modem_sleep_deregister(MODEM_BLE_MODULE);
+        esp_phy_rf_deinit(PHY_BT_MODULE);
         return ESP_ERR_INVALID_STATE;
     }
 
@@ -571,7 +579,10 @@ esp_err_t esp_bt_controller_disable(void)
     }
 
     if (ret == ESP_BT_MODE_IDLE) {
-        esp_phy_rf_deinit();
+        /* TODO: Need to de-register classic BT once it supports
+         * modem sleep */
+        esp_modem_sleep_deregister(MODEM_BLE_MODULE);
+        esp_phy_rf_deinit(PHY_BT_MODULE);
         btdm_controller_status = ESP_BT_CONTROLLER_STATUS_INITED;
     }
 
index b76cf6aa307acca2bf3634f2534c84b89f60a054..9a93239bc8f54bc5434e8a3dec98e2a8b6b9454a 160000 (submodule)
@@ -1 +1 @@
-Subproject commit b76cf6aa307acca2bf3634f2534c84b89f60a054
+Subproject commit 9a93239bc8f54bc5434e8a3dec98e2a8b6b9454a
index af62a206e8abb7a11b0bd4f96ab3de2cbed3fc47..3c94b0e9581af2cceb8a3fb4df228d899a38df93 100644 (file)
@@ -822,12 +822,40 @@ menu Wi-Fi
 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"
@@ -919,27 +947,33 @@ config ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM
 
 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"
index 872b5e5ab71430268575174acb7827adfd9db003..342652a197f7948c5a1f329f66bde438725dfd31 100644 (file)
 extern "C" {
 #endif
 
+typedef enum {
+    COEX_PREFER_WIFI = 0,
+    COEX_PREFER_BT,
+    COEX_PREFER_BALANCE,
+    COEX_PREFER_NUM,
+} coex_prefer_t;
+
 /**
  * @brief Init software coexist
+ *        extern function for internal use.
  *
  * @return Init ok or failed.
  */
@@ -27,24 +35,62 @@ esp_err_t coex_init(void);
 
 /**
  * @brief De-init software coexist
+ *        extern function for internal use.
  */
 void coex_deinit(void);
 
 /**
- * @brief Get software coexist enable or not
+ * @brief Pause software coexist
+ *        extern function for internal use.
+ */
+void coex_pause(void);
+
+/**
+ * @brief Resume software coexist
+ *        extern function for internal use.
+ */
+void coex_resume(void);
+
+/**
+ * @brief Get software coexist version string
+ *        extern function for internal use.
+ * @return : version string
+ */
+const char *coex_version_get(void);
+
+/**
+ * @brief Coexist performance preference set from libbt.a
+ *        extern function for internal use.
  *
- * @return software coexist enable status.
+ *  @param prefer : the prefer enumeration value
+ *  @return : ESP_OK - success, other - failed
  */
-bool coexist_get_enable(void);
+esp_err_t coex_preference_set(coex_prefer_t prefer);
 
 /**
- * @brief Set software coexist enable or not
+ * @brief Get software coexist version string
  *
- * @param enable software coexist or disable it
+ * @return : version string
+ */
+static inline const char *esp_coex_version_get(void)
+{
+    return coex_version_get();
+}
+
+/**
+ * @brief Set coexist preference of performance
+ *  For example, if prefer to bluetooth, then it will make A2DP(play audio via classic bt)
+ *  more smooth while wifi is runnning something.
+ *  If prefer to wifi, it will do similar things as prefer to bluetooth.
+ *  Default, it prefer to balance.
  *
- * @return Void.
+ *  @param prefer : the prefer enumeration value
+ *  @return : ESP_OK - success, other - failed
  */
-void coexist_set_enable(bool enable);
+static inline esp_err_t esp_coex_preference_set(coex_prefer_t prefer)
+{
+    return coex_preference_set(prefer);
+}
 
 #ifdef __cplusplus
 }
index 1184c4e8dcc5de34a459618d4bba36c03ed18027..75cb8fb58e712b91ebe2f887b11215f73f050b9e 100644 (file)
@@ -46,6 +46,43 @@ typedef enum {
     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
  *
@@ -130,8 +167,8 @@ esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_da
  * @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
@@ -142,12 +179,32 @@ esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data,
  *
  * @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
 }
index 88108bf0a10f2813797c1aaa908a2e1966ce9afb..63be8ba9bd12221ec0aa8cf0f2989bc62cde5141 100755 (executable)
@@ -174,8 +174,9 @@ typedef struct {
 }wifi_fast_scan_threshold_t;
 
 typedef enum {
-    WIFI_PS_NONE,    /**< No power save */
-    WIFI_PS_MODEM,   /**< Modem power save */
+    WIFI_PS_NONE,        /**< No power save */
+    WIFI_PS_MIN_MODEM,   /**< Minimum modem power save. In this mode, station wakes up to receive beacon every DTIM period */
+    WIFI_PS_MAX_MODEM,   /**< Maximum modem power save. In this mode, station wakes up to receive beacon every listen interval */
 } wifi_ps_type_t;
 
 #define WIFI_PROTOCOL_11B         1
@@ -208,6 +209,7 @@ typedef struct {
     bool bssid_set;        /**< whether set MAC address of target AP or not. Generally, station_config.bssid_set needs to be 0; and it needs to be 1 only when users need to check the MAC address of the AP.*/
     uint8_t bssid[6];     /**< MAC address of target AP*/
     uint8_t channel;       /**< channel of target AP. Set to 1~13 to scan starting from the specified channel before connecting to AP. If the channel of AP is unknown, set it to 0.*/
+    uint16_t listen_interval;   /**< Listen interval for ESP32 station to receive beacon in maximum power save mode, units: beacon interval */
     wifi_sort_method_t sort_method;    /**< sort the connect AP in the list by rssi or security mode */
     wifi_fast_scan_threshold_t  threshold;     /**< When scan_method is set to WIFI_FAST_SCAN, only APs which have an auth mode that is more secure than the selected auth mode and a signal stronger than the minimum RSSI will be used. */
 } wifi_sta_config_t;
index c47bfa9e70fda83d969aaf8bee28cb5714eb1b11..608bf369c66c33c15b0cc198e573e421c7103e1f 160000 (submodule)
@@ -1 +1 @@
-Subproject commit c47bfa9e70fda83d969aaf8bee28cb5714eb1b11
+Subproject commit 608bf369c66c33c15b0cc198e573e421c7103e1f
index 325da27b5eb31106f629d257bfabd92662ba4d89..626891372fa2474cab149eec5fe5213a9e24b41a 100644 (file)
 #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();
@@ -56,55 +73,264 @@ void IRAM_ATTR phy_exit_critical(uint32_t level)
     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"
@@ -274,7 +500,7 @@ static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle,
     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);
@@ -300,7 +526,7 @@ void esp_phy_load_cal_and_init(void)
         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);
@@ -308,7 +534,7 @@ void esp_phy_load_cal_and_init(void)
         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);
index 226d993bcc4f8ee39b3766de0eb42fb27178a959..61ce2ce6cb18e65ce8e7ce2211c89e5acd40600b 100644 (file)
@@ -1201,9 +1201,11 @@ By default, all Wi-Fi management frames are processed by the Wi-Fi driver, and t
 
 ESP32 Wi-Fi Power-saving Mode
 -----------------------------------
-Currently, ESP32 Wi-Fi supports the Modem-sleep mode which refers to WMM (Wi-Fi Multi Media) power-saving mode in the IEEE 802.11 protocol. If the Modem-sleep mode is enabled and the Wi-Fi enters a sleep state, then, RF, PHY and BB are turned off in order to reduce power consumption. Modem-sleep mode works in Station-only mode and the station must be connected to the AP first.
+Currently, ESP32 Wi-Fi supports the Modem-sleep mode which refers to the legacy power-saving mode in the IEEE 802.11 protocol. Modem-sleep mode works in Station-only mode and the station must connect to the AP first. If the Modem-sleep mode is enabled, station will switch between active and doze state periodically. In doze state, RF, PHY and BB are turned off in order to reduce power consumption. Station can keep connection with AP in modem-sleep mode.
 
-Call esp_wifi_set_ps(WIFI_PS_MODEM) to enable Modem-sleep mode after calling esp_wifi_init(). About 10 seconds after the station connects to the AP, Modem-sleep will start. When the station disconnects from the AP, Modem-sleep will stop.
+Modem-sleep mode includes minimum and maximum power save modes. In minimum power save mode, station wakes up every DTIM to receive beacon. Broadcast data will not be lost because it is transmitted after DTIM. However, it can not save much more power if DTIM is short for DTIM is determined by AP. In maximum power save mode, station wakes up every listen interval to receive beacon. Broadcast data may be lost because station may be in doze state at DTIM time. If listen interval is longer, more power is saved but broadcast data is more easy to lose. Listen interval can be configured by calling API esp_wifi_set_config() before connecting to AP.
+
+Call esp_wifi_set_ps(WIFI_PS_MIN_MODEM) to enable Modem-sleep minimum power save mode or esp_wifi_set_ps(WIFI_PS_MAX_MODEM) to enable Modem-sleep maximum power save mode after calling esp_wifi_init(). When station connects to AP, Modem-sleep will start. When station disconnects from AP, Modem-sleep will stop.
 
 ESP32 Wi-Fi Connect Crypto
 -----------------------------------
index 70c87c86b104b30ff151b5641f4e78458e6dfe85..2a436fcd097dad05b238d6d94d8307528bb7bbf4 100644 (file)
@@ -11,17 +11,25 @@ config WIFI_PASSWORD
     default "mypassword"
     help
        WiFi password (WPA or WPA2) for the example to use.
+       
+config WIFI_LISTEN_INTERVAL
+    int "WiFi listen interval"
+    default 3
+    help
+       WiFi listen interval for station to receive beacon from AP.
 
 choice POWER_SAVE_MODE
     prompt "power save mode"
-    default POWER_SAVE_MODEM
+    default POWER_SAVE_MIN_MODEM
     help
        Power save mode for the esp32 to use.
 
 config POWER_SAVE_NONE
     bool "none"
-config POWER_SAVE_MODEM
-    bool "modem"
+config POWER_SAVE_MIN_MODEM
+    bool "minimum modem"
+config POWER_SAVE_MAX_MODEM
+    bool "maximum modem"
 endchoice
 
 endmenu
index f1d665fb91d74888500c24445c2ac8927affb095..507994b2cc56efff42d61101f26a2043ee259cc3 100644 (file)
 #define DEFAULT_SSID CONFIG_WIFI_SSID
 #define DEFAULT_PWD CONFIG_WIFI_PASSWORD
 
-#if CONFIG_POWER_SAVE_MODEM
-#define DEFAULT_PS_MODE WIFI_PS_MODEM
+#define DEFAULT_LISTEN_INTERVAL CONFIG_WIFI_LISTEN_INTERVAL
+
+#if CONFIG_POWER_SAVE_MIN_MODEM
+#define DEFAULT_PS_MODE WIFI_PS_MIN_MODEM
+#elif CONFIG_POWER_SAVE_MAX_MODEM
+#define DEFAULT_PS_MODE WIFI_PS_MAX_MODEM
 #elif CONFIG_POWER_SAVE_NONE
 #define DEFAULT_PS_MODE WIFI_PS_NONE
 #else
@@ -70,6 +74,7 @@ static void wifi_power_save(void)
        .sta = {
            .ssid = DEFAULT_SSID,
            .password = DEFAULT_PWD,
+           .listen_interval = DEFAULT_LISTEN_INTERVAL,
        },
     };
     ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));