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:
Renz Bagaporo 2018-05-17 16:19:29 +08:00 committed by bot
parent 893003357a
commit af629b3547
14 changed files with 275 additions and 24 deletions

View File

@ -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)
SOURCE_FILES = \
main.c \
main.cpp \
test_fatfs.cpp \
$(addprefix ../src/, \
diskio.c \
ff.c \
ffsystem.c \
ffunicode.c \
)
ffunicode.c \
diskio_spiflash.c \
) \
$(addprefix ./stubs/, log/log.c)
INCLUDE_FLAGS = $(addprefix -I,\
../src \
. \
$(addprefix ./stubs/, \
driver/include \
freertos/include \
sdmmc/include \
) \
../../esp32/include \
. \
$(addprefix ./stubs/, \
driver/include \
freertos/include \
sdmmc/include \
log/include \
) \
../../esp32/include \
$(TEST_PARTITION_SIM_DIR)/include \
$(TEST_WL_COMPONENT)/include \
../../../tools/catch \
)
CPPFLAGS += $(INCLUDE_FLAGS) -g
CFLAGS += -fprofile-arcs -g
CXXFLAGS += -std=c++11 -Wall -Werror -fprofile-arcs -g
LDFLAGS += -lstdc++ -fprofile-arcs
GCOV ?= gcov
OBJ_FILES = $(SOURCE_FILES:.c=.o)
$(OBJ_FILES): %.o: %.c
CPPFLAGS += $(INCLUDE_FLAGS) -D CONFIG_LOG_DEFAULT_LEVEL -g
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)
gcc $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES)
OBJ_FILES = $(filter %.o, $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o))
$(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)
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)
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)
$(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

View File

@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include "catch.hpp"

View File

@ -0,0 +1,3 @@
# pragma once
#define CONFIG_WL_SECTOR_SIZE 4096

View File

@ -0,0 +1,6 @@
#pragma once
#include <stdlib.h>
#include "sdmmc_types.h"

View File

@ -0,0 +1,11 @@
#pragma once
#if defined(__cplusplus)
extern "C" {
#endif
typedef int sdmmc_card_t;
#if defined(__cplusplus)
}
#endif

View File

@ -0,0 +1,4 @@
#pragma once
#include "projdefs.h"
#include "semphr.h"

View File

@ -0,0 +1,11 @@
#pragma once
#if defined(__cplusplus)
extern "C" {
#endif
#define pdTRUE 1
#if defined(__cplusplus)
}
#endif

View File

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

View 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

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

View File

@ -0,0 +1,3 @@
#pragma once
#include "esp_err.h"

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

View File

@ -2,9 +2,14 @@
#include <stdbool.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_spi_flash.h"
#if defined(__cplusplus)
extern "C" { // Make sure we have C-declarations in C++ programs
#endif
/**
* @brief Partition type
* @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);
#if defined(__cplusplus)
}
#endif

View File

@ -4,15 +4,15 @@
#include "esp_spi_flash.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)
{
int handle = -1;
for (int i = 0; i < PARTITIONS_MAX; i++) {
for (int i = 0; i < EMULATORS_MAX; i++) {
if (emulators[i] == NULL) {
emulators[i] = new Flash_Emulator(start + size, SPI_FLASH_SEC_SIZE, SPI_FLASH_WRITE_SIZE);
handle = i;