Merge pull request #163 from sheinz/feature/spiffs

File system support. SPIFFS integration.
This commit is contained in:
Johan Kanflo 2016-08-02 15:42:22 +02:00 committed by GitHub
commit 083aa0451a
22 changed files with 1709 additions and 9 deletions

7
.gitmodules vendored
View file

@ -10,4 +10,9 @@
[submodule "bootloader/rboot"] [submodule "bootloader/rboot"]
path = bootloader/rboot path = bootloader/rboot
url = https://github.com/raburton/rboot.git 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

View file

@ -68,7 +68,7 @@ Q := @
vecho := @echo vecho := @echo
endif 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) all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE)
@ -209,8 +209,9 @@ $(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR)
$(vecho) "FW $@" $(vecho) "FW $@"
$(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE) $(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE)
flash: $(FW_FILE) flash: all
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE) $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) \
0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE) $(SPIFFS_ESPTOOL_ARGS)
erase_flash: erase_flash:
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash

View file

@ -41,7 +41,7 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
} }
/* syscall implementation for stdio write to UART */ /* 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) { if(fd != r->_stdout->_file) {
r->_errno = EBADF; 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 /* Stub syscall implementations follow, to allow compiling newlib functions that
pull these in via various codepaths 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__((weak, alias("syscall_returns_enosys")))
__attribute__((alias("syscall_returns_enosys"))) int _fstat_r(struct _reent *r, int fd, void *buf); int _open_r(struct _reent *r, const char *pathname, int flags, int mode);
__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 _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 /* Generic stub for any newlib syscall that fails with errno ENOSYS
("Function not implemented") and a return value equivalent to ("Function not implemented") and a return value equivalent to

View file

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

View file

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

@ -0,0 +1 @@
Subproject commit 2ad547adc5f725594b3c6752f036ff4401b221fc

View file

@ -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 <stdio.h>
#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);
}

11
examples/spiffs/Makefile Normal file
View file

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

View file

@ -0,0 +1 @@
This file will go to SPIFFS image.

View file

@ -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);
}

153
extras/spiffs/README.md Normal file
View file

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

View file

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

198
extras/spiffs/esp_spiffs.c Normal file
View file

@ -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 <espressif/spi_flash.h>
#include <stdbool.h>
#include <esp/uart.h>
#include <fcntl.h>
#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);
}

View file

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

View file

@ -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 <string.h>
/**
* 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;
}

View file

@ -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 <stdint.h>
#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__

View file

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

View file

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

View file

@ -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 <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <dirent.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#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;
}

1
extras/spiffs/spiffs Submodule

@ -0,0 +1 @@
Subproject commit c6e94fdca5c1601b90c027167f8d453c48e482c4

View file

@ -0,0 +1,263 @@
/*
* spiffs_config.h
*
* Created on: Jul 3, 2013
* Author: petera
*/
#ifndef SPIFFS_CONFIG_H_
#define SPIFFS_CONFIG_H_
// ----------- 8< ------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <ctype.h>
// #include <FreeRTOS.h> // 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_ */

View file

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