mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/spiffs2' into 'master'
Add SPIFFS Component to IDF See merge request !1229
This commit is contained in:
commit
9a26296a0e
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -29,3 +29,7 @@
|
||||
[submodule "components/libsodium/libsodium"]
|
||||
path = components/libsodium/libsodium
|
||||
url = https://github.com/jedisct1/libsodium.git
|
||||
|
||||
[submodule "components/spiffs/spiffs"]
|
||||
path = components/spiffs/spiffs
|
||||
url = https://github.com/pellepl/spiffs.git
|
||||
|
@ -32,7 +32,10 @@ esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path,
|
||||
const size_t workbuf_size = 4096;
|
||||
void *workbuf = NULL;
|
||||
|
||||
esp_partition_t *data_partition = (esp_partition_t *)esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label);
|
||||
esp_partition_subtype_t subtype = partition_label ?
|
||||
ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_FAT;
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
subtype, partition_label);
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
|
139
components/spiffs/Kconfig
Normal file
139
components/spiffs/Kconfig
Normal file
@ -0,0 +1,139 @@
|
||||
menu "SPIFFS Configuration"
|
||||
|
||||
config SPIFFS_MAX_PARTITIONS
|
||||
int "Maximum Number of Partitions"
|
||||
default 3
|
||||
range 1 10
|
||||
help
|
||||
Define maximum number of partitions
|
||||
that can be mounted.
|
||||
|
||||
menu "SPIFFS Cache Configuration"
|
||||
config SPIFFS_CACHE
|
||||
bool "Enable SPIFFS Cache"
|
||||
default "y"
|
||||
help
|
||||
Enables/disable memory read
|
||||
caching of nucleus file system
|
||||
operations.
|
||||
|
||||
config SPIFFS_CACHE_WR
|
||||
bool "Enable SPIFFS Write Caching"
|
||||
default "y"
|
||||
depends on SPIFFS_CACHE
|
||||
help
|
||||
Enables memory write caching for
|
||||
file descriptors in hydrogen.
|
||||
|
||||
config SPIFFS_CACHE_STATS
|
||||
bool "Enable SPIFFS Cache Statistics"
|
||||
default "n"
|
||||
depends on SPIFFS_CACHE
|
||||
help
|
||||
Enable/disable statistics on caching.
|
||||
Debug/test purpose only.
|
||||
|
||||
endmenu
|
||||
|
||||
config SPIFFS_PAGE_CHECK
|
||||
bool "Enable SPIFFS Page Check"
|
||||
default "y"
|
||||
help
|
||||
Always check header of each
|
||||
accessed page to ensure consistent state.
|
||||
If enabled it will increase number
|
||||
of reads, will increase flash.
|
||||
|
||||
config SPIFFS_GC_MAX_RUNS
|
||||
int "Set Maximum GC Runs"
|
||||
default 10
|
||||
range 1 255
|
||||
help
|
||||
Define maximum number of gc runs to
|
||||
perform to reach desired free pages.
|
||||
|
||||
config SPIFFS_GC_STATS
|
||||
bool "Enable SPIFFS GC Statistics"
|
||||
default "n"
|
||||
help
|
||||
Enable/disable statistics on gc.
|
||||
Debug/test purpose only.
|
||||
|
||||
config SPIFFS_OBJ_NAME_LEN
|
||||
int "Set SPIFFS Maximum Name Length"
|
||||
default 32
|
||||
range 1 256
|
||||
help
|
||||
Object name maximum length. Note that this length
|
||||
include the zero-termination character,
|
||||
meaning maximum string of characters can at most be
|
||||
SPIFFS_OBJ_NAME_LEN - 1.
|
||||
|
||||
config SPIFFS_USE_MAGIC
|
||||
bool "Enable SPIFFS Filesystem Magic"
|
||||
default "y"
|
||||
help
|
||||
Enable this to have an identifiable spiffs filesystem.
|
||||
This will look for a magic in all sectors
|
||||
to determine if this is a valid spiffs system
|
||||
or not on mount point.
|
||||
|
||||
config SPIFFS_USE_MAGIC_LENGTH
|
||||
bool "Enable SPIFFS Filesystem Length Magic"
|
||||
default "y"
|
||||
depends on SPIFFS_USE_MAGIC
|
||||
help
|
||||
If this option is enabled, the magic will also be dependent
|
||||
on the length of the filesystem. For example, a filesystem
|
||||
configured and formatted for 4 megabytes will not be accepted
|
||||
for mounting with a configuration defining the filesystem as 2 megabytes.
|
||||
|
||||
menu "Debug Configuration"
|
||||
|
||||
config SPIFFS_DBG
|
||||
bool "Enable general SPIFFS debug"
|
||||
default "n"
|
||||
help
|
||||
Enabling this option will print
|
||||
general debug mesages to the console
|
||||
|
||||
config SPIFFS_API_DBG
|
||||
bool "Enable SPIFFS API debug"
|
||||
default "n"
|
||||
help
|
||||
Enabling this option will print
|
||||
API debug mesages to the console
|
||||
|
||||
config SPIFFS_GC_DBG
|
||||
bool "Enable SPIFFS Garbage Cleaner debug"
|
||||
default "n"
|
||||
help
|
||||
Enabling this option will print
|
||||
GC debug mesages to the console
|
||||
|
||||
config SPIFFS_CACHE_DBG
|
||||
bool "Enable SPIFFS Cache debug"
|
||||
default "n"
|
||||
depends on SPIFFS_CACHE
|
||||
help
|
||||
Enabling this option will print
|
||||
Cache debug mesages to the console
|
||||
|
||||
config SPIFFS_CHECK_DBG
|
||||
bool "Enable SPIFFS Filesystem Check debug"
|
||||
default "n"
|
||||
help
|
||||
Enabling this option will print
|
||||
Filesystem Check debug mesages
|
||||
to the console
|
||||
|
||||
config SPIFFS_TEST_VISUALISATION
|
||||
bool "Enable SPIFFS Filesystem Visualization"
|
||||
default "n"
|
||||
help
|
||||
Enable this option to enable SPIFFS_vis function
|
||||
in the api.
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
3
components/spiffs/component.mk
Normal file
3
components/spiffs/component.mk
Normal file
@ -0,0 +1,3 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
COMPONENT_PRIV_INCLUDEDIRS := spiffs/src
|
||||
COMPONENT_SRCDIRS := . spiffs/src
|
766
components/spiffs/esp_spiffs.c
Normal file
766
components/spiffs/esp_spiffs.c
Normal file
@ -0,0 +1,766 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp_spiffs.h"
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/lock.h>
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_err.h"
|
||||
#include "rom/spi_flash.h"
|
||||
|
||||
static const char * TAG = "SPIFFS";
|
||||
|
||||
/**
|
||||
* @brief SPIFFS definition structure
|
||||
*/
|
||||
typedef struct {
|
||||
spiffs *fs; /*!< Handle to the underlying SPIFFS */
|
||||
SemaphoreHandle_t lock; /*!< FS lock */
|
||||
const esp_partition_t* partition; /*!< The partition on which SPIFFS is located */
|
||||
char base_path[ESP_VFS_PATH_MAX+1]; /*!< Mount point */
|
||||
bool by_label; /*!< Partition was mounted by label */
|
||||
spiffs_config cfg; /*!< SPIFFS Mount configuration */
|
||||
uint8_t *work; /*!< Work Buffer */
|
||||
uint8_t *fds; /*!< File Descriptor Buffer */
|
||||
uint32_t fds_sz; /*!< File Descriptor Buffer Length */
|
||||
uint8_t *cache; /*!< Cache Buffer */
|
||||
uint32_t cache_sz; /*!< Cache Buffer Length */
|
||||
} esp_spiffs_t;
|
||||
|
||||
/**
|
||||
* @brief SPIFFS DIR structure
|
||||
*/
|
||||
typedef struct {
|
||||
DIR dir; /*!< VFS DIR struct */
|
||||
spiffs_DIR d; /*!< SPIFFS DIR struct */
|
||||
struct dirent e; /*!< Last open dirent */
|
||||
long offset; /*!< Offset of the current dirent */
|
||||
char path[SPIFFS_OBJ_NAME_LEN]; /*!< Requested directory name */
|
||||
} vfs_spiffs_dir_t;
|
||||
|
||||
static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode);
|
||||
static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size);
|
||||
static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size);
|
||||
static int vfs_spiffs_close(void* ctx, int fd);
|
||||
static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode);
|
||||
static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st);
|
||||
static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st);
|
||||
static int vfs_spiffs_unlink(void* ctx, const char *path);
|
||||
static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2);
|
||||
static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst);
|
||||
static DIR* vfs_spiffs_opendir(void* ctx, const char* name);
|
||||
static int vfs_spiffs_closedir(void* ctx, DIR* pdir);
|
||||
static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir);
|
||||
static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir,
|
||||
struct dirent* entry, struct dirent** out_dirent);
|
||||
static long vfs_spiffs_telldir(void* ctx, DIR* pdir);
|
||||
static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset);
|
||||
static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode);
|
||||
static int vfs_spiffs_rmdir(void* ctx, const char* name);
|
||||
|
||||
static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS];
|
||||
|
||||
void spiffs_api_lock(spiffs *fs)
|
||||
{
|
||||
xSemaphoreTake(((esp_spiffs_t *)(fs->user_data))->lock, portMAX_DELAY);
|
||||
}
|
||||
|
||||
void spiffs_api_unlock(spiffs *fs)
|
||||
{
|
||||
xSemaphoreGive(((esp_spiffs_t *)(fs->user_data))->lock);
|
||||
}
|
||||
|
||||
static s32_t spiffs_api_read(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *dst)
|
||||
{
|
||||
esp_err_t err = esp_partition_read(((esp_spiffs_t *)(fs->user_data))->partition,
|
||||
addr, dst, size);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "failed to read addr %08x, size %08x, err %d", addr, size, err);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s32_t spiffs_api_write(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *src)
|
||||
{
|
||||
esp_err_t err = esp_partition_write(((esp_spiffs_t *)(fs->user_data))->partition,
|
||||
addr, src, size);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "failed to write addr %08x, size %08x, err %d", addr, size, err);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s32_t spiffs_api_erase(spiffs *fs, uint32_t addr, uint32_t size)
|
||||
{
|
||||
esp_err_t err = esp_partition_erase_range(((esp_spiffs_t *)(fs->user_data))->partition,
|
||||
addr, size);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "failed to erase addr %08x, size %08x, err %d", addr, size, err);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spiffs_api_check(spiffs *fs, spiffs_check_type type,
|
||||
spiffs_check_report report, uint32_t arg1, uint32_t arg2)
|
||||
{
|
||||
static const char * spiffs_check_type_str[3] = {
|
||||
"LOOKUP",
|
||||
"INDEX",
|
||||
"PAGE"
|
||||
};
|
||||
|
||||
static const char * spiffs_check_report_str[7] = {
|
||||
"PROGRESS",
|
||||
"ERROR",
|
||||
"FIX INDEX",
|
||||
"FIX LOOKUP",
|
||||
"DELETE ORPHANED INDEX",
|
||||
"DELETE PAGE",
|
||||
"DELETE BAD FILE"
|
||||
};
|
||||
|
||||
if (report != SPIFFS_CHECK_PROGRESS) {
|
||||
ESP_LOGE(TAG, "CHECK: type:%s, report:%s, %x:%x", spiffs_check_type_str[type],
|
||||
spiffs_check_report_str[report], arg1, arg2);
|
||||
} else {
|
||||
ESP_LOGV(TAG, "CHECK PROGRESS: report:%s, %x:%x",
|
||||
spiffs_check_report_str[report], arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_spiffs_free(esp_spiffs_t ** efs)
|
||||
{
|
||||
esp_spiffs_t * e = *efs;
|
||||
if (*efs == NULL) {
|
||||
return;
|
||||
}
|
||||
*efs = NULL;
|
||||
|
||||
if (e->fs) {
|
||||
SPIFFS_unmount(e->fs);
|
||||
free(e->fs);
|
||||
}
|
||||
vSemaphoreDelete(e->lock);
|
||||
free(e->fds);
|
||||
free(e->cache);
|
||||
free(e->work);
|
||||
free(e);
|
||||
}
|
||||
|
||||
static esp_err_t esp_spiffs_by_label(const char* label, int * index){
|
||||
int i;
|
||||
esp_spiffs_t * p;
|
||||
for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) {
|
||||
p = _efs[i];
|
||||
if (p) {
|
||||
if (!label && !p->by_label) {
|
||||
*index = i;
|
||||
return ESP_OK;
|
||||
}
|
||||
if (label && p->by_label && strncmp(label, p->partition->label, 17) == 0) {
|
||||
*index = i;
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static esp_err_t esp_spiffs_get_empty(int * index){
|
||||
int i;
|
||||
for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) {
|
||||
if (_efs[i] == NULL) {
|
||||
*index = i;
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static esp_err_t esp_spiffs_init(const esp_vfs_spiffs_conf_t* conf)
|
||||
{
|
||||
int index;
|
||||
//find if such partition is already mounted
|
||||
if (esp_spiffs_by_label(conf->partition_label, &index) == ESP_OK) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (esp_spiffs_get_empty(&index) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "max mounted partitions reached");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_partition_subtype_t subtype = conf->partition_label ?
|
||||
ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_SPIFFS;
|
||||
const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
subtype, conf->partition_label);
|
||||
if (!partition) {
|
||||
ESP_LOGE(TAG, "spiffs partition could not be found");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (partition->encrypted) {
|
||||
ESP_LOGE(TAG, "spiffs can not run on encrypted partition");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_spiffs_t * efs = malloc(sizeof(esp_spiffs_t));
|
||||
if (efs == NULL) {
|
||||
ESP_LOGE(TAG, "esp_spiffs could not be malloced");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memset(efs, 0, sizeof(esp_spiffs_t));
|
||||
|
||||
efs->cfg.hal_erase_f = spiffs_api_erase;
|
||||
efs->cfg.hal_read_f = spiffs_api_read;
|
||||
efs->cfg.hal_write_f = spiffs_api_write;
|
||||
efs->cfg.log_block_size = g_rom_flashchip.sector_size;
|
||||
efs->cfg.log_page_size = g_rom_flashchip.page_size;
|
||||
efs->cfg.phys_addr = 0;
|
||||
efs->cfg.phys_erase_block = g_rom_flashchip.sector_size;
|
||||
efs->cfg.phys_size = partition->size;
|
||||
|
||||
efs->by_label = conf->partition_label != NULL;
|
||||
|
||||
efs->lock = xSemaphoreCreateMutex();
|
||||
if (efs->lock == NULL) {
|
||||
ESP_LOGE(TAG, "mutex lock could not be created");
|
||||
esp_spiffs_free(&efs);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
efs->fds_sz = conf->max_files * sizeof(spiffs_fd);
|
||||
efs->fds = malloc(efs->fds_sz);
|
||||
if (efs->fds == NULL) {
|
||||
ESP_LOGE(TAG, "fd buffer could not be malloced");
|
||||
esp_spiffs_free(&efs);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memset(efs->fds, 0, efs->fds_sz);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
efs->cache_sz = sizeof(spiffs_cache) + conf->max_files * (sizeof(spiffs_cache_page)
|
||||
+ efs->cfg.log_page_size);
|
||||
efs->cache = malloc(efs->cache_sz);
|
||||
if (efs->cache == NULL) {
|
||||
ESP_LOGE(TAG, "cache buffer could not be malloced");
|
||||
esp_spiffs_free(&efs);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memset(efs->cache, 0, efs->cache_sz);
|
||||
#endif
|
||||
|
||||
const uint32_t work_sz = efs->cfg.log_page_size * 2;
|
||||
efs->work = malloc(work_sz);
|
||||
if (efs->work == NULL) {
|
||||
ESP_LOGE(TAG, "work buffer could not be malloced");
|
||||
esp_spiffs_free(&efs);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memset(efs->work, 0, work_sz);
|
||||
|
||||
efs->fs = malloc(sizeof(spiffs));
|
||||
if (efs->fs == NULL) {
|
||||
ESP_LOGE(TAG, "spiffs could not be malloced");
|
||||
esp_spiffs_free(&efs);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memset(efs->fs, 0, sizeof(spiffs));
|
||||
|
||||
efs->fs->user_data = (void *)efs;
|
||||
efs->partition = partition;
|
||||
|
||||
s32_t res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz,
|
||||
efs->cache, efs->cache_sz, spiffs_api_check);
|
||||
|
||||
if (conf->format_if_mount_failed && res != SPIFFS_OK) {
|
||||
ESP_LOGW(TAG, "mount failed, %i. formatting...", SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
res = SPIFFS_format(efs->fs);
|
||||
if (res != SPIFFS_OK) {
|
||||
ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
esp_spiffs_free(&efs);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz,
|
||||
efs->cache, efs->cache_sz, spiffs_api_check);
|
||||
}
|
||||
if (res != SPIFFS_OK) {
|
||||
ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
esp_spiffs_free(&efs);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
_efs[index] = efs;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool esp_spiffs_mounted(const char* partition_label)
|
||||
{
|
||||
int index;
|
||||
if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
return (SPIFFS_mounted(_efs[index]->fs));
|
||||
}
|
||||
|
||||
esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes)
|
||||
{
|
||||
int index;
|
||||
if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
SPIFFS_info(_efs[index]->fs, total_bytes, used_bytes);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_spiffs_format(const char* partition_label)
|
||||
{
|
||||
bool mount_on_success = false;
|
||||
int index;
|
||||
esp_err_t err = esp_spiffs_by_label(partition_label, &index);
|
||||
if (err != ESP_OK) {
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.format_if_mount_failed = true,
|
||||
.partition_label = partition_label,
|
||||
.max_files = 1
|
||||
};
|
||||
err = esp_spiffs_init(&conf);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
err = esp_spiffs_by_label(partition_label, &index);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
esp_spiffs_free(&_efs[index]);
|
||||
return ESP_OK;
|
||||
} else if (SPIFFS_mounted(_efs[index]->fs)) {
|
||||
SPIFFS_unmount(_efs[index]->fs);
|
||||
mount_on_success = true;
|
||||
}
|
||||
s32_t res = SPIFFS_format(_efs[index]->fs);
|
||||
if (res != SPIFFS_OK) {
|
||||
ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(_efs[index]->fs));
|
||||
SPIFFS_clearerr(_efs[index]->fs);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (mount_on_success) {
|
||||
res = SPIFFS_mount(_efs[index]->fs, &_efs[index]->cfg, _efs[index]->work,
|
||||
_efs[index]->fds, _efs[index]->fds_sz, _efs[index]->cache,
|
||||
_efs[index]->cache_sz, spiffs_api_check);
|
||||
if (res != SPIFFS_OK) {
|
||||
ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(_efs[index]->fs));
|
||||
SPIFFS_clearerr(_efs[index]->fs);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)
|
||||
{
|
||||
assert(conf->base_path);
|
||||
const esp_vfs_t vfs = {
|
||||
.flags = ESP_VFS_FLAG_CONTEXT_PTR,
|
||||
.write_p = &vfs_spiffs_write,
|
||||
.lseek_p = &vfs_spiffs_lseek,
|
||||
.read_p = &vfs_spiffs_read,
|
||||
.open_p = &vfs_spiffs_open,
|
||||
.close_p = &vfs_spiffs_close,
|
||||
.fstat_p = &vfs_spiffs_fstat,
|
||||
.stat_p = &vfs_spiffs_stat,
|
||||
.link_p = &vfs_spiffs_link,
|
||||
.unlink_p = &vfs_spiffs_unlink,
|
||||
.rename_p = &vfs_spiffs_rename,
|
||||
.opendir_p = &vfs_spiffs_opendir,
|
||||
.closedir_p = &vfs_spiffs_closedir,
|
||||
.readdir_p = &vfs_spiffs_readdir,
|
||||
.readdir_r_p = &vfs_spiffs_readdir_r,
|
||||
.seekdir_p = &vfs_spiffs_seekdir,
|
||||
.telldir_p = &vfs_spiffs_telldir,
|
||||
.mkdir_p = &vfs_spiffs_mkdir,
|
||||
.rmdir_p = &vfs_spiffs_rmdir
|
||||
};
|
||||
|
||||
esp_err_t err = esp_spiffs_init(conf);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
int index;
|
||||
if (esp_spiffs_by_label(conf->partition_label, &index) != ESP_OK) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
strlcat(_efs[index]->base_path, conf->base_path, ESP_VFS_PATH_MAX + 1);
|
||||
err = esp_vfs_register(conf->base_path, &vfs, _efs[index]);
|
||||
if (err != ESP_OK) {
|
||||
esp_spiffs_free(&_efs[index]);
|
||||
return err;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_spiffs_unregister(const char* partition_label)
|
||||
{
|
||||
int index;
|
||||
if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_err_t err = esp_vfs_unregister(_efs[index]->base_path);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
esp_spiffs_free(&_efs[index]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int spiffs_res_to_errno(s32_t fr)
|
||||
{
|
||||
switch(fr) {
|
||||
case SPIFFS_OK :
|
||||
return 0;
|
||||
case SPIFFS_ERR_NOT_MOUNTED :
|
||||
return ENODEV;
|
||||
case SPIFFS_ERR_NOT_A_FS :
|
||||
return ENODEV;
|
||||
case SPIFFS_ERR_FULL :
|
||||
return ENOSPC;
|
||||
case SPIFFS_ERR_BAD_DESCRIPTOR :
|
||||
return EBADF;
|
||||
case SPIFFS_ERR_MOUNTED :
|
||||
return EEXIST;
|
||||
case SPIFFS_ERR_FILE_EXISTS :
|
||||
return EEXIST;
|
||||
case SPIFFS_ERR_NOT_FOUND :
|
||||
return ENOENT;
|
||||
case SPIFFS_ERR_NOT_A_FILE :
|
||||
return ENOENT;
|
||||
case SPIFFS_ERR_DELETED :
|
||||
return ENOENT;
|
||||
case SPIFFS_ERR_FILE_DELETED :
|
||||
return ENOENT;
|
||||
case SPIFFS_ERR_NAME_TOO_LONG :
|
||||
return ENAMETOOLONG;
|
||||
case SPIFFS_ERR_RO_NOT_IMPL :
|
||||
return EROFS;
|
||||
case SPIFFS_ERR_RO_ABORTED_OPERATION :
|
||||
return EROFS;
|
||||
default :
|
||||
return EIO;
|
||||
}
|
||||
return ENOTSUP;
|
||||
}
|
||||
|
||||
static int spiffs_mode_conv(int m)
|
||||
{
|
||||
int res = 0;
|
||||
int acc_mode = m & O_ACCMODE;
|
||||
if (acc_mode == O_RDONLY) {
|
||||
res |= SPIFFS_O_RDONLY;
|
||||
} else if (acc_mode == O_WRONLY) {
|
||||
res |= SPIFFS_O_WRONLY;
|
||||
} else if (acc_mode == O_RDWR) {
|
||||
res |= SPIFFS_O_RDWR;
|
||||
}
|
||||
if ((m & O_CREAT) && (m & O_EXCL)) {
|
||||
res |= SPIFFS_O_CREAT | SPIFFS_O_EXCL;
|
||||
} else if ((m & O_CREAT) && (m & O_TRUNC)) {
|
||||
res |= SPIFFS_O_CREAT | SPIFFS_O_TRUNC;
|
||||
} else if (m & O_APPEND) {
|
||||
res |= SPIFFS_O_APPEND;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode)
|
||||
{
|
||||
assert(path);
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
int fd = SPIFFS_open(efs->fs, path, spiffs_mode_conv(flags), mode);
|
||||
if (fd < 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size)
|
||||
{
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
ssize_t res = SPIFFS_write(efs->fs, fd, (void *)data, size);
|
||||
if (res < 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size)
|
||||
{
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
ssize_t res = SPIFFS_read(efs->fs, fd, dst, size);
|
||||
if (res < 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int vfs_spiffs_close(void* ctx, int fd)
|
||||
{
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
int res = SPIFFS_close(efs->fs, fd);
|
||||
if (res < 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode)
|
||||
{
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
off_t res = SPIFFS_lseek(efs->fs, fd, offset, mode);
|
||||
if (res < 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st)
|
||||
{
|
||||
assert(st);
|
||||
spiffs_stat s;
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
off_t res = SPIFFS_fstat(efs->fs, fd, &s);
|
||||
if (res < 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return -1;
|
||||
}
|
||||
st->st_size = s.size;
|
||||
st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st)
|
||||
{
|
||||
assert(path);
|
||||
assert(st);
|
||||
spiffs_stat s;
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
off_t res = SPIFFS_stat(efs->fs, path, &s);
|
||||
if (res < 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
st->st_size = s.size;
|
||||
st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
|
||||
st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst)
|
||||
{
|
||||
assert(src);
|
||||
assert(dst);
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
int res = SPIFFS_rename(efs->fs, src, dst);
|
||||
if (res < 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int vfs_spiffs_unlink(void* ctx, const char *path)
|
||||
{
|
||||
assert(path);
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
int res = SPIFFS_remove(efs->fs, path);
|
||||
if (res < 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static DIR* vfs_spiffs_opendir(void* ctx, const char* name)
|
||||
{
|
||||
assert(name);
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
vfs_spiffs_dir_t * dir = calloc(1, sizeof(vfs_spiffs_dir_t));
|
||||
if (!dir) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
if (!SPIFFS_opendir(efs->fs, name, &dir->d)) {
|
||||
free(dir);
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return NULL;
|
||||
}
|
||||
dir->offset = 0;
|
||||
strlcpy(dir->path, name, SPIFFS_OBJ_NAME_LEN);
|
||||
return (DIR*) dir;
|
||||
}
|
||||
|
||||
static int vfs_spiffs_closedir(void* ctx, DIR* pdir)
|
||||
{
|
||||
assert(pdir);
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
|
||||
int res = SPIFFS_closedir(&dir->d);
|
||||
free(dir);
|
||||
if (res < 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir)
|
||||
{
|
||||
assert(pdir);
|
||||
vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
|
||||
struct dirent* out_dirent;
|
||||
int err = vfs_spiffs_readdir_r(ctx, pdir, &dir->e, &out_dirent);
|
||||
if (err != 0) {
|
||||
errno = err;
|
||||
return NULL;
|
||||
}
|
||||
return out_dirent;
|
||||
}
|
||||
|
||||
static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir, struct dirent* entry,
|
||||
struct dirent** out_dirent)
|
||||
{
|
||||
assert(pdir);
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
|
||||
struct spiffs_dirent out;
|
||||
if (SPIFFS_readdir(&dir->d, &out) == 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
if (!errno) {
|
||||
*out_dirent = NULL;
|
||||
}
|
||||
return errno;
|
||||
}
|
||||
const char * item_name = (const char *)out.name;
|
||||
size_t plen = strlen(dir->path);
|
||||
if (plen > 1) {
|
||||
if (strncasecmp(dir->path, (const char *)out.name, plen) || out.name[plen] != '/' || !out.name[plen+1]) {
|
||||
return vfs_spiffs_readdir_r(ctx, pdir, entry, out_dirent);
|
||||
}
|
||||
item_name += plen + 1;
|
||||
} else if (item_name[0] == '/') {
|
||||
item_name++;
|
||||
}
|
||||
entry->d_ino = 0;
|
||||
entry->d_type = out.type;
|
||||
snprintf(entry->d_name, SPIFFS_OBJ_NAME_LEN, "%s", item_name);
|
||||
dir->offset++;
|
||||
*out_dirent = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long vfs_spiffs_telldir(void* ctx, DIR* pdir)
|
||||
{
|
||||
assert(pdir);
|
||||
vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
|
||||
return dir->offset;
|
||||
}
|
||||
|
||||
static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset)
|
||||
{
|
||||
assert(pdir);
|
||||
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||
vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
|
||||
struct spiffs_dirent tmp;
|
||||
if (offset < dir->offset) {
|
||||
//rewind dir
|
||||
SPIFFS_closedir(&dir->d);
|
||||
if (!SPIFFS_opendir(efs->fs, NULL, &dir->d)) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return;
|
||||
}
|
||||
dir->offset = 0;
|
||||
}
|
||||
while (dir->offset < offset) {
|
||||
if (SPIFFS_readdir(&dir->d, &tmp) == 0) {
|
||||
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||
SPIFFS_clearerr(efs->fs);
|
||||
return;
|
||||
}
|
||||
size_t plen = strlen(dir->path);
|
||||
if (plen > 1) {
|
||||
if (strncasecmp(dir->path, (const char *)tmp.name, plen) || tmp.name[plen] != '/' || !tmp.name[plen+1]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
dir->offset++;
|
||||
}
|
||||
}
|
||||
|
||||
static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode)
|
||||
{
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int vfs_spiffs_rmdir(void* ctx, const char* name)
|
||||
{
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2)
|
||||
{
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
94
components/spiffs/include/esp_spiffs.h
Normal file
94
components/spiffs/include/esp_spiffs.h
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _ESP_SPIFFS_H_
|
||||
#define _ESP_SPIFFS_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for esp_vfs_spiffs_register
|
||||
*/
|
||||
typedef struct {
|
||||
const char* base_path; /*!< File path prefix associated with the filesystem. */
|
||||
const char* partition_label; /*!< Optional, label of SPIFFS partition to use. If set to NULL, first partition with subtype=spiffs will be used. */
|
||||
size_t max_files; /*!< Maximum files that could be open at the same time. */
|
||||
bool format_if_mount_failed; /*!< If true, it will format the file system if it fails to mount. */
|
||||
} esp_vfs_spiffs_conf_t;
|
||||
|
||||
/**
|
||||
* Register and mount SPIFFS to VFS with given path prefix.
|
||||
*
|
||||
* @param conf Pointer to esp_vfs_spiffs_conf_t configuration structure
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK if success
|
||||
* - ESP_ERR_NO_MEM if objects could not be allocated
|
||||
* - ESP_ERR_INVALID_STATE if already mounted or partition is encrypted
|
||||
* - ESP_ERR_NOT_FOUND if partition for SPIFFS was not found
|
||||
* - ESP_FAIL if mount or format fails
|
||||
*/
|
||||
esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf);
|
||||
|
||||
/**
|
||||
* Unregister and unmount SPIFFS from VFS
|
||||
*
|
||||
* @param partition_label Optional, label of the partition to unregister.
|
||||
* If not specified, first partition with subtype=spiffs is used.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK if successful
|
||||
* - ESP_ERR_INVALID_STATE already unregistered
|
||||
*/
|
||||
esp_err_t esp_vfs_spiffs_unregister(const char* partition_label);
|
||||
|
||||
/**
|
||||
* Check if SPIFFS is mounted
|
||||
*
|
||||
* @param partition_label Optional, label of the partition to check.
|
||||
* If not specified, first partition with subtype=spiffs is used.
|
||||
*
|
||||
* @return
|
||||
* - true if mounted
|
||||
* - false if not mounted
|
||||
*/
|
||||
bool esp_spiffs_mounted(const char* partition_label);
|
||||
|
||||
/**
|
||||
* Format the SPIFFS partition
|
||||
*
|
||||
* @param partition_label Optional, label of the partition to format.
|
||||
* If not specified, first partition with subtype=spiffs is used.
|
||||
* @return
|
||||
* - ESP_OK if successful
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t esp_spiffs_format(const char* partition_label);
|
||||
|
||||
/**
|
||||
* Get information for SPIFFS
|
||||
*
|
||||
* @param partition_label Optional, label of the partition to get info for.
|
||||
* If not specified, first partition with subtype=spiffs is used.
|
||||
* @param[out] total_bytes Size of the file system
|
||||
* @param[out] used_bytes Current used bytes in the file system
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK if success
|
||||
* - ESP_ERR_INVALID_STATE if not mounted
|
||||
*/
|
||||
esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes);
|
||||
|
||||
#endif /* _ESP_SPIFFS_H_ */
|
313
components/spiffs/include/spiffs_config.h
Executable file
313
components/spiffs/include/spiffs_config.h
Executable file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* spiffs_config.h
|
||||
*
|
||||
* Created on: Jul 3, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#ifndef SPIFFS_CONFIG_H_
|
||||
#define SPIFFS_CONFIG_H_
|
||||
|
||||
// ----------- 8< ------------
|
||||
// Following includes are for the linux test build of spiffs
|
||||
// These may/should/must be removed/altered/replaced in your target
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
// compile time switches
|
||||
#define SPIFFS_TAG "SPIFFS"
|
||||
|
||||
// Set generic spiffs debug output call.
|
||||
#if CONGIG_SPIFFS_DBG
|
||||
#define SPIFFS_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define SPIFFS_DBG(...)
|
||||
#endif
|
||||
#if CONGIG_SPIFFS_API_DBG
|
||||
#define SPIFFS_API_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define SPIFFS_API_DBG(...)
|
||||
#endif
|
||||
#if CONGIG_SPIFFS_DBG
|
||||
#define SPIFFS_GC_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define SPIFFS_GC_DBG(...)
|
||||
#endif
|
||||
#if CONGIG_SPIFFS_CACHE_DBG
|
||||
#define SPIFFS_CACHE_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define SPIFFS_CACHE_DBG(...)
|
||||
#endif
|
||||
#if CONGIG_SPIFFS_CHECK_DBG
|
||||
#define SPIFFS_CHECK_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define SPIFFS_CHECK_DBG(...)
|
||||
#endif
|
||||
|
||||
// needed types
|
||||
typedef signed int s32_t;
|
||||
typedef unsigned int u32_t;
|
||||
typedef signed short s16_t;
|
||||
typedef unsigned short u16_t;
|
||||
typedef signed char s8_t;
|
||||
typedef unsigned char u8_t;
|
||||
|
||||
struct spiffs_t;
|
||||
extern void spiffs_api_lock(struct spiffs_t *fs);
|
||||
extern void spiffs_api_unlock(struct spiffs_t *fs);
|
||||
|
||||
// Defines spiffs debug print formatters
|
||||
// some general signed number
|
||||
#define _SPIPRIi "%d"
|
||||
// address
|
||||
#define _SPIPRIad "%08x"
|
||||
// block
|
||||
#define _SPIPRIbl "%04x"
|
||||
// page
|
||||
#define _SPIPRIpg "%04x"
|
||||
// span index
|
||||
#define _SPIPRIsp "%04x"
|
||||
// file descriptor
|
||||
#define _SPIPRIfd "%d"
|
||||
// file object id
|
||||
#define _SPIPRIid "%04x"
|
||||
// file flags
|
||||
#define _SPIPRIfl "%02x"
|
||||
|
||||
|
||||
// Enable/disable API functions to determine exact number of bytes
|
||||
// for filedescriptor and cache buffers. Once decided for a configuration,
|
||||
// this can be disabled to reduce flash.
|
||||
#define SPIFFS_BUFFER_HELP 0
|
||||
|
||||
// Enables/disable memory read caching of nucleus file system operations.
|
||||
// If enabled, memory area must be provided for cache in SPIFFS_mount.
|
||||
#ifdef CONFIG_SPIFFS_CACHE
|
||||
#define SPIFFS_CACHE (1)
|
||||
#else
|
||||
#define SPIFFS_CACHE (0)
|
||||
#endif
|
||||
#if SPIFFS_CACHE
|
||||
// Enables memory write caching for file descriptors in hydrogen
|
||||
#ifdef CONFIG_SPIFFS_CACHE_WR
|
||||
#define SPIFFS_CACHE_WR (1)
|
||||
#else
|
||||
#define SPIFFS_CACHE_WR (0)
|
||||
#endif
|
||||
|
||||
// Enable/disable statistics on caching. Debug/test purpose only.
|
||||
#ifdef CONFIG_SPIFFS_CACHE_STATS
|
||||
#define SPIFFS_CACHE_STATS (1)
|
||||
#else
|
||||
#define SPIFFS_CACHE_STATS (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Always check header of each accessed page to ensure consistent state.
|
||||
// If enabled it will increase number of reads, will increase flash.
|
||||
#ifdef CONFIG_SPIFFS_PAGE_CHECK
|
||||
#define SPIFFS_PAGE_CHECK (1)
|
||||
#else
|
||||
#define SPIFFS_PAGE_CHECK (0)
|
||||
#endif
|
||||
|
||||
// Define maximum number of gc runs to perform to reach desired free pages.
|
||||
#define SPIFFS_GC_MAX_RUNS CONFIG_SPIFFS_GC_MAX_RUNS
|
||||
|
||||
// Enable/disable statistics on gc. Debug/test purpose only.
|
||||
#ifdef CONFIG_SPIFFS_GC_STATS
|
||||
#define SPIFFS_GC_STATS (1)
|
||||
#else
|
||||
#define SPIFFS_GC_STATS (0)
|
||||
#endif
|
||||
|
||||
// Garbage collecting examines all pages in a block which and sums up
|
||||
// to a block score. Deleted pages normally gives positive score and
|
||||
// used pages normally gives a negative score (as these must be moved).
|
||||
// To have a fair wear-leveling, the erase age is also included in score,
|
||||
// whose factor normally is the most positive.
|
||||
// The larger the score, the more likely it is that the block will
|
||||
// picked for garbage collection.
|
||||
|
||||
// Garbage collecting heuristics - weight used for deleted pages.
|
||||
#define SPIFFS_GC_HEUR_W_DELET (5)
|
||||
// Garbage collecting heuristics - weight used for used pages.
|
||||
#define SPIFFS_GC_HEUR_W_USED (-1)
|
||||
// Garbage collecting heuristics - weight used for time between
|
||||
// last erased and erase of this block.
|
||||
#define SPIFFS_GC_HEUR_W_ERASE_AGE (50)
|
||||
|
||||
// Object name maximum length. Note that this length include the
|
||||
// zero-termination character, meaning maximum string of characters
|
||||
// can at most be SPIFFS_OBJ_NAME_LEN - 1.
|
||||
#define SPIFFS_OBJ_NAME_LEN (CONFIG_SPIFFS_OBJ_NAME_LEN)
|
||||
|
||||
// Maximum length of the metadata associated with an object.
|
||||
// Setting to non-zero value enables metadata-related API but also
|
||||
// changes the on-disk format, so the change is not backward-compatible.
|
||||
//
|
||||
// Do note: the meta length must never exceed
|
||||
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64)
|
||||
//
|
||||
// This is derived from following:
|
||||
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
|
||||
// spiffs_object_ix_header fields + at least some LUT entries)
|
||||
#define SPIFFS_OBJ_META_LEN (0)
|
||||
|
||||
// Size of buffer allocated on stack used when copying data.
|
||||
// Lower value generates more read/writes. No meaning having it bigger
|
||||
// than logical page size.
|
||||
#define SPIFFS_COPY_BUFFER_STACK (256)
|
||||
|
||||
// Enable this to have an identifiable spiffs filesystem. This will look for
|
||||
// a magic in all sectors to determine if this is a valid spiffs system or
|
||||
// not on mount point. If not, SPIFFS_format must be called prior to mounting
|
||||
// again.
|
||||
#ifdef CONFIG_SPIFFS_USE_MAGIC
|
||||
#define SPIFFS_USE_MAGIC (1)
|
||||
#else
|
||||
#define SPIFFS_USE_MAGIC (0)
|
||||
#endif
|
||||
|
||||
#if SPIFFS_USE_MAGIC
|
||||
// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is
|
||||
// enabled, the magic will also be dependent on the length of the filesystem.
|
||||
// For example, a filesystem configured and formatted for 4 megabytes will not
|
||||
// be accepted for mounting with a configuration defining the filesystem as 2
|
||||
// megabytes.
|
||||
#ifdef CONFIG_SPIFFS_USE_MAGIC_LENGTH
|
||||
#define SPIFFS_USE_MAGIC_LENGTH (1)
|
||||
#else
|
||||
#define SPIFFS_USE_MAGIC_LENGTH (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
|
||||
// These should be defined on a multithreaded system
|
||||
|
||||
// define this to enter a mutex if you're running on a multithreaded system
|
||||
#define SPIFFS_LOCK(fs) spiffs_api_lock(fs)
|
||||
// define this to exit a mutex if you're running on a multithreaded system
|
||||
#define SPIFFS_UNLOCK(fs) spiffs_api_unlock(fs)
|
||||
|
||||
// Enable if only one spiffs instance with constant configuration will exist
|
||||
// on the target. This will reduce calculations, flash and memory accesses.
|
||||
// Parts of configuration must be defined below instead of at time of mount.
|
||||
#define SPIFFS_SINGLETON 0
|
||||
|
||||
// Enable this if your target needs aligned data for index tables
|
||||
#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 0
|
||||
|
||||
// Enable this if you want the HAL callbacks to be called with the spiffs struct
|
||||
#define SPIFFS_HAL_CALLBACK_EXTRA 1
|
||||
|
||||
// Enable this if you want to add an integer offset to all file handles
|
||||
// (spiffs_file). This is useful if running multiple instances of spiffs on
|
||||
// same target, in order to recognise to what spiffs instance a file handle
|
||||
// belongs.
|
||||
// NB: This adds config field fh_ix_offset in the configuration struct when
|
||||
// mounting, which must be defined.
|
||||
#define SPIFFS_FILEHDL_OFFSET 0
|
||||
|
||||
// Enable this to compile a read only version of spiffs.
|
||||
// This will reduce binary size of spiffs. All code comprising modification
|
||||
// of the file system will not be compiled. Some config will be ignored.
|
||||
// HAL functions for erasing and writing to spi-flash may be null. Cache
|
||||
// can be disabled for even further binary size reduction (and ram savings).
|
||||
// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL.
|
||||
// If the file system cannot be mounted due to aborted erase operation and
|
||||
// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be
|
||||
// returned.
|
||||
// Might be useful for e.g. bootloaders and such.
|
||||
#define SPIFFS_READ_ONLY 0
|
||||
|
||||
// Enable this to add a temporal file cache using the fd buffer.
|
||||
// The effects of the cache is that SPIFFS_open will find the file faster in
|
||||
// certain cases. It will make it a lot easier for spiffs to find files
|
||||
// opened frequently, reducing number of readings from the spi flash for
|
||||
// finding those files.
|
||||
// This will grow each fd by 6 bytes. If your files are opened in patterns
|
||||
// with a degree of temporal locality, the system is optimized.
|
||||
// Examples can be letting spiffs serve web content, where one file is the css.
|
||||
// The css is accessed for each html file that is opened, meaning it is
|
||||
// accessed almost every second time a file is opened. Another example could be
|
||||
// a log file that is often opened, written, and closed.
|
||||
// The size of the cache is number of given file descriptors, as it piggybacks
|
||||
// on the fd update mechanism. The cache lives in the closed file descriptors.
|
||||
// When closed, the fd know the whereabouts of the file. Instead of forgetting
|
||||
// this, the temporal cache will keep handling updates to that file even if the
|
||||
// fd is closed. If the file is opened again, the location of the file is found
|
||||
// directly. If all available descriptors become opened, all cache memory is
|
||||
// lost.
|
||||
#define SPIFFS_TEMPORAL_FD_CACHE 1
|
||||
|
||||
// Temporal file cache hit score. Each time a file is opened, all cached files
|
||||
// will lose one point. If the opened file is found in cache, that entry will
|
||||
// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this
|
||||
// value for the specific access patterns of the application. However, it must
|
||||
// be between 1 (no gain for hitting a cached entry often) and 255.
|
||||
#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 4
|
||||
|
||||
// Enable to be able to map object indices to memory.
|
||||
// This allows for faster and more deterministic reading if cases of reading
|
||||
// large files and when changing file offset by seeking around a lot.
|
||||
// When mapping a file's index, the file system will be scanned for index pages
|
||||
// and the info will be put in memory provided by user. When reading, the
|
||||
// memory map can be looked up instead of searching for index pages on the
|
||||
// medium. This way, user can trade memory against performance.
|
||||
// Whole, parts of, or future parts not being written yet can be mapped. The
|
||||
// memory array will be owned by spiffs and updated accordingly during garbage
|
||||
// collecting or when modifying the indices. The latter is invoked by when the
|
||||
// file is modified in some way. The index buffer is tied to the file
|
||||
// descriptor.
|
||||
#define SPIFFS_IX_MAP 1
|
||||
|
||||
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
|
||||
// in the api. This function will visualize all filesystem using given printf
|
||||
// function.
|
||||
#ifdef CONFIG_SPIFFS_TEST_VISUALISATION
|
||||
#define SPIFFS_TEST_VISUALISATION 1
|
||||
#else
|
||||
#define SPIFFS_TEST_VISUALISATION 0
|
||||
#endif
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
#ifndef spiffs_printf
|
||||
#define spiffs_printf(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||
#endif
|
||||
// spiffs_printf argument for a free page
|
||||
#define SPIFFS_TEST_VIS_FREE_STR "_"
|
||||
// spiffs_printf argument for a deleted page
|
||||
#define SPIFFS_TEST_VIS_DELE_STR "/"
|
||||
// spiffs_printf argument for an index page for given object id
|
||||
#define SPIFFS_TEST_VIS_INDX_STR(id) "i"
|
||||
// spiffs_printf argument for a data page for given object id
|
||||
#define SPIFFS_TEST_VIS_DATA_STR(id) "d"
|
||||
#endif
|
||||
|
||||
// Types depending on configuration such as the amount of flash bytes
|
||||
// given to spiffs file system in total (spiffs_file_system_size),
|
||||
// the logical block size (log_block_size), and the logical page size
|
||||
// (log_page_size)
|
||||
|
||||
// Block index type. Make sure the size of this type can hold
|
||||
// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size
|
||||
typedef u16_t spiffs_block_ix;
|
||||
// Page index type. Make sure the size of this type can hold
|
||||
// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size
|
||||
typedef u16_t spiffs_page_ix;
|
||||
// Object id type - most significant bit is reserved for index flag. Make sure the
|
||||
// size of this type can hold the highest object id on a full system,
|
||||
// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2
|
||||
typedef u16_t spiffs_obj_id;
|
||||
// Object span index type. Make sure the size of this type can
|
||||
// hold the largest possible span index on the system -
|
||||
// i.e. (spiffs_file_system_size / log_page_size) - 1
|
||||
typedef u16_t spiffs_span_ix;
|
||||
|
||||
#endif /* SPIFFS_CONFIG_H_ */
|
1
components/spiffs/spiffs
Submodule
1
components/spiffs/spiffs
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 794f0478d2aa9c978c3844da6e97f14239a1e061
|
1
components/spiffs/test/component.mk
Normal file
1
components/spiffs/test/component.mk
Normal file
@ -0,0 +1 @@
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
506
components/spiffs/test/test_spiffs.c
Normal file
506
components/spiffs/test/test_spiffs.c
Normal file
@ -0,0 +1,506 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/unistd.h>
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_spiffs.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_partition.h"
|
||||
|
||||
const char* spiffs_test_hello_str = "Hello, World!\n";
|
||||
const char* spiffs_test_partition_label = "flash_test";
|
||||
|
||||
void test_spiffs_create_file_with_text(const char* name, const char* text)
|
||||
{
|
||||
FILE* f = fopen(name, "wb");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
TEST_ASSERT_TRUE(fputs(text, f) != EOF);
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
}
|
||||
|
||||
void test_spiffs_overwrite_append(const char* filename)
|
||||
{
|
||||
/* Create new file with 'aaaa' */
|
||||
test_spiffs_create_file_with_text(filename, "aaaa");
|
||||
|
||||
/* Append 'bbbb' to file */
|
||||
FILE *f_a = fopen(filename, "a");
|
||||
TEST_ASSERT_NOT_NULL(f_a);
|
||||
TEST_ASSERT_NOT_EQUAL(EOF, fputs("bbbb", f_a));
|
||||
TEST_ASSERT_EQUAL(0, fclose(f_a));
|
||||
|
||||
/* Read back 8 bytes from file, verify it's 'aaaabbbb' */
|
||||
char buf[10] = { 0 };
|
||||
FILE *f_r = fopen(filename, "r");
|
||||
TEST_ASSERT_NOT_NULL(f_r);
|
||||
TEST_ASSERT_EQUAL(8, fread(buf, 1, 8, f_r));
|
||||
TEST_ASSERT_EQUAL_STRING_LEN("aaaabbbb", buf, 8);
|
||||
|
||||
/* Be sure we're at end of file */
|
||||
TEST_ASSERT_EQUAL(0, fread(buf, 1, 8, f_r));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f_r));
|
||||
|
||||
/* Overwrite file with 'cccc' */
|
||||
test_spiffs_create_file_with_text(filename, "cccc");
|
||||
|
||||
/* Verify file now only contains 'cccc' */
|
||||
f_r = fopen(filename, "r");
|
||||
TEST_ASSERT_NOT_NULL(f_r);
|
||||
bzero(buf, sizeof(buf));
|
||||
TEST_ASSERT_EQUAL(4, fread(buf, 1, 8, f_r)); // trying to read 8 bytes, only expecting 4
|
||||
TEST_ASSERT_EQUAL_STRING_LEN("cccc", buf, 4);
|
||||
TEST_ASSERT_EQUAL(0, fclose(f_r));
|
||||
}
|
||||
|
||||
void test_spiffs_read_file(const char* filename)
|
||||
{
|
||||
FILE* f = fopen(filename, "r");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
char buf[32] = { 0 };
|
||||
int cb = fread(buf, 1, sizeof(buf), f);
|
||||
TEST_ASSERT_EQUAL(strlen(spiffs_test_hello_str), cb);
|
||||
TEST_ASSERT_EQUAL(0, strcmp(spiffs_test_hello_str, buf));
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
}
|
||||
|
||||
void test_spiffs_open_max_files(const char* filename_prefix, size_t files_count)
|
||||
{
|
||||
FILE** files = calloc(files_count, sizeof(FILE*));
|
||||
for (size_t i = 0; i < files_count; ++i) {
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "%s_%d.txt", filename_prefix, i);
|
||||
files[i] = fopen(name, "w");
|
||||
TEST_ASSERT_NOT_NULL(files[i]);
|
||||
}
|
||||
/* close everything and clean up */
|
||||
for (size_t i = 0; i < files_count; ++i) {
|
||||
fclose(files[i]);
|
||||
}
|
||||
free(files);
|
||||
}
|
||||
|
||||
void test_spiffs_lseek(const char* filename)
|
||||
{
|
||||
FILE* f = fopen(filename, "wb+");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n"));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR));
|
||||
TEST_ASSERT_EQUAL('9', fgetc(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET));
|
||||
TEST_ASSERT_EQUAL('3', fgetc(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END));
|
||||
TEST_ASSERT_EQUAL('8', fgetc(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
|
||||
TEST_ASSERT_EQUAL(11, ftell(f));
|
||||
TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n"));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
|
||||
TEST_ASSERT_EQUAL(15, ftell(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
|
||||
char buf[20];
|
||||
TEST_ASSERT_EQUAL(15, fread(buf, 1, sizeof(buf), f));
|
||||
const char ref_buf[] = "0123456789\nabc\n";
|
||||
TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
}
|
||||
|
||||
void test_spiffs_stat(const char* filename)
|
||||
{
|
||||
test_spiffs_create_file_with_text(filename, "foo\n");
|
||||
struct stat st;
|
||||
TEST_ASSERT_EQUAL(0, stat(filename, &st));
|
||||
TEST_ASSERT(st.st_mode & S_IFREG);
|
||||
TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
|
||||
}
|
||||
|
||||
void test_spiffs_unlink(const char* filename)
|
||||
{
|
||||
test_spiffs_create_file_with_text(filename, "unlink\n");
|
||||
|
||||
TEST_ASSERT_EQUAL(0, unlink(filename));
|
||||
|
||||
TEST_ASSERT_NULL(fopen(filename, "r"));
|
||||
}
|
||||
|
||||
void test_spiffs_rename(const char* filename_prefix)
|
||||
{
|
||||
char name_dst[64];
|
||||
char name_src[64];
|
||||
snprintf(name_dst, sizeof(name_dst), "%s_dst.txt", filename_prefix);
|
||||
snprintf(name_src, sizeof(name_src), "%s_src.txt", filename_prefix);
|
||||
|
||||
unlink(name_dst);
|
||||
unlink(name_src);
|
||||
|
||||
FILE* f = fopen(name_src, "w+");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
char* str = "0123456789";
|
||||
for (int i = 0; i < 400; ++i) {
|
||||
TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f));
|
||||
}
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
TEST_ASSERT_EQUAL(0, rename(name_src, name_dst));
|
||||
TEST_ASSERT_NULL(fopen(name_src, "r"));
|
||||
FILE* fdst = fopen(name_dst, "r");
|
||||
TEST_ASSERT_NOT_NULL(fdst);
|
||||
TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END));
|
||||
TEST_ASSERT_EQUAL(4000, ftell(fdst));
|
||||
TEST_ASSERT_EQUAL(0, fclose(fdst));
|
||||
}
|
||||
|
||||
void test_spiffs_can_opendir(const char* path)
|
||||
{
|
||||
char name_dir_file[64];
|
||||
const char * file_name = "test_opd.txt";
|
||||
snprintf(name_dir_file, sizeof(name_dir_file), "%s/%s", path, file_name);
|
||||
unlink(name_dir_file);
|
||||
test_spiffs_create_file_with_text(name_dir_file, "test_opendir\n");
|
||||
DIR* dir = opendir(path);
|
||||
TEST_ASSERT_NOT_NULL(dir);
|
||||
bool found = false;
|
||||
while (true) {
|
||||
struct dirent* de = readdir(dir);
|
||||
if (!de) {
|
||||
break;
|
||||
}
|
||||
if (strcasecmp(de->d_name, file_name) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_TRUE(found);
|
||||
TEST_ASSERT_EQUAL(0, closedir(dir));
|
||||
unlink(name_dir_file);
|
||||
}
|
||||
|
||||
void test_spiffs_opendir_readdir_rewinddir(const char* dir_prefix)
|
||||
{
|
||||
char name_dir_inner_file[64];
|
||||
char name_dir_inner[64];
|
||||
char name_dir_file3[64];
|
||||
char name_dir_file2[64];
|
||||
char name_dir_file1[64];
|
||||
|
||||
snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/inner/3.txt", dir_prefix);
|
||||
snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/inner", dir_prefix);
|
||||
snprintf(name_dir_file3, sizeof(name_dir_file2), "%s/boo.bin", dir_prefix);
|
||||
snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/2.txt", dir_prefix);
|
||||
snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/1.txt", dir_prefix);
|
||||
|
||||
unlink(name_dir_inner_file);
|
||||
rmdir(name_dir_inner);
|
||||
unlink(name_dir_file1);
|
||||
unlink(name_dir_file2);
|
||||
unlink(name_dir_file3);
|
||||
rmdir(dir_prefix);
|
||||
|
||||
test_spiffs_create_file_with_text(name_dir_file1, "1\n");
|
||||
test_spiffs_create_file_with_text(name_dir_file2, "2\n");
|
||||
test_spiffs_create_file_with_text(name_dir_file3, "\01\02\03");
|
||||
test_spiffs_create_file_with_text(name_dir_inner_file, "3\n");
|
||||
|
||||
DIR* dir = opendir(dir_prefix);
|
||||
TEST_ASSERT_NOT_NULL(dir);
|
||||
int count = 0;
|
||||
const char* names[4];
|
||||
while(count < 4) {
|
||||
struct dirent* de = readdir(dir);
|
||||
if (!de) {
|
||||
break;
|
||||
}
|
||||
printf("found '%s'\n", de->d_name);
|
||||
if (strcasecmp(de->d_name, "1.txt") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "1.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "2.txt") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "2.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "inner/3.txt") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "inner/3.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "boo.bin") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "boo.bin";
|
||||
++count;
|
||||
} else {
|
||||
TEST_FAIL_MESSAGE("unexpected directory entry");
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_EQUAL(count, 4);
|
||||
|
||||
rewinddir(dir);
|
||||
struct dirent* de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
|
||||
seekdir(dir, 3);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
|
||||
seekdir(dir, 1);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
|
||||
seekdir(dir, 2);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, closedir(dir));
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char* filename;
|
||||
bool write;
|
||||
size_t word_count;
|
||||
int seed;
|
||||
SemaphoreHandle_t done;
|
||||
int result;
|
||||
} read_write_test_arg_t;
|
||||
|
||||
#define READ_WRITE_TEST_ARG_INIT(name, seed_) \
|
||||
{ \
|
||||
.filename = name, \
|
||||
.seed = seed_, \
|
||||
.word_count = 4096, \
|
||||
.write = true, \
|
||||
.done = xSemaphoreCreateBinary() \
|
||||
}
|
||||
|
||||
static void read_write_task(void* param)
|
||||
{
|
||||
read_write_test_arg_t* args = (read_write_test_arg_t*) param;
|
||||
FILE* f = fopen(args->filename, args->write ? "wb" : "rb");
|
||||
if (f == NULL) {
|
||||
args->result = ESP_ERR_NOT_FOUND;
|
||||
goto done;
|
||||
}
|
||||
|
||||
srand(args->seed);
|
||||
for (size_t i = 0; i < args->word_count; ++i) {
|
||||
uint32_t val = rand();
|
||||
if (args->write) {
|
||||
int cnt = fwrite(&val, sizeof(val), 1, f);
|
||||
if (cnt != 1) {
|
||||
ets_printf("E(w): i=%d, cnt=%d val=%d\n\n", i, cnt, val);
|
||||
args->result = ESP_FAIL;
|
||||
goto close;
|
||||
}
|
||||
} else {
|
||||
uint32_t rval;
|
||||
int cnt = fread(&rval, sizeof(rval), 1, f);
|
||||
if (cnt != 1) {
|
||||
ets_printf("E(r): i=%d, cnt=%d rval=%d\n\n", i, cnt, rval);
|
||||
args->result = ESP_FAIL;
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
}
|
||||
args->result = ESP_OK;
|
||||
|
||||
close:
|
||||
fclose(f);
|
||||
|
||||
done:
|
||||
xSemaphoreGive(args->done);
|
||||
vTaskDelay(1);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void test_spiffs_concurrent(const char* filename_prefix)
|
||||
{
|
||||
char names[4][64];
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
snprintf(names[i], sizeof(names[i]), "%s%d", filename_prefix, i + 1);
|
||||
unlink(names[i]);
|
||||
}
|
||||
|
||||
read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT(names[0], 1);
|
||||
read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT(names[1], 2);
|
||||
|
||||
printf("writing f1 and f2\n");
|
||||
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, 0);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, 1);
|
||||
|
||||
xSemaphoreTake(args1.done, portMAX_DELAY);
|
||||
printf("f1 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
|
||||
xSemaphoreTake(args2.done, portMAX_DELAY);
|
||||
printf("f2 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
|
||||
|
||||
args1.write = false;
|
||||
args2.write = false;
|
||||
read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT(names[2], 3);
|
||||
read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT(names[3], 4);
|
||||
|
||||
printf("reading f1 and f2, writing f3 and f4\n");
|
||||
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw3", 2048, &args3, 3, NULL, 1);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw4", 2048, &args4, 3, NULL, 0);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, 0);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, 1);
|
||||
|
||||
xSemaphoreTake(args1.done, portMAX_DELAY);
|
||||
printf("f1 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
|
||||
xSemaphoreTake(args2.done, portMAX_DELAY);
|
||||
printf("f2 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
|
||||
xSemaphoreTake(args3.done, portMAX_DELAY);
|
||||
printf("f3 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args3.result);
|
||||
xSemaphoreTake(args4.done, portMAX_DELAY);
|
||||
printf("f4 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args4.result);
|
||||
|
||||
vSemaphoreDelete(args1.done);
|
||||
vSemaphoreDelete(args2.done);
|
||||
vSemaphoreDelete(args3.done);
|
||||
vSemaphoreDelete(args4.done);
|
||||
}
|
||||
|
||||
|
||||
static void test_setup()
|
||||
{
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = "/spiffs",
|
||||
.partition_label = spiffs_test_partition_label,
|
||||
.max_files = 5,
|
||||
.format_if_mount_failed = true
|
||||
};
|
||||
|
||||
TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
|
||||
}
|
||||
|
||||
static void test_teardown()
|
||||
{
|
||||
TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
|
||||
}
|
||||
|
||||
TEST_CASE("can format partition", "[spiffs]")
|
||||
{
|
||||
const esp_partition_t* part = get_test_data_partition();
|
||||
TEST_ASSERT_NOT_NULL(part);
|
||||
TEST_ESP_OK(esp_partition_erase_range(part, 0, part->size));
|
||||
test_setup();
|
||||
size_t total = 0, used = 0;
|
||||
TEST_ESP_OK(esp_spiffs_info(spiffs_test_partition_label, &total, &used));
|
||||
printf("total: %d, used: %d\n", total, used);
|
||||
TEST_ASSERT_EQUAL(0, used);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("can create and write file", "[spiffs]")
|
||||
{
|
||||
test_setup();
|
||||
test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("can read file", "[spiffs]")
|
||||
{
|
||||
test_setup();
|
||||
test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str);
|
||||
test_spiffs_read_file("/spiffs/hello.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("can open maximum number of files", "[spiffs]")
|
||||
{
|
||||
size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = "/spiffs",
|
||||
.partition_label = spiffs_test_partition_label,
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = max_files
|
||||
};
|
||||
TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
|
||||
test_spiffs_open_max_files("/spiffs/f", max_files);
|
||||
TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
|
||||
}
|
||||
|
||||
TEST_CASE("overwrite and append file", "[spiffs]")
|
||||
{
|
||||
test_setup();
|
||||
test_spiffs_overwrite_append("/spiffs/hello.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("can lseek", "[spiffs]")
|
||||
{
|
||||
test_setup();
|
||||
test_spiffs_lseek("/spiffs/seek.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("stat returns correct values", "[spiffs]")
|
||||
{
|
||||
test_setup();
|
||||
test_spiffs_stat("/spiffs/stat.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("unlink removes a file", "[spiffs]")
|
||||
{
|
||||
test_setup();
|
||||
test_spiffs_unlink("/spiffs/unlink.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("rename moves a file", "[spiffs]")
|
||||
{
|
||||
test_setup();
|
||||
test_spiffs_rename("/spiffs/move");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("can opendir root directory of FS", "[spiffs]")
|
||||
{
|
||||
test_setup();
|
||||
test_spiffs_can_opendir("/spiffs");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("opendir, readdir, rewinddir, seekdir work as expected", "[spiffs]")
|
||||
{
|
||||
test_setup();
|
||||
test_spiffs_opendir_readdir_rewinddir("/spiffs/dir");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("multiple tasks can use same volume", "[spiffs]")
|
||||
{
|
||||
test_setup();
|
||||
test_spiffs_concurrent("/spiffs/f");
|
||||
test_teardown();
|
||||
}
|
@ -91,6 +91,8 @@ INPUT = \
|
||||
../components/spi_flash/include/esp_spi_flash.h \
|
||||
../components/spi_flash/include/esp_partition.h \
|
||||
../components/bootloader_support/include/esp_flash_encrypt.h \
|
||||
## SPIFFS
|
||||
../components/spiffs/include/esp_spiffs.h \
|
||||
## SD/MMC Card Host
|
||||
## NOTE: for three lines below header_file.inc is not used
|
||||
../components/sdmmc/include/sdmmc_cmd.h \
|
||||
|
@ -10,6 +10,7 @@ Storage API
|
||||
Virtual Filesystem <vfs>
|
||||
FAT Filesystem <fatfs>
|
||||
Wear Levelling <wear-levelling>
|
||||
SPIFFS Filesystem <spiffs>
|
||||
|
||||
|
||||
Example code for this API section is provided in :example:`storage` directory of ESP-IDF examples.
|
||||
|
53
docs/api-reference/storage/spiffs.rst
Normal file
53
docs/api-reference/storage/spiffs.rst
Normal file
@ -0,0 +1,53 @@
|
||||
SPIFFS Filesystem
|
||||
=================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
SPIFFS is a file system intended for SPI NOR flash devices on embedded targets.
|
||||
It supports wear leveling, file system consistency checks and more.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
- Presently, spiffs does not support directories. It produces a flat structure. If SPIFFS is mounted under ``/spiffs`` creating a file with path ``/spiffs/tmp/myfile.txt`` will create a file called ``/tmp/myfile.txt`` in SPIFFS, instead of ``myfile.txt`` under directory ``/spiffs/tmp``.
|
||||
- It is not a realtime stack. One write operation might last much longer than another.
|
||||
- Presently, it does not detect or handle bad blocks.
|
||||
|
||||
Tools
|
||||
-----
|
||||
|
||||
Host-Side tools for creating SPIFS partition images exist and one such tool is `mkspiffs <https://github.com/igrr/mkspiffs>`_.
|
||||
You can use it to create image from a given folder and then flash that image with ``esptool.py``
|
||||
|
||||
To do that you need to obtain some parameters:
|
||||
|
||||
- Block Size: 4096 (standard for SPI Flash)
|
||||
- Page Size: 256 (standard for SPI Flash)
|
||||
- Image Size: Size of the partition in bytes (can be obtained from partition table)
|
||||
- Partition Offset: Starting address of the partition (can be obtained from partition table)
|
||||
|
||||
To pack a folder into 1 Megabyte image::
|
||||
|
||||
mkspiffs -c [src_folder] -b 4096 -p 256 -s 0x100000 spiffs.bin
|
||||
|
||||
To flash the image to ESP32 at offset 0x110000::
|
||||
|
||||
python esptool.py --chip esp32 --port [port] --baud [baud] write_flash -z 0x110000 spiffs.bin
|
||||
|
||||
See also
|
||||
--------
|
||||
|
||||
- :doc:`Partition Table documentation <../../api-guides/partition-tables>`
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
An example for using SPIFFS is provided in :example:`storage/spiffs` directory. This example initializes and mounts SPIFFS partition, and writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information.
|
||||
|
||||
High level API Reference
|
||||
------------------------
|
||||
|
||||
* :component_file:`spiffs/include/esp_spiffs.h`
|
||||
|
||||
.. include:: /_build/inc/esp_spiffs.inc
|
9
examples/storage/spiffs/Makefile
Normal file
9
examples/storage/spiffs/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := spiffs
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
27
examples/storage/spiffs/README.md
Normal file
27
examples/storage/spiffs/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# SPIFFS example
|
||||
|
||||
This example demonstrates how to use SPIFFS with ESP32. Example does the following steps:
|
||||
|
||||
1. Use an "all-in-one" `esp_vfs_spiffs_register` function to:
|
||||
- initialize SPIFFS,
|
||||
- mount SPIFFS filesystem using SPIFFS library (and format, if the filesystem can not be mounted),
|
||||
- register SPIFFS filesystem in VFS, enabling C standard library and POSIX functions to be used.
|
||||
2. Create a file using `fopen` and write to it using `fprintf`.
|
||||
3. Rename the file. Before renaming, check if destination file already exists using `stat` function, and remove it using `unlink` function.
|
||||
4. Open renamed file for reading, read back the line, and print it to the terminal.
|
||||
|
||||
## Example output
|
||||
|
||||
Here is an example console output. In this case `format_if_mount_failed` parameter was set to `true` in the source code. SPIFFS was unformatted, so the initial mount has failed. SPIFFS was then formatted, and mounted again.
|
||||
|
||||
```
|
||||
I (195) example: Initializing SPIFFS
|
||||
E (195) SPIFFS: mount failed, -10025. formatting...
|
||||
I (4525) example: Opening file
|
||||
I (4635) example: File written
|
||||
I (4685) example: Renaming file
|
||||
I (4735) example: Reading file
|
||||
I (4735) example: Read from file: 'Hello World!'
|
||||
I (4735) example: SPIFFS unmounted
|
||||
```
|
||||
|
4
examples/storage/spiffs/main/component.mk
Normal file
4
examples/storage/spiffs/main/component.mk
Normal file
@ -0,0 +1,4 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
99
examples/storage/spiffs/main/spiffs_example_main.c
Normal file
99
examples/storage/spiffs/main/spiffs_example_main.c
Normal file
@ -0,0 +1,99 @@
|
||||
/* SPIFFS filesystem example.
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_spiffs.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing SPIFFS");
|
||||
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = "/spiffs",
|
||||
.partition_label = NULL,
|
||||
.max_files = 5,
|
||||
.format_if_mount_failed = true
|
||||
};
|
||||
|
||||
// Use settings defined above to initialize and mount SPIFFS filesystem.
|
||||
// Note: esp_vfs_spiffs_register is an all-in-one convenience function.
|
||||
esp_err_t ret = esp_vfs_spiffs_register(&conf);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
ESP_LOGE(TAG, "Failed to mount or format filesystem");
|
||||
} else if (ret == ESP_ERR_NOT_FOUND) {
|
||||
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%d)", ret);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
size_t total = 0, used = 0;
|
||||
ret = esp_spiffs_info(NULL, &total, &used);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get SPIFFS partition information");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
|
||||
}
|
||||
|
||||
// Use POSIX and C standard library functions to work with files.
|
||||
// First create a file.
|
||||
ESP_LOGI(TAG, "Opening file");
|
||||
FILE* f = fopen("/spiffs/hello.txt", "w");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
fprintf(f, "Hello World!\n");
|
||||
fclose(f);
|
||||
ESP_LOGI(TAG, "File written");
|
||||
|
||||
// Check if destination file exists before renaming
|
||||
struct stat st;
|
||||
if (stat("/spiffs/foo.txt", &st) == 0) {
|
||||
// Delete it if it exists
|
||||
unlink("/spiffs/foo.txt");
|
||||
}
|
||||
|
||||
// Rename original file
|
||||
ESP_LOGI(TAG, "Renaming file");
|
||||
if (rename("/spiffs/hello.txt", "/spiffs/foo.txt") != 0) {
|
||||
ESP_LOGE(TAG, "Rename failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Open renamed file for reading
|
||||
ESP_LOGI(TAG, "Reading file");
|
||||
f = fopen("/spiffs/foo.txt", "r");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
char line[64];
|
||||
fgets(line, sizeof(line), f);
|
||||
fclose(f);
|
||||
// strip newline
|
||||
char* pos = strchr(line, '\n');
|
||||
if (pos) {
|
||||
*pos = '\0';
|
||||
}
|
||||
ESP_LOGI(TAG, "Read from file: '%s'", line);
|
||||
|
||||
// All done, unmount partition and disable SPIFFS
|
||||
esp_vfs_spiffs_unregister(NULL);
|
||||
ESP_LOGI(TAG, "SPIFFS unmounted");
|
||||
}
|
6
examples/storage/spiffs/partitions_example.csv
Normal file
6
examples/storage/spiffs/partitions_example.csv
Normal file
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, spiffs, , 0xF0000,
|
|
5
examples/storage/spiffs/sdkconfig.defaults
Normal file
5
examples/storage/spiffs/sdkconfig.defaults
Normal file
@ -0,0 +1,5 @@
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
|
||||
CONFIG_APP_OFFSET=0x10000
|
@ -6,5 +6,6 @@ components/esptool_py/esptool @GENERAL_MIRROR_SERVER@/idf/
|
||||
components/libsodium/libsodium @GENERAL_MIRROR_SERVER@/idf/libsodium.git ALLOW_TO_SYNC_FROM_PUBLIC
|
||||
components/micro-ecc/micro-ecc @GENERAL_MIRROR_SERVER@/idf/micro-ecc.git ALLOW_TO_SYNC_FROM_PUBLIC
|
||||
components/nghttp/nghttp2 @GENERAL_MIRROR_SERVER@/idf/nghttp2.git ALLOW_TO_SYNC_FROM_PUBLIC
|
||||
components/spiffs/spiffs @GENERAL_MIRROR_SERVER@/idf/spiffs.git ALLOW_TO_SYNC_FROM_PUBLIC
|
||||
third-party/mruby @GENERAL_MIRROR_SERVER@/idf/mruby.git ALLOW_TO_SYNC_FROM_PUBLIC
|
||||
third-party/neverbleed @GENERAL_MIRROR_SERVER@/idf/neverbleed.git ALLOW_TO_SYNC_FROM_PUBLIC
|
||||
|
@ -10,7 +10,5 @@ factory, 0, 0, 0x10000, 0x140000
|
||||
# (done this way so tests can run in 2MB of flash.)
|
||||
ota_0, 0, ota_0, , 64K
|
||||
ota_1, 0, ota_1, , 64K
|
||||
# flash_test partition used for SPI flash tests and WL FAT partition
|
||||
# 528K is the minimal size needed to create a FAT partition
|
||||
# (128 sectors for FAT + 4 sectors for WL)
|
||||
# flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests
|
||||
flash_test, data, fat, , 528K
|
||||
|
|
Loading…
x
Reference in New Issue
Block a user