diff --git a/.gitmodules b/.gitmodules index 7a3370e..5dddd18 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,4 +10,9 @@ [submodule "bootloader/rboot"] 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 +[submodule "examples/posix_fs/fs-test"] + path = examples/posix_fs/fs-test + url = https://github.com/sheinz/fs-test diff --git a/common.mk b/common.mk index 172fbe2..c3923c8 100644 --- a/common.mk +++ b/common.mk @@ -68,7 +68,7 @@ Q := @ vecho := @echo endif -.PHONY: all clean flash erase_flash +.PHONY: all clean flash erase_flash test size rebuild all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE) @@ -209,8 +209,9 @@ $(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR) $(vecho) "FW $@" $(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE) -flash: $(FW_FILE) - $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE) +flash: all + $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) \ + 0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE) $(SPIFFS_ESPTOOL_ARGS) erase_flash: $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash diff --git a/core/newlib_syscalls.c b/core/newlib_syscalls.c index 023872c..577ecda 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,23 @@ __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__((weak, alias("syscall_returns_enosys"))) +int _fstat_r(struct _reent *r, int fd, void *buf); + +__attribute__((weak, alias("syscall_returns_enosys"))) +int _stat_r(struct _reent *r, const char *pathname, void *buf); + +__attribute__((weak, 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/README.md b/examples/posix_fs/README.md new file mode 100644 index 0000000..7f05bd4 --- /dev/null +++ b/examples/posix_fs/README.md @@ -0,0 +1,10 @@ +# POSIX file access example + +This example runs several file system tests on ESP8266. +It uses fs-test library to perform file operations test. fs-test library uses +only POSIX file functions so can be run on host system as well. + +Currently included tests: + * File system load test. Perform multiple file operations in random order. + * File system speed test. Measures files read/write speed. + diff --git a/examples/posix_fs/fs-test b/examples/posix_fs/fs-test new file mode 160000 index 0000000..2ad547a --- /dev/null +++ b/examples/posix_fs/fs-test @@ -0,0 +1 @@ +Subproject commit 2ad547adc5f725594b3c6752f036ff4401b221fc diff --git a/examples/posix_fs/posix_fs_example.c b/examples/posix_fs/posix_fs_example.c new file mode 100644 index 0000000..2f5ad14 --- /dev/null +++ b/examples/posix_fs/posix_fs_example.c @@ -0,0 +1,55 @@ +#include "espressif/esp_common.h" +#include "esp/uart.h" +#include "esp/timer.h" +#include "FreeRTOS.h" +#include "task.h" +#include "esp8266.h" +#include + +#include "esp_spiffs.h" +#include "spiffs.h" + +#include "fs-test/fs_test.h" + +static fs_time_t get_current_time() +{ + return timer_get_count(FRC2) / 5000; // to get roughly 1ms resolution +} + +void test_task(void *pvParameters) +{ + esp_spiffs_init(); + esp_spiffs_mount(); + SPIFFS_unmount(&fs); // 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_load_test_run(100)) { + printf("PASS\n"); + } else { + printf("FAIL\n"); + } + + vTaskDelay(5000 / portTICK_RATE_MS); + float write_rate, read_rate; + if (fs_speed_test_run(get_current_time, &write_rate, &read_rate)) { + printf("Read speed: %.0f bytes/s\n", read_rate * 1000); + printf("Write speed: %.0f bytes/s\n", write_rate * 1000); + } 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/examples/spiffs/Makefile b/examples/spiffs/Makefile new file mode 100644 index 0000000..7327707 --- /dev/null +++ b/examples/spiffs/Makefile @@ -0,0 +1,11 @@ +PROGRAM=spiffs_example +EXTRA_COMPONENTS = extras/spiffs +FLASH_SIZE = 32 + +# spiffs configuration +SPIFFS_BASE_ADDR = 0x200000 +SPIFFS_SIZE = 0x010000 + +include ../../common.mk + +$(eval $(call make_spiffs_image,files)) diff --git a/examples/spiffs/files/test.txt b/examples/spiffs/files/test.txt new file mode 100644 index 0000000..c86fb0c --- /dev/null +++ b/examples/spiffs/files/test.txt @@ -0,0 +1 @@ +This file will go to SPIFFS image. diff --git a/examples/spiffs/spiffs_example.c b/examples/spiffs/spiffs_example.c new file mode 100644 index 0000000..98decaf --- /dev/null +++ b/examples/spiffs/spiffs_example.c @@ -0,0 +1,104 @@ +#include "espressif/esp_common.h" +#include "esp/uart.h" +#include "FreeRTOS.h" +#include "task.h" +#include "esp8266.h" + +#include "fcntl.h" +#include "unistd.h" + +#include "spiffs.h" +#include "esp_spiffs.h" + + +static void example_read_file_posix() +{ + const int buf_size = 0xFF; + uint8_t buf[buf_size]; + + int fd = open("test.txt", O_RDONLY); + if (fd < 0) { + printf("Error opening file\n"); + return; + } + + int read_bytes = read(fd, buf, buf_size); + printf("Read %d bytes\n", read_bytes); + + buf[read_bytes] = '\0'; // zero terminate string + printf("Data: %s\n", buf); + + close(fd); +} + +static void example_read_file_spiffs() +{ + const int buf_size = 0xFF; + uint8_t buf[buf_size]; + + spiffs_file fd = SPIFFS_open(&fs, "other.txt", SPIFFS_RDONLY, 0); + if (fd < 0) { + printf("Error opening file\n"); + return; + } + + int read_bytes = SPIFFS_read(&fs, fd, buf, buf_size); + printf("Read %d bytes\n", read_bytes); + + buf[read_bytes] = '\0'; // zero terminate string + printf("Data: %s\n", buf); + + SPIFFS_close(&fs, fd); +} + +static void example_write_file() +{ + uint8_t buf[] = "Example data, written by ESP8266"; + + int fd = open("other.txt", O_WRONLY|O_CREAT, 0); + if (fd < 0) { + printf("Error opening file\n"); + return; + } + + int written = write(fd, buf, sizeof(buf)); + printf("Written %d bytes\n", written); + + close(fd); +} + +static void example_fs_info() +{ + uint32_t total, used; + SPIFFS_info(&fs, &total, &used); + printf("Total: %d bytes, used: %d bytes", total, used); +} + +void test_task(void *pvParameters) +{ + esp_spiffs_init(); + if (esp_spiffs_mount() != SPIFFS_OK) { + printf("Error mount SPIFFS\n"); + } + + while (1) { + vTaskDelay(2000 / portTICK_RATE_MS); + + example_write_file(); + + example_read_file_posix(); + + example_read_file_spiffs(); + + example_fs_info(); + + printf("\n\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/README.md b/extras/spiffs/README.md new file mode 100644 index 0000000..76d2081 --- /dev/null +++ b/extras/spiffs/README.md @@ -0,0 +1,153 @@ +# SPIFFS ESP8266 File system + +This component adds file system support for ESP8266. File system of choice +for ESP8266 is [SPIFFS](https://github.com/pellepl/spiffs). +It was specifically designed to use with SPI NOR flash on embedded systems. +The main advantage of SPIFFS is wear leveling, which prolongs life time +of a flash memory. + +## Features + + * SPIFFS - embedded file system for NOR flash memory. + * POSIX file operations. + * Static files upload to ESP8266 file system within build process. + * SPIFFS singleton configuration. Only one instance of FS on a device. + +## Usage + +In order to use file system in a project the following steps should be made: + * Add SPIFFS component in a project Makefile `EXTRA_COMPONENTS = extras/spiffs` + * Specify your flash size in the Makefile `FLASH_SIZE = 32` + * Specify the start address of file system region on the flash memory +`SPIFFS_BASE_ADDR = 0x200000` + * If you want to upload files to a file system during flash process specify +the directory with files `$(eval $(call make_spiffs_image,files))` + +In the end the Makefile should look like: + +``` +PROGRAM=spiffs_example +EXTRA_COMPONENTS = extras/spiffs +FLASH_SIZE = 32 + +SPIFFS_BASE_ADDR = 0x200000 +SPIFFS_SIZE = 0x100000 + +include ../../common.mk + +$(eval $(call make_spiffs_image,files)) +``` + +Note: Macro call to prepare SPIFFS image for flashing should go after +`include common.mk` + +### Files upload + +To upload files to a file system during flash process the following macro is +used: + +``` +$(eval $(call make_spiffs_image,files)) +``` + +It enables the build of a helper utility **mkspiffs**. This utility creates +an SPIFFS image with files in the specified directory. + +The SPIFFS image is created during build stage, after `make` is run. +The image is flashed into the device along with firmware during flash stage, +after `make flash` is run. + +**mkspiffs** utility uses the same SPIFFS source code and the same +configuration as ESP8266. So the created image should always be compatible +with SPIFFS on a device. + +The build process will catch any changes in files directory and rebuild the +image each time `make` is run. +The build process will handle SPIFFS_SIZE change and rebuild **mkspiffs** +utility and the image. + +## Example + +### Mount + +``` +esp_spiffs_init(); // allocate memory buffers +if (esp_spiffs_mount() != SPIFFS_OK) { + printf("Error mounting SPIFFS\n"); +} +``` + +### Format + +Formatting SPIFFS is a little bit awkward. Before formatting SPIFFS must be +mounted and unmounted. +``` +esp_spiffs_init(); +if (esp_spiffs_mount() != SPIFFS_OK) { + printf("Error mount SPIFFS\n"); +} +SPIFFS_unmount(&fs); // FS must be unmounted before formating +if (SPIFFS_format(&fs) == SPIFFS_OK) { + printf("Format complete\n"); +} else { + printf("Format failed\n"); +} +esp_spiffs_mount(); +``` + +### POSIX read + +Nothing special here. + +``` +const int buf_size = 0xFF; +uint8_t buf[buf_size]; + +int fd = open("test.txt", O_RDONLY); +if (fd < 0) { + printf("Error opening file\n"); +} + +read(fd, buf, buf_size); +printf("Data: %s\n", buf); + +close(fd); +``` + +### SPIFFS read + +SPIFFS interface is intended to be as close to POSIX as possible. + +``` +const int buf_size = 0xFF; +uint8_t buf[buf_size]; + +spiffs_file fd = SPIFFS_open(&fs, "other.txt", SPIFFS_RDONLY, 0); +if (fd < 0) { + printf("Error opening file\n"); +} + +SPIFFS_read(&fs, fd, buf, buf_size); +printf("Data: %s\n", buf); + +SPIFFS_close(&fs, fd); +``` + +### POSIX write + +``` +uint8_t buf[] = "Example data, written by ESP8266"; + +int fd = open("other.txt", O_WRONLY|O_CREAT, 0); +if (fd < 0) { + printf("Error opening file\n"); +} + +write(fd, buf, sizeof(buf)); + +close(fd); +``` + +## Resources + +[SPIFFS](https://github.com/pellepl/spiffs) diff --git a/extras/spiffs/component.mk b/extras/spiffs/component.mk new file mode 100644 index 0000000..f7f5a49 --- /dev/null +++ b/extras/spiffs/component.mk @@ -0,0 +1,65 @@ +# 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) + + +# Create an SPIFFS image of specified directory and flash it with +# the rest of the firmware. +# +# Argumens: +# $(1) - directory with files which go into spiffs image +# +# Example: +# $(eval $(call make_spiffs_image,files)) +define make_spiffs_image +SPIFFS_IMAGE = $(addprefix $(FIRMWARE_DIR),spiffs.bin) +MKSPIFFS_DIR = $(ROOT)/extras/spiffs/mkspiffs +MKSPIFFS = $$(MKSPIFFS_DIR)/mkspiffs +SPIFFS_FILE_LIST = $(shell find $(1)) + +all: $$(SPIFFS_IMAGE) + +clean: clean_spiffs_img clean_mkspiffs + +$$(SPIFFS_IMAGE): $$(MKSPIFFS) $$(SPIFFS_FILE_LIST) + $$< $(1) $$@ + +# Rebuild SPIFFS if Makefile is changed, where SPIFF_SIZE is defined +$$(spiffs_ROOT)spiffs_config.h: Makefile + $$(Q) touch $$@ + +$$(MKSPIFFS)_MAKE: + $$(MAKE) -C $$(MKSPIFFS_DIR) SPIFFS_SIZE=$(SPIFFS_SIZE) + +# if SPIFFS_SIZE in Makefile is changed rebuild mkspiffs +$$(MKSPIFFS): Makefile + $$(MAKE) -C $$(MKSPIFFS_DIR) clean + $$(MAKE) -C $$(MKSPIFFS_DIR) SPIFFS_SIZE=$(SPIFFS_SIZE) + +clean_spiffs_img: + $$(Q) rm -f $$(SPIFFS_IMAGE) + +clean_mkspiffs: + $$(Q) $$(MAKE) -C $$(MKSPIFFS_DIR) clean + +# run make for mkspiffs always +all: $$(MKSPIFFS)_MAKE + +.PHONY: $$(MKSPIFFS)_MAKE + +SPIFFS_ESPTOOL_ARGS = $(SPIFFS_BASE_ADDR) $$(SPIFFS_IMAGE) +endef + +$(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..9ae4dea --- /dev/null +++ b/extras/spiffs/esp_spiffs.c @@ -0,0 +1,198 @@ +/** + * 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 +#include +#include +#include "esp_spiffs_flash.h" + +spiffs fs; + +typedef struct { + void *buf; + uint32_t size; +} fs_buf_t; + +static fs_buf_t work_buf = {0}; +static fs_buf_t fds_buf = {0}; +static fs_buf_t cache_buf = {0}; + +/** + * Number of file descriptors opened at the same time + */ +#define ESP_SPIFFS_FD_NUMBER 5 + +#define ESP_SPIFFS_CACHE_PAGES 5 + +static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) +{ + if (esp_spiffs_flash_read(addr, dst, size) == ESP_SPIFFS_FLASH_ERROR) { + return SPIFFS_ERR_INTERNAL; + } + + return SPIFFS_OK; +} + +static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) +{ + if (esp_spiffs_flash_write(addr, src, size) == ESP_SPIFFS_FLASH_ERROR) { + return SPIFFS_ERR_INTERNAL; + } + + return SPIFFS_OK; +} + +static s32_t esp_spiffs_erase(u32_t addr, u32_t size) +{ + uint32_t sectors = size / SPI_FLASH_SEC_SIZE; + + for (uint32_t i = 0; i < sectors; i++) { + if (esp_spiffs_flash_erase_sector(addr + (SPI_FLASH_SEC_SIZE * i)) + == ESP_SPIFFS_FLASH_ERROR) { + return SPIFFS_ERR_INTERNAL; + } + } + + return SPIFFS_OK; +} + +void esp_spiffs_init() +{ + work_buf.size = 2 * SPIFFS_CFG_LOG_PAGE_SZ(); + fds_buf.size = SPIFFS_buffer_bytes_for_filedescs(&fs, ESP_SPIFFS_FD_NUMBER); + cache_buf.size= SPIFFS_buffer_bytes_for_cache(&fs, ESP_SPIFFS_CACHE_PAGES); + + work_buf.buf = malloc(work_buf.size); + fds_buf.buf = malloc(fds_buf.size); + cache_buf.buf = malloc(cache_buf.size); +} + +void esp_spiffs_deinit() +{ + free(work_buf.buf); + work_buf.buf = 0; + + free(fds_buf.buf); + fds_buf.buf = 0; + + free(cache_buf.buf); + cache_buf.buf = 0; +} + +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; + + config.fh_ix_offset = 3; + + printf("SPIFFS size: %d\n", SPIFFS_SIZE); + printf("SPIFFS memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", + work_buf.size, fds_buf.size, cache_buf.size); + + int32_t err = SPIFFS_mount(&fs, &config, (uint8_t*)work_buf.buf, + (uint8_t*)fds_buf.buf, fds_buf.size, + cache_buf.buf, cache_buf.size, 0); + + if (err != SPIFFS_OK) { + printf("Error spiffs mount: %d\n", err); + } + + return err; +} + +// 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); + } + 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; +} + +// 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); + } + 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; +} + +int _open_r(struct _reent *r, const char *pathname, int flags, int 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; + if (flags & O_EXCL) spiffs_flags |= SPIFFS_EXCL; + /* if (flags & O_DIRECT) spiffs_flags |= SPIFFS_DIRECT; no support in newlib */ + + return SPIFFS_open(&fs, pathname, spiffs_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); +} + +int _fstat_r(struct _reent *r, int fd, void *buf) +{ + spiffs_stat s; + struct stat *sb = (struct stat*)buf; + + int result = SPIFFS_fstat(&fs, (spiffs_file)fd, &s); + sb->st_size = s.size; + + return result; +} + +int _stat_r(struct _reent *r, const char *pathname, void *buf) +{ + spiffs_stat s; + struct stat *sb = (struct stat*)buf; + + int result = SPIFFS_stat(&fs, pathname, &s); + sb->st_size = s.size; + + return result; +} + +off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence) +{ + return SPIFFS_lseek(&fs, (spiffs_file)fd, offset, whence); +} diff --git a/extras/spiffs/esp_spiffs.h b/extras/spiffs/esp_spiffs.h new file mode 100644 index 0000000..60dc7f0 --- /dev/null +++ b/extras/spiffs/esp_spiffs.h @@ -0,0 +1,39 @@ +/** + * 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; + +/** + * Prepare for SPIFFS mount. + * + * The function allocates all the necessary buffers. + */ +void esp_spiffs_init(); + +/** + * Free all memory buffers that were used by SPIFFS. + * + * The function should be called after SPIFFS unmount if the file system is not + * going to need any more. + */ +void esp_spiffs_deinit(); + +/** + * Mount SPIFFS. + * + * esp_spiffs_init must be called first. + * + * Return SPIFFS return code. + */ +int32_t esp_spiffs_mount(); + +#endif // __ESP_SPIFFS_H__ diff --git a/extras/spiffs/esp_spiffs_flash.c b/extras/spiffs/esp_spiffs_flash.c new file mode 100644 index 0000000..f0a6049 --- /dev/null +++ b/extras/spiffs/esp_spiffs_flash.c @@ -0,0 +1,273 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "esp_spiffs_flash.h" +#include "flashchip.h" +#include "espressif/spi_flash.h" +#include "FreeRTOS.h" +#include "esp/rom.h" +#include "esp/spi_regs.h" +#include + +/** + * Note about Wait_SPI_Idle. + * + * Each write/erase flash operation sets BUSY bit in flash status register. + * If attempt to access flash while BUSY bit is set operation will fail. + * Function Wait_SPI_Idle loops until this bit is not cleared. + * + * The approach in the following code is that each write function that is + * accessible from the outside should leave flash in Idle state. + * The read operations doesn't set BUSY bit in a flash. So they do not wait. + * They relay that previous operation is completely finished. + * + * This approach is different from ESP8266 bootrom where Wait_SPI_Idle is + * called where it needed and not. + */ + +#define SPI_WRITE_MAX_SIZE 64 + +// 64 bytes read causes hang +// http://bbs.espressif.com/viewtopic.php?f=6&t=2439 +#define SPI_READ_MAX_SIZE 60 + + +/** + * Copy unaligned data to 4-byte aligned destination buffer. + * + * @param words Number of 4-byte words to write. + * + * @see unaligned_memcpy.S + */ +void memcpy_unaligned_src(volatile uint32_t *dst, uint8_t *src, uint8_t words); + +/** + * Copy 4-byte aligned source data to unaligned destination buffer. + * + * @param bytes Number of byte to copy to dst. + * + * @see unaligned_memcpy.S + */ +void memcpy_unaligned_dst(uint8_t *dst, volatile uint32_t *src, uint8_t bytes); + + +/** + * Low level SPI flash write. Write block of data up to 64 bytes. + */ +static inline void IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr, + uint8_t *buf, uint32_t size) +{ + uint32_t words = size >> 2; + if (size & 0b11) { + words++; + } + + Wait_SPI_Idle(chip); // wait for previous write to finish + + SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24); + + memcpy_unaligned_src(SPI(0).W, buf, words); + + SPI_write_enable(chip); + + SPI(0).CMD = SPI_CMD_PP; + while (SPI(0).CMD) {} +} + +/** + * Write a page of flash. Data block should not cross page boundary. + */ +static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_addr, + uint8_t *buf, uint32_t size) +{ + // check if block to write doesn't cross page boundary + if (flashchip->page_size < size + (dest_addr % flashchip->page_size)) { + return ESP_SPIFFS_FLASH_ERROR; + } + + if (size < 1) { + return ESP_SPIFFS_FLASH_OK; + } + + while (size >= SPI_WRITE_MAX_SIZE) { + spi_write_data(flashchip, dest_addr, buf, SPI_WRITE_MAX_SIZE); + + size -= SPI_WRITE_MAX_SIZE; + dest_addr += SPI_WRITE_MAX_SIZE; + buf += SPI_WRITE_MAX_SIZE; + + if (size < 1) { + return ESP_SPIFFS_FLASH_OK; + } + } + + spi_write_data(flashchip, dest_addr, buf, size); + + return ESP_SPIFFS_FLASH_OK; +} + +/** + * Split block of data into pages and write pages. + */ +static uint32_t IRAM spi_write(uint32_t addr, uint8_t *dst, uint32_t size) +{ + if (sdk_flashchip.chip_size < (addr + size)) { + return ESP_SPIFFS_FLASH_ERROR; + } + + uint32_t write_bytes_to_page = sdk_flashchip.page_size - + (addr % sdk_flashchip.page_size); // TODO: place for optimization + + if (size < write_bytes_to_page) { + if (spi_write_page(&sdk_flashchip, addr, dst, size)) { + return ESP_SPIFFS_FLASH_ERROR; + } + } else { + if (spi_write_page(&sdk_flashchip, addr, dst, write_bytes_to_page)) { + return ESP_SPIFFS_FLASH_ERROR; + } + + uint32_t offset = write_bytes_to_page; + uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size; + for (uint8_t i = 0; i != pages_to_write; i++) { + if (spi_write_page(&sdk_flashchip, addr + offset, + dst + offset, sdk_flashchip.page_size)) { + return ESP_SPIFFS_FLASH_ERROR; + } + offset += sdk_flashchip.page_size; + } + + if (spi_write_page(&sdk_flashchip, addr + offset, + dst + offset, size - offset)) { + return ESP_SPIFFS_FLASH_ERROR; + } + } + + return ESP_SPIFFS_FLASH_OK; +} + +uint32_t IRAM esp_spiffs_flash_write(uint32_t addr, uint8_t *buf, uint32_t size) +{ + uint32_t result = ESP_SPIFFS_FLASH_ERROR; + + if (buf) { + vPortEnterCritical(); + Cache_Read_Disable(); + + result = spi_write(addr, buf, size); + + // make sure all write operations is finished before exiting + Wait_SPI_Idle(&sdk_flashchip); + + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + } + + return result; +} + +/** + * Read SPI flash up to 64 bytes. + */ +static inline void IRAM read_block(sdk_flashchip_t *chip, uint32_t addr, + uint8_t *buf, uint32_t size) +{ + SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24); + SPI(0).CMD = SPI_CMD_READ; + + while (SPI(0).CMD) {}; + + memcpy_unaligned_dst(buf, SPI(0).W, size); +} + +/** + * Read SPI flash data. Data region doesn't need to be page aligned. + */ +static inline uint32_t IRAM read_data(sdk_flashchip_t *flashchip, uint32_t addr, + uint8_t *dst, uint32_t size) +{ + if (size < 1) { + return ESP_SPIFFS_FLASH_OK; + } + + if ((addr + size) > flashchip->chip_size) { + return ESP_SPIFFS_FLASH_ERROR; + } + + while (size >= SPI_READ_MAX_SIZE) { + read_block(flashchip, addr, dst, SPI_READ_MAX_SIZE); + dst += SPI_READ_MAX_SIZE; + size -= SPI_READ_MAX_SIZE; + addr += SPI_READ_MAX_SIZE; + } + + if (size > 0) { + read_block(flashchip, addr, dst, size); + } + + return ESP_SPIFFS_FLASH_OK; +} + +uint32_t IRAM esp_spiffs_flash_read(uint32_t dest_addr, uint8_t *buf, uint32_t size) +{ + uint32_t result = ESP_SPIFFS_FLASH_ERROR; + + if (buf) { + vPortEnterCritical(); + Cache_Read_Disable(); + + result = read_data(&sdk_flashchip, dest_addr, buf, size); + + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + } + + return result; +} + +uint32_t IRAM esp_spiffs_flash_erase_sector(uint32_t addr) +{ + if ((addr + sdk_flashchip.sector_size) > sdk_flashchip.chip_size) { + return ESP_SPIFFS_FLASH_ERROR; + } + + if (addr & 0xFFF) { + return ESP_SPIFFS_FLASH_ERROR; + } + + vPortEnterCritical(); + Cache_Read_Disable(); + + SPI_write_enable(&sdk_flashchip); + + SPI(0).ADDR = addr & 0x00FFFFFF; + SPI(0).CMD = SPI_CMD_SE; + while (SPI(0).CMD) {}; + + Wait_SPI_Idle(&sdk_flashchip); + + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + + return ESP_SPIFFS_FLASH_OK; +} diff --git a/extras/spiffs/esp_spiffs_flash.h b/extras/spiffs/esp_spiffs_flash.h new file mode 100644 index 0000000..abfe864 --- /dev/null +++ b/extras/spiffs/esp_spiffs_flash.h @@ -0,0 +1,64 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef __ESP_SPIFFS_FLASH_H__ +#define __ESP_SPIFFS_FLASH_H__ + +#include +#include "common_macros.h" + +#define ESP_SPIFFS_FLASH_OK 0 +#define ESP_SPIFFS_FLASH_ERROR 1 + +/** + * Read data from SPI flash. + * + * @param addr Address to read from. Can be not aligned. + * @param buf Buffer to read to. Doesn't have to be aligned. + * @param size Size of data to read. Buffer size must be >= than data size. + * + * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR + */ +uint32_t IRAM esp_spiffs_flash_read(uint32_t addr, uint8_t *buf, uint32_t size); + +/** + * Write data to SPI flash. + * + * @param addr Address to write to. Can be not aligned. + * @param buf Buffer of data to write to flash. Doesn't have to be aligned. + * @param size Size of data to write. Buffer size must be >= than data size. + * + * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR + */ +uint32_t IRAM esp_spiffs_flash_write(uint32_t addr, uint8_t *buf, uint32_t size); + +/** + * Erase a sector. + * + * @param addr Address of sector to erase. Must be sector aligned. + * + * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR + */ +uint32_t IRAM esp_spiffs_flash_erase_sector(uint32_t addr); + +#endif // __ESP_SPIFFS_FLASH_H__ diff --git a/extras/spiffs/mkspiffs/Makefile b/extras/spiffs/mkspiffs/Makefile new file mode 100644 index 0000000..0b8b49e --- /dev/null +++ b/extras/spiffs/mkspiffs/Makefile @@ -0,0 +1,45 @@ +# Check if SPIFFS_SIZE defined only if not cleaning +ifneq ($(MAKECMDGOALS),clean) +ifndef SPIFFS_SIZE +define ERROR_MSG +Variable SPIFFS_SIZE is not defined. +Cannot build mkspiffs without SPIFFS_SIZE. +Please specify it in your application Makefile. + +endef +$(error $(ERROR_MSG)) +endif +endif + +# explicitly use gcc as in xtensa build environment it might be set to +# cross compiler +CC = gcc + +SOURCES := spiffs_hydrogen.c +SOURCES += spiffs_cache.c +SOURCES += spiffs_gc.c +SOURCES += spiffs_check.c +SOURCES += spiffs_nucleus.c +SOURCES += mkspiffs.c + +OBJECTS := $(SOURCES:.c=.o) + +VPATH = ../spiffs/src + +CFLAGS += -I.. +CFLAGS += -DSPIFFS_BASE_ADDR=0 # for image base addr is start of the image +CFLAGS += -DSPIFFS_SIZE=$(SPIFFS_SIZE) + +all: mkspiffs + +$(OBJECTS): $(SOURCES) + +$(OBJECTS): ../spiffs_config.h + +mkspiffs: $(OBJECTS) + +clean: + @rm -f mkspiffs + @rm -f *.o + +.PHONY: all clean diff --git a/extras/spiffs/mkspiffs/README.md b/extras/spiffs/mkspiffs/README.md new file mode 100644 index 0000000..5f6943d --- /dev/null +++ b/extras/spiffs/mkspiffs/README.md @@ -0,0 +1,34 @@ +# mkspiffs Create spiffs image + +mkspiffs is a command line utility to create an image of SPIFFS in order +to write to flash. + +## Usage + +mkspiffs will be built automatically if you include the following line in your +makefile: + +``` +$(eval $(call make_spiffs_image,files)) +``` + +where *files* is the directory with files that should go into SPIFFS image. + +Or you can build mkspiffs manually with: + +``` +make SPIFFS_SIZE=0x100000 +``` + +mkspiffs cannot be built without specifying SPIFFS size because it uses the +same SPIFFS sources as the firmware. And for the firmware SPIFFS size is +compile time defined. + +Please note that if you change SPIFFS_SIZE you need to rebuild mkspiffs. +The easiest way is to run `make clean` for you project. + +To manually generate SPIFFS image from directory, run: + +``` +mkspiffs DIRECTORY IMAGE_NAME +``` diff --git a/extras/spiffs/mkspiffs/mkspiffs.c b/extras/spiffs/mkspiffs/mkspiffs.c new file mode 100644 index 0000000..a1a0513 --- /dev/null +++ b/extras/spiffs/mkspiffs/mkspiffs.c @@ -0,0 +1,241 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spiffs_config.h" +#include "../spiffs/src/spiffs.h" + +static spiffs fs; +static void *image = 0; +static void *work_buf = 0; +static void *fds_buf = 0; +static void *cache_buf = 0; + +static void print_usage(const char *prog_name, const char *error_msg) +{ + if (error_msg) { + printf("Error: %s\n", error_msg); + } + printf("Usage: "); + printf("\t%s DIRECTORY IMAGE_NAME\n\n", prog_name); + printf("Example:\n"); + printf("\t%s ./my_files spiffs.img\n\n", prog_name); +} + +static s32_t _read_data(u32_t addr, u32_t size, u8_t *dst) +{ + memcpy(dst, (uint8_t*)image + addr, size); + return SPIFFS_OK; +} + +static s32_t _write_data(u32_t addr, u32_t size, u8_t *src) +{ + uint32_t i; + uint8_t *dst = image + addr; + + for (i = 0; i < size; i++) { + dst[i] &= src[i]; // mimic NOR flash, flip only 1 to 0 + } + return SPIFFS_OK; +} + +static s32_t _erase_data(u32_t addr, u32_t size) +{ + memset((uint8_t*)image + addr, 0xFF, size); + return SPIFFS_OK; +} + +static bool init_spiffs(bool allocate_mem) +{ + spiffs_config config = {0}; + printf("Initializing SPIFFS, size=%d\n", SPIFFS_SIZE); + + config.hal_read_f = _read_data; + config.hal_write_f = _write_data; + config.hal_erase_f = _erase_data; + + int workBufSize = 2 * SPIFFS_CFG_LOG_PAGE_SZ(); + int fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&fs, 5); + int cacheBufSize = SPIFFS_buffer_bytes_for_cache(&fs, 5); + + if (allocate_mem) { + image = malloc(SPIFFS_SIZE); + 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); + + return err == SPIFFS_OK; +} + +static bool format_spiffs() +{ + SPIFFS_unmount(&fs); + + if (SPIFFS_format(&fs) == SPIFFS_OK) { + printf("Format complete\n"); + } else { + printf("Failed to format SPIFFS\n"); + return false; + } + + if (!init_spiffs(false)) { + printf("Failed to mount SPIFFS\n"); + return false; + } + return true; +} + +static void spiffs_free() +{ + free(image); + image = NULL; + + free(work_buf); + work_buf = NULL; + + free(fds_buf); + fds_buf = NULL; + + free(cache_buf); + cache_buf = NULL; +} + +static bool process_file(const char *src_file, const char *dst_file) +{ + int fd; + const int buf_size = 256; + uint8_t buf[buf_size]; + int data_len; + + fd = open(src_file, O_RDONLY); + if (fd < 0) { + printf("Error openning file: %s\n", src_file); + } + + spiffs_file out_fd = SPIFFS_open(&fs, dst_file, + SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0); + while ((data_len = read(fd, buf, buf_size)) != 0) { + if (SPIFFS_write(&fs, out_fd, buf, data_len) != data_len) { + printf("Error writing to SPIFFS file\n"); + break; + } + } + SPIFFS_close(&fs, out_fd); + close(fd); + return true; +} + +static bool process_directory(const char *direcotry) +{ + DIR *dp; + struct dirent *ep; + char path[256]; + + dp = opendir(direcotry); + if (dp != NULL) { + while ((ep = readdir(dp)) != 0) { + if (!strcmp(ep->d_name, ".") || + !strcmp(ep->d_name, "..")) { + continue; + } + if (ep->d_type != DT_REG) { + continue; // not a regular file + } + sprintf(path, "%s/%s", direcotry, ep->d_name); + printf("Processing file %s\n", path); + if (!process_file(path, ep->d_name)) { + printf("Error processing file\n"); + break; + } + } + closedir(dp); + } else { + printf("Error reading direcotry: %s\n", direcotry); + } + return true; +} + +static bool write_image(const char *out_file) +{ + int fd; + int size = SPIFFS_SIZE; + uint8_t *p = (uint8_t*)image; + fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + printf("Error creating file %s\n", out_file); + return false; + } + + printf("Writing image to file: %s\n", out_file); + + while (size != 0) { + write(fd, p, SPIFFS_CFG_LOG_PAGE_SZ()); + p += SPIFFS_CFG_LOG_PAGE_SZ(); + size -= SPIFFS_CFG_LOG_PAGE_SZ(); + } + + close(fd); + return true; +} + +int main(int argc, char *argv[]) +{ + int result = 0; + + if (argc != 3) { + print_usage(argv[0], NULL); + return -1; + } + + init_spiffs(/*allocate_mem=*/true); + + if (format_spiffs()) { + if (process_directory(argv[1])) { + if (!write_image(argv[2])) { + printf("Error writing image\n"); + } + } else { + printf("Error processing direcotry\n"); + } + } else { + printf("Error formating spiffs\n"); + } + + spiffs_free(); + return result; +} 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..b782d48 --- /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 (1) +#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 (1) +#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) (8*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 1 +#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_ */ diff --git a/extras/spiffs/unaligned_memcpy.S b/extras/spiffs/unaligned_memcpy.S new file mode 100644 index 0000000..b96c92c --- /dev/null +++ b/extras/spiffs/unaligned_memcpy.S @@ -0,0 +1,112 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + .text + .section .iram1.text, "x" + .literal_position + +/** + * Copy unaligned data to 4-byte aligned buffer. + */ + .align 4 + .global memcpy_unaligned_src + .type memcpy_unaligned_src, @function +memcpy_unaligned_src: +/* a2: dst, a3: src, a4: size */ + ssa8l a3 + srli a3, a3, 2 + slli a3, a3, 2 + beqz a4, u_src_end + l32i a6, a3, 0 +u_src_loop: + l32i a7, a3, 4 + src a8, a7, a6 + memw + s32i a8, a2, 0 + mov a6, a7 + addi a3, a3, 4 + addi a2, a2, 4 + addi a4, a4, -1 + bnez a4, u_src_loop +u_src_end: + movi a2, 0 + ret.n + + +/** + * Copy data from 4-byte aligned source to unaligned destination buffer. + */ + .align 4 + .global memcpy_unaligned_dst + .type memcpy_unaligned_dst, @function +memcpy_unaligned_dst: +/* a2: dst, a3: src, a4: size */ + beqz.n a4, u_dst_end + extui a5, a4, 0, 2 + beqz.n a5, aligned_dst_loop +u_dst_loop: + /* Load data word */ + memw + l32i.n a5, a3, 0 + + /* Save byte number 0 */ + s8i a5, a2, 0 + addi.n a4, a4, -1 + beqz a4, u_dst_end + addi.n a2, a2, 1 + + /* Shift and save byte number 1 */ + srli a5, a5, 8 + s8i a5, a2, 0 + addi.n a4, a4, -1 + beqz a4, u_dst_end + addi.n a2, a2, 1 + + /* Shift and save byte number 2 */ + srli a5, a5, 8 + s8i a5, a2, 0 + addi.n a4, a4, -1 + beqz a4, u_dst_end + addi.n a2, a2, 1 + + /* Shift and save byte number 3 */ + srli a5, a5, 8 + s8i a5, a2, 0 + addi.n a4, a4, -1 + addi.n a2, a2, 1 + + /* Next word */ + addi.n a3, a3, 4 + bnez.n a4, u_dst_loop + ret.n +aligned_dst_loop: + memw + l32i a5, a3, 0 + s32i a5, a2, 0 + addi.n a3, a3, 4 + addi.n a2, a2, 4 + addi.n a4, a4, -4 + bnez.n a4, aligned_dst_loop +u_dst_end: ret.n +