mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
feat(storage/blockdev): Add memory mapping device driver
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES esp_blockdev)
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_blockdev.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create a memory-backed block device that wraps an existing buffer
|
||||
*
|
||||
* @param buffer Pointer to the storage backing the device
|
||||
* @param buffer_size Size of the storage buffer in bytes
|
||||
* @param geometry Desired geometry for the device; geometry->disk_size must not exceed buffer_size
|
||||
* @param read_only Set true to disallow write/erase operations on the mapped memory
|
||||
* @param out Output pointer that receives the created block device handle; unchanged on failure
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG Invalid argument provided
|
||||
* @return ESP_ERR_INVALID_SIZE Provided buffer or geometry is incompatible
|
||||
* @return ESP_ERR_NO_MEM Memory allocation for device descriptor failed
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_blockdev_memory_get_from_buffer(uint8_t *buffer, size_t buffer_size, const esp_blockdev_geometry_t *geometry, bool read_only, esp_blockdev_handle_t *out);
|
||||
|
||||
/**
|
||||
* @brief Create a memory-backed block device with internally allocated storage
|
||||
*
|
||||
* @param geometry Desired geometry for the device; geometry->disk_size determines the allocation size
|
||||
* @param caps Heap capability flags passed to heap_caps_malloc
|
||||
* @param out Output pointer that receives the created block device handle; unchanged on failure
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG Invalid argument provided
|
||||
* @return ESP_ERR_INVALID_SIZE Allocation size exceeds supported limits
|
||||
* @return ESP_ERR_NO_MEM Memory allocation failed
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_blockdev_memory_create_with_heap_caps(const esp_blockdev_geometry_t *geometry, uint32_t caps, esp_blockdev_handle_t *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
#include "esp_blockdev.h"
|
||||
#include "esp_blockdev/memory.h"
|
||||
|
||||
static const char *TAG = "esp_blockdev/memory";
|
||||
|
||||
typedef struct {
|
||||
esp_blockdev_t dev;
|
||||
uint8_t *buffer;
|
||||
size_t capacity;
|
||||
bool owns_buffer;
|
||||
} esp_blockdev_memory_t;
|
||||
|
||||
static esp_err_t bd_memory_read(esp_blockdev_handle_t dev_handle, uint8_t *dst_buf, size_t dst_buf_size, uint64_t src_addr, size_t data_read_len)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "The dev_handle cannot be NULL");
|
||||
ESP_RETURN_ON_FALSE(dst_buf != NULL, ESP_ERR_INVALID_ARG, TAG, "The destination buffer cannot be NULL");
|
||||
ESP_RETURN_ON_FALSE(data_read_len <= dst_buf_size, ESP_ERR_INVALID_SIZE, TAG, "Destination buffer too small");
|
||||
ESP_RETURN_ON_FALSE(src_addr + data_read_len <= dev_handle->geometry.disk_size, ESP_ERR_INVALID_ARG, TAG, "The address range falls outside of the disk");
|
||||
|
||||
esp_blockdev_memory_t *dev = (esp_blockdev_memory_t *)dev_handle;
|
||||
size_t offset = (size_t)src_addr;
|
||||
|
||||
memcpy(dst_buf, dev->buffer + offset, data_read_len);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t bd_memory_write(esp_blockdev_handle_t dev_handle, const uint8_t* src_buf, uint64_t dst_addr, size_t data_write_len)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "The dev_handle cannot be NULL");
|
||||
ESP_RETURN_ON_FALSE(src_buf != NULL, ESP_ERR_INVALID_ARG, TAG, "The source buffer cannot be NULL");
|
||||
ESP_RETURN_ON_FALSE(!dev_handle->device_flags.read_only, ESP_ERR_INVALID_STATE, TAG, "The device is read-only");
|
||||
ESP_RETURN_ON_FALSE(dst_addr + data_write_len <= dev_handle->geometry.disk_size, ESP_ERR_INVALID_ARG, TAG, "The address range falls outside of the disk");
|
||||
|
||||
esp_blockdev_memory_t *dev = (esp_blockdev_memory_t *)dev_handle;
|
||||
|
||||
size_t offset = (size_t)dst_addr;
|
||||
|
||||
memcpy(dev->buffer + offset, src_buf, data_write_len);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t bd_memory_erase(esp_blockdev_handle_t dev_handle, uint64_t start_addr, size_t erase_len)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "The dev_handle cannot be NULL");
|
||||
ESP_RETURN_ON_FALSE(!dev_handle->device_flags.read_only, ESP_ERR_INVALID_STATE, TAG, "The device is read-only");
|
||||
ESP_RETURN_ON_FALSE(start_addr + erase_len <= dev_handle->geometry.disk_size, ESP_ERR_INVALID_ARG, TAG, "The address range falls outside of the disk");
|
||||
|
||||
esp_blockdev_memory_t *dev = (esp_blockdev_memory_t *)dev_handle;
|
||||
|
||||
size_t offset = (size_t)start_addr;
|
||||
uint8_t erase_value = dev_handle->device_flags.default_val_after_erase ? 0xFF : 0;
|
||||
|
||||
memset(dev->buffer + offset, erase_value, erase_len);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t bd_memory_sync(esp_blockdev_handle_t dev_handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "The dev_handle cannot be NULL");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t bd_memory_ioctl(esp_blockdev_handle_t dev_handle, const uint8_t cmd, void *args)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "The dev_handle cannot be NULL");
|
||||
(void)cmd;
|
||||
(void)args;
|
||||
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static esp_err_t bd_memory_release(esp_blockdev_handle_t dev_handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "The dev_handle cannot be NULL");
|
||||
|
||||
esp_blockdev_memory_t *dev = (esp_blockdev_memory_t *)dev_handle;
|
||||
|
||||
if (dev->owns_buffer && dev->buffer != NULL) {
|
||||
heap_caps_free(dev->buffer);
|
||||
}
|
||||
|
||||
free(dev);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const esp_blockdev_ops_t g_memory_blockdev_ops = {
|
||||
.read = bd_memory_read,
|
||||
.write = bd_memory_write,
|
||||
.erase = bd_memory_erase,
|
||||
.sync = bd_memory_sync,
|
||||
.ioctl = bd_memory_ioctl,
|
||||
.release = bd_memory_release,
|
||||
};
|
||||
|
||||
static esp_err_t esp_blockdev_memory_create_internal(uint8_t *buffer, size_t buffer_size, const esp_blockdev_geometry_t *geometry, bool owns_buffer, bool read_only, esp_blockdev_handle_t *out)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(out != NULL, ESP_ERR_INVALID_ARG, TAG, "The out pointer cannot be NULL");
|
||||
*out = ESP_BLOCKDEV_HANDLE_INVALID;
|
||||
ESP_RETURN_ON_FALSE(buffer != NULL, ESP_ERR_INVALID_ARG, TAG, "The buffer cannot be NULL");
|
||||
ESP_RETURN_ON_FALSE(geometry != NULL, ESP_ERR_INVALID_ARG, TAG, "Geometry pointer cannot be NULL");
|
||||
ESP_RETURN_ON_FALSE(geometry->disk_size > 0, ESP_ERR_INVALID_ARG, TAG, "Disk size must be greater than zero");
|
||||
ESP_RETURN_ON_FALSE(geometry->disk_size <= SIZE_MAX, ESP_ERR_INVALID_SIZE, TAG, "Disk size exceeds addressable memory span");
|
||||
ESP_RETURN_ON_FALSE(geometry->disk_size <= buffer_size, ESP_ERR_INVALID_SIZE, TAG, "Buffer smaller than requested disk size");
|
||||
|
||||
esp_blockdev_memory_t *dev = calloc(1, sizeof(esp_blockdev_memory_t));
|
||||
ESP_RETURN_ON_FALSE(dev != NULL, ESP_ERR_NO_MEM, TAG, "Failed to allocate device structure");
|
||||
|
||||
*dev = (esp_blockdev_memory_t) {
|
||||
.dev = {
|
||||
.ctx = NULL,
|
||||
.device_flags = {
|
||||
.read_only = read_only,
|
||||
.encrypted = false,
|
||||
.erase_before_write = 0,
|
||||
.and_type_write = 0,
|
||||
.default_val_after_erase = false,
|
||||
},
|
||||
.geometry = *geometry,
|
||||
.ops = &g_memory_blockdev_ops,
|
||||
},
|
||||
.buffer = buffer,
|
||||
.capacity = buffer_size,
|
||||
.owns_buffer = owns_buffer,
|
||||
};
|
||||
|
||||
*out = (esp_blockdev_handle_t)dev;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_blockdev_memory_get_from_buffer(uint8_t *buffer, size_t buffer_size, const esp_blockdev_geometry_t *geometry, bool read_only, esp_blockdev_handle_t *out)
|
||||
{
|
||||
return esp_blockdev_memory_create_internal(buffer, buffer_size, geometry, false, read_only, out);
|
||||
}
|
||||
|
||||
esp_err_t esp_blockdev_memory_create_with_heap_caps(const esp_blockdev_geometry_t *geometry, uint32_t caps, esp_blockdev_handle_t *out)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(geometry != NULL, ESP_ERR_INVALID_ARG, TAG, "Geometry pointer cannot be NULL");
|
||||
|
||||
size_t alloc_size = (size_t)geometry->disk_size;
|
||||
uint8_t *buffer = heap_caps_malloc(alloc_size, caps);
|
||||
ESP_RETURN_ON_FALSE(buffer != NULL, ESP_ERR_NO_MEM, TAG, "Failed to allocate device buffer");
|
||||
|
||||
memset(buffer, 0x00, alloc_size);
|
||||
|
||||
esp_err_t err = esp_blockdev_memory_create_internal(buffer, alloc_size, geometry, true, false, out);
|
||||
if (err != ESP_OK) {
|
||||
heap_caps_free(buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||
|
||||
components/esp_blockdev_util/test_apps/memory_blockdev:
|
||||
enable:
|
||||
- if: INCLUDE_DEFAULT == 1 or IDF_TARGET == "linux"
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32", "esp32c3", "linux"]
|
||||
temporary: true
|
||||
reason: cover Xtensa and RISC-V targets
|
||||
@@ -0,0 +1,11 @@
|
||||
# This is the project CMakeLists.txt file for the memory blockdev test application
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"$ENV{IDF_PATH}/tools/test_apps/components"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../../"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../../../esp_blockdev")
|
||||
|
||||
set(COMPONENTS main)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(memory_blockdev_test)
|
||||
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | Linux |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | ----- |
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "test_memory_blockdev.c"
|
||||
PRIV_REQUIRES unity esp_blockdev esp_blockdev_util)
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
|
||||
#include "esp_blockdev.h"
|
||||
#include "esp_blockdev/memory.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
TEST_CASE("memory blockdev basic operations", "[memory_blockdev]")
|
||||
{
|
||||
uint8_t backing[128];
|
||||
memset(backing, 0x5A, sizeof(backing));
|
||||
|
||||
const esp_blockdev_geometry_t geometry = {
|
||||
.disk_size = sizeof(backing),
|
||||
.read_size = 1,
|
||||
.write_size = 1,
|
||||
.erase_size = 1,
|
||||
.recommended_write_size = 16,
|
||||
.recommended_read_size = 16,
|
||||
.recommended_erase_size = 32,
|
||||
};
|
||||
|
||||
esp_blockdev_handle_t dev = NULL;
|
||||
TEST_ESP_OK(esp_blockdev_memory_get_from_buffer(backing, sizeof(backing), &geometry, false, &dev));
|
||||
TEST_ASSERT_NOT_NULL(dev);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT64(geometry.disk_size, dev->geometry.disk_size);
|
||||
TEST_ASSERT_EQUAL_UINT32(geometry.read_size, dev->geometry.read_size);
|
||||
TEST_ASSERT_EQUAL_UINT32(geometry.write_size, dev->geometry.write_size);
|
||||
TEST_ASSERT_EQUAL_UINT32(geometry.erase_size, dev->geometry.erase_size);
|
||||
TEST_ASSERT_FALSE(dev->device_flags.read_only);
|
||||
TEST_ASSERT_FALSE(dev->device_flags.default_val_after_erase);
|
||||
|
||||
uint8_t write_data[32];
|
||||
for (size_t i = 0; i < sizeof(write_data); ++i) {
|
||||
write_data[i] = (uint8_t)(i + 1);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(dev->ops->write(dev, write_data, 48, sizeof(write_data)));
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_data, backing + 48, sizeof(write_data));
|
||||
|
||||
uint8_t read_buf[sizeof(write_data)];
|
||||
memset(read_buf, 0, sizeof(read_buf));
|
||||
TEST_ESP_OK(dev->ops->read(dev, read_buf, sizeof(read_buf), 48, sizeof(read_buf)));
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_data, read_buf, sizeof(write_data));
|
||||
|
||||
TEST_ESP_OK(dev->ops->erase(dev, 48, sizeof(write_data)));
|
||||
TEST_ASSERT_EACH_EQUAL_UINT8(0x00, backing + 48, sizeof(write_data));
|
||||
|
||||
TEST_ESP_OK(dev->ops->sync(dev));
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, dev->ops->ioctl(dev, ESP_BLOCKDEV_CMD_USER_BASE, NULL));
|
||||
|
||||
TEST_ESP_OK(dev->ops->release(dev));
|
||||
}
|
||||
|
||||
TEST_CASE("memory blockdev enforces read-only mappings", "[memory_blockdev]")
|
||||
{
|
||||
uint8_t backing[64];
|
||||
for (size_t i = 0; i < sizeof(backing); ++i) {
|
||||
backing[i] = (uint8_t)(i ^ 0xAA);
|
||||
}
|
||||
|
||||
const esp_blockdev_geometry_t ro_geometry = {
|
||||
.disk_size = sizeof(backing),
|
||||
.read_size = 1,
|
||||
.write_size = 0,
|
||||
.erase_size = 0,
|
||||
.recommended_write_size = 0,
|
||||
.recommended_read_size = 8,
|
||||
.recommended_erase_size = 0,
|
||||
};
|
||||
|
||||
esp_blockdev_handle_t dev = NULL;
|
||||
TEST_ESP_OK(esp_blockdev_memory_get_from_buffer(backing, sizeof(backing), &ro_geometry, true, &dev));
|
||||
TEST_ASSERT_NOT_NULL(dev);
|
||||
TEST_ASSERT_TRUE(dev->device_flags.read_only);
|
||||
|
||||
uint8_t read_buf[16];
|
||||
TEST_ESP_OK(dev->ops->read(dev, read_buf, sizeof(read_buf), 0, sizeof(read_buf)));
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(backing, read_buf, sizeof(read_buf));
|
||||
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, dev->ops->write(dev, read_buf, 0, sizeof(read_buf)));
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, dev->ops->erase(dev, 0, sizeof(read_buf)));
|
||||
|
||||
TEST_ESP_OK(dev->ops->release(dev));
|
||||
|
||||
/* Release must not take ownership of external buffers */
|
||||
const uint8_t expected_prefix[] = {0xAA, 0xAB, 0xA8, 0xA9};
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_prefix, backing, sizeof(expected_prefix));
|
||||
}
|
||||
|
||||
TEST_CASE("memory blockdev validates arguments", "[memory_blockdev]")
|
||||
{
|
||||
uint8_t backing[32];
|
||||
memset(backing, 0x11, sizeof(backing));
|
||||
|
||||
esp_blockdev_geometry_t geometry = {
|
||||
.disk_size = sizeof(backing),
|
||||
.read_size = 1,
|
||||
.write_size = 1,
|
||||
.erase_size = 1,
|
||||
.recommended_write_size = 0,
|
||||
.recommended_read_size = 0,
|
||||
.recommended_erase_size = 0,
|
||||
};
|
||||
|
||||
esp_blockdev_handle_t dev = (esp_blockdev_handle_t)0xDEADBEEF;
|
||||
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_blockdev_memory_get_from_buffer(NULL, sizeof(backing), &geometry, false, &dev));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, dev);
|
||||
|
||||
dev = (esp_blockdev_handle_t)0xDEADBEEF;
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_blockdev_memory_get_from_buffer(backing, sizeof(backing), NULL, false, &dev));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, dev);
|
||||
|
||||
geometry.disk_size = sizeof(backing) + 1;
|
||||
dev = (esp_blockdev_handle_t)0xDEADBEEF;
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_blockdev_memory_get_from_buffer(backing, sizeof(backing), &geometry, false, &dev));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, dev);
|
||||
|
||||
geometry.disk_size = 0;
|
||||
dev = (esp_blockdev_handle_t)0xDEADBEEF;
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_blockdev_memory_get_from_buffer(backing, sizeof(backing), &geometry, false, &dev));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, dev);
|
||||
|
||||
geometry.disk_size = sizeof(backing);
|
||||
esp_blockdev_handle_t heap_dev = NULL;
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_blockdev_memory_create_with_heap_caps(NULL, MALLOC_CAP_8BIT, &heap_dev));
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_blockdev_memory_create_with_heap_caps(&geometry, MALLOC_CAP_8BIT, NULL));
|
||||
|
||||
/* Successful create for subsequent bounds checks */
|
||||
TEST_ESP_OK(esp_blockdev_memory_get_from_buffer(backing, sizeof(backing), &geometry, false, &dev));
|
||||
TEST_ASSERT_NOT_NULL(dev);
|
||||
|
||||
uint8_t read_buf[16];
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, dev->ops->read(dev, read_buf, sizeof(read_buf) - 1, 0, sizeof(read_buf)));
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, dev->ops->read(dev, read_buf, sizeof(read_buf), sizeof(backing) - 4, sizeof(read_buf)));
|
||||
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, dev->ops->write(dev, read_buf, sizeof(backing) - 8, sizeof(read_buf) + 1));
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, dev->ops->erase(dev, sizeof(backing) - 4, sizeof(read_buf)));
|
||||
|
||||
TEST_ESP_OK(dev->ops->release(dev));
|
||||
}
|
||||
|
||||
TEST_CASE("memory blockdev heap allocation creates zeroed storage", "[memory_blockdev]")
|
||||
{
|
||||
const esp_blockdev_geometry_t geometry = {
|
||||
.disk_size = 64,
|
||||
.read_size = 1,
|
||||
.write_size = 1,
|
||||
.erase_size = 1,
|
||||
.recommended_write_size = 0,
|
||||
.recommended_read_size = 0,
|
||||
.recommended_erase_size = 0,
|
||||
};
|
||||
|
||||
esp_blockdev_handle_t dev = NULL;
|
||||
TEST_ESP_OK(esp_blockdev_memory_create_with_heap_caps(&geometry, MALLOC_CAP_8BIT, &dev));
|
||||
TEST_ASSERT_NOT_NULL(dev);
|
||||
TEST_ASSERT_FALSE(dev->device_flags.read_only);
|
||||
TEST_ASSERT_FALSE(dev->device_flags.default_val_after_erase);
|
||||
|
||||
uint8_t read_buf[64];
|
||||
TEST_ESP_OK(dev->ops->read(dev, read_buf, sizeof(read_buf), 0, sizeof(read_buf)));
|
||||
TEST_ASSERT_EACH_EQUAL_UINT8(0x00, read_buf, sizeof(read_buf));
|
||||
|
||||
uint8_t pattern[8];
|
||||
memset(pattern, 0xCD, sizeof(pattern));
|
||||
|
||||
TEST_ESP_OK(dev->ops->write(dev, pattern, 4, sizeof(pattern)));
|
||||
TEST_ESP_OK(dev->ops->read(dev, read_buf, sizeof(pattern), 4, sizeof(pattern)));
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(pattern, read_buf, sizeof(pattern));
|
||||
|
||||
TEST_ESP_OK(dev->ops->release(dev));
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['esp32', 'esp32c3', 'linux'], indirect=['target'])
|
||||
def test_blockdev_memory_device(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_UNITY_ENABLE_64BIT=y
|
||||
Reference in New Issue
Block a user