├── components └── fatfs │ ├── test_fatfs_host │ ├── main.cpp │ ├── partition_table.csv │ ├── sdkconfig │ │ └── sdkconfig.h │ ├── component.mk │ ├── Makefile.files │ ├── test_fatfs.cpp │ └── Makefile │ ├── test │ ├── fatfs.img │ ├── component.mk │ ├── CMakeLists.txt │ ├── test_fatfs_common.h │ ├── test_fatfs_spiflash.c │ ├── test_fatfs_rawflash.c │ └── test_fatfs_sdmmc.c │ ├── component.mk │ ├── CMakeLists.txt │ ├── src │ ├── 00readme.txt │ ├── diskio.h │ ├── ffsystem.c │ ├── diskio.c │ ├── 00history.txt │ └── ffconf.h │ ├── port │ ├── linux │ │ └── ffsystem.c │ └── freertos │ │ └── ffsystem.c │ ├── vfs │ ├── vfs_fat_internal.h │ ├── vfs_fat_spiflash.c │ └── vfs_fat_sdmmc.c │ ├── diskio │ ├── diskio_rawflash.h │ ├── diskio_wl.h │ ├── diskio_sdmmc.h │ ├── diskio_impl.h │ ├── diskio.c │ ├── diskio_sdmmc.c │ ├── diskio_rawflash.c │ └── diskio_wl.c │ └── Kconfig ├── .gitignore ├── documentation └── crystal_sdcard.png ├── CMakeLists.txt ├── lib ├── ConsoleTable │ ├── CMakeLists.txt │ ├── library.json │ ├── LICENSE.md │ ├── main.cpp │ ├── .gitignore │ ├── README.md │ ├── ConsoleTable.cpp │ └── ConsoleTable.h ├── benchmarks │ ├── benchmark_printout.h │ ├── statistics.h │ ├── benchmark_tester.h │ ├── benchmark_printout.cpp │ └── benchmark_tester.c ├── filesystem │ ├── unbuffered_filesystem.h │ ├── buffered_filesystem.h │ ├── filesystem_struct.h │ ├── unbuffered_filesystem.c │ └── buffered_filesystem.c ├── virtual_directory │ ├── virtual_directory.h │ └── virtual_directory.c ├── runner │ └── runner.h ├── log_c │ ├── LICENSE │ ├── log.h │ ├── README.md │ └── log.c └── README ├── src ├── CMakeLists.txt ├── sd_mount.h ├── main.cpp └── sd_mount.c ├── .vscode └── extensions.json ├── test └── README ├── platformio.ini └── include └── README /components/fatfs/test_fatfs_host/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/arduino.json 2 | .vscode/c_cpp_properties.json 3 | .vscode/launch.json 4 | .vscode/settings.json 5 | .pio 6 | -------------------------------------------------------------------------------- /components/fatfs/test/fatfs.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drorgl/esp32-sd-card-benchmarks/HEAD/components/fatfs/test/fatfs.img -------------------------------------------------------------------------------- /documentation/crystal_sdcard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drorgl/esp32-sd-card-benchmarks/HEAD/documentation/crystal_sdcard.png -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.0) 2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 3 | project(esp32-sd-card-benchmarks) 4 | -------------------------------------------------------------------------------- /components/fatfs/test/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive 2 | COMPONENT_EMBED_TXTFILES := fatfs.img 3 | -------------------------------------------------------------------------------- /components/fatfs/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_ADD_INCLUDEDIRS := diskio vfs src 2 | COMPONENT_SRCDIRS := diskio vfs port/freertos src 3 | COMPONENT_OBJEXCLUDE := src/diskio.o src/ffsystem.o 4 | -------------------------------------------------------------------------------- /lib/ConsoleTable/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | project(ConsoleTable) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | add_executable(ConsoleTable main.cpp ConsoleTable.cpp ConsoleTable.h) -------------------------------------------------------------------------------- /lib/benchmarks/benchmark_printout.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "statistics.h" 4 | #include 5 | 6 | void print(struct statistics &stats); 7 | void addTable(ConsoleTable &table, struct statistics &stats); -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file was automatically generated for projects 2 | # without default 'CMakeLists.txt' file. 3 | 4 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) 5 | 6 | idf_component_register(SRCS ${app_sources}) 7 | -------------------------------------------------------------------------------- /lib/filesystem/unbuffered_filesystem.h: -------------------------------------------------------------------------------- 1 | # pragma once 2 | 3 | #include "filesystem_struct.h" 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | extern const filesystem unbuffered_filesystem; 9 | #ifdef __cplusplus 10 | } 11 | #endif -------------------------------------------------------------------------------- /components/fatfs/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRC_DIRS . 2 | PRIV_INCLUDE_DIRS . 3 | PRIV_REQUIRES cmock test_utils vfs fatfs 4 | EMBED_TXTFILES fatfs.img 5 | ) 6 | -------------------------------------------------------------------------------- /src/sd_mount.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" 5 | { 6 | #endif 7 | 8 | // void mount_sd(); 9 | void unmound_sd(); 10 | 11 | void mount_sd_spi(); 12 | void mount_sd_sdmmc(); 13 | 14 | #ifdef __cplusplus 15 | } 16 | #endif -------------------------------------------------------------------------------- /lib/filesystem/buffered_filesystem.h: -------------------------------------------------------------------------------- 1 | # pragma once 2 | 3 | #include "filesystem_struct.h" 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | extern const filesystem buffered_filesystem; 10 | void set_override_buffer_size(uint32_t override_size); 11 | #ifdef __cplusplus 12 | } 13 | #endif -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /lib/benchmarks/statistics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct statistics 7 | { 8 | bool random; 9 | bool sync; 10 | uint32_t block_size; 11 | bool rw; 12 | uint32_t aligned; 13 | uint64_t time; 14 | uint64_t bytes; 15 | uint64_t iops; 16 | }; -------------------------------------------------------------------------------- /lib/virtual_directory/virtual_directory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" 9 | { 10 | #endif 11 | int vd_chdir(const char *path); 12 | 13 | void vd_cwd(char *cwd_buffer, size_t cwd_buffer_length); 14 | #ifdef __cplusplus 15 | } 16 | #endif -------------------------------------------------------------------------------- /components/fatfs/test_fatfs_host/partition_table.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap 3 | nvs, data, nvs, 0x9000, 0x6000, 4 | phy_init, data, phy, 0xf000, 0x1000, 5 | factory, app, factory, 0x10000, 1M, 6 | storage, data, fat, , 1M, 7 | -------------------------------------------------------------------------------- /components/fatfs/test_fatfs_host/sdkconfig/sdkconfig.h: -------------------------------------------------------------------------------- 1 | # pragma once 2 | #define CONFIG_IDF_TARGET_ESP32 1 3 | #define CONFIG_WL_SECTOR_SIZE 4096 4 | #define CONFIG_LOG_DEFAULT_LEVEL 3 5 | #define CONFIG_PARTITION_TABLE_OFFSET 0x8000 6 | #define CONFIG_ESPTOOLPY_FLASHSIZE "8MB" 7 | //currently use the legacy implementation, since the stubs for new HAL are not done yet 8 | #define CONFIG_SPI_FLASH_USE_LEGACY_IMPL 1 9 | -------------------------------------------------------------------------------- /lib/filesystem/filesystem_struct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct 6 | { 7 | FILE *(*fopen)(const char *filename, const char *mode, const size_t buffer_size); 8 | off_t (*seek)(FILE *file, off_t offset, int whence); // SEEK_SET / SEEK_CUR / SEEK_END 9 | ssize_t (*write)(FILE *file, const void *data, size_t len); 10 | ssize_t (*read)(FILE *file, void *data, size_t len); 11 | int (*close)(FILE *file); 12 | } filesystem; -------------------------------------------------------------------------------- /components/fatfs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(srcs "diskio/diskio.c" 2 | "diskio/diskio_rawflash.c" 3 | "diskio/diskio_sdmmc.c" 4 | "diskio/diskio_wl.c" 5 | "src/ff.c" 6 | "port/freertos/ffsystem.c" 7 | "src/ffunicode.c" 8 | "vfs/vfs_fat.c" 9 | "vfs/vfs_fat_sdmmc.c" 10 | "vfs/vfs_fat_spiflash.c") 11 | 12 | idf_component_register(SRCS ${srcs} 13 | INCLUDE_DIRS diskio vfs src 14 | REQUIRES wear_levelling sdmmc 15 | ) 16 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Test Runner and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/en/latest/advanced/unit-testing/index.html 12 | -------------------------------------------------------------------------------- /lib/ConsoleTable/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ConsoleTable", 3 | "version": "1.0.0", 4 | "keywords": "Console Table", 5 | "description": "ConsoleTable is a library that allows you to organize your data in a table based structure.", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/766F6964/ConsoleTable" 9 | }, 10 | "build": { 11 | "srcFilter":[ 12 | "-", 13 | "+" 14 | ] 15 | }, 16 | "license":"MIT", 17 | "frameworks": "*", 18 | "dependencies":[ 19 | 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /components/fatfs/test_fatfs_host/component.mk: -------------------------------------------------------------------------------- 1 | include $(COMPONENT_PATH)/Makefile.files 2 | 3 | COMPONENT_OWNBUILDTARGET := 1 4 | COMPONENT_OWNCLEANTARGET := 1 5 | 6 | COMPONENT_ADD_INCLUDEDIRS := $(INCLUDE_DIRS) 7 | 8 | .PHONY: build 9 | build: $(SDKCONFIG_HEADER) 10 | $(MAKE) -C $(COMPONENT_PATH) lib SDKCONFIG=$(SDKCONFIG_HEADER) BUILD_DIR=$(COMPONENT_BUILD_DIR) COMPONENT=$(COMPONENT_NAME) 11 | 12 | CLEAN_FILES := component_project_vars.mk 13 | .PHONY: clean 14 | clean: 15 | $(summary) RM $(CLEAN_FILES) 16 | rm -f $(CLEAN_FILES) 17 | $(MAKE) -C $(COMPONENT_PATH) clean SDKCONFIG=$(SDKCONFIG_HEADER) BUILD_DIR=$(COMPONENT_BUILD_DIR) COMPONENT=$(COMPONENT_NAME) 18 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | 12 | [platformio] 13 | default_envs = native 14 | 15 | [env:esp32] 16 | platform = espressif32 17 | board = esp32dev 18 | framework = espidf 19 | build_flags = -std=c++11 -Wall -O3 -g -DESP32 20 | monitor_speed=115200 21 | monitor_filters = esp32_exception_decoder 22 | 23 | [env:native] 24 | platform = native 25 | build_flags = -std=c++11 -Dtrue=1 -O3 -Wall -g 26 | -------------------------------------------------------------------------------- /lib/runner/runner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #ifdef ARDUINO 6 | 7 | void loop(); 8 | void setup(); 9 | 10 | #elif defined(ESP_PLATFORM) 11 | 12 | void app_main(); 13 | 14 | #else 15 | 16 | int main(int argc, char **argv); 17 | 18 | #endif 19 | 20 | } 21 | #endif 22 | 23 | #ifdef ARDUINO 24 | 25 | #define MAIN() \ 26 | void loop(){ \ 27 | while(1){} 28 | } \ 29 | int call_setup();\ 30 | void setup(){\ 31 | call_setup();\ 32 | }\ 33 | int call_setup() 34 | 35 | #elif defined(ESP_PLATFORM) 36 | 37 | #define MAIN() \ 38 | int call_app_main();\ 39 | void app_main(){\ 40 | call_app_main();\ 41 | }\ 42 | int call_app_main() 43 | 44 | #else 45 | 46 | #define MAIN() \ 47 | int main(int argc, char **argv) 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /components/fatfs/test_fatfs_host/Makefile.files: -------------------------------------------------------------------------------- 1 | SOURCE_FILES := \ 2 | $(addprefix ../src/, \ 3 | ff.c \ 4 | ffunicode.c \ 5 | ) \ 6 | $(addprefix ../diskio/,\ 7 | diskio.c \ 8 | diskio_wl.c \ 9 | ) \ 10 | ../port/linux/ffsystem.c 11 | 12 | INCLUDE_DIRS := \ 13 | . \ 14 | ../diskio \ 15 | ../src \ 16 | $(addprefix ../../spi_flash/sim/stubs/, \ 17 | app_update/include \ 18 | driver/include \ 19 | freertos/include \ 20 | log/include \ 21 | newlib/include \ 22 | sdmmc/include \ 23 | vfs/include \ 24 | ) \ 25 | $(addprefix ../../../components/, \ 26 | esp_rom/include \ 27 | esp_system/include \ 28 | xtensa/include \ 29 | xtensa/esp32/include \ 30 | soc/esp32/include \ 31 | soc/include \ 32 | esp32/include \ 33 | esp_common/include \ 34 | bootloader_support/include \ 35 | app_update/include \ 36 | hal/include \ 37 | spi_flash/include \ 38 | wear_levelling/include \ 39 | ) 40 | -------------------------------------------------------------------------------- /components/fatfs/src/00readme.txt: -------------------------------------------------------------------------------- 1 | FatFs Module Source Files R0.13c 2 | 3 | 4 | FILES 5 | 6 | 00readme.txt This file. 7 | 00history.txt Revision history. 8 | ff.c FatFs module. 9 | ffconf.h Configuration file of FatFs module. 10 | ff.h Common include file for FatFs and application module. 11 | diskio.h Common include file for FatFs and disk I/O module. 12 | diskio.c An example of glue function to attach existing disk I/O module to FatFs. 13 | ffunicode.c Optional Unicode utility functions. 14 | ffsystem.c An example of optional O/S related functions. 15 | 16 | 17 | Low level disk I/O module is not included in this archive because the FatFs 18 | module is only a generic file system layer and it does not depend on any specific 19 | storage device. You need to provide a low level disk I/O module written to 20 | control the storage device that attached to the target system. 21 | -------------------------------------------------------------------------------- /lib/benchmarks/benchmark_tester.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "statistics.h" 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | void set_filesystem(const filesystem *_filesystem); 14 | 15 | void create_empty_file(const char *filename, uint64_t size); 16 | 17 | void test_write_sequential(bool sync, uint32_t block_size, struct statistics *stats); 18 | void test_write_random(bool sync, uint32_t block_size, struct statistics *stats); 19 | void test_write_random_aligned(bool sync, uint32_t block_size, struct statistics *stats); 20 | 21 | void test_read_sequential(bool sync, uint32_t block_size, struct statistics *stats); 22 | void test_read_random(bool sync, uint32_t block_size, struct statistics *stats); 23 | void test_read_random_aligned(bool sync, uint32_t block_size, struct statistics *stats); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif -------------------------------------------------------------------------------- /lib/ConsoleTable/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2020 766F6964 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /lib/ConsoleTable/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ConsoleTable.h" 3 | 4 | int main() { 5 | 6 | ConsoleTable table{"Country", "Capital", "Population", "Area", "Currency"}; 7 | 8 | table.setPadding(2); 9 | table.setStyle(0); 10 | 11 | table += {"Germany", "Berlin", "82,800,000", "357,168 km2", "Euro"}; 12 | table += {"France", "Paris", "67,201,000", "640,679 km2 ", "Euro"}; 13 | table += {"South Korea", "Seoul", "51,446,201", "100,210 km2 ", "South Korean Won"}; 14 | table += {"Australia", "Canberra", "24,877,800", "7,692,024 km2", "Australian Dollar"}; 15 | table += {"China", "Beijing", "1,403,500,365", "9,596,961 km2", "Yuan"}; 16 | table += {"Iceland", "Reykjavik", "348,580", "102,775 km2", "Icelandic Krona"}; 17 | table += {"Netherlands", "Amsterdam", "17,200,671", "41,543 km2", "Euro"}; 18 | 19 | table.updateRow(3, 1, "NEW ENTRY"); 20 | table.updateHeader(2, "NEW HEADER"); 21 | 22 | table -= 2; 23 | table -= 1; 24 | table -= 0; 25 | table.sort(true); 26 | 27 | std::cout << table; 28 | 29 | return 0; 30 | } -------------------------------------------------------------------------------- /lib/log_c/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 rxi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /components/fatfs/port/linux/ffsystem.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* OS Dependent Functions for FatFs */ 3 | /* (C)ChaN, 2018 */ 4 | /*------------------------------------------------------------------------*/ 5 | 6 | 7 | #include "ff.h" 8 | #include 9 | 10 | /* This is the implementation for host-side testing on Linux. 11 | * Host-side tests are single threaded, so lock functionality isn't needed. 12 | */ 13 | 14 | void* ff_memalloc(UINT msize) 15 | { 16 | return malloc(msize); 17 | } 18 | 19 | void ff_memfree(void* mblock) 20 | { 21 | free(mblock); 22 | } 23 | 24 | /* 1:Function succeeded, 0:Could not create the sync object */ 25 | int ff_cre_syncobj(BYTE vol, FF_SYNC_t* sobj) 26 | { 27 | *sobj = NULL; 28 | return 1; 29 | } 30 | 31 | /* 1:Function succeeded, 0:Could not delete due to an error */ 32 | int ff_del_syncobj(FF_SYNC_t sobj) 33 | { 34 | return 1; 35 | } 36 | 37 | /* 1:Function succeeded, 0:Could not acquire lock */ 38 | int ff_req_grant (FF_SYNC_t sobj) 39 | { 40 | return 1; 41 | } 42 | 43 | void ff_rel_grant (FF_SYNC_t sobj) 44 | { 45 | } 46 | -------------------------------------------------------------------------------- /components/fatfs/vfs/vfs_fat_internal.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include "esp_vfs_fat.h" 18 | #include 19 | #include 20 | 21 | static inline size_t esp_vfs_fat_get_allocation_unit_size( 22 | size_t sector_size, size_t requested_size) 23 | { 24 | size_t alloc_unit_size = requested_size; 25 | const size_t max_sectors_per_cylinder = 128; 26 | const size_t max_size = sector_size * max_sectors_per_cylinder; 27 | alloc_unit_size = MAX(alloc_unit_size, sector_size); 28 | alloc_unit_size = MIN(alloc_unit_size, max_size); 29 | return alloc_unit_size; 30 | } 31 | -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /components/fatfs/diskio/diskio_rawflash.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef _DISKIO_RAWFLASH_DEFINED 16 | #define _DISKIO_RAWFLASH_DEFINED 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | #include "esp_partition.h" 23 | 24 | /** 25 | * Register spi flash partition 26 | * 27 | * @param pdrv drive number 28 | * @param part_handle pointer to raw flash partition. 29 | */ 30 | esp_err_t ff_diskio_register_raw_partition(unsigned char pdrv, const esp_partition_t* part_handle); 31 | unsigned char ff_diskio_get_pdrv_raw(const esp_partition_t* part_handle); 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | 37 | #endif // _DISKIO_RAWFLASH_DEFINED 38 | -------------------------------------------------------------------------------- /components/fatfs/diskio/diskio_wl.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef _DISKIO_WL_DEFINED 16 | #define _DISKIO_WL_DEFINED 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | #include "wear_levelling.h" 23 | 24 | 25 | /** 26 | * Register spi flash partition 27 | * 28 | * @param pdrv drive number 29 | * @param flash_handle handle of the wear levelling partition. 30 | */ 31 | esp_err_t ff_diskio_register_wl_partition(unsigned char pdrv, wl_handle_t flash_handle); 32 | unsigned char ff_diskio_get_pdrv_wl(wl_handle_t flash_handle); 33 | void ff_diskio_clear_pdrv_wl(wl_handle_t flash_handle); 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | #endif // _DISKIO_WL_DEFINED 40 | -------------------------------------------------------------------------------- /lib/filesystem/unbuffered_filesystem.c: -------------------------------------------------------------------------------- 1 | #include "unbuffered_filesystem.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | static FILE *unbuffered_fopen(const char *filename, const char *mode, const size_t buffer_size) 8 | { 9 | log_info("unuffered file open %s as %s", filename, mode); 10 | return fopen(filename, mode); 11 | } 12 | 13 | static off_t unbuffered_seek(FILE *file, off_t offset, int whence) 14 | { 15 | return lseek(fileno(file), offset, whence); 16 | } 17 | 18 | static ssize_t unbuffered_write(FILE *file, const void *data, size_t len) 19 | { 20 | if (write(fileno(file), data, len) == -1) 21 | { 22 | log_error("Error writing to backing store\n"); 23 | return 0; 24 | } 25 | return len; 26 | } 27 | 28 | static ssize_t unbuffered_read(FILE *file, void *data, size_t len) 29 | { 30 | if (read(fileno(file), data, len) != len) 31 | { 32 | log_error("Error reading from backing store\n"); 33 | return 0; 34 | } 35 | return len; 36 | } 37 | 38 | static int unbuffered_close(FILE *file) 39 | { 40 | return fclose(file); 41 | } 42 | 43 | const filesystem unbuffered_filesystem = { 44 | .fopen = unbuffered_fopen, 45 | .seek = unbuffered_seek, 46 | .write = unbuffered_write, 47 | .read = unbuffered_read, 48 | .close = unbuffered_close}; -------------------------------------------------------------------------------- /components/fatfs/diskio/diskio_sdmmc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include "sdmmc_cmd.h" 18 | #include "driver/sdmmc_defs.h" 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | /** 25 | * Register SD/MMC diskio driver 26 | * 27 | * @param pdrv drive number 28 | * @param card pointer to sdmmc_card_t structure describing a card; card should be initialized before calling f_mount. 29 | */ 30 | void ff_diskio_register_sdmmc(unsigned char pdrv, sdmmc_card_t* card); 31 | 32 | /** 33 | * @brief Get the driver number corresponding to a card 34 | * 35 | * @param card The card to get its driver 36 | * @return Driver number to the card 37 | */ 38 | BYTE ff_diskio_get_pdrv_card(const sdmmc_card_t* card); 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /lib/ConsoleTable/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/clion+all 2 | 3 | ### CLion+all ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff: 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/dictionaries 11 | 12 | # Sensitive or high-churn files: 13 | .idea/**/dataSources/ 14 | .idea/**/dataSources.ids 15 | .idea/**/dataSources.xml 16 | .idea/**/dataSources.local.xml 17 | .idea/**/sqlDataSources.xml 18 | .idea/**/dynamic.xml 19 | .idea/**/uiDesigner.xml 20 | 21 | # Gradle: 22 | .idea/**/gradle.xml 23 | .idea/**/libraries 24 | 25 | # CMake 26 | cmake-build-debug/ 27 | 28 | # Mongo Explorer plugin: 29 | .idea/**/mongoSettings.xml 30 | 31 | ## File-based project format: 32 | *.iws 33 | 34 | ## Plugin-specific files: 35 | 36 | # IntelliJ 37 | /out/ 38 | 39 | # mpeltonen/sbt-idea plugin 40 | .idea_modules/ 41 | 42 | # JIRA plugin 43 | atlassian-ide-plugin.xml 44 | 45 | # Cursive Clojure plugin 46 | .idea/replstate.xml 47 | 48 | # Ruby plugin and RubyMine 49 | /.rakeTasks 50 | 51 | # Crashlytics plugin (for Android Studio and IntelliJ) 52 | com_crashlytics_export_strings.xml 53 | crashlytics.properties 54 | crashlytics-build.properties 55 | fabric.properties 56 | 57 | ### CLion+all Patch ### 58 | # Ignores the whole idea folder 59 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 60 | 61 | .idea/ 62 | 63 | 64 | # End of https://www.gitignore.io/api/clion+all -------------------------------------------------------------------------------- /lib/log_c/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See `log.c` for details. 6 | */ 7 | 8 | #ifndef LOG_H 9 | #define LOG_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define LOG_VERSION "0.1.0" 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | typedef struct { 23 | va_list ap; 24 | const char *fmt; 25 | const char *file; 26 | struct tm *time; 27 | void *udata; 28 | int line; 29 | int level; 30 | } log_Event; 31 | 32 | typedef void (*log_LogFn)(log_Event *ev); 33 | typedef void (*log_LockFn)(bool lock, void *udata); 34 | 35 | enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; 36 | 37 | // #define LOG_DISABLE 38 | 39 | #ifndef LOG_DISABLE 40 | #define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) 41 | #define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 42 | #define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 43 | #define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) 44 | #define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) 45 | #define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) 46 | #else 47 | #define log_trace(...) 48 | #define log_debug(...) 49 | #define log_info(...) 50 | #define log_warn(...) 51 | #define log_error(...) 52 | #define log_fatal(...) 53 | #endif 54 | 55 | const char* log_level_string(int level); 56 | void log_set_lock(log_LockFn fn, void *udata); 57 | void log_set_level(int level); 58 | void log_set_quiet(bool enable); 59 | int log_add_callback(log_LogFn fn, void *udata, int level); 60 | int log_add_fp(FILE *fp, int level); 61 | 62 | void log_log(int level, const char *file, int line, const char *fmt, ...); 63 | 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /lib/filesystem/buffered_filesystem.c: -------------------------------------------------------------------------------- 1 | #include "buffered_filesystem.h" 2 | 3 | #include 4 | 5 | static uint32_t override_buffer_size = 0; 6 | 7 | void set_override_buffer_size(uint32_t override_size) 8 | { 9 | override_buffer_size = override_size; 10 | } 11 | 12 | static FILE *buffered_fopen(const char *filename, const char *mode, const size_t buffer_size) 13 | { 14 | log_info("Buffered file open %s as %s", filename, mode ); 15 | FILE *file = fopen(filename, mode); 16 | 17 | if (override_buffer_size == 0) 18 | { 19 | log_info("setting buffer size %d", (int)buffer_size); 20 | if (setvbuf(file, NULL, _IOFBF, buffer_size) != 0) 21 | { 22 | log_error("setvbuf failed"); 23 | perror ("The following error occurred"); 24 | } 25 | } 26 | else 27 | { 28 | log_info("setting buffer size %d", (int)override_buffer_size); 29 | if (setvbuf(file, NULL, _IOFBF, override_buffer_size) != 0) 30 | { 31 | log_error("setvbuf failed"); 32 | perror ("The following error occurred"); 33 | } 34 | } 35 | log_info("File opened successfully as %d", fileno(file)); 36 | return file; 37 | } 38 | 39 | static off_t buffered_seek(FILE *file, off_t offset, int whence) 40 | { 41 | if (fseek(file, offset, whence) != 0) 42 | { 43 | log_error("fseek() failed"); 44 | perror ("The following error occurred"); 45 | return -1; 46 | } 47 | return offset; 48 | } 49 | 50 | static ssize_t buffered_write(FILE *file, const void *data, size_t len) 51 | { 52 | size_t write_result = fwrite(data,len, 1, file); 53 | if (write_result != 1) 54 | { 55 | log_error("Error writing to backing store %d %d\n", (int)len, write_result); 56 | perror ("The following error occurred"); 57 | return 0; 58 | } 59 | return len; 60 | } 61 | 62 | static ssize_t buffered_read(FILE *file, void *data, size_t len) 63 | { 64 | size_t read_result = fread(data, len, 1, file); 65 | if (read_result != 1) 66 | { 67 | log_error("Error reading to backing store\n"); 68 | perror ("The following error occurred"); 69 | return 0; 70 | } 71 | return len; 72 | } 73 | 74 | static int buffered_close(FILE *file) 75 | { 76 | return fclose(file); 77 | } 78 | 79 | const filesystem buffered_filesystem = { 80 | .fopen = buffered_fopen, 81 | .seek = buffered_seek, 82 | .write = buffered_write, 83 | .read = buffered_read, 84 | .close = buffered_close}; -------------------------------------------------------------------------------- /components/fatfs/test/test_fatfs_common.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | /** 18 | * @file test_fatfs_common.h 19 | * @brief Common routines for FAT-on-SDMMC and FAT-on-WL tests 20 | */ 21 | 22 | #define HEAP_SIZE_CAPTURE(heap_size) \ 23 | heap_size = esp_get_free_heap_size(); 24 | 25 | #define HEAP_SIZE_CHECK(heap_size, tolerance) \ 26 | do {\ 27 | size_t final_heap_size = esp_get_free_heap_size(); \ 28 | if (final_heap_size < heap_size - tolerance) { \ 29 | printf("Initial heap size: %d, final: %d, diff=%d\n", heap_size, final_heap_size, heap_size - final_heap_size); \ 30 | } \ 31 | } while(0) 32 | 33 | const char* fatfs_test_hello_str; 34 | const char* fatfs_test_hello_str_utf; 35 | 36 | void test_fatfs_create_file_with_text(const char* name, const char* text); 37 | 38 | void test_fatfs_overwrite_append(const char* filename); 39 | 40 | void test_fatfs_read_file(const char* filename); 41 | 42 | void test_fatfs_read_file_utf_8(const char* filename); 43 | 44 | void test_fatfs_pread_file(const char* filename); 45 | 46 | void test_fatfs_pwrite_file(const char* filename); 47 | 48 | void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count); 49 | 50 | void test_fatfs_lseek(const char* filename); 51 | 52 | void test_fatfs_truncate_file(const char* path); 53 | 54 | void test_fatfs_stat(const char* filename, const char* root_dir); 55 | 56 | void test_fatfs_utime(const char* filename, const char* root_dir); 57 | 58 | void test_fatfs_unlink(const char* filename); 59 | 60 | void test_fatfs_link_rename(const char* filename_prefix); 61 | 62 | void test_fatfs_concurrent(const char* filename_prefix); 63 | 64 | void test_fatfs_mkdir_rmdir(const char* filename_prefix); 65 | 66 | void test_fatfs_can_opendir(const char* path); 67 | 68 | void test_fatfs_opendir_readdir_rewinddir(const char* dir_prefix); 69 | 70 | void test_fatfs_opendir_readdir_rewinddir_utf_8(const char* dir_prefix); 71 | 72 | void test_fatfs_rw_speed(const char* filename, void* buf, size_t buf_size, size_t file_size, bool write); 73 | -------------------------------------------------------------------------------- /components/fatfs/diskio/diskio_impl.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | #include 22 | typedef unsigned int UINT; 23 | typedef unsigned char BYTE; 24 | typedef uint32_t DWORD; 25 | 26 | #define FF_DRV_NOT_USED 0xFF 27 | 28 | #include "diskio.h" 29 | #include "esp_err.h" 30 | 31 | /** 32 | * Structure of pointers to disk IO driver functions. 33 | * 34 | * See FatFs documentation for details about these functions 35 | */ 36 | typedef struct { 37 | DSTATUS (*init) (unsigned char pdrv); /*!< disk initialization function */ 38 | DSTATUS (*status) (unsigned char pdrv); /*!< disk status check function */ 39 | DRESULT (*read) (unsigned char pdrv, unsigned char* buff, uint32_t sector, unsigned count); /*!< sector read function */ 40 | DRESULT (*write) (unsigned char pdrv, const unsigned char* buff, uint32_t sector, unsigned count); /*!< sector write function */ 41 | DRESULT (*ioctl) (unsigned char pdrv, unsigned char cmd, void* buff); /*!< function to get info about disk and do some misc operations */ 42 | } ff_diskio_impl_t; 43 | 44 | /** 45 | * Register or unregister diskio driver for given drive number. 46 | * 47 | * When FATFS library calls one of disk_xxx functions for driver number pdrv, 48 | * corresponding function in discio_impl for given pdrv will be called. 49 | * 50 | * @param pdrv drive number 51 | * @param discio_impl pointer to ff_diskio_impl_t structure with diskio functions 52 | * or NULL to unregister and free previously registered drive 53 | */ 54 | void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl); 55 | 56 | #define ff_diskio_unregister(pdrv_) ff_diskio_register(pdrv_, NULL) 57 | 58 | 59 | /** 60 | * Get next available drive number 61 | * 62 | * @param out_pdrv pointer to the byte to set if successful 63 | * 64 | * @return ESP_OK on success 65 | * ESP_ERR_NOT_FOUND if all drives are attached 66 | */ 67 | esp_err_t ff_diskio_get_drive(BYTE* out_pdrv); 68 | 69 | 70 | #ifdef __cplusplus 71 | } 72 | #endif 73 | -------------------------------------------------------------------------------- /lib/benchmarks/benchmark_printout.cpp: -------------------------------------------------------------------------------- 1 | #include "benchmark_printout.h" 2 | #include 3 | 4 | #define __STDC_FORMAT_MACROS 1 5 | #include 6 | 7 | template 8 | std::string FormatWithCommas(T value) 9 | { 10 | std::string temp = std::to_string((int64_t)value); 11 | 12 | unsigned int numLength = temp.size(); 13 | 14 | temp.reserve((numLength - 1) / 3 + numLength); 15 | for (unsigned int i = 1; i <= (numLength - 1) / 3; i++) 16 | temp.insert(numLength - i * 3, 1, ','); 17 | 18 | return temp; 19 | 20 | // for (auto i = 0; i < number.length(); i++){ 21 | // if (((i % 3) == 0) && (i != 0) && (i != number.length())){ 22 | // number.insert(number.length() - i,","); 23 | // } 24 | // } 25 | // return number; 26 | } 27 | 28 | std::string humanSize(uint64_t bytes) 29 | { 30 | char *suffix[] = {"B", "KB", "MB", "GB", "TB"}; 31 | char length = sizeof(suffix) / sizeof(suffix[0]); 32 | 33 | int i = 0; 34 | double dblBytes = bytes; 35 | 36 | if (bytes > 1024) { 37 | for (i = 0; (bytes / 1024) > 0 && i 2 | #include 3 | 4 | #include "ff.h" 5 | #include "esp_partition.h" 6 | #include "wear_levelling.h" 7 | #include "diskio_impl.h" 8 | #include "diskio_wl.h" 9 | 10 | #include "catch.hpp" 11 | 12 | extern "C" void _spi_flash_init(const char* chip_size, size_t block_size, size_t sector_size, size_t page_size, const char* partition_bin); 13 | 14 | TEST_CASE("create volume, open file, write and read back data", "[fatfs]") 15 | { 16 | _spi_flash_init(CONFIG_ESPTOOLPY_FLASHSIZE, CONFIG_WL_SECTOR_SIZE * 16, CONFIG_WL_SECTOR_SIZE, CONFIG_WL_SECTOR_SIZE, "partition_table.bin"); 17 | 18 | FRESULT fr_result; 19 | BYTE pdrv; 20 | FATFS fs; 21 | FIL file; 22 | UINT bw; 23 | 24 | esp_err_t esp_result; 25 | 26 | const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, "storage"); 27 | 28 | // Mount wear-levelled partition 29 | wl_handle_t wl_handle; 30 | esp_result = wl_mount(partition, &wl_handle); 31 | REQUIRE(esp_result == ESP_OK); 32 | 33 | // Get a physical drive 34 | esp_result = ff_diskio_get_drive(&pdrv); 35 | REQUIRE(esp_result == ESP_OK); 36 | 37 | // Register physical drive as wear-levelled partition 38 | esp_result = ff_diskio_register_wl_partition(pdrv, wl_handle); 39 | 40 | // Create FAT volume on the entire disk 41 | DWORD part_list[] = {100, 0, 0, 0}; 42 | BYTE work_area[FF_MAX_SS]; 43 | 44 | fr_result = f_fdisk(pdrv, part_list, work_area); 45 | REQUIRE(fr_result == FR_OK); 46 | fr_result = f_mkfs("", FM_ANY, 0, work_area, sizeof(work_area)); // Use default volume 47 | 48 | // Mount the volume 49 | fr_result = f_mount(&fs, "", 0); 50 | REQUIRE(fr_result == FR_OK); 51 | 52 | // Open, write and read data 53 | fr_result = f_open(&file, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE); 54 | REQUIRE(fr_result == FR_OK); 55 | 56 | // Generate data 57 | uint32_t data_size = 100000; 58 | 59 | char *data = (char*) malloc(data_size); 60 | char *read = (char*) malloc(data_size); 61 | 62 | for(uint32_t i = 0; i < data_size; i += sizeof(i)) 63 | { 64 | *((uint32_t*)(data + i)) = i; 65 | } 66 | 67 | // Write generated data 68 | fr_result = f_write(&file, data, data_size, &bw); 69 | REQUIRE(fr_result == FR_OK); 70 | REQUIRE(bw == data_size); 71 | 72 | // Move to beginning of file 73 | fr_result = f_lseek(&file, 0); 74 | REQUIRE(fr_result == FR_OK); 75 | 76 | // Read written data 77 | fr_result = f_read(&file, read, data_size, &bw); 78 | REQUIRE(fr_result == FR_OK); 79 | REQUIRE(bw == data_size); 80 | 81 | REQUIRE(memcmp(data, read, data_size) == 0); 82 | 83 | // Close file 84 | fr_result = f_close(&file); 85 | REQUIRE(fr_result == FR_OK); 86 | 87 | // Unmount default volume 88 | fr_result = f_mount(0, "", 0); 89 | REQUIRE(fr_result == FR_OK); 90 | 91 | free(read); 92 | free(data); 93 | } 94 | -------------------------------------------------------------------------------- /lib/log_c/README.md: -------------------------------------------------------------------------------- 1 | # log.c 2 | A simple logging library implemented in C99 3 | 4 | ![screenshot](https://cloud.githubusercontent.com/assets/3920290/23831970/a2415e96-0723-11e7-9886-f8f5d2de60fe.png) 5 | 6 | 7 | ## Usage 8 | **[log.c](src/log.c?raw=1)** and **[log.h](src/log.h?raw=1)** should be dropped 9 | into an existing project and compiled along with it. The library provides 6 10 | function-like macros for logging: 11 | 12 | ```c 13 | log_trace(const char *fmt, ...); 14 | log_debug(const char *fmt, ...); 15 | log_info(const char *fmt, ...); 16 | log_warn(const char *fmt, ...); 17 | log_error(const char *fmt, ...); 18 | log_fatal(const char *fmt, ...); 19 | ``` 20 | 21 | Each function takes a printf format string followed by additional arguments: 22 | 23 | ```c 24 | log_trace("Hello %s", "world") 25 | ``` 26 | 27 | Resulting in a line with the given format printed to stderr: 28 | 29 | ``` 30 | 20:18:26 TRACE src/main.c:11: Hello world 31 | ``` 32 | 33 | 34 | #### log_set_quiet(bool enable) 35 | Quiet-mode can be enabled by passing `true` to the `log_set_quiet()` function. 36 | While this mode is enabled the library will not output anything to `stderr`, but 37 | will continue to write to files and callbacks if any are set. 38 | 39 | 40 | #### log_set_level(int level) 41 | The current logging level can be set by using the `log_set_level()` function. 42 | All logs below the given level will not be written to `stderr`. By default the 43 | level is `LOG_TRACE`, such that nothing is ignored. 44 | 45 | 46 | #### log_add_fp(FILE *fp, int level) 47 | One or more file pointers where the log will be written can be provided to the 48 | library by using the `log_add_fp()` function. The data written to the file 49 | output is of the following format: 50 | 51 | ``` 52 | 2047-03-11 20:18:26 TRACE src/main.c:11: Hello world 53 | ``` 54 | 55 | Any messages below the given `level` are ignored. If the library failed to add a 56 | file pointer a value less-than-zero is returned. 57 | 58 | 59 | #### log_add_callback(log_LogFn fn, void *udata, int level) 60 | One or more callback functions which are called with the log data can be 61 | provided to the library by using the `log_add_callback()` function. A callback 62 | function is passed a `log_Event` structure containing the `line` number, 63 | `filename`, `fmt` string, `va` printf va\_list, `level` and the given `udata`. 64 | 65 | 66 | #### log_set_lock(log_LockFn fn, void *udata) 67 | If the log will be written to from multiple threads a lock function can be set. 68 | The function is passed the boolean `true` if the lock should be acquired or 69 | `false` if the lock should be released and the given `udata` value. 70 | 71 | 72 | #### const char* log_level_string(int level) 73 | Returns the name of the given log level as a string. 74 | 75 | 76 | #### LOG_USE_COLOR 77 | If the library is compiled with `-DLOG_USE_COLOR` ANSI color escape codes will 78 | be used when printing. 79 | 80 | 81 | ## License 82 | This library is free software; you can redistribute it and/or modify it under 83 | the terms of the MIT license. See [LICENSE](LICENSE) for details. 84 | -------------------------------------------------------------------------------- /components/fatfs/diskio/diskio.c: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*/ 2 | /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */ 3 | /* ESP-IDF port Copyright 2016 Espressif Systems (Shanghai) PTE LTD */ 4 | /*-----------------------------------------------------------------------*/ 5 | /* If a working storage control module is available, it should be */ 6 | /* attached to the FatFs via a glue function rather than modifying it. */ 7 | /* This is an example of glue functions to attach various exsisting */ 8 | /* storage control modules to the FatFs module with a defined API. */ 9 | /*-----------------------------------------------------------------------*/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "diskio_impl.h" 16 | #include "ffconf.h" 17 | #include "ff.h" 18 | 19 | static ff_diskio_impl_t * s_impls[FF_VOLUMES] = { NULL }; 20 | 21 | #if FF_MULTI_PARTITION /* Multiple partition configuration */ 22 | PARTITION VolToPart[] = { 23 | {0, 0}, /* Logical drive 0 ==> Physical drive 0, auto detection */ 24 | {1, 0} /* Logical drive 1 ==> Physical drive 1, auto detection */ 25 | }; 26 | #endif 27 | 28 | esp_err_t ff_diskio_get_drive(BYTE* out_pdrv) 29 | { 30 | BYTE i; 31 | for(i=0; iinit(pdrv); 63 | } 64 | DSTATUS ff_disk_status (BYTE pdrv) 65 | { 66 | return s_impls[pdrv]->status(pdrv); 67 | } 68 | DRESULT ff_disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) 69 | { 70 | return s_impls[pdrv]->read(pdrv, buff, sector, count); 71 | } 72 | DRESULT ff_disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) 73 | { 74 | return s_impls[pdrv]->write(pdrv, buff, sector, count); 75 | } 76 | DRESULT ff_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff) 77 | { 78 | return s_impls[pdrv]->ioctl(pdrv, cmd, buff); 79 | } 80 | 81 | DWORD get_fattime(void) 82 | { 83 | time_t t = time(NULL); 84 | struct tm tmr; 85 | localtime_r(&t, &tmr); 86 | int year = tmr.tm_year < 80 ? 0 : tmr.tm_year - 80; 87 | return ((DWORD)(year) << 25) 88 | | ((DWORD)(tmr.tm_mon + 1) << 21) 89 | | ((DWORD)tmr.tm_mday << 16) 90 | | (WORD)(tmr.tm_hour << 11) 91 | | (WORD)(tmr.tm_min << 5) 92 | | (WORD)(tmr.tm_sec >> 1); 93 | } 94 | -------------------------------------------------------------------------------- /components/fatfs/diskio/diskio_sdmmc.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "diskio_impl.h" 16 | #include "ffconf.h" 17 | #include "ff.h" 18 | #include "sdmmc_cmd.h" 19 | #include "esp_log.h" 20 | #include "esp_compiler.h" 21 | 22 | static sdmmc_card_t* s_cards[FF_VOLUMES] = { NULL }; 23 | 24 | static const char* TAG = "diskio_sdmmc"; 25 | 26 | DSTATUS ff_sdmmc_initialize (BYTE pdrv) 27 | { 28 | return 0; 29 | } 30 | 31 | DSTATUS ff_sdmmc_status (BYTE pdrv) 32 | { 33 | return 0; 34 | } 35 | 36 | DRESULT ff_sdmmc_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) 37 | { 38 | sdmmc_card_t* card = s_cards[pdrv]; 39 | assert(card); 40 | esp_err_t err = sdmmc_read_sectors(card, buff, sector, count); 41 | if (unlikely(err != ESP_OK)) { 42 | ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err); 43 | return RES_ERROR; 44 | } 45 | return RES_OK; 46 | } 47 | 48 | DRESULT ff_sdmmc_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) 49 | { 50 | sdmmc_card_t* card = s_cards[pdrv]; 51 | assert(card); 52 | esp_err_t err = sdmmc_write_sectors(card, buff, sector, count); 53 | if (unlikely(err != ESP_OK)) { 54 | ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err); 55 | return RES_ERROR; 56 | } 57 | return RES_OK; 58 | } 59 | 60 | DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff) 61 | { 62 | sdmmc_card_t* card = s_cards[pdrv]; 63 | assert(card); 64 | switch(cmd) { 65 | case CTRL_SYNC: 66 | return RES_OK; 67 | case GET_SECTOR_COUNT: 68 | *((DWORD*) buff) = card->csd.capacity; 69 | return RES_OK; 70 | case GET_SECTOR_SIZE: 71 | *((WORD*) buff) = card->csd.sector_size; 72 | return RES_OK; 73 | case GET_BLOCK_SIZE: 74 | return RES_ERROR; 75 | } 76 | return RES_ERROR; 77 | } 78 | 79 | void ff_diskio_register_sdmmc(BYTE pdrv, sdmmc_card_t* card) 80 | { 81 | static const ff_diskio_impl_t sdmmc_impl = { 82 | .init = &ff_sdmmc_initialize, 83 | .status = &ff_sdmmc_status, 84 | .read = &ff_sdmmc_read, 85 | .write = &ff_sdmmc_write, 86 | .ioctl = &ff_sdmmc_ioctl 87 | }; 88 | s_cards[pdrv] = card; 89 | ff_diskio_register(pdrv, &sdmmc_impl); 90 | } 91 | 92 | BYTE ff_diskio_get_pdrv_card(const sdmmc_card_t* card) 93 | { 94 | for (int i = 0; i < FF_VOLUMES; i++) { 95 | if (card == s_cards[i]) { 96 | return i; 97 | } 98 | } 99 | return 0xff; 100 | } 101 | -------------------------------------------------------------------------------- /components/fatfs/diskio/diskio_rawflash.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include "diskio_impl.h" 17 | #include "ffconf.h" 18 | #include "ff.h" 19 | #include "esp_log.h" 20 | #include "diskio_rawflash.h" 21 | #include "esp_compiler.h" 22 | 23 | static const char* TAG = "diskio_rawflash"; 24 | 25 | const esp_partition_t* ff_raw_handles[FF_VOLUMES]; 26 | 27 | 28 | DSTATUS ff_raw_initialize (BYTE pdrv) 29 | { 30 | return 0; 31 | } 32 | 33 | DSTATUS ff_raw_status (BYTE pdrv) 34 | { 35 | return 0; 36 | } 37 | 38 | DRESULT ff_raw_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count) 39 | { 40 | ESP_LOGV(TAG, "ff_raw_read - pdrv=%i, sector=%i, count=%in", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count); 41 | const esp_partition_t* part = ff_raw_handles[pdrv]; 42 | assert(part); 43 | esp_err_t err = esp_partition_read(part, sector * SPI_FLASH_SEC_SIZE, buff, count * SPI_FLASH_SEC_SIZE); 44 | if (unlikely(err != ESP_OK)) { 45 | ESP_LOGE(TAG, "esp_partition_read failed (0x%x)", err); 46 | return RES_ERROR; 47 | } 48 | return RES_OK; 49 | } 50 | 51 | 52 | DRESULT ff_raw_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) 53 | { 54 | return RES_ERROR; 55 | } 56 | 57 | DRESULT ff_raw_ioctl (BYTE pdrv, BYTE cmd, void *buff) 58 | { 59 | const esp_partition_t* part = ff_raw_handles[pdrv]; 60 | ESP_LOGV(TAG, "ff_raw_ioctl: cmd=%in", cmd); 61 | assert(part); 62 | switch (cmd) { 63 | case CTRL_SYNC: 64 | return RES_OK; 65 | case GET_SECTOR_COUNT: 66 | *((DWORD *) buff) = part->size / SPI_FLASH_SEC_SIZE; 67 | return RES_OK; 68 | case GET_SECTOR_SIZE: 69 | *((WORD *) buff) = SPI_FLASH_SEC_SIZE; 70 | return RES_OK; 71 | case GET_BLOCK_SIZE: 72 | return RES_ERROR; 73 | } 74 | return RES_ERROR; 75 | } 76 | 77 | 78 | esp_err_t ff_diskio_register_raw_partition(BYTE pdrv, const esp_partition_t* part_handle) 79 | { 80 | if (pdrv >= FF_VOLUMES) { 81 | return ESP_ERR_INVALID_ARG; 82 | } 83 | static const ff_diskio_impl_t raw_impl = { 84 | .init = &ff_raw_initialize, 85 | .status = &ff_raw_status, 86 | .read = &ff_raw_read, 87 | .write = &ff_raw_write, 88 | .ioctl = &ff_raw_ioctl 89 | }; 90 | ff_diskio_register(pdrv, &raw_impl); 91 | ff_raw_handles[pdrv] = part_handle; 92 | return ESP_OK; 93 | 94 | } 95 | 96 | 97 | BYTE ff_diskio_get_pdrv_raw(const esp_partition_t* part_handle) 98 | { 99 | for (int i = 0; i < FF_VOLUMES; i++) { 100 | if (part_handle == ff_raw_handles[i]) { 101 | return i; 102 | } 103 | } 104 | return 0xff; 105 | } 106 | -------------------------------------------------------------------------------- /lib/virtual_directory/virtual_directory.c: -------------------------------------------------------------------------------- 1 | #include "virtual_directory.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | static char currdir[PATH_MAX + 1] = ""; 12 | 13 | static char *previous_strrchr(const char *str, char *from, char delimiter) 14 | { 15 | while (from >= str) 16 | { 17 | if (from[0] == delimiter) 18 | { 19 | return from; 20 | } 21 | from--; 22 | } 23 | return NULL; 24 | } 25 | 26 | static void normalize_path(const char *path, char *normalized) 27 | { 28 | strcpy(normalized, path); 29 | // printf("normalized1 %s\r\n", normalized); 30 | bool has_path_traversal = true; 31 | //remove all /./ 32 | while (has_path_traversal) 33 | { 34 | char *dot = strstr(normalized, "/./"); 35 | if (dot != NULL) 36 | { 37 | strcpy(dot, dot + 2); 38 | } 39 | else 40 | { 41 | has_path_traversal = false; 42 | } 43 | // printf("normalized2 %s\r\n", normalized); 44 | } 45 | 46 | // printf("normalized3 %s\r\n", normalized); 47 | has_path_traversal = true; 48 | while (has_path_traversal) 49 | { 50 | // printf("normalized3.1 %s\r\n", normalized); 51 | char *dotdot = strstr(normalized, "/../"); 52 | if (dotdot != NULL) 53 | { 54 | // printf("found ..\r\n"); 55 | if (dotdot == normalized) 56 | { 57 | // printf("already on the 1st /\r\n"); 58 | //if its the first /, do nothing, just remove the .. 59 | strcpy(dotdot, dotdot + 3); 60 | } 61 | else 62 | { 63 | //find previous / 64 | char *previous_slash = previous_strrchr(normalized, dotdot - 1, '/'); 65 | if (previous_slash != NULL) 66 | { 67 | // printf("removing previous /\r\n"); 68 | //if its not, remove the previous / 69 | strcpy(previous_slash, dotdot + 3); 70 | } 71 | else 72 | { 73 | // printf("didn't find /\r\n"); 74 | has_path_traversal = false; 75 | } 76 | } 77 | } 78 | else 79 | { 80 | // printf("has no ..\r\n"); 81 | has_path_traversal = false; 82 | } 83 | // printf("normalized4 %s\r\n", normalized); 84 | } 85 | // printf("normalized5 %s\r\n", normalized); 86 | } 87 | 88 | int vd_chdir(const char *path) 89 | { 90 | if (!path) 91 | { 92 | errno = EFAULT; 93 | return -1; 94 | } 95 | 96 | if (!*path) 97 | { 98 | errno = ENOENT; 99 | return -1; 100 | } 101 | 102 | if (path[0] == '/' || path[0] == '\\' || path[1] == ':') 103 | { 104 | strncpy(currdir, path, PATH_MAX); 105 | } 106 | else if (currdir[strlen(currdir) - 1] != '/' && currdir[strlen(currdir) - 1] != '\\') 107 | { 108 | strncat(currdir, "/", sizeof(currdir)-1); //handle "\" ? 109 | strncat(currdir, path, sizeof(currdir)-1); 110 | } 111 | 112 | char npath[256] = {0}; 113 | 114 | normalize_path(currdir, npath); 115 | 116 | strncpy(currdir, npath, PATH_MAX); 117 | 118 | return 0; 119 | } 120 | 121 | void vd_cwd(char *cwd_buffer, size_t cwd_buffer_length) 122 | { 123 | strncpy(cwd_buffer, currdir, cwd_buffer_length); 124 | } -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define NELEMS(x) (sizeof(x) / sizeof((x)[0])) 6 | 7 | #ifdef ESP32 8 | #include "sd_mount.h" 9 | #endif 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | uint32_t block_size[] = {512, 1024, 2 * 1024, 4 * 1024, 8 * 1024, 16 * 1024, 32 * 1024, 64 * 1024}; 18 | // bool syncs[] = {false, true}; 19 | bool syncs[] = {false}; 20 | 21 | void execute_full_benchmark() 22 | { 23 | ConsoleTable table{"Block", "R/W", "Rnd/Seq", "Sync", "Aligned", "time", "iops", "bytes", "bps"}; 24 | table.setPadding(2); 25 | table.setStyle(1); 26 | 27 | // write sequential 28 | for (uint32_t s = 0; s < NELEMS(syncs); s++) 29 | { 30 | for (uint32_t i = 0; i < NELEMS(block_size); i++) 31 | { 32 | struct statistics stats; 33 | test_write_sequential(syncs[s], block_size[i], &stats); 34 | print(stats); 35 | addTable(table, stats); 36 | printf("\r\n"); 37 | test_read_sequential(syncs[s], block_size[i], &stats); 38 | print(stats); 39 | addTable(table, stats); 40 | printf("\r\n"); 41 | } 42 | } 43 | // write random 44 | for (uint32_t s = 0; s < NELEMS(syncs); s++) 45 | { 46 | for (uint32_t i = 0; i < NELEMS(block_size); i++) 47 | { 48 | struct statistics stats; 49 | test_write_random(syncs[s], block_size[i], &stats); 50 | print(stats); 51 | addTable(table, stats); 52 | printf("\r\n"); 53 | test_read_random(syncs[s], block_size[i], &stats); 54 | print(stats); 55 | addTable(table, stats); 56 | printf("\r\n"); 57 | } 58 | } 59 | 60 | for (uint32_t s = 0; s < NELEMS(syncs); s++) 61 | { 62 | for (uint32_t i = 0; i < NELEMS(block_size); i++) 63 | { 64 | struct statistics stats; 65 | test_write_random_aligned(syncs[s], block_size[i], &stats); 66 | print(stats); 67 | addTable(table, stats); 68 | printf("\r\n"); 69 | test_read_random_aligned(syncs[s], block_size[i], &stats); 70 | print(stats); 71 | addTable(table, stats); 72 | printf("\r\n"); 73 | } 74 | } 75 | 76 | std::cout << table; 77 | } 78 | 79 | void execute_filesystem_tests() 80 | { 81 | printf("Executing default newlib buffer size buffered test...\n"); 82 | set_override_buffer_size(128); 83 | set_filesystem(&buffered_filesystem); 84 | execute_full_benchmark(); 85 | 86 | printf("Executing 2048 buffer size buffered test...\n"); 87 | set_override_buffer_size(2048); 88 | set_filesystem(&buffered_filesystem); 89 | execute_full_benchmark(); 90 | 91 | printf("Executing 4096 buffer size buffered test...\n"); 92 | set_override_buffer_size(4096); 93 | set_filesystem(&buffered_filesystem); 94 | execute_full_benchmark(); 95 | 96 | printf("Executing unbuffered test...\n"); 97 | set_filesystem(&unbuffered_filesystem); 98 | execute_full_benchmark(); 99 | } 100 | 101 | MAIN() 102 | { 103 | printf("Starting...\r\n"); 104 | 105 | #ifdef ESP32 106 | printf("Testing using SPI...\n"); 107 | mount_sd_spi(); 108 | execute_filesystem_tests(); 109 | unmound_sd(); 110 | 111 | printf("Testing using SDMMC...\n"); 112 | mount_sd_sdmmc(); 113 | execute_filesystem_tests(); 114 | unmound_sd(); 115 | #else 116 | execute_filesystem_tests(); 117 | #endif 118 | return 0; 119 | } -------------------------------------------------------------------------------- /components/fatfs/test_fatfs_host/Makefile: -------------------------------------------------------------------------------- 1 | ifndef COMPONENT 2 | COMPONENT := fatfs 3 | endif 4 | 5 | COMPONENT_LIB := lib$(COMPONENT).a 6 | TEST_PROGRAM := test_$(COMPONENT) 7 | 8 | STUBS_LIB_DIR := ../../../components/spi_flash/sim/stubs 9 | STUBS_LIB_BUILD_DIR := $(STUBS_LIB_DIR)/build 10 | STUBS_LIB := libstubs.a 11 | 12 | SPI_FLASH_SIM_DIR := ../../../components/spi_flash/sim 13 | SPI_FLASH_SIM_BUILD_DIR := $(SPI_FLASH_SIM_DIR)/build 14 | SPI_FLASH_SIM_LIB := libspi_flash.a 15 | 16 | WEAR_LEVELLING_DIR := ../../../components/wear_levelling/test_wl_host 17 | WEAR_LEVELLING_BUILD_DIR := $(WEAR_LEVELLING_DIR)/build 18 | WEAR_LEVELLING_LIB := libwl.a 19 | 20 | include Makefile.files 21 | 22 | all: test 23 | 24 | ifndef SDKCONFIG 25 | SDKCONFIG_DIR := $(dir $(realpath sdkconfig/sdkconfig.h)) 26 | SDKCONFIG := $(SDKCONFIG_DIR)sdkconfig.h 27 | else 28 | SDKCONFIG_DIR := $(dir $(realpath $(SDKCONFIG))) 29 | endif 30 | 31 | INCLUDE_FLAGS := $(addprefix -I, $(INCLUDE_DIRS) $(SDKCONFIG_DIR) ../../../tools/catch) 32 | 33 | CPPFLAGS += $(INCLUDE_FLAGS) -g -m32 34 | CXXFLAGS += $(INCLUDE_FLAGS) -std=c++11 -g -m32 35 | 36 | # Build libraries that this component is dependent on 37 | $(STUBS_LIB_BUILD_DIR)/$(STUBS_LIB): force 38 | $(MAKE) -C $(STUBS_LIB_DIR) lib SDKCONFIG=$(SDKCONFIG) 39 | 40 | $(SPI_FLASH_SIM_BUILD_DIR)/$(SPI_FLASH_SIM_LIB): force 41 | $(MAKE) -C $(SPI_FLASH_SIM_DIR) lib SDKCONFIG=$(SDKCONFIG) 42 | 43 | $(WEAR_LEVELLING_BUILD_DIR)/$(WEAR_LEVELLING_LIB): force 44 | $(MAKE) -C $(WEAR_LEVELLING_DIR) lib SDKCONFIG=$(SDKCONFIG) 45 | 46 | # Create target for building this component as a library 47 | CFILES := $(filter %.c, $(SOURCE_FILES)) 48 | CPPFILES := $(filter %.cpp, $(SOURCE_FILES)) 49 | 50 | CTARGET = ${2}/$(patsubst %.c,%.o,$(notdir ${1})) 51 | CPPTARGET = ${2}/$(patsubst %.cpp,%.o,$(notdir ${1})) 52 | 53 | ifndef BUILD_DIR 54 | BUILD_DIR := build 55 | endif 56 | 57 | OBJ_FILES := $(addprefix $(BUILD_DIR)/, $(filter %.o, $(notdir $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o)))) 58 | 59 | define COMPILE_C 60 | $(call CTARGET, ${1}, $(BUILD_DIR)) : ${1} $(SDKCONFIG) 61 | mkdir -p $(BUILD_DIR) 62 | $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(call CTARGET, ${1}, $(BUILD_DIR)) ${1} 63 | endef 64 | 65 | define COMPILE_CPP 66 | $(call CPPTARGET, ${1}, $(BUILD_DIR)) : ${1} $(SDKCONFIG) 67 | mkdir -p $(BUILD_DIR) 68 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $(call CPPTARGET, ${1}, $(BUILD_DIR)) ${1} 69 | endef 70 | 71 | $(BUILD_DIR)/$(COMPONENT_LIB): $(OBJ_FILES) $(SDKCONFIG) 72 | mkdir -p $(BUILD_DIR) 73 | $(AR) rcs $@ $^ 74 | 75 | lib: $(BUILD_DIR)/$(COMPONENT_LIB) 76 | 77 | $(foreach cfile, $(CFILES), $(eval $(call COMPILE_C, $(cfile)))) 78 | $(foreach cxxfile, $(CPPFILES), $(eval $(call COMPILE_CPP, $(cxxfile)))) 79 | 80 | # Create target for building this component as a test 81 | TEST_SOURCE_FILES = \ 82 | test_fatfs.cpp \ 83 | main.cpp \ 84 | 85 | TEST_OBJ_FILES = $(filter %.o, $(TEST_SOURCE_FILES:.cpp=.o) $(TEST_SOURCE_FILES:.c=.o)) 86 | 87 | $(TEST_PROGRAM): lib $(TEST_OBJ_FILES) $(WEAR_LEVELLING_BUILD_DIR)/$(WEAR_LEVELLING_LIB) $(SPI_FLASH_SIM_BUILD_DIR)/$(SPI_FLASH_SIM_LIB) $(STUBS_LIB_BUILD_DIR)/$(STUBS_LIB) partition_table.bin $(SDKCONFIG) 88 | g++ $(LDFLAGS) $(CXXFLAGS) -o $@ $(TEST_OBJ_FILES) -L$(BUILD_DIR) -l:$(COMPONENT_LIB) -L$(WEAR_LEVELLING_BUILD_DIR) -l:$(WEAR_LEVELLING_LIB) -L$(SPI_FLASH_SIM_BUILD_DIR) -l:$(SPI_FLASH_SIM_LIB) -L$(STUBS_LIB_BUILD_DIR) -l:$(STUBS_LIB) 89 | 90 | test: $(TEST_PROGRAM) 91 | ./$(TEST_PROGRAM) 92 | 93 | # Create other necessary targets 94 | partition_table.bin: partition_table.csv 95 | python ../../../components/partition_table/gen_esp32part.py --verify $< $@ 96 | 97 | force: 98 | 99 | # Create target to cleanup files 100 | clean: 101 | $(MAKE) -C $(STUBS_LIB_DIR) clean 102 | $(MAKE) -C $(SPI_FLASH_SIM_DIR) clean 103 | $(MAKE) -C $(WEAR_LEVELLING_DIR) clean 104 | rm -f $(OBJ_FILES) $(TEST_OBJ_FILES) $(TEST_PROGRAM) $(COMPONENT_LIB) partition_table.bin 105 | 106 | .PHONY: all lib test clean force 107 | -------------------------------------------------------------------------------- /components/fatfs/port/freertos/ffsystem.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Sample Code of OS Dependent Functions for FatFs */ 3 | /* (C)ChaN, 2017 */ 4 | /*------------------------------------------------------------------------*/ 5 | 6 | 7 | #include 8 | #include 9 | #include "ff.h" 10 | #include "sdkconfig.h" 11 | #ifdef CONFIG_FATFS_ALLOC_PREFER_EXTRAM 12 | #include "esp_heap_caps.h" 13 | #endif 14 | 15 | void* ff_memalloc ( /* Returns pointer to the allocated memory block (null on not enough core) */ 16 | unsigned msize /* Number of bytes to allocate */ 17 | ) 18 | { 19 | #ifdef CONFIG_FATFS_ALLOC_PREFER_EXTRAM 20 | return heap_caps_malloc_prefer(msize, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM, 21 | MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL); 22 | #else 23 | return malloc(msize); 24 | #endif 25 | } 26 | 27 | 28 | /*------------------------------------------------------------------------*/ 29 | /* Free a memory block */ 30 | /*------------------------------------------------------------------------*/ 31 | 32 | void ff_memfree ( 33 | void* mblock /* Pointer to the memory block to free (nothing to do for null) */ 34 | ) 35 | { 36 | free(mblock); /* Free the memory block with POSIX API */ 37 | } 38 | 39 | 40 | 41 | 42 | #if FF_FS_REENTRANT /* Mutal exclusion */ 43 | 44 | /*------------------------------------------------------------------------*/ 45 | /* Create a Synchronization Object */ 46 | /*------------------------------------------------------------------------*/ 47 | /* This function is called in f_mount() function to create a new 48 | / synchronization object for the volume, such as semaphore and mutex. 49 | / When a 0 is returned, the f_mount() function fails with FR_INT_ERR. 50 | */ 51 | 52 | 53 | int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */ 54 | BYTE vol, /* Corresponding volume (logical drive number) */ 55 | FF_SYNC_t *sobj /* Pointer to return the created sync object */ 56 | ) 57 | { 58 | *sobj = xSemaphoreCreateMutex(); 59 | return (*sobj != NULL) ? 1 : 0; 60 | } 61 | 62 | 63 | /*------------------------------------------------------------------------*/ 64 | /* Delete a Synchronization Object */ 65 | /*------------------------------------------------------------------------*/ 66 | /* This function is called in f_mount() function to delete a synchronization 67 | / object that created with ff_cre_syncobj() function. When a 0 is returned, 68 | / the f_mount() function fails with FR_INT_ERR. 69 | */ 70 | 71 | int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */ 72 | FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ 73 | ) 74 | { 75 | vSemaphoreDelete(sobj); 76 | return 1; 77 | } 78 | 79 | 80 | /*------------------------------------------------------------------------*/ 81 | /* Request Grant to Access the Volume */ 82 | /*------------------------------------------------------------------------*/ 83 | /* This function is called on entering file functions to lock the volume. 84 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 85 | */ 86 | 87 | int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ 88 | FF_SYNC_t sobj /* Sync object to wait */ 89 | ) 90 | { 91 | return (xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE) ? 1 : 0; 92 | } 93 | 94 | 95 | /*------------------------------------------------------------------------*/ 96 | /* Release Grant to Access the Volume */ 97 | /*------------------------------------------------------------------------*/ 98 | /* This function is called on leaving file functions to unlock the volume. 99 | */ 100 | 101 | void ff_rel_grant ( 102 | FF_SYNC_t sobj /* Sync object to be signaled */ 103 | ) 104 | { 105 | xSemaphoreGive(sobj); 106 | } 107 | 108 | #endif // FF_FS_REENTRANT 109 | -------------------------------------------------------------------------------- /components/fatfs/diskio/diskio_wl.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include "diskio_impl.h" 17 | #include "ffconf.h" 18 | #include "ff.h" 19 | #include "esp_log.h" 20 | #include "diskio_wl.h" 21 | #include "wear_levelling.h" 22 | #include "esp_compiler.h" 23 | 24 | static const char* TAG = "ff_diskio_spiflash"; 25 | 26 | wl_handle_t ff_wl_handles[FF_VOLUMES] = { 27 | WL_INVALID_HANDLE, 28 | WL_INVALID_HANDLE, 29 | }; 30 | 31 | DSTATUS ff_wl_initialize (BYTE pdrv) 32 | { 33 | return 0; 34 | } 35 | 36 | DSTATUS ff_wl_status (BYTE pdrv) 37 | { 38 | return 0; 39 | } 40 | 41 | DRESULT ff_wl_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count) 42 | { 43 | ESP_LOGV(TAG, "ff_wl_read - pdrv=%i, sector=%i, count=%i\n", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count); 44 | wl_handle_t wl_handle = ff_wl_handles[pdrv]; 45 | assert(wl_handle + 1); 46 | esp_err_t err = wl_read(wl_handle, sector * wl_sector_size(wl_handle), buff, count * wl_sector_size(wl_handle)); 47 | if (unlikely(err != ESP_OK)) { 48 | ESP_LOGE(TAG, "wl_read failed (%d)", err); 49 | return RES_ERROR; 50 | } 51 | return RES_OK; 52 | } 53 | 54 | DRESULT ff_wl_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) 55 | { 56 | ESP_LOGV(TAG, "ff_wl_write - pdrv=%i, sector=%i, count=%i\n", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count); 57 | wl_handle_t wl_handle = ff_wl_handles[pdrv]; 58 | assert(wl_handle + 1); 59 | esp_err_t err = wl_erase_range(wl_handle, sector * wl_sector_size(wl_handle), count * wl_sector_size(wl_handle)); 60 | if (unlikely(err != ESP_OK)) { 61 | ESP_LOGE(TAG, "wl_erase_range failed (%d)", err); 62 | return RES_ERROR; 63 | } 64 | err = wl_write(wl_handle, sector * wl_sector_size(wl_handle), buff, count * wl_sector_size(wl_handle)); 65 | if (unlikely(err != ESP_OK)) { 66 | ESP_LOGE(TAG, "wl_write failed (%d)", err); 67 | return RES_ERROR; 68 | } 69 | return RES_OK; 70 | } 71 | 72 | DRESULT ff_wl_ioctl (BYTE pdrv, BYTE cmd, void *buff) 73 | { 74 | wl_handle_t wl_handle = ff_wl_handles[pdrv]; 75 | ESP_LOGV(TAG, "ff_wl_ioctl: cmd=%i\n", cmd); 76 | assert(wl_handle + 1); 77 | switch (cmd) { 78 | case CTRL_SYNC: 79 | return RES_OK; 80 | case GET_SECTOR_COUNT: 81 | *((DWORD *) buff) = wl_size(wl_handle) / wl_sector_size(wl_handle); 82 | return RES_OK; 83 | case GET_SECTOR_SIZE: 84 | *((WORD *) buff) = wl_sector_size(wl_handle); 85 | return RES_OK; 86 | case GET_BLOCK_SIZE: 87 | return RES_ERROR; 88 | } 89 | return RES_ERROR; 90 | } 91 | 92 | 93 | esp_err_t ff_diskio_register_wl_partition(BYTE pdrv, wl_handle_t flash_handle) 94 | { 95 | if (pdrv >= FF_VOLUMES) { 96 | return ESP_ERR_INVALID_ARG; 97 | } 98 | static const ff_diskio_impl_t wl_impl = { 99 | .init = &ff_wl_initialize, 100 | .status = &ff_wl_status, 101 | .read = &ff_wl_read, 102 | .write = &ff_wl_write, 103 | .ioctl = &ff_wl_ioctl 104 | }; 105 | ff_wl_handles[pdrv] = flash_handle; 106 | ff_diskio_register(pdrv, &wl_impl); 107 | return ESP_OK; 108 | } 109 | 110 | BYTE ff_diskio_get_pdrv_wl(wl_handle_t flash_handle) 111 | { 112 | for (int i = 0; i < FF_VOLUMES; i++) { 113 | if (flash_handle == ff_wl_handles[i]) { 114 | return i; 115 | } 116 | } 117 | return 0xff; 118 | } 119 | 120 | void ff_diskio_clear_pdrv_wl(wl_handle_t flash_handle) 121 | { 122 | for (int i = 0; i < FF_VOLUMES; i++) { 123 | if (flash_handle == ff_wl_handles[i]) { 124 | ff_wl_handles[i] = WL_INVALID_HANDLE; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/ConsoleTable/README.md: -------------------------------------------------------------------------------- 1 | # ConsoleTable 2 | ConsoleTable is a library that allows you to organize your data in a table based structure. 3 | 4 | ## Overview 5 | 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 7 | 8 | Sometimes, it can be very frustrating to display data in a console with proper alignment. 9 | To make this easier, ConsoleTable was created. It is a customizable text based table structure which is really convenient to use. 10 | It allows you to organize your data in a table structure where you can easily add, update and delete rows. 11 | Furthermore, it comes with sorting functionality as well as different layouts the user can choose from. 12 | This is an example how the table can look like: 13 | ``` 14 | +---------------+-------------+-----------------+-----------------+---------------------+ 15 | | Country | Capital | Population | Area | Currency | 16 | +---------------+-------------+-----------------+-----------------+---------------------+ 17 | | Australia | NEW ENTRY | 24,877,800 | 7,692,024 km2 | Australian Dollar | 18 | | China | Beijing | 1,403,500,365 | 9,596,961 km2 | Yuan | 19 | | France | Paris | 67,201,000 | 640,679 km2 | Euro | 20 | | Germany | Berlin | 82,800,000 | 357,168 km2 | Euro | 21 | | Iceland | Reykjavik | 348,580 | 102,775 km2 | Icelandic Krona | 22 | | Netherlands | Amsterdam | 17,200,671 | 41,543 km2 | Euro | 23 | +---------------+-------------+-----------------+-----------------+---------------------+ 24 | ``` 25 | 26 | ## Usage 27 | To create a new table, a `ConsoleTable` object must be initialized. The constructor expects a `std::initializer_list`, which resembles the table headers. 28 | ```cpp 29 | ConsoleTable table{"Country", "Capital", "Population", "Area", "Currency"}; 30 | ``` 31 | Next, the padding size must be set. The padding size defines the spacing between the text in each cell of the table and the border of the cell. Default value is `1`. 32 | The padding size can be set as follows 33 | ```cpp 34 | table.setPadding(2); // Sets a padding of 2 for each cell 35 | ``` 36 | The user can choose between a total of four different table layouts. `0` is the default style. `1` is a single, `2` a double outlined style and `3` an invisible table styles with no lines at all. To change the style call 37 | ```cpp 38 | table.setStyle(0); // Sets the default table style 39 | ``` 40 | It's important to note that some styles might not be displayed correctly if your system doesn't support special characters. 41 | The default style `0` as well as style `3` should work on all systems because only ASCII characters are used for the layout. 42 | 43 | To add new rows of data to the table use the `+=` operator followed by a list of `strings`. 44 | ```cpp 45 | table += {"Germany", "Berlin", "82,800,000", "357,168 km2", "Euro"}; 46 | table += {"France", "Paris", "67,201,000", "640,679 km2 ", "Euro"}; 47 | table += {"South Korea", "Seoul", "51,446,201", "100,210 km2 ", "South Korean Won"}; 48 | table += {"Australia", "Canberra", "24,877,800", "7,692,024 km2", "Australian Dollar"}; 49 | table += {"China", "Beijing", "1,403,500,365", "9,596,961 km2", "Yuan"}; 50 | table += {"Iceland", "Reykjavik", "348,580", "102,775 km2", "Icelandic Krona"}; 51 | table += {"Netherlands", "Amsterdam", "17,200,671", "41,543 km2", "Euro"}; 52 | ``` 53 | To change the update the value of a specific cell in the console table use the `updateRow(int, int, string)` function. 54 | The first parameter is the index of the row, the second one the index of the column that should be updated. The last parameter is the new `string` that should be set. 55 | ```cpp 56 | table.updateRow(3, 1, "NEW ENTRY"); // Update row 3, column 1 57 | ``` 58 | The same thing applies for the headers. If you want to change the text of one of the header fields, simply provide the index of the field you like to update and call 59 | ```cpp 60 | table.updateHeader(3, "NEW CAPTION"); // Update header field at index 3 61 | ``` 62 | To output the table to the console, simply use the insertion operator followed by the `ConsoleTable` object. 63 | ```cpp 64 | std::cout << table; // Display table in the console 65 | ``` 66 | 67 | It is also possible to sort the table based on the first column. To do that, call 68 | ```cpp 69 | table.sort(true); // Sort ascending 70 | table.sort(false); // Sort descending 71 | ``` 72 | To remove rows from the table use the `-=` operator followed by the index of the row that should be removed. 73 | ```cpp 74 | table -= 2; // Removes row at index 2 from the table 75 | ``` 76 | 77 | ## Requirements 78 | This library requires C++11 to compile. 79 | -------------------------------------------------------------------------------- /lib/log_c/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 rxi 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include "log.h" 24 | 25 | #define MAX_CALLBACKS 32 26 | 27 | typedef struct { 28 | log_LogFn fn; 29 | void *udata; 30 | int level; 31 | } Callback; 32 | 33 | static struct { 34 | void *udata; 35 | log_LockFn lock; 36 | int level; 37 | bool quiet; 38 | Callback callbacks[MAX_CALLBACKS]; 39 | } L; 40 | 41 | 42 | static const char *level_strings[] = { 43 | "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" 44 | }; 45 | 46 | #ifdef LOG_USE_COLOR 47 | static const char *level_colors[] = { 48 | "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" 49 | }; 50 | #endif 51 | 52 | 53 | static void stdout_callback(log_Event *ev) { 54 | char buf[16]; 55 | buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; 56 | #ifdef LOG_USE_COLOR 57 | fprintf( 58 | ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", 59 | buf, level_colors[ev->level], level_strings[ev->level], 60 | ev->file, ev->line); 61 | #else 62 | fprintf( 63 | ev->udata, "%s %-5s %s:%d: ", 64 | buf, level_strings[ev->level], ev->file, ev->line); 65 | #endif 66 | vfprintf(ev->udata, ev->fmt, ev->ap); 67 | fprintf(ev->udata, "\r\n"); 68 | fflush(ev->udata); 69 | } 70 | 71 | 72 | static void file_callback(log_Event *ev) { 73 | char buf[64]; 74 | buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; 75 | fprintf( 76 | ev->udata, "%s %-5s %s:%d: ", 77 | buf, level_strings[ev->level], ev->file, ev->line); 78 | vfprintf(ev->udata, ev->fmt, ev->ap); 79 | fprintf(ev->udata, "\n"); 80 | fflush(ev->udata); 81 | } 82 | 83 | 84 | static void lock(void) { 85 | if (L.lock) { L.lock(true, L.udata); } 86 | } 87 | 88 | 89 | static void unlock(void) { 90 | if (L.lock) { L.lock(false, L.udata); } 91 | } 92 | 93 | 94 | const char* log_level_string(int level) { 95 | return level_strings[level]; 96 | } 97 | 98 | 99 | void log_set_lock(log_LockFn fn, void *udata) { 100 | L.lock = fn; 101 | L.udata = udata; 102 | } 103 | 104 | 105 | void log_set_level(int level) { 106 | L.level = level; 107 | } 108 | 109 | 110 | void log_set_quiet(bool enable) { 111 | L.quiet = enable; 112 | } 113 | 114 | 115 | int log_add_callback(log_LogFn fn, void *udata, int level) { 116 | for (int i = 0; i < MAX_CALLBACKS; i++) { 117 | if (!L.callbacks[i].fn) { 118 | L.callbacks[i] = (Callback) { fn, udata, level }; 119 | return 0; 120 | } 121 | } 122 | return -1; 123 | } 124 | 125 | 126 | int log_add_fp(FILE *fp, int level) { 127 | return log_add_callback(file_callback, fp, level); 128 | } 129 | 130 | 131 | static void init_event(log_Event *ev, void *udata) { 132 | if (!ev->time) { 133 | time_t t = time(NULL); 134 | ev->time = localtime(&t); 135 | } 136 | ev->udata = udata; 137 | } 138 | 139 | 140 | void log_log(int level, const char *file, int line, const char *fmt, ...) { 141 | log_Event ev = { 142 | .fmt = fmt, 143 | .file = file, 144 | .line = line, 145 | .level = level, 146 | }; 147 | 148 | lock(); 149 | 150 | if (!L.quiet && level >= L.level) { 151 | init_event(&ev, stderr); 152 | va_start(ev.ap, fmt); 153 | stdout_callback(&ev); 154 | va_end(ev.ap); 155 | } 156 | 157 | for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { 158 | Callback *cb = &L.callbacks[i]; 159 | if (level >= cb->level) { 160 | init_event(&ev, cb->udata); 161 | va_start(ev.ap, fmt); 162 | cb->fn(&ev); 163 | va_end(ev.ap); 164 | } 165 | } 166 | 167 | unlock(); 168 | } 169 | -------------------------------------------------------------------------------- /components/fatfs/src/ffsystem.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Sample Code of OS Dependent Functions for FatFs */ 3 | /* (C)ChaN, 2018 */ 4 | /*------------------------------------------------------------------------*/ 5 | 6 | 7 | #include "ff.h" 8 | 9 | 10 | #if FF_USE_LFN == 3 /* Dynamic memory allocation */ 11 | 12 | /*------------------------------------------------------------------------*/ 13 | /* Allocate a memory block */ 14 | /*------------------------------------------------------------------------*/ 15 | 16 | void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */ 17 | UINT msize /* Number of bytes to allocate */ 18 | ) 19 | { 20 | return malloc(msize); /* Allocate a new memory block with POSIX API */ 21 | } 22 | 23 | 24 | /*------------------------------------------------------------------------*/ 25 | /* Free a memory block */ 26 | /*------------------------------------------------------------------------*/ 27 | 28 | void ff_memfree ( 29 | void* mblock /* Pointer to the memory block to free (nothing to do if null) */ 30 | ) 31 | { 32 | free(mblock); /* Free the memory block with POSIX API */ 33 | } 34 | 35 | #endif 36 | 37 | 38 | 39 | #if FF_FS_REENTRANT /* Mutal exclusion */ 40 | 41 | /*------------------------------------------------------------------------*/ 42 | /* Create a Synchronization Object */ 43 | /*------------------------------------------------------------------------*/ 44 | /* This function is called in f_mount() function to create a new 45 | / synchronization object for the volume, such as semaphore and mutex. 46 | / When a 0 is returned, the f_mount() function fails with FR_INT_ERR. 47 | */ 48 | 49 | //const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */ 50 | 51 | 52 | int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */ 53 | BYTE vol, /* Corresponding volume (logical drive number) */ 54 | FF_SYNC_t* sobj /* Pointer to return the created sync object */ 55 | ) 56 | { 57 | /* Win32 */ 58 | *sobj = CreateMutex(NULL, FALSE, NULL); 59 | return (int)(*sobj != INVALID_HANDLE_VALUE); 60 | 61 | /* uITRON */ 62 | // T_CSEM csem = {TA_TPRI,1,1}; 63 | // *sobj = acre_sem(&csem); 64 | // return (int)(*sobj > 0); 65 | 66 | /* uC/OS-II */ 67 | // OS_ERR err; 68 | // *sobj = OSMutexCreate(0, &err); 69 | // return (int)(err == OS_NO_ERR); 70 | 71 | /* FreeRTOS */ 72 | // *sobj = xSemaphoreCreateMutex(); 73 | // return (int)(*sobj != NULL); 74 | 75 | /* CMSIS-RTOS */ 76 | // *sobj = osMutexCreate(&Mutex[vol]); 77 | // return (int)(*sobj != NULL); 78 | } 79 | 80 | 81 | /*------------------------------------------------------------------------*/ 82 | /* Delete a Synchronization Object */ 83 | /*------------------------------------------------------------------------*/ 84 | /* This function is called in f_mount() function to delete a synchronization 85 | / object that created with ff_cre_syncobj() function. When a 0 is returned, 86 | / the f_mount() function fails with FR_INT_ERR. 87 | */ 88 | 89 | int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */ 90 | FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ 91 | ) 92 | { 93 | /* Win32 */ 94 | return (int)CloseHandle(sobj); 95 | 96 | /* uITRON */ 97 | // return (int)(del_sem(sobj) == E_OK); 98 | 99 | /* uC/OS-II */ 100 | // OS_ERR err; 101 | // OSMutexDel(sobj, OS_DEL_ALWAYS, &err); 102 | // return (int)(err == OS_NO_ERR); 103 | 104 | /* FreeRTOS */ 105 | // vSemaphoreDelete(sobj); 106 | // return 1; 107 | 108 | /* CMSIS-RTOS */ 109 | // return (int)(osMutexDelete(sobj) == osOK); 110 | } 111 | 112 | 113 | /*------------------------------------------------------------------------*/ 114 | /* Request Grant to Access the Volume */ 115 | /*------------------------------------------------------------------------*/ 116 | /* This function is called on entering file functions to lock the volume. 117 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 118 | */ 119 | 120 | int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ 121 | FF_SYNC_t sobj /* Sync object to wait */ 122 | ) 123 | { 124 | /* Win32 */ 125 | return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0); 126 | 127 | /* uITRON */ 128 | // return (int)(wai_sem(sobj) == E_OK); 129 | 130 | /* uC/OS-II */ 131 | // OS_ERR err; 132 | // OSMutexPend(sobj, FF_FS_TIMEOUT, &err)); 133 | // return (int)(err == OS_NO_ERR); 134 | 135 | /* FreeRTOS */ 136 | // return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE); 137 | 138 | /* CMSIS-RTOS */ 139 | // return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK); 140 | } 141 | 142 | 143 | /*------------------------------------------------------------------------*/ 144 | /* Release Grant to Access the Volume */ 145 | /*------------------------------------------------------------------------*/ 146 | /* This function is called on leaving file functions to unlock the volume. 147 | */ 148 | 149 | void ff_rel_grant ( 150 | FF_SYNC_t sobj /* Sync object to be signaled */ 151 | ) 152 | { 153 | /* Win32 */ 154 | ReleaseMutex(sobj); 155 | 156 | /* uITRON */ 157 | // sig_sem(sobj); 158 | 159 | /* uC/OS-II */ 160 | // OSMutexPost(sobj); 161 | 162 | /* FreeRTOS */ 163 | // xSemaphoreGive(sobj); 164 | 165 | /* CMSIS-RTOS */ 166 | // osMutexRelease(sobj); 167 | } 168 | 169 | #endif 170 | -------------------------------------------------------------------------------- /lib/ConsoleTable/ConsoleTable.cpp: -------------------------------------------------------------------------------- 1 | #include "ConsoleTable.h" 2 | 3 | ConsoleTable::ConsoleTable(std::initializer_list headers) : headers{headers} 4 | { 5 | for (const auto &column : headers) 6 | { 7 | widths.push_back(column.length()); 8 | } 9 | } 10 | 11 | void ConsoleTable::setPadding(unsigned int n) 12 | { 13 | padding = n; 14 | } 15 | 16 | void ConsoleTable::setStyle(unsigned int n) 17 | { 18 | switch (n) 19 | { 20 | case 0: 21 | style = BasicStyle; 22 | break; 23 | case 1: 24 | style = LineStyle; 25 | break; 26 | case 2: 27 | style = DoubleLineStyle; 28 | break; 29 | case 3: 30 | style = InvisibleStyle; 31 | break; 32 | default: 33 | style = BasicStyle; 34 | break; 35 | } 36 | } 37 | 38 | bool ConsoleTable::addRow(std::initializer_list row) 39 | { 40 | if (row.size() > widths.size()) 41 | { 42 | #ifdef __cpp_exceptions 43 | throw std::invalid_argument{"Appended row size must be same as header size"}; 44 | #else 45 | printf("Appended row size must be same as header size\r\n"); 46 | #endif 47 | } 48 | 49 | auto r = std::vector{row}; 50 | rows.push_back(r); 51 | for (unsigned int i = 0; i < r.size(); ++i) 52 | { 53 | widths[i] = std::max(r[i].size(), widths[i]); 54 | } 55 | return true; 56 | } 57 | 58 | bool ConsoleTable::removeRow(unsigned int index) 59 | { 60 | if (index > rows.size()) 61 | return false; 62 | 63 | rows.erase(rows.begin() + index); 64 | return true; 65 | } 66 | 67 | ConsoleTable &ConsoleTable::operator+=(std::initializer_list row) 68 | { 69 | if (row.size() > widths.size()) 70 | { 71 | #ifdef __cpp_exceptions 72 | throw std::invalid_argument{"Appended row size must be same as header size"}; 73 | #else 74 | printf("Appended row size must be same as header size\r\n"); 75 | #endif 76 | } 77 | 78 | addRow(row); 79 | return *this; 80 | } 81 | 82 | ConsoleTable &ConsoleTable::operator-=(const uint32_t rowIndex) 83 | { 84 | if (rows.size() < rowIndex) 85 | { 86 | #ifdef __cpp_exceptions 87 | throw std::out_of_range{"Row index out of range."}; 88 | #else 89 | printf("Row index out of range.\r\n"); 90 | #endif 91 | } 92 | 93 | removeRow(rowIndex); 94 | return *this; 95 | } 96 | 97 | std::string ConsoleTable::getLine(RowType rowType) const 98 | { 99 | std::stringstream line; 100 | line << rowType.left; 101 | for (unsigned int i = 0; i < widths.size(); ++i) 102 | { 103 | for (unsigned int j = 0; j < (widths[i] + padding + padding); ++j) 104 | { 105 | line << style.horizontal; 106 | } 107 | line << (i == widths.size() - 1 ? rowType.right : rowType.intersect); 108 | } 109 | return line.str() + "\n"; 110 | } 111 | 112 | std::string ConsoleTable::getHeaders(Headers headers) const 113 | { 114 | std::stringstream line; 115 | line << style.vertical; 116 | for (unsigned int i = 0; i < headers.size(); ++i) 117 | { 118 | std::string text = headers[i]; 119 | line << SPACE_CHARACTER * padding + text + SPACE_CHARACTER * (widths[i] - text.length()) + SPACE_CHARACTER * padding; 120 | line << style.vertical; 121 | } 122 | line << "\n"; 123 | return line.str(); 124 | } 125 | 126 | std::string ConsoleTable::getRows(Rows rows) const 127 | { 128 | std::stringstream line; 129 | for (auto &row : rows) 130 | { 131 | line << style.vertical; 132 | for (unsigned int j = 0; j < row.size(); ++j) 133 | { 134 | std::string text = row[j]; 135 | line << SPACE_CHARACTER * padding + text + SPACE_CHARACTER * (widths[j] - text.length()) + SPACE_CHARACTER * padding; 136 | line << style.vertical; 137 | } 138 | line << "\n"; 139 | } 140 | 141 | return line.str(); 142 | } 143 | 144 | std::ostream &operator<<(std::ostream &out, const ConsoleTable &consoleTable) 145 | { 146 | out << consoleTable.getLine(consoleTable.style.top); 147 | out << consoleTable.getHeaders(consoleTable.headers); 148 | out << consoleTable.getLine(consoleTable.style.middle); 149 | out << consoleTable.getRows(consoleTable.rows); 150 | out << consoleTable.getLine(consoleTable.style.bottom); 151 | return out; 152 | } 153 | 154 | bool ConsoleTable::sort(bool ascending) 155 | { 156 | if (ascending) 157 | std::sort(rows.begin(), rows.end(), std::less>()); 158 | else 159 | std::sort(rows.begin(), rows.end(), std::greater>()); 160 | return true; 161 | } 162 | 163 | void ConsoleTable::updateRow(unsigned int row, unsigned int header, std::string data) 164 | { 165 | if (row > rows.size() - 1) 166 | { 167 | #ifdef __cpp_exceptions 168 | throw std::out_of_range{"Row index out of range."}; 169 | #else 170 | printf("Row index out of range.\r\n"); 171 | #endif 172 | } 173 | if (header > headers.size() - 1) 174 | { 175 | #ifdef __cpp_exceptions 176 | throw std::out_of_range{"Header index out of range."}; 177 | #else 178 | printf("Header index out of range.\r\n"); 179 | #endif 180 | } 181 | 182 | rows[row][header] = data; 183 | } 184 | 185 | void ConsoleTable::updateHeader(unsigned int header, std::string text) 186 | { 187 | if (header > headers.size()) 188 | { 189 | #ifdef __cpp_exceptions 190 | throw std::out_of_range{"Header index out of range."}; 191 | #else 192 | printf("Header index out of range.\r\n"); 193 | #endif 194 | } 195 | 196 | headers[header] = text; 197 | } 198 | 199 | std::string operator*(const std::string &other, int repeats) 200 | { 201 | std::string ret; 202 | ret.reserve(other.size() * repeats); 203 | for (; repeats; --repeats) 204 | ret.append(other); 205 | return ret; 206 | } 207 | -------------------------------------------------------------------------------- /lib/ConsoleTable/ConsoleTable.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSOLETABLE_CONSOLETABLE_H 2 | #define CONSOLETABLE_CONSOLETABLE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | class ConsoleTable { 12 | public: 13 | 14 | typedef std::vector Headers; 15 | typedef std::vector> Rows; 16 | typedef std::vector Widths; 17 | 18 | /// Initialize a new ConsoleTable 19 | /// \param headers Stringlist of the tables headers 20 | ConsoleTable(std::initializer_list headers); 21 | 22 | 23 | /// Sets the distance from the text to the cell border 24 | /// \param n Spaces between the text and the cell border 25 | void setPadding(unsigned int n); 26 | 27 | 28 | /// Sets the style of the table, default is 0 29 | /// n = 0 : Basic table style 30 | /// n = 1 : Single lined table style 31 | /// n = 2 : Double lined table style 32 | /// n = 3 : Invisivle table lines style 33 | /// \param n The table style number 34 | void setStyle(unsigned int n); 35 | 36 | 37 | /// Sorts the table rows based on the first column 38 | /// \param ascending Should table be sorted ascending or descending 39 | /// \return True if sorting was successful, otherwise false 40 | bool sort(bool ascending); 41 | 42 | 43 | /// Adds a new row to the table 44 | /// \param row A list of strings to add as row 45 | /// \return True if the value was added successfully, otherwise false 46 | bool addRow(std::initializer_list row); 47 | 48 | 49 | /// Removes a row from the table by the row index 50 | /// \param index The index of the row that should be removed 51 | /// \return True if the row was removed successfully, otherwise false 52 | bool removeRow(unsigned int index); 53 | 54 | 55 | /// Update an existing table cell with new data 56 | /// \param row The index of the row that needs to be updated 57 | /// \param header The index of the column that needs to be updated 58 | /// \param data The new data that should be assigned to teh cell 59 | void updateRow(unsigned int row, unsigned int header, std::string data); 60 | 61 | 62 | /// Update a header with new text 63 | /// \param header Index of the header that should be updated 64 | /// \param text The new teext of the new header 65 | void updateHeader(unsigned int header, std::string text); 66 | 67 | 68 | /// Operator of the addRow() function 69 | /// \param row A list of strings to add as row 70 | /// \return this 71 | ConsoleTable &operator+=(std::initializer_list row); 72 | 73 | 74 | /// Operator of the removeRow() function 75 | /// \param rowIndex The index of the row that should be removed 76 | /// \return this 77 | ConsoleTable &operator-=(uint32_t rowIndex); 78 | 79 | 80 | private: 81 | 82 | /// Holds all header strings of the table 83 | Headers headers; 84 | 85 | 86 | /// Holds all rows of the table 87 | Rows rows; 88 | 89 | 90 | /// Holds the size of widest string of each column of the table 91 | Widths widths; 92 | 93 | /// Defines row type 94 | struct RowType { 95 | std::string left; 96 | std::string intersect; 97 | std::string right; 98 | }; 99 | 100 | /// Defines table style rows (top, middle, bottom etc) 101 | struct TableStyle { 102 | std::string horizontal; 103 | std::string vertical; 104 | RowType top; 105 | RowType middle; 106 | RowType bottom; 107 | }; 108 | 109 | 110 | /// Basic style - works on all systems, used as default style 111 | TableStyle BasicStyle = {"-", "|", {"+", "+", "+"}, {"+", "+", "+"}, {"+", "+", "+"}}; 112 | 113 | 114 | /// Single lined style - requires speecial character support 115 | TableStyle LineStyle = {"━", "┃", {"┏", "┳", "┓"}, {"┣", "╋", "┫"}, {"┗", "┻", "┛"}}; 116 | 117 | 118 | /// Single double style - requires speecial character support 119 | TableStyle DoubleLineStyle = {"═", "║", {"╔", "╦", "╗"}, {"╠", "╬", "╣"}, {"╚", "╩", "╝"}}; 120 | 121 | 122 | /// No visible table outlines - works on all systems 123 | TableStyle InvisibleStyle = {" ", " ", {" ", " ", " "}, {" ", " ", " "}, {" ", " ", " "}}; 124 | 125 | 126 | /// Current table style 127 | TableStyle style = BasicStyle; 128 | 129 | 130 | /// Space character constant 131 | const std::string SPACE_CHARACTER = " "; 132 | 133 | 134 | /// The distance between the cell text and the cell border 135 | unsigned int padding = 1; 136 | 137 | 138 | /// Returns a formatted horizontal separation line for the table 139 | /// \param rowType The type of the row (top, middle, bottom) 140 | /// \return The formatted row string 141 | std::string getLine(RowType rowType) const; 142 | 143 | 144 | /// Returns a formatted header string 145 | /// \param headers The Headers-object that holds the header strings 146 | /// \return The formatted header string 147 | std::string getHeaders(Headers headers) const; 148 | 149 | 150 | /// Returns a formmatted row string 151 | /// \param rows The Rows-object that holds all rows of the table 152 | /// \return A formatted string of all rows in the table 153 | std::string getRows(Rows rows) const; 154 | 155 | 156 | /// Writes the entire table with all its contents in the output stream 157 | /// This can be used to display the table using the std::cout function 158 | /// \param out The output stream the table should be written to 159 | /// \param consoleTable The ConsoleTable-object 160 | /// \return Output stream with the formatted table string 161 | friend std::ostream &operator<<(std::ostream &out, const ConsoleTable &consoleTable); 162 | 163 | }; 164 | 165 | 166 | /// Repeats a given string n times 167 | /// \param other The string to repeat 168 | /// \param repeats The amount the string should be repeated 169 | /// \return The repeated string 170 | std::string operator*(const std::string &other, int repeats); 171 | 172 | 173 | #endif //CONSOLETABLE_CONSOLETABLE_H -------------------------------------------------------------------------------- /components/fatfs/src/diskio.c: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*/ 2 | /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */ 3 | /*-----------------------------------------------------------------------*/ 4 | /* If a working storage control module is available, it should be */ 5 | /* attached to the FatFs via a glue function rather than modifying it. */ 6 | /* This is an example of glue functions to attach various exsisting */ 7 | /* storage control modules to the FatFs module with a defined API. */ 8 | /*-----------------------------------------------------------------------*/ 9 | 10 | #include "ff.h" /* Obtains integer types */ 11 | #include "diskio.h" /* Declarations of disk functions */ 12 | 13 | /* Definitions of physical drive number for each drive */ 14 | #define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */ 15 | #define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */ 16 | #define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */ 17 | 18 | 19 | /*-----------------------------------------------------------------------*/ 20 | /* Get Drive Status */ 21 | /*-----------------------------------------------------------------------*/ 22 | 23 | DSTATUS disk_status ( 24 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 25 | ) 26 | { 27 | DSTATUS stat; 28 | int result; 29 | 30 | switch (pdrv) { 31 | case DEV_RAM : 32 | result = RAM_disk_status(); 33 | 34 | // translate the reslut code here 35 | 36 | return stat; 37 | 38 | case DEV_MMC : 39 | result = MMC_disk_status(); 40 | 41 | // translate the reslut code here 42 | 43 | return stat; 44 | 45 | case DEV_USB : 46 | result = USB_disk_status(); 47 | 48 | // translate the reslut code here 49 | 50 | return stat; 51 | } 52 | return STA_NOINIT; 53 | } 54 | 55 | 56 | 57 | /*-----------------------------------------------------------------------*/ 58 | /* Inidialize a Drive */ 59 | /*-----------------------------------------------------------------------*/ 60 | 61 | DSTATUS disk_initialize ( 62 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 63 | ) 64 | { 65 | DSTATUS stat; 66 | int result; 67 | 68 | switch (pdrv) { 69 | case DEV_RAM : 70 | result = RAM_disk_initialize(); 71 | 72 | // translate the reslut code here 73 | 74 | return stat; 75 | 76 | case DEV_MMC : 77 | result = MMC_disk_initialize(); 78 | 79 | // translate the reslut code here 80 | 81 | return stat; 82 | 83 | case DEV_USB : 84 | result = USB_disk_initialize(); 85 | 86 | // translate the reslut code here 87 | 88 | return stat; 89 | } 90 | return STA_NOINIT; 91 | } 92 | 93 | 94 | 95 | /*-----------------------------------------------------------------------*/ 96 | /* Read Sector(s) */ 97 | /*-----------------------------------------------------------------------*/ 98 | 99 | DRESULT disk_read ( 100 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 101 | BYTE *buff, /* Data buffer to store read data */ 102 | DWORD sector, /* Start sector in LBA */ 103 | UINT count /* Number of sectors to read */ 104 | ) 105 | { 106 | DRESULT res; 107 | int result; 108 | 109 | switch (pdrv) { 110 | case DEV_RAM : 111 | // translate the arguments here 112 | 113 | result = RAM_disk_read(buff, sector, count); 114 | 115 | // translate the reslut code here 116 | 117 | return res; 118 | 119 | case DEV_MMC : 120 | // translate the arguments here 121 | 122 | result = MMC_disk_read(buff, sector, count); 123 | 124 | // translate the reslut code here 125 | 126 | return res; 127 | 128 | case DEV_USB : 129 | // translate the arguments here 130 | 131 | result = USB_disk_read(buff, sector, count); 132 | 133 | // translate the reslut code here 134 | 135 | return res; 136 | } 137 | 138 | return RES_PARERR; 139 | } 140 | 141 | 142 | 143 | /*-----------------------------------------------------------------------*/ 144 | /* Write Sector(s) */ 145 | /*-----------------------------------------------------------------------*/ 146 | 147 | #if FF_FS_READONLY == 0 148 | 149 | DRESULT disk_write ( 150 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 151 | const BYTE *buff, /* Data to be written */ 152 | DWORD sector, /* Start sector in LBA */ 153 | UINT count /* Number of sectors to write */ 154 | ) 155 | { 156 | DRESULT res; 157 | int result; 158 | 159 | switch (pdrv) { 160 | case DEV_RAM : 161 | // translate the arguments here 162 | 163 | result = RAM_disk_write(buff, sector, count); 164 | 165 | // translate the reslut code here 166 | 167 | return res; 168 | 169 | case DEV_MMC : 170 | // translate the arguments here 171 | 172 | result = MMC_disk_write(buff, sector, count); 173 | 174 | // translate the reslut code here 175 | 176 | return res; 177 | 178 | case DEV_USB : 179 | // translate the arguments here 180 | 181 | result = USB_disk_write(buff, sector, count); 182 | 183 | // translate the reslut code here 184 | 185 | return res; 186 | } 187 | 188 | return RES_PARERR; 189 | } 190 | 191 | #endif 192 | 193 | 194 | /*-----------------------------------------------------------------------*/ 195 | /* Miscellaneous Functions */ 196 | /*-----------------------------------------------------------------------*/ 197 | 198 | DRESULT disk_ioctl ( 199 | BYTE pdrv, /* Physical drive nmuber (0..) */ 200 | BYTE cmd, /* Control code */ 201 | void *buff /* Buffer to send/receive control data */ 202 | ) 203 | { 204 | DRESULT res; 205 | int result; 206 | 207 | switch (pdrv) { 208 | case DEV_RAM : 209 | 210 | // Process of the command for the RAM drive 211 | 212 | return res; 213 | 214 | case DEV_MMC : 215 | 216 | // Process of the command for the MMC/SD card 217 | 218 | return res; 219 | 220 | case DEV_USB : 221 | 222 | // Process of the command the USB drive 223 | 224 | return res; 225 | } 226 | 227 | return RES_PARERR; 228 | } 229 | -------------------------------------------------------------------------------- /src/sd_mount.c: -------------------------------------------------------------------------------- 1 | #include "sd_mount.h" 2 | 3 | #ifdef ESP32 4 | 5 | #include "esp_log.h" 6 | #include 7 | #include "driver/sdspi_host.h" 8 | #include "driver/sdmmc_host.h" 9 | #include "sdmmc_cmd.h" 10 | 11 | #include 12 | 13 | #include 14 | 15 | #define PIN_NUM_MISO 2 16 | #define PIN_NUM_MOSI 15 17 | #define PIN_NUM_CLK 14 18 | #define PIN_NUM_CS 13 19 | #define SPI_DMA_CHAN 1 20 | 21 | #define TAG "SD" 22 | 23 | void mount_sd_spi() 24 | { 25 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 26 | .format_if_mount_failed = false, 27 | .max_files = 7, 28 | .allocation_unit_size = 256 * 1024}; 29 | 30 | ESP_LOGI(TAG, "Using SPI peripheral"); 31 | sdmmc_host_t host = SDSPI_HOST_DEFAULT(); 32 | // host.max_freq_khz = SDMMC_FREQ_52M; 33 | spi_bus_config_t bus_cfg = { 34 | .mosi_io_num = PIN_NUM_MOSI, 35 | .miso_io_num = PIN_NUM_MISO, 36 | .sclk_io_num = PIN_NUM_CLK, 37 | .quadwp_io_num = -1, 38 | .quadhd_io_num = -1, 39 | .max_transfer_sz = 4000, 40 | }; 41 | esp_err_t ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CHAN); 42 | if (ret != ESP_OK) 43 | { 44 | ESP_LOGE(TAG, "Failed to initialize bus."); 45 | return; 46 | } 47 | 48 | // This initializes the slot without card detect (CD) and write protect (WP) signals. 49 | // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. 50 | sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); 51 | slot_config.gpio_cs = PIN_NUM_CS; 52 | slot_config.host_id = host.slot; 53 | 54 | sdmmc_card_t *card; 55 | while (true) 56 | { 57 | esp_err_t ret = esp_vfs_fat_sdspi_mount("/sdcard", &host, &slot_config, &mount_config, &card); 58 | 59 | if (ret != ESP_OK) 60 | { 61 | if (ret == ESP_FAIL) 62 | { 63 | ESP_LOGE(TAG, "Failed to mount filesystem. " 64 | "If you want the card to be formatted, set format_if_mount_failed = true."); 65 | } 66 | else 67 | { 68 | ESP_LOGE(TAG, "Failed to initialize the card (%s). " 69 | "Make sure SD card lines have pull-up resistors in place.", 70 | esp_err_to_name(ret)); 71 | } 72 | vTaskDelay(pdMS_TO_TICKS(1000)); 73 | } 74 | else 75 | { 76 | break; 77 | } 78 | } 79 | 80 | sdmmc_card_print_info(stdout, card); 81 | vd_chdir("/sdcard/emu/"); 82 | } 83 | void mount_sd_sdmmc() 84 | { 85 | 86 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 87 | .format_if_mount_failed = false, 88 | .max_files = 7, 89 | .allocation_unit_size = 256 * 1024}; 90 | 91 | sdmmc_card_t *card; 92 | 93 | ESP_LOGI(TAG, "Using SDMMC peripheral"); 94 | 95 | const gpio_config_t mtck_d3 = { 96 | .pin_bit_mask = (1ULL << GPIO_NUM_13), 97 | .intr_type = GPIO_INTR_DISABLE, 98 | .mode = GPIO_MODE_INPUT, 99 | .pull_up_en = true, 100 | .pull_down_en = false}; 101 | ESP_ERROR_CHECK(gpio_config(&mtck_d3)); 102 | 103 | sdmmc_host_t host = SDMMC_HOST_DEFAULT(); 104 | host.max_freq_khz = SDMMC_FREQ_52M; 105 | 106 | // This initializes the slot without card detect (CD) and write protect (WP) signals. 107 | // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. 108 | sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); 109 | 110 | // Set bus width to use: 111 | #ifdef CONFIG_SDMMC_BUS_WIDTH_4 112 | slot_config.width = 4; 113 | #else 114 | slot_config.width = 1; 115 | #endif 116 | 117 | // On chips where the GPIOs used for SD card can be configured, set them in 118 | // the slot_config structure: 119 | #ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX 120 | slot_config.clk = CONFIG_EXAMPLE_PIN_CLK; 121 | slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD; 122 | slot_config.d0 = CONFIG_EXAMPLE_PIN_D0; 123 | #ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 124 | slot_config.d1 = CONFIG_EXAMPLE_PIN_D1; 125 | slot_config.d2 = CONFIG_EXAMPLE_PIN_D2; 126 | slot_config.d3 = CONFIG_EXAMPLE_PIN_D3; 127 | #endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 128 | #endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX 129 | 130 | // Enable internal pullups on enabled pins. The internal pullups 131 | // are insufficient however, please make sure 10k external pullups are 132 | // connected on the bus. This is for debug / example purpose only. 133 | slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; 134 | 135 | ESP_LOGI(TAG, "Mounting filesystem"); 136 | while (true) 137 | { 138 | esp_err_t ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card); 139 | 140 | if (ret != ESP_OK) 141 | { 142 | if (ret == ESP_FAIL) 143 | { 144 | ESP_LOGE(TAG, "Failed to mount filesystem. " 145 | "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option."); 146 | } 147 | else 148 | { 149 | ESP_LOGE(TAG, "Failed to initialize the card (%s). " 150 | "Make sure SD card lines have pull-up resistors in place.", 151 | esp_err_to_name(ret)); 152 | } 153 | vTaskDelay(pdMS_TO_TICKS(1000)); 154 | }else{ 155 | break; 156 | } 157 | } 158 | ESP_LOGI(TAG, "Filesystem mounted"); 159 | 160 | // Card has been initialized, print its properties 161 | sdmmc_card_print_info(stdout, card); 162 | printf("CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n", 163 | card->csd.csd_ver, 164 | card->csd.sector_size, card->csd.capacity, card->csd.read_block_len); 165 | printf("SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width); 166 | 167 | vd_chdir("/sdcard/emu/"); 168 | } 169 | 170 | void unmound_sd() 171 | { 172 | esp_vfs_fat_sdmmc_unmount(); 173 | ESP_LOGI(TAG, "Card unmounted"); 174 | } 175 | 176 | #endif -------------------------------------------------------------------------------- /components/fatfs/test/test_fatfs_spiflash.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "unity.h" 22 | #include "test_utils.h" 23 | #include "esp_log.h" 24 | #include "esp_system.h" 25 | #include "esp_vfs.h" 26 | #include "esp_vfs_fat.h" 27 | #include "freertos/FreeRTOS.h" 28 | #include "freertos/task.h" 29 | #include "test_fatfs_common.h" 30 | #include "wear_levelling.h" 31 | #include "esp_partition.h" 32 | 33 | 34 | static wl_handle_t s_test_wl_handle; 35 | static void test_setup(void) 36 | { 37 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 38 | .format_if_mount_failed = true, 39 | .max_files = 5 40 | }; 41 | 42 | TEST_ESP_OK(esp_vfs_fat_spiflash_mount("/spiflash", NULL, &mount_config, &s_test_wl_handle)); 43 | } 44 | 45 | static void test_teardown(void) 46 | { 47 | TEST_ESP_OK(esp_vfs_fat_spiflash_unmount("/spiflash", s_test_wl_handle)); 48 | } 49 | 50 | TEST_CASE("(WL) can format partition", "[fatfs][wear_levelling]") 51 | { 52 | const esp_partition_t* part = get_test_data_partition(); 53 | esp_partition_erase_range(part, 0, part->size); 54 | test_setup(); 55 | test_teardown(); 56 | } 57 | 58 | TEST_CASE("(WL) can create and write file", "[fatfs][wear_levelling]") 59 | { 60 | test_setup(); 61 | test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str); 62 | test_teardown(); 63 | } 64 | 65 | TEST_CASE("(WL) can read file", "[fatfs][wear_levelling]") 66 | { 67 | test_setup(); 68 | test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str); 69 | test_fatfs_read_file("/spiflash/hello.txt"); 70 | test_teardown(); 71 | } 72 | 73 | TEST_CASE("(WL) can read file with pread", "[fatfs][wear_levelling]") 74 | { 75 | test_setup(); 76 | test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str); 77 | test_fatfs_pread_file("/spiflash/hello.txt"); 78 | test_teardown(); 79 | } 80 | 81 | TEST_CASE("(WL) pwrite() works well", "[fatfs][wear_levelling]") 82 | { 83 | test_setup(); 84 | test_fatfs_pwrite_file("/spiflash/hello.txt"); 85 | test_teardown(); 86 | } 87 | 88 | TEST_CASE("(WL) can open maximum number of files", "[fatfs][wear_levelling]") 89 | { 90 | size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */ 91 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 92 | .format_if_mount_failed = true, 93 | .max_files = max_files 94 | }; 95 | TEST_ESP_OK(esp_vfs_fat_spiflash_mount("/spiflash", NULL, &mount_config, &s_test_wl_handle)); 96 | test_fatfs_open_max_files("/spiflash/f", max_files); 97 | TEST_ESP_OK(esp_vfs_fat_spiflash_unmount("/spiflash", s_test_wl_handle)); 98 | } 99 | 100 | TEST_CASE("(WL) overwrite and append file", "[fatfs][wear_levelling]") 101 | { 102 | test_setup(); 103 | test_fatfs_overwrite_append("/spiflash/hello.txt"); 104 | test_teardown(); 105 | } 106 | 107 | TEST_CASE("(WL) can lseek", "[fatfs][wear_levelling]") 108 | { 109 | test_setup(); 110 | test_fatfs_lseek("/spiflash/seek.txt"); 111 | test_teardown(); 112 | } 113 | 114 | TEST_CASE("(WL) can truncate", "[fatfs][wear_levelling]") 115 | { 116 | test_setup(); 117 | test_fatfs_truncate_file("/spiflash/truncate.txt"); 118 | test_teardown(); 119 | } 120 | 121 | TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]") 122 | { 123 | test_setup(); 124 | test_fatfs_stat("/spiflash/stat.txt", "/spiflash"); 125 | test_teardown(); 126 | } 127 | 128 | TEST_CASE("(WL) utime sets modification time", "[fatfs][wear_levelling]") 129 | { 130 | test_setup(); 131 | test_fatfs_utime("/spiflash/utime.txt", "/spiflash"); 132 | test_teardown(); 133 | } 134 | 135 | TEST_CASE("(WL) unlink removes a file", "[fatfs][wear_levelling]") 136 | { 137 | test_setup(); 138 | test_fatfs_unlink("/spiflash/unlink.txt"); 139 | test_teardown(); 140 | } 141 | 142 | TEST_CASE("(WL) link copies a file, rename moves a file", "[fatfs][wear_levelling]") 143 | { 144 | test_setup(); 145 | test_fatfs_link_rename("/spiflash/link"); 146 | test_teardown(); 147 | } 148 | 149 | TEST_CASE("(WL) can create and remove directories", "[fatfs][wear_levelling]") 150 | { 151 | test_setup(); 152 | test_fatfs_mkdir_rmdir("/spiflash/dir"); 153 | test_teardown(); 154 | } 155 | 156 | TEST_CASE("(WL) can opendir root directory of FS", "[fatfs][wear_levelling]") 157 | { 158 | test_setup(); 159 | test_fatfs_can_opendir("/spiflash"); 160 | test_teardown(); 161 | } 162 | 163 | TEST_CASE("(WL) opendir, readdir, rewinddir, seekdir work as expected", "[fatfs][wear_levelling]") 164 | { 165 | test_setup(); 166 | test_fatfs_opendir_readdir_rewinddir("/spiflash/dir"); 167 | test_teardown(); 168 | } 169 | 170 | TEST_CASE("(WL) multiple tasks can use same volume", "[fatfs][wear_levelling]") 171 | { 172 | test_setup(); 173 | test_fatfs_concurrent("/spiflash/f"); 174 | test_teardown(); 175 | } 176 | 177 | TEST_CASE("(WL) write/read speed test", "[fatfs][wear_levelling][timeout=60]") 178 | { 179 | /* Erase partition before running the test to get consistent results */ 180 | const esp_partition_t* part = get_test_data_partition(); 181 | esp_partition_erase_range(part, 0, part->size); 182 | 183 | test_setup(); 184 | 185 | const size_t buf_size = 16 * 1024; 186 | uint32_t* buf = (uint32_t*) calloc(1, buf_size); 187 | esp_fill_random(buf, buf_size); 188 | const size_t file_size = 256 * 1024; 189 | const char* file = "/spiflash/256k.bin"; 190 | 191 | test_fatfs_rw_speed(file, buf, 4 * 1024, file_size, true); 192 | test_fatfs_rw_speed(file, buf, 8 * 1024, file_size, true); 193 | test_fatfs_rw_speed(file, buf, 16 * 1024, file_size, true); 194 | 195 | test_fatfs_rw_speed(file, buf, 4 * 1024, file_size, false); 196 | test_fatfs_rw_speed(file, buf, 8 * 1024, file_size, false); 197 | test_fatfs_rw_speed(file, buf, 16 * 1024, file_size, false); 198 | 199 | unlink(file); 200 | 201 | free(buf); 202 | test_teardown(); 203 | } 204 | 205 | /* 206 | * In FatFs menuconfig, set CONFIG_FATFS_API_ENCODING to UTF-8 and set the 207 | * Codepage to CP936 (Simplified Chinese) in order to run the following tests. 208 | * Ensure that the text editor is UTF-8 compatible when compiling these tests. 209 | */ 210 | #if defined(CONFIG_FATFS_API_ENCODING_UTF_8) && (CONFIG_FATFS_CODEPAGE == 936) 211 | TEST_CASE("(WL) can read file with UTF-8 encoded strings", "[fatfs][wear_levelling]") 212 | { 213 | test_setup(); 214 | test_fatfs_create_file_with_text("/spiflash/测试文件.txt", fatfs_test_hello_str_utf); 215 | test_fatfs_read_file_utf_8("/spiflash/测试文件.txt"); 216 | test_teardown(); 217 | } 218 | 219 | TEST_CASE("(WL) opendir, readdir, rewinddir, seekdir work as expected using UTF-8 encoded strings", "[fatfs][wear_levelling]") 220 | { 221 | test_setup(); 222 | test_fatfs_opendir_readdir_rewinddir_utf_8("/spiflash/目录"); 223 | test_teardown(); 224 | } 225 | #endif 226 | 227 | #ifdef CONFIG_SPIRAM 228 | TEST_CASE("FATFS prefers SPI RAM for allocations", "[fatfs]") 229 | { 230 | test_setup(); 231 | DIR* dir = opendir("/spiflash"); 232 | TEST_ASSERT_NOT_NULL(dir); 233 | TEST_ASSERT(esp_ptr_external_ram(dir)); 234 | closedir(dir); 235 | test_teardown(); 236 | } 237 | #endif // CONFIG_SPIRAM 238 | -------------------------------------------------------------------------------- /components/fatfs/vfs/vfs_fat_spiflash.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include "esp_log.h" 18 | #include "esp_vfs.h" 19 | #include "esp_vfs_fat.h" 20 | #include "vfs_fat_internal.h" 21 | #include "diskio_impl.h" 22 | 23 | #include "diskio_rawflash.h" 24 | 25 | #include "wear_levelling.h" 26 | #include "diskio_wl.h" 27 | 28 | static const char *TAG = "vfs_fat_spiflash"; 29 | esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path, 30 | const char* partition_label, 31 | const esp_vfs_fat_mount_config_t* mount_config, 32 | wl_handle_t* wl_handle) 33 | { 34 | esp_err_t result = ESP_OK; 35 | const size_t workbuf_size = 4096; 36 | void *workbuf = NULL; 37 | 38 | esp_partition_subtype_t subtype = partition_label ? 39 | ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_FAT; 40 | const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, 41 | subtype, partition_label); 42 | if (data_partition == NULL) { 43 | ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label); 44 | return ESP_ERR_NOT_FOUND; 45 | } 46 | 47 | result = wl_mount(data_partition, wl_handle); 48 | if (result != ESP_OK) { 49 | ESP_LOGE(TAG, "failed to mount wear levelling layer. result = %i", result); 50 | return result; 51 | } 52 | // connect driver to FATFS 53 | BYTE pdrv = 0xFF; 54 | if (ff_diskio_get_drive(&pdrv) != ESP_OK) { 55 | ESP_LOGD(TAG, "the maximum count of volumes is already mounted"); 56 | return ESP_ERR_NO_MEM; 57 | } 58 | ESP_LOGD(TAG, "using pdrv=%i", pdrv); 59 | char drv[3] = {(char)('0' + pdrv), ':', 0}; 60 | 61 | result = ff_diskio_register_wl_partition(pdrv, *wl_handle); 62 | if (result != ESP_OK) { 63 | ESP_LOGE(TAG, "ff_diskio_register_wl_partition failed pdrv=%i, error - 0x(%x)", pdrv, result); 64 | goto fail; 65 | } 66 | FATFS *fs; 67 | result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs); 68 | if (result == ESP_ERR_INVALID_STATE) { 69 | // it's okay, already registered with VFS 70 | } else if (result != ESP_OK) { 71 | ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result); 72 | goto fail; 73 | } 74 | 75 | // Try to mount partition 76 | FRESULT fresult = f_mount(fs, drv, 1); 77 | if (fresult != FR_OK) { 78 | ESP_LOGW(TAG, "f_mount failed (%d)", fresult); 79 | if (!((fresult == FR_NO_FILESYSTEM || fresult == FR_INT_ERR) 80 | && mount_config->format_if_mount_failed)) { 81 | result = ESP_FAIL; 82 | goto fail; 83 | } 84 | workbuf = ff_memalloc(workbuf_size); 85 | if (workbuf == NULL) { 86 | result = ESP_ERR_NO_MEM; 87 | goto fail; 88 | } 89 | size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size( 90 | CONFIG_WL_SECTOR_SIZE, 91 | mount_config->allocation_unit_size); 92 | ESP_LOGI(TAG, "Formatting FATFS partition, allocation unit size=%d", alloc_unit_size); 93 | fresult = f_mkfs(drv, FM_ANY | FM_SFD, alloc_unit_size, workbuf, workbuf_size); 94 | if (fresult != FR_OK) { 95 | result = ESP_FAIL; 96 | ESP_LOGE(TAG, "f_mkfs failed (%d)", fresult); 97 | goto fail; 98 | } 99 | free(workbuf); 100 | workbuf = NULL; 101 | ESP_LOGI(TAG, "Mounting again"); 102 | fresult = f_mount(fs, drv, 0); 103 | if (fresult != FR_OK) { 104 | result = ESP_FAIL; 105 | ESP_LOGE(TAG, "f_mount failed after formatting (%d)", fresult); 106 | goto fail; 107 | } 108 | } 109 | return ESP_OK; 110 | 111 | fail: 112 | free(workbuf); 113 | esp_vfs_fat_unregister_path(base_path); 114 | ff_diskio_unregister(pdrv); 115 | return result; 116 | } 117 | 118 | esp_err_t esp_vfs_fat_spiflash_unmount(const char *base_path, wl_handle_t wl_handle) 119 | { 120 | BYTE pdrv = ff_diskio_get_pdrv_wl(wl_handle); 121 | if (pdrv == 0xff) { 122 | return ESP_ERR_INVALID_STATE; 123 | } 124 | char drv[3] = {(char)('0' + pdrv), ':', 0}; 125 | 126 | f_mount(0, drv, 0); 127 | ff_diskio_unregister(pdrv); 128 | ff_diskio_clear_pdrv_wl(wl_handle); 129 | // release partition driver 130 | esp_err_t err_drv = wl_unmount(wl_handle); 131 | esp_err_t err = esp_vfs_fat_unregister_path(base_path); 132 | if (err == ESP_OK) err = err_drv; 133 | return err; 134 | } 135 | 136 | esp_err_t esp_vfs_fat_rawflash_mount(const char* base_path, 137 | const char* partition_label, 138 | const esp_vfs_fat_mount_config_t* mount_config) 139 | { 140 | esp_err_t result = ESP_OK; 141 | 142 | const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, 143 | ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label); 144 | if (data_partition == NULL) { 145 | ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label); 146 | return ESP_ERR_NOT_FOUND; 147 | } 148 | 149 | // connect driver to FATFS 150 | BYTE pdrv = 0xFF; 151 | if (ff_diskio_get_drive(&pdrv) != ESP_OK) { 152 | ESP_LOGD(TAG, "the maximum count of volumes is already mounted"); 153 | return ESP_ERR_NO_MEM; 154 | } 155 | ESP_LOGD(TAG, "using pdrv=%i", pdrv); 156 | char drv[3] = {(char)('0' + pdrv), ':', 0}; 157 | 158 | result = ff_diskio_register_raw_partition(pdrv, data_partition); 159 | if (result != ESP_OK) { 160 | ESP_LOGE(TAG, "ff_diskio_register_raw_partition failed pdrv=%i, error - 0x(%x)", pdrv, result); 161 | goto fail; 162 | } 163 | 164 | FATFS *fs; 165 | result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs); 166 | if (result == ESP_ERR_INVALID_STATE) { 167 | // it's okay, already registered with VFS 168 | } else if (result != ESP_OK) { 169 | ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result); 170 | goto fail; 171 | } 172 | 173 | // Try to mount partition 174 | FRESULT fresult = f_mount(fs, drv, 1); 175 | if (fresult != FR_OK) { 176 | ESP_LOGW(TAG, "f_mount failed (%d)", fresult); 177 | result = ESP_FAIL; 178 | goto fail; 179 | } 180 | return ESP_OK; 181 | 182 | fail: 183 | esp_vfs_fat_unregister_path(base_path); 184 | ff_diskio_unregister(pdrv); 185 | return result; 186 | } 187 | 188 | 189 | esp_err_t esp_vfs_fat_rawflash_unmount(const char *base_path, const char* partition_label) 190 | { 191 | const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, 192 | ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label); 193 | 194 | if (data_partition == NULL) { 195 | ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label); 196 | return ESP_ERR_NOT_FOUND; 197 | } 198 | 199 | BYTE pdrv = ff_diskio_get_pdrv_raw(data_partition); 200 | if (pdrv == 0xff) { 201 | return ESP_ERR_INVALID_STATE; 202 | } 203 | char drv[3] = {(char)('0' + pdrv), ':', 0}; 204 | 205 | f_mount(0, drv, 0); 206 | ff_diskio_unregister(pdrv); 207 | esp_err_t err = esp_vfs_fat_unregister_path(base_path); 208 | return err; 209 | } 210 | -------------------------------------------------------------------------------- /components/fatfs/Kconfig: -------------------------------------------------------------------------------- 1 | menu "FAT Filesystem support" 2 | 3 | choice FATFS_CHOOSE_CODEPAGE 4 | prompt "OEM Code Page" 5 | default FATFS_CODEPAGE_437 6 | help 7 | OEM code page used for file name encodings. 8 | 9 | If "Dynamic" is selected, code page can be chosen at runtime using 10 | f_setcp function. Note that choosing this option will increase 11 | application size by ~480kB. 12 | 13 | config FATFS_CODEPAGE_DYNAMIC 14 | bool "Dynamic (all code pages supported)" 15 | config FATFS_CODEPAGE_437 16 | bool "US (CP437)" 17 | config FATFS_CODEPAGE_720 18 | bool "Arabic (CP720)" 19 | config FATFS_CODEPAGE_737 20 | bool "Greek (CP737)" 21 | config FATFS_CODEPAGE_771 22 | bool "KBL (CP771)" 23 | config FATFS_CODEPAGE_775 24 | bool "Baltic (CP775)" 25 | config FATFS_CODEPAGE_850 26 | bool "Latin 1 (CP850)" 27 | config FATFS_CODEPAGE_852 28 | bool "Latin 2 (CP852)" 29 | config FATFS_CODEPAGE_855 30 | bool "Cyrillic (CP855)" 31 | config FATFS_CODEPAGE_857 32 | bool "Turkish (CP857)" 33 | config FATFS_CODEPAGE_860 34 | bool "Portugese (CP860)" 35 | config FATFS_CODEPAGE_861 36 | bool "Icelandic (CP861)" 37 | config FATFS_CODEPAGE_862 38 | bool "Hebrew (CP862)" 39 | config FATFS_CODEPAGE_863 40 | bool "Canadian French (CP863)" 41 | config FATFS_CODEPAGE_864 42 | bool "Arabic (CP864)" 43 | config FATFS_CODEPAGE_865 44 | bool "Nordic (CP865)" 45 | config FATFS_CODEPAGE_866 46 | bool "Russian (CP866)" 47 | config FATFS_CODEPAGE_869 48 | bool "Greek 2 (CP869)" 49 | config FATFS_CODEPAGE_932 50 | bool "Japanese (DBCS) (CP932)" 51 | config FATFS_CODEPAGE_936 52 | bool "Simplified Chinese (DBCS) (CP936)" 53 | config FATFS_CODEPAGE_949 54 | bool "Korean (DBCS) (CP949)" 55 | config FATFS_CODEPAGE_950 56 | bool "Traditional Chinese (DBCS) (CP950)" 57 | 58 | endchoice 59 | 60 | config FATFS_CODEPAGE 61 | int 62 | default 0 if FATFS_CODEPAGE_DYNAMIC 63 | default 437 if FATFS_CODEPAGE_437 64 | default 720 if FATFS_CODEPAGE_720 65 | default 737 if FATFS_CODEPAGE_737 66 | default 771 if FATFS_CODEPAGE_771 67 | default 775 if FATFS_CODEPAGE_775 68 | default 850 if FATFS_CODEPAGE_850 69 | default 852 if FATFS_CODEPAGE_852 70 | default 855 if FATFS_CODEPAGE_855 71 | default 857 if FATFS_CODEPAGE_857 72 | default 860 if FATFS_CODEPAGE_860 73 | default 861 if FATFS_CODEPAGE_861 74 | default 862 if FATFS_CODEPAGE_862 75 | default 863 if FATFS_CODEPAGE_863 76 | default 864 if FATFS_CODEPAGE_864 77 | default 865 if FATFS_CODEPAGE_865 78 | default 866 if FATFS_CODEPAGE_866 79 | default 869 if FATFS_CODEPAGE_869 80 | default 932 if FATFS_CODEPAGE_932 81 | default 936 if FATFS_CODEPAGE_936 82 | default 949 if FATFS_CODEPAGE_949 83 | default 950 if FATFS_CODEPAGE_950 84 | default 437 85 | 86 | choice FATFS_LONG_FILENAMES 87 | prompt "Long filename support" 88 | default FATFS_LFN_NONE 89 | help 90 | Support long filenames in FAT. Long filename data increases 91 | memory usage. FATFS can be configured to store the buffer for 92 | long filename data in stack or heap. 93 | 94 | config FATFS_LFN_NONE 95 | bool "No long filenames" 96 | config FATFS_LFN_HEAP 97 | bool "Long filename buffer in heap" 98 | config FATFS_LFN_STACK 99 | bool "Long filename buffer on stack" 100 | endchoice 101 | 102 | config FATFS_MAX_LFN 103 | int "Max long filename length" 104 | depends on !FATFS_LFN_NONE 105 | default 255 106 | range 12 255 107 | help 108 | Maximum long filename length. Can be reduced to save RAM. 109 | 110 | choice FATFS_API_ENCODING 111 | prompt "API character encoding" 112 | depends on !FATFS_LFN_NONE 113 | default FATFS_API_ENCODING_ANSI_OEM 114 | help 115 | Choose encoding for character and string arguments/returns when using 116 | FATFS APIs. The encoding of arguments will usually depend on text 117 | editor settings. 118 | 119 | config FATFS_API_ENCODING_ANSI_OEM 120 | bool "API uses ANSI/OEM encoding" 121 | config FATFS_API_ENCODING_UTF_16 122 | bool "API uses UTF-16 encoding" 123 | config FATFS_API_ENCODING_UTF_8 124 | bool "API uses UTF-8 encoding" 125 | endchoice 126 | 127 | config FATFS_FS_LOCK 128 | int "Number of simultaneously open files protected by lock function" 129 | default 0 130 | range 0 65535 131 | help 132 | This option sets the FATFS configuration value _FS_LOCK. 133 | The option _FS_LOCK switches file lock function to control duplicated file open 134 | and illegal operation to open objects. 135 | 136 | * 0: Disable file lock function. To avoid volume corruption, application 137 | should avoid illegal open, remove and rename to the open objects. 138 | 139 | * >0: Enable file lock function. The value defines how many files/sub-directories 140 | can be opened simultaneously under file lock control. 141 | 142 | Note that the file lock control is independent of re-entrancy. 143 | 144 | config FATFS_TIMEOUT_MS 145 | int "Timeout for acquiring a file lock, ms" 146 | default 10000 147 | help 148 | This option sets FATFS configuration value _FS_TIMEOUT, scaled to milliseconds. 149 | Sets the number of milliseconds FATFS will wait to acquire a mutex when 150 | operating on an open file. For example, if one task is performing a lenghty 151 | operation, another task will wait for the first task to release the lock, 152 | and time out after amount of time set by this option. 153 | 154 | 155 | config FATFS_PER_FILE_CACHE 156 | bool "Use separate cache for each file" 157 | default y 158 | help 159 | This option affects FATFS configuration value _FS_TINY. 160 | 161 | If this option is set, _FS_TINY is 0, and each open file has its own cache, 162 | size of the cache is equal to the _MAX_SS variable (512 or 4096 bytes). 163 | This option uses more RAM if more than 1 file is open, but needs less reads 164 | and writes to the storage for some operations. 165 | 166 | If this option is not set, _FS_TINY is 1, and single cache is used for 167 | all open files, size is also equal to _MAX_SS variable. This reduces the 168 | amount of heap used when multiple files are open, but increases the number 169 | of read and write operations which FATFS needs to make. 170 | 171 | 172 | config FATFS_ALLOC_PREFER_EXTRAM 173 | bool "Perfer external RAM when allocating FATFS buffers" 174 | default y 175 | depends on SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC 176 | help 177 | When the option is enabled, internal buffers used by FATFS will be allocated 178 | from external RAM. If the allocation from external RAM fails, the buffer will 179 | be allocated from the internal RAM. 180 | Disable this option if optimizing for performance. Enable this option if 181 | optimizing for internal memory size. 182 | 183 | 184 | config FATFS_USE_FASTSEEK 185 | bool "Enable fast seek algorithm when using lseek function through VFS FAT" 186 | default n 187 | help 188 | The fast seek feature enables fast backward/long seek operations without 189 | FAT access by using an in-memory CLMT (cluster link map table). 190 | Please note, fast-seek is only allowed for read-mode files, if a 191 | file is opened in write-mode, the seek mechanism will automatically fallback 192 | to the default implementation. 193 | 194 | 195 | config FATFS_FAST_SEEK_BUFFER_SIZE 196 | int "Fast seek CLMT buffer size" 197 | default 64 198 | depends on FATFS_USE_FASTSEEK 199 | help 200 | If fast seek algorithm is enabled, this defines the size of 201 | CLMT buffer used by this algorithm in 32-bit word units. 202 | This value should be chosen based on prior knowledge of 203 | maximum elements of each file entry would store. 204 | 205 | endmenu 206 | -------------------------------------------------------------------------------- /components/fatfs/test/test_fatfs_rawflash.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "unity.h" 22 | #include "test_utils.h" 23 | #include "esp_log.h" 24 | #include "esp_system.h" 25 | #include "esp_vfs.h" 26 | #include "esp_vfs_fat.h" 27 | #include "freertos/FreeRTOS.h" 28 | #include "freertos/task.h" 29 | #include "test_fatfs_common.h" 30 | #include "esp_partition.h" 31 | #include "ff.h" 32 | #include "esp_rom_sys.h" 33 | 34 | 35 | static void test_setup(size_t max_files) 36 | { 37 | extern const char fatfs_start[] asm("_binary_fatfs_img_start"); 38 | extern const char fatfs_end[] asm("_binary_fatfs_img_end"); 39 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 40 | .format_if_mount_failed = false, 41 | .max_files = max_files 42 | }; 43 | const esp_partition_t* part = get_test_data_partition(); 44 | 45 | TEST_ASSERT(part->size == (fatfs_end - fatfs_start - 1)); 46 | 47 | spi_flash_mmap_handle_t mmap_handle; 48 | const void* mmap_ptr; 49 | TEST_ESP_OK(esp_partition_mmap(part, 0, part->size, SPI_FLASH_MMAP_DATA, &mmap_ptr, &mmap_handle)); 50 | bool content_valid = memcmp(fatfs_start, mmap_ptr, part->size) == 0; 51 | spi_flash_munmap(mmap_handle); 52 | 53 | if (!content_valid) { 54 | printf("Copying fatfs.img into test partition...\n"); 55 | esp_partition_erase_range(part, 0, part->size); 56 | for (int i = 0; i < part->size; i+= SPI_FLASH_SEC_SIZE) { 57 | ESP_ERROR_CHECK( esp_partition_write(part, i, fatfs_start + i, SPI_FLASH_SEC_SIZE) ); 58 | } 59 | } 60 | 61 | TEST_ESP_OK(esp_vfs_fat_rawflash_mount("/spiflash", "flash_test", &mount_config)); 62 | } 63 | 64 | static void test_teardown(void) 65 | { 66 | TEST_ESP_OK(esp_vfs_fat_rawflash_unmount("/spiflash","flash_test")); 67 | } 68 | 69 | TEST_CASE("(raw) can read file", "[fatfs]") 70 | { 71 | test_setup(5); 72 | FILE* f = fopen("/spiflash/hello.txt", "r"); 73 | TEST_ASSERT_NOT_NULL(f); 74 | char buf[32] = { 0 }; 75 | int cb = fread(buf, 1, sizeof(buf), f); 76 | TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), cb); 77 | TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf)); 78 | TEST_ASSERT_EQUAL(0, fclose(f)); 79 | test_teardown(); 80 | } 81 | 82 | TEST_CASE("(raw) can open maximum number of files", "[fatfs]") 83 | { 84 | size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */ 85 | test_setup(max_files); 86 | 87 | FILE** files = calloc(max_files, sizeof(FILE*)); 88 | for (size_t i = 0; i < max_files; ++i) { 89 | char name[32]; 90 | snprintf(name, sizeof(name), "/spiflash/f/%d.txt", i + 1); 91 | files[i] = fopen(name, "r"); 92 | TEST_ASSERT_NOT_NULL(files[i]); 93 | } 94 | /* close everything and clean up */ 95 | for (size_t i = 0; i < max_files; ++i) { 96 | fclose(files[i]); 97 | } 98 | free(files); 99 | test_teardown(); 100 | 101 | } 102 | 103 | 104 | TEST_CASE("(raw) can lseek", "[fatfs]") 105 | { 106 | test_setup(5); 107 | FILE* f = fopen("/spiflash/hello.txt", "r"); 108 | TEST_ASSERT_NOT_NULL(f); 109 | TEST_ASSERT_EQUAL(0, fseek(f, 2, SEEK_CUR)); 110 | TEST_ASSERT_EQUAL('l', fgetc(f)); 111 | TEST_ASSERT_EQUAL(0, fseek(f, 4, SEEK_SET)); 112 | TEST_ASSERT_EQUAL('o', fgetc(f)); 113 | TEST_ASSERT_EQUAL(0, fseek(f, -5, SEEK_END)); 114 | TEST_ASSERT_EQUAL('r', fgetc(f)); 115 | TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_END)); 116 | TEST_ASSERT_EQUAL(17, ftell(f)); 117 | 118 | TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END)); 119 | TEST_ASSERT_EQUAL(14, ftell(f)); 120 | TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET)); 121 | test_teardown(); 122 | } 123 | 124 | TEST_CASE("(raw) stat returns correct values", "[fatfs]") 125 | { 126 | test_setup(5); 127 | struct tm tm; 128 | tm.tm_year = 2018 - 1900; 129 | tm.tm_mon = 5; // Note: month can be 0-11 & not 1-12 130 | tm.tm_mday = 13; 131 | tm.tm_hour = 11; 132 | tm.tm_min = 2; 133 | tm.tm_sec = 10; 134 | time_t t = mktime(&tm); 135 | printf("Reference time: %s", asctime(&tm)); 136 | 137 | struct stat st; 138 | TEST_ASSERT_EQUAL(0, stat("/spiflash/stat.txt", &st)); 139 | 140 | time_t mtime = st.st_mtime; 141 | struct tm mtm; 142 | localtime_r(&mtime, &mtm); 143 | printf("File time: %s", asctime(&mtm)); 144 | TEST_ASSERT(mtime > t); // Modification time should be in future wrt ref time 145 | 146 | TEST_ASSERT(st.st_mode & S_IFREG); 147 | TEST_ASSERT_FALSE(st.st_mode & S_IFDIR); 148 | 149 | memset(&st, 0, sizeof(st)); 150 | TEST_ASSERT_EQUAL(0, stat("/spiflash", &st)); 151 | TEST_ASSERT(st.st_mode & S_IFDIR); 152 | TEST_ASSERT_FALSE(st.st_mode & S_IFREG); 153 | 154 | test_teardown(); 155 | } 156 | 157 | 158 | 159 | TEST_CASE("(raw) can opendir root directory of FS", "[fatfs]") 160 | { 161 | test_setup(5); 162 | DIR* dir = opendir("/spiflash"); 163 | TEST_ASSERT_NOT_NULL(dir); 164 | bool found = false; 165 | while (true) { 166 | struct dirent* de = readdir(dir); 167 | if (!de) { 168 | break; 169 | } 170 | if (strcasecmp(de->d_name, "test_opd.txt") == 0) { 171 | found = true; 172 | break; 173 | } 174 | } 175 | TEST_ASSERT_TRUE(found); 176 | TEST_ASSERT_EQUAL(0, closedir(dir)); 177 | 178 | test_teardown(); 179 | } 180 | TEST_CASE("(raw) opendir, readdir, rewinddir, seekdir work as expected", "[fatfs]") 181 | { 182 | test_setup(5); 183 | 184 | DIR* dir = opendir("/spiflash/dir"); 185 | TEST_ASSERT_NOT_NULL(dir); 186 | int count = 0; 187 | const char* names[4]; 188 | while(count < 4) { 189 | struct dirent* de = readdir(dir); 190 | if (!de) { 191 | break; 192 | } 193 | printf("found '%s'\n", de->d_name); 194 | if (strcasecmp(de->d_name, "1.txt") == 0) { 195 | TEST_ASSERT_TRUE(de->d_type == DT_REG); 196 | names[count] = "1.txt"; 197 | ++count; 198 | } else if (strcasecmp(de->d_name, "2.txt") == 0) { 199 | TEST_ASSERT_TRUE(de->d_type == DT_REG); 200 | names[count] = "2.txt"; 201 | ++count; 202 | } else if (strcasecmp(de->d_name, "inner") == 0) { 203 | TEST_ASSERT_TRUE(de->d_type == DT_DIR); 204 | names[count] = "inner"; 205 | ++count; 206 | } else if (strcasecmp(de->d_name, "boo.bin") == 0) { 207 | TEST_ASSERT_TRUE(de->d_type == DT_REG); 208 | names[count] = "boo.bin"; 209 | ++count; 210 | } else { 211 | TEST_FAIL_MESSAGE("unexpected directory entry"); 212 | } 213 | } 214 | TEST_ASSERT_EQUAL(count, 4); 215 | 216 | rewinddir(dir); 217 | struct dirent* de = readdir(dir); 218 | TEST_ASSERT_NOT_NULL(de); 219 | TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0])); 220 | seekdir(dir, 3); 221 | de = readdir(dir); 222 | TEST_ASSERT_NOT_NULL(de); 223 | TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3])); 224 | seekdir(dir, 1); 225 | de = readdir(dir); 226 | TEST_ASSERT_NOT_NULL(de); 227 | TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1])); 228 | seekdir(dir, 2); 229 | de = readdir(dir); 230 | TEST_ASSERT_NOT_NULL(de); 231 | TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2])); 232 | 233 | TEST_ASSERT_EQUAL(0, closedir(dir)); 234 | 235 | test_teardown(); 236 | } 237 | 238 | 239 | typedef struct { 240 | const char* filename; 241 | size_t word_count; 242 | int seed; 243 | int val; 244 | SemaphoreHandle_t done; 245 | int result; 246 | } read_test_arg_t; 247 | 248 | #define READ_TEST_ARG_INIT(name, seed_, val_) \ 249 | { \ 250 | .filename = name, \ 251 | .seed = seed_, \ 252 | .word_count = 8000, \ 253 | .val = val_, \ 254 | .done = xSemaphoreCreateBinary() \ 255 | } 256 | 257 | static void read_task(void* param) 258 | { 259 | read_test_arg_t* args = (read_test_arg_t*) param; 260 | FILE* f = fopen(args->filename, "rb"); 261 | if (f == NULL) { 262 | args->result = ESP_ERR_NOT_FOUND; 263 | goto done; 264 | } 265 | 266 | srand(args->seed); 267 | for (size_t i = 0; i < args->word_count; ++i) { 268 | uint32_t rval; 269 | int cnt = fread(&rval, sizeof(rval), 1, f); 270 | if (cnt != 1 || rval != args->val) { 271 | esp_rom_printf("E(r): i=%d, cnt=%d rval=%d val=%d\n\n", i, cnt, rval, args->val); 272 | args->result = ESP_FAIL; 273 | goto close; 274 | } 275 | } 276 | args->result = ESP_OK; 277 | 278 | close: 279 | fclose(f); 280 | 281 | done: 282 | xSemaphoreGive(args->done); 283 | vTaskDelay(1); 284 | vTaskDelete(NULL); 285 | } 286 | 287 | 288 | TEST_CASE("(raw) multiple tasks can use same volume", "[fatfs]") 289 | { 290 | test_setup(5); 291 | char names[4][64]; 292 | for (size_t i = 0; i < 4; ++i) { 293 | snprintf(names[i], sizeof(names[i]), "/spiflash/ccrnt/%d.txt", i + 1); 294 | } 295 | 296 | read_test_arg_t args1 = READ_TEST_ARG_INIT(names[0], 1, 0x31313131); 297 | read_test_arg_t args2 = READ_TEST_ARG_INIT(names[1], 2, 0x32323232); 298 | read_test_arg_t args3 = READ_TEST_ARG_INIT(names[2], 3, 0x33333333); 299 | read_test_arg_t args4 = READ_TEST_ARG_INIT(names[3], 4, 0x34343434); 300 | 301 | const int cpuid_0 = 0; 302 | const int cpuid_1 = portNUM_PROCESSORS - 1; 303 | const int stack_size = 4096; 304 | 305 | printf("reading files 1.txt 2.txt 3.txt 4.txt \n"); 306 | 307 | xTaskCreatePinnedToCore(&read_task, "r1", stack_size, &args1, 3, NULL, cpuid_1); 308 | xTaskCreatePinnedToCore(&read_task, "r2", stack_size, &args2, 3, NULL, cpuid_0); 309 | xTaskCreatePinnedToCore(&read_task, "r3", stack_size, &args3, 3, NULL, cpuid_0); 310 | xTaskCreatePinnedToCore(&read_task, "r4", stack_size, &args4, 3, NULL, cpuid_1); 311 | 312 | xSemaphoreTake(args1.done, portMAX_DELAY); 313 | printf("1.txt done\n"); 314 | TEST_ASSERT_EQUAL(ESP_OK, args1.result); 315 | xSemaphoreTake(args2.done, portMAX_DELAY); 316 | printf("2.txt done\n"); 317 | TEST_ASSERT_EQUAL(ESP_OK, args2.result); 318 | xSemaphoreTake(args3.done, portMAX_DELAY); 319 | printf("3.txt done\n"); 320 | TEST_ASSERT_EQUAL(ESP_OK, args3.result); 321 | xSemaphoreTake(args4.done, portMAX_DELAY); 322 | printf("4.txt done\n"); 323 | TEST_ASSERT_EQUAL(ESP_OK, args4.result); 324 | 325 | vSemaphoreDelete(args1.done); 326 | vSemaphoreDelete(args2.done); 327 | vSemaphoreDelete(args3.done); 328 | vSemaphoreDelete(args4.done); 329 | test_teardown(); 330 | } 331 | 332 | TEST_CASE("(raw) read speed test", "[fatfs][timeout=60]") 333 | { 334 | test_setup(5); 335 | 336 | const size_t buf_size = 16 * 1024; 337 | uint32_t* buf = (uint32_t*) calloc(1, buf_size); 338 | const size_t file_size = 256 * 1024; 339 | const char* file = "/spiflash/256k.bin"; 340 | 341 | test_fatfs_rw_speed(file, buf, 4 * 1024, file_size, false); 342 | test_fatfs_rw_speed(file, buf, 8 * 1024, file_size, false); 343 | test_fatfs_rw_speed(file, buf, 16 * 1024, file_size, false); 344 | 345 | free(buf); 346 | test_teardown(); 347 | } 348 | -------------------------------------------------------------------------------- /components/fatfs/src/00history.txt: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------- 2 | Revision history of FatFs module 3 | ---------------------------------------------------------------------------- 4 | 5 | R0.00 (February 26, 2006) 6 | 7 | Prototype. 8 | 9 | 10 | 11 | R0.01 (April 29, 2006) 12 | 13 | The first release. 14 | 15 | 16 | 17 | R0.02 (June 01, 2006) 18 | 19 | Added FAT12 support. 20 | Removed unbuffered mode. 21 | Fixed a problem on small (<32M) partition. 22 | 23 | 24 | 25 | R0.02a (June 10, 2006) 26 | 27 | Added a configuration option (_FS_MINIMUM). 28 | 29 | 30 | 31 | R0.03 (September 22, 2006) 32 | 33 | Added f_rename(). 34 | Changed option _FS_MINIMUM to _FS_MINIMIZE. 35 | 36 | 37 | 38 | R0.03a (December 11, 2006) 39 | 40 | Improved cluster scan algorithm to write files fast. 41 | Fixed f_mkdir() creates incorrect directory on FAT32. 42 | 43 | 44 | 45 | R0.04 (February 04, 2007) 46 | 47 | Added f_mkfs(). 48 | Supported multiple drive system. 49 | Changed some interfaces for multiple drive system. 50 | Changed f_mountdrv() to f_mount(). 51 | 52 | 53 | 54 | R0.04a (April 01, 2007) 55 | 56 | Supported multiple partitions on a physical drive. 57 | Added a capability of extending file size to f_lseek(). 58 | Added minimization level 3. 59 | Fixed an endian sensitive code in f_mkfs(). 60 | 61 | 62 | 63 | R0.04b (May 05, 2007) 64 | 65 | Added a configuration option _USE_NTFLAG. 66 | Added FSINFO support. 67 | Fixed DBCS name can result FR_INVALID_NAME. 68 | Fixed short seek (<= csize) collapses the file object. 69 | 70 | 71 | 72 | R0.05 (August 25, 2007) 73 | 74 | Changed arguments of f_read(), f_write() and f_mkfs(). 75 | Fixed f_mkfs() on FAT32 creates incorrect FSINFO. 76 | Fixed f_mkdir() on FAT32 creates incorrect directory. 77 | 78 | 79 | 80 | R0.05a (February 03, 2008) 81 | 82 | Added f_truncate() and f_utime(). 83 | Fixed off by one error at FAT sub-type determination. 84 | Fixed btr in f_read() can be mistruncated. 85 | Fixed cached sector is not flushed when create and close without write. 86 | 87 | 88 | 89 | R0.06 (April 01, 2008) 90 | 91 | Added fputc(), fputs(), fprintf() and fgets(). 92 | Improved performance of f_lseek() on moving to the same or following cluster. 93 | 94 | 95 | 96 | R0.07 (April 01, 2009) 97 | 98 | Merged Tiny-FatFs as a configuration option. (_FS_TINY) 99 | Added long file name feature. (_USE_LFN) 100 | Added multiple code page feature. (_CODE_PAGE) 101 | Added re-entrancy for multitask operation. (_FS_REENTRANT) 102 | Added auto cluster size selection to f_mkfs(). 103 | Added rewind option to f_readdir(). 104 | Changed result code of critical errors. 105 | Renamed string functions to avoid name collision. 106 | 107 | 108 | 109 | R0.07a (April 14, 2009) 110 | 111 | Septemberarated out OS dependent code on reentrant cfg. 112 | Added multiple sector size feature. 113 | 114 | 115 | 116 | R0.07c (June 21, 2009) 117 | 118 | Fixed f_unlink() can return FR_OK on error. 119 | Fixed wrong cache control in f_lseek(). 120 | Added relative path feature. 121 | Added f_chdir() and f_chdrive(). 122 | Added proper case conversion to extended character. 123 | 124 | 125 | 126 | R0.07e (November 03, 2009) 127 | 128 | Septemberarated out configuration options from ff.h to ffconf.h. 129 | Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH. 130 | Fixed name matching error on the 13 character boundary. 131 | Added a configuration option, _LFN_UNICODE. 132 | Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. 133 | 134 | 135 | 136 | R0.08 (May 15, 2010) 137 | 138 | Added a memory configuration option. (_USE_LFN = 3) 139 | Added file lock feature. (_FS_SHARE) 140 | Added fast seek feature. (_USE_FASTSEEK) 141 | Changed some types on the API, XCHAR->TCHAR. 142 | Changed .fname in the FILINFO structure on Unicode cfg. 143 | String functions support UTF-8 encoding files on Unicode cfg. 144 | 145 | 146 | 147 | R0.08a (August 16, 2010) 148 | 149 | Added f_getcwd(). (_FS_RPATH = 2) 150 | Added sector erase feature. (_USE_ERASE) 151 | Moved file lock semaphore table from fs object to the bss. 152 | Fixed f_mkfs() creates wrong FAT32 volume. 153 | 154 | 155 | 156 | R0.08b (January 15, 2011) 157 | 158 | Fast seek feature is also applied to f_read() and f_write(). 159 | f_lseek() reports required table size on creating CLMP. 160 | Extended format syntax of f_printf(). 161 | Ignores duplicated directory separators in given path name. 162 | 163 | 164 | 165 | R0.09 (September 06, 2011) 166 | 167 | f_mkfs() supports multiple partition to complete the multiple partition feature. 168 | Added f_fdisk(). 169 | 170 | 171 | 172 | R0.09a (August 27, 2012) 173 | 174 | Changed f_open() and f_opendir() reject null object pointer to avoid crash. 175 | Changed option name _FS_SHARE to _FS_LOCK. 176 | Fixed assertion failure due to OS/2 EA on FAT12/16 volume. 177 | 178 | 179 | 180 | R0.09b (January 24, 2013) 181 | 182 | Added f_setlabel() and f_getlabel(). 183 | 184 | 185 | 186 | R0.10 (October 02, 2013) 187 | 188 | Added selection of character encoding on the file. (_STRF_ENCODE) 189 | Added f_closedir(). 190 | Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO) 191 | Added forced mount feature with changes of f_mount(). 192 | Improved behavior of volume auto detection. 193 | Improved write throughput of f_puts() and f_printf(). 194 | Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write(). 195 | Fixed f_write() can be truncated when the file size is close to 4GB. 196 | Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error. 197 | 198 | 199 | 200 | R0.10a (January 15, 2014) 201 | 202 | Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID) 203 | Added a configuration option of minimum sector size. (_MIN_SS) 204 | 2nd argument of f_rename() can have a drive number and it will be ignored. 205 | Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10) 206 | Fixed f_close() invalidates the file object without volume lock. 207 | Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10) 208 | Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07) 209 | 210 | 211 | 212 | R0.10b (May 19, 2014) 213 | 214 | Fixed a hard error in the disk I/O layer can collapse the directory entry. 215 | Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07) 216 | 217 | 218 | 219 | R0.10c (November 09, 2014) 220 | 221 | Added a configuration option for the platforms without RTC. (_FS_NORTC) 222 | Changed option name _USE_ERASE to _USE_TRIM. 223 | Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b) 224 | Fixed a potential problem of FAT access that can appear on disk error. 225 | Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08) 226 | 227 | 228 | 229 | R0.11 (February 09, 2015) 230 | 231 | Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND) 232 | Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c) 233 | Fixed _FS_NORTC option does not work properly. (appeared at R0.10c) 234 | 235 | 236 | 237 | R0.11a (September 05, 2015) 238 | 239 | Fixed wrong media change can lead a deadlock at thread-safe configuration. 240 | Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE) 241 | Removed some code pages actually not exist on the standard systems. (_CODE_PAGE) 242 | Fixed errors in the case conversion teble of code page 437 and 850 (ff.c). 243 | Fixed errors in the case conversion teble of Unicode (cc*.c). 244 | 245 | 246 | 247 | R0.12 (April 12, 2016) 248 | 249 | Added support for exFAT file system. (_FS_EXFAT) 250 | Added f_expand(). (_USE_EXPAND) 251 | Changed some members in FINFO structure and behavior of f_readdir(). 252 | Added an option _USE_CHMOD. 253 | Removed an option _WORD_ACCESS. 254 | Fixed errors in the case conversion table of Unicode (cc*.c). 255 | 256 | 257 | 258 | R0.12a (July 10, 2016) 259 | 260 | Added support for creating exFAT volume with some changes of f_mkfs(). 261 | Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed. 262 | f_forward() is available regardless of _FS_TINY. 263 | Fixed f_mkfs() creates wrong volume. (appeared at R0.12) 264 | Fixed wrong memory read in create_name(). (appeared at R0.12) 265 | Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD. 266 | 267 | 268 | 269 | R0.12b (September 04, 2016) 270 | 271 | Made f_rename() be able to rename objects with the same name but case. 272 | Fixed an error in the case conversion teble of code page 866. (ff.c) 273 | Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12) 274 | Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12) 275 | Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12) 276 | Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12) 277 | Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12) 278 | Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12) 279 | 280 | 281 | 282 | R0.12c (March 04, 2017) 283 | 284 | Improved write throughput at the fragmented file on the exFAT volume. 285 | Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN. 286 | Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12) 287 | Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c) 288 | 289 | 290 | 291 | R0.13 (May 21, 2017) 292 | 293 | Changed heading character of configuration keywords "_" to "FF_". 294 | Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead. 295 | Added f_setcp(), run-time code page configuration. (FF_CODE_PAGE = 0) 296 | Improved cluster allocation time on stretch a deep buried cluster chain. 297 | Improved processing time of f_mkdir() with large cluster size by using FF_USE_LFN = 3. 298 | Improved NoFatChain flag of the fragmented file to be set after it is truncated and got contiguous. 299 | Fixed archive attribute is left not set when a file on the exFAT volume is renamed. (appeared at R0.12) 300 | Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c) 301 | Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c) 302 | 303 | 304 | 305 | R0.13a (October 14, 2017) 306 | 307 | Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2) 308 | Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF). 309 | Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk(). 310 | Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09) 311 | Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c) 312 | Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12) 313 | 314 | 315 | 316 | R0.13b (April 07, 2018) 317 | 318 | Added support for UTF-32 encoding on the API. (FF_LFN_UNICODE = 3) 319 | Added support for Unix style volume ID. (FF_STR_VOLUME_ID = 2) 320 | Fixed accesing any object on the exFAT root directory beyond the cluster boundary can fail. (appeared at R0.12c) 321 | Fixed f_setlabel() does not reject some invalid characters. (appeared at R0.09b) 322 | 323 | 324 | 325 | R0.13c (October 14, 2018) 326 | Supported stdint.h for C99 and later. (integer.h was included in ff.h) 327 | Fixed reading a directory gets infinite loop when the last directory entry is not empty. (appeared at R0.12) 328 | Fixed creating a sub-directory in the fragmented sub-directory on the exFAT volume collapses FAT chain of the parent directory. (appeared at R0.12) 329 | Fixed f_getcwd() cause output buffer overrun when the buffer has a valid drive number. (appeared at R0.13b) 330 | -------------------------------------------------------------------------------- /lib/benchmarks/benchmark_tester.c: -------------------------------------------------------------------------------- 1 | #include "benchmark_tester.h" 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | // static uint32_t max_file_size = 1024 * 1024 * 1; 16 | static uint32_t max_file_size = 1024 * 512; 17 | 18 | filesystem const *fs; 19 | 20 | void set_filesystem(const filesystem *_filesystem) 21 | { 22 | fs = _filesystem; 23 | } 24 | 25 | void randomize_bytes(uint8_t *bytes, size_t size) 26 | { 27 | for (size_t i = 0; i < size; i++) 28 | { 29 | bytes[i] = rand(); 30 | } 31 | } 32 | 33 | uint64_t randomValue(uint64_t lower, uint64_t upper) 34 | { 35 | uint64_t num = (rand() % 36 | (upper - lower + 1)) + 37 | lower; 38 | return num; 39 | } 40 | 41 | void create_empty_file(const char *filename, uint64_t size) 42 | { 43 | char fullpath[256]; 44 | vd_cwd(fullpath, sizeof(fullpath)); 45 | strncat(fullpath, filename, sizeof(fullpath) - 1); 46 | 47 | FILE *create_f = fs->fopen(fullpath, "wb", 4096); 48 | if (create_f == NULL) 49 | { 50 | log_error("fopen() failed"); 51 | } 52 | 53 | if (fs->seek(create_f, size, SEEK_SET) == -1) 54 | { 55 | log_error("fseek() failed"); 56 | } 57 | 58 | if (fs->write(create_f, "\0", 1) != 1) 59 | { 60 | log_error("fputc() failed"); 61 | } 62 | fs->close(create_f); 63 | } 64 | 65 | void test_write_sequential(bool sync, uint32_t block_size, struct statistics *stats) 66 | { 67 | const char *filename = "test_file1.tmp"; 68 | char fullpath[256]; 69 | vd_cwd(fullpath, sizeof(fullpath)); 70 | strncat(fullpath, filename, sizeof(fullpath) - 1); 71 | 72 | FILE *f = fs->fopen(fullpath, "wb", block_size); 73 | if (f == NULL) 74 | { 75 | log_error("fopen() failed"); 76 | } 77 | uint8_t *buf = (uint8_t *)malloc(block_size); 78 | assert(buf); 79 | randomize_bytes(buf, block_size); 80 | 81 | uint32_t iops = 0; 82 | uint32_t total_time = 0; 83 | uint32_t written = 0; 84 | struct timeval tv_start; 85 | gettimeofday(&tv_start, NULL); 86 | 87 | while (written < max_file_size) 88 | { 89 | if (fs->write(f, buf, block_size) !=block_size) 90 | { 91 | log_error("Error writing to backing store\n"); 92 | } 93 | if (sync) 94 | { 95 | // fflush(f); 96 | } 97 | written += block_size; 98 | iops++; 99 | } 100 | 101 | struct timeval tv_end; 102 | gettimeofday(&tv_end, NULL); 103 | 104 | float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec); 105 | total_time += (t_s * 1e3); 106 | 107 | stats->sync = sync; 108 | stats->rw = true; 109 | stats->random = false; 110 | stats->block_size = block_size; 111 | stats->aligned = 0; 112 | 113 | stats->time = total_time; 114 | stats->bytes = written; 115 | stats->iops = iops; 116 | 117 | free(buf); 118 | // close(f); 119 | fs->close(f); 120 | } 121 | 122 | void test_write_random(bool sync, uint32_t block_size, struct statistics *stats) 123 | { 124 | const char *test_filename = "test_file2.tmp"; 125 | char fullpath[256]; 126 | vd_cwd(fullpath, sizeof(fullpath)); 127 | strncat(fullpath, test_filename, sizeof(fullpath) - 1); 128 | 129 | create_empty_file(test_filename, max_file_size); 130 | 131 | FILE *f = fs->fopen(fullpath, "r+b", block_size); 132 | if (f == NULL) 133 | { 134 | log_error("fopen() failed"); 135 | } 136 | uint8_t *buf = (uint8_t *)malloc(block_size); 137 | assert(buf); 138 | randomize_bytes(buf, block_size); 139 | 140 | uint32_t iops = 0; 141 | uint32_t total_time = 0; 142 | uint32_t written = 0; 143 | srand(time(NULL)); 144 | 145 | struct timeval tv_start; 146 | gettimeofday(&tv_start, NULL); 147 | 148 | while (written < max_file_size) 149 | { 150 | uint64_t location = randomValue(0, max_file_size - block_size); 151 | if (fs->seek(f, location, SEEK_SET) != location) 152 | { 153 | log_warn("Error seeking in backing store"); 154 | } 155 | 156 | if (fs->write(f, buf, block_size) != block_size) 157 | { 158 | log_error("Error writing to backing store\n"); 159 | } 160 | if (sync) 161 | { 162 | // fflush(f); 163 | } 164 | written += block_size; 165 | iops++; 166 | } 167 | 168 | struct timeval tv_end; 169 | gettimeofday(&tv_end, NULL); 170 | 171 | float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec); 172 | total_time += (t_s * 1e3); 173 | 174 | stats->sync = sync; 175 | stats->rw = true; 176 | stats->random = true; 177 | stats->block_size = block_size; 178 | stats->aligned = 0; 179 | 180 | stats->time = total_time; 181 | stats->bytes = written; 182 | stats->iops = iops; 183 | 184 | free(buf); 185 | fs->close(f); 186 | } 187 | void test_write_random_aligned(bool sync, uint32_t block_size, struct statistics *stats) 188 | { 189 | const char *test_filename = "test_file3.tmp"; 190 | char fullpath[256]; 191 | vd_cwd(fullpath, sizeof(fullpath)); 192 | strncat(fullpath, test_filename, sizeof(fullpath) - 1); 193 | 194 | create_empty_file(test_filename, max_file_size); 195 | 196 | FILE *f = fs->fopen(fullpath, "r+b", block_size); 197 | if (f == NULL) 198 | { 199 | log_error("fopen() failed"); 200 | } 201 | uint8_t *buf = (uint8_t *)malloc(block_size); 202 | assert(buf); 203 | randomize_bytes(buf, block_size); 204 | 205 | uint32_t iops = 0; 206 | uint32_t total_time = 0; 207 | uint32_t written = 0; 208 | srand(time(NULL)); 209 | 210 | struct timeval tv_start; 211 | gettimeofday(&tv_start, NULL); 212 | 213 | while (written < max_file_size) 214 | { 215 | uint64_t location = randomValue(0, (max_file_size / block_size) - 1) * block_size; 216 | 217 | if (fs->seek(f, location, SEEK_SET) != location) 218 | { 219 | log_warn("Error seeking in backing store"); 220 | } 221 | 222 | if (fs->write(f, buf, block_size) != block_size) 223 | { 224 | log_error("Error writing to backing store\n"); 225 | } 226 | if (sync) 227 | { 228 | // fflush(f); 229 | } 230 | written += block_size; 231 | iops++; 232 | } 233 | 234 | struct timeval tv_end; 235 | gettimeofday(&tv_end, NULL); 236 | 237 | float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec); 238 | total_time += (t_s * 1e3); 239 | 240 | stats->sync = sync; 241 | stats->rw = true; 242 | stats->random = true; 243 | stats->block_size = block_size; 244 | stats->aligned = block_size; 245 | 246 | stats->time = total_time; 247 | stats->bytes = written; 248 | stats->iops = iops; 249 | 250 | free(buf); 251 | fs->close(f); 252 | } 253 | 254 | void test_read_sequential(bool sync, uint32_t block_size, struct statistics *stats) 255 | { 256 | const char *test_filename = "test_file.tmp"; 257 | char fullpath[256]; 258 | vd_cwd(fullpath, sizeof(fullpath)); 259 | strncat(fullpath, test_filename, sizeof(fullpath) - 1); 260 | 261 | create_empty_file(test_filename, max_file_size); 262 | 263 | // printf("opening %s\r\n", fullpath); 264 | FILE *f = fs->fopen(fullpath, "r+b", block_size); 265 | // int f = open(fullpath, O_RDONLY,0644); 266 | if (f == NULL) 267 | { 268 | log_error("fopen() failed"); 269 | } 270 | uint8_t *buf = (uint8_t *)malloc(block_size); 271 | assert(buf); 272 | randomize_bytes(buf, block_size); 273 | 274 | uint32_t iops = 0; 275 | uint32_t total_time = 0; 276 | uint32_t written = 0; 277 | srand(time(NULL)); 278 | 279 | struct timeval tv_start; 280 | gettimeofday(&tv_start, NULL); 281 | 282 | while (written < max_file_size) 283 | { 284 | if (fs->read(f, buf, block_size) != block_size) 285 | { 286 | log_error("Error reading from backing store\n"); 287 | } 288 | if (sync) 289 | { 290 | // fflush(f); 291 | } 292 | written += block_size; 293 | iops++; 294 | } 295 | 296 | struct timeval tv_end; 297 | gettimeofday(&tv_end, NULL); 298 | 299 | float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec); 300 | total_time += (t_s * 1e3); 301 | 302 | stats->sync = sync; 303 | stats->rw = false; 304 | stats->random = false; 305 | stats->block_size = block_size; 306 | stats->aligned = 0; 307 | 308 | stats->time = total_time; 309 | stats->bytes = written; 310 | stats->iops = iops; 311 | 312 | free(buf); 313 | // close(f); 314 | fs->close(f); 315 | } 316 | void test_read_random(bool sync, uint32_t block_size, struct statistics *stats) 317 | { 318 | const char *test_filename = "test_file.tmp"; 319 | char fullpath[256]; 320 | vd_cwd(fullpath, sizeof(fullpath)); 321 | strncat(fullpath, test_filename, sizeof(fullpath) - 1); 322 | 323 | create_empty_file(test_filename, max_file_size); 324 | 325 | FILE *f = fs->fopen(fullpath, "r+b", block_size); 326 | if (f == NULL) 327 | { 328 | log_error("fopen() failed"); 329 | } 330 | uint8_t *buf = (uint8_t *)malloc(block_size); 331 | assert(buf); 332 | randomize_bytes(buf, block_size); 333 | 334 | uint32_t iops = 0; 335 | uint32_t total_time = 0; 336 | uint32_t written = 0; 337 | srand(time(NULL)); 338 | 339 | struct timeval tv_start; 340 | gettimeofday(&tv_start, NULL); 341 | 342 | while (written < max_file_size) 343 | { 344 | uint64_t location = randomValue(0, max_file_size - block_size); 345 | if (fs->seek(f, location, SEEK_SET) != location) 346 | { 347 | log_warn("Error seeking in backing store"); 348 | } 349 | 350 | if (fs->read(f, buf, block_size) != block_size) 351 | { 352 | log_error("Error reading from backing store\n"); 353 | } 354 | if (sync) 355 | { 356 | // fflush(f); 357 | } 358 | written += block_size; 359 | iops++; 360 | } 361 | 362 | struct timeval tv_end; 363 | gettimeofday(&tv_end, NULL); 364 | 365 | float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec); 366 | total_time += (t_s * 1e3); 367 | 368 | stats->sync = sync; 369 | stats->rw = false; 370 | stats->random = true; 371 | stats->block_size = block_size; 372 | stats->aligned = 0; 373 | 374 | stats->time = total_time; 375 | stats->bytes = written; 376 | stats->iops = iops; 377 | 378 | free(buf); 379 | // close(f); 380 | fs->close(f); 381 | } 382 | void test_read_random_aligned(bool sync, uint32_t block_size, struct statistics *stats) 383 | { 384 | const char *test_filename = "test_file.tmp"; 385 | char fullpath[256]; 386 | vd_cwd(fullpath, sizeof(fullpath)); 387 | strncat(fullpath, test_filename, sizeof(fullpath) - 1); 388 | 389 | create_empty_file(test_filename, max_file_size); 390 | 391 | // printf("opening %s\r\n", fullpath); 392 | FILE *f = fs->fopen(fullpath, "r+b", block_size); 393 | if (f == NULL) 394 | { 395 | log_error("fopen() failed"); 396 | } 397 | uint8_t *buf = (uint8_t *)malloc(block_size); 398 | assert(buf); 399 | randomize_bytes(buf, block_size); 400 | 401 | uint32_t iops = 0; 402 | uint32_t total_time = 0; 403 | uint32_t written = 0; 404 | srand(time(NULL)); 405 | 406 | struct timeval tv_start; 407 | gettimeofday(&tv_start, NULL); 408 | 409 | while (written < max_file_size) 410 | { 411 | uint64_t location = randomValue(0, (max_file_size / block_size) - 1) * block_size; 412 | if (fs->seek(f, location, SEEK_SET) != location) 413 | { 414 | log_warn("Error seeking in backing store"); 415 | } 416 | 417 | if (fs->read(f, buf, block_size) != block_size) 418 | { 419 | log_error("Error reading from backing store\n"); 420 | } 421 | if (sync) 422 | { 423 | // fflush(f); 424 | } 425 | written += block_size; 426 | iops++; 427 | } 428 | 429 | struct timeval tv_end; 430 | gettimeofday(&tv_end, NULL); 431 | 432 | float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec); 433 | total_time += (t_s * 1e3); 434 | 435 | stats->sync = sync; 436 | stats->rw = false; 437 | stats->random = true; 438 | stats->block_size = block_size; 439 | stats->aligned = block_size; 440 | 441 | stats->time = total_time; 442 | stats->bytes = written; 443 | stats->iops = iops; 444 | 445 | free(buf); 446 | // close(f); 447 | fs->close(f); 448 | } 449 | -------------------------------------------------------------------------------- /components/fatfs/test/test_fatfs_sdmmc.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "unity.h" 22 | #include "esp_log.h" 23 | #include "esp_system.h" 24 | #include "esp_vfs.h" 25 | #include "esp_vfs_fat.h" 26 | #include "freertos/FreeRTOS.h" 27 | #include "freertos/task.h" 28 | #include "driver/sdmmc_defs.h" 29 | #include "sdmmc_cmd.h" 30 | #include "ff.h" 31 | #include "test_fatfs_common.h" 32 | #include "soc/soc_caps.h" 33 | 34 | #define SDSPI_MOSI_PIN 15 35 | #define SDSPI_MISO_PIN 2 36 | #define SDSPI_CS_PIN 13 37 | #define SDSPI_CLK_PIN 14 38 | #define SDSPI_HOST_ID SPI2_HOST 39 | 40 | 41 | #if SOC_SDMMC_HOST_SUPPORTED 42 | #include "driver/sdmmc_host.h" 43 | 44 | 45 | static void test_setup(void) 46 | { 47 | sdmmc_host_t host = SDMMC_HOST_DEFAULT(); 48 | sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); 49 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 50 | .format_if_mount_failed = true, 51 | .max_files = 5, 52 | .allocation_unit_size = 16 * 1024 53 | }; 54 | TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); 55 | } 56 | 57 | static void test_teardown(void) 58 | { 59 | TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); 60 | } 61 | 62 | static const char* test_filename = "/sdcard/hello.txt"; 63 | 64 | TEST_CASE("Mount fails cleanly without card inserted", "[fatfs][sd][ignore]") 65 | { 66 | size_t heap_size; 67 | HEAP_SIZE_CAPTURE(heap_size); 68 | sdmmc_host_t host = SDMMC_HOST_DEFAULT(); 69 | sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); 70 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 71 | .format_if_mount_failed = false, 72 | .max_files = 5 73 | }; 74 | 75 | for (int i = 0; i < 3; ++i) { 76 | printf("Initializing card, attempt %d\n", i); 77 | esp_err_t err = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL); 78 | printf("err=%d\n", err); 79 | TEST_ESP_ERR(ESP_ERR_TIMEOUT, err); 80 | } 81 | HEAP_SIZE_CHECK(heap_size, 0); 82 | } 83 | 84 | TEST_CASE("(SD) can create and write file", "[fatfs][sd][test_env=UT_T1_SDMODE][timeout=60]") 85 | { 86 | test_setup(); 87 | test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str); 88 | test_teardown(); 89 | } 90 | 91 | TEST_CASE("(SD) can read file", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 92 | { 93 | test_setup(); 94 | test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str); 95 | test_fatfs_read_file(test_filename); 96 | test_teardown(); 97 | } 98 | 99 | TEST_CASE("(SD) can read file with pread()", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 100 | { 101 | test_setup(); 102 | test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str); 103 | test_fatfs_pread_file(test_filename); 104 | test_teardown(); 105 | } 106 | 107 | TEST_CASE("(SD) pwrite() works well", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 108 | { 109 | test_setup(); 110 | test_fatfs_pwrite_file(test_filename); 111 | test_teardown(); 112 | } 113 | 114 | TEST_CASE("(SD) overwrite and append file", "[fatfs][sd][test_env=UT_T1_SDMODE][timeout=60]") 115 | { 116 | test_setup(); 117 | test_fatfs_overwrite_append(test_filename); 118 | test_teardown(); 119 | } 120 | 121 | TEST_CASE("(SD) can lseek", "[fatfs][sd][test_env=UT_T1_SDMODE][timeout=60]") 122 | { 123 | test_setup(); 124 | test_fatfs_lseek("/sdcard/seek.txt"); 125 | test_teardown(); 126 | } 127 | 128 | TEST_CASE("(SD) can truncate", "[fatfs][sd][test_env=UT_T1_SDMODE][timeout=60]") 129 | { 130 | test_setup(); 131 | test_fatfs_truncate_file("/sdcard/truncate.txt"); 132 | test_teardown(); 133 | } 134 | 135 | TEST_CASE("(SD) stat returns correct values", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 136 | { 137 | test_setup(); 138 | test_fatfs_stat("/sdcard/stat.txt", "/sdcard"); 139 | test_teardown(); 140 | } 141 | 142 | TEST_CASE("(SD) utime sets modification time", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 143 | { 144 | test_setup(); 145 | test_fatfs_utime("/sdcard/utime.txt", "/sdcard"); 146 | test_teardown(); 147 | } 148 | 149 | TEST_CASE("(SD) unlink removes a file", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 150 | { 151 | test_setup(); 152 | test_fatfs_unlink("/sdcard/unlink.txt"); 153 | test_teardown(); 154 | } 155 | 156 | TEST_CASE("(SD) link copies a file, rename moves a file", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 157 | { 158 | test_setup(); 159 | test_fatfs_link_rename("/sdcard/link"); 160 | test_teardown(); 161 | } 162 | 163 | TEST_CASE("(SD) can create and remove directories", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 164 | { 165 | test_setup(); 166 | test_fatfs_mkdir_rmdir("/sdcard/dir"); 167 | test_teardown(); 168 | } 169 | 170 | TEST_CASE("(SD) can opendir root directory of FS", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 171 | { 172 | test_setup(); 173 | test_fatfs_can_opendir("/sdcard"); 174 | test_teardown(); 175 | } 176 | 177 | TEST_CASE("(SD) opendir, readdir, rewinddir, seekdir work as expected", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 178 | { 179 | test_setup(); 180 | test_fatfs_opendir_readdir_rewinddir("/sdcard/dir"); 181 | test_teardown(); 182 | } 183 | 184 | TEST_CASE("(SD) multiple tasks can use same volume", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") 185 | { 186 | test_setup(); 187 | test_fatfs_concurrent("/sdcard/f"); 188 | test_teardown(); 189 | } 190 | 191 | static void sdmmc_speed_test(void *buf, size_t buf_size, size_t file_size, bool write); 192 | 193 | TEST_CASE("(SD) write/read speed test", "[fatfs][sd][test_env=UT_T1_SDMODE][timeout=60]") 194 | { 195 | size_t heap_size; 196 | HEAP_SIZE_CAPTURE(heap_size); 197 | 198 | const size_t buf_size = 16 * 1024; 199 | uint32_t* buf = (uint32_t*) calloc(1, buf_size); 200 | esp_fill_random(buf, buf_size); 201 | const size_t file_size = 1 * 1024 * 1024; 202 | 203 | sdmmc_speed_test(buf, 4 * 1024, file_size, true); 204 | sdmmc_speed_test(buf, 8 * 1024, file_size, true); 205 | sdmmc_speed_test(buf, 16 * 1024, file_size, true); 206 | 207 | sdmmc_speed_test(buf, 4 * 1024, file_size, false); 208 | sdmmc_speed_test(buf, 8 * 1024, file_size, false); 209 | sdmmc_speed_test(buf, 16 * 1024, file_size, false); 210 | 211 | free(buf); 212 | 213 | HEAP_SIZE_CHECK(heap_size, 0); 214 | } 215 | 216 | static void sdmmc_speed_test(void *buf, size_t buf_size, size_t file_size, bool write) 217 | { 218 | sdmmc_host_t host = SDMMC_HOST_DEFAULT(); 219 | host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; 220 | sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); 221 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 222 | .format_if_mount_failed = write, 223 | .max_files = 5, 224 | .allocation_unit_size = 64 * 1024 225 | }; 226 | TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL)); 227 | 228 | test_fatfs_rw_speed("/sdcard/4mb.bin", buf, buf_size, file_size, write); 229 | 230 | TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount()); 231 | } 232 | 233 | TEST_CASE("(SD) mount two FAT partitions, SDMMC and WL, at the same time", "[fatfs][sd][test_env=UT_T1_SDMODE][timeout=60]") 234 | { 235 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 236 | .format_if_mount_failed = true, 237 | .max_files = 5 238 | }; 239 | 240 | const char* filename_sd = "/sdcard/sd.txt"; 241 | const char* filename_wl = "/spiflash/wl.txt"; 242 | const char* str_sd = "this is sd\n"; 243 | const char* str_wl = "this is spiflash\n"; 244 | 245 | /* Erase flash before the firs use */ 246 | const esp_partition_t *test_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "flash_test"); 247 | esp_partition_erase_range(test_partition, 0, test_partition->size); 248 | printf("Partition erased: addr- 0x%08x, size- 0x%08x\n", test_partition->address, test_partition->size); 249 | 250 | /* Mount FATFS in SD can WL at the same time. Create a file on each FS */ 251 | wl_handle_t wl_handle = WL_INVALID_HANDLE; 252 | test_setup(); 253 | TEST_ESP_OK(esp_vfs_fat_spiflash_mount("/spiflash", NULL, &mount_config, &wl_handle)); 254 | unlink(filename_sd); 255 | unlink(filename_wl); 256 | test_fatfs_create_file_with_text(filename_sd, str_sd); 257 | test_fatfs_create_file_with_text(filename_wl, str_wl); 258 | TEST_ESP_OK(esp_vfs_fat_spiflash_unmount("/spiflash", wl_handle)); 259 | test_teardown(); 260 | 261 | /* Check that the file "sd.txt" was created on FS in SD, and has the right data */ 262 | test_setup(); 263 | TEST_ASSERT_NULL(fopen(filename_wl, "r")); 264 | FILE* f = fopen(filename_sd, "r"); 265 | TEST_ASSERT_NOT_NULL(f); 266 | char buf[64]; 267 | TEST_ASSERT_NOT_NULL(fgets(buf, sizeof(buf) - 1, f)); 268 | TEST_ASSERT_EQUAL(0, strcmp(buf, str_sd)); 269 | fclose(f); 270 | test_teardown(); 271 | 272 | /* Check that the file "wl.txt" was created on FS in WL, and has the right data */ 273 | TEST_ESP_OK(esp_vfs_fat_spiflash_mount("/spiflash", NULL, &mount_config, &wl_handle)); 274 | TEST_ASSERT_NULL(fopen(filename_sd, "r")); 275 | f = fopen(filename_wl, "r"); 276 | TEST_ASSERT_NOT_NULL(f); 277 | TEST_ASSERT_NOT_NULL(fgets(buf, sizeof(buf) - 1, f)); 278 | TEST_ASSERT_EQUAL(0, strcmp(buf, str_wl)); 279 | fclose(f); 280 | TEST_ESP_OK(esp_vfs_fat_spiflash_unmount("/spiflash", wl_handle)); 281 | } 282 | 283 | /* 284 | * In FatFs menuconfig, set CONFIG_FATFS_API_ENCODING to UTF-8 and set the 285 | * Codepage to CP936 (Simplified Chinese) in order to run the following tests. 286 | * Ensure that the text editor is UTF-8 compatible when compiling these tests. 287 | */ 288 | #if defined(CONFIG_FATFS_API_ENCODING_UTF_8) && (CONFIG_FATFS_CODEPAGE == 936) 289 | 290 | static const char* test_filename_utf_8 = "/sdcard/测试文件.txt"; 291 | 292 | TEST_CASE("(SD) can read file using UTF-8 encoded strings", "[fatfs][sd][test_env=UT_T1_SDMODE]") 293 | { 294 | test_setup(); 295 | test_fatfs_create_file_with_text(test_filename_utf_8, fatfs_test_hello_str_utf); 296 | test_fatfs_read_file_utf_8(test_filename_utf_8); 297 | test_teardown(); 298 | } 299 | 300 | TEST_CASE("(SD) opendir, readdir, rewinddir, seekdir work as expected using UTF-8 encoded strings", "[fatfs][ignore]") 301 | { 302 | test_setup(); 303 | test_fatfs_opendir_readdir_rewinddir_utf_8("/sdcard/目录"); 304 | test_teardown(); 305 | } 306 | #endif // CONFIG_FATFS_API_ENCODING_UTF_8 && CONFIG_FATFS_CODEPAGE == 936 307 | 308 | #endif //SDMMC HOST SUPPORTED 309 | 310 | #if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3) 311 | //no runners 312 | static void sdspi_speed_test(void *buf, size_t buf_size, size_t file_size, bool write); 313 | 314 | TEST_CASE("(SDSPI) write/read speed test", "[fatfs][sd][test_env=UT_T1_SPIMODE][timeout=60]") 315 | { 316 | size_t heap_size; 317 | HEAP_SIZE_CAPTURE(heap_size); 318 | 319 | const size_t buf_size = 16 * 1024; 320 | uint32_t* buf = (uint32_t*) calloc(1, buf_size); 321 | esp_fill_random(buf, buf_size); 322 | const size_t file_size = 1 * 1024 * 1024; 323 | 324 | spi_bus_config_t bus_cfg = { 325 | .mosi_io_num = SDSPI_MOSI_PIN, 326 | .miso_io_num = SDSPI_MISO_PIN, 327 | .sclk_io_num = SDSPI_CLK_PIN, 328 | .quadwp_io_num = -1, 329 | .quadhd_io_num = -1, 330 | .max_transfer_sz = 4000, 331 | }; 332 | esp_err_t err = spi_bus_initialize(SDSPI_HOST_ID, &bus_cfg, 1); 333 | TEST_ESP_OK(err); 334 | 335 | sdspi_speed_test(buf, 4 * 1024, file_size, true); 336 | sdspi_speed_test(buf, 8 * 1024, file_size, true); 337 | sdspi_speed_test(buf, 16 * 1024, file_size, true); 338 | 339 | sdspi_speed_test(buf, 4 * 1024, file_size, false); 340 | sdspi_speed_test(buf, 8 * 1024, file_size, false); 341 | sdspi_speed_test(buf, 16 * 1024, file_size, false); 342 | 343 | free(buf); 344 | spi_bus_free(SDSPI_HOST_ID); 345 | 346 | HEAP_SIZE_CHECK(heap_size, 0); 347 | } 348 | 349 | static void sdspi_speed_test(void *buf, size_t buf_size, size_t file_size, bool write) 350 | { 351 | const char path[] = "/sdcard"; 352 | sdmmc_card_t *card; 353 | card = NULL; 354 | sdspi_device_config_t device_cfg = { 355 | .gpio_cs = SDSPI_CS_PIN, 356 | .host_id = SDSPI_HOST_ID, 357 | .gpio_cd = SDSPI_SLOT_NO_CD, 358 | .gpio_wp = SDSPI_SLOT_NO_WP, 359 | .gpio_int = SDSPI_SLOT_NO_INT, 360 | }; 361 | 362 | sdmmc_host_t host = SDSPI_HOST_DEFAULT(); 363 | host.slot = SDSPI_HOST_ID; 364 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 365 | .format_if_mount_failed = write, 366 | .max_files = 5, 367 | .allocation_unit_size = 64 * 1024 368 | }; 369 | TEST_ESP_OK(esp_vfs_fat_sdspi_mount(path, &host, &device_cfg, &mount_config, &card)); 370 | 371 | test_fatfs_rw_speed("/sdcard/4mb.bin", buf, buf_size, file_size, write); 372 | 373 | TEST_ESP_OK(esp_vfs_fat_sdcard_unmount(path, card)); 374 | } 375 | 376 | #endif //TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3) 377 | -------------------------------------------------------------------------------- /components/fatfs/vfs/vfs_fat_sdmmc.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include "esp_log.h" 18 | #include "esp_vfs.h" 19 | #include "esp_vfs_fat.h" 20 | #include "vfs_fat_internal.h" 21 | #include "driver/sdspi_host.h" 22 | #include "sdmmc_cmd.h" 23 | #include "diskio_impl.h" 24 | #include "diskio_sdmmc.h" 25 | #include "soc/soc_caps.h" 26 | #include "driver/sdmmc_defs.h" 27 | 28 | #if SOC_SDMMC_HOST_SUPPORTED 29 | #include "driver/sdmmc_host.h" 30 | #endif 31 | 32 | static const char* TAG = "vfs_fat_sdmmc"; 33 | static sdmmc_card_t* s_card = NULL; 34 | static uint8_t s_pdrv = FF_DRV_NOT_USED; 35 | static char * s_base_path = NULL; 36 | 37 | #define CHECK_EXECUTE_RESULT(err, str) do { \ 38 | if ((err) !=ESP_OK) { \ 39 | ESP_LOGE(TAG, str" (0x%x).", err); \ 40 | goto cleanup; \ 41 | } \ 42 | } while(0) 43 | 44 | static void call_host_deinit(const sdmmc_host_t *host_config); 45 | static esp_err_t partition_card(const esp_vfs_fat_mount_config_t *mount_config, 46 | const char *drv, sdmmc_card_t *card, BYTE pdrv); 47 | 48 | static esp_err_t mount_prepare_mem(const char *base_path, 49 | BYTE *out_pdrv, 50 | char **out_dup_path, 51 | sdmmc_card_t** out_card) 52 | { 53 | esp_err_t err = ESP_OK; 54 | char* dup_path = NULL; 55 | sdmmc_card_t* card = NULL; 56 | 57 | // connect SDMMC driver to FATFS 58 | BYTE pdrv = FF_DRV_NOT_USED; 59 | if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == FF_DRV_NOT_USED) { 60 | ESP_LOGD(TAG, "the maximum count of volumes is already mounted"); 61 | return ESP_ERR_NO_MEM; 62 | 63 | } 64 | 65 | // not using ff_memalloc here, as allocation in internal RAM is preferred 66 | card = (sdmmc_card_t*)malloc(sizeof(sdmmc_card_t)); 67 | if (card == NULL) { 68 | ESP_LOGD(TAG, "could not locate new sdmmc_card_t"); 69 | err = ESP_ERR_NO_MEM; 70 | goto cleanup; 71 | } 72 | 73 | dup_path = strdup(base_path); 74 | if(!dup_path){ 75 | ESP_LOGD(TAG, "could not copy base_path"); 76 | err = ESP_ERR_NO_MEM; 77 | goto cleanup; 78 | } 79 | 80 | *out_card = card; 81 | *out_pdrv = pdrv; 82 | *out_dup_path = dup_path; 83 | return ESP_OK; 84 | cleanup: 85 | free(card); 86 | free(dup_path); 87 | return err; 88 | } 89 | 90 | static esp_err_t mount_to_vfs_fat(const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t *card, uint8_t pdrv, 91 | const char *base_path) 92 | { 93 | FATFS* fs = NULL; 94 | esp_err_t err; 95 | ff_diskio_register_sdmmc(pdrv, card); 96 | ESP_LOGD(TAG, "using pdrv=%i", pdrv); 97 | char drv[3] = {(char)('0' + pdrv), ':', 0}; 98 | 99 | // connect FATFS to VFS 100 | err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs); 101 | if (err == ESP_ERR_INVALID_STATE) { 102 | // it's okay, already registered with VFS 103 | } else if (err != ESP_OK) { 104 | ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err); 105 | goto fail; 106 | } 107 | 108 | // Try to mount partition 109 | FRESULT res = f_mount(fs, drv, 1); 110 | if (res != FR_OK) { 111 | err = ESP_FAIL; 112 | ESP_LOGW(TAG, "failed to mount card (%d)", res); 113 | if (!((res == FR_NO_FILESYSTEM || res == FR_INT_ERR) 114 | && mount_config->format_if_mount_failed)) { 115 | goto fail; 116 | } 117 | 118 | err = partition_card(mount_config, drv, card, pdrv); 119 | if (err != ESP_OK) { 120 | goto fail; 121 | } 122 | 123 | ESP_LOGW(TAG, "mounting again"); 124 | res = f_mount(fs, drv, 0); 125 | if (res != FR_OK) { 126 | err = ESP_FAIL; 127 | ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res); 128 | goto fail; 129 | } 130 | } 131 | return ESP_OK; 132 | 133 | fail: 134 | if (fs) { 135 | f_mount(NULL, drv, 0); 136 | } 137 | esp_vfs_fat_unregister_path(base_path); 138 | ff_diskio_unregister(pdrv); 139 | return err; 140 | } 141 | 142 | static esp_err_t partition_card(const esp_vfs_fat_mount_config_t *mount_config, 143 | const char *drv, sdmmc_card_t *card, BYTE pdrv) 144 | { 145 | FRESULT res = FR_OK; 146 | esp_err_t err; 147 | const size_t workbuf_size = 4096; 148 | void* workbuf = NULL; 149 | ESP_LOGW(TAG, "partitioning card"); 150 | 151 | workbuf = ff_memalloc(workbuf_size); 152 | if (workbuf == NULL) { 153 | return ESP_ERR_NO_MEM; 154 | } 155 | 156 | DWORD plist[] = {100, 0, 0, 0}; 157 | res = f_fdisk(pdrv, plist, workbuf); 158 | if (res != FR_OK) { 159 | err = ESP_FAIL; 160 | ESP_LOGD(TAG, "f_fdisk failed (%d)", res); 161 | goto fail; 162 | } 163 | size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size( 164 | card->csd.sector_size, 165 | mount_config->allocation_unit_size); 166 | ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size); 167 | res = f_mkfs(drv, FM_ANY, alloc_unit_size, workbuf, workbuf_size); 168 | if (res != FR_OK) { 169 | err = ESP_FAIL; 170 | ESP_LOGD(TAG, "f_mkfs failed (%d)", res); 171 | goto fail; 172 | } 173 | 174 | free(workbuf); 175 | return ESP_OK; 176 | fail: 177 | free(workbuf); 178 | return err; 179 | } 180 | 181 | #if SOC_SDMMC_HOST_SUPPORTED 182 | static esp_err_t init_sdmmc_host(int slot, const void *slot_config, int *out_slot) 183 | { 184 | *out_slot = slot; 185 | return sdmmc_host_init_slot(slot, (const sdmmc_slot_config_t*) slot_config); 186 | } 187 | 188 | static esp_err_t init_sdspi_host_deprecated(int slot, const void *slot_config, int *out_slot) 189 | { 190 | *out_slot = slot; 191 | return sdspi_host_init_slot(slot, (const sdspi_slot_config_t*) slot_config); 192 | } 193 | 194 | esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, 195 | const sdmmc_host_t* host_config, 196 | const void* slot_config, 197 | const esp_vfs_fat_mount_config_t* mount_config, 198 | sdmmc_card_t** out_card) 199 | { 200 | esp_err_t err; 201 | int card_handle = -1; //uninitialized 202 | sdmmc_card_t* card = NULL; 203 | BYTE pdrv = FF_DRV_NOT_USED; 204 | char* dup_path = NULL; 205 | bool host_inited = false; 206 | 207 | err = mount_prepare_mem(base_path, &pdrv, &dup_path, &card); 208 | if (err != ESP_OK) { 209 | ESP_LOGE(TAG, "mount_prepare failed"); 210 | return err; 211 | } 212 | 213 | if (host_config->flags == SDMMC_HOST_FLAG_SPI) { 214 | //Deprecated API 215 | //the init() function is usually empty, doesn't require any deinit to revert it 216 | err = (*host_config->init)(); 217 | CHECK_EXECUTE_RESULT(err, "host init failed"); 218 | err = init_sdspi_host_deprecated(host_config->slot, slot_config, &card_handle); 219 | CHECK_EXECUTE_RESULT(err, "slot init failed"); 220 | //Set `host_inited` to true to indicate that host_config->deinit() needs 221 | //to be called to revert `init_sdspi_host_deprecated`; set `card_handle` 222 | //to -1 to indicate that no other deinit is required. 223 | host_inited = true; 224 | card_handle = -1; 225 | } else { 226 | err = (*host_config->init)(); 227 | CHECK_EXECUTE_RESULT(err, "host init failed"); 228 | //deinit() needs to be called to revert the init 229 | host_inited = true; 230 | //If this failed (indicated by card_handle != -1), slot deinit needs to called() 231 | //leave card_handle as is to indicate that (though slot deinit not implemented yet. 232 | err = init_sdmmc_host(host_config->slot, slot_config, &card_handle); 233 | CHECK_EXECUTE_RESULT(err, "slot init failed"); 234 | } 235 | 236 | // probe and initialize card 237 | err = sdmmc_card_init(host_config, card); 238 | CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed"); 239 | 240 | err = mount_to_vfs_fat(mount_config, card, pdrv, dup_path); 241 | CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed"); 242 | 243 | if (out_card != NULL) { 244 | *out_card = card; 245 | } 246 | if (s_card == NULL) { 247 | //store the ctx locally to be back-compatible 248 | s_card = card; 249 | s_pdrv = pdrv; 250 | s_base_path = dup_path; 251 | } else { 252 | free(dup_path); 253 | } 254 | return ESP_OK; 255 | cleanup: 256 | if (host_inited) { 257 | call_host_deinit(host_config); 258 | } 259 | free(card); 260 | free(dup_path); 261 | return err; 262 | } 263 | #endif 264 | 265 | static esp_err_t init_sdspi_host(int slot, const void *slot_config, int *out_slot) 266 | { 267 | esp_err_t err = sdspi_host_init_device((const sdspi_device_config_t*)slot_config, out_slot); 268 | if (err != ESP_OK) { 269 | ESP_LOGE(TAG, 270 | "Failed to attach sdspi device onto an SPI bus (rc=0x%x), please initialize the \ 271 | bus first and check the device parameters." 272 | , err); 273 | } 274 | return err; 275 | } 276 | 277 | esp_err_t esp_vfs_fat_sdspi_mount(const char* base_path, 278 | const sdmmc_host_t* host_config_input, 279 | const sdspi_device_config_t* slot_config, 280 | const esp_vfs_fat_mount_config_t* mount_config, 281 | sdmmc_card_t** out_card) 282 | { 283 | const sdmmc_host_t* host_config = host_config_input; 284 | esp_err_t err; 285 | int card_handle = -1; //uninitialized 286 | bool host_inited = false; 287 | BYTE pdrv = FF_DRV_NOT_USED; 288 | sdmmc_card_t* card = NULL; 289 | char* dup_path = NULL; 290 | 291 | err = mount_prepare_mem(base_path, &pdrv, &dup_path, &card); 292 | if (err != ESP_OK) { 293 | ESP_LOGE(TAG, "mount_prepare failed"); 294 | return err; 295 | } 296 | 297 | //the init() function is usually empty, doesn't require any deinit to revert it 298 | err = (*host_config->init)(); 299 | CHECK_EXECUTE_RESULT(err, "host init failed"); 300 | 301 | err = init_sdspi_host(host_config->slot, slot_config, &card_handle); 302 | CHECK_EXECUTE_RESULT(err, "slot init failed"); 303 | //Set `host_inited` to true to indicate that host_config->deinit() needs 304 | //to be called to revert `init_sdspi_host` 305 | host_inited = true; 306 | 307 | /* 308 | * The `slot` argument inside host_config should be replaced by the SD SPI handled returned 309 | * above. But the input pointer is const, so create a new variable. 310 | */ 311 | sdmmc_host_t new_config; 312 | if (card_handle != host_config->slot) { 313 | new_config = *host_config_input; 314 | host_config = &new_config; 315 | new_config.slot = card_handle; 316 | } 317 | 318 | // probe and initialize card 319 | err = sdmmc_card_init(host_config, card); 320 | CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed"); 321 | 322 | err = mount_to_vfs_fat(mount_config, card, pdrv, dup_path); 323 | CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed"); 324 | 325 | if (out_card != NULL) { 326 | *out_card = card; 327 | } 328 | if (s_card == NULL) { 329 | //store the ctx locally to be back-compatible 330 | s_card = card; 331 | s_pdrv = pdrv; 332 | s_base_path = dup_path; 333 | } else { 334 | free(dup_path); 335 | } 336 | return ESP_OK; 337 | 338 | cleanup: 339 | if (host_inited) { 340 | call_host_deinit(host_config); 341 | } 342 | free(card); 343 | free(dup_path); 344 | return err; 345 | 346 | } 347 | 348 | static void local_card_remove(void) 349 | { 350 | s_card = NULL; 351 | free(s_base_path); 352 | s_base_path = NULL; 353 | s_pdrv = FF_DRV_NOT_USED; 354 | } 355 | 356 | static void call_host_deinit(const sdmmc_host_t *host_config) 357 | { 358 | if (host_config->flags & SDMMC_HOST_FLAG_DEINIT_ARG) { 359 | host_config->deinit_p(host_config->slot); 360 | } else { 361 | host_config->deinit(); 362 | } 363 | } 364 | 365 | static esp_err_t unmount_card_core(const char *base_path, sdmmc_card_t *card) 366 | { 367 | BYTE pdrv = ff_diskio_get_pdrv_card(card); 368 | if (pdrv == 0xff) { 369 | return ESP_ERR_INVALID_ARG; 370 | } 371 | 372 | // unmount 373 | char drv[3] = {(char)('0' + pdrv), ':', 0}; 374 | f_mount(0, drv, 0); 375 | // release SD driver 376 | ff_diskio_unregister(pdrv); 377 | 378 | call_host_deinit(&card->host); 379 | free(card); 380 | 381 | esp_err_t err = esp_vfs_fat_unregister_path(base_path); 382 | return err; 383 | } 384 | 385 | esp_err_t esp_vfs_fat_sdmmc_unmount(void) 386 | { 387 | sdmmc_card_t* card = s_card; 388 | esp_err_t err = unmount_card_core(s_base_path, card); 389 | local_card_remove(); 390 | return err; 391 | } 392 | 393 | esp_err_t esp_vfs_fat_sdcard_unmount(const char *base_path, sdmmc_card_t *card) 394 | { 395 | esp_err_t err = unmount_card_core(base_path, card); 396 | if (s_card == card) { 397 | local_card_remove(); 398 | } 399 | return err; 400 | } 401 | -------------------------------------------------------------------------------- /components/fatfs/src/ffconf.h: -------------------------------------------------------------------------------- 1 | #include "sdkconfig.h" 2 | 3 | /*---------------------------------------------------------------------------/ 4 | / FatFs Functional Configurations 5 | /---------------------------------------------------------------------------*/ 6 | 7 | #define FFCONF_DEF 86604 /* Revision ID */ 8 | 9 | /*---------------------------------------------------------------------------/ 10 | / Function Configurations 11 | /---------------------------------------------------------------------------*/ 12 | 13 | #define FF_FS_READONLY 0 14 | /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) 15 | / Read-only configuration removes writing API functions, f_write(), f_sync(), 16 | / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 17 | / and optional writing functions as well. */ 18 | 19 | 20 | #define FF_FS_MINIMIZE 0 21 | /* This option defines minimization level to remove some basic API functions. 22 | / 23 | / 0: Basic functions are fully enabled. 24 | / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() 25 | / are removed. 26 | / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. 27 | / 3: f_lseek() function is removed in addition to 2. */ 28 | 29 | 30 | #define FF_USE_STRFUNC 0 31 | /* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf(). 32 | / 33 | / 0: Disable string functions. 34 | / 1: Enable without LF-CRLF conversion. 35 | / 2: Enable with LF-CRLF conversion. */ 36 | 37 | 38 | #define FF_USE_FIND 0 39 | /* This option switches filtered directory read functions, f_findfirst() and 40 | / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ 41 | 42 | 43 | #define FF_USE_MKFS 1 44 | /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ 45 | 46 | 47 | #define FF_USE_FASTSEEK CONFIG_FATFS_USE_FASTSEEK 48 | /* This option switches fast seek function. (0:Disable or 1:Enable) */ 49 | 50 | 51 | #define FF_USE_EXPAND 0 52 | /* This option switches f_expand function. (0:Disable or 1:Enable) */ 53 | 54 | 55 | #define FF_USE_CHMOD 1 56 | /* This option switches attribute manipulation functions, f_chmod() and f_utime(). 57 | / (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ 58 | 59 | 60 | #define FF_USE_LABEL 0 61 | /* This option switches volume label functions, f_getlabel() and f_setlabel(). 62 | / (0:Disable or 1:Enable) */ 63 | 64 | 65 | #define FF_USE_FORWARD 0 66 | /* This option switches f_forward() function. (0:Disable or 1:Enable) */ 67 | 68 | 69 | /*---------------------------------------------------------------------------/ 70 | / Locale and Namespace Configurations 71 | /---------------------------------------------------------------------------*/ 72 | 73 | #define FF_CODE_PAGE CONFIG_FATFS_CODEPAGE 74 | /* This option specifies the OEM code page to be used on the target system. 75 | / Incorrect code page setting can cause a file open failure. 76 | / 77 | / 437 - U.S. 78 | / 720 - Arabic 79 | / 737 - Greek 80 | / 771 - KBL 81 | / 775 - Baltic 82 | / 850 - Latin 1 83 | / 852 - Latin 2 84 | / 855 - Cyrillic 85 | / 857 - Turkish 86 | / 860 - Portuguese 87 | / 861 - Icelandic 88 | / 862 - Hebrew 89 | / 863 - Canadian French 90 | / 864 - Arabic 91 | / 865 - Nordic 92 | / 866 - Russian 93 | / 869 - Greek 2 94 | / 932 - Japanese (DBCS) 95 | / 936 - Simplified Chinese (DBCS) 96 | / 949 - Korean (DBCS) 97 | / 950 - Traditional Chinese (DBCS) 98 | / 0 - Include all code pages above and configured by f_setcp() 99 | */ 100 | 101 | 102 | #if defined(CONFIG_FATFS_LFN_STACK) 103 | #define FF_USE_LFN 2 104 | #elif defined(CONFIG_FATFS_LFN_HEAP) 105 | #define FF_USE_LFN 3 106 | #else /* CONFIG_FATFS_LFN_NONE */ 107 | #define FF_USE_LFN 0 108 | #endif 109 | 110 | #ifdef CONFIG_FATFS_MAX_LFN 111 | #define FF_MAX_LFN CONFIG_FATFS_MAX_LFN 112 | #endif 113 | 114 | /* The FF_USE_LFN switches the support for LFN (long file name). 115 | / 116 | / 0: Disable LFN. FF_MAX_LFN has no effect. 117 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 118 | / 2: Enable LFN with dynamic working buffer on the STACK. 119 | / 3: Enable LFN with dynamic working buffer on the HEAP. 120 | / 121 | / To enable the LFN, ffunicode.c needs to be added to the project. The LFN function 122 | / requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and 123 | / additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. 124 | / The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can 125 | / be in range of 12 to 255. It is recommended to be set 255 to fully support LFN 126 | / specification. 127 | / When use stack for the working buffer, take care on stack overflow. When use heap 128 | / memory for the working buffer, memory management functions, ff_memalloc() and 129 | / ff_memfree() in ffsystem.c, need to be added to the project. */ 130 | 131 | 132 | #ifdef CONFIG_FATFS_API_ENCODING_UTF_8 133 | #define FF_LFN_UNICODE 2 134 | #elif defined(CONFIG_FATFS_API_ENCODING_UTF_16) 135 | #define FF_LFN_UNICODE 1 136 | #else /* CONFIG_FATFS_API_ENCODING_ANSI_OEM */ 137 | #define FF_LFN_UNICODE 0 138 | #endif 139 | /* This option switches the character encoding on the API when LFN is enabled. 140 | / 141 | / 0: ANSI/OEM in current CP (TCHAR = char) 142 | / 1: Unicode in UTF-16 (TCHAR = WCHAR) 143 | / 2: Unicode in UTF-8 (TCHAR = char) 144 | / 3: Unicode in UTF-32 (TCHAR = DWORD) 145 | / 146 | / Also behavior of string I/O functions will be affected by this option. 147 | / When LFN is not enabled, this option has no effect. */ 148 | 149 | 150 | #define FF_LFN_BUF 255 151 | #define FF_SFN_BUF 12 152 | /* This set of options defines size of file name members in the FILINFO structure 153 | / which is used to read out directory items. These values should be suffcient for 154 | / the file names to read. The maximum possible length of the read file name depends 155 | / on character encoding. When LFN is not enabled, these options have no effect. */ 156 | 157 | 158 | #define FF_STRF_ENCODE 3 159 | /* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(), 160 | / f_putc(), f_puts and f_printf() convert the character encoding in it. 161 | / This option selects assumption of character encoding ON THE FILE to be 162 | / read/written via those functions. 163 | / 164 | / 0: ANSI/OEM in current CP 165 | / 1: Unicode in UTF-16LE 166 | / 2: Unicode in UTF-16BE 167 | / 3: Unicode in UTF-8 168 | */ 169 | 170 | 171 | #define FF_FS_RPATH 0 172 | /* This option configures support for relative path. 173 | / 174 | / 0: Disable relative path and remove related functions. 175 | / 1: Enable relative path. f_chdir() and f_chdrive() are available. 176 | / 2: f_getcwd() function is available in addition to 1. 177 | */ 178 | 179 | 180 | /*---------------------------------------------------------------------------/ 181 | / Drive/Volume Configurations 182 | /---------------------------------------------------------------------------*/ 183 | 184 | #define FF_VOLUMES 2 185 | /* Number of volumes (logical drives) to be used. (1-10) */ 186 | 187 | 188 | #define FF_STR_VOLUME_ID 0 189 | #define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" 190 | /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. 191 | / When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive 192 | / number in the path name. FF_VOLUME_STRS defines the volume ID strings for each 193 | / logical drives. Number of items must not be less than FF_VOLUMES. Valid 194 | / characters for the volume ID strings are A-Z, a-z and 0-9, however, they are 195 | / compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is 196 | / not defined, a user defined volume string table needs to be defined as: 197 | / 198 | / const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... 199 | */ 200 | 201 | 202 | #define FF_MULTI_PARTITION 1 203 | /* This option switches support for multiple volumes on the physical drive. 204 | / By default (0), each logical drive number is bound to the same physical drive 205 | / number and only an FAT volume found on the physical drive will be mounted. 206 | / When this function is enabled (1), each logical drive number can be bound to 207 | / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() 208 | / funciton will be available. */ 209 | 210 | /* SD card sector size */ 211 | #define FF_SS_SDCARD 512 212 | /* wear_levelling library sector size */ 213 | #define FF_SS_WL CONFIG_WL_SECTOR_SIZE 214 | 215 | #define FF_MIN_SS MIN(FF_SS_SDCARD, FF_SS_WL) 216 | #define FF_MAX_SS MAX(FF_SS_SDCARD, FF_SS_WL) 217 | /* This set of options configures the range of sector size to be supported. (512, 218 | / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and 219 | / harddisk. But a larger value may be required for on-board flash memory and some 220 | / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured 221 | / for variable sector size mode and disk_ioctl() function needs to implement 222 | / GET_SECTOR_SIZE command. */ 223 | 224 | 225 | #define FF_USE_TRIM 0 226 | /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) 227 | / To enable Trim function, also CTRL_TRIM command should be implemented to the 228 | / disk_ioctl() function. */ 229 | 230 | 231 | #define FF_FS_NOFSINFO 0 232 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 233 | / option, and f_getfree() function at first time after volume mount will force 234 | / a full FAT scan. Bit 1 controls the use of last allocated cluster number. 235 | / 236 | / bit0=0: Use free cluster count in the FSINFO if available. 237 | / bit0=1: Do not trust free cluster count in the FSINFO. 238 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 239 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 240 | */ 241 | 242 | 243 | 244 | /*---------------------------------------------------------------------------/ 245 | / System Configurations 246 | /---------------------------------------------------------------------------*/ 247 | 248 | #define FF_FS_TINY (!CONFIG_FATFS_PER_FILE_CACHE) 249 | /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) 250 | / At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. 251 | / Instead of private sector buffer eliminated from the file object, common sector 252 | / buffer in the filesystem object (FATFS) is used for the file data transfer. */ 253 | 254 | 255 | #define FF_FS_EXFAT 1 256 | /* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) 257 | / To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) 258 | / Note that enabling exFAT discards ANSI C (C89) compatibility. */ 259 | 260 | 261 | #define FF_FS_NORTC 0 262 | #define FF_NORTC_MON 1 263 | #define FF_NORTC_MDAY 1 264 | #define FF_NORTC_YEAR 2018 265 | /* The option FF_FS_NORTC switches timestamp functiton. If the system does not have 266 | / any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable 267 | / the timestamp function. Every object modified by FatFs will have a fixed timestamp 268 | / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. 269 | / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be 270 | / added to the project to read current time form real-time clock. FF_NORTC_MON, 271 | / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. 272 | / These options have no effect at read-only configuration (FF_FS_READONLY = 1). */ 273 | 274 | 275 | #define FF_FS_LOCK CONFIG_FATFS_FS_LOCK 276 | /* The option FF_FS_LOCK switches file lock function to control duplicated file open 277 | / and illegal operation to open objects. This option must be 0 when FF_FS_READONLY 278 | / is 1. 279 | / 280 | / 0: Disable file lock function. To avoid volume corruption, application program 281 | / should avoid illegal open, remove and rename to the open objects. 282 | / >0: Enable file lock function. The value defines how many files/sub-directories 283 | / can be opened simultaneously under file lock control. Note that the file 284 | / lock control is independent of re-entrancy. */ 285 | 286 | 287 | #define FF_FS_REENTRANT 1 288 | #define FF_FS_TIMEOUT (CONFIG_FATFS_TIMEOUT_MS / portTICK_PERIOD_MS) 289 | #define FF_SYNC_t SemaphoreHandle_t 290 | /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs 291 | / module itself. Note that regardless of this option, file access to different 292 | / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() 293 | / and f_fdisk() function, are always not re-entrant. Only file/directory access 294 | / to the same volume is under control of this function. 295 | / 296 | / 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. 297 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 298 | / ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() 299 | / function, must be added to the project. Samples are available in 300 | / option/syscall.c. 301 | / 302 | / The FF_FS_TIMEOUT defines timeout period in unit of time tick. 303 | / The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, 304 | / SemaphoreHandle_t and etc. A header file for O/S definitions needs to be 305 | / included somewhere in the scope of ff.h. */ 306 | 307 | #include 308 | #include "freertos/FreeRTOS.h" 309 | #include "freertos/semphr.h" 310 | 311 | /* Some memory allocation functions are declared here in addition to ff.h, so that 312 | they can be used also by external code when LFN feature is disabled. 313 | */ 314 | void* ff_memalloc (unsigned msize); 315 | void ff_memfree(void*); 316 | 317 | 318 | /*--- End of configuration options ---*/ 319 | 320 | /* Redefine names of disk IO functions to prevent name collisions */ 321 | #define disk_initialize ff_disk_initialize 322 | #define disk_status ff_disk_status 323 | #define disk_read ff_disk_read 324 | #define disk_write ff_disk_write 325 | #define disk_ioctl ff_disk_ioctl 326 | --------------------------------------------------------------------------------