Merge branch 'fix/fix_issues_when_support_multiple_phy_init_data_v5.5' into 'release/v5.5'

Fix/fix issues when support multiple phy init data v5.5(backport v5.5)

See merge request espressif/esp-idf!52586
This commit is contained in:
Jiang Jiang Jian
2026-09-10 11:26:09 +08:00
11 changed files with 337 additions and 58 deletions

View File

@@ -26,8 +26,8 @@ extern "C" {
#if CONFIG_ESP_PHY_MULTIPLE_INIT_DATA_BIN
#define PHY_CRC_ALGORITHM 1
#define PHY_COUNTRY_CODE_LEN 2
#define PHY_INIT_DATA_TYPE_OFFSET 254
#define PHY_SUPPORT_MULTIPLE_BIN_OFFSET 253
#define PHY_INIT_DATA_TYPE_OFFSET 126
#define PHY_SUPPORT_MULTIPLE_BIN_OFFSET 125
#endif
extern const char phy_init_magic_pre[];

View File

@@ -283,6 +283,16 @@ void phy_wait_freq_hw_hop_done(void);
void phy_track_temp_debug(uint8_t debug_flag, uint8_t track_temp);
#endif
#if CONFIG_ESP_PHY_MULTIPLE_INIT_DATA_BIN
/**
* @brief Get the PHY init data type that is currently applied to the PHY
*
* @return the applied init data type, ESP_PHY_INIT_DATA_TYPE_DEFAULT as long as no
* certified init data has been selected by a country code
*/
phy_init_data_type_t esp_phy_get_init_data_type(void);
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -118,6 +118,13 @@ static uint8_t s_phy_modem_init_ref = 0;
extern uint8_t multi_phy_init_data_bin_start[] asm("_binary_phy_multiple_init_data_bin_start");
extern uint8_t multi_phy_init_data_bin_end[] asm("_binary_phy_multiple_init_data_bin_end");
#endif
/* Both flags live in the last bytes of an init data entry */
_Static_assert(PHY_INIT_DATA_TYPE_OFFSET == sizeof(esp_phy_init_data_t) - 2,
"PHY_INIT_DATA_TYPE_OFFSET does not match the PHY init data of this target");
_Static_assert(PHY_SUPPORT_MULTIPLE_BIN_OFFSET == sizeof(esp_phy_init_data_t) - 3,
"PHY_SUPPORT_MULTIPLE_BIN_OFFSET does not match the PHY init data of this target");
/* The following static variables are only used by Wi-Fi tasks, so they can be handled without lock */
static phy_init_data_type_t s_phy_init_data_type = 0;
@@ -1208,6 +1215,11 @@ esp_err_t esp_phy_update_init_data(phy_init_data_type_t init_data_type)
free(init_data_store);
return ESP_OK;
}
phy_init_data_type_t esp_phy_get_init_data_type(void)
{
return s_current_apply_phy_init_data;
}
#endif
esp_err_t esp_phy_update_country_info(const char *country)

View File

@@ -3,9 +3,6 @@
tools/test_apps/phy/phy_multi_init_data_test:
disable:
- if: SOC_WIFI_SUPPORTED != 1
disable_test:
- if: IDF_TARGET in ["esp32c5", "esp32c61"]
reason: lack of runner
tools/test_apps/phy/phy_tsens:
disable:

View File

@@ -1,2 +1,29 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- |
This project tests the multiple PHY init data bin support, i.e. `CONFIG_ESP_PHY_MULTIPLE_INIT_DATA_BIN`.
The tests cover:
- the layout, the checksums and the certified init data types of `phy_multiple_init_data.bin`
- loading the bin through `esp_phy_get_init_data()`
- switching the PHY init data type from a country code, both through `esp_phy_update_country_info()`
and through `esp_wifi_set_country_code()`
- the fallback to the default init data for countries without a certified init data
Two configurations are tested:
| Config | Where the bin lives |
| ---------------------------------- | ------------------------------------------------------------------------ |
| `phy_multiple_init_data` | flashed into the `phy` data partition |
| `phy_multiple_init_data_embed` | embedded into the application binary (`..._MULTIPLE_INIT_DATA_BIN_EMBED`) |
Which of the two paths was taken is only observable in the log, so `app_main` loads the bin
once before starting the unity runner.
Run the tests with:
```bash
idf.py set-target <target>
idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.ci.phy_multiple_init_data" build flash monitor
```

View File

@@ -1,2 +1,7 @@
idf_component_register(SRCS "phy_init_data_main.c"
INCLUDE_DIRS ".")
set(srcs "phy_init_data_main.c"
"test_phy_multiple_init_data.c")
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
PRIV_REQUIRES unity esp_phy esp_wifi esp_event nvs_flash esp_partition
WHOLE_ARCHIVE)

View File

@@ -1,55 +1,21 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/* test phy init data bin options
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
static const char *TAG = "phy init";
static EventGroupHandle_t s_wifi_event_group;
void wifi_init(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init finished.");
}
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_phy_init.h"
void app_main(void)
{
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
/* Whether the bin comes from the phy partition or from the application binary is
only observable in the log emitted while it is loaded, so trigger the load once
here to keep that visible in the boot output. */
const esp_phy_init_data_t *init_data = esp_phy_get_init_data();
if (init_data != NULL) {
esp_phy_release_init_data(init_data);
}
ESP_ERROR_CHECK(ret);
wifi_init();
unity_run_menu();
}

View File

@@ -0,0 +1,253 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_phy_init.h"
#include "esp_private/phy.h"
#include "esp_rom_crc.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "phy_init_data.h"
#include "sdkconfig.h"
/* esp_phy_get_init_data_type() only exists with this option on, and the whole point of
these cases is the multiple bin, so a build without it simply carries no test case. */
#if CONFIG_ESP_PHY_MULTIPLE_INIT_DATA_BIN
#if CONFIG_ESP_PHY_MULTIPLE_INIT_DATA_BIN_EMBED
extern const uint8_t multi_phy_init_data_bin_start[] asm("_binary_phy_multiple_init_data_bin_start");
extern const uint8_t multi_phy_init_data_bin_end[] asm("_binary_phy_multiple_init_data_bin_end");
#else
#include "esp_partition.h"
#endif
/* phy_multiple_init_data.bin layout:
* [magic][default init data][magic][control info][certified init data] * number
*/
#define PHY_INIT_DATA_LEN (sizeof(esp_phy_init_data_t))
#define CONTROL_INFO_OFFSET (PHY_INIT_MAGIC_LEN + PHY_INIT_DATA_LEN + PHY_INIT_MAGIC_LEN)
#define MULTIPLE_DATA_OFFSET (CONTROL_INFO_OFFSET + sizeof(phy_control_info_data_t))
static uint8_t *multiple_init_data_load(size_t *out_len)
{
uint8_t *blob = NULL;
size_t len = 0;
#if CONFIG_ESP_PHY_MULTIPLE_INIT_DATA_BIN_EMBED
len = (size_t)(multi_phy_init_data_bin_end - multi_phy_init_data_bin_start);
blob = malloc(len);
TEST_ASSERT_NOT_NULL(blob);
memcpy(blob, multi_phy_init_data_bin_start, len);
#else
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_PHY, NULL);
TEST_ASSERT_NOT_NULL_MESSAGE(partition, "no phy data partition in the partition table");
len = partition->size;
blob = malloc(len);
TEST_ASSERT_NOT_NULL(blob);
TEST_ESP_OK(esp_partition_read(partition, 0, blob, len));
#endif
*out_len = len;
return blob;
}
/* The checksums in the bin are stored big-endian, see phy_crc_check_init_data() */
static bool crc32_matches(const uint8_t *data, size_t len, const uint8_t *expected)
{
uint32_t crc = esp_rom_crc32_le(0, data, len);
const uint8_t crc_be[4] = { crc >> 24, crc >> 16, crc >> 8, crc };
return memcmp(crc_be, expected, sizeof(crc_be)) == 0;
}
static void control_info_get(const uint8_t *blob, phy_control_info_data_t *out_info)
{
memcpy(out_info, blob + CONTROL_INFO_OFFSET, sizeof(*out_info));
}
static void wifi_start(void)
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
TEST_ESP_OK(nvs_flash_erase());
err = nvs_flash_init();
}
TEST_ESP_OK(err);
TEST_ESP_OK(esp_event_loop_create_default());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
TEST_ESP_OK(esp_wifi_init(&cfg));
TEST_ESP_OK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
TEST_ESP_OK(esp_wifi_set_mode(WIFI_MODE_STA));
TEST_ESP_OK(esp_wifi_start());
}
static void wifi_stop(void)
{
TEST_ESP_OK(esp_wifi_stop());
TEST_ESP_OK(esp_wifi_deinit());
TEST_ESP_OK(esp_event_loop_delete_default());
TEST_ESP_OK(nvs_flash_deinit());
}
TEST_CASE("multiple PHY init data bin matches the PHY init data of this target", "[phy_multiple_init_data]")
{
size_t blob_len = 0;
uint8_t *blob = multiple_init_data_load(&blob_len);
TEST_ASSERT_GREATER_OR_EQUAL_UINT32(MULTIPLE_DATA_OFFSET, blob_len);
TEST_ASSERT_EQUAL_MEMORY_MESSAGE(PHY_INIT_MAGIC, blob, PHY_INIT_MAGIC_LEN, "leading magic");
TEST_ASSERT_EQUAL_MEMORY_MESSAGE(PHY_INIT_MAGIC, blob + CONTROL_INFO_OFFSET - PHY_INIT_MAGIC_LEN,
PHY_INIT_MAGIC_LEN, "trailing magic");
/* Without this flag esp_phy_update_country_info() rejects every country code */
TEST_ASSERT_NOT_EQUAL_MESSAGE(0, blob[PHY_INIT_MAGIC_LEN + PHY_SUPPORT_MULTIPLE_BIN_OFFSET],
"bin is not marked as supporting multiple init data");
phy_control_info_data_t info;
control_info_get(blob, &info);
TEST_ASSERT_EQUAL(PHY_CRC_ALGORITHM, info.check_algorithm);
TEST_ASSERT_GREATER_THAN(0, info.number);
/* Big-endian per-entry length, a mismatch means the bin was built for another PHY */
TEST_ASSERT_EQUAL_MESSAGE(PHY_INIT_DATA_LEN, ((uint16_t)info.length[0] << 8) | info.length[1],
"per-entry length differs from sizeof(esp_phy_init_data_t)");
TEST_ASSERT_TRUE_MESSAGE(crc32_matches(info.multiple_bin_checksum,
sizeof(info) - sizeof(info.control_info_checksum),
info.control_info_checksum),
"control info checksum mismatch");
size_t multiple_data_len = PHY_INIT_DATA_LEN * info.number;
TEST_ASSERT_GREATER_OR_EQUAL_UINT32(MULTIPLE_DATA_OFFSET + multiple_data_len, blob_len);
TEST_ASSERT_TRUE_MESSAGE(crc32_matches(blob + MULTIPLE_DATA_OFFSET, multiple_data_len,
info.multiple_bin_checksum),
"certified init data checksum mismatch");
free(blob);
}
TEST_CASE("certified PHY init data types are known and unique", "[phy_multiple_init_data]")
{
size_t blob_len = 0;
uint8_t *blob = multiple_init_data_load(&blob_len);
phy_control_info_data_t info;
control_info_get(blob, &info);
/* A bin may carry certifications that phy_init_data_type_t does not name yet,
so the type byte is only bounded by its own width */
bool seen[256] = { false };
unsigned unnamed = 0;
for (uint8_t i = 0; i < info.number; i++) {
uint8_t type = blob[MULTIPLE_DATA_OFFSET + i * PHY_INIT_DATA_LEN + PHY_INIT_DATA_TYPE_OFFSET];
/* The DEFAULT type is the block in front of the control info, not a certified entry */
TEST_ASSERT_NOT_EQUAL(ESP_PHY_INIT_DATA_TYPE_DEFAULT, type);
TEST_ASSERT_FALSE_MESSAGE(seen[type], "duplicated init data type");
seen[type] = true;
if (type >= ESP_PHY_INIT_DATA_TYPE_NUMBER) {
unnamed++;
}
}
printf("%u certified entries, %u of them unnamed by phy_init_data_type_t\n", info.number, unnamed);
/* The country codes exercised by this app have to be certified in the bin */
static const phy_init_data_type_t required[] = {
ESP_PHY_INIT_DATA_TYPE_SRRC,
ESP_PHY_INIT_DATA_TYPE_FCC,
ESP_PHY_INIT_DATA_TYPE_CE,
ESP_PHY_INIT_DATA_TYPE_NCC,
ESP_PHY_INIT_DATA_TYPE_KCC,
ESP_PHY_INIT_DATA_TYPE_MIC,
};
for (size_t i = 0; i < sizeof(required) / sizeof(required[0]); i++) {
TEST_ASSERT_TRUE_MESSAGE(seen[required[i]], "a country code tested below is not certified");
}
free(blob);
}
TEST_CASE("esp_phy_get_init_data loads the multiple PHY init data bin", "[phy_multiple_init_data]")
{
const esp_phy_init_data_t *init_data = esp_phy_get_init_data();
TEST_ASSERT_NOT_NULL(init_data);
esp_phy_release_init_data(init_data);
}
TEST_CASE("country code selects the certified PHY init data", "[phy_multiple_init_data]")
{
static const struct {
const char *country;
phy_init_data_type_t type;
} certified[] = {
{ "CN", ESP_PHY_INIT_DATA_TYPE_SRRC },
{ "US", ESP_PHY_INIT_DATA_TYPE_FCC },
{ "DE", ESP_PHY_INIT_DATA_TYPE_CE },
{ "TW", ESP_PHY_INIT_DATA_TYPE_NCC },
{ "KR", ESP_PHY_INIT_DATA_TYPE_KCC },
{ "JP", ESP_PHY_INIT_DATA_TYPE_MIC },
};
wifi_start();
for (size_t i = 0; i < sizeof(certified) / sizeof(certified[0]); i++) {
TEST_ESP_OK(esp_phy_update_country_info(certified[i].country));
TEST_ASSERT_EQUAL_MESSAGE(certified[i].type, esp_phy_get_init_data_type(), certified[i].country);
}
/* Re-applying the same country keeps the PHY on the same init data */
TEST_ESP_OK(esp_phy_update_country_info("JP"));
TEST_ASSERT_EQUAL(ESP_PHY_INIT_DATA_TYPE_MIC, esp_phy_get_init_data_type());
wifi_stop();
}
TEST_CASE("uncertified country codes fall back to the default PHY init data", "[phy_multiple_init_data]")
{
wifi_start();
TEST_ESP_OK(esp_phy_update_country_info("CN"));
TEST_ASSERT_EQUAL(ESP_PHY_INIT_DATA_TYPE_SRRC, esp_phy_get_init_data_type());
/* AU maps to ACMA, for which the bin carries no certified init data */
TEST_ESP_OK(esp_phy_update_country_info("AU"));
TEST_ASSERT_EQUAL(ESP_PHY_INIT_DATA_TYPE_DEFAULT, esp_phy_get_init_data_type());
/* An unmapped country code is answered with the default init data as well */
TEST_ESP_OK(esp_phy_update_country_info("ZZ"));
TEST_ASSERT_EQUAL(ESP_PHY_INIT_DATA_TYPE_DEFAULT, esp_phy_get_init_data_type());
wifi_stop();
}
TEST_CASE("esp_wifi_set_country_code updates the PHY init data", "[phy_multiple_init_data]")
{
wifi_start();
TEST_ESP_OK(esp_wifi_set_country_code("JP", false));
/* The Wi-Fi task drives the PHY update, so the type may lag the API call */
for (int i = 0; i < 100 && esp_phy_get_init_data_type() != ESP_PHY_INIT_DATA_TYPE_MIC; i++) {
vTaskDelay(pdMS_TO_TICKS(10));
}
TEST_ASSERT_EQUAL(ESP_PHY_INIT_DATA_TYPE_MIC, esp_phy_get_init_data_type());
/* wifi_country_t.cc is 3 octets (ISO code + environment), not a C string */
char country_code[3] = { 0 };
TEST_ESP_OK(esp_wifi_get_country_code(country_code));
TEST_ASSERT_EQUAL('J', country_code[0]);
TEST_ASSERT_EQUAL('P', country_code[1]);
wifi_stop();
}
#endif // CONFIG_ESP_PHY_MULTIPLE_INIT_DATA_BIN

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded_idf.dut import IdfDut
@@ -14,10 +14,17 @@ from pytest_embedded_idf.utils import idf_parametrize
],
indirect=True,
)
@idf_parametrize('target', ['esp32', 'esp32c2', 'esp32c3', 'esp32c6', 'esp32s2', 'esp32s3'], indirect=['target'])
@idf_parametrize(
'target',
['esp32', 'esp32c2', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32c61', 'esp32s2', 'esp32s3'],
indirect=['target'],
)
def test_phy_multi_init_data_bin(dut: IdfDut, config: str) -> None:
if config == 'phy_multiple_init_data':
dut.expect_exact('Support multiple PHY init data bins')
else:
# Where the bin was loaded from is only visible in the log, and app_main loads it
# once before handing over to the unity runner.
if config == 'phy_multiple_init_data_embed':
dut.expect_exact('loading embedded multiple PHY init data')
dut.expect_exact('wifi_init finished')
dut.expect_exact('Support multiple PHY init data bins')
# each case switches the PHY init data type, so start every one of them from a fresh boot
dut.run_all_single_board_cases(reset=True)

View File

@@ -0,0 +1,2 @@
# ignore task watchdog triggered by unity_run_menu
CONFIG_ESP_TASK_WDT_INIT=n