mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
250 lines
7.7 KiB
C++
250 lines
7.7 KiB
C++
/*
|
||
* 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;
|
||
}
|