Merge branch 'feature/fatfs_bdl_v6.0' into 'release/v6.0'

feat(fatfs): Add BDL support to FatFS component (v6.0)

See merge request espressif/esp-idf!52557
This commit is contained in:
Martin Vychodil
2026-09-10 16:10:00 +08:00
32 changed files with 1393 additions and 6 deletions

View File

@@ -9,6 +9,17 @@ examples/storage/fatfs:
- if: IDF_TARGET != "esp32"
reason: only one target needed
examples/storage/fatfs/bdl_wl:
depends_components:
- *common_components
- esp_blockdev
- fatfs
- vfs
- wear_leveling
disable_test:
- if: IDF_TARGET != "esp32"
reason: only one target needed
examples/storage/fatfs/ext_flash:
depends_components:
- *common_components

View File

@@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
idf_build_set_property(MINIMAL_BUILD ON)
project(fatfs_bdl_wl)

View File

@@ -0,0 +1,55 @@
| 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 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- |
# FatFS over BDL (Block Device Layer) - Wear-Levelling Stack
This example demonstrates mounting a FAT filesystem using the Block Device Layer (BDL) interface
instead of the legacy `wl_handle_t`-based API.
## BDL Stack
The BDL stack constructed in this example:
```
FatFS (VFS + POSIX API)
|
diskio_bdl (generic BDL diskio adapter)
|
WL BDL (wear-levelling, via wl_get_blockdev())
|
Partition BDL (flash partition, via esp_partition_get_blockdev())
|
SPI Flash (physical storage)
```
The key advantage of BDL is that **the same `diskio_bdl` adapter works with any BDL device**.
You can swap the bottom of the stack (e.g., use `sdmmc_get_blockdev()` for an SD card) without
changing the FatFS integration code.
## How to use example
### Build and flash
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type `Ctrl-]`.)
## Example output
```
I (321) example: Creating partition BDL for 'storage' partition
I (331) example: Partition BDL: disk_size=1048576, erase_size=4096
I (331) example: Creating WL BDL on top of partition BDL
I (341) example: WL BDL: disk_size=...., erase_size=4096
I (341) example: Mounting FAT filesystem via BDL
I (741) example: Filesystem mounted
I (741) example: Opening file
I (841) example: File written
I (841) example: Reading file
I (841) example: Read from file: 'Hello from FatFS over BDL!'
I (841) example: Unmounting FAT filesystem
I (941) example: Releasing BDL devices
I (941) example: Done
```

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "fatfs_bdl_wl_main.c"
PRIV_REQUIRES vfs fatfs esp_blockdev esp_partition wear_levelling
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,133 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/*
* FatFS over BDL (Block Device Layer) - Wear-Levelling stack example
*
* Demonstrates building a BDL stack and mounting FatFS on top of it:
*
* +-----------+
* | FatFS | <- file system (VFS + FatFS)
* +-----------+
* | diskio_bdl| <- FatFS diskio driver for BDL devices
* +-----------+
* | WL BDL | <- wear-levelling BDL layer (wl_get_blockdev)
* +-----------+
* | Part BDL | <- partition BDL layer (esp_partition_get_blockdev)
* +-----------+
* | SPI Flash | <- physical storage
* +-----------+
*
* The BDL approach decouples FatFS from any specific storage driver.
* The same diskio_bdl adapter works with any BDL-compatible bottom device:
* - partition BDL (flash partition)
* - sdmmc BDL (SD/eMMC card)
* - memory BDL (RAM disk for testing)
* - or any custom BDL implementation
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_vfs.h"
#include "esp_vfs_fat.h"
#include "esp_partition.h"
#include "esp_blockdev.h"
#include "wear_levelling.h"
static const char *TAG = "example";
const char *base_path = "/spiflash";
void app_main(void)
{
/* ------------------------------------------------------------------ */
/* Step 1: Build the BDL stack */
/* ------------------------------------------------------------------ */
ESP_LOGI(TAG, "Creating partition BDL for 'storage' partition");
esp_blockdev_handle_t part_bdl = NULL;
ESP_ERROR_CHECK(esp_partition_get_blockdev(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT,
"storage", &part_bdl));
ESP_LOGI(TAG, " Partition BDL: disk_size=%llu, erase_size=%u",
(unsigned long long)part_bdl->geometry.disk_size,
(unsigned)part_bdl->geometry.erase_size);
ESP_LOGI(TAG, "Creating WL BDL on top of partition BDL");
esp_blockdev_handle_t wl_bdl = NULL;
ESP_ERROR_CHECK(wl_get_blockdev(part_bdl, &wl_bdl));
ESP_LOGI(TAG, " WL BDL: disk_size=%llu, erase_size=%u",
(unsigned long long)wl_bdl->geometry.disk_size,
(unsigned)wl_bdl->geometry.erase_size);
/* ------------------------------------------------------------------ */
/* Step 2: Mount FatFS on the BDL device */
/* ------------------------------------------------------------------ */
ESP_LOGI(TAG, "Mounting FAT filesystem via BDL");
const esp_vfs_fat_mount_config_t mount_config = {
.max_files = 4,
.format_if_mount_failed = true,
.allocation_unit_size = CONFIG_WL_SECTOR_SIZE,
.use_one_fat = false,
};
ESP_ERROR_CHECK(esp_vfs_fat_bdl_mount(base_path, wl_bdl, &mount_config));
ESP_LOGI(TAG, "Filesystem mounted");
/* ------------------------------------------------------------------ */
/* Step 3: Use POSIX file operations */
/* ------------------------------------------------------------------ */
const char *filename = "/spiflash/example.txt";
ESP_LOGI(TAG, "Opening file");
FILE *f = fopen(filename, "wb");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for writing");
return;
}
fprintf(f, "Hello from FatFS over BDL!\n");
fclose(f);
ESP_LOGI(TAG, "File written");
ESP_LOGI(TAG, "Reading file");
f = fopen(filename, "r");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for reading");
return;
}
char line[128];
fgets(line, sizeof(line), f);
fclose(f);
char *pos = strchr(line, '\n');
if (pos) {
*pos = '\0';
}
ESP_LOGI(TAG, "Read from file: '%s'", line);
/* ------------------------------------------------------------------ */
/* Step 4: Unmount and tear down the BDL stack */
/* ------------------------------------------------------------------ */
ESP_LOGI(TAG, "Unmounting FAT filesystem");
ESP_ERROR_CHECK(esp_vfs_fat_bdl_unmount(base_path, wl_bdl));
ESP_LOGI(TAG, "Releasing BDL devices");
wl_bdl->ops->release(wl_bdl);
part_bdl->ops->release(part_bdl);
ESP_LOGI(TAG, "Done");
}

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 1M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, fat, , 1M,

View File

@@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR 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'], indirect=['target'])
def test_examples_fatfs_bdl_wl(dut: Dut) -> None:
dut.expect('example: Mounting FAT filesystem via BDL', timeout=90)
dut.expect('example: Filesystem mounted', timeout=90)
dut.expect('example: Opening file', timeout=90)
dut.expect('example: File written', timeout=90)
dut.expect('example: Reading file', timeout=90)
dut.expect("example: Read from file: 'Hello from FatFS over BDL!'", timeout=90)
dut.expect('example: Unmounting FAT filesystem', timeout=90)
dut.expect('example: Done', timeout=90)

View File

@@ -0,0 +1,4 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y