Skip to content
Merged
5 changes: 5 additions & 0 deletions components/esp_driver_i2s/i2s_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,11 @@ uint32_t i2s_get_source_clk_freq(i2s_clock_src_t clk_src, uint32_t mclk_freq_hz)
if (clk_src == I2S_CLK_SRC_APLL) {
return i2s_set_get_apll_freq(mclk_freq_hz);
}
#endif
#ifdef I2S_LL_DEFAULT_CLK_SRC
if (clk_src == I2S_CLK_SRC_DEFAULT) {
clk_src = I2S_LL_DEFAULT_CLK_SRC;
}
#endif
esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_freq);
return clk_freq;
Expand Down
8 changes: 4 additions & 4 deletions components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ TEST_CASE("I2S_loopback_test", "[i2s]")
TEST_ESP_OK(i2s_del_channel(rx_handle));
}

#if SOC_I2S_NUM > 1
#if SOC_I2S_NUM > 1 && !CONFIG_IDF_TARGET_ESP32P4
TEST_CASE("I2S_master_write_slave_read_test", "[i2s]")
{
i2s_chan_handle_t tx_handle;
Expand Down Expand Up @@ -798,10 +798,10 @@ TEST_CASE("I2S_default_PLL_clock_test", "[i2s]")
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));

// ESP32-P4 has no PLL except XTAL
#if !CONFIG_IDF_TARGET_ESP32P4
#ifdef I2S_LL_DEFAULT_CLK_SRC
std_cfg.clk_cfg.clk_src = I2S_LL_DEFAULT_CLK_SRC;
#endif
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
#endif // CONFIG_IDF_TARGET_ESP32P4
#if SOC_I2S_SUPPORTS_XTAL
std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_XTAL;
i2s_test_common_sample_rate(rx_handle, &std_cfg.clk_cfg);
Expand Down
21 changes: 19 additions & 2 deletions components/hal/esp32p4/include/hal/i2s_ll.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -23,6 +23,7 @@
#include "soc/soc_etm_source.h"
#include "hal/i2s_types.h"
#include "hal/hal_utils.h"
#include "hal/config.h"


#ifdef __cplusplus
Expand All @@ -41,7 +42,14 @@ extern "C" {
#define I2S_LL_SLOT_FRAME_BIT_MAX 512 // Up-to 512 bits in one frame, determined by MAX(half_sample_bits) * 2

#define I2S_LL_XTAL_CLK_FREQ (40 * 1000000) // XTAL_CLK: 40MHz
#define I2S_LL_DEFAULT_CLK_FREQ I2S_LL_XTAL_CLK_FREQ // No PLL clock source on P4, use XTAL as default
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300
#define I2S_LL_DEFAULT_CLK_FREQ (160 * 1000000) // PLL_F160M_CLK: 160MHz
#define I2S_LL_DEFAULT_CLK_SRC I2S_CLK_SRC_PLL_160M
#else
#define I2S_LL_DEFAULT_CLK_FREQ I2S_LL_XTAL_CLK_FREQ // No PLL clock source before version 3, use XTAL as default
#define I2S_LL_DEFAULT_CLK_SRC I2S_CLK_SRC_XTAL
#endif


#define I2S_LL_ETM_EVENT_TABLE(i2s_port, chan_dir, event) \
(uint32_t[SOC_I2S_NUM][2][I2S_ETM_EVENT_MAX]){ \
Expand Down Expand Up @@ -414,6 +422,15 @@ static inline uint32_t i2s_ll_get_clk_src(i2s_clock_src_t src)
return 1;
case I2S_CLK_SRC_EXTERNAL:
return 2;
case I2S_CLK_SRC_DEFAULT:
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300
return 3;
// Only support PLL_160M on P4 ver3 and later
case I2S_CLK_SRC_PLL_160M:
return 3;
#else
return 0;
#endif
default:
HAL_ASSERT(false && "unsupported clock source");
return -1;
Expand Down
78 changes: 77 additions & 1 deletion components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -3806,6 +3806,82 @@ TEST_CASE("nvs multiple write with same key but different types", "[nvs]")
TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
}

TEST_CASE("nvs multiple write with same key blob and string involved", "[nvs]")
{
PartitionEmulationFixture f(0, 10);

nvs_handle_t handle_1;
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);)

for (uint16_t j = NVS_FLASH_SECTOR; j < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++j) {
f.erase(j);
}
TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(),
NVS_FLASH_SECTOR,
NVS_FLASH_SECTOR_COUNT_MIN));

TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1));

nvs_erase_all(handle_1);

const char key_name[] = "foo";

// integer variables
int32_t v32;
int8_t v8;

// string
#define str_data_len 64
const char str_data[] = "string data";
char str_buf[str_data_len] = {0};
size_t str_len = str_data_len;

// blob
#define blob_data_len 64
uint8_t blob_data[blob_data_len] = {0};
uint8_t blob_buf[blob_data_len] = {0};
size_t blob_read_size;

// first write is i32
TEST_ESP_OK(nvs_set_i32(handle_1, key_name, (int32_t)12345678));

TEST_ESP_ERR(nvs_get_i8(handle_1, key_name, &v8), ESP_ERR_NVS_NOT_FOUND);
TEST_ESP_OK(nvs_get_i32(handle_1, key_name, &v32));
TEST_ESP_ERR(nvs_get_str(handle_1, key_name, str_buf, &str_len), ESP_ERR_NVS_NOT_FOUND);
TEST_ESP_ERR(nvs_get_blob(handle_1, key_name, blob_buf, &blob_read_size), ESP_ERR_NVS_NOT_FOUND);


// second write is string
TEST_ESP_OK(nvs_set_str(handle_1, key_name, str_data));

TEST_ESP_ERR(nvs_get_i8(handle_1, key_name, &v8), ESP_ERR_NVS_NOT_FOUND);
TEST_ESP_ERR(nvs_get_i32(handle_1, key_name, &v32), ESP_ERR_NVS_NOT_FOUND);
TEST_ESP_OK(nvs_get_str(handle_1, key_name, str_buf, &str_len));
TEST_ESP_ERR(nvs_get_blob(handle_1, key_name, blob_buf, &blob_read_size), ESP_ERR_NVS_NOT_FOUND);

// third write is blob
TEST_ESP_OK(nvs_set_blob(handle_1, key_name, blob_data, blob_data_len));

TEST_ESP_ERR(nvs_get_i8(handle_1, key_name, &v8), ESP_ERR_NVS_NOT_FOUND);
TEST_ESP_ERR(nvs_get_i32(handle_1, key_name, &v32), ESP_ERR_NVS_NOT_FOUND);
TEST_ESP_ERR(nvs_get_str(handle_1, key_name, str_buf, &str_len), ESP_ERR_NVS_NOT_FOUND);
TEST_ESP_OK(nvs_get_blob(handle_1, key_name, blob_buf, &blob_read_size));

// fourth write is i8
TEST_ESP_OK(nvs_set_i8(handle_1, key_name, (int8_t)12));

TEST_ESP_OK(nvs_get_i8(handle_1, key_name, &v8));
TEST_ESP_ERR(nvs_get_i32(handle_1, key_name, &v32), ESP_ERR_NVS_NOT_FOUND);
TEST_ESP_ERR(nvs_get_str(handle_1, key_name, str_buf, &str_len), ESP_ERR_NVS_NOT_FOUND);
TEST_ESP_ERR(nvs_get_blob(handle_1, key_name, blob_buf, &blob_read_size), ESP_ERR_NVS_NOT_FOUND);

nvs_close(handle_1);

TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
}

TEST_CASE("nvs find key tests", "[nvs]")
{
const size_t buff_len = 4096;
Expand Down
87 changes: 62 additions & 25 deletions components/nvs_flash/src/nvs_page.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,33 +250,19 @@ esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, c
return ESP_OK;
}

esp_err_t Page::readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize, uint8_t chunkIdx, VerOffset chunkStart)
// Reads the data entries of the variable length item.
// The metadata entry is already read in the item object.
// index is the index of the metadata entry on the page.
// data is pointer to the buffer where the data will be copied to. It has to be at least
// item.varLength.dataSize bytes long.
// The function returns ESP_OK if the data was read successfully, or an error code if there was an error.
esp_err_t Page::readVariableLengthItemData(const Item& item, const size_t index, void* data)
{
size_t index = 0;
Item item;

if (mState == PageState::INVALID) {
return ESP_ERR_NVS_INVALID_STATE;
}

esp_err_t rc = findItem(nsIndex, datatype, key, index, item, chunkIdx, chunkStart);
if (rc != ESP_OK) {
return rc;
}

if (!isVariableLengthType(datatype)) {
if (dataSize != getAlignmentForType(datatype)) {
return ESP_ERR_NVS_TYPE_MISMATCH;
}

memcpy(data, item.data, dataSize);
return ESP_OK;
}

if (dataSize < static_cast<size_t>(item.varLength.dataSize)) {
return ESP_ERR_NVS_INVALID_LENGTH;
}

esp_err_t rc;
uint8_t* dst = reinterpret_cast<uint8_t*>(data);
size_t left = item.varLength.dataSize;
for (size_t i = index + 1; i < index + item.span; ++i) {
Expand All @@ -301,6 +287,36 @@ esp_err_t Page::readItem(uint8_t nsIndex, ItemType datatype, const char* key, vo
return ESP_OK;
}

esp_err_t Page::readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize, uint8_t chunkIdx, VerOffset chunkStart)
{
size_t index = 0;
Item item;

if (mState == PageState::INVALID) {
return ESP_ERR_NVS_INVALID_STATE;
}

esp_err_t rc = findItem(nsIndex, datatype, key, index, item, chunkIdx, chunkStart);
if (rc != ESP_OK) {
return rc;
}

if (!isVariableLengthType(datatype)) {
if (dataSize != getAlignmentForType(datatype)) {
return ESP_ERR_NVS_TYPE_MISMATCH;
}

memcpy(data, item.data, dataSize);
return ESP_OK;
}

if (dataSize < static_cast<size_t>(item.varLength.dataSize)) {
return ESP_ERR_NVS_INVALID_LENGTH;
}

return readVariableLengthItemData(item, index, data);
}

esp_err_t Page::cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx, VerOffset chunkStart)
{
size_t index = 0;
Expand Down Expand Up @@ -330,9 +346,23 @@ esp_err_t Page::cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, con
return ESP_ERR_NVS_INVALID_LENGTH;
}

// We have metadata of the variable length data chunk. It contains the length of the data and the crc32.
// As a first step we can calculate the crc32 of the data buffer to be compared with the crc32 of the item in the flash.
// If they are not equal, immediately return ESP_ERR_NVS_CONTENT_DIFFERS.
// If they are equal, to avoid crc32 collision false positive, we will read the data from the flash entry by entry and compare
// it with the respective chunk of input data buffer. The crc32 of the data read from the flash will be calculated on the fly.
// At the end, we will compare the crc32 of the data read from the flash with the crc32 of the metadata item in the flash to make sure
// that the data in the flash is not corrupted.
if (Item::calculateCrc32(reinterpret_cast<const uint8_t * >(data), item.varLength.dataSize) != item.varLength.dataCrc32) {
return ESP_ERR_NVS_CONTENT_DIFFERS;
}

const uint8_t* dst = reinterpret_cast<const uint8_t*>(data);
size_t left = item.varLength.dataSize;
for (size_t i = index + 1; i < index + item.span; ++i) {
uint32_t accumulatedCRC32;
size_t initial_index = index + 1;

for (size_t i = initial_index; i < index + item.span; ++i) {
Item ditem;
rc = readEntry(i, ditem);
if (rc != ESP_OK) {
Expand All @@ -343,11 +373,18 @@ esp_err_t Page::cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, con
if (memcmp(dst, ditem.rawData, willCopy)) {
return ESP_ERR_NVS_CONTENT_DIFFERS;
}

// Calculate the crc32 of the actual ditem.rawData buffer. Do not pass accumulatedCRC32 in the first call.
// In the first call, calculateCrc32 will use its default. In the subsequent calls, accumulatedCRC32 is the crc32 of the previous buffer.
accumulatedCRC32 = Item::calculateCrc32(ditem.rawData, willCopy, (i == initial_index) ? nullptr : &accumulatedCRC32);

left -= willCopy;
dst += willCopy;
}
if (Item::calculateCrc32(reinterpret_cast<const uint8_t * >(data), item.varLength.dataSize) != item.varLength.dataCrc32) {
return ESP_ERR_NVS_NOT_FOUND;
// Check if the CRC32 calculated on the fly matches the variable length data CRC32 indicated in the metadata entry.
// If they are not equal, it means the data in the flash is corrupt, we will return ESP_ERR_NVS_CONTENT_DIFFERS.
if (accumulatedCRC32 != item.varLength.dataCrc32) {
return ESP_ERR_NVS_CONTENT_DIFFERS;
}

return ESP_OK;
Expand Down
2 changes: 2 additions & 0 deletions components/nvs_flash/src/nvs_page.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ class Page : public intrusive_list_node<Page>, public ExceptionlessAllocatable

esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY);

esp_err_t readVariableLengthItemData(const Item& item, const size_t index, void* data);

esp_err_t readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);

esp_err_t cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
Expand Down
Loading
Loading