Add SPIFFS Component to IDF

This commit is contained in:
me-no-dev 2017-09-07 17:37:59 +03:00
parent 1f8d93f0eb
commit dd87deb278
21 changed files with 2039 additions and 4 deletions

4
.gitmodules vendored
View file

@ -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

View file

@ -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
View 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

View file

@ -0,0 +1,3 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := spiffs/src
COMPONENT_SRCDIRS := . spiffs/src

View 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;
}

View 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_ */

View 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_ */

@ -0,0 +1 @@
Subproject commit 794f0478d2aa9c978c3844da6e97f14239a1e061

View file

@ -0,0 +1 @@
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

View 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();
}

View file

@ -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 \

View file

@ -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.

View 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

View 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

View 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
```

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View 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");
}

View 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,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, spiffs, , 0xF0000,

View 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

View file

@ -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

View file

@ -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

1 # Special partition table for unit test app
10 # (done this way so tests can run in 2MB of flash.)
11 ota_0, 0, ota_0, , 64K
12 ota_1, 0, ota_1, , 64K
13 # flash_test partition used for SPI flash tests and WL FAT partition # flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests
# 528K is the minimal size needed to create a FAT partition
# (128 sectors for FAT + 4 sectors for WL)
14 flash_test, data, fat, , 528K