From ab795350fbe920ac86d34bccb9ac354c6d212c92 Mon Sep 17 00:00:00 2001 From: sheinz Date: Mon, 27 Jun 2016 18:06:06 +0300 Subject: [PATCH 1/3] Draft implementation of SPIFFS integration --- .gitmodules | 3 + examples/spiffs/Makefile | 9 ++ examples/spiffs/spiffs_example.c | 180 +++++++++++++++++++++ extras/spiffs/component.mk | 17 ++ extras/spiffs/esp_spiffs.c | 187 ++++++++++++++++++++++ extras/spiffs/esp_spiffs.h | 28 ++++ extras/spiffs/spiffs | 1 + extras/spiffs/spiffs_config.h | 263 +++++++++++++++++++++++++++++++ 8 files changed, 688 insertions(+) create mode 100644 examples/spiffs/Makefile create mode 100644 examples/spiffs/spiffs_example.c create mode 100644 extras/spiffs/component.mk create mode 100644 extras/spiffs/esp_spiffs.c create mode 100644 extras/spiffs/esp_spiffs.h create mode 160000 extras/spiffs/spiffs create mode 100644 extras/spiffs/spiffs_config.h diff --git a/.gitmodules b/.gitmodules index 7a3370e..00dadd8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,6 @@ path = bootloader/rboot url = https://github.com/raburton/rboot.git +[submodule "extras/spiffs/spiffs"] + path = extras/spiffs/spiffs + url = https://github.com/pellepl/spiffs.git diff --git a/examples/spiffs/Makefile b/examples/spiffs/Makefile new file mode 100644 index 0000000..b342df7 --- /dev/null +++ b/examples/spiffs/Makefile @@ -0,0 +1,9 @@ +PROGRAM=spiffs_example +EXTRA_COMPONENTS = extras/spiffs +FLASH_SIZE = 32 + +# spiffs configuration +SPIFFS_BASE_ADDR = 0x200000 +SPIFFS_SIZE = 0x100000 + +include ../../common.mk diff --git a/examples/spiffs/spiffs_example.c b/examples/spiffs/spiffs_example.c new file mode 100644 index 0000000..226ee51 --- /dev/null +++ b/examples/spiffs/spiffs_example.c @@ -0,0 +1,180 @@ +#include "espressif/esp_common.h" +#include "esp/uart.h" +#include "FreeRTOS.h" +#include "task.h" +#include "esp8266.h" + +#include "spiffs.h" +#include "esp_spiffs.h" + + +#define TEST_FILE_NAME_LEN 16 +#define TEST_FILES 32 +#define TEST_FILE_MAX_SIZE 8192 + +typedef struct { + char name[TEST_FILE_NAME_LEN]; + uint16_t size; + uint8_t first_data_byte; +} TestFile; + +static TestFile test_files[TEST_FILES]; + +inline static void fill_test_data(uint8_t *src, uint16_t size, uint8_t first_byte) +{ + while (size--) { + *src++ = first_byte++; + } +} + +static bool write_test_files() +{ + uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE); + bool result = true; + + for (uint8_t i = 0; i < TEST_FILES; i++) { + sprintf(test_files[i].name, "file_%d.dat", i); + spiffs_file f = SPIFFS_open(&fs, test_files[i].name, + SPIFFS_CREAT|SPIFFS_RDWR|SPIFFS_TRUNC, 0); + if (f < 0) { + printf("Open file operation failed\n"); + result = false; + break; + } + test_files[i].size = rand() % TEST_FILE_MAX_SIZE; + test_files[i].first_data_byte = rand() % 256; + fill_test_data(buf, test_files[i].size, test_files[i].first_data_byte); + + printf("Writing file %s size=%d\n", test_files[i].name, + test_files[i].size); + int32_t written = SPIFFS_write(&fs, f, buf, test_files[i].size); + if (written != test_files[i].size) { + printf("Write file operation failed, written=%d\n", written); + result = false; + break; + } + SPIFFS_close(&fs, f); + } + free(buf); + return result; +} + +inline static bool verify_test_data(uint8_t *data, uint16_t size, + uint8_t first_byte) +{ + while (size--) { + if (*data++ != first_byte++) { + return false; + } + } + return true; +} + +static bool verify_test_files() +{ + uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE); + bool result = true; + + for (uint8_t i = 0; i < TEST_FILES; i++) { + printf("Verifying file %s\n", test_files[i].name); + spiffs_file f = SPIFFS_open(&fs, test_files[i].name, SPIFFS_RDONLY, 0); + if (f < 0) { + printf("Open file operation failed\n"); + result = false; + break; + } + + int32_t n = SPIFFS_read(&fs, f, buf, test_files[i].size); + if (n != test_files[i].size) { + printf("Read file operation failed\n"); + result = false; + break; + } + + if (!verify_test_data(buf, test_files[i].size, + test_files[i].first_data_byte)) { + printf("Data verification failed\n"); + result = false; + break; + } + + SPIFFS_close(&fs, f); + } + + free(buf); + return result; +} + +static bool cleanup_test_files() +{ + bool result = true; + + for (uint8_t i = 0; i < TEST_FILES; i++) { + printf("Removing file %s\n", test_files[i].name); + if (SPIFFS_remove(&fs, test_files[i].name) != SPIFFS_OK) { + printf("Remove file operation failed\n"); + result = false; + break; + } + } + return result; +} + +inline static void print_info() +{ + uint32_t total, used; + + SPIFFS_info(&fs, &total, &used); + + printf("FS total=%d bytes, used=%d bytes\n", total, used); + printf("FS %d %% used\n", 100 * used/total); + + // File system structure visualisation + // SPIFFS_vis(&fs); +} + +void test_task(void *pvParameters) +{ + bool result = true; + + esp_spiffs_mount(); + esp_spiffs_unmount(); // FS must be unmounted before formating + if (SPIFFS_format(&fs) == SPIFFS_OK) { + printf("Format complete\n"); + } else { + printf("Format failed\n"); + } + esp_spiffs_mount(); + + while (1) { + vTaskDelay(5000 / portTICK_RATE_MS); + + result = write_test_files(); + + if (result) { + result = verify_test_files(); + } + + print_info(); + + if (result) { + result = cleanup_test_files(); + } + + if (result) { + printf("Test passed!\n"); + } else { + printf("Test failed!\n"); + while (1) { + vTaskDelay(1); + } + } + } +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + + xTaskCreate(test_task, (signed char *)"test_task", 1024, NULL, 2, NULL); +} diff --git a/extras/spiffs/component.mk b/extras/spiffs/component.mk new file mode 100644 index 0000000..fcd5572 --- /dev/null +++ b/extras/spiffs/component.mk @@ -0,0 +1,17 @@ +# Component makefile for extras/spiffs + +SPIFFS_BASE_ADDR ?= 0x300000 +SPIFFS_SIZE ?= 0x100000 + +INC_DIRS += $(spiffs_ROOT) +INC_DIRS += $(spiffs_ROOT)spiffs/src + +# args for passing into compile rule generation +spiffs_SRC_DIR = $(spiffs_ROOT)spiffs/src +spiffs_SRC_DIR += $(spiffs_ROOT) + +spiffs_CFLAGS = $(CFLAGS) +spiffs_CFLAGS += -DSPIFFS_BASE_ADDR=$(SPIFFS_BASE_ADDR) +spiffs_CFLAGS += -DSPIFFS_SIZE=$(SPIFFS_SIZE) + +$(eval $(call component_compile_rules,spiffs)) diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c new file mode 100644 index 0000000..d6ec7d6 --- /dev/null +++ b/extras/spiffs/esp_spiffs.c @@ -0,0 +1,187 @@ +/** + * ESP8266 SPIFFS HAL configuration. + * + * Part of esp-open-rtos + * Copyright (c) 2016 sheinz https://github.com/sheinz + * MIT License + */ +#include "esp_spiffs.h" +#include "spiffs.h" +#include +#include + +spiffs fs; + +static void *work_buf = 0; +static void *fds_buf = 0; +static void *cache_buf = 0; + +/* + * Flash addresses and size alignment is a rip-off of Arduino implementation. + */ + +static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) +{ + uint32_t result = SPIFFS_OK; + uint32_t alignedBegin = (addr + 3) & (~3); + uint32_t alignedEnd = (addr + size) & (~3); + if (alignedEnd < alignedBegin) { + alignedEnd = alignedBegin; + } + + if (addr < alignedBegin) { + uint32_t nb = alignedBegin - addr; + uint32_t tmp; + if (sdk_spi_flash_read(alignedEnd - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_read failed\n"); + return SPIFFS_ERR_INTERNAL; + } + memcpy(dst, &tmp + 4 - nb, nb); + } + + if (alignedEnd != alignedBegin) { + if (sdk_spi_flash_read(alignedBegin, + (uint32_t*) (dst + alignedBegin - addr), + alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_read failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + + if (addr + size > alignedEnd) { + uint32_t nb = addr + size - alignedEnd; + uint32_t tmp; + if (sdk_spi_flash_read(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_read failed\n"); + return SPIFFS_ERR_INTERNAL; + } + + memcpy(dst + size - nb, &tmp, nb); + } + + return result; +} + +static const int UNALIGNED_WRITE_BUFFER_SIZE = 512; + +static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) +{ + uint32_t alignedBegin = (addr + 3) & (~3); + uint32_t alignedEnd = (addr + size) & (~3); + if (alignedEnd < alignedBegin) { + alignedEnd = alignedBegin; + } + + if (addr < alignedBegin) { + uint32_t ofs = alignedBegin - addr; + uint32_t nb = (size < ofs) ? size : ofs; + uint8_t tmp[4] __attribute__((aligned(4))) = {0xff, 0xff, 0xff, 0xff}; + memcpy(tmp + 4 - ofs, src, nb); + if (sdk_spi_flash_write(alignedBegin - 4, (uint32_t*) tmp, 4) + != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + + if (alignedEnd != alignedBegin) { + uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr); + uint32_t srcAlign = ((uint32_t) srcLeftover) & 3; + if (!srcAlign) { + if (sdk_spi_flash_write(alignedBegin, (uint32_t*) srcLeftover, + alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + else { + uint8_t buf[UNALIGNED_WRITE_BUFFER_SIZE]; + for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft; ) { + size_t willCopy = sizeLeft < sizeof(buf) ? sizeLeft : sizeof(buf); + memcpy(buf, srcLeftover, willCopy); + + if (sdk_spi_flash_write(alignedBegin, (uint32_t*) buf, willCopy) + != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + + sizeLeft -= willCopy; + srcLeftover += willCopy; + alignedBegin += willCopy; + } + } + } + + if (addr + size > alignedEnd) { + uint32_t nb = addr + size - alignedEnd; + uint32_t tmp = 0xffffffff; + memcpy(&tmp, src + size - nb, nb); + + if (sdk_spi_flash_write(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + + return SPIFFS_OK; +} + +static s32_t esp_spiffs_erase(u32_t addr, u32_t size) +{ + if (addr % SPI_FLASH_SEC_SIZE) { + printf("Unaligned erase addr=%x\n", addr); + } + if (size % SPI_FLASH_SEC_SIZE) { + printf("Unaligned erase size=%d\n", size); + } + + const uint32_t sector = addr / SPI_FLASH_SEC_SIZE; + const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE; + + for (uint32_t i = 0; i < sectorCount; ++i) { + sdk_spi_flash_erase_sector(sector + i); + } + return SPIFFS_OK; +} + +int32_t esp_spiffs_mount() +{ + spiffs_config config = {0}; + + config.hal_read_f = esp_spiffs_read; + config.hal_write_f = esp_spiffs_write; + config.hal_erase_f = esp_spiffs_erase; + + size_t workBufSize = 2 * SPIFFS_CFG_LOG_PAGE_SZ(); + size_t fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&fs, 5); + size_t cacheBufSize = SPIFFS_buffer_bytes_for_cache(&fs, 5); + + work_buf = malloc(workBufSize); + fds_buf = malloc(fdsBufSize); + cache_buf = malloc(cacheBufSize); + printf("spiffs memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", + workBufSize, fdsBufSize, cacheBufSize); + + int32_t err = SPIFFS_mount(&fs, &config, work_buf, fds_buf, fdsBufSize, + cache_buf, cacheBufSize, 0); + + if (err != SPIFFS_OK) { + printf("Error spiffs mount: %d\n", err); + } + + return err; +} + +void esp_spiffs_unmount() +{ + SPIFFS_unmount(&fs); + + free(work_buf); + free(fds_buf); + free(cache_buf); + + work_buf = 0; + fds_buf = 0; + cache_buf = 0; +} diff --git a/extras/spiffs/esp_spiffs.h b/extras/spiffs/esp_spiffs.h new file mode 100644 index 0000000..3022d51 --- /dev/null +++ b/extras/spiffs/esp_spiffs.h @@ -0,0 +1,28 @@ +/** + * ESP8266 SPIFFS HAL configuration. + * + * Part of esp-open-rtos + * Copyright (c) 2016 sheinz https://github.com/sheinz + * MIT License + */ +#ifndef __ESP_SPIFFS_H__ +#define __ESP_SPIFFS_H__ + +#include "spiffs.h" + +extern spiffs fs; + +/** + * Provide SPIFFS with all necessary configuration, allocate memory buffers + * and mount SPIFFS. + * + * Return SPIFFS return code. + */ +int32_t esp_spiffs_mount(); + +/** + * Unmount SPIFFS and free all allocated buffers. + */ +void esp_spiffs_unmount(); + +#endif // __ESP_SPIFFS_H__ diff --git a/extras/spiffs/spiffs b/extras/spiffs/spiffs new file mode 160000 index 0000000..c6e94fd --- /dev/null +++ b/extras/spiffs/spiffs @@ -0,0 +1 @@ +Subproject commit c6e94fdca5c1601b90c027167f8d453c48e482c4 diff --git a/extras/spiffs/spiffs_config.h b/extras/spiffs/spiffs_config.h new file mode 100644 index 0000000..7b8c1a7 --- /dev/null +++ b/extras/spiffs/spiffs_config.h @@ -0,0 +1,263 @@ +/* + * spiffs_config.h + * + * Created on: Jul 3, 2013 + * Author: petera + */ + +#ifndef SPIFFS_CONFIG_H_ +#define SPIFFS_CONFIG_H_ + +// ----------- 8< ------------ +#include +#include +#include +#include +#include +#include +// #include // for vPortEnterCritical/vPortExitCritical +// ----------- >8 ------------ + +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; + +// compile time switches + +// Set generic spiffs debug output call. +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for garbage collecting. +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for caching. +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for system consistency checks. +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__) +#endif + +// 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. +#ifndef SPIFFS_BUFFER_HELP +#define SPIFFS_BUFFER_HELP 1 +#endif + +// Enables/disable memory read caching of nucleus file system operations. +// If enabled, memory area must be provided for cache in SPIFFS_mount. +#ifndef SPIFFS_CACHE +#define SPIFFS_CACHE 1 +#endif +#if SPIFFS_CACHE +// Enables memory write caching for file descriptors in hydrogen +#ifndef SPIFFS_CACHE_WR +#define SPIFFS_CACHE_WR 1 +#endif + +// Enable/disable statistics on caching. Debug/test purpose only. +#ifndef SPIFFS_CACHE_STATS +#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. +#ifndef SPIFFS_PAGE_CHECK +#define SPIFFS_PAGE_CHECK 1 +#endif + +// Define maximum number of gc runs to perform to reach desired free pages. +#ifndef SPIFFS_GC_MAX_RUNS +#define SPIFFS_GC_MAX_RUNS 3 +#endif + +// Enable/disable statistics on gc. Debug/test purpose only. +#ifndef SPIFFS_GC_STATS +#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. +#ifndef SPIFFS_GC_HEUR_W_DELET +#define SPIFFS_GC_HEUR_W_DELET (5) +#endif +// Garbage collecting heuristics - weight used for used pages. +#ifndef SPIFFS_GC_HEUR_W_USED +#define SPIFFS_GC_HEUR_W_USED (-1) +#endif +// Garbage collecting heuristics - weight used for time between +// last erased and erase of this block. +#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE +#define SPIFFS_GC_HEUR_W_ERASE_AGE (50) +#endif + +// 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. +#ifndef SPIFFS_OBJ_NAME_LEN +#define SPIFFS_OBJ_NAME_LEN (32) +#endif + +// 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. +#ifndef SPIFFS_COPY_BUFFER_STACK +#define SPIFFS_COPY_BUFFER_STACK (64) +#endif + +// 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. +#ifndef SPIFFS_USE_MAGIC +#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. +#ifndef SPIFFS_USE_MAGIC_LENGTH +#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 +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) // vPortEnterCritical() +#endif +// define this to exit a mutex if you're running on a multithreaded system +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) // vPortExitCritical() +#endif + +// 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. +#ifndef SPIFFS_SINGLETON +#define SPIFFS_SINGLETON 1 +#endif + +#if SPIFFS_SINGLETON +// Instead of giving parameters in config struct, singleton build must +// give parameters in defines below. +#ifndef SPIFFS_CFG_PHYS_SZ +#define SPIFFS_CFG_PHYS_SZ(ignore) (SPIFFS_SIZE) +#endif +#ifndef SPIFFS_CFG_PHYS_ERASE_SZ +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4*1024) +#endif +#ifndef SPIFFS_CFG_PHYS_ADDR +#define SPIFFS_CFG_PHYS_ADDR(ignore) (SPIFFS_BASE_ADDR) +#endif +#ifndef SPIFFS_CFG_LOG_PAGE_SZ +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) +#endif +#ifndef SPIFFS_CFG_LOG_BLOCK_SZ +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (4*1024) +#endif +#endif + +// Enable this if your target needs aligned data for index tables +#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES +#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1 +#endif + +// Enable this if you want the HAL callbacks to be called with the spiffs struct +#ifndef SPIFFS_HAL_CALLBACK_EXTRA +#define SPIFFS_HAL_CALLBACK_EXTRA 0 +#endif + +// 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. +#ifndef SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FILEHDL_OFFSET 0 +#endif + +// 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. +#ifndef SPIFFS_READ_ONLY +#define SPIFFS_READ_ONLY 0 +#endif + +// 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. +#ifndef SPIFFS_TEST_VISUALISATION +#define SPIFFS_TEST_VISUALISATION 1 +#endif +#if SPIFFS_TEST_VISUALISATION +#ifndef spiffs_printf +#define spiffs_printf(...) printf(__VA_ARGS__) +#endif +// spiffs_printf argument for a free page +#ifndef SPIFFS_TEST_VIS_FREE_STR +#define SPIFFS_TEST_VIS_FREE_STR "_" +#endif +// spiffs_printf argument for a deleted page +#ifndef SPIFFS_TEST_VIS_DELE_STR +#define SPIFFS_TEST_VIS_DELE_STR "/" +#endif +// spiffs_printf argument for an index page for given object id +#ifndef SPIFFS_TEST_VIS_INDX_STR +#define SPIFFS_TEST_VIS_INDX_STR(id) "i" +#endif +// spiffs_printf argument for a data page for given object id +#ifndef SPIFFS_TEST_VIS_DATA_STR +#define SPIFFS_TEST_VIS_DATA_STR(id) "d" +#endif +#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_ */ From b71a7ad23758fd3bf23628ed687e5e0aabee7a26 Mon Sep 17 00:00:00 2001 From: sheinz Date: Thu, 30 Jun 2016 17:38:05 +0300 Subject: [PATCH 2/3] Use SPIFFS for POSIX file access. Draft. Not tested. --- .gitmodules | 3 ++ core/newlib_syscalls.c | 20 ++++++++--- examples/posix_fs/Makefile | 11 ++++++ examples/posix_fs/fs-test | 1 + examples/posix_fs/posix_fs_example.c | 41 ++++++++++++++++++++++ extras/spiffs/esp_spiffs.c | 51 ++++++++++++++++++++++++++++ 6 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 examples/posix_fs/Makefile create mode 160000 examples/posix_fs/fs-test create mode 100644 examples/posix_fs/posix_fs_example.c diff --git a/.gitmodules b/.gitmodules index 00dadd8..d3fc285 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,3 +14,6 @@ [submodule "extras/spiffs/spiffs"] path = extras/spiffs/spiffs url = https://github.com/pellepl/spiffs.git +[submodule "examples/posix_fs/fs-test"] + path = examples/posix_fs/fs-test + url = https://github.com/sheinz/fs-test diff --git a/core/newlib_syscalls.c b/core/newlib_syscalls.c index 023872c..c8104a9 100644 --- a/core/newlib_syscalls.c +++ b/core/newlib_syscalls.c @@ -41,7 +41,7 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr) } /* syscall implementation for stdio write to UART */ -long _write_r(struct _reent *r, int fd, const char *ptr, int len ) +__attribute__((weak)) long _write_r(struct _reent *r, int fd, const char *ptr, int len ) { if(fd != r->_stdout->_file) { r->_errno = EBADF; @@ -79,10 +79,20 @@ __attribute__((weak)) long _read_r( struct _reent *r, int fd, char *ptr, int len /* Stub syscall implementations follow, to allow compiling newlib functions that pull these in via various codepaths */ -__attribute__((alias("syscall_returns_enosys"))) int _open_r(struct _reent *r, const char *pathname, int flags, int mode); -__attribute__((alias("syscall_returns_enosys"))) int _fstat_r(struct _reent *r, int fd, void *buf); -__attribute__((alias("syscall_returns_enosys"))) int _close_r(struct _reent *r, int fd); -__attribute__((alias("syscall_returns_enosys"))) off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence); +__attribute__((weak, alias("syscall_returns_enosys"))) +int _open_r(struct _reent *r, const char *pathname, int flags, int mode); + +__attribute__((weak, alias("syscall_returns_enosys"))) +int _close_r(struct _reent *r, int fd); + +__attribute__((weak, alias("syscall_returns_enosys"))) +int _unlink_r(struct _reent *r, const char *path); + +__attribute__((alias("syscall_returns_enosys"))) +int _fstat_r(struct _reent *r, int fd, void *buf); + +__attribute__((alias("syscall_returns_enosys"))) +off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence); /* Generic stub for any newlib syscall that fails with errno ENOSYS ("Function not implemented") and a return value equivalent to diff --git a/examples/posix_fs/Makefile b/examples/posix_fs/Makefile new file mode 100644 index 0000000..bf45ed7 --- /dev/null +++ b/examples/posix_fs/Makefile @@ -0,0 +1,11 @@ +PROGRAM=posix_fs_example +PROGRAM_EXTRA_SRC_FILES=./fs-test/fs_test.c + +EXTRA_COMPONENTS = extras/spiffs +FLASH_SIZE = 32 + +# spiffs configuration +SPIFFS_BASE_ADDR = 0x200000 +SPIFFS_SIZE = 0x100000 + +include ../../common.mk diff --git a/examples/posix_fs/fs-test b/examples/posix_fs/fs-test new file mode 160000 index 0000000..983ed83 --- /dev/null +++ b/examples/posix_fs/fs-test @@ -0,0 +1 @@ +Subproject commit 983ed830a8d2bd1a3eaa586ed608530f9d29201e diff --git a/examples/posix_fs/posix_fs_example.c b/examples/posix_fs/posix_fs_example.c new file mode 100644 index 0000000..7e3ad8f --- /dev/null +++ b/examples/posix_fs/posix_fs_example.c @@ -0,0 +1,41 @@ +#include "espressif/esp_common.h" +#include "esp/uart.h" +#include "FreeRTOS.h" +#include "task.h" +#include "esp8266.h" +#include + +#include "esp_spiffs.h" +#include "spiffs.h" + +#include "fs-test/fs_test.h" + + +void test_task(void *pvParameters) +{ + esp_spiffs_mount(); + esp_spiffs_unmount(); // FS must be unmounted before formating + if (SPIFFS_format(&fs) == SPIFFS_OK) { + printf("Format complete\n"); + } else { + printf("Format failed\n"); + } + esp_spiffs_mount(); + + while (1) { + vTaskDelay(5000 / portTICK_RATE_MS); + + if (fs_test_run(10000)) { + printf("PASS\n"); + } else { + printf("FAIL\n"); + } + } +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + + xTaskCreate(test_task, (signed char *)"test_task", 1024, NULL, 2, NULL); +} diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index d6ec7d6..194d697 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -9,6 +9,7 @@ #include "spiffs.h" #include #include +#include spiffs fs; @@ -185,3 +186,53 @@ void esp_spiffs_unmount() fds_buf = 0; cache_buf = 0; } + +/* syscall implementation for stdio write to UART */ +long _write_r(struct _reent *r, int fd, const char *ptr, int len ) +{ + if(fd != r->_stdout->_file) { + return SPIFFS_write(&fs, (spiffs_file)fd, (char*)ptr, len); + } + for(int i = 0; i < len; i++) { + /* Auto convert CR to CRLF, ignore other LFs (compatible with Espressif SDK behaviour) */ + if(ptr[i] == '\r') + continue; + if(ptr[i] == '\n') + uart_putc(0, '\r'); + uart_putc(0, ptr[i]); + } + return len; +} + +/* syscall implementation for stdio read from UART */ +long _read_r( struct _reent *r, int fd, char *ptr, int len ) +{ + int ch, i; + + if(fd != r->_stdin->_file) { + return SPIFFS_read(&fs, (spiffs_file)fd, ptr, len); + } + uart_rxfifo_wait(0, 1); + for(i = 0; i < len; i++) { + ch = uart_getc_nowait(0); + if (ch < 0) break; + ptr[i] = ch; + } + return i; +} + +/* syscall implementation for stdio write to UART */ +int _open_r(struct _reent *r, const char *pathname, int flags, int mode) +{ + return SPIFFS_open(&fs, pathname, flags, mode); +} + +int _close_r(struct _reent *r, int fd) +{ + return SPIFFS_close(&fs, (spiffs_file)fd); +} + +int _unlink_r(struct _reent *r, const char *path) +{ + return SPIFFS_remove(&fs, path); +} From 0f9d991ba7fe84655de9f2a57c98bb89492533c5 Mon Sep 17 00:00:00 2001 From: sheinz Date: Thu, 30 Jun 2016 22:18:07 +0300 Subject: [PATCH 3/3] Fixed libc and SPIFFS integration. Test passes on ESP-12E module. --- examples/posix_fs/fs-test | 2 +- examples/posix_fs/posix_fs_example.c | 2 +- extras/spiffs/esp_spiffs.c | 31 +++++++++++++++++++++------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/examples/posix_fs/fs-test b/examples/posix_fs/fs-test index 983ed83..218c523 160000 --- a/examples/posix_fs/fs-test +++ b/examples/posix_fs/fs-test @@ -1 +1 @@ -Subproject commit 983ed830a8d2bd1a3eaa586ed608530f9d29201e +Subproject commit 218c5235584429f407d619e5e35f90732ad505f3 diff --git a/examples/posix_fs/posix_fs_example.c b/examples/posix_fs/posix_fs_example.c index 7e3ad8f..d84be5e 100644 --- a/examples/posix_fs/posix_fs_example.c +++ b/examples/posix_fs/posix_fs_example.c @@ -25,7 +25,7 @@ void test_task(void *pvParameters) while (1) { vTaskDelay(5000 / portTICK_RATE_MS); - if (fs_test_run(10000)) { + if (fs_test_run(1000)) { printf("PASS\n"); } else { printf("FAIL\n"); diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index 194d697..4b5ef77 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -10,6 +10,7 @@ #include #include #include +#include spiffs fs; @@ -187,11 +188,15 @@ void esp_spiffs_unmount() cache_buf = 0; } -/* syscall implementation for stdio write to UART */ +#define FD_OFFSET 3 + +// This implementation replaces implementation in core/newlib_syscals.c long _write_r(struct _reent *r, int fd, const char *ptr, int len ) { if(fd != r->_stdout->_file) { - return SPIFFS_write(&fs, (spiffs_file)fd, (char*)ptr, len); + long ret = SPIFFS_write(&fs, (spiffs_file)(fd - FD_OFFSET), + (char*)ptr, len); + return ret; } for(int i = 0; i < len; i++) { /* Auto convert CR to CRLF, ignore other LFs (compatible with Espressif SDK behaviour) */ @@ -204,13 +209,14 @@ long _write_r(struct _reent *r, int fd, const char *ptr, int len ) return len; } -/* syscall implementation for stdio read from UART */ +// This implementation replaces implementation in core/newlib_syscals.c long _read_r( struct _reent *r, int fd, char *ptr, int len ) { int ch, i; if(fd != r->_stdin->_file) { - return SPIFFS_read(&fs, (spiffs_file)fd, ptr, len); + long ret = SPIFFS_read(&fs, (spiffs_file)(fd - FD_OFFSET), ptr, len); + return ret; } uart_rxfifo_wait(0, 1); for(i = 0; i < len; i++) { @@ -221,15 +227,26 @@ long _read_r( struct _reent *r, int fd, char *ptr, int len ) return i; } -/* syscall implementation for stdio write to UART */ int _open_r(struct _reent *r, const char *pathname, int flags, int mode) { - return SPIFFS_open(&fs, pathname, flags, mode); + uint32_t spiffs_flags = SPIFFS_RDONLY; + + if (flags & O_CREAT) spiffs_flags |= SPIFFS_CREAT; + if (flags & O_APPEND) spiffs_flags |= SPIFFS_APPEND; + if (flags & O_TRUNC) spiffs_flags |= SPIFFS_TRUNC; + if (flags & O_RDONLY) spiffs_flags |= SPIFFS_RDONLY; + if (flags & O_WRONLY) spiffs_flags |= SPIFFS_WRONLY; + + int ret = SPIFFS_open(&fs, pathname, spiffs_flags, mode); + if (ret > 0) { + return ret + FD_OFFSET; + } + return ret; } int _close_r(struct _reent *r, int fd) { - return SPIFFS_close(&fs, (spiffs_file)fd); + return SPIFFS_close(&fs, (spiffs_file)(fd - FD_OFFSET)); } int _unlink_r(struct _reent *r, const char *path)