From 164190b8c8bfdb26fc045b08ba4d2900d2d88812 Mon Sep 17 00:00:00 2001 From: yinqingzhao Date: Mon, 24 Aug 2026 10:31:37 +0800 Subject: [PATCH] feat(wifi): support disable dfs channels in apsta example --- docs/en/api-guides/wifi-driver/overview.rst | 3 +- .../zh_CN/api-guides/wifi-driver/overview.rst | 3 +- examples/wifi/softap_sta/README.md | 9 ++ .../wifi/softap_sta/main/Kconfig.projbuild | 24 ++++++ examples/wifi/softap_sta/main/softap_sta.c | 83 +++++++++++++++++++ 5 files changed, 120 insertions(+), 2 deletions(-) diff --git a/docs/en/api-guides/wifi-driver/overview.rst b/docs/en/api-guides/wifi-driver/overview.rst index e46e06061f4..d80f194d899 100644 --- a/docs/en/api-guides/wifi-driver/overview.rst +++ b/docs/en/api-guides/wifi-driver/overview.rst @@ -924,6 +924,7 @@ Wi-Fi Bandwidth Mode 1. If the STA connects to an AP on a DFS channel, the SoftAP is allowed to switch to the same DFS channel using CSA (Channel Switch Announcement). 2. When the STA disconnects, the SoftAP will switch back to a non-DFS channel via CSA to remain compliant with regulations. + 3. To completely prevent the SoftAP from operating on DFS channels, call :cpp:func:`esp_wifi_set_country()` to set ``policy`` to ``WIFI_COUNTRY_POLICY_MANUAL`` and restrict ``wifi_5g_channel_mask`` to the non-DFS channels of the current country/region. ``wifi_5g_channel_mask`` takes effect only when policy is ``WIFI_COUNTRY_POLICY_MANUAL``. Note: in this case, if the target AP operates on a DFS channel, the STA cannot connect to that AP. See :component_file:`esp_wifi/regulatory/esp_wifi_regulatory.txt` for the DFS channel range of each country/region. .. @@ -1058,7 +1059,7 @@ Wi-Fi Country Code * - policy - Country/region policy. When the configured country/region conflicts with that of the connected AP, this field determines which information to use. Details are explained below. * - wifi_5g_channel_mask - - Bitmask indicating allowed 5 GHz channels for the station/AP. The mapping between channel numbers and bits can be found in :cpp:enum:`wifi_5g_channel_bit_t`. + - Bitmask indicating allowed 5 GHz channels for the station/AP. The mapping between channel numbers and bits can be found in :cpp:enum:`wifi_5g_channel_bit_t`. A mask of 0 means 5 GHz channels are allowed according to local regulatory rules. The configured mask takes effect only when ``policy`` is ``WIFI_COUNTRY_POLICY_MANUAL``. To disable DFS channels, restrict the mask to the non-DFS channels of the current country/region; the STA will then be unable to connect to an AP operating on a DFS channel. A default configuration example:: diff --git a/docs/zh_CN/api-guides/wifi-driver/overview.rst b/docs/zh_CN/api-guides/wifi-driver/overview.rst index a5dcef22ca9..582a010c30d 100644 --- a/docs/zh_CN/api-guides/wifi-driver/overview.rst +++ b/docs/zh_CN/api-guides/wifi-driver/overview.rst @@ -924,6 +924,7 @@ Wi-Fi 带宽模式 1. 当 STA 连接到位于 DFS 信道的 AP 时,SoftAP 允许通过 CSA (Channel Switch Announcement) 切换至相同的 DFS 信道。 2. 当 STA 断开连接后,SoftAP 将通过 CSA 切换回非 DFS 信道,确保符合监管要求。 + 3. 如需要完全禁止 SoftAP 工作在 DFS 信道上,可通过 :cpp:func:`esp_wifi_set_country()` 将 ``policy`` 设为 ``WIFI_COUNTRY_POLICY_MANUAL``,并将 ``wifi_5g_channel_mask`` 限制为当前国家/地区的非 DFS 信道。``wifi_5g_channel_mask`` 仅在 policy 为 ``WIFI_COUNTRY_POLICY_MANUAL`` 时生效。注意:此时若目标 AP 工作在 DFS 信道上,STA 将无法与该 AP 建立连接。各国 DFS 信道范围见 :component_file:`esp_wifi/regulatory/esp_wifi_regulatory.txt`。 .. @@ -1058,7 +1059,7 @@ Wi-Fi 国家/地区代码 * - policy - 国家/地区策略。当配置的国家/地区与连接 AP 的信息冲突时,此字段决定应使用哪一方的信息。详情见下文说明。 * - wifi_5g_channel_mask - - 表示 station/AP 在 5 GHz 频段中可使用的信道掩码。信道值与位的对应关系详见 :cpp:enum:`wifi_5g_channel_bit_t`。 + - 表示 station/AP 在 5 GHz 频段中可使用的信道掩码。信道值与位的对应关系详见 :cpp:enum:`wifi_5g_channel_bit_t`。掩码为 0 时,5 GHz 信道按当地监管规则允许使用。配置的掩码仅在 ``policy`` 为 ``WIFI_COUNTRY_POLICY_MANUAL`` 时生效。如需禁用 DFS 信道,请将掩码限制为当前国家/地区的非 DFS 信道;此时 STA 也无法连接到工作在 DFS 信道上的 AP。 默认配置示例如下:: diff --git a/examples/wifi/softap_sta/README.md b/examples/wifi/softap_sta/README.md index 8a2a0b450f5..87b0ecc4f07 100644 --- a/examples/wifi/softap_sta/README.md +++ b/examples/wifi/softap_sta/README.md @@ -23,6 +23,15 @@ In the `Example Configuration` menu: * Set `WiFi Remote AP SSID`. * Set `WiFi Remote AP Password`. +* (5 GHz capable chips only) Set the DFS channel configuration under `DFS Channel Configuration`. + * Set `Country code` to select the regulatory domain (e.g. `US` for FCC, `DE` for CE, `01` for world safe mode). + * Enable `Disable DFS channels` to forbid both the STA and the SoftAP from using any DFS channel of the + selected regulatory domain. STA will not be able to connect to an AP that operates on a DFS channel. + The non-DFS channels are looked up from the esp_wifi regulatory table for the configured country, and + the 5 GHz channel bitmask (`wifi_country_t.wifi_5g_channel_mask`) is restricted to them with the + country policy switched to manual. This is useful for certification setups (e.g. FCC, CE) where DFS + channels must be avoided. + Optional: If necessary, modify the other choices to suit your needs. ### Build and Flash diff --git a/examples/wifi/softap_sta/main/Kconfig.projbuild b/examples/wifi/softap_sta/main/Kconfig.projbuild index 32e5377d2e7..df1f411d06f 100644 --- a/examples/wifi/softap_sta/main/Kconfig.projbuild +++ b/examples/wifi/softap_sta/main/Kconfig.projbuild @@ -85,4 +85,28 @@ menu "Example Configuration" endchoice endmenu + + menu "DFS Channel Configuration" + depends on SOC_WIFI_SUPPORT_5G + comment "DFS Channel Configuration (5 GHz capable chips only)" + + config ESP_WIFI_COUNTRY_CODE + string "Country code" + default "01" + help + Country code used to configure the regulatory domain (e.g. "US" for FCC, "DE" for CE, + "01" for world safe mode). The set of DFS channels depends on this regulatory domain. + + config ESP_WIFI_DISABLE_DFS_CHANNELS + bool "Disable DFS channels" + default n + help + If enabled, the device (both STA and SoftAP) will not use any DFS channel of the + configured regulatory domain. As a result, STA cannot connect to an AP that operates + on a DFS channel. The non-DFS channels are derived by looking up the country in the + esp_wifi regulatory table, and the 5 GHz channel bitmask + (wifi_country_t.wifi_5g_channel_mask) is restricted to them with the country policy + switched to manual. This can be useful for certification setups (e.g. FCC, CE) + where DFS channels must be avoided. + endmenu endmenu diff --git a/examples/wifi/softap_sta/main/softap_sta.c b/examples/wifi/softap_sta/main/softap_sta.c index 0f496e55939..6e3b6e367b4 100644 --- a/examples/wifi/softap_sta/main/softap_sta.c +++ b/examples/wifi/softap_sta/main/softap_sta.c @@ -170,6 +170,86 @@ esp_netif_t *wifi_init_sta(void) return esp_netif_sta; } +#if CONFIG_ESP_WIFI_DISABLE_DFS_CHANNELS +extern const wifi_regdomain_t regdomain_table[]; +extern const wifi_regulatory_t regulatory_data[]; + +static const wifi_regulatory_t *wifi_lookup_regulatory(const char *cc) +{ + for (int i = 0; regdomain_table[i].cn[0] != '#'; i++) { + if (regdomain_table[i].cn[0] == cc[0] && regdomain_table[i].cn[1] == cc[1]) { + return ®ulatory_data[regdomain_table[i].regulatory_type]; + } + } + return NULL; +} + +static uint32_t wifi_get_non_dfs_5g_channel_mask(const wifi_regulatory_t *reg) +{ + uint32_t mask = 0; + + for (int i = 0; i < reg->n_reg_rules; i++) { + const wifi_reg_rule_t *rule = ®->reg_rules[i]; + if (rule->is_dfs || rule->start_channel <= 14) { + continue; + } + for (uint8_t ch = rule->start_channel; ch <= rule->end_channel; ch += 4) { + mask |= CHANNEL_TO_BIT(ch); + } + } + return mask; +} + +// static uint32_t wifi_get_non_dfs_5g_channel_mask_by_type(uint8_t type) +// { +// return wifi_get_non_dfs_5g_channel_mask(®ulatory_data[type]); +// } + +static esp_err_t wifi_configure_country_without_dfs_channels(void) +{ + const char *cc = CONFIG_ESP_WIFI_COUNTRY_CODE; + const wifi_regulatory_t *reg = wifi_lookup_regulatory(cc); + if (!reg) { + ESP_LOGE(TAG_STA, "Unknown country code '%s'", cc); + return ESP_ERR_INVALID_ARG; + } + + wifi_country_t country = {0}; + memcpy(country.cc, cc, 2); + country.policy = WIFI_COUNTRY_POLICY_MANUAL; + uint8_t schan = 0; + uint8_t echan = 0; + for (int i = 0; i < reg->n_reg_rules; i++) { + const wifi_reg_rule_t *rule = ®->reg_rules[i]; + if (rule->start_channel > 14) { + continue; + } + if (schan == 0 || rule->start_channel < schan) { + schan = rule->start_channel; + } + if (rule->end_channel > echan) { + echan = rule->end_channel; + } + } + if (schan != 0) { + country.schan = schan; + country.nchan = echan - schan + 1; + } + + /* A zero mask does not mean "no 5 GHz channel allowed", it means "fall back to + the local regulatory rules", which would leave the DFS channels usable. Some + domains (AM for instance) mark every 5 GHz rule as DFS, so there the only way + to stay off DFS is to stay off 5 GHz altogether. */ + country.wifi_5g_channel_mask = wifi_get_non_dfs_5g_channel_mask(reg); + if (country.wifi_5g_channel_mask == 0) { + ESP_ERROR_CHECK(esp_wifi_set_band_mode(WIFI_BAND_MODE_2G_ONLY)); + ESP_LOGW(TAG_STA, "'%s' has no non-DFS 5 GHz channel, restricting to 2.4 GHz", cc); + } + ESP_ERROR_CHECK(esp_wifi_set_country(&country)); + return ESP_OK; +} +#endif /* CONFIG_ESP_WIFI_DISABLE_DFS_CHANNELS */ + void softap_set_dns_addr(esp_netif_t *esp_netif_ap,esp_netif_t *esp_netif_sta) { esp_netif_dns_info_t dns; @@ -231,6 +311,9 @@ void app_main(void) /* Start WiFi */ ESP_ERROR_CHECK(esp_wifi_start() ); +#if CONFIG_ESP_WIFI_DISABLE_DFS_CHANNELS + ESP_ERROR_CHECK(wifi_configure_country_without_dfs_channels()); +#endif /* * Wait until either the connection is established (WIFI_CONNECTED_BIT) or * connection failed for the maximum number of re-tries (WIFI_FAIL_BIT).