Files
esp-idf/components/wear_levelling/wl_blockdev.cpp
T
2026-03-23 20:33:18 +01:00

250 lines
7.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <new>
#include <sys/lock.h>
#include "wear_levelling.h"
#include "WL_Config.h"
#include "WL_Ext_Cfg.h"
#include "WL_Flash.h"
#include "WL_Ext_Perf.h"
#include "WL_Ext_Safe.h"
#include "BDL_Access.h"
#ifndef WL_DEFAULT_UPDATERATE
#define WL_DEFAULT_UPDATERATE 16
#endif
#ifndef WL_DEFAULT_TEMP_BUFF_SIZE
#define WL_DEFAULT_TEMP_BUFF_SIZE 32
#endif
#ifndef WL_DEFAULT_WRITE_SIZE
#define WL_DEFAULT_WRITE_SIZE 16
#endif
#ifndef WL_DEFAULT_START_ADDR
#define WL_DEFAULT_START_ADDR 0
#endif
#ifndef WL_CURRENT_VERSION
#define WL_CURRENT_VERSION 2
#endif
static const char *TAG = "wl_blockdev";
/* ========================================================================= */
/* BDL path standalone WL instance, no wl_handle_t */
/* ========================================================================= */
typedef struct {
WL_Flash *wl_instance;
BDL_Access *bdl_access;
esp_blockdev_handle_t bottom_bdl;
_lock_t lock;
} wl_bdl_ctx_t;
static esp_err_t wl_bdl_read(esp_blockdev_handle_t dev, uint8_t *dst, size_t dst_size,
uint64_t src_addr, size_t len)
{
if (dst_size < len) {
return ESP_ERR_INVALID_ARG;
}
if (src_addr % dev->geometry.read_size != 0 || len % dev->geometry.read_size) {
return ESP_ERR_INVALID_SIZE;
}
wl_bdl_ctx_t *ctx = (wl_bdl_ctx_t *)dev->ctx;
_lock_acquire(&ctx->lock);
esp_err_t ret = ctx->wl_instance->read((size_t)src_addr, dst, len);
_lock_release(&ctx->lock);
return ret;
}
static esp_err_t wl_bdl_write(esp_blockdev_handle_t dev, const uint8_t *src,
uint64_t dst_addr, size_t len)
{
if (dst_addr % dev->geometry.write_size != 0 || len % dev->geometry.write_size) {
return ESP_ERR_INVALID_SIZE;
}
wl_bdl_ctx_t *ctx = (wl_bdl_ctx_t *)dev->ctx;
_lock_acquire(&ctx->lock);
esp_err_t ret = ctx->wl_instance->write((size_t)dst_addr, src, len);
_lock_release(&ctx->lock);
return ret;
}
static esp_err_t wl_bdl_erase(esp_blockdev_handle_t dev, uint64_t start, size_t len)
{
if (start % dev->geometry.erase_size != 0 || len % dev->geometry.erase_size) {
return ESP_ERR_INVALID_SIZE;
}
wl_bdl_ctx_t *ctx = (wl_bdl_ctx_t *)dev->ctx;
_lock_acquire(&ctx->lock);
esp_err_t ret = ctx->wl_instance->erase_range((size_t)start, len);
_lock_release(&ctx->lock);
return ret;
}
static esp_err_t wl_bdl_sync(esp_blockdev_handle_t dev)
{
wl_bdl_ctx_t *ctx = (wl_bdl_ctx_t *)dev->ctx;
_lock_acquire(&ctx->lock);
esp_err_t ret = ctx->wl_instance->flush();
_lock_release(&ctx->lock);
return ret;
}
static esp_err_t wl_bdl_ioctl(esp_blockdev_handle_t dev, const uint8_t cmd, void *args)
{
wl_bdl_ctx_t *ctx = (wl_bdl_ctx_t *)dev->ctx;
if (ctx->bottom_bdl == ESP_BLOCKDEV_HANDLE_INVALID) {
return ESP_ERR_NOT_ALLOWED;
}
if (ctx->bottom_bdl->ops->ioctl == NULL) {
return ESP_ERR_NOT_SUPPORTED;
}
return ctx->bottom_bdl->ops->ioctl(ctx->bottom_bdl, cmd, args);
}
static esp_err_t wl_bdl_release(esp_blockdev_handle_t dev)
{
wl_bdl_ctx_t *ctx = (wl_bdl_ctx_t *)dev->ctx;
if (ctx->wl_instance) {
if (!ctx->bdl_access->is_readonly()) {
ctx->wl_instance->flush();
}
ctx->wl_instance->~WL_Flash();
free(ctx->wl_instance);
}
if (ctx->bdl_access) {
ctx->bdl_access->~BDL_Access();
free(ctx->bdl_access);
}
_lock_close(&ctx->lock);
free(ctx);
free(dev);
return ESP_OK;
}
static const esp_blockdev_ops_t s_wl_bdl_ops = {
.read = wl_bdl_read,
.write = wl_bdl_write,
.erase = wl_bdl_erase,
.sync = wl_bdl_sync,
.ioctl = wl_bdl_ioctl,
.release = wl_bdl_release,
};
esp_err_t wl_get_blockdev(const esp_blockdev_handle_t bdl_bottom_device,
esp_blockdev_handle_t *out_bdl_handle_ptr)
{
if (bdl_bottom_device == ESP_BLOCKDEV_HANDLE_INVALID || out_bdl_handle_ptr == NULL) {
return ESP_ERR_INVALID_ARG;
}
*out_bdl_handle_ptr = ESP_BLOCKDEV_HANDLE_INVALID;
if (bdl_bottom_device->geometry.disk_size > UINT32_MAX) {
ESP_LOGE(TAG, "%s: disk_size 0x%016" PRIx64 " exceeds uint32_t range, "
"WL configuration would be silently truncated",
__func__, bdl_bottom_device->geometry.disk_size);
return ESP_ERR_INVALID_SIZE;
}
esp_err_t result = ESP_OK;
wl_bdl_ctx_t *ctx = (wl_bdl_ctx_t *)calloc(1, sizeof(wl_bdl_ctx_t));
if (!ctx) {
return ESP_ERR_NO_MEM;
}
ctx->bottom_bdl = bdl_bottom_device;
void *access_mem = malloc(sizeof(BDL_Access));
if (!access_mem) {
free(ctx);
return ESP_ERR_NO_MEM;
}
ctx->bdl_access = new (access_mem) BDL_Access(bdl_bottom_device);
void *wl_mem = NULL;
#if CONFIG_WL_SECTOR_SIZE == 512
#if CONFIG_WL_SECTOR_MODE == 1
wl_mem = malloc(sizeof(WL_Ext_Safe));
if (!wl_mem) { result = ESP_ERR_NO_MEM; goto fail; }
ctx->wl_instance = new (wl_mem) WL_Ext_Safe();
#else
wl_mem = malloc(sizeof(WL_Ext_Perf));
if (!wl_mem) { result = ESP_ERR_NO_MEM; goto fail; }
ctx->wl_instance = new (wl_mem) WL_Ext_Perf();
#endif
#endif
#if CONFIG_WL_SECTOR_SIZE == 4096
wl_mem = malloc(sizeof(WL_Flash));
if (!wl_mem) { result = ESP_ERR_NO_MEM; goto fail; }
ctx->wl_instance = new (wl_mem) WL_Flash();
#endif
{
wl_ext_cfg_t cfg;
cfg.wl_partition_start_addr = WL_DEFAULT_START_ADDR;
cfg.wl_partition_size = (uint32_t)bdl_bottom_device->geometry.disk_size;
cfg.wl_page_size = bdl_bottom_device->geometry.erase_size;
cfg.flash_sector_size = bdl_bottom_device->geometry.erase_size;
cfg.wl_update_rate = WL_DEFAULT_UPDATERATE;
cfg.wl_pos_update_record_size = WL_DEFAULT_WRITE_SIZE;
cfg.version = WL_CURRENT_VERSION;
cfg.wl_temp_buff_size = WL_DEFAULT_TEMP_BUFF_SIZE;
cfg.fat_sector_size = CONFIG_WL_SECTOR_SIZE;
result = ctx->wl_instance->config(&cfg, ctx->bdl_access);
if (result != ESP_OK) {
ESP_LOGE(TAG, "%s: config failed, result=0x%x", __func__, result);
goto fail;
}
result = ctx->wl_instance->init();
if (result != ESP_OK) {
ESP_LOGE(TAG, "%s: init failed, result=0x%x", __func__, result);
goto fail;
}
}
{
esp_blockdev_t *dev = (esp_blockdev_t *)calloc(1, sizeof(esp_blockdev_t));
if (!dev) { result = ESP_ERR_NO_MEM; goto fail; }
dev->ctx = ctx;
dev->ops = &s_wl_bdl_ops;
dev->device_flags = bdl_bottom_device->device_flags;
dev->geometry.disk_size = ctx->wl_instance->get_flash_size();
dev->geometry.read_size = bdl_bottom_device->geometry.read_size;
dev->geometry.write_size = bdl_bottom_device->geometry.write_size;
dev->geometry.erase_size = ctx->wl_instance->get_sector_size();
dev->geometry.recommended_read_size = bdl_bottom_device->geometry.recommended_read_size;
dev->geometry.recommended_write_size = bdl_bottom_device->geometry.recommended_write_size;
dev->geometry.recommended_erase_size = ctx->wl_instance->get_sector_size();
_lock_init(&ctx->lock);
*out_bdl_handle_ptr = dev;
}
return ESP_OK;
fail:
if (ctx->wl_instance) { ctx->wl_instance->~WL_Flash(); free(ctx->wl_instance); }
if (ctx->bdl_access) { ctx->bdl_access->~BDL_Access(); free(ctx->bdl_access); }
free(ctx);
return result;
}