块设备层
概述
块设备层 (BDL) 定义了一套 C 语言接口,使面向存储的组件可在无需专用适配器的情况下完成数据交换。每个块设备以 esp_blockdev_handle_t 句柄的形式对外提供该接口,通过该句柄可访问设备标志、几何信息以及 components/esp_blockdev/include/esp_blockdev.h 中定义的一组受支持操作。上层代码可读取这些元数据,并调用可用的回调执行 I/O 操作。
统一的接口支持串联多个通用组件,灵活组合成可适配各类存储场景的 BDL 协议栈。驱动程序提供用于访问物理设备的句柄;中间件组件使用该句柄并扩展其能力(如划分设备空间、添加磨损均衡功能),同时向下一层暴露新的句柄。协议栈最顶层的组件(如文件系统)是纯设备使用者。只要每一层均遵循下文约定的接口规范,该模型即可实现文件系统、中间件与物理驱动的任意组合使用。

块设备层堆栈示例
使用块设备
句柄
块设备通过 esp_blockdev_handle_t 进行访问。可以从对应的组件获取该句柄,遵循 <component>_get_blockdev() 的命名约定;当设备不再使用时,必须调用对应的 <component>_release_blockdev() 辅助函数来释放该句柄。注意,应该将句柄视为黑盒对象:只能通过 components/esp_blockdev/include/esp_blockdev.h 中提供的公开 API 来使用,不要移动或修改其所引用的内存。
几何与标志
每个设备会发布一个 esp_blockdev_geometry_t 结构体,用于报告容量以及最小读、写、擦除粒度。可选的推荐大小可作为性能提示,但不能替代针对强制取值的对齐检查。配套的 esp_blockdev_flags_t 结构体声明只读介质、加密或先擦后写等属性。中间件可以改变表观几何大小,但在创建时必须验证底层是否满足其要求,并确保后续对底层设备的访问始终符合其约束。
操作
esp_blockdev_ops_t 结构体定义读、写、擦除、同步、ioctl 与释放等回调。在调用回调之前,调用方必须确保对应函数指针非 NULL; NULL 表示不支持该操作。调用方有责任根据几何数据校验对齐与边界,并遵守由标识位指定的约束要求(例如在类 NAND 介质上写之前先执行擦除)。
典型流程
从驱动程序或中间件提供商处获取一个句柄。
检查几何结构和标志,以确定所需的对齐方式、可用容量和特殊处理方式。
通过提供商公开的操作表发出读取、写入、擦除和同步请求。
将句柄转发给上层的组件,或者在所有操作完成后释放该句柄。
示例
esp_blockdev_handle_t dev = my_component_get_blockdev();
const esp_blockdev_geometry_t *geometry = dev->geometry;
if (dev->ops->read && (sizeof(buffer) % geometry->read_size) == 0) {
ESP_ERROR_CHECK(dev->ops->read(dev, buffer, sizeof(buffer), 0, sizeof(buffer)));
}
if (dev->ops->release) {
ESP_ERROR_CHECK(dev->ops->release(dev));
}
约定
标志(esp_blockdev_flags_t)
标志在设备创建时初始化一次,且在句柄整个生命周期内必须保持不变。
read_only要求写、擦除及会改变状态的 ioctl 命令失败,并返回诸如ESP_ERR_INVALID_STATE的错误。encrypted表示介质上的数据已加密;上层不得假定可见明文或透明映射。erase_before_write告知调用方:成功写入前必须先擦除目标范围。若在未插入擦除操作的情况下对同一范围多次写入,行为未定义,但很可能导致数据损坏。and_type_write表示 NAND/NOR 风格行为:编程仅将位清为 0(1→0),实际存储existing_bits & new_bits。已为 0 的位在写请求写入 1 时仍保持为 0;只有先擦除才能恢复为 1。default_val_after_erase标识擦除后区域读回为0x00还是0xFF,以便中间件保持哨兵值一致。
几何参数 (esp_blockdev_geometry_t)
disk_size为可访问的总容量(字节);任何结束偏移量超出该值的请求都必须被拒绝。read_size、write_size与erase_size为强制对齐单位(字节);在执行操作前,偏移与长度都必须和相应的大小对齐。调用方遵循推荐大小时可提高吞吐量,但不能替代最低对齐检查;实现方必须接受所有符合强制粒度要求的请求。
当用户看到同一底层设备的可读写与只读两种变体时,除
read_only标志外,两者的几何参数必须相同。特别是read_size、write_size与erase_size应保持一致;recommended_*也应该保持一致,除非有特别的需要(此类情况应在文档中说明)。
操作 (esp_blockdev_ops_t)
read(dev_handle, dst_buf, dst_buf_size, src_addr, data_read_len)行为:成功时,将恰好
data_read_len字节数据复制到dst_buf。前置条件:
dst_buf有效,src_addr与data_read_len按read_size对齐,src_addr + data_read_len <= disk_size;且data_read_len <= dst_buf_size。后置条件:复制成功则返回
ESP_OK,失败则透传相应的ESP_ERR_*错误码。
write(dev_handle, src_buf, dst_addr, data_write_len)前置条件:设备非
read_only;src_buf至少覆盖data_write_len字节;偏移与长度按write_size对齐且在disk_size范围内。行为:若设置了
erase_before_write,调用方必须先执行erase。若设置了and_type_write,硬件会将新内容与现有内容进行按位与运算 (AND),存储结果变为old_value & new_value;除非先擦除该范围,否则先前写入操作清除的位将保持清除状态。后置条件:设备接受请求的范围后,将返回
ESP_OK(在sync运行之前,数据可能仍驻留在中间缓冲区中)。对齐错误、超出范围或只读尝试应会显现ESP_ERR_INVALID_ARG或ESP_ERR_INVALID_STATE,且实现方必须避免使该范围处于部分更新状态。说明:在具有
and_type_write的设备上,写操作依赖于现有内容,因此在写之后立即读取相同(或重叠)范围时,可能需要先执行sync,以确保缓存数据反映完全合并后的值。
erase(dev_handle, start_addr, erase_len)前置条件:设备允许擦除;
start_addr与erase_len按erase_size对齐;范围在disk_size内。后置条件:成功时该范围读回为
default_val_after_erase。未对齐或越界请求应返回ESP_ERR_INVALID_ARG;硬件故障应通过驱动相关的ESP_ERR_*码向上传递。
sync(dev_handle)刷新挂起的写入操作。省略此回调的设备将采用直写语义运行。
后置条件:在返回
ESP_OK之前,所有先前已报告的写操作均到达稳定存储(包括所有底层设备)。超时或传输问题应表现为ESP_ERR_TIMEOUT或其他相关ESP_ERR_*。
ioctl(dev_handle, cmd, args)命令标识
0x00–0x7F保留给 ESP-IDF 系统使用; 0x80–0xFF 可供用户自定义扩展。每个命令定义各自的载荷布局;由于
args为void *类型,封装层只能对其理解的命令校验或重新解释缓冲区,否则必须将载荷视为不透明数据。无法处理某命令的封装层应在合适时机将其原样转发给协议栈中的下一设备;仅最底层设备应对无法识别的命令返回
ESP_ERR_NOT_SUPPORTED。当协议栈存在非透明地址映射时,转发嵌入原始地址的命令本质上不安全:中间层无法翻译不透明载荷,因此行为未定义且通常会失败。此类命令应被显式拦截,或在堆叠配置中注明为不支持。
release(dev_handle)可选的析构函数,用于释放设备资源。该函数必须具有幂等性,即确保重复调用时要么成功,要么返回诸如
ESP_ERR_INVALID_STATE等无害错误。
错误处理
回调在成功时返回 ESP_OK,并应原样透传 ESP_ERR_* 错误码以帮助调用方诊断错误。中间件与应用程序须透传底层设备的错误,而不是在协议栈内掩盖这些错误。 NULL 函数指针视为“不支持该操作”。
验证
实现方应包含覆盖对齐检查、由标志驱动的行为(只读、先擦后写、NAND 风格写)以及错误在堆叠设备间正确透传等测试。封装较低层句柄的中间件还必须验证句柄生命周期管理在整个协议栈中保持一致。
API 参考
Header File
This header file can be included with:
#include "esp_blockdev.h"
This header file is a part of the API provided by the
esp_blockdevcomponent. To declare that your component depends onesp_blockdev, add the following to your CMakeLists.txt:REQUIRES esp_blockdev
or
PRIV_REQUIRES esp_blockdev
Structures
-
struct esp_blockdev_cmd_arg_erase_t
Input arguments for erase-related block-device ioctl commands.
Pass a pointer to this structure as the
argsargument to esp_blockdev_ops_t::ioctl when using ESP_BLOCKDEV_CMD_MARK_DELETED or ESP_BLOCKDEV_CMD_ERASE_CONTENTS. Bothstart_addranderase_lenmust be aligned to esp_blockdev_geometry_t::erase_size for the target device.Public Members
-
uint64_t start_addr
Byte offset of the range from the start of the device.
Must be a multiple of esp_blockdev_geometry_t::erase_size.
-
size_t erase_len
Length of the range in bytes.
Must be a multiple of esp_blockdev_geometry_t::erase_size.
-
uint64_t start_addr
-
struct esp_blockdev_flags_t
Block device property flags (
esp_blockdev_flags_t)Various properties and characteristics of a BDL device. Represented as a
structwhen__DOXYGEN__is defined (for documentation only) and as aunionin normal builds.备注
The macros
ESP_BLOCKDEV_FLAGS_CONFIG_DEFAULTandESP_BLOCKDEV_FLAGS_INST_CONFIG_DEFAULTprovide a typical configuration for ESP-IDF components that expose a BDL interface. Use them as a starting point for custom initializers.Public Members
-
bool read_only
no erase/write operations allowed
-
bool encrypted
the device data is encrypted
-
bool erase_before_write
erasing required before any write operation
-
bool and_type_write
0-bits cannot be changed to 1-bits (NAND/NOR flash behavior)
-
bool default_val_after_erase
default bit value after erasing (0 or 1)
-
bool reserved[27]
Reserved for future blockdev flags
-
uint32_t val
Raw bitfield view for bulk initialization
-
bool read_only
-
struct esp_blockdev_geometry_t
Block device geometry.
Granularity and capacity parameters for read, write, and erase operations on the device.
Public Members
-
uint64_t disk_size
Total addressable size of the device in bytes.
Mandatory for every block device.
-
size_t read_size
Minimum read transaction size in bytes.
Mandatory. All read lengths and offsets must respect this granularity where required by the driver.
-
size_t write_size
Minimum write transaction size in bytes.
Mandatory for read/write devices; set to
0for read-only devices.
-
size_t erase_size
Minimum erase granularity in bytes.
Mandatory for read/write devices; set to
0for read-only devices.备注
Often corresponds to the logical sector size used by file systems.
-
size_t recommended_write_size
Suggested write chunk size in bytes for best performance.
Set to
0if not used. Optional hint for upper layers.
-
size_t recommended_read_size
Suggested read chunk size in bytes for best performance.
Set to
0if not used. Optional hint for upper layers.
-
size_t recommended_erase_size
Suggested erase chunk size in bytes for best performance.
Set to
0if not used. Optional hint for upper layers.备注
Often matches the physical erase block of the underlying flash or similar media.
-
uint64_t disk_size
-
struct esp_blockdev_ops_t
Block device operations.
Function table for read, write, erase, sync, ioctl, and release on a block device instance.
Public Members
-
esp_err_t (*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)
Read data from the device.
Reads the requested number of bytes from the device at the given offset into the output buffer.
- Parameters
dev_handle Target device handle
dst_buf Output buffer to receive the data read
dst_buf_size Size of the destination buffer (in bytes)
src_addr Source address for the read (offset in the device's address space)
data_read_len Data read length (in bytes)
- Return value
ESP_OK on success
Another
esp_err_terror code on failure
-
esp_err_t (*write)(esp_blockdev_handle_t dev_handle, const uint8_t *src_buf, uint64_t dst_addr, size_t data_write_len)
Write data to the device.
Writes the requested number of bytes from the input buffer to the device at the given offset.
- Parameters
dev_handle Target device handle
src_buf Input buffer providing the data to write
dst_addr Destination address for the write (offset in the device's address space)
data_write_len Length of the data to be written (in bytes)
- Return value
ESP_OK on success
Another
esp_err_terror code on failure
-
esp_err_t (*erase)(esp_blockdev_handle_t dev_handle, uint64_t start_addr, size_t erase_len)
Erase an address range on the device.
Erases the address range
[start_addr, start_addr + erase_len). Botherase_lenandstart_addrmust be aligned to multiples of the erase block size in bytes (erase_sizein esp_blockdev_geometry_t). The implementation may use one or more internal operations, depending on the device. The result must be that data in the range is no longer readable as previously written content.- Parameters
dev_handle Target device handle
start_addr Start address for the erase operation
erase_len Length of the address range to erase (in bytes)
- Return value
ESP_OK on success
Another
esp_err_terror code on failure
-
esp_err_t (*sync)(esp_blockdev_handle_t dev_handle)
Commit pending writes to the device.
Commits all pending writes and blocks until they complete. Does nothing if the device does not cache writes.
- Parameters
dev_handle Target device handle
- Return value
ESP_OK on success
Another
esp_err_terror code on failure
-
esp_err_t (*ioctl)(esp_blockdev_handle_t dev_handle, const uint8_t cmd, void *args)
Device-specific I/O control.
Each command has parameters that the handler must validate. Implementations may extend commands within these ranges:
0x00-0x7F: IDF device commands (system)
0x80-0xFF: user-defined commands
Refer to the
ESP_BLOCKDEV_CMD_macros in this header.- Parameters
dev_handle Target device handle
cmd Command ID
args Command-specific arguments
- Return value
ESP_OK on success
Another
esp_err_terror code on failure
-
esp_err_t (*release)(esp_blockdev_handle_t dev_handle)
Release resources for this device handle.
- Parameters
dev_handle Target device handle
- Return value
ESP_OK on success
Another
esp_err_terror code on failure
-
esp_err_t (*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)
-
struct esp_blockdev
Generic IDF block device interface.
Describes a block device that supports common disk operations and exposes parameters needed to integrate it into a component stack.
备注
The context pointer
ctxholds driver-private data; the BDL layer does not interpret it.备注
The
opstable may be shared by multiple instances of the same driver type.Public Members
-
void *ctx
Owner-provided context; not interpreted by the BDL layer
-
esp_blockdev_flags_t device_flags
Capabilities and requirements
-
esp_blockdev_geometry_t geometry
Operation granularity and capacity
-
const esp_blockdev_ops_t *ops
Function table implementing the device
-
void *ctx
Macros
-
ESP_BLOCKDEV_FLAGS_CONFIG_DEFAULT()
Brace-enclosed initializer for
esp_blockdev_flags_twith typical ESP-IDF defaults.
-
ESP_BLOCKDEV_FLAGS_INST_CONFIG_DEFAULT(flags)
Assign typical ESP-IDF default values to an existing
esp_blockdev_flags_t.- 参数:
flags -- Flags instance to initialize (statement form, not an expression)
-
ESP_BLOCKDEV_HANDLE_INVALID
Sentinel for an invalid block device handle.
Compare handles to
NULL; this macro expands toNULLfor readability.
Type Definitions
-
typedef struct esp_blockdev esp_blockdev_t
Forward declaration; see esp_blockdev
-
typedef esp_blockdev_t *esp_blockdev_handle_t
Opaque handle to a block device instance.
Pointer to esp_blockdev. This is the only public identifier for a BDL instance: it is returned from the device factory and must be released through esp_blockdev_ops_t::release when no longer needed.