forked from espressif/esp-idf
fatfs: Make fatfs runnable on host
Makes fatfs component runnable on host. Depends on the host library build of wear levelling and flash emulator. Includes a basic sanity test of mounting a volume, opening a file, writing to the file, reading the file, closing the file and unmounting volume.
This commit is contained in:
@@ -1,45 +1,83 @@
|
|||||||
TEST_PROGRAM=fatfs_host
|
TEST_PROGRAM=test_fatfs
|
||||||
|
|
||||||
|
# Use wear levelling
|
||||||
|
TEST_WL_COMPONENT=$(IDF_PATH)/components/wear_levelling
|
||||||
|
TEST_WL_DIR=$(TEST_WL_COMPONENT)/test_wl_host
|
||||||
|
TEST_WL_LIB=libtest_wl.a
|
||||||
|
|
||||||
|
TEST_PARTITION_SIM_DIR=$(IDF_PATH)/components/spi_flash/sim
|
||||||
|
TEST_PARTITION_SIM_LIB=libpartition_sim.a
|
||||||
|
|
||||||
all: $(TEST_PROGRAM)
|
all: $(TEST_PROGRAM)
|
||||||
|
|
||||||
SOURCE_FILES = \
|
SOURCE_FILES = \
|
||||||
main.c \
|
main.cpp \
|
||||||
|
test_fatfs.cpp \
|
||||||
$(addprefix ../src/, \
|
$(addprefix ../src/, \
|
||||||
diskio.c \
|
diskio.c \
|
||||||
ff.c \
|
ff.c \
|
||||||
ffsystem.c \
|
ffsystem.c \
|
||||||
ffunicode.c \
|
ffunicode.c \
|
||||||
)
|
diskio_spiflash.c \
|
||||||
|
) \
|
||||||
|
$(addprefix ./stubs/, log/log.c)
|
||||||
|
|
||||||
INCLUDE_FLAGS = $(addprefix -I,\
|
INCLUDE_FLAGS = $(addprefix -I,\
|
||||||
../src \
|
../src \
|
||||||
. \
|
. \
|
||||||
$(addprefix ./stubs/, \
|
$(addprefix ./stubs/, \
|
||||||
driver/include \
|
driver/include \
|
||||||
freertos/include \
|
freertos/include \
|
||||||
sdmmc/include \
|
sdmmc/include \
|
||||||
) \
|
log/include \
|
||||||
../../esp32/include \
|
) \
|
||||||
|
../../esp32/include \
|
||||||
|
$(TEST_PARTITION_SIM_DIR)/include \
|
||||||
|
$(TEST_WL_COMPONENT)/include \
|
||||||
|
../../../tools/catch \
|
||||||
)
|
)
|
||||||
|
|
||||||
CPPFLAGS += $(INCLUDE_FLAGS) -g
|
GCOV ?= gcov
|
||||||
CFLAGS += -fprofile-arcs -g
|
|
||||||
CXXFLAGS += -std=c++11 -Wall -Werror -fprofile-arcs -g
|
|
||||||
LDFLAGS += -lstdc++ -fprofile-arcs
|
|
||||||
|
|
||||||
OBJ_FILES = $(SOURCE_FILES:.c=.o)
|
CPPFLAGS += $(INCLUDE_FLAGS) -D CONFIG_LOG_DEFAULT_LEVEL -g
|
||||||
$(OBJ_FILES): %.o: %.c
|
CFLAGS += -fprofile-arcs -ftest-coverage
|
||||||
|
CXXFLAGS += -std=c++11 -Wall -Werror -fprofile-arcs -ftest-coverage
|
||||||
|
LDFLAGS += -lstdc++ -fprofile-arcs -ftest-coverage
|
||||||
|
|
||||||
$(TEST_PROGRAM): $(OBJ_FILES)
|
OBJ_FILES = $(filter %.o, $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o))
|
||||||
gcc $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES)
|
|
||||||
|
$(TEST_WL_DIR)/$(TEST_WL_LIB): force
|
||||||
|
$(MAKE) -C $(TEST_WL_DIR) lib
|
||||||
|
|
||||||
|
$(TEST_PARTITION_SIM_DIR)/$(TEST_PARTITION_SIM_LIB): force
|
||||||
|
$(MAKE) -C $(TEST_PARTITION_SIM_DIR) lib
|
||||||
|
|
||||||
|
force:
|
||||||
|
|
||||||
$(TEST_PROGRAM): $(OBJ_FILES) $(TEST_WL_DIR)/$(TEST_WL_LIB) $(TEST_PARTITION_SIM_DIR)/$(TEST_PARTITION_SIM_LIB)
|
$(TEST_PROGRAM): $(OBJ_FILES) $(TEST_WL_DIR)/$(TEST_WL_LIB) $(TEST_PARTITION_SIM_DIR)/$(TEST_PARTITION_SIM_LIB)
|
||||||
g++ $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES) -L$(TEST_PARTITION_SIM_DIR) -l:$(TEST_PARTITION_SIM_LIB) -L$(TEST_WL_DIR) -l:$(TEST_WL_LIB)
|
g++ $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES) -L$(TEST_PARTITION_SIM_DIR) -l:$(TEST_PARTITION_SIM_LIB) -L$(TEST_WL_DIR) -l:$(TEST_WL_LIB)
|
||||||
|
|
||||||
test: $(TEST_PROGRAM)
|
run: $(TEST_PROGRAM)
|
||||||
./$(TEST_PROGRAM)
|
./$(TEST_PROGRAM)
|
||||||
|
|
||||||
clean:
|
COVERAGE_FILES = $(OBJ_FILES:.o=.gc*) $(OBJ_FILES:.o=.gc*)
|
||||||
|
|
||||||
|
$(COVERAGE_FILES): $(TEST_PROGRAM) lib
|
||||||
|
|
||||||
|
coverage.info: $(COVERAGE_FILES)
|
||||||
|
find ../ -name "*.gcno" -exec $(GCOV) -r -pb {} +
|
||||||
|
lcov --capture --directory ../ --no-external --output-file coverage.info --gcov-tool $(GCOV)
|
||||||
|
|
||||||
|
coverage_report: coverage.info
|
||||||
|
genhtml coverage.info --output-directory coverage_report
|
||||||
|
@echo "Coverage report is in coverage_report/index.html"
|
||||||
|
|
||||||
|
clean:
|
||||||
rm -f $(OBJ_FILES) $(TEST_PROGRAM)
|
rm -f $(OBJ_FILES) $(TEST_PROGRAM)
|
||||||
|
$(MAKE) -C $(TEST_WL_DIR) clean
|
||||||
|
$(MAKE) -C $(TEST_PARTITION_SIM_DIR) clean
|
||||||
|
rm -f $(COVERAGE_FILES) *.gcov
|
||||||
|
rm -rf coverage_report/
|
||||||
|
rm -f coverage.info
|
||||||
|
|
||||||
.PHONY: clean all
|
.PHONY: clean all
|
||||||
|
2
components/fatfs/test_fatfs_host/main.cpp
Normal file
2
components/fatfs/test_fatfs_host/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include "catch.hpp"
|
3
components/fatfs/test_fatfs_host/sdkconfig.h
Normal file
3
components/fatfs/test_fatfs_host/sdkconfig.h
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# pragma once
|
||||||
|
|
||||||
|
#define CONFIG_WL_SECTOR_SIZE 4096
|
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "sdmmc_types.h"
|
||||||
|
|
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef int sdmmc_card_t;
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
@@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "projdefs.h"
|
||||||
|
#include "semphr.h"
|
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define pdTRUE 1
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define vSemaphoreDelete( xSemaphore )
|
||||||
|
#define xSemaphoreCreateMutex() ((void*)(1))
|
||||||
|
#define xSemaphoreGive( xSemaphore )
|
||||||
|
#define xSemaphoreTake( xSemaphore, xBlockTime ) pdTRUE
|
||||||
|
|
||||||
|
typedef void* SemaphoreHandle_t;
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
45
components/fatfs/test_fatfs_host/stubs/log/include/esp_log.h
Normal file
45
components/fatfs/test_fatfs_host/stubs/log/include/esp_log.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" { // Make sure we have C-declarations in C++ programs
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ESP_LOG_NONE, /*!< No log output */
|
||||||
|
ESP_LOG_ERROR, /*!< Critical errors, software module can not recover on its own */
|
||||||
|
ESP_LOG_WARN, /*!< Error conditions from which recovery measures have been taken */
|
||||||
|
ESP_LOG_INFO, /*!< Information messages which describe normal flow of events */
|
||||||
|
ESP_LOG_DEBUG, /*!< Extra information which is not necessary for normal use (values, pointers, sizes, etc). */
|
||||||
|
ESP_LOG_VERBOSE /*!< Bigger chunks of debugging information, or frequent messages which can potentially flood the output. */
|
||||||
|
} esp_log_level_t;
|
||||||
|
|
||||||
|
#define LOG_COLOR_E
|
||||||
|
#define LOG_COLOR_W
|
||||||
|
#define LOG_COLOR_I
|
||||||
|
#define LOG_COLOR_D
|
||||||
|
#define LOG_COLOR_V
|
||||||
|
#define LOG_RESET_COLOR
|
||||||
|
|
||||||
|
uint32_t esp_log_timestamp(void);
|
||||||
|
void esp_log_write(esp_log_level_t level, const char* tag, const char* format, ...) __attribute__ ((format (printf, 3, 4)));
|
||||||
|
|
||||||
|
#define LOG_FORMAT(letter, format) LOG_COLOR_ ## letter #letter " (%d) %s: " format LOG_RESET_COLOR "\n"
|
||||||
|
|
||||||
|
#define ESP_LOGE( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR) { esp_log_write(ESP_LOG_ERROR, tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
|
||||||
|
|
||||||
|
#define ESP_LOGV( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_VERBOSE) { esp_log_write(ESP_LOG_VERBOSE, tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
|
||||||
|
|
||||||
|
#define ESP_LOGD( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { esp_log_write(ESP_LOG_DEBUG, tag, LOG_FORMAT(D, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
|
||||||
|
|
||||||
|
#define ESP_LOGW( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) { esp_log_write(ESP_LOG_WARN, tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
20
components/fatfs/test_fatfs_host/stubs/log/log.c
Normal file
20
components/fatfs/test_fatfs_host/stubs/log/log.c
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
void esp_log_write(esp_log_level_t level,
|
||||||
|
const char *tag,
|
||||||
|
const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
vprintf(format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t esp_log_timestamp()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
83
components/fatfs/test_fatfs_host/test_fatfs.cpp
Normal file
83
components/fatfs/test_fatfs_host/test_fatfs.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ff.h"
|
||||||
|
#include "esp_partition.h"
|
||||||
|
#include "wear_levelling.h"
|
||||||
|
#include "diskio.h"
|
||||||
|
#include "diskio_spiflash.h"
|
||||||
|
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
TEST_CASE("create volume, open file, write and read back data", "[fatfs]")
|
||||||
|
{
|
||||||
|
FRESULT fr_result;
|
||||||
|
BYTE pdrv;
|
||||||
|
FATFS fs;
|
||||||
|
FIL file;
|
||||||
|
UINT bw;
|
||||||
|
|
||||||
|
esp_err_t esp_result;
|
||||||
|
|
||||||
|
// Create a 4MB partition
|
||||||
|
uint32_t size = 0x00400000;
|
||||||
|
int flash_handle = esp_flash_create(size, 4096, 1);
|
||||||
|
esp_partition_t partition = esp_partition_create(size, 0, flash_handle);
|
||||||
|
|
||||||
|
// Mount wear-levelled partition
|
||||||
|
wl_handle_t wl_handle;
|
||||||
|
esp_result = wl_mount(&partition, &wl_handle);
|
||||||
|
REQUIRE(esp_result == ESP_OK);
|
||||||
|
|
||||||
|
// Get a physical drive
|
||||||
|
esp_result = ff_diskio_get_drive(&pdrv);
|
||||||
|
REQUIRE(esp_result == ESP_OK);
|
||||||
|
|
||||||
|
// Register physical drive as wear-levelled partition
|
||||||
|
esp_result = ff_diskio_register_wl_partition(pdrv, wl_handle);
|
||||||
|
|
||||||
|
// Create FAT volume on the entire disk
|
||||||
|
DWORD part_list[] = {100, 0, 0, 0};
|
||||||
|
BYTE work_area[FF_MAX_SS];
|
||||||
|
|
||||||
|
fr_result = f_fdisk(pdrv, part_list, work_area);
|
||||||
|
REQUIRE(fr_result == FR_OK);
|
||||||
|
fr_result = f_mkfs("", FM_ANY, 0, work_area, sizeof(work_area)); // Use default volume
|
||||||
|
|
||||||
|
// Mount the volume
|
||||||
|
fr_result = f_mount(&fs, "", 0);
|
||||||
|
REQUIRE(fr_result == FR_OK);
|
||||||
|
|
||||||
|
// Open, write and read data
|
||||||
|
fr_result = f_open(&file, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
|
||||||
|
REQUIRE(fr_result == FR_OK);
|
||||||
|
|
||||||
|
const char data[] = "Hello, World!";
|
||||||
|
char *read = (char*) malloc(sizeof(data));
|
||||||
|
|
||||||
|
fr_result = f_write(&file, data, sizeof(data), &bw);
|
||||||
|
REQUIRE(fr_result == FR_OK);
|
||||||
|
REQUIRE(bw == sizeof(data));
|
||||||
|
|
||||||
|
// Move to beginning of file
|
||||||
|
fr_result = f_lseek(&file, 0);
|
||||||
|
REQUIRE(fr_result == FR_OK);
|
||||||
|
|
||||||
|
fr_result = f_read(&file, read, sizeof(data), &bw);
|
||||||
|
REQUIRE(fr_result == FR_OK);
|
||||||
|
REQUIRE(bw == sizeof(data));
|
||||||
|
|
||||||
|
REQUIRE(strcmp(data, read) == 0);
|
||||||
|
|
||||||
|
// Close file
|
||||||
|
fr_result = f_close(&file);
|
||||||
|
REQUIRE(fr_result == FR_OK);
|
||||||
|
|
||||||
|
// Unmount default volume
|
||||||
|
fr_result = f_mount(0, "", 0);
|
||||||
|
REQUIRE(fr_result == FR_OK);
|
||||||
|
|
||||||
|
esp_partition_delete(partition);
|
||||||
|
|
||||||
|
free(read);
|
||||||
|
}
|
@@ -2,9 +2,14 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_spi_flash.h"
|
#include "esp_spi_flash.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" { // Make sure we have C-declarations in C++ programs
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Partition type
|
* @brief Partition type
|
||||||
* @note Keep this enum in sync with PartitionDefinition class gen_esp32part.py
|
* @note Keep this enum in sync with PartitionDefinition class gen_esp32part.py
|
||||||
@@ -83,3 +88,7 @@ esp_partition_t esp_partition_create(uint32_t size, uint32_t start);
|
|||||||
|
|
||||||
void esp_partition_delete(esp_partition_t partition);
|
void esp_partition_delete(esp_partition_t partition);
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@@ -4,15 +4,15 @@
|
|||||||
#include "esp_spi_flash.h"
|
#include "esp_spi_flash.h"
|
||||||
#include "Flash_Emulator.h"
|
#include "Flash_Emulator.h"
|
||||||
|
|
||||||
#define PARTITIONS_MAX 8
|
#define EMULATORS_MAX 8
|
||||||
|
|
||||||
static Flash_Emulator* emulators[PARTITIONS_MAX];
|
static Flash_Emulator* emulators[EMULATORS_MAX];
|
||||||
|
|
||||||
esp_partition_t esp_partition_create(uint32_t size, uint32_t start)
|
esp_partition_t esp_partition_create(uint32_t size, uint32_t start)
|
||||||
{
|
{
|
||||||
int handle = -1;
|
int handle = -1;
|
||||||
|
|
||||||
for (int i = 0; i < PARTITIONS_MAX; i++) {
|
for (int i = 0; i < EMULATORS_MAX; i++) {
|
||||||
if (emulators[i] == NULL) {
|
if (emulators[i] == NULL) {
|
||||||
emulators[i] = new Flash_Emulator(start + size, SPI_FLASH_SEC_SIZE, SPI_FLASH_WRITE_SIZE);
|
emulators[i] = new Flash_Emulator(start + size, SPI_FLASH_SEC_SIZE, SPI_FLASH_WRITE_SIZE);
|
||||||
handle = i;
|
handle = i;
|
||||||
|
Reference in New Issue
Block a user