mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-05-23 10:08:06 +08:00
Merge branch 'feature/add_fatfs' into 'master'
feat(fatfs): add FATFS for ESP8266 See merge request sdk/ESP8266_RTOS_SDK!1319
This commit is contained in:
179
components/esp8266/include/driver/sdmmc_types.h
Normal file
179
components/esp8266/include/driver/sdmmc_types.h
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
* Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SDMMC_TYPES_H_
|
||||
#define _SDMMC_TYPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
/**
|
||||
* Decoded values from SD card Card Specific Data register
|
||||
*/
|
||||
typedef struct {
|
||||
int csd_ver; /*!< CSD structure format */
|
||||
int mmc_ver; /*!< MMC version (for CID format) */
|
||||
int capacity; /*!< total number of sectors */
|
||||
int sector_size; /*!< sector size in bytes */
|
||||
int read_block_len; /*!< block length for reads */
|
||||
int card_command_class; /*!< Card Command Class for SD */
|
||||
int tr_speed; /*!< Max transfer speed */
|
||||
} sdmmc_csd_t;
|
||||
|
||||
/**
|
||||
* Decoded values from SD card Card IDentification register
|
||||
*/
|
||||
typedef struct {
|
||||
int mfg_id; /*!< manufacturer identification number */
|
||||
int oem_id; /*!< OEM/product identification number */
|
||||
char name[8]; /*!< product name (MMC v1 has the longest) */
|
||||
int revision; /*!< product revision */
|
||||
int serial; /*!< product serial number */
|
||||
int date; /*!< manufacturing date */
|
||||
} sdmmc_cid_t;
|
||||
|
||||
/**
|
||||
* Decoded values from SD Configuration Register
|
||||
*/
|
||||
typedef struct {
|
||||
int sd_spec; /*!< SD Physical layer specification version, reported by card */
|
||||
int bus_width; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */
|
||||
} sdmmc_scr_t;
|
||||
|
||||
/**
|
||||
* Decoded values of Extended Card Specific Data
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t power_class; /*!< Power class used by the card */
|
||||
} sdmmc_ext_csd_t;
|
||||
|
||||
/**
|
||||
* SD/MMC command response buffer
|
||||
*/
|
||||
typedef uint32_t sdmmc_response_t[4];
|
||||
|
||||
/**
|
||||
* SD SWITCH_FUNC response buffer
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t data[512 / 8 / sizeof(uint32_t)]; /*!< response data */
|
||||
} sdmmc_switch_func_rsp_t;
|
||||
|
||||
/**
|
||||
* SD/MMC command information
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t opcode; /*!< SD or MMC command index */
|
||||
uint32_t arg; /*!< SD/MMC command argument */
|
||||
sdmmc_response_t response; /*!< response buffer */
|
||||
void* data; /*!< buffer to send or read into */
|
||||
size_t datalen; /*!< length of data buffer */
|
||||
size_t blklen; /*!< block length */
|
||||
int flags; /*!< see below */
|
||||
/** @cond */
|
||||
#define SCF_ITSDONE 0x0001 /*!< command is complete */
|
||||
#define SCF_CMD(flags) ((flags) & 0x00f0)
|
||||
#define SCF_CMD_AC 0x0000
|
||||
#define SCF_CMD_ADTC 0x0010
|
||||
#define SCF_CMD_BC 0x0020
|
||||
#define SCF_CMD_BCR 0x0030
|
||||
#define SCF_CMD_READ 0x0040 /*!< read command (data expected) */
|
||||
#define SCF_RSP_BSY 0x0100
|
||||
#define SCF_RSP_136 0x0200
|
||||
#define SCF_RSP_CRC 0x0400
|
||||
#define SCF_RSP_IDX 0x0800
|
||||
#define SCF_RSP_PRESENT 0x1000
|
||||
/* response types */
|
||||
#define SCF_RSP_R0 0 /*!< none */
|
||||
#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
#define SCF_RSP_R1B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
|
||||
#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136)
|
||||
#define SCF_RSP_R3 (SCF_RSP_PRESENT)
|
||||
#define SCF_RSP_R4 (SCF_RSP_PRESENT)
|
||||
#define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
|
||||
#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
/* special flags */
|
||||
#define SCF_WAIT_BUSY 0x2000 /*!< Wait for completion of card busy signal before returning */
|
||||
/** @endcond */
|
||||
esp_err_t error; /*!< error returned from transfer */
|
||||
int timeout_ms; /*!< response timeout, in milliseconds */
|
||||
} sdmmc_command_t;
|
||||
|
||||
/**
|
||||
* SD/MMC Host description
|
||||
*
|
||||
* This structure defines properties of SD/MMC host and functions
|
||||
* of SD/MMC host which can be used by upper layers.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t flags; /*!< flags defining host properties */
|
||||
#define SDMMC_HOST_FLAG_1BIT BIT(0) /*!< host supports 1-line SD and MMC protocol */
|
||||
#define SDMMC_HOST_FLAG_4BIT BIT(1) /*!< host supports 4-line SD and MMC protocol */
|
||||
#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */
|
||||
#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */
|
||||
#define SDMMC_HOST_FLAG_DDR BIT(4) /*!< host supports DDR mode for SD/MMC */
|
||||
int slot; /*!< slot number, to be passed to host functions */
|
||||
int max_freq_khz; /*!< max frequency supported by the host */
|
||||
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
|
||||
#define SDMMC_FREQ_HIGHSPEED 40000 /*!< SD High speed (limited by clock divider) */
|
||||
#define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */
|
||||
#define SDMMC_FREQ_52M 52000 /*!< MMC 52MHz speed */
|
||||
#define SDMMC_FREQ_26M 26000 /*!< MMC 26MHz speed */
|
||||
float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */
|
||||
esp_err_t (*init)(void); /*!< Host function to initialize the driver */
|
||||
esp_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */
|
||||
size_t (*get_bus_width)(int slot); /*!< host function to get bus width */
|
||||
esp_err_t (*set_bus_ddr_mode)(int slot, bool ddr_enable); /*!< host function to set DDR mode */
|
||||
esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */
|
||||
esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */
|
||||
esp_err_t (*deinit)(void); /*!< host function to deinitialize the driver */
|
||||
esp_err_t (*io_int_enable)(int slot); /*!< Host function to enable SDIO interrupt line */
|
||||
esp_err_t (*io_int_wait)(int slot, TickType_t timeout_ticks); /*!< Host function to wait for SDIO interrupt line to be active */
|
||||
int command_timeout_ms; /*!< timeout, in milliseconds, of a single command. Set to 0 to use the default value. */
|
||||
} sdmmc_host_t;
|
||||
|
||||
/**
|
||||
* SD/MMC card information structure
|
||||
*/
|
||||
typedef struct {
|
||||
sdmmc_host_t host; /*!< Host with which the card is associated */
|
||||
uint32_t ocr; /*!< OCR (Operation Conditions Register) value */
|
||||
union {
|
||||
sdmmc_cid_t cid; /*!< decoded CID (Card IDentification) register value */
|
||||
sdmmc_response_t raw_cid; /*!< raw CID of MMC card to be decoded
|
||||
after the CSD is fetched in the data transfer mode*/
|
||||
};
|
||||
sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
|
||||
sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
|
||||
sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */
|
||||
uint16_t rca; /*!< RCA (Relative Card Address) */
|
||||
uint16_t max_freq_khz; /*!< Maximum frequency, in kHz, supported by the card */
|
||||
uint32_t is_mem : 1; /*!< Bit indicates if the card is a memory card */
|
||||
uint32_t is_sdio : 1; /*!< Bit indicates if the card is an IO card */
|
||||
uint32_t is_mmc : 1; /*!< Bit indicates if the card is MMC */
|
||||
uint32_t num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */
|
||||
uint32_t log_bus_width : 2; /*!< log2(bus width supported by card) */
|
||||
uint32_t is_ddr : 1; /*!< Card supports DDR mode */
|
||||
uint32_t reserved : 23; /*!< Reserved for future expansion */
|
||||
} sdmmc_card_t;
|
||||
|
||||
|
||||
#endif // _SDMMC_TYPES_H_
|
186
components/esp8266/include/driver/sdspi_host.h
Normal file
186
components/esp8266/include/driver/sdspi_host.h
Normal file
@ -0,0 +1,186 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "sdmmc_types.h"
|
||||
#include "driver/gpio.h"
|
||||
// #include "driver/spi_master.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default sdmmc_host_t structure initializer for SD over SPI driver
|
||||
*
|
||||
* Uses SPI mode and max frequency set to 20MHz
|
||||
*
|
||||
* 'slot' can be set to one of HSPI_HOST, VSPI_HOST.
|
||||
*/
|
||||
/*#define SDSPI_HOST_DEFAULT() {\
|
||||
.flags = SDMMC_HOST_FLAG_SPI, \
|
||||
.slot = HSPI_HOST, \
|
||||
.max_freq_khz = SDMMC_FREQ_DEFAULT, \
|
||||
.io_voltage = 3.3f, \
|
||||
.init = &sdspi_host_init, \
|
||||
.set_bus_width = NULL, \
|
||||
.get_bus_width = NULL, \
|
||||
.set_bus_ddr_mode = NULL, \
|
||||
.set_card_clk = &sdspi_host_set_card_clk, \
|
||||
.do_transaction = &sdspi_host_do_transaction, \
|
||||
.deinit = &sdspi_host_deinit, \
|
||||
.io_int_enable = &sdspi_host_io_int_enable, \
|
||||
.io_int_wait = &sdspi_host_io_int_wait, \
|
||||
.command_timeout_ms = 0, \
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Extra configuration for SPI host
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_num_t gpio_miso; ///< GPIO number of MISO signal
|
||||
gpio_num_t gpio_mosi; ///< GPIO number of MOSI signal
|
||||
gpio_num_t gpio_sck; ///< GPIO number of SCK signal
|
||||
gpio_num_t gpio_cs; ///< GPIO number of CS signal
|
||||
gpio_num_t gpio_cd; ///< GPIO number of card detect signal
|
||||
gpio_num_t gpio_wp; ///< GPIO number of write protect signal
|
||||
gpio_num_t gpio_int; ///< GPIO number of interrupt line (input) for SDIO card.
|
||||
int dma_channel; ///< DMA channel to be used by SPI driver (1 or 2)
|
||||
} sdspi_slot_config_t;
|
||||
|
||||
#define SDSPI_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used
|
||||
#define SDSPI_SLOT_NO_WP GPIO_NUM_NC ///< indicates that write protect line is not used
|
||||
#define SDSPI_SLOT_NO_INT GPIO_NUM_NC ///< indicates that interrupt line is not used
|
||||
|
||||
/**
|
||||
* Macro defining default configuration of SPI host
|
||||
*/
|
||||
#define SDSPI_SLOT_CONFIG_DEFAULT() {\
|
||||
.gpio_miso = GPIO_NUM_2, \
|
||||
.gpio_mosi = GPIO_NUM_15, \
|
||||
.gpio_sck = GPIO_NUM_14, \
|
||||
.gpio_cs = GPIO_NUM_13, \
|
||||
.gpio_cd = SDSPI_SLOT_NO_CD, \
|
||||
.gpio_wp = SDSPI_SLOT_NO_WP, \
|
||||
.gpio_int = GPIO_NUM_NC, \
|
||||
.dma_channel = 1 \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize SD SPI driver
|
||||
*
|
||||
* @note This function is not thread safe
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - other error codes may be returned in future versions
|
||||
*/
|
||||
esp_err_t sdspi_host_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize SD SPI driver for the specific SPI controller
|
||||
*
|
||||
* @note This function is not thread safe
|
||||
*
|
||||
* @note The SDIO over sdspi needs an extra interrupt line. Call ``gpio_install_isr_service()`` before this function.
|
||||
*
|
||||
* @param slot SPI controller to use (HSPI_HOST or VSPI_HOST)
|
||||
* @param slot_config pointer to slot configuration structure
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if sdspi_init_slot has invalid arguments
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - other errors from the underlying spi_master and gpio drivers
|
||||
*/
|
||||
esp_err_t sdspi_host_init_slot(int slot, const sdspi_slot_config_t* slot_config);
|
||||
|
||||
/**
|
||||
* @brief Send command to the card and get response
|
||||
*
|
||||
* This function returns when command is sent and response is received,
|
||||
* or data is transferred, or timeout occurs.
|
||||
*
|
||||
* @note This function is not thread safe w.r.t. init/deinit functions,
|
||||
* and bus width/clock speed configuration functions. Multiple tasks
|
||||
* can call sdspi_host_do_transaction as long as other sdspi_host_*
|
||||
* functions are not called.
|
||||
*
|
||||
* @param slot SPI controller (HSPI_HOST or VSPI_HOST)
|
||||
* @param cmdinfo pointer to structure describing command and data to transfer
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_TIMEOUT if response or data transfer has timed out
|
||||
* - ESP_ERR_INVALID_CRC if response or data transfer CRC check has failed
|
||||
* - ESP_ERR_INVALID_RESPONSE if the card has sent an invalid response
|
||||
*/
|
||||
esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo);
|
||||
|
||||
/**
|
||||
* @brief Set card clock frequency
|
||||
*
|
||||
* Currently only integer fractions of 40MHz clock can be used.
|
||||
* For High Speed cards, 40MHz can be used.
|
||||
* For Default Speed cards, 20MHz can be used.
|
||||
*
|
||||
* @note This function is not thread safe
|
||||
*
|
||||
* @param slot SPI controller (HSPI_HOST or VSPI_HOST)
|
||||
* @param freq_khz card clock frequency, in kHz
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - other error codes may be returned in the future
|
||||
*/
|
||||
esp_err_t sdspi_host_set_card_clk(int slot, uint32_t freq_khz);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Release resources allocated using sdspi_host_init
|
||||
*
|
||||
* @note This function is not thread safe
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if sdspi_host_init function has not been called
|
||||
*/
|
||||
esp_err_t sdspi_host_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Enable SDIO interrupt.
|
||||
*
|
||||
* @param slot SPI controller to use (HSPI_HOST or VSPI_HOST)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t sdspi_host_io_int_enable(int slot);
|
||||
|
||||
/**
|
||||
* @brief Wait for SDIO interrupt until timeout.
|
||||
*
|
||||
* @param slot SPI controller to use (HSPI_HOST or VSPI_HOST)
|
||||
* @param timeout_ticks Ticks to wait before timeout.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t sdspi_host_io_int_wait(int slot, TickType_t timeout_ticks);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
33
components/esp_common/include/esp_compiler.h
Normal file
33
components/esp_common/include/esp_compiler.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2016-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef __ESP_COMPILER_H
|
||||
#define __ESP_COMPILER_H
|
||||
|
||||
/*
|
||||
* The likely and unlikely macro pairs:
|
||||
* These macros are useful to place when application
|
||||
* knows the majority ocurrence of a decision paths,
|
||||
* placing one of these macros can hint the compiler
|
||||
* to reorder instructions producing more optimized
|
||||
* code.
|
||||
*/
|
||||
#if (CONFIG_COMPILER_OPTIMIZATION_PERF)
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
#endif
|
17
components/fatfs/CMakeLists.txt
Normal file
17
components/fatfs/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
set(srcs "diskio/diskio.c"
|
||||
"diskio/diskio_rawflash.c"
|
||||
"diskio/diskio_wl.c"
|
||||
"src/ff.c"
|
||||
"port/freertos/ffsystem.c"
|
||||
"src/ffunicode.c"
|
||||
"vfs/vfs_fat.c"
|
||||
"vfs/vfs_fat_spiflash.c")
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32")
|
||||
list(APPEND srcs "vfs/vfs_fat_sdmmc.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS diskio vfs src
|
||||
REQUIRES wear_levelling
|
||||
)
|
183
components/fatfs/Kconfig
Normal file
183
components/fatfs/Kconfig
Normal file
@ -0,0 +1,183 @@
|
||||
menu "FAT Filesystem support"
|
||||
|
||||
choice FATFS_CHOOSE_CODEPAGE
|
||||
prompt "OEM Code Page"
|
||||
default FATFS_CODEPAGE_437
|
||||
help
|
||||
OEM code page used for file name encodings.
|
||||
|
||||
If "Dynamic" is selected, code page can be chosen at runtime using
|
||||
f_setcp function. Note that choosing this option will increase
|
||||
application size by ~480kB.
|
||||
|
||||
config FATFS_CODEPAGE_DYNAMIC
|
||||
bool "Dynamic (all code pages supported)"
|
||||
config FATFS_CODEPAGE_437
|
||||
bool "US (CP437)"
|
||||
config FATFS_CODEPAGE_720
|
||||
bool "Arabic (CP720)"
|
||||
config FATFS_CODEPAGE_737
|
||||
bool "Greek (CP737)"
|
||||
config FATFS_CODEPAGE_771
|
||||
bool "KBL (CP771)"
|
||||
config FATFS_CODEPAGE_775
|
||||
bool "Baltic (CP775)"
|
||||
config FATFS_CODEPAGE_850
|
||||
bool "Latin 1 (CP850)"
|
||||
config FATFS_CODEPAGE_852
|
||||
bool "Latin 2 (CP852)"
|
||||
config FATFS_CODEPAGE_855
|
||||
bool "Cyrillic (CP855)"
|
||||
config FATFS_CODEPAGE_857
|
||||
bool "Turkish (CP857)"
|
||||
config FATFS_CODEPAGE_860
|
||||
bool "Portugese (CP860)"
|
||||
config FATFS_CODEPAGE_861
|
||||
bool "Icelandic (CP861)"
|
||||
config FATFS_CODEPAGE_862
|
||||
bool "Hebrew (CP862)"
|
||||
config FATFS_CODEPAGE_863
|
||||
bool "Canadian French (CP863)"
|
||||
config FATFS_CODEPAGE_864
|
||||
bool "Arabic (CP864)"
|
||||
config FATFS_CODEPAGE_865
|
||||
bool "Nordic (CP865)"
|
||||
config FATFS_CODEPAGE_866
|
||||
bool "Russian (CP866)"
|
||||
config FATFS_CODEPAGE_869
|
||||
bool "Greek 2 (CP869)"
|
||||
config FATFS_CODEPAGE_932
|
||||
bool "Japanese (DBCS) (CP932)"
|
||||
config FATFS_CODEPAGE_936
|
||||
bool "Simplified Chinese (DBCS) (CP936)"
|
||||
config FATFS_CODEPAGE_949
|
||||
bool "Korean (DBCS) (CP949)"
|
||||
config FATFS_CODEPAGE_950
|
||||
bool "Traditional Chinese (DBCS) (CP950)"
|
||||
|
||||
endchoice
|
||||
|
||||
config FATFS_CODEPAGE
|
||||
int
|
||||
default 0 if FATFS_CODEPAGE_DYNAMIC
|
||||
default 437 if FATFS_CODEPAGE_437
|
||||
default 720 if FATFS_CODEPAGE_720
|
||||
default 737 if FATFS_CODEPAGE_737
|
||||
default 771 if FATFS_CODEPAGE_771
|
||||
default 775 if FATFS_CODEPAGE_775
|
||||
default 850 if FATFS_CODEPAGE_850
|
||||
default 852 if FATFS_CODEPAGE_852
|
||||
default 855 if FATFS_CODEPAGE_855
|
||||
default 857 if FATFS_CODEPAGE_857
|
||||
default 860 if FATFS_CODEPAGE_860
|
||||
default 861 if FATFS_CODEPAGE_861
|
||||
default 862 if FATFS_CODEPAGE_862
|
||||
default 863 if FATFS_CODEPAGE_863
|
||||
default 864 if FATFS_CODEPAGE_864
|
||||
default 865 if FATFS_CODEPAGE_865
|
||||
default 866 if FATFS_CODEPAGE_866
|
||||
default 869 if FATFS_CODEPAGE_869
|
||||
default 932 if FATFS_CODEPAGE_932
|
||||
default 936 if FATFS_CODEPAGE_936
|
||||
default 949 if FATFS_CODEPAGE_949
|
||||
default 950 if FATFS_CODEPAGE_950
|
||||
default 437
|
||||
|
||||
choice FATFS_LONG_FILENAMES
|
||||
prompt "Long filename support"
|
||||
default FATFS_LFN_NONE
|
||||
help
|
||||
Support long filenames in FAT. Long filename data increases
|
||||
memory usage. FATFS can be configured to store the buffer for
|
||||
long filename data in stack or heap.
|
||||
|
||||
config FATFS_LFN_NONE
|
||||
bool "No long filenames"
|
||||
config FATFS_LFN_HEAP
|
||||
bool "Long filename buffer in heap"
|
||||
config FATFS_LFN_STACK
|
||||
bool "Long filename buffer on stack"
|
||||
endchoice
|
||||
|
||||
config FATFS_MAX_LFN
|
||||
int "Max long filename length"
|
||||
depends on !FATFS_LFN_NONE
|
||||
default 255
|
||||
range 12 255
|
||||
help
|
||||
Maximum long filename length. Can be reduced to save RAM.
|
||||
|
||||
choice FATFS_API_ENCODING
|
||||
prompt "API character encoding"
|
||||
depends on !FATFS_LFN_NONE
|
||||
default FATFS_API_ENCODING_ANSI_OEM
|
||||
help
|
||||
Choose encoding for character and string arguments/returns when using
|
||||
FATFS APIs. The encoding of arguments will usually depend on text
|
||||
editor settings.
|
||||
|
||||
config FATFS_API_ENCODING_ANSI_OEM
|
||||
bool "API uses ANSI/OEM encoding"
|
||||
config FATFS_API_ENCODING_UTF_16
|
||||
bool "API uses UTF-16 encoding"
|
||||
config FATFS_API_ENCODING_UTF_8
|
||||
bool "API uses UTF-8 encoding"
|
||||
endchoice
|
||||
|
||||
config FATFS_FS_LOCK
|
||||
int "Number of simultaneously open files protected by lock function"
|
||||
default 0
|
||||
range 0 65535
|
||||
help
|
||||
This option sets the FATFS configuration value _FS_LOCK.
|
||||
The option _FS_LOCK switches file lock function to control duplicated file open
|
||||
and illegal operation to open objects.
|
||||
|
||||
* 0: Disable file lock function. To avoid volume corruption, application
|
||||
should avoid illegal open, remove and rename to the open objects.
|
||||
|
||||
* >0: Enable file lock function. The value defines how many files/sub-directories
|
||||
can be opened simultaneously under file lock control.
|
||||
|
||||
Note that the file lock control is independent of re-entrancy.
|
||||
|
||||
config FATFS_TIMEOUT_MS
|
||||
int "Timeout for acquiring a file lock, ms"
|
||||
default 10000
|
||||
help
|
||||
This option sets FATFS configuration value _FS_TIMEOUT, scaled to milliseconds.
|
||||
Sets the number of milliseconds FATFS will wait to acquire a mutex when
|
||||
operating on an open file. For example, if one task is performing a lenghty
|
||||
operation, another task will wait for the first task to release the lock,
|
||||
and time out after amount of time set by this option.
|
||||
|
||||
|
||||
config FATFS_PER_FILE_CACHE
|
||||
bool "Use separate cache for each file"
|
||||
default y
|
||||
help
|
||||
This option affects FATFS configuration value _FS_TINY.
|
||||
|
||||
If this option is set, _FS_TINY is 0, and each open file has its own cache,
|
||||
size of the cache is equal to the _MAX_SS variable (512 or 4096 bytes).
|
||||
This option uses more RAM if more than 1 file is open, but needs less reads
|
||||
and writes to the storage for some operations.
|
||||
|
||||
If this option is not set, _FS_TINY is 1, and single cache is used for
|
||||
all open files, size is also equal to _MAX_SS variable. This reduces the
|
||||
amount of heap used when multiple files are open, but increases the number
|
||||
of read and write operations which FATFS needs to make.
|
||||
|
||||
|
||||
config FATFS_ALLOC_PREFER_EXTRAM
|
||||
bool "Perfer external RAM when allocating FATFS buffers"
|
||||
default y
|
||||
depends on SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC
|
||||
help
|
||||
When the option is enabled, internal buffers used by FATFS will be allocated
|
||||
from external RAM. If the allocation from external RAM fails, the buffer will
|
||||
be allocated from the internal RAM.
|
||||
Disable this option if optimizing for performance. Enable this option if
|
||||
optimizing for internal memory size.
|
||||
|
||||
endmenu
|
3
components/fatfs/component.mk
Normal file
3
components/fatfs/component.mk
Normal file
@ -0,0 +1,3 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := diskio vfs src
|
||||
COMPONENT_SRCDIRS := diskio vfs port/freertos src
|
||||
COMPONENT_OBJEXCLUDE := src/diskio.o src/ffsystem.o diskio/diskio_sdmmc.o vfs/vfs_fat_sdmmc.o
|
93
components/fatfs/diskio/diskio.c
Normal file
93
components/fatfs/diskio/diskio.c
Normal file
@ -0,0 +1,93 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */
|
||||
/* ESP-IDF port Copyright 2016 Espressif Systems (Shanghai) PTE LTD */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include "diskio_impl.h"
|
||||
#include "ffconf.h"
|
||||
#include "ff.h"
|
||||
|
||||
static ff_diskio_impl_t * s_impls[FF_VOLUMES] = { NULL };
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
PARTITION VolToPart[] = {
|
||||
{0, 0}, /* Logical drive 0 ==> Physical drive 0, auto detection */
|
||||
{1, 0} /* Logical drive 1 ==> Physical drive 1, auto detection */
|
||||
};
|
||||
#endif
|
||||
|
||||
esp_err_t ff_diskio_get_drive(BYTE* out_pdrv)
|
||||
{
|
||||
BYTE i;
|
||||
for(i=0; i<FF_VOLUMES; i++) {
|
||||
if (!s_impls[i]) {
|
||||
*out_pdrv = i;
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl)
|
||||
{
|
||||
assert(pdrv < FF_VOLUMES);
|
||||
|
||||
if (s_impls[pdrv]) {
|
||||
ff_diskio_impl_t* im = s_impls[pdrv];
|
||||
s_impls[pdrv] = NULL;
|
||||
free(im);
|
||||
}
|
||||
|
||||
if (!discio_impl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ff_diskio_impl_t * impl = (ff_diskio_impl_t *)malloc(sizeof(ff_diskio_impl_t));
|
||||
assert(impl != NULL);
|
||||
memcpy(impl, discio_impl, sizeof(ff_diskio_impl_t));
|
||||
s_impls[pdrv] = impl;
|
||||
}
|
||||
|
||||
DSTATUS ff_disk_initialize (BYTE pdrv)
|
||||
{
|
||||
return s_impls[pdrv]->init(pdrv);
|
||||
}
|
||||
DSTATUS ff_disk_status (BYTE pdrv)
|
||||
{
|
||||
return s_impls[pdrv]->status(pdrv);
|
||||
}
|
||||
DRESULT ff_disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
return s_impls[pdrv]->read(pdrv, buff, sector, count);
|
||||
}
|
||||
DRESULT ff_disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
return s_impls[pdrv]->write(pdrv, buff, sector, count);
|
||||
}
|
||||
DRESULT ff_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff)
|
||||
{
|
||||
return s_impls[pdrv]->ioctl(pdrv, cmd, buff);
|
||||
}
|
||||
|
||||
DWORD get_fattime(void)
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
struct tm tmr;
|
||||
localtime_r(&t, &tmr);
|
||||
int year = tmr.tm_year < 80 ? 0 : tmr.tm_year - 80;
|
||||
return ((DWORD)(year) << 25)
|
||||
| ((DWORD)(tmr.tm_mon + 1) << 21)
|
||||
| ((DWORD)tmr.tm_mday << 16)
|
||||
| (WORD)(tmr.tm_hour << 11)
|
||||
| (WORD)(tmr.tm_min << 5)
|
||||
| (WORD)(tmr.tm_sec >> 1);
|
||||
}
|
71
components/fatfs/diskio/diskio_impl.h
Normal file
71
components/fatfs/diskio/diskio_impl.h
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT;
|
||||
typedef unsigned char BYTE;
|
||||
typedef uint32_t DWORD;
|
||||
|
||||
#include "diskio.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* Structure of pointers to disk IO driver functions.
|
||||
*
|
||||
* See FatFs documentation for details about these functions
|
||||
*/
|
||||
typedef struct {
|
||||
DSTATUS (*init) (unsigned char pdrv); /*!< disk initialization function */
|
||||
DSTATUS (*status) (unsigned char pdrv); /*!< disk status check function */
|
||||
DRESULT (*read) (unsigned char pdrv, unsigned char* buff, uint32_t sector, unsigned count); /*!< sector read function */
|
||||
DRESULT (*write) (unsigned char pdrv, const unsigned char* buff, uint32_t sector, unsigned count); /*!< sector write function */
|
||||
DRESULT (*ioctl) (unsigned char pdrv, unsigned char cmd, void* buff); /*!< function to get info about disk and do some misc operations */
|
||||
} ff_diskio_impl_t;
|
||||
|
||||
/**
|
||||
* Register or unregister diskio driver for given drive number.
|
||||
*
|
||||
* When FATFS library calls one of disk_xxx functions for driver number pdrv,
|
||||
* corresponding function in discio_impl for given pdrv will be called.
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param discio_impl pointer to ff_diskio_impl_t structure with diskio functions
|
||||
* or NULL to unregister and free previously registered drive
|
||||
*/
|
||||
void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl);
|
||||
|
||||
#define ff_diskio_unregister(pdrv_) ff_diskio_register(pdrv_, NULL)
|
||||
|
||||
|
||||
/**
|
||||
* Get next available drive number
|
||||
*
|
||||
* @param out_pdrv pointer to the byte to set if successful
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_NOT_FOUND if all drives are attached
|
||||
*/
|
||||
esp_err_t ff_diskio_get_drive(BYTE* out_pdrv);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
105
components/fatfs/diskio/diskio_rawflash.c
Normal file
105
components/fatfs/diskio/diskio_rawflash.c
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include "diskio_impl.h"
|
||||
#include "ffconf.h"
|
||||
#include "ff.h"
|
||||
#include "esp_log.h"
|
||||
#include "diskio_rawflash.h"
|
||||
#include "esp_compiler.h"
|
||||
|
||||
static const char* TAG = "diskio_rawflash";
|
||||
|
||||
const esp_partition_t* ff_raw_handles[FF_VOLUMES];
|
||||
|
||||
|
||||
DSTATUS ff_raw_initialize (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DSTATUS ff_raw_status (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_raw_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
ESP_LOGV(TAG, "ff_raw_read - pdrv=%i, sector=%i, count=%in", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count);
|
||||
const esp_partition_t* part = ff_raw_handles[pdrv];
|
||||
assert(part);
|
||||
esp_err_t err = esp_partition_read(part, sector * SPI_FLASH_SEC_SIZE, buff, count * SPI_FLASH_SEC_SIZE);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "esp_partition_read failed (0x%x)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
||||
DRESULT ff_raw_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
DRESULT ff_raw_ioctl (BYTE pdrv, BYTE cmd, void *buff)
|
||||
{
|
||||
const esp_partition_t* part = ff_raw_handles[pdrv];
|
||||
ESP_LOGV(TAG, "ff_raw_ioctl: cmd=%in", cmd);
|
||||
assert(part);
|
||||
switch (cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD *) buff) = part->size / SPI_FLASH_SEC_SIZE;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD *) buff) = SPI_FLASH_SEC_SIZE;
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ff_diskio_register_raw_partition(BYTE pdrv, const esp_partition_t* part_handle)
|
||||
{
|
||||
if (pdrv >= FF_VOLUMES) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
static const ff_diskio_impl_t raw_impl = {
|
||||
.init = &ff_raw_initialize,
|
||||
.status = &ff_raw_status,
|
||||
.read = &ff_raw_read,
|
||||
.write = &ff_raw_write,
|
||||
.ioctl = &ff_raw_ioctl
|
||||
};
|
||||
ff_diskio_register(pdrv, &raw_impl);
|
||||
ff_raw_handles[pdrv] = part_handle;
|
||||
return ESP_OK;
|
||||
|
||||
}
|
||||
|
||||
|
||||
BYTE ff_diskio_get_pdrv_raw(const esp_partition_t* part_handle)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (part_handle == ff_raw_handles[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
37
components/fatfs/diskio/diskio_rawflash.h
Normal file
37
components/fatfs/diskio/diskio_rawflash.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _DISKIO_RAWFLASH_DEFINED
|
||||
#define _DISKIO_RAWFLASH_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_partition.h"
|
||||
|
||||
/**
|
||||
* Register spi flash partition
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param part_handle pointer to raw flash partition.
|
||||
*/
|
||||
esp_err_t ff_diskio_register_raw_partition(unsigned char pdrv, const esp_partition_t* part_handle);
|
||||
unsigned char ff_diskio_get_pdrv_raw(const esp_partition_t* part_handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _DISKIO_RAWFLASH_DEFINED
|
91
components/fatfs/diskio/diskio_sdmmc.c
Normal file
91
components/fatfs/diskio/diskio_sdmmc.c
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "diskio_impl.h"
|
||||
#include "ffconf.h"
|
||||
#include "ff.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_compiler.h"
|
||||
|
||||
static sdmmc_card_t* s_cards[FF_VOLUMES] = { NULL };
|
||||
|
||||
static const char* TAG = "diskio_sdmmc";
|
||||
|
||||
DSTATUS ff_sdmmc_initialize (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DSTATUS ff_sdmmc_status (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_read_sectors(card, buff, sector, count);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_write_sectors(card, buff, sector, count);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
switch(cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD*) buff) = card->csd.capacity;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD*) buff) = card->csd.sector_size;
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
void ff_diskio_register_sdmmc(BYTE pdrv, sdmmc_card_t* card)
|
||||
{
|
||||
static const ff_diskio_impl_t sdmmc_impl = {
|
||||
.init = &ff_sdmmc_initialize,
|
||||
.status = &ff_sdmmc_status,
|
||||
.read = &ff_sdmmc_read,
|
||||
.write = &ff_sdmmc_write,
|
||||
.ioctl = &ff_sdmmc_ioctl
|
||||
};
|
||||
s_cards[pdrv] = card;
|
||||
ff_diskio_register(pdrv, &sdmmc_impl);
|
||||
}
|
||||
|
35
components/fatfs/diskio/diskio_sdmmc.h
Normal file
35
components/fatfs/diskio/diskio_sdmmc.h
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Register SD/MMC diskio driver
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param card pointer to sdmmc_card_t structure describing a card; card should be initialized before calling f_mount.
|
||||
*/
|
||||
void ff_diskio_register_sdmmc(unsigned char pdrv, sdmmc_card_t* card);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
127
components/fatfs/diskio/diskio_wl.c
Normal file
127
components/fatfs/diskio/diskio_wl.c
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include "diskio_impl.h"
|
||||
#include "ffconf.h"
|
||||
#include "ff.h"
|
||||
#include "esp_log.h"
|
||||
#include "diskio_wl.h"
|
||||
#include "wear_levelling.h"
|
||||
#include "esp_compiler.h"
|
||||
|
||||
static const char* TAG = "ff_diskio_spiflash";
|
||||
|
||||
wl_handle_t ff_wl_handles[FF_VOLUMES] = {
|
||||
WL_INVALID_HANDLE,
|
||||
WL_INVALID_HANDLE,
|
||||
};
|
||||
|
||||
DSTATUS ff_wl_initialize (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DSTATUS ff_wl_status (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_wl_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
ESP_LOGV(TAG, "ff_wl_read - pdrv=%i, sector=%i, count=%i\n", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count);
|
||||
wl_handle_t wl_handle = ff_wl_handles[pdrv];
|
||||
assert(wl_handle + 1);
|
||||
esp_err_t err = wl_read(wl_handle, sector * wl_sector_size(wl_handle), buff, count * wl_sector_size(wl_handle));
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "wl_read failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_wl_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
ESP_LOGV(TAG, "ff_wl_write - pdrv=%i, sector=%i, count=%i\n", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count);
|
||||
wl_handle_t wl_handle = ff_wl_handles[pdrv];
|
||||
assert(wl_handle + 1);
|
||||
esp_err_t err = wl_erase_range(wl_handle, sector * wl_sector_size(wl_handle), count * wl_sector_size(wl_handle));
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "wl_erase_range failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
err = wl_write(wl_handle, sector * wl_sector_size(wl_handle), buff, count * wl_sector_size(wl_handle));
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "wl_write failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_wl_ioctl (BYTE pdrv, BYTE cmd, void *buff)
|
||||
{
|
||||
wl_handle_t wl_handle = ff_wl_handles[pdrv];
|
||||
ESP_LOGV(TAG, "ff_wl_ioctl: cmd=%i\n", cmd);
|
||||
assert(wl_handle + 1);
|
||||
switch (cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD *) buff) = wl_size(wl_handle) / wl_sector_size(wl_handle);
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD *) buff) = wl_sector_size(wl_handle);
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ff_diskio_register_wl_partition(BYTE pdrv, wl_handle_t flash_handle)
|
||||
{
|
||||
if (pdrv >= FF_VOLUMES) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
static const ff_diskio_impl_t wl_impl = {
|
||||
.init = &ff_wl_initialize,
|
||||
.status = &ff_wl_status,
|
||||
.read = &ff_wl_read,
|
||||
.write = &ff_wl_write,
|
||||
.ioctl = &ff_wl_ioctl
|
||||
};
|
||||
ff_wl_handles[pdrv] = flash_handle;
|
||||
ff_diskio_register(pdrv, &wl_impl);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
BYTE ff_diskio_get_pdrv_wl(wl_handle_t flash_handle)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (flash_handle == ff_wl_handles[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
void ff_diskio_clear_pdrv_wl(wl_handle_t flash_handle)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (flash_handle == ff_wl_handles[i]) {
|
||||
ff_wl_handles[i] = WL_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
}
|
39
components/fatfs/diskio/diskio_wl.h
Normal file
39
components/fatfs/diskio/diskio_wl.h
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _DISKIO_WL_DEFINED
|
||||
#define _DISKIO_WL_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "wear_levelling.h"
|
||||
|
||||
|
||||
/**
|
||||
* Register spi flash partition
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param flash_handle handle of the wear levelling partition.
|
||||
*/
|
||||
esp_err_t ff_diskio_register_wl_partition(unsigned char pdrv, wl_handle_t flash_handle);
|
||||
unsigned char ff_diskio_get_pdrv_wl(wl_handle_t flash_handle);
|
||||
void ff_diskio_clear_pdrv_wl(wl_handle_t flash_handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _DISKIO_WL_DEFINED
|
108
components/fatfs/port/freertos/ffsystem.c
Normal file
108
components/fatfs/port/freertos/ffsystem.c
Normal file
@ -0,0 +1,108 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Sample Code of OS Dependent Functions for FatFs */
|
||||
/* (C)ChaN, 2017 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "ff.h"
|
||||
#include "sdkconfig.h"
|
||||
#ifdef CONFIG_FATFS_ALLOC_EXTRAM_FIRST
|
||||
#include "esp_heap_caps.h"
|
||||
#endif
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null on not enough core) */
|
||||
unsigned msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
#ifdef CONFIG_FATFS_ALLOC_EXTRAM_FIRST
|
||||
return heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM,
|
||||
MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
|
||||
#else
|
||||
return malloc(msize);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Free a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free (nothing to do for null) */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block with POSIX API */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if FF_FS_REENTRANT /* Mutal exclusion */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Create a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to create a new
|
||||
/ synchronization object for the volume, such as semaphore and mutex.
|
||||
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
|
||||
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
|
||||
BYTE vol, /* Corresponding volume (logical drive number) */
|
||||
FF_SYNC_t *sobj /* Pointer to return the created sync object */
|
||||
)
|
||||
{
|
||||
*sobj = xSemaphoreCreateMutex();
|
||||
return (*sobj != NULL) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Delete a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to delete a synchronization
|
||||
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
|
||||
/ the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
|
||||
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
|
||||
)
|
||||
{
|
||||
vSemaphoreDelete(sobj);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Request Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on entering file functions to lock the volume.
|
||||
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
||||
*/
|
||||
|
||||
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
|
||||
FF_SYNC_t sobj /* Sync object to wait */
|
||||
)
|
||||
{
|
||||
return (xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Release Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on leaving file functions to unlock the volume.
|
||||
*/
|
||||
|
||||
void ff_rel_grant (
|
||||
FF_SYNC_t sobj /* Sync object to be signaled */
|
||||
)
|
||||
{
|
||||
xSemaphoreGive(sobj);
|
||||
}
|
||||
|
||||
#endif // FF_FS_REENTRANT
|
46
components/fatfs/port/linux/ffsystem.c
Normal file
46
components/fatfs/port/linux/ffsystem.c
Normal file
@ -0,0 +1,46 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* OS Dependent Functions for FatFs */
|
||||
/* (C)ChaN, 2018 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "ff.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* This is the implementation for host-side testing on Linux.
|
||||
* Host-side tests are single threaded, so lock functionality isn't needed.
|
||||
*/
|
||||
|
||||
void* ff_memalloc(UINT msize)
|
||||
{
|
||||
return malloc(msize);
|
||||
}
|
||||
|
||||
void ff_memfree(void* mblock)
|
||||
{
|
||||
free(mblock);
|
||||
}
|
||||
|
||||
/* 1:Function succeeded, 0:Could not create the sync object */
|
||||
int ff_cre_syncobj(BYTE vol, FF_SYNC_t* sobj)
|
||||
{
|
||||
*sobj = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 1:Function succeeded, 0:Could not delete due to an error */
|
||||
int ff_del_syncobj(FF_SYNC_t sobj)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 1:Function succeeded, 0:Could not acquire lock */
|
||||
int ff_req_grant (FF_SYNC_t sobj)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ff_rel_grant (FF_SYNC_t sobj)
|
||||
{
|
||||
}
|
||||
|
330
components/fatfs/src/00history.txt
Normal file
330
components/fatfs/src/00history.txt
Normal file
@ -0,0 +1,330 @@
|
||||
----------------------------------------------------------------------------
|
||||
Revision history of FatFs module
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
R0.00 (February 26, 2006)
|
||||
|
||||
Prototype.
|
||||
|
||||
|
||||
|
||||
R0.01 (April 29, 2006)
|
||||
|
||||
The first release.
|
||||
|
||||
|
||||
|
||||
R0.02 (June 01, 2006)
|
||||
|
||||
Added FAT12 support.
|
||||
Removed unbuffered mode.
|
||||
Fixed a problem on small (<32M) partition.
|
||||
|
||||
|
||||
|
||||
R0.02a (June 10, 2006)
|
||||
|
||||
Added a configuration option (_FS_MINIMUM).
|
||||
|
||||
|
||||
|
||||
R0.03 (September 22, 2006)
|
||||
|
||||
Added f_rename().
|
||||
Changed option _FS_MINIMUM to _FS_MINIMIZE.
|
||||
|
||||
|
||||
|
||||
R0.03a (December 11, 2006)
|
||||
|
||||
Improved cluster scan algorithm to write files fast.
|
||||
Fixed f_mkdir() creates incorrect directory on FAT32.
|
||||
|
||||
|
||||
|
||||
R0.04 (February 04, 2007)
|
||||
|
||||
Added f_mkfs().
|
||||
Supported multiple drive system.
|
||||
Changed some interfaces for multiple drive system.
|
||||
Changed f_mountdrv() to f_mount().
|
||||
|
||||
|
||||
|
||||
R0.04a (April 01, 2007)
|
||||
|
||||
Supported multiple partitions on a physical drive.
|
||||
Added a capability of extending file size to f_lseek().
|
||||
Added minimization level 3.
|
||||
Fixed an endian sensitive code in f_mkfs().
|
||||
|
||||
|
||||
|
||||
R0.04b (May 05, 2007)
|
||||
|
||||
Added a configuration option _USE_NTFLAG.
|
||||
Added FSINFO support.
|
||||
Fixed DBCS name can result FR_INVALID_NAME.
|
||||
Fixed short seek (<= csize) collapses the file object.
|
||||
|
||||
|
||||
|
||||
R0.05 (August 25, 2007)
|
||||
|
||||
Changed arguments of f_read(), f_write() and f_mkfs().
|
||||
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
|
||||
Fixed f_mkdir() on FAT32 creates incorrect directory.
|
||||
|
||||
|
||||
|
||||
R0.05a (February 03, 2008)
|
||||
|
||||
Added f_truncate() and f_utime().
|
||||
Fixed off by one error at FAT sub-type determination.
|
||||
Fixed btr in f_read() can be mistruncated.
|
||||
Fixed cached sector is not flushed when create and close without write.
|
||||
|
||||
|
||||
|
||||
R0.06 (April 01, 2008)
|
||||
|
||||
Added fputc(), fputs(), fprintf() and fgets().
|
||||
Improved performance of f_lseek() on moving to the same or following cluster.
|
||||
|
||||
|
||||
|
||||
R0.07 (April 01, 2009)
|
||||
|
||||
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
|
||||
Added long file name feature. (_USE_LFN)
|
||||
Added multiple code page feature. (_CODE_PAGE)
|
||||
Added re-entrancy for multitask operation. (_FS_REENTRANT)
|
||||
Added auto cluster size selection to f_mkfs().
|
||||
Added rewind option to f_readdir().
|
||||
Changed result code of critical errors.
|
||||
Renamed string functions to avoid name collision.
|
||||
|
||||
|
||||
|
||||
R0.07a (April 14, 2009)
|
||||
|
||||
Septemberarated out OS dependent code on reentrant cfg.
|
||||
Added multiple sector size feature.
|
||||
|
||||
|
||||
|
||||
R0.07c (June 21, 2009)
|
||||
|
||||
Fixed f_unlink() can return FR_OK on error.
|
||||
Fixed wrong cache control in f_lseek().
|
||||
Added relative path feature.
|
||||
Added f_chdir() and f_chdrive().
|
||||
Added proper case conversion to extended character.
|
||||
|
||||
|
||||
|
||||
R0.07e (November 03, 2009)
|
||||
|
||||
Septemberarated out configuration options from ff.h to ffconf.h.
|
||||
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
|
||||
Fixed name matching error on the 13 character boundary.
|
||||
Added a configuration option, _LFN_UNICODE.
|
||||
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
|
||||
|
||||
|
||||
|
||||
R0.08 (May 15, 2010)
|
||||
|
||||
Added a memory configuration option. (_USE_LFN = 3)
|
||||
Added file lock feature. (_FS_SHARE)
|
||||
Added fast seek feature. (_USE_FASTSEEK)
|
||||
Changed some types on the API, XCHAR->TCHAR.
|
||||
Changed .fname in the FILINFO structure on Unicode cfg.
|
||||
String functions support UTF-8 encoding files on Unicode cfg.
|
||||
|
||||
|
||||
|
||||
R0.08a (August 16, 2010)
|
||||
|
||||
Added f_getcwd(). (_FS_RPATH = 2)
|
||||
Added sector erase feature. (_USE_ERASE)
|
||||
Moved file lock semaphore table from fs object to the bss.
|
||||
Fixed f_mkfs() creates wrong FAT32 volume.
|
||||
|
||||
|
||||
|
||||
R0.08b (January 15, 2011)
|
||||
|
||||
Fast seek feature is also applied to f_read() and f_write().
|
||||
f_lseek() reports required table size on creating CLMP.
|
||||
Extended format syntax of f_printf().
|
||||
Ignores duplicated directory separators in given path name.
|
||||
|
||||
|
||||
|
||||
R0.09 (September 06, 2011)
|
||||
|
||||
f_mkfs() supports multiple partition to complete the multiple partition feature.
|
||||
Added f_fdisk().
|
||||
|
||||
|
||||
|
||||
R0.09a (August 27, 2012)
|
||||
|
||||
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
|
||||
Changed option name _FS_SHARE to _FS_LOCK.
|
||||
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
|
||||
|
||||
|
||||
|
||||
R0.09b (January 24, 2013)
|
||||
|
||||
Added f_setlabel() and f_getlabel().
|
||||
|
||||
|
||||
|
||||
R0.10 (October 02, 2013)
|
||||
|
||||
Added selection of character encoding on the file. (_STRF_ENCODE)
|
||||
Added f_closedir().
|
||||
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
|
||||
Added forced mount feature with changes of f_mount().
|
||||
Improved behavior of volume auto detection.
|
||||
Improved write throughput of f_puts() and f_printf().
|
||||
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
|
||||
Fixed f_write() can be truncated when the file size is close to 4GB.
|
||||
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error.
|
||||
|
||||
|
||||
|
||||
R0.10a (January 15, 2014)
|
||||
|
||||
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
|
||||
Added a configuration option of minimum sector size. (_MIN_SS)
|
||||
2nd argument of f_rename() can have a drive number and it will be ignored.
|
||||
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
|
||||
Fixed f_close() invalidates the file object without volume lock.
|
||||
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
|
||||
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10b (May 19, 2014)
|
||||
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10c (November 09, 2014)
|
||||
|
||||
Added a configuration option for the platforms without RTC. (_FS_NORTC)
|
||||
Changed option name _USE_ERASE to _USE_TRIM.
|
||||
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
|
||||
Fixed a potential problem of FAT access that can appear on disk error.
|
||||
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
|
||||
|
||||
|
||||
|
||||
R0.11 (February 09, 2015)
|
||||
|
||||
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
|
||||
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
|
||||
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
|
||||
|
||||
|
||||
|
||||
R0.11a (September 05, 2015)
|
||||
|
||||
Fixed wrong media change can lead a deadlock at thread-safe configuration.
|
||||
Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE)
|
||||
Removed some code pages actually not exist on the standard systems. (_CODE_PAGE)
|
||||
Fixed errors in the case conversion teble of code page 437 and 850 (ff.c).
|
||||
Fixed errors in the case conversion teble of Unicode (cc*.c).
|
||||
|
||||
|
||||
|
||||
R0.12 (April 12, 2016)
|
||||
|
||||
Added support for exFAT file system. (_FS_EXFAT)
|
||||
Added f_expand(). (_USE_EXPAND)
|
||||
Changed some members in FINFO structure and behavior of f_readdir().
|
||||
Added an option _USE_CHMOD.
|
||||
Removed an option _WORD_ACCESS.
|
||||
Fixed errors in the case conversion table of Unicode (cc*.c).
|
||||
|
||||
|
||||
|
||||
R0.12a (July 10, 2016)
|
||||
|
||||
Added support for creating exFAT volume with some changes of f_mkfs().
|
||||
Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed.
|
||||
f_forward() is available regardless of _FS_TINY.
|
||||
Fixed f_mkfs() creates wrong volume. (appeared at R0.12)
|
||||
Fixed wrong memory read in create_name(). (appeared at R0.12)
|
||||
Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD.
|
||||
|
||||
|
||||
|
||||
R0.12b (September 04, 2016)
|
||||
|
||||
Made f_rename() be able to rename objects with the same name but case.
|
||||
Fixed an error in the case conversion teble of code page 866. (ff.c)
|
||||
Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12)
|
||||
Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12)
|
||||
Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12)
|
||||
Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12)
|
||||
Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12)
|
||||
Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12)
|
||||
|
||||
|
||||
|
||||
R0.12c (March 04, 2017)
|
||||
|
||||
Improved write throughput at the fragmented file on the exFAT volume.
|
||||
Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN.
|
||||
Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12)
|
||||
Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c)
|
||||
|
||||
|
||||
|
||||
R0.13 (May 21, 2017)
|
||||
|
||||
Changed heading character of configuration keywords "_" to "FF_".
|
||||
Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead.
|
||||
Added f_setcp(), run-time code page configuration. (FF_CODE_PAGE = 0)
|
||||
Improved cluster allocation time on stretch a deep buried cluster chain.
|
||||
Improved processing time of f_mkdir() with large cluster size by using FF_USE_LFN = 3.
|
||||
Improved NoFatChain flag of the fragmented file to be set after it is truncated and got contiguous.
|
||||
Fixed archive attribute is left not set when a file on the exFAT volume is renamed. (appeared at R0.12)
|
||||
Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c)
|
||||
Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c)
|
||||
|
||||
|
||||
|
||||
R0.13a (October 14, 2017)
|
||||
|
||||
Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2)
|
||||
Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF).
|
||||
Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk().
|
||||
Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09)
|
||||
Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c)
|
||||
Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12)
|
||||
|
||||
|
||||
|
||||
R0.13b (April 07, 2018)
|
||||
|
||||
Added support for UTF-32 encoding on the API. (FF_LFN_UNICODE = 3)
|
||||
Added support for Unix style volume ID. (FF_STR_VOLUME_ID = 2)
|
||||
Fixed accesing any object on the exFAT root directory beyond the cluster boundary can fail. (appeared at R0.12c)
|
||||
Fixed f_setlabel() does not reject some invalid characters. (appeared at R0.09b)
|
||||
|
||||
|
||||
|
||||
R0.13c (October 14, 2018)
|
||||
Supported stdint.h for C99 and later. (integer.h was included in ff.h)
|
||||
Fixed reading a directory gets infinite loop when the last directory entry is not empty. (appeared at R0.12)
|
||||
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)
|
||||
Fixed f_getcwd() cause output buffer overrun when the buffer has a valid drive number. (appeared at R0.13b)
|
||||
|
21
components/fatfs/src/00readme.txt
Normal file
21
components/fatfs/src/00readme.txt
Normal file
@ -0,0 +1,21 @@
|
||||
FatFs Module Source Files R0.13c
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
00history.txt Revision history.
|
||||
ff.c FatFs module.
|
||||
ffconf.h Configuration file of FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
ffunicode.c Optional Unicode utility functions.
|
||||
ffsystem.c An example of optional O/S related functions.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and it does not depend on any specific
|
||||
storage device. You need to provide a low level disk I/O module written to
|
||||
control the storage device that attached to the target system.
|
||||
|
229
components/fatfs/src/diskio.c
Normal file
229
components/fatfs/src/diskio.c
Normal file
@ -0,0 +1,229 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include "ff.h" /* Obtains integer types */
|
||||
#include "diskio.h" /* Declarations of disk functions */
|
||||
|
||||
/* Definitions of physical drive number for each drive */
|
||||
#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
|
||||
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
|
||||
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_status (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
DSTATUS stat;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
result = RAM_disk_status();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_MMC :
|
||||
result = MMC_disk_status();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_USB :
|
||||
result = USB_disk_status();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
}
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Inidialize a Drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_initialize (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
DSTATUS stat;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
result = RAM_disk_initialize();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_MMC :
|
||||
result = MMC_disk_initialize();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_USB :
|
||||
result = USB_disk_initialize();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
}
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_read (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
DWORD sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
// translate the arguments here
|
||||
|
||||
result = RAM_disk_read(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_MMC :
|
||||
// translate the arguments here
|
||||
|
||||
result = MMC_disk_read(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_USB :
|
||||
// translate the arguments here
|
||||
|
||||
result = USB_disk_read(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#if FF_FS_READONLY == 0
|
||||
|
||||
DRESULT disk_write (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
DWORD sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
// translate the arguments here
|
||||
|
||||
result = RAM_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_MMC :
|
||||
// translate the arguments here
|
||||
|
||||
result = MMC_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_USB :
|
||||
// translate the arguments here
|
||||
|
||||
result = USB_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_ioctl (
|
||||
BYTE pdrv, /* Physical drive nmuber (0..) */
|
||||
BYTE cmd, /* Control code */
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
|
||||
// Process of the command for the RAM drive
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_MMC :
|
||||
|
||||
// Process of the command for the MMC/SD card
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_USB :
|
||||
|
||||
// Process of the command the USB drive
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
77
components/fatfs/src/diskio.h
Normal file
77
components/fatfs/src/diskio.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*-----------------------------------------------------------------------/
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2014 /
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
/* Results of Disk Functions */
|
||||
typedef enum {
|
||||
RES_OK = 0, /* 0: Successful */
|
||||
RES_ERROR, /* 1: R/W Error */
|
||||
RES_WRPRT, /* 2: Write Protected */
|
||||
RES_NOTRDY, /* 3: Not Ready */
|
||||
RES_PARERR /* 4: Invalid Parameter */
|
||||
} DRESULT;
|
||||
|
||||
|
||||
/*---------------------------------------*/
|
||||
/* Prototypes for disk control functions */
|
||||
|
||||
|
||||
DSTATUS disk_initialize (BYTE pdrv);
|
||||
DSTATUS disk_status (BYTE pdrv);
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||
#define STA_PROTECT 0x04 /* Write protected */
|
||||
|
||||
|
||||
/* Command code for disk_ioctrl fucntion */
|
||||
|
||||
/* Generic command (Used by FatFs) */
|
||||
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
|
||||
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
|
||||
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
|
||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
|
||||
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
|
||||
|
||||
/* Generic command (Not used by FatFs) */
|
||||
#define CTRL_POWER 5 /* Get/Set power status */
|
||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||
#define CTRL_EJECT 7 /* Eject media */
|
||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
#define MMC_GET_TYPE 10 /* Get card type */
|
||||
#define MMC_GET_CSD 11 /* Get CSD */
|
||||
#define MMC_GET_CID 12 /* Get CID */
|
||||
#define MMC_GET_OCR 13 /* Get OCR */
|
||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
|
||||
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
|
||||
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||
#define ATA_GET_MODEL 21 /* Get model name */
|
||||
#define ATA_GET_SN 22 /* Get serial number */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
6566
components/fatfs/src/ff.c
Normal file
6566
components/fatfs/src/ff.c
Normal file
File diff suppressed because it is too large
Load Diff
405
components/fatfs/src/ff.h
Normal file
405
components/fatfs/src/ff.h
Normal file
@ -0,0 +1,405 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.13c /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2018, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
/ that the following condition is met:
|
||||
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ and any warranties related to this software are DISCLAIMED.
|
||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||
/ by use of this software.
|
||||
/
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 86604 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
|
||||
#if FF_DEFINED != FFCONF_DEF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
|
||||
/* Integer types used for FatFs API */
|
||||
|
||||
#if defined(_WIN32) /* Main development platform */
|
||||
#define FF_INTDEF 2
|
||||
#include <windows.h>
|
||||
typedef unsigned __int64 QWORD;
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
|
||||
#define FF_INTDEF 2
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
||||
typedef uint16_t WCHAR; /* 16-bit unsigned integer */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
||||
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
||||
#else /* Earlier than C99 */
|
||||
#define FF_INTDEF 1
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef unsigned short WORD; /* 16-bit unsigned integer */
|
||||
typedef unsigned short WCHAR; /* 16-bit unsigned integer */
|
||||
typedef unsigned long DWORD; /* 32-bit unsigned integer */
|
||||
#endif
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
#endif
|
||||
|
||||
#if FF_STR_VOLUME_ID
|
||||
#ifndef FF_VOLUME_STRS
|
||||
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API */
|
||||
|
||||
#ifndef _INC_TCHAR
|
||||
#define _INC_TCHAR
|
||||
|
||||
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) u8 ## x
|
||||
#define _TEXT(x) u8 ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
|
||||
typedef DWORD TCHAR;
|
||||
#define _T(x) U ## x
|
||||
#define _TEXT(x) U ## x
|
||||
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
|
||||
#error Wrong FF_LFN_UNICODE setting
|
||||
#else /* ANSI/OEM code in SBCS/DBCS */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of file size variables */
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
#if FF_INTDEF != 2
|
||||
#error exFAT feature wants C99 or later
|
||||
#endif
|
||||
typedef QWORD FSIZE_t;
|
||||
#else
|
||||
typedef DWORD FSIZE_t;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Filesystem object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* Filesystem type (0:not mounted) */
|
||||
BYTE pdrv; /* Associated physical drive */
|
||||
BYTE n_fats; /* Number of FATs (1 or 2) */
|
||||
BYTE wflag; /* win[] flag (b0:dirty) */
|
||||
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
|
||||
WORD id; /* Volume mount ID */
|
||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
WORD csize; /* Cluster size [sectors] */
|
||||
#if FF_MAX_SS != FF_MIN_SS
|
||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if FF_USE_LFN
|
||||
WCHAR* lfnbuf; /* LFN working buffer */
|
||||
#endif
|
||||
#if FF_FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
|
||||
#endif
|
||||
#if FF_FS_REENTRANT
|
||||
FF_SYNC_t sobj; /* Identifier of sync object */
|
||||
#endif
|
||||
#if !FF_FS_READONLY
|
||||
DWORD last_clst; /* Last allocated cluster */
|
||||
DWORD free_clst; /* Number of free clusters */
|
||||
#endif
|
||||
#if FF_FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
|
||||
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
|
||||
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
|
||||
#endif
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||
DWORD fsize; /* Size of an FAT [sectors] */
|
||||
DWORD volbase; /* Volume base sector */
|
||||
DWORD fatbase; /* FAT base sector */
|
||||
DWORD dirbase; /* Root directory base sector/cluster */
|
||||
DWORD database; /* Data base sector */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD bitbase; /* Allocation bitmap base sector */
|
||||
#endif
|
||||
DWORD winsect; /* Current sector appearing in the win[] */
|
||||
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* Object ID and allocation information (FFOBJID) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the hosting volume of this object */
|
||||
WORD id; /* Hosting volume mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
|
||||
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
||||
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
|
||||
#endif
|
||||
#if FF_FS_LOCK
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
} FFOBJID;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !FF_FS_READONLY
|
||||
DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
|
||||
#endif
|
||||
#if FF_USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
|
||||
#endif
|
||||
#if !FF_FS_TINY
|
||||
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (FF_DIR) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector (0:Read operation has terminated) */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if FF_USE_LFN
|
||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||
#endif
|
||||
#if FF_USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} FF_DIR;
|
||||
|
||||
|
||||
|
||||
/* File information structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
FSIZE_t fsize; /* File size */
|
||||
WORD fdate; /* Modified date */
|
||||
WORD ftime; /* Modified time */
|
||||
BYTE fattrib; /* File attribute */
|
||||
#if FF_USE_LFN
|
||||
TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
|
||||
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
|
||||
#else
|
||||
TCHAR fname[12 + 1]; /* File name */
|
||||
#endif
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
|
||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs module application interface */
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
|
||||
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate the file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
|
||||
FRESULT f_opendir (FF_DIR* dp, const TCHAR* path); /* Open a directory */
|
||||
FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (FF_DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_findfirst (FF_DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||
FRESULT f_findnext (FF_DIR* dp, FILINFO* fno); /* Find next file */
|
||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
|
||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
|
||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
|
||||
FRESULT f_setcp (WORD cp); /* Set current code page */
|
||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
#define f_unmount(path) f_mount(0, path, 0)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional user defined functions */
|
||||
|
||||
/* RTC function */
|
||||
#if !FF_FS_READONLY && !FF_FS_NORTC
|
||||
DWORD get_fattime (void);
|
||||
#endif
|
||||
|
||||
/* LFN support functions */
|
||||
#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
|
||||
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
|
||||
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
|
||||
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
|
||||
#endif
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||
void ff_memfree (void* mblock); /* Free memory block */
|
||||
#endif
|
||||
|
||||
/* Sync functions */
|
||||
#if FF_FS_REENTRANT
|
||||
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
|
||||
int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
|
||||
void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
|
||||
int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and offset address */
|
||||
|
||||
|
||||
/* File access mode and open method flags (3rd argument of f_open) */
|
||||
#define FA_READ 0x01
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define FA_OPEN_APPEND 0x30
|
||||
|
||||
/* Fast seek controls (2nd argument of f_lseek) */
|
||||
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
||||
|
||||
/* Format options (2nd argument of f_mkfs) */
|
||||
#define FM_FAT 0x01
|
||||
#define FM_FAT32 0x02
|
||||
#define FM_EXFAT 0x04
|
||||
#define FM_ANY 0x07
|
||||
#define FM_SFD 0x08
|
||||
|
||||
/* Filesystem type (FATFS.fs_type) */
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
#define FS_EXFAT 4
|
||||
|
||||
/* File attribute bits for directory entry (FILINFO.fattrib) */
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FF_DEFINED */
|
325
components/fatfs/src/ffconf.h
Normal file
325
components/fatfs/src/ffconf.h
Normal file
@ -0,0 +1,325 @@
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs Functional Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 86604 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_READONLY 0
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
|
||||
#define FF_FS_MINIMIZE 0
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: Basic functions are fully enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
|
||||
/ are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
|
||||
#define FF_USE_STRFUNC 0
|
||||
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define FF_USE_FIND 0
|
||||
/* This option switches filtered directory read functions, f_findfirst() and
|
||||
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||
|
||||
|
||||
#define FF_USE_MKFS 1
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_FASTSEEK 0
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_CHMOD 1
|
||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||
|
||||
|
||||
#define FF_USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_CODE_PAGE CONFIG_FATFS_CODEPAGE
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect code page setting can cause a file open failure.
|
||||
/
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
/ 771 - KBL
|
||||
/ 775 - Baltic
|
||||
/ 850 - Latin 1
|
||||
/ 852 - Latin 2
|
||||
/ 855 - Cyrillic
|
||||
/ 857 - Turkish
|
||||
/ 860 - Portuguese
|
||||
/ 861 - Icelandic
|
||||
/ 862 - Hebrew
|
||||
/ 863 - Canadian French
|
||||
/ 864 - Arabic
|
||||
/ 865 - Nordic
|
||||
/ 866 - Russian
|
||||
/ 869 - Greek 2
|
||||
/ 932 - Japanese (DBCS)
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
/ 0 - Include all code pages above and configured by f_setcp()
|
||||
*/
|
||||
|
||||
|
||||
#if defined(CONFIG_FATFS_LFN_STACK)
|
||||
#define FF_USE_LFN 2
|
||||
#elif defined(CONFIG_FATFS_LFN_HEAP)
|
||||
#define FF_USE_LFN 3
|
||||
#else /* CONFIG_FATFS_LFN_NONE */
|
||||
#define FF_USE_LFN 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FATFS_MAX_LFN
|
||||
#define FF_MAX_LFN CONFIG_FATFS_MAX_LFN
|
||||
#endif
|
||||
|
||||
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||
/
|
||||
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
|
||||
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
|
||||
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
|
||||
/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
|
||||
/ specification.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree() in ffsystem.c, need to be added to the project. */
|
||||
|
||||
|
||||
#ifdef CONFIG_FATFS_API_ENCODING_UTF_8
|
||||
#define FF_LFN_UNICODE 2
|
||||
#elif defined(CONFIG_FATFS_API_ENCODING_UTF_16)
|
||||
#define FF_LFN_UNICODE 1
|
||||
#else /* CONFIG_FATFS_API_ENCODING_ANSI_OEM */
|
||||
#define FF_LFN_UNICODE 0
|
||||
#endif
|
||||
/* This option switches the character encoding on the API when LFN is enabled.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP (TCHAR = char)
|
||||
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
|
||||
/ 2: Unicode in UTF-8 (TCHAR = char)
|
||||
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
|
||||
/
|
||||
/ Also behavior of string I/O functions will be affected by this option.
|
||||
/ When LFN is not enabled, this option has no effect. */
|
||||
|
||||
|
||||
#define FF_LFN_BUF 255
|
||||
#define FF_SFN_BUF 12
|
||||
/* This set of options defines size of file name members in the FILINFO structure
|
||||
/ which is used to read out directory items. These values should be suffcient for
|
||||
/ the file names to read. The maximum possible length of the read file name depends
|
||||
/ on character encoding. When LFN is not enabled, these options have no effect. */
|
||||
|
||||
|
||||
#define FF_STRF_ENCODE 3
|
||||
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
|
||||
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
|
||||
/ This option selects assumption of character encoding ON THE FILE to be
|
||||
/ read/written via those functions.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP
|
||||
/ 1: Unicode in UTF-16LE
|
||||
/ 2: Unicode in UTF-16BE
|
||||
/ 3: Unicode in UTF-8
|
||||
*/
|
||||
|
||||
|
||||
#define FF_FS_RPATH 0
|
||||
/* This option configures support for relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_VOLUMES 2
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
|
||||
#define FF_STR_VOLUME_ID 0
|
||||
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
|
||||
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
|
||||
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
|
||||
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
|
||||
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
|
||||
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
|
||||
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
|
||||
/ not defined, a user defined volume string table needs to be defined as:
|
||||
/
|
||||
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
|
||||
*/
|
||||
|
||||
|
||||
#define FF_MULTI_PARTITION 1
|
||||
/* This option switches support for multiple volumes on the physical drive.
|
||||
/ By default (0), each logical drive number is bound to the same physical drive
|
||||
/ number and only an FAT volume found on the physical drive will be mounted.
|
||||
/ When this function is enabled (1), each logical drive number can be bound to
|
||||
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
|
||||
/ funciton will be available. */
|
||||
|
||||
/* SD card sector size */
|
||||
#define FF_SS_SDCARD 512
|
||||
/* wear_levelling library sector size */
|
||||
#define FF_SS_WL CONFIG_WL_SECTOR_SIZE
|
||||
|
||||
#define FF_MIN_SS MIN(FF_SS_SDCARD, FF_SS_WL)
|
||||
#define FF_MAX_SS MAX(FF_SS_SDCARD, FF_SS_WL)
|
||||
/* This set of options configures the range of sector size to be supported. (512,
|
||||
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
|
||||
/ for variable sector size mode and disk_ioctl() function needs to implement
|
||||
/ GET_SECTOR_SIZE command. */
|
||||
|
||||
|
||||
#define FF_USE_TRIM 0
|
||||
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define FF_FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_TINY (!CONFIG_FATFS_PER_FILE_CACHE)
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
|
||||
|
||||
|
||||
#define FF_FS_EXFAT 0
|
||||
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
|
||||
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
|
||||
#define FF_FS_NORTC 0
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2018
|
||||
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
|
||||
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
|
||||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
|
||||
|
||||
|
||||
#define FF_FS_LOCK CONFIG_FATFS_FS_LOCK
|
||||
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock function. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock control is independent of re-entrancy. */
|
||||
|
||||
|
||||
#define FF_FS_REENTRANT 1
|
||||
#define FF_FS_TIMEOUT (CONFIG_FATFS_TIMEOUT_MS / portTICK_PERIOD_MS)
|
||||
#define FF_SYNC_t SemaphoreHandle_t
|
||||
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this function.
|
||||
/
|
||||
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function, must be added to the project. Samples are available in
|
||||
/ option/syscall.c.
|
||||
/
|
||||
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.h. */
|
||||
|
||||
#include <sys/param.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
/* Some memory allocation functions are declared here in addition to ff.h, so that
|
||||
they can be used also by external code when LFN feature is disabled.
|
||||
*/
|
||||
void* ff_memalloc (unsigned msize);
|
||||
void ff_memfree(void*);
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
|
||||
/* Redefine names of disk IO functions to prevent name collisions */
|
||||
#define disk_initialize ff_disk_initialize
|
||||
#define disk_status ff_disk_status
|
||||
#define disk_read ff_disk_read
|
||||
#define disk_write ff_disk_write
|
||||
#define disk_ioctl ff_disk_ioctl
|
170
components/fatfs/src/ffsystem.c
Normal file
170
components/fatfs/src/ffsystem.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Sample Code of OS Dependent Functions for FatFs */
|
||||
/* (C)ChaN, 2018 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "ff.h"
|
||||
|
||||
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Allocate a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
|
||||
UINT msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
return malloc(msize); /* Allocate a new memory block with POSIX API */
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Free a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free (nothing to do if null) */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block with POSIX API */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if FF_FS_REENTRANT /* Mutal exclusion */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Create a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to create a new
|
||||
/ synchronization object for the volume, such as semaphore and mutex.
|
||||
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
//const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */
|
||||
|
||||
|
||||
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
|
||||
BYTE vol, /* Corresponding volume (logical drive number) */
|
||||
FF_SYNC_t* sobj /* Pointer to return the created sync object */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
*sobj = CreateMutex(NULL, FALSE, NULL);
|
||||
return (int)(*sobj != INVALID_HANDLE_VALUE);
|
||||
|
||||
/* uITRON */
|
||||
// T_CSEM csem = {TA_TPRI,1,1};
|
||||
// *sobj = acre_sem(&csem);
|
||||
// return (int)(*sobj > 0);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// *sobj = OSMutexCreate(0, &err);
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// *sobj = xSemaphoreCreateMutex();
|
||||
// return (int)(*sobj != NULL);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// *sobj = osMutexCreate(&Mutex[vol]);
|
||||
// return (int)(*sobj != NULL);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Delete a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to delete a synchronization
|
||||
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
|
||||
/ the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
|
||||
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
return (int)CloseHandle(sobj);
|
||||
|
||||
/* uITRON */
|
||||
// return (int)(del_sem(sobj) == E_OK);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// OSMutexDel(sobj, OS_DEL_ALWAYS, &err);
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// vSemaphoreDelete(sobj);
|
||||
// return 1;
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// return (int)(osMutexDelete(sobj) == osOK);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Request Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on entering file functions to lock the volume.
|
||||
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
||||
*/
|
||||
|
||||
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
|
||||
FF_SYNC_t sobj /* Sync object to wait */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);
|
||||
|
||||
/* uITRON */
|
||||
// return (int)(wai_sem(sobj) == E_OK);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// OSMutexPend(sobj, FF_FS_TIMEOUT, &err));
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Release Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on leaving file functions to unlock the volume.
|
||||
*/
|
||||
|
||||
void ff_rel_grant (
|
||||
FF_SYNC_t sobj /* Sync object to be signaled */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
ReleaseMutex(sobj);
|
||||
|
||||
/* uITRON */
|
||||
// sig_sem(sobj);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OSMutexPost(sobj);
|
||||
|
||||
/* FreeRTOS */
|
||||
// xSemaphoreGive(sobj);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// osMutexRelease(sobj);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
15597
components/fatfs/src/ffunicode.c
Normal file
15597
components/fatfs/src/ffunicode.c
Normal file
File diff suppressed because it is too large
Load Diff
5
components/fatfs/test/CMakeLists.txt
Normal file
5
components/fatfs/test/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity test_utils vfs fatfs
|
||||
EMBED_TXTFILES fatfs.img
|
||||
)
|
2
components/fatfs/test/component.mk
Normal file
2
components/fatfs/test/component.mk
Normal file
@ -0,0 +1,2 @@
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
||||
COMPONENT_EMBED_TXTFILES := fatfs.img
|
BIN
components/fatfs/test/fatfs.img
Normal file
BIN
components/fatfs/test/fatfs.img
Normal file
Binary file not shown.
812
components/fatfs/test/test_fatfs_common.c
Normal file
812
components/fatfs/test/test_fatfs_common.c
Normal file
@ -0,0 +1,812 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <utime.h>
|
||||
#include "unity.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "ff.h"
|
||||
#include "test_fatfs_common.h"
|
||||
|
||||
const char* fatfs_test_hello_str = "Hello, World!\n";
|
||||
const char* fatfs_test_hello_str_utf = "世界,你好!\n";
|
||||
|
||||
void test_fatfs_create_file_with_text(const char* name, const char* text)
|
||||
{
|
||||
FILE* f = fopen(name, "wb");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
TEST_ASSERT_TRUE(fputs(text, f) != EOF);
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
}
|
||||
|
||||
void test_fatfs_overwrite_append(const char* filename)
|
||||
{
|
||||
/* Create new file with 'aaaa' */
|
||||
test_fatfs_create_file_with_text(filename, "aaaa");
|
||||
|
||||
/* Append 'bbbb' to file */
|
||||
FILE *f_a = fopen(filename, "a");
|
||||
TEST_ASSERT_NOT_NULL(f_a);
|
||||
TEST_ASSERT_NOT_EQUAL(EOF, fputs("bbbb", f_a));
|
||||
TEST_ASSERT_EQUAL(0, fclose(f_a));
|
||||
|
||||
/* Read back 8 bytes from file, verify it's 'aaaabbbb' */
|
||||
char buf[10] = { 0 };
|
||||
FILE *f_r = fopen(filename, "r");
|
||||
TEST_ASSERT_NOT_NULL(f_r);
|
||||
TEST_ASSERT_EQUAL(8, fread(buf, 1, 8, f_r));
|
||||
TEST_ASSERT_EQUAL_STRING_LEN("aaaabbbb", buf, 8);
|
||||
|
||||
/* Be sure we're at end of file */
|
||||
TEST_ASSERT_EQUAL(0, fread(buf, 1, 8, f_r));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f_r));
|
||||
|
||||
/* Overwrite file with 'cccc' */
|
||||
test_fatfs_create_file_with_text(filename, "cccc");
|
||||
|
||||
/* Verify file now only contains 'cccc' */
|
||||
f_r = fopen(filename, "r");
|
||||
TEST_ASSERT_NOT_NULL(f_r);
|
||||
bzero(buf, sizeof(buf));
|
||||
TEST_ASSERT_EQUAL(4, fread(buf, 1, 8, f_r)); // trying to read 8 bytes, only expecting 4
|
||||
TEST_ASSERT_EQUAL_STRING_LEN("cccc", buf, 4);
|
||||
TEST_ASSERT_EQUAL(0, fclose(f_r));
|
||||
}
|
||||
|
||||
void test_fatfs_read_file(const char* filename)
|
||||
{
|
||||
FILE* f = fopen(filename, "r");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
char buf[32] = { 0 };
|
||||
int cb = fread(buf, 1, sizeof(buf), f);
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), cb);
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
}
|
||||
|
||||
void test_fatfs_read_file_utf_8(const char* filename)
|
||||
{
|
||||
FILE* f = fopen(filename, "r");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
char buf[64] = { 0 }; //Doubled buffer size to allow for longer UTF-8 strings
|
||||
int cb = fread(buf, 1, sizeof(buf), f);
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str_utf), cb);
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str_utf, buf));
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
}
|
||||
|
||||
void test_fatfs_pread_file(const char* filename)
|
||||
{
|
||||
char buf[32] = { 0 };
|
||||
const int fd = open(filename, O_RDONLY);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
int r = pread(fd, buf, sizeof(buf), 0); // it is a regular read() with offset==0
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), r);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = pread(fd, buf, sizeof(buf), 1); // offset==1
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str + 1, buf));
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str) - 1, r);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = pread(fd, buf, sizeof(buf), 5); // offset==5
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str + 5, buf));
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str) - 5, r);
|
||||
|
||||
// regular read() should work now because pread() should not affect the current position in file
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = read(fd, buf, sizeof(buf)); // note that this is read() and not pread()
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), r);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = pread(fd, buf, sizeof(buf), 10); // offset==10
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str + 10, buf));
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str) - 10, r);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = pread(fd, buf, sizeof(buf), strlen(fatfs_test_hello_str) + 1); // offset to EOF
|
||||
TEST_ASSERT_EQUAL(0, r);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
}
|
||||
|
||||
static void test_pwrite(const char *filename, off_t offset, const char *msg)
|
||||
{
|
||||
const int fd = open(filename, O_WRONLY);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
const off_t current_pos = lseek(fd, 0, SEEK_END); // O_APPEND is not the same - jumps to the end only before write()
|
||||
|
||||
const int r = pwrite(fd, msg, strlen(msg), offset);
|
||||
TEST_ASSERT_EQUAL(strlen(msg), r);
|
||||
|
||||
TEST_ASSERT_EQUAL(current_pos, lseek(fd, 0, SEEK_CUR)); // pwrite should not move the pointer
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
}
|
||||
|
||||
static void test_file_content(const char *filename, const char *msg)
|
||||
{
|
||||
char buf[32] = { 0 };
|
||||
const int fd = open(filename, O_RDONLY);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
int r = read(fd, buf, sizeof(buf));
|
||||
TEST_ASSERT_NOT_EQUAL(-1, r);
|
||||
TEST_ASSERT_EQUAL(0, strcmp(msg, buf));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
}
|
||||
|
||||
void test_fatfs_pwrite_file(const char *filename)
|
||||
{
|
||||
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
|
||||
test_pwrite(filename, 0, "Hello");
|
||||
test_file_content(filename, "Hello");
|
||||
|
||||
test_pwrite(filename, strlen("Hello"), ", world!");
|
||||
test_file_content(filename, "Hello, world!");
|
||||
test_pwrite(filename, strlen("Hello, "), "Dolly");
|
||||
test_file_content(filename, "Hello, Dolly!");
|
||||
}
|
||||
|
||||
void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count)
|
||||
{
|
||||
FILE** files = calloc(files_count, sizeof(FILE*));
|
||||
for (size_t i = 0; i < files_count; ++i) {
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "%s_%d.txt", filename_prefix, i);
|
||||
files[i] = fopen(name, "w");
|
||||
TEST_ASSERT_NOT_NULL(files[i]);
|
||||
}
|
||||
/* close everything and clean up */
|
||||
for (size_t i = 0; i < files_count; ++i) {
|
||||
fclose(files[i]);
|
||||
}
|
||||
free(files);
|
||||
}
|
||||
|
||||
void test_fatfs_lseek(const char* filename)
|
||||
{
|
||||
FILE* f = fopen(filename, "wb+");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n"));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR));
|
||||
TEST_ASSERT_EQUAL('9', fgetc(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET));
|
||||
TEST_ASSERT_EQUAL('3', fgetc(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END));
|
||||
TEST_ASSERT_EQUAL('8', fgetc(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_END));
|
||||
TEST_ASSERT_EQUAL(14, ftell(f));
|
||||
TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n"));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
|
||||
TEST_ASSERT_EQUAL(18, ftell(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
|
||||
char buf[20];
|
||||
TEST_ASSERT_EQUAL(18, fread(buf, 1, sizeof(buf), f));
|
||||
const char ref_buf[] = "0123456789\n\0\0\0abc\n";
|
||||
TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
}
|
||||
|
||||
void test_fatfs_truncate_file(const char* filename)
|
||||
{
|
||||
int read = 0;
|
||||
int truncated_len = 0;
|
||||
|
||||
const char input[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
char output[sizeof(input)];
|
||||
|
||||
FILE* f = fopen(filename, "wb");
|
||||
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
TEST_ASSERT_EQUAL(strlen(input), fprintf(f, input));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
|
||||
|
||||
// Extending file beyond size is not supported
|
||||
TEST_ASSERT_EQUAL(-1, truncate(filename, strlen(input) + 1));
|
||||
TEST_ASSERT_EQUAL(errno, EPERM);
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, truncate(filename, -1));
|
||||
TEST_ASSERT_EQUAL(errno, EPERM);
|
||||
|
||||
|
||||
// Truncating should succeed
|
||||
const char truncated_1[] = "ABCDEFGHIJ";
|
||||
truncated_len = strlen(truncated_1);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, truncate(filename, truncated_len));
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
|
||||
memset(output, 0, sizeof(output));
|
||||
read = fread(output, 1, sizeof(output), f);
|
||||
|
||||
TEST_ASSERT_EQUAL(truncated_len, read);
|
||||
TEST_ASSERT_EQUAL_STRING_LEN(truncated_1, output, truncated_len);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
|
||||
|
||||
// Once truncated, the new file size should be the basis
|
||||
// whether truncation should succeed or not
|
||||
TEST_ASSERT_EQUAL(-1, truncate(filename, truncated_len + 1));
|
||||
TEST_ASSERT_EQUAL(EPERM, errno);
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, truncate(filename, strlen(input)));
|
||||
TEST_ASSERT_EQUAL(EPERM, errno);
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, truncate(filename, strlen(input) + 1));
|
||||
TEST_ASSERT_EQUAL(EPERM, errno);
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, truncate(filename, -1));
|
||||
TEST_ASSERT_EQUAL(EPERM, errno);
|
||||
|
||||
|
||||
// Truncating a truncated file should succeed
|
||||
const char truncated_2[] = "ABCDE";
|
||||
truncated_len = strlen(truncated_2);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, truncate(filename, truncated_len));
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
|
||||
memset(output, 0, sizeof(output));
|
||||
read = fread(output, 1, sizeof(output), f);
|
||||
|
||||
TEST_ASSERT_EQUAL(truncated_len, read);
|
||||
TEST_ASSERT_EQUAL_STRING_LEN(truncated_2, output, truncated_len);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
}
|
||||
|
||||
void test_fatfs_stat(const char* filename, const char* root_dir)
|
||||
{
|
||||
struct tm tm;
|
||||
tm.tm_year = 2017 - 1900;
|
||||
tm.tm_mon = 11;
|
||||
tm.tm_mday = 8;
|
||||
tm.tm_hour = 19;
|
||||
tm.tm_min = 51;
|
||||
tm.tm_sec = 10;
|
||||
time_t t = mktime(&tm);
|
||||
printf("Setting time: %s", asctime(&tm));
|
||||
struct timeval now = { .tv_sec = t };
|
||||
settimeofday(&now, NULL);
|
||||
|
||||
test_fatfs_create_file_with_text(filename, "foo\n");
|
||||
|
||||
struct stat st;
|
||||
TEST_ASSERT_EQUAL(0, stat(filename, &st));
|
||||
time_t mtime = st.st_mtime;
|
||||
struct tm mtm;
|
||||
localtime_r(&mtime, &mtm);
|
||||
printf("File time: %s", asctime(&mtm));
|
||||
TEST_ASSERT(abs(mtime - t) < 2); // fatfs library stores time with 2 second precision
|
||||
|
||||
TEST_ASSERT(st.st_mode & S_IFREG);
|
||||
TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
|
||||
|
||||
memset(&st, 0, sizeof(st));
|
||||
TEST_ASSERT_EQUAL(0, stat(root_dir, &st));
|
||||
TEST_ASSERT(st.st_mode & S_IFDIR);
|
||||
TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
|
||||
}
|
||||
|
||||
void test_fatfs_utime(const char* filename, const char* root_dir)
|
||||
{
|
||||
struct stat achieved_stat;
|
||||
struct tm desired_tm;
|
||||
struct utimbuf desired_time = {
|
||||
.actime = 0, // access time is not supported
|
||||
.modtime = 0,
|
||||
};
|
||||
time_t false_now = 0;
|
||||
memset(&desired_tm, 0, sizeof(struct tm));
|
||||
|
||||
{
|
||||
// Setting up a false actual time - used when the file is created and for modification with the current time
|
||||
desired_tm.tm_mon = 10 - 1;
|
||||
desired_tm.tm_mday = 31;
|
||||
desired_tm.tm_year = 2018 - 1900;
|
||||
desired_tm.tm_hour = 10;
|
||||
desired_tm.tm_min = 35;
|
||||
desired_tm.tm_sec = 23;
|
||||
|
||||
false_now = mktime(&desired_tm);
|
||||
|
||||
struct timeval now = { .tv_sec = false_now };
|
||||
settimeofday(&now, NULL);
|
||||
}
|
||||
test_fatfs_create_file_with_text(filename, "");
|
||||
|
||||
// 00:00:00. January 1st, 1980 - FATFS cannot handle earlier dates
|
||||
desired_tm.tm_mon = 1 - 1;
|
||||
desired_tm.tm_mday = 1;
|
||||
desired_tm.tm_year = 1980 - 1900;
|
||||
desired_tm.tm_hour = 0;
|
||||
desired_tm.tm_min = 0;
|
||||
desired_tm.tm_sec = 0;
|
||||
printf("Testing mod. time: %s", asctime(&desired_tm));
|
||||
desired_time.modtime = mktime(&desired_tm);
|
||||
TEST_ASSERT_EQUAL(0, utime(filename, &desired_time));
|
||||
TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
|
||||
TEST_ASSERT_EQUAL_UINT32(desired_time.modtime, achieved_stat.st_mtime);
|
||||
|
||||
// current time
|
||||
TEST_ASSERT_EQUAL(0, utime(filename, NULL));
|
||||
TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
|
||||
printf("Mod. time changed to (false actual time): %s", ctime(&achieved_stat.st_mtime));
|
||||
TEST_ASSERT_NOT_EQUAL(desired_time.modtime, achieved_stat.st_mtime);
|
||||
TEST_ASSERT(false_now - achieved_stat.st_mtime <= 2); // two seconds of tolerance are given
|
||||
|
||||
// 23:59:08. December 31st, 2037
|
||||
desired_tm.tm_mon = 12 - 1;
|
||||
desired_tm.tm_mday = 31;
|
||||
desired_tm.tm_year = 2037 - 1900;
|
||||
desired_tm.tm_hour = 23;
|
||||
desired_tm.tm_min = 59;
|
||||
desired_tm.tm_sec = 8;
|
||||
printf("Testing mod. time: %s", asctime(&desired_tm));
|
||||
desired_time.modtime = mktime(&desired_tm);
|
||||
TEST_ASSERT_EQUAL(0, utime(filename, &desired_time));
|
||||
TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
|
||||
TEST_ASSERT_EQUAL_UINT32(desired_time.modtime, achieved_stat.st_mtime);
|
||||
|
||||
//WARNING: it has the Unix Millenium bug (Y2K38)
|
||||
|
||||
// 00:00:00. January 1st, 1970 - FATFS cannot handle years before 1980
|
||||
desired_tm.tm_mon = 1 - 1;
|
||||
desired_tm.tm_mday = 1;
|
||||
desired_tm.tm_year = 1970 - 1900;
|
||||
desired_tm.tm_hour = 0;
|
||||
desired_tm.tm_min = 0;
|
||||
desired_tm.tm_sec = 0;
|
||||
printf("Testing mod. time: %s", asctime(&desired_tm));
|
||||
desired_time.modtime = mktime(&desired_tm);
|
||||
TEST_ASSERT_EQUAL(-1, utime(filename, &desired_time));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
}
|
||||
|
||||
void test_fatfs_unlink(const char* filename)
|
||||
{
|
||||
test_fatfs_create_file_with_text(filename, "unlink\n");
|
||||
|
||||
TEST_ASSERT_EQUAL(0, unlink(filename));
|
||||
|
||||
TEST_ASSERT_NULL(fopen(filename, "r"));
|
||||
}
|
||||
|
||||
void test_fatfs_link_rename(const char* filename_prefix)
|
||||
{
|
||||
char name_copy[64];
|
||||
char name_dst[64];
|
||||
char name_src[64];
|
||||
snprintf(name_copy, sizeof(name_copy), "%s_cpy.txt", filename_prefix);
|
||||
snprintf(name_dst, sizeof(name_dst), "%s_dst.txt", filename_prefix);
|
||||
snprintf(name_src, sizeof(name_src), "%s_src.txt", filename_prefix);
|
||||
|
||||
unlink(name_copy);
|
||||
unlink(name_dst);
|
||||
unlink(name_src);
|
||||
|
||||
FILE* f = fopen(name_src, "w+");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
const char* str = "0123456789";
|
||||
for (int i = 0; i < 4000; ++i) {
|
||||
TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f));
|
||||
}
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
TEST_ASSERT_EQUAL(0, link(name_src, name_copy));
|
||||
FILE* fcopy = fopen(name_copy, "r");
|
||||
TEST_ASSERT_NOT_NULL(fcopy);
|
||||
TEST_ASSERT_EQUAL(0, fseek(fcopy, 0, SEEK_END));
|
||||
TEST_ASSERT_EQUAL(40000, ftell(fcopy));
|
||||
TEST_ASSERT_EQUAL(0, fclose(fcopy));
|
||||
TEST_ASSERT_EQUAL(0, rename(name_copy, name_dst));
|
||||
TEST_ASSERT_NULL(fopen(name_copy, "r"));
|
||||
FILE* fdst = fopen(name_dst, "r");
|
||||
TEST_ASSERT_NOT_NULL(fdst);
|
||||
TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END));
|
||||
TEST_ASSERT_EQUAL(40000, ftell(fdst));
|
||||
TEST_ASSERT_EQUAL(0, fclose(fdst));
|
||||
}
|
||||
|
||||
void test_fatfs_mkdir_rmdir(const char* filename_prefix)
|
||||
{
|
||||
char name_dir1[64];
|
||||
char name_dir2[64];
|
||||
char name_dir2_file[64];
|
||||
snprintf(name_dir1, sizeof(name_dir1), "%s1", filename_prefix);
|
||||
snprintf(name_dir2, sizeof(name_dir2), "%s2", filename_prefix);
|
||||
snprintf(name_dir2_file, sizeof(name_dir2_file), "%s2/1.txt", filename_prefix);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, mkdir(name_dir1, 0755));
|
||||
struct stat st;
|
||||
TEST_ASSERT_EQUAL(0, stat(name_dir1, &st));
|
||||
TEST_ASSERT_TRUE(st.st_mode & S_IFDIR);
|
||||
TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
|
||||
TEST_ASSERT_EQUAL(0, rmdir(name_dir1));
|
||||
TEST_ASSERT_EQUAL(-1, stat(name_dir1, &st));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, mkdir(name_dir2, 0755));
|
||||
test_fatfs_create_file_with_text(name_dir2_file, "foo\n");
|
||||
TEST_ASSERT_EQUAL(0, stat(name_dir2, &st));
|
||||
TEST_ASSERT_TRUE(st.st_mode & S_IFDIR);
|
||||
TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
|
||||
TEST_ASSERT_EQUAL(0, stat(name_dir2_file, &st));
|
||||
TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
|
||||
TEST_ASSERT_TRUE(st.st_mode & S_IFREG);
|
||||
TEST_ASSERT_EQUAL(-1, rmdir(name_dir2));
|
||||
TEST_ASSERT_EQUAL(0, unlink(name_dir2_file));
|
||||
TEST_ASSERT_EQUAL(0, rmdir(name_dir2));
|
||||
}
|
||||
|
||||
void test_fatfs_can_opendir(const char* path)
|
||||
{
|
||||
char name_dir_file[64];
|
||||
const char * file_name = "test_opd.txt";
|
||||
snprintf(name_dir_file, sizeof(name_dir_file), "%s/%s", path, file_name);
|
||||
unlink(name_dir_file);
|
||||
test_fatfs_create_file_with_text(name_dir_file, "test_opendir\n");
|
||||
DIR* dir = opendir(path);
|
||||
TEST_ASSERT_NOT_NULL(dir);
|
||||
bool found = false;
|
||||
while (true) {
|
||||
struct dirent* de = readdir(dir);
|
||||
if (!de) {
|
||||
break;
|
||||
}
|
||||
if (strcasecmp(de->d_name, file_name) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_TRUE(found);
|
||||
TEST_ASSERT_EQUAL(0, closedir(dir));
|
||||
unlink(name_dir_file);
|
||||
}
|
||||
|
||||
void test_fatfs_opendir_readdir_rewinddir(const char* dir_prefix)
|
||||
{
|
||||
char name_dir_inner_file[64];
|
||||
char name_dir_inner[64];
|
||||
char name_dir_file3[64];
|
||||
char name_dir_file2[64];
|
||||
char name_dir_file1[64];
|
||||
|
||||
snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/inner/3.txt", dir_prefix);
|
||||
snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/inner", dir_prefix);
|
||||
snprintf(name_dir_file3, sizeof(name_dir_file2), "%s/boo.bin", dir_prefix);
|
||||
snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/2.txt", dir_prefix);
|
||||
snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/1.txt", dir_prefix);
|
||||
|
||||
unlink(name_dir_inner_file);
|
||||
rmdir(name_dir_inner);
|
||||
unlink(name_dir_file1);
|
||||
unlink(name_dir_file2);
|
||||
unlink(name_dir_file3);
|
||||
rmdir(dir_prefix);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, mkdir(dir_prefix, 0755));
|
||||
test_fatfs_create_file_with_text(name_dir_file1, "1\n");
|
||||
test_fatfs_create_file_with_text(name_dir_file2, "2\n");
|
||||
test_fatfs_create_file_with_text(name_dir_file3, "\01\02\03");
|
||||
TEST_ASSERT_EQUAL(0, mkdir(name_dir_inner, 0755));
|
||||
test_fatfs_create_file_with_text(name_dir_inner_file, "3\n");
|
||||
|
||||
DIR* dir = opendir(dir_prefix);
|
||||
TEST_ASSERT_NOT_NULL(dir);
|
||||
int count = 0;
|
||||
const char* names[4];
|
||||
while(count < 4) {
|
||||
struct dirent* de = readdir(dir);
|
||||
if (!de) {
|
||||
break;
|
||||
}
|
||||
printf("found '%s'\n", de->d_name);
|
||||
if (strcasecmp(de->d_name, "1.txt") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "1.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "2.txt") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "2.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "inner") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_DIR);
|
||||
names[count] = "inner";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "boo.bin") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "boo.bin";
|
||||
++count;
|
||||
} else {
|
||||
TEST_FAIL_MESSAGE("unexpected directory entry");
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_EQUAL(count, 4);
|
||||
|
||||
rewinddir(dir);
|
||||
struct dirent* de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
|
||||
seekdir(dir, 3);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
|
||||
seekdir(dir, 1);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
|
||||
seekdir(dir, 2);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, closedir(dir));
|
||||
}
|
||||
|
||||
void test_fatfs_opendir_readdir_rewinddir_utf_8(const char* dir_prefix)
|
||||
{
|
||||
char name_dir_inner_file[64];
|
||||
char name_dir_inner[64];
|
||||
char name_dir_file3[64];
|
||||
char name_dir_file2[64];
|
||||
char name_dir_file1[64];
|
||||
|
||||
snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/内部目录/内部文件.txt", dir_prefix);
|
||||
snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/内部目录", dir_prefix);
|
||||
snprintf(name_dir_file3, sizeof(name_dir_file3), "%s/文件三.bin", dir_prefix);
|
||||
snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/文件二.txt", dir_prefix);
|
||||
snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/文件一.txt", dir_prefix);
|
||||
|
||||
unlink(name_dir_inner_file);
|
||||
rmdir(name_dir_inner);
|
||||
unlink(name_dir_file1);
|
||||
unlink(name_dir_file2);
|
||||
unlink(name_dir_file3);
|
||||
rmdir(dir_prefix);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, mkdir(dir_prefix, 0755));
|
||||
test_fatfs_create_file_with_text(name_dir_file1, "一号\n");
|
||||
test_fatfs_create_file_with_text(name_dir_file2, "二号\n");
|
||||
test_fatfs_create_file_with_text(name_dir_file3, "\0一\0二\0三");
|
||||
TEST_ASSERT_EQUAL(0, mkdir(name_dir_inner, 0755));
|
||||
test_fatfs_create_file_with_text(name_dir_inner_file, "三号\n");
|
||||
|
||||
DIR* dir = opendir(dir_prefix);
|
||||
TEST_ASSERT_NOT_NULL(dir);
|
||||
int count = 0;
|
||||
const char* names[4];
|
||||
while(count < 4) {
|
||||
struct dirent* de = readdir(dir);
|
||||
if (!de) {
|
||||
break;
|
||||
}
|
||||
printf("found '%s'\n", de->d_name);
|
||||
if (strcasecmp(de->d_name, "文件一.txt") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "文件一.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "文件二.txt") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "文件二.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "内部目录") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_DIR);
|
||||
names[count] = "内部目录";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "文件三.bin") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "文件三.bin";
|
||||
++count;
|
||||
} else {
|
||||
TEST_FAIL_MESSAGE("unexpected directory entry");
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_EQUAL(count, 4);
|
||||
|
||||
rewinddir(dir);
|
||||
struct dirent* de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
|
||||
seekdir(dir, 3);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
|
||||
seekdir(dir, 1);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
|
||||
seekdir(dir, 2);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, closedir(dir));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char* filename;
|
||||
bool write;
|
||||
size_t word_count;
|
||||
int seed;
|
||||
SemaphoreHandle_t done;
|
||||
int result;
|
||||
} read_write_test_arg_t;
|
||||
|
||||
#define READ_WRITE_TEST_ARG_INIT(name, seed_) \
|
||||
{ \
|
||||
.filename = name, \
|
||||
.seed = seed_, \
|
||||
.word_count = 8192, \
|
||||
.write = true, \
|
||||
.done = xSemaphoreCreateBinary() \
|
||||
}
|
||||
|
||||
static void read_write_task(void* param)
|
||||
{
|
||||
read_write_test_arg_t* args = (read_write_test_arg_t*) param;
|
||||
FILE* f = fopen(args->filename, args->write ? "wb" : "rb");
|
||||
if (f == NULL) {
|
||||
args->result = ESP_ERR_NOT_FOUND;
|
||||
goto done;
|
||||
}
|
||||
|
||||
srand(args->seed);
|
||||
for (size_t i = 0; i < args->word_count; ++i) {
|
||||
uint32_t val = i * 77;
|
||||
if (args->write) {
|
||||
int cnt = fwrite(&val, sizeof(val), 1, f);
|
||||
if (cnt != 1) {
|
||||
ets_printf("E(w): i=%d, cnt=%d val=%d\n\n", i, cnt, val);
|
||||
args->result = ESP_FAIL;
|
||||
goto close;
|
||||
}
|
||||
} else {
|
||||
uint32_t rval;
|
||||
int cnt = fread(&rval, sizeof(rval), 1, f);
|
||||
if (cnt != 1 || rval != val) {
|
||||
ets_printf("E(r): i=%d, cnt=%d rval=%d val=%d\n\n", i, cnt, rval, val);
|
||||
args->result = ESP_FAIL;
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
}
|
||||
args->result = ESP_OK;
|
||||
|
||||
close:
|
||||
fclose(f);
|
||||
|
||||
done:
|
||||
xSemaphoreGive(args->done);
|
||||
vTaskDelay(1);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void test_fatfs_concurrent(const char* filename_prefix)
|
||||
{
|
||||
char names[4][64];
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
snprintf(names[i], sizeof(names[i]), "%s%d", filename_prefix, i + 1);
|
||||
unlink(names[i]);
|
||||
}
|
||||
|
||||
read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT(names[0], 1);
|
||||
read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT(names[1], 2);
|
||||
|
||||
printf("writing f1 and f2\n");
|
||||
|
||||
const int cpuid_0 = 0;
|
||||
const int cpuid_1 = portNUM_PROCESSORS - 1;
|
||||
const int stack_size = 4096;
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw1", stack_size, &args1, 3, NULL, cpuid_0);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw2", stack_size, &args2, 3, NULL, cpuid_1);
|
||||
|
||||
xSemaphoreTake(args1.done, portMAX_DELAY);
|
||||
printf("f1 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
|
||||
xSemaphoreTake(args2.done, portMAX_DELAY);
|
||||
printf("f2 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
|
||||
|
||||
args1.write = false;
|
||||
args2.write = false;
|
||||
read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT(names[2], 3);
|
||||
read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT(names[3], 4);
|
||||
|
||||
printf("reading f1 and f2, writing f3 and f4\n");
|
||||
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw3", stack_size, &args3, 3, NULL, cpuid_1);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw4", stack_size, &args4, 3, NULL, cpuid_0);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw1", stack_size, &args1, 3, NULL, cpuid_0);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw2", stack_size, &args2, 3, NULL, cpuid_1);
|
||||
|
||||
xSemaphoreTake(args1.done, portMAX_DELAY);
|
||||
printf("f1 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
|
||||
xSemaphoreTake(args2.done, portMAX_DELAY);
|
||||
printf("f2 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
|
||||
xSemaphoreTake(args3.done, portMAX_DELAY);
|
||||
printf("f3 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args3.result);
|
||||
xSemaphoreTake(args4.done, portMAX_DELAY);
|
||||
printf("f4 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args4.result);
|
||||
|
||||
vSemaphoreDelete(args1.done);
|
||||
vSemaphoreDelete(args2.done);
|
||||
vSemaphoreDelete(args3.done);
|
||||
vSemaphoreDelete(args4.done);
|
||||
}
|
||||
|
||||
void test_fatfs_rw_speed(const char* filename, void* buf, size_t buf_size, size_t file_size, bool is_write)
|
||||
{
|
||||
const size_t buf_count = file_size / buf_size;
|
||||
|
||||
FILE* f = fopen(filename, (is_write) ? "wb" : "rb");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
|
||||
struct timeval tv_start;
|
||||
gettimeofday(&tv_start, NULL);
|
||||
for (size_t n = 0; n < buf_count; ++n) {
|
||||
if (is_write) {
|
||||
TEST_ASSERT_EQUAL(buf_size, write(fileno(f), buf, buf_size));
|
||||
} else {
|
||||
if (read(fileno(f), buf, buf_size) != buf_size) {
|
||||
printf("reading at n=%d, eof=%d", n, feof(f));
|
||||
TEST_FAIL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct timeval tv_end;
|
||||
gettimeofday(&tv_end, NULL);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
|
||||
float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec);
|
||||
printf("%s %d bytes (block size %d) in %.3fms (%.3f MB/s)\n",
|
||||
(is_write)?"Wrote":"Read", file_size, buf_size, t_s * 1e3,
|
||||
file_size / (1024.0f * 1024.0f * t_s));
|
||||
}
|
73
components/fatfs/test/test_fatfs_common.h
Normal file
73
components/fatfs/test/test_fatfs_common.h
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file test_fatfs_common.h
|
||||
* @brief Common routines for FAT-on-SDMMC and FAT-on-WL tests
|
||||
*/
|
||||
|
||||
#define HEAP_SIZE_CAPTURE(heap_size) \
|
||||
heap_size = esp_get_free_heap_size();
|
||||
|
||||
#define HEAP_SIZE_CHECK(heap_size, tolerance) \
|
||||
do {\
|
||||
size_t final_heap_size = esp_get_free_heap_size(); \
|
||||
if (final_heap_size < heap_size - tolerance) { \
|
||||
printf("Initial heap size: %d, final: %d, diff=%d\n", heap_size, final_heap_size, heap_size - final_heap_size); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
const char* fatfs_test_hello_str;
|
||||
const char* fatfs_test_hello_str_utf;
|
||||
|
||||
void test_fatfs_create_file_with_text(const char* name, const char* text);
|
||||
|
||||
void test_fatfs_overwrite_append(const char* filename);
|
||||
|
||||
void test_fatfs_read_file(const char* filename);
|
||||
|
||||
void test_fatfs_read_file_utf_8(const char* filename);
|
||||
|
||||
void test_fatfs_pread_file(const char* filename);
|
||||
|
||||
void test_fatfs_pwrite_file(const char* filename);
|
||||
|
||||
void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count);
|
||||
|
||||
void test_fatfs_lseek(const char* filename);
|
||||
|
||||
void test_fatfs_truncate_file(const char* path);
|
||||
|
||||
void test_fatfs_stat(const char* filename, const char* root_dir);
|
||||
|
||||
void test_fatfs_utime(const char* filename, const char* root_dir);
|
||||
|
||||
void test_fatfs_unlink(const char* filename);
|
||||
|
||||
void test_fatfs_link_rename(const char* filename_prefix);
|
||||
|
||||
void test_fatfs_concurrent(const char* filename_prefix);
|
||||
|
||||
void test_fatfs_mkdir_rmdir(const char* filename_prefix);
|
||||
|
||||
void test_fatfs_can_opendir(const char* path);
|
||||
|
||||
void test_fatfs_opendir_readdir_rewinddir(const char* dir_prefix);
|
||||
|
||||
void test_fatfs_opendir_readdir_rewinddir_utf_8(const char* dir_prefix);
|
||||
|
||||
void test_fatfs_rw_speed(const char* filename, void* buf, size_t buf_size, size_t file_size, bool write);
|
||||
|
363
components/fatfs/test/test_fatfs_rawflash.c
Normal file
363
components/fatfs/test/test_fatfs_rawflash.c
Normal file
@ -0,0 +1,363 @@
|
||||
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/unistd.h>
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "test_fatfs_common.h"
|
||||
#include "esp_partition.h"
|
||||
#include "ff.h"
|
||||
|
||||
|
||||
static void test_setup(size_t max_files)
|
||||
{
|
||||
extern const char fatfs_start[] asm("_binary_fatfs_img_start");
|
||||
extern const char fatfs_end[] asm("_binary_fatfs_img_end");
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = false,
|
||||
.max_files = max_files
|
||||
};
|
||||
const esp_partition_t* part = get_test_data_partition();
|
||||
|
||||
TEST_ASSERT(part->size >= (fatfs_end - fatfs_start - 1));
|
||||
|
||||
// spi_flash_mmap_handle_t mmap_handle;
|
||||
// const void* mmap_ptr;
|
||||
// TEST_ESP_OK(esp_partition_mmap(part, 0, part->size, SPI_FLASH_MMAP_DATA, &mmap_ptr, &mmap_handle));
|
||||
// bool content_valid = memcmp(fatfs_start, mmap_ptr, part->size) == 0;
|
||||
// spi_flash_munmap(mmap_handle);
|
||||
|
||||
size_t max_space_size = MIN(part->size, fatfs_end - fatfs_start - 1);
|
||||
bool content_valid = true;
|
||||
char *buf = malloc(4096);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
|
||||
for (size_t i = 0; i < max_space_size; i += 4096) {
|
||||
size_t bytes = MIN(4096, max_space_size - i);
|
||||
|
||||
TEST_ESP_OK(esp_partition_read(part, i, buf, bytes));
|
||||
if (memcmp(fatfs_start + i, buf, bytes)) {
|
||||
content_valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
if (!content_valid) {
|
||||
printf("Copying fatfs.img into test partition...\n");
|
||||
esp_partition_erase_range(part, 0, part->size);
|
||||
for (int i = 0; i < part->size; i+= SPI_FLASH_SEC_SIZE) {
|
||||
ESP_ERROR_CHECK( esp_partition_write(part, i, fatfs_start + i, SPI_FLASH_SEC_SIZE) );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ESP_OK(esp_vfs_fat_rawflash_mount("/spiflash", "flash_test", &mount_config));
|
||||
}
|
||||
|
||||
static void test_teardown(void)
|
||||
{
|
||||
TEST_ESP_OK(esp_vfs_fat_rawflash_unmount("/spiflash","flash_test"));
|
||||
}
|
||||
|
||||
TEST_CASE("(raw) can read file", "[fatfs]")
|
||||
{
|
||||
test_setup(5);
|
||||
FILE* f = fopen("/spiflash/hello.txt", "r");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
char buf[32] = { 0 };
|
||||
int cb = fread(buf, 1, sizeof(buf), f);
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), cb);
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(raw) can open maximum number of files", "[fatfs]")
|
||||
{
|
||||
size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */
|
||||
test_setup(max_files);
|
||||
|
||||
FILE** files = calloc(max_files, sizeof(FILE*));
|
||||
for (size_t i = 0; i < max_files; ++i) {
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "/spiflash/f/%d.txt", i + 1);
|
||||
files[i] = fopen(name, "r");
|
||||
TEST_ASSERT_NOT_NULL(files[i]);
|
||||
}
|
||||
/* close everything and clean up */
|
||||
for (size_t i = 0; i < max_files; ++i) {
|
||||
fclose(files[i]);
|
||||
}
|
||||
free(files);
|
||||
test_teardown();
|
||||
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("(raw) can lseek", "[fatfs]")
|
||||
{
|
||||
test_setup(5);
|
||||
FILE* f = fopen("/spiflash/hello.txt", "r");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 2, SEEK_CUR));
|
||||
TEST_ASSERT_EQUAL('l', fgetc(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 4, SEEK_SET));
|
||||
TEST_ASSERT_EQUAL('o', fgetc(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, -5, SEEK_END));
|
||||
TEST_ASSERT_EQUAL('r', fgetc(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_END));
|
||||
TEST_ASSERT_EQUAL(17, ftell(f));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
|
||||
TEST_ASSERT_EQUAL(14, ftell(f));
|
||||
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(raw) stat returns correct values", "[fatfs]")
|
||||
{
|
||||
test_setup(5);
|
||||
struct tm tm;
|
||||
tm.tm_year = 2018 - 1900;
|
||||
tm.tm_mon = 5; // Note: month can be 0-11 & not 1-12
|
||||
tm.tm_mday = 13;
|
||||
tm.tm_hour = 11;
|
||||
tm.tm_min = 2;
|
||||
tm.tm_sec = 10;
|
||||
time_t t = mktime(&tm);
|
||||
printf("Reference time: %s", asctime(&tm));
|
||||
|
||||
struct stat st;
|
||||
TEST_ASSERT_EQUAL(0, stat("/spiflash/stat.txt", &st));
|
||||
|
||||
time_t mtime = st.st_mtime;
|
||||
struct tm mtm;
|
||||
localtime_r(&mtime, &mtm);
|
||||
printf("File time: %s", asctime(&mtm));
|
||||
TEST_ASSERT(mtime > t); // Modification time should be in future wrt ref time
|
||||
|
||||
TEST_ASSERT(st.st_mode & S_IFREG);
|
||||
TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
|
||||
|
||||
memset(&st, 0, sizeof(st));
|
||||
TEST_ASSERT_EQUAL(0, stat("/spiflash", &st));
|
||||
TEST_ASSERT(st.st_mode & S_IFDIR);
|
||||
TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_CASE("(raw) can opendir root directory of FS", "[fatfs]")
|
||||
{
|
||||
test_setup(5);
|
||||
DIR* dir = opendir("/spiflash");
|
||||
TEST_ASSERT_NOT_NULL(dir);
|
||||
bool found = false;
|
||||
while (true) {
|
||||
struct dirent* de = readdir(dir);
|
||||
if (!de) {
|
||||
break;
|
||||
}
|
||||
if (strcasecmp(de->d_name, "test_opd.txt") == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_TRUE(found);
|
||||
TEST_ASSERT_EQUAL(0, closedir(dir));
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
TEST_CASE("(raw) opendir, readdir, rewinddir, seekdir work as expected", "[fatfs]")
|
||||
{
|
||||
test_setup(5);
|
||||
|
||||
DIR* dir = opendir("/spiflash/dir");
|
||||
TEST_ASSERT_NOT_NULL(dir);
|
||||
int count = 0;
|
||||
const char* names[4];
|
||||
while(count < 4) {
|
||||
struct dirent* de = readdir(dir);
|
||||
if (!de) {
|
||||
break;
|
||||
}
|
||||
printf("found '%s'\n", de->d_name);
|
||||
if (strcasecmp(de->d_name, "1.txt") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "1.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "2.txt") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "2.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "inner") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_DIR);
|
||||
names[count] = "inner";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "boo.bin") == 0) {
|
||||
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||
names[count] = "boo.bin";
|
||||
++count;
|
||||
} else {
|
||||
TEST_FAIL_MESSAGE("unexpected directory entry");
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_EQUAL(count, 4);
|
||||
|
||||
rewinddir(dir);
|
||||
struct dirent* de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
|
||||
seekdir(dir, 3);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
|
||||
seekdir(dir, 1);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
|
||||
seekdir(dir, 2);
|
||||
de = readdir(dir);
|
||||
TEST_ASSERT_NOT_NULL(de);
|
||||
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, closedir(dir));
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char* filename;
|
||||
size_t word_count;
|
||||
int seed;
|
||||
int val;
|
||||
SemaphoreHandle_t done;
|
||||
int result;
|
||||
} read_test_arg_t;
|
||||
|
||||
#define READ_TEST_ARG_INIT(name, seed_, val_) \
|
||||
{ \
|
||||
.filename = name, \
|
||||
.seed = seed_, \
|
||||
.word_count = 8000, \
|
||||
.val = val_, \
|
||||
.done = xSemaphoreCreateBinary() \
|
||||
}
|
||||
|
||||
static void read_task(void* param)
|
||||
{
|
||||
read_test_arg_t* args = (read_test_arg_t*) param;
|
||||
FILE* f = fopen(args->filename, "rb");
|
||||
if (f == NULL) {
|
||||
args->result = ESP_ERR_NOT_FOUND;
|
||||
goto done;
|
||||
}
|
||||
|
||||
srand(args->seed);
|
||||
for (size_t i = 0; i < args->word_count; ++i) {
|
||||
uint32_t rval;
|
||||
int cnt = fread(&rval, sizeof(rval), 1, f);
|
||||
if (cnt != 1 || rval != args->val) {
|
||||
ets_printf("E(r): i=%d, cnt=%d rval=%d val=%d\n\n", i, cnt, rval, args->val);
|
||||
args->result = ESP_FAIL;
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
args->result = ESP_OK;
|
||||
|
||||
close:
|
||||
fclose(f);
|
||||
|
||||
done:
|
||||
xSemaphoreGive(args->done);
|
||||
vTaskDelay(1);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("(raw) multiple tasks can use same volume", "[fatfs]")
|
||||
{
|
||||
test_setup(5);
|
||||
char names[4][64];
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
snprintf(names[i], sizeof(names[i]), "/spiflash/ccrnt/%d.txt", i + 1);
|
||||
}
|
||||
|
||||
read_test_arg_t args1 = READ_TEST_ARG_INIT(names[0], 1, 0x31313131);
|
||||
read_test_arg_t args2 = READ_TEST_ARG_INIT(names[1], 2, 0x32323232);
|
||||
read_test_arg_t args3 = READ_TEST_ARG_INIT(names[2], 3, 0x33333333);
|
||||
read_test_arg_t args4 = READ_TEST_ARG_INIT(names[3], 4, 0x34343434);
|
||||
|
||||
const int cpuid_0 = 0;
|
||||
const int cpuid_1 = portNUM_PROCESSORS - 1;
|
||||
const int stack_size = 4096;
|
||||
|
||||
printf("reading files 1.txt 2.txt 3.txt 4.txt \n");
|
||||
|
||||
xTaskCreatePinnedToCore(&read_task, "r1", stack_size, &args1, 3, NULL, cpuid_1);
|
||||
xTaskCreatePinnedToCore(&read_task, "r2", stack_size, &args2, 3, NULL, cpuid_0);
|
||||
xTaskCreatePinnedToCore(&read_task, "r3", stack_size, &args3, 3, NULL, cpuid_0);
|
||||
xTaskCreatePinnedToCore(&read_task, "r4", stack_size, &args4, 3, NULL, cpuid_1);
|
||||
|
||||
xSemaphoreTake(args1.done, portMAX_DELAY);
|
||||
printf("1.txt done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
|
||||
xSemaphoreTake(args2.done, portMAX_DELAY);
|
||||
printf("2.txt done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
|
||||
xSemaphoreTake(args3.done, portMAX_DELAY);
|
||||
printf("3.txt done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args3.result);
|
||||
xSemaphoreTake(args4.done, portMAX_DELAY);
|
||||
printf("4.txt done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args4.result);
|
||||
|
||||
vSemaphoreDelete(args1.done);
|
||||
vSemaphoreDelete(args2.done);
|
||||
vSemaphoreDelete(args3.done);
|
||||
vSemaphoreDelete(args4.done);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(raw) read speed test", "[fatfs][timeout=60]")
|
||||
{
|
||||
test_setup(5);
|
||||
|
||||
const size_t buf_size = 16 * 1024;
|
||||
uint32_t* buf = (uint32_t*) calloc(1, buf_size);
|
||||
const size_t file_size = 256 * 1024;
|
||||
const char* file = "/spiflash/256k.bin";
|
||||
|
||||
test_fatfs_rw_speed(file, buf, 4 * 1024, file_size, false);
|
||||
test_fatfs_rw_speed(file, buf, 8 * 1024, file_size, false);
|
||||
test_fatfs_rw_speed(file, buf, 16 * 1024, file_size, false);
|
||||
|
||||
free(buf);
|
||||
test_teardown();
|
||||
}
|
304
components/fatfs/test/test_fatfs_sdmmc.c
Normal file
304
components/fatfs/test/test_fatfs_sdmmc.c
Normal file
@ -0,0 +1,304 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#if 0
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/unistd.h>
|
||||
#include "unity.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "ff.h"
|
||||
#include "test_fatfs_common.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef SOC_SDMMC_HOST_SUPPORTED
|
||||
#include "driver/sdmmc_host.h"
|
||||
|
||||
static void test_setup(void)
|
||||
{
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 5,
|
||||
.allocation_unit_size = 16 * 1024
|
||||
};
|
||||
TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL));
|
||||
}
|
||||
|
||||
static void test_teardown(void)
|
||||
{
|
||||
TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount());
|
||||
}
|
||||
|
||||
static const char* test_filename = "/sdcard/hello.txt";
|
||||
|
||||
TEST_CASE("Mount fails cleanly without card inserted", "[fatfs][sd][ignore]")
|
||||
{
|
||||
size_t heap_size;
|
||||
HEAP_SIZE_CAPTURE(heap_size);
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = false,
|
||||
.max_files = 5
|
||||
};
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
printf("Initializing card, attempt %d\n", i);
|
||||
esp_err_t err = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL);
|
||||
printf("err=%d\n", err);
|
||||
TEST_ESP_ERR(ESP_ERR_TIMEOUT, err);
|
||||
}
|
||||
HEAP_SIZE_CHECK(heap_size, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) can create and write file", "[fatfs][sd][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) can read file", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str);
|
||||
test_fatfs_read_file(test_filename);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) can read file with pread()", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str);
|
||||
test_fatfs_pread_file(test_filename);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) pwrite() works well", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_pwrite_file(test_filename);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) overwrite and append file", "[fatfs][sd][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_overwrite_append(test_filename);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) can lseek", "[fatfs][sd][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_lseek("/sdcard/seek.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) can truncate", "[fatfs][sd][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_truncate_file("/sdcard/truncate.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) stat returns correct values", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_stat("/sdcard/stat.txt", "/sdcard");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) utime sets modification time", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_utime("/sdcard/utime.txt", "/sdcard");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) unlink removes a file", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_unlink("/sdcard/unlink.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) link copies a file, rename moves a file", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_link_rename("/sdcard/link");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) can create and remove directories", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_mkdir_rmdir("/sdcard/dir");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) can opendir root directory of FS", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_can_opendir("/sdcard");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) opendir, readdir, rewinddir, seekdir work as expected", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_opendir_readdir_rewinddir("/sdcard/dir");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) multiple tasks can use same volume", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_concurrent("/sdcard/f");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void speed_test(void* buf, size_t buf_size, size_t file_size, bool write);
|
||||
|
||||
TEST_CASE("(SD) write/read speed test", "[fatfs][sd][test_env=UT_T1_SDMODE][timeout=60]")
|
||||
{
|
||||
size_t heap_size;
|
||||
HEAP_SIZE_CAPTURE(heap_size);
|
||||
|
||||
const size_t buf_size = 16 * 1024;
|
||||
uint32_t* buf = (uint32_t*) calloc(1, buf_size);
|
||||
esp_fill_random(buf, buf_size);
|
||||
const size_t file_size = 1 * 1024 * 1024;
|
||||
|
||||
speed_test(buf, 4 * 1024, file_size, true);
|
||||
speed_test(buf, 8 * 1024, file_size, true);
|
||||
speed_test(buf, 16 * 1024, file_size, true);
|
||||
|
||||
speed_test(buf, 4 * 1024, file_size, false);
|
||||
speed_test(buf, 8 * 1024, file_size, false);
|
||||
speed_test(buf, 16 * 1024, file_size, false);
|
||||
|
||||
free(buf);
|
||||
|
||||
HEAP_SIZE_CHECK(heap_size, 0);
|
||||
}
|
||||
|
||||
static void speed_test(void* buf, size_t buf_size, size_t file_size, bool write)
|
||||
{
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = write,
|
||||
.max_files = 5,
|
||||
.allocation_unit_size = 64 * 1024
|
||||
};
|
||||
TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL));
|
||||
|
||||
test_fatfs_rw_speed("/sdcard/4mb.bin", buf, buf_size, file_size, write);
|
||||
|
||||
TEST_ESP_OK(esp_vfs_fat_sdmmc_unmount());
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) mount two FAT partitions, SDMMC and WL, at the same time", "[fatfs][sd][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 5
|
||||
};
|
||||
|
||||
const char* filename_sd = "/sdcard/sd.txt";
|
||||
const char* filename_wl = "/spiflash/wl.txt";
|
||||
const char* str_sd = "this is sd\n";
|
||||
const char* str_wl = "this is spiflash\n";
|
||||
|
||||
/* Erase flash before the firs use */
|
||||
const esp_partition_t *test_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "flash_test");
|
||||
esp_partition_erase_range(test_partition, 0, test_partition->size);
|
||||
printf("Partition erased: addr- 0x%08x, size- 0x%08x\n", test_partition->address, test_partition->size);
|
||||
|
||||
/* Mount FATFS in SD can WL at the same time. Create a file on each FS */
|
||||
wl_handle_t wl_handle = WL_INVALID_HANDLE;
|
||||
test_setup();
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_mount("/spiflash", NULL, &mount_config, &wl_handle));
|
||||
unlink(filename_sd);
|
||||
unlink(filename_wl);
|
||||
test_fatfs_create_file_with_text(filename_sd, str_sd);
|
||||
test_fatfs_create_file_with_text(filename_wl, str_wl);
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount("/spiflash", wl_handle));
|
||||
test_teardown();
|
||||
|
||||
/* Check that the file "sd.txt" was created on FS in SD, and has the right data */
|
||||
test_setup();
|
||||
TEST_ASSERT_NULL(fopen(filename_wl, "r"));
|
||||
FILE* f = fopen(filename_sd, "r");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
char buf[64];
|
||||
TEST_ASSERT_NOT_NULL(fgets(buf, sizeof(buf) - 1, f));
|
||||
TEST_ASSERT_EQUAL(0, strcmp(buf, str_sd));
|
||||
fclose(f);
|
||||
test_teardown();
|
||||
|
||||
/* Check that the file "wl.txt" was created on FS in WL, and has the right data */
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_mount("/spiflash", NULL, &mount_config, &wl_handle));
|
||||
TEST_ASSERT_NULL(fopen(filename_sd, "r"));
|
||||
f = fopen(filename_wl, "r");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
TEST_ASSERT_NOT_NULL(fgets(buf, sizeof(buf) - 1, f));
|
||||
TEST_ASSERT_EQUAL(0, strcmp(buf, str_wl));
|
||||
fclose(f);
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount("/spiflash", wl_handle));
|
||||
}
|
||||
|
||||
/*
|
||||
* In FatFs menuconfig, set CONFIG_FATFS_API_ENCODING to UTF-8 and set the
|
||||
* Codepage to CP936 (Simplified Chinese) in order to run the following tests.
|
||||
* Ensure that the text editor is UTF-8 compatible when compiling these tests.
|
||||
*/
|
||||
#if defined(CONFIG_FATFS_API_ENCODING_UTF_8) && (CONFIG_FATFS_CODEPAGE == 936)
|
||||
|
||||
static const char* test_filename_utf_8 = "/sdcard/测试文件.txt";
|
||||
|
||||
TEST_CASE("(SD) can read file using UTF-8 encoded strings", "[fatfs][sd][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_file_with_text(test_filename_utf_8, fatfs_test_hello_str_utf);
|
||||
test_fatfs_read_file_utf_8(test_filename_utf_8);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) opendir, readdir, rewinddir, seekdir work as expected using UTF-8 encoded strings", "[fatfs][ignore]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_opendir_readdir_rewinddir_utf_8("/sdcard/目录");
|
||||
test_teardown();
|
||||
}
|
||||
#endif // CONFIG_FATFS_API_ENCODING_UTF_8 && CONFIG_FATFS_CODEPAGE == 936
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
225
components/fatfs/test/test_fatfs_spiflash.c
Normal file
225
components/fatfs/test/test_fatfs_spiflash.c
Normal file
@ -0,0 +1,225 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/unistd.h>
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "test_fatfs_common.h"
|
||||
#include "wear_levelling.h"
|
||||
#include "esp_partition.h"
|
||||
|
||||
|
||||
static wl_handle_t s_test_wl_handle;
|
||||
static void test_setup(void)
|
||||
{
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 5
|
||||
};
|
||||
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_mount("/spiflash", NULL, &mount_config, &s_test_wl_handle));
|
||||
}
|
||||
|
||||
static void test_teardown(void)
|
||||
{
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount("/spiflash", s_test_wl_handle));
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can format partition", "[fatfs][wear_levelling]")
|
||||
{
|
||||
const esp_partition_t* part = get_test_data_partition();
|
||||
esp_partition_erase_range(part, 0, part->size);
|
||||
test_setup();
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can create and write file", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can read file", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str);
|
||||
test_fatfs_read_file("/spiflash/hello.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can read file with pread", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str);
|
||||
test_fatfs_pread_file("/spiflash/hello.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) pwrite() works well", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_pwrite_file("/spiflash/hello.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can open maximum number of files", "[fatfs][wear_levelling]")
|
||||
{
|
||||
size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = max_files
|
||||
};
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_mount("/spiflash", NULL, &mount_config, &s_test_wl_handle));
|
||||
test_fatfs_open_max_files("/spiflash/f", max_files);
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount("/spiflash", s_test_wl_handle));
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) overwrite and append file", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_overwrite_append("/spiflash/hello.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can lseek", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_lseek("/spiflash/seek.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can truncate", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_truncate_file("/spiflash/truncate.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_stat("/spiflash/stat.txt", "/spiflash");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) utime sets modification time", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_utime("/spiflash/utime.txt", "/spiflash");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) unlink removes a file", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_unlink("/spiflash/unlink.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) link copies a file, rename moves a file", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_link_rename("/spiflash/link");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can create and remove directories", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_mkdir_rmdir("/spiflash/dir");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can opendir root directory of FS", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_can_opendir("/spiflash");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) opendir, readdir, rewinddir, seekdir work as expected", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_opendir_readdir_rewinddir("/spiflash/dir");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) multiple tasks can use same volume", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_concurrent("/spiflash/f");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) write/read speed test", "[fatfs][wear_levelling][timeout=60]")
|
||||
{
|
||||
/* Erase partition before running the test to get consistent results */
|
||||
const esp_partition_t* part = get_test_data_partition();
|
||||
esp_partition_erase_range(part, 0, part->size);
|
||||
|
||||
test_setup();
|
||||
|
||||
const size_t buf_size = 16 * 1024;
|
||||
uint32_t* buf = (uint32_t*) calloc(1, buf_size);
|
||||
esp_fill_random(buf, buf_size);
|
||||
const size_t file_size = 256 * 1024;
|
||||
const char* file = "/spiflash/256k.bin";
|
||||
|
||||
test_fatfs_rw_speed(file, buf, 4 * 1024, file_size, true);
|
||||
test_fatfs_rw_speed(file, buf, 8 * 1024, file_size, true);
|
||||
test_fatfs_rw_speed(file, buf, 16 * 1024, file_size, true);
|
||||
|
||||
test_fatfs_rw_speed(file, buf, 4 * 1024, file_size, false);
|
||||
test_fatfs_rw_speed(file, buf, 8 * 1024, file_size, false);
|
||||
test_fatfs_rw_speed(file, buf, 16 * 1024, file_size, false);
|
||||
|
||||
unlink(file);
|
||||
|
||||
free(buf);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
/*
|
||||
* In FatFs menuconfig, set CONFIG_FATFS_API_ENCODING to UTF-8 and set the
|
||||
* Codepage to CP936 (Simplified Chinese) in order to run the following tests.
|
||||
* Ensure that the text editor is UTF-8 compatible when compiling these tests.
|
||||
*/
|
||||
#if defined(CONFIG_FATFS_API_ENCODING_UTF_8) && (CONFIG_FATFS_CODEPAGE == 936)
|
||||
TEST_CASE("(WL) can read file with UTF-8 encoded strings", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_file_with_text("/spiflash/测试文件.txt", fatfs_test_hello_str_utf);
|
||||
test_fatfs_read_file_utf_8("/spiflash/测试文件.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) opendir, readdir, rewinddir, seekdir work as expected using UTF-8 encoded strings", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_opendir_readdir_rewinddir_utf_8("/spiflash/目录");
|
||||
test_teardown();
|
||||
}
|
||||
#endif
|
106
components/fatfs/test_fatfs_host/Makefile
Normal file
106
components/fatfs/test_fatfs_host/Makefile
Normal file
@ -0,0 +1,106 @@
|
||||
ifndef COMPONENT
|
||||
COMPONENT := fatfs
|
||||
endif
|
||||
|
||||
COMPONENT_LIB := lib$(COMPONENT).a
|
||||
TEST_PROGRAM := test_$(COMPONENT)
|
||||
|
||||
STUBS_LIB_DIR := ../../../components/spi_flash/sim/stubs
|
||||
STUBS_LIB_BUILD_DIR := $(STUBS_LIB_DIR)/build
|
||||
STUBS_LIB := libstubs.a
|
||||
|
||||
SPI_FLASH_SIM_DIR := ../../../components/spi_flash/sim
|
||||
SPI_FLASH_SIM_BUILD_DIR := $(SPI_FLASH_SIM_DIR)/build
|
||||
SPI_FLASH_SIM_LIB := libspi_flash.a
|
||||
|
||||
WEAR_LEVELLING_DIR := ../../../components/wear_levelling/test_wl_host
|
||||
WEAR_LEVELLING_BUILD_DIR := $(WEAR_LEVELLING_DIR)/build
|
||||
WEAR_LEVELLING_LIB := libwl.a
|
||||
|
||||
include Makefile.files
|
||||
|
||||
all: test
|
||||
|
||||
ifndef SDKCONFIG
|
||||
SDKCONFIG_DIR := $(dir $(realpath sdkconfig/sdkconfig.h))
|
||||
SDKCONFIG := $(SDKCONFIG_DIR)sdkconfig.h
|
||||
else
|
||||
SDKCONFIG_DIR := $(dir $(realpath $(SDKCONFIG)))
|
||||
endif
|
||||
|
||||
INCLUDE_FLAGS := $(addprefix -I, $(INCLUDE_DIRS) $(SDKCONFIG_DIR) ../../../tools/catch)
|
||||
|
||||
CPPFLAGS += $(INCLUDE_FLAGS) -g -m32
|
||||
CXXFLAGS += $(INCLUDE_FLAGS) -std=c++11 -g -m32
|
||||
|
||||
# Build libraries that this component is dependent on
|
||||
$(STUBS_LIB_BUILD_DIR)/$(STUBS_LIB): force
|
||||
$(MAKE) -C $(STUBS_LIB_DIR) lib SDKCONFIG=$(SDKCONFIG)
|
||||
|
||||
$(SPI_FLASH_SIM_BUILD_DIR)/$(SPI_FLASH_SIM_LIB): force
|
||||
$(MAKE) -C $(SPI_FLASH_SIM_DIR) lib SDKCONFIG=$(SDKCONFIG)
|
||||
|
||||
$(WEAR_LEVELLING_BUILD_DIR)/$(WEAR_LEVELLING_LIB): force
|
||||
$(MAKE) -C $(WEAR_LEVELLING_DIR) lib SDKCONFIG=$(SDKCONFIG)
|
||||
|
||||
# Create target for building this component as a library
|
||||
CFILES := $(filter %.c, $(SOURCE_FILES))
|
||||
CPPFILES := $(filter %.cpp, $(SOURCE_FILES))
|
||||
|
||||
CTARGET = ${2}/$(patsubst %.c,%.o,$(notdir ${1}))
|
||||
CPPTARGET = ${2}/$(patsubst %.cpp,%.o,$(notdir ${1}))
|
||||
|
||||
ifndef BUILD_DIR
|
||||
BUILD_DIR := build
|
||||
endif
|
||||
|
||||
OBJ_FILES := $(addprefix $(BUILD_DIR)/, $(filter %.o, $(notdir $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o))))
|
||||
|
||||
define COMPILE_C
|
||||
$(call CTARGET, ${1}, $(BUILD_DIR)) : ${1} $(SDKCONFIG)
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(call CTARGET, ${1}, $(BUILD_DIR)) ${1}
|
||||
endef
|
||||
|
||||
define COMPILE_CPP
|
||||
$(call CPPTARGET, ${1}, $(BUILD_DIR)) : ${1} $(SDKCONFIG)
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $(call CPPTARGET, ${1}, $(BUILD_DIR)) ${1}
|
||||
endef
|
||||
|
||||
$(BUILD_DIR)/$(COMPONENT_LIB): $(OBJ_FILES) $(SDKCONFIG)
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
lib: $(BUILD_DIR)/$(COMPONENT_LIB)
|
||||
|
||||
$(foreach cfile, $(CFILES), $(eval $(call COMPILE_C, $(cfile))))
|
||||
$(foreach cxxfile, $(CPPFILES), $(eval $(call COMPILE_CPP, $(cxxfile))))
|
||||
|
||||
# Create target for building this component as a test
|
||||
TEST_SOURCE_FILES = \
|
||||
test_fatfs.cpp \
|
||||
main.cpp \
|
||||
|
||||
TEST_OBJ_FILES = $(filter %.o, $(TEST_SOURCE_FILES:.cpp=.o) $(TEST_SOURCE_FILES:.c=.o))
|
||||
|
||||
$(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)
|
||||
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)
|
||||
|
||||
test: $(TEST_PROGRAM)
|
||||
./$(TEST_PROGRAM)
|
||||
|
||||
# Create other necessary targets
|
||||
partition_table.bin: partition_table.csv
|
||||
python ../../../components/partition_table/gen_esp32part.py --verify $< $@
|
||||
|
||||
force:
|
||||
|
||||
# Create target to cleanup files
|
||||
clean:
|
||||
$(MAKE) -C $(STUBS_LIB_DIR) clean
|
||||
$(MAKE) -C $(SPI_FLASH_SIM_DIR) clean
|
||||
$(MAKE) -C $(WEAR_LEVELLING_DIR) clean
|
||||
rm -f $(OBJ_FILES) $(TEST_OBJ_FILES) $(TEST_PROGRAM) $(COMPONENT_LIB) partition_table.bin
|
||||
|
||||
.PHONY: all lib test clean force
|
38
components/fatfs/test_fatfs_host/Makefile.files
Normal file
38
components/fatfs/test_fatfs_host/Makefile.files
Normal file
@ -0,0 +1,38 @@
|
||||
SOURCE_FILES := \
|
||||
$(addprefix ../src/, \
|
||||
ff.c \
|
||||
ffunicode.c \
|
||||
) \
|
||||
$(addprefix ../diskio/,\
|
||||
diskio.c \
|
||||
diskio_wl.c \
|
||||
) \
|
||||
../port/linux/ffsystem.c
|
||||
|
||||
INCLUDE_DIRS := \
|
||||
. \
|
||||
../diskio \
|
||||
../src \
|
||||
$(addprefix ../../spi_flash/sim/stubs/, \
|
||||
app_update/include \
|
||||
driver/include \
|
||||
esp32/include \
|
||||
freertos/include \
|
||||
log/include \
|
||||
newlib/include \
|
||||
sdmmc/include \
|
||||
vfs/include \
|
||||
) \
|
||||
$(addprefix ../../../components/, \
|
||||
esp_rom/include \
|
||||
xtensa/include \
|
||||
xtensa/esp32/include \
|
||||
soc/esp32/include \
|
||||
soc/include \
|
||||
esp32/include \
|
||||
esp_common/include \
|
||||
bootloader_support/include \
|
||||
app_update/include \
|
||||
spi_flash/include \
|
||||
wear_levelling/include \
|
||||
)
|
17
components/fatfs/test_fatfs_host/component.mk
Normal file
17
components/fatfs/test_fatfs_host/component.mk
Normal file
@ -0,0 +1,17 @@
|
||||
include $(COMPONENT_PATH)/Makefile.files
|
||||
|
||||
COMPONENT_OWNBUILDTARGET := 1
|
||||
COMPONENT_OWNCLEANTARGET := 1
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := $(INCLUDE_DIRS)
|
||||
|
||||
.PHONY: build
|
||||
build: $(SDKCONFIG_HEADER)
|
||||
$(MAKE) -C $(COMPONENT_PATH) lib SDKCONFIG=$(SDKCONFIG_HEADER) BUILD_DIR=$(COMPONENT_BUILD_DIR) COMPONENT=$(COMPONENT_NAME)
|
||||
|
||||
CLEAN_FILES := component_project_vars.mk
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(summary) RM $(CLEAN_FILES)
|
||||
rm -f $(CLEAN_FILES)
|
||||
$(MAKE) -C $(COMPONENT_PATH) clean SDKCONFIG=$(SDKCONFIG_HEADER) BUILD_DIR=$(COMPONENT_BUILD_DIR) COMPONENT=$(COMPONENT_NAME)
|
2
components/fatfs/test_fatfs_host/main.cpp
Normal file
2
components/fatfs/test_fatfs_host/main.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch.hpp"
|
6
components/fatfs/test_fatfs_host/partition_table.csv
Normal file
6
components/fatfs/test_fatfs_host/partition_table.csv
Normal file
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, fat, , 1M,
|
|
8
components/fatfs/test_fatfs_host/sdkconfig/sdkconfig.h
Normal file
8
components/fatfs/test_fatfs_host/sdkconfig/sdkconfig.h
Normal file
@ -0,0 +1,8 @@
|
||||
# pragma once
|
||||
#define CONFIG_IDF_TARGET_ESP32 1
|
||||
#define CONFIG_WL_SECTOR_SIZE 4096
|
||||
#define CONFIG_LOG_DEFAULT_LEVEL 3
|
||||
#define CONFIG_PARTITION_TABLE_OFFSET 0x8000
|
||||
#define CONFIG_ESPTOOLPY_FLASHSIZE "8MB"
|
||||
//currently use the legacy implementation, since the stubs for new HAL are not done yet
|
||||
#define CONFIG_SPI_FLASH_USE_LEGACY_IMPL
|
93
components/fatfs/test_fatfs_host/test_fatfs.cpp
Normal file
93
components/fatfs/test_fatfs_host/test_fatfs.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ff.h"
|
||||
#include "esp_partition.h"
|
||||
#include "wear_levelling.h"
|
||||
#include "diskio_impl.h"
|
||||
#include "diskio_wl.h"
|
||||
|
||||
#include "catch.hpp"
|
||||
|
||||
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);
|
||||
|
||||
TEST_CASE("create volume, open file, write and read back data", "[fatfs]")
|
||||
{
|
||||
_spi_flash_init(CONFIG_ESPTOOLPY_FLASHSIZE, CONFIG_WL_SECTOR_SIZE * 16, CONFIG_WL_SECTOR_SIZE, CONFIG_WL_SECTOR_SIZE, "partition_table.bin");
|
||||
|
||||
FRESULT fr_result;
|
||||
BYTE pdrv;
|
||||
FATFS fs;
|
||||
FIL file;
|
||||
UINT bw;
|
||||
|
||||
esp_err_t esp_result;
|
||||
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, "storage");
|
||||
|
||||
// Mount wear-levelled partition
|
||||
wl_handle_t wl_handle;
|
||||
esp_result = wl_mount(partition, &wl_handle);
|
||||
REQUIRE(esp_result == ESP_OK);
|
||||
|
||||
// Get a physical drive
|
||||
esp_result = ff_diskio_get_drive(&pdrv);
|
||||
REQUIRE(esp_result == ESP_OK);
|
||||
|
||||
// Register physical drive as wear-levelled partition
|
||||
esp_result = ff_diskio_register_wl_partition(pdrv, wl_handle);
|
||||
|
||||
// Create FAT volume on the entire disk
|
||||
DWORD part_list[] = {100, 0, 0, 0};
|
||||
BYTE work_area[FF_MAX_SS];
|
||||
|
||||
fr_result = f_fdisk(pdrv, part_list, work_area);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
fr_result = f_mkfs("", FM_ANY, 0, work_area, sizeof(work_area)); // Use default volume
|
||||
|
||||
// Mount the volume
|
||||
fr_result = f_mount(&fs, "", 0);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
|
||||
// Open, write and read data
|
||||
fr_result = f_open(&file, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
|
||||
// Generate data
|
||||
uint32_t data_size = 100000;
|
||||
|
||||
char *data = (char*) malloc(data_size);
|
||||
char *read = (char*) malloc(data_size);
|
||||
|
||||
for(uint32_t i = 0; i < data_size; i += sizeof(i))
|
||||
{
|
||||
*((uint32_t*)(data + i)) = i;
|
||||
}
|
||||
|
||||
// Write generated data
|
||||
fr_result = f_write(&file, data, data_size, &bw);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
REQUIRE(bw == data_size);
|
||||
|
||||
// Move to beginning of file
|
||||
fr_result = f_lseek(&file, 0);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
|
||||
// Read written data
|
||||
fr_result = f_read(&file, read, data_size, &bw);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
REQUIRE(bw == data_size);
|
||||
|
||||
REQUIRE(memcmp(data, read, data_size) == 0);
|
||||
|
||||
// Close file
|
||||
fr_result = f_close(&file);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
|
||||
// Unmount default volume
|
||||
fr_result = f_mount(0, "", 0);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
|
||||
free(read);
|
||||
free(data);
|
||||
}
|
236
components/fatfs/vfs/esp_vfs_fat.h
Normal file
236
components/fatfs/vfs/esp_vfs_fat.h
Normal file
@ -0,0 +1,236 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#include "ff.h"
|
||||
#include "wear_levelling.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Register FATFS with VFS component
|
||||
*
|
||||
* This function registers given FAT drive in VFS, at the specified base path.
|
||||
* If only one drive is used, fat_drive argument can be an empty string.
|
||||
* Refer to FATFS library documentation on how to specify FAT drive.
|
||||
* This function also allocates FATFS structure which should be used for f_mount
|
||||
* call.
|
||||
*
|
||||
* @note This function doesn't mount the drive into FATFS, it just connects
|
||||
* POSIX and C standard library IO function with FATFS. You need to mount
|
||||
* desired drive into FATFS separately.
|
||||
*
|
||||
* @param base_path path prefix where FATFS should be registered
|
||||
* @param fat_drive FATFS drive specification; if only one drive is used, can be an empty string
|
||||
* @param max_files maximum number of files which can be open at the same time
|
||||
* @param[out] out_fs pointer to FATFS structure which can be used for FATFS f_mount call is returned via this argument.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_register was already called
|
||||
* - ESP_ERR_NO_MEM if not enough memory or too many VFSes already registered
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive,
|
||||
size_t max_files, FATFS** out_fs);
|
||||
|
||||
/**
|
||||
* @brief Un-register FATFS from VFS
|
||||
*
|
||||
* @note FATFS structure returned by esp_vfs_fat_register is destroyed after
|
||||
* this call. Make sure to call f_mount function to unmount it before
|
||||
* calling esp_vfs_fat_unregister_ctx.
|
||||
* Difference between this function and the one above is that this one
|
||||
* will release the correct drive, while the one above will release
|
||||
* the last registered one
|
||||
*
|
||||
* @param base_path path prefix where FATFS is registered. This is the same
|
||||
* used when esp_vfs_fat_register was called
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_unregister_path(const char* base_path);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Configuration arguments for esp_vfs_fat_sdmmc_mount and esp_vfs_fat_spiflash_mount functions
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* If FAT partition can not be mounted, and this parameter is true,
|
||||
* create partition table and format the filesystem.
|
||||
*/
|
||||
bool format_if_mount_failed;
|
||||
int max_files; ///< Max number of open files
|
||||
/**
|
||||
* If format_if_mount_failed is set, and mount fails, format the card
|
||||
* with given allocation unit size. Must be a power of 2, between sector
|
||||
* size and 128 * sector size.
|
||||
* For SD cards, sector size is always 512 bytes. For wear_levelling,
|
||||
* sector size is determined by CONFIG_WL_SECTOR_SIZE option.
|
||||
*
|
||||
* Using larger allocation unit size will result in higher read/write
|
||||
* performance and higher overhead when storing small files.
|
||||
*
|
||||
* Setting this field to 0 will result in allocation unit set to the
|
||||
* sector size.
|
||||
*/
|
||||
size_t allocation_unit_size;
|
||||
} esp_vfs_fat_mount_config_t;
|
||||
|
||||
// Compatibility definition
|
||||
typedef esp_vfs_fat_mount_config_t esp_vfs_fat_sdmmc_mount_config_t;
|
||||
|
||||
/**
|
||||
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
* - initializes SDMMC driver or SPI driver with configuration in host_config
|
||||
* - initializes SD card with configuration in slot_config
|
||||
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
* For real world applications, developers should implement the logic of
|
||||
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||
* with proper error checking and handling of exceptional conditions.
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||
* @param host_config Pointer to structure describing SDMMC host. When using
|
||||
* SDMMC peripheral, this structure can be initialized using
|
||||
* SDMMC_HOST_DEFAULT() macro. When using SPI peripheral,
|
||||
* this structure can be initialized using SDSPI_HOST_DEFAULT()
|
||||
* macro.
|
||||
* @param slot_config Pointer to structure with slot configuration.
|
||||
* For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t
|
||||
* structure initialized using SDMMC_SLOT_CONFIG_DEFAULT.
|
||||
* For SPI peripheral, pass a pointer to sdspi_slot_config_t
|
||||
* structure initialized using SDSPI_SLOT_CONFIG_DEFAULT.
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path,
|
||||
const sdmmc_host_t* host_config,
|
||||
const void* slot_config,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
sdmmc_card_t** out_card);
|
||||
|
||||
/**
|
||||
* @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_sdmmc_mount
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdmmc_unmount(void);
|
||||
|
||||
/**
|
||||
* @brief Convenience function to initialize FAT filesystem in SPI flash and register it in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
*
|
||||
* - finds the partition with defined partition_label. Partition label should be
|
||||
* configured in the partition table.
|
||||
* - initializes flash wear levelling library on top of the given partition
|
||||
* - mounts FAT partition using FATFS library on top of flash wear levelling
|
||||
* library
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
*
|
||||
* @param base_path path where FATFS partition should be mounted (e.g. "/spiflash")
|
||||
* @param partition_label label of the partition which should be used
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] wl_handle wear levelling driver handle
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from wear levelling library, SPI flash driver, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle);
|
||||
|
||||
/**
|
||||
* @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_spiflash_mount
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/spiflash")
|
||||
* @param wl_handle wear levelling driver handle returned by esp_vfs_fat_spiflash_mount
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount(const char* base_path, wl_handle_t wl_handle);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convenience function to initialize read-only FAT filesystem and register it in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
*
|
||||
* - finds the partition with defined partition_label. Partition label should be
|
||||
* configured in the partition table.
|
||||
* - mounts FAT partition using FATFS library
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* @note Wear levelling is not used when FAT is mounted in read-only mode using this function.
|
||||
*
|
||||
* @param base_path path where FATFS partition should be mounted (e.g. "/spiflash")
|
||||
* @param partition_label label of the partition which should be used
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_rawflash_mount was already called for the same partition
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SPI flash driver, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_rawflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config);
|
||||
|
||||
/**
|
||||
* @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_rawflash_mount
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/spiflash")
|
||||
* @param partition_label label of partition to be unmounted
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_rawflash_unmount(const char* base_path, const char* partition_label);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
956
components/fatfs/vfs/vfs_fat.c
Normal file
956
components/fatfs/vfs/vfs_fat.c
Normal file
@ -0,0 +1,956 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/lock.h>
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_log.h"
|
||||
#include "ff.h"
|
||||
#include "diskio_impl.h"
|
||||
|
||||
typedef struct {
|
||||
char fat_drive[8]; /* FAT drive name */
|
||||
char base_path[ESP_VFS_PATH_MAX]; /* base path in VFS where partition is registered */
|
||||
size_t max_files; /* max number of simultaneously open files; size of files[] array */
|
||||
_lock_t lock; /* guard for access to this structure */
|
||||
FATFS fs; /* fatfs library FS structure */
|
||||
char tmp_path_buf[FILENAME_MAX+3]; /* temporary buffer used to prepend drive name to the path */
|
||||
char tmp_path_buf2[FILENAME_MAX+3]; /* as above; used in functions which take two path arguments */
|
||||
bool *o_append; /* O_APPEND is stored here for each max_files entries (because O_APPEND is not compatible with FA_OPEN_APPEND) */
|
||||
FIL files[0]; /* array with max_files entries; must be the final member of the structure */
|
||||
} vfs_fat_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
DIR dir;
|
||||
long offset;
|
||||
FF_DIR ffdir;
|
||||
FILINFO filinfo;
|
||||
struct dirent cur_dirent;
|
||||
} vfs_fat_dir_t;
|
||||
|
||||
/* Date and time storage formats in FAT */
|
||||
typedef union {
|
||||
struct {
|
||||
uint16_t mday : 5; /* Day of month, 1 - 31 */
|
||||
uint16_t mon : 4; /* Month, 1 - 12 */
|
||||
uint16_t year : 7; /* Year, counting from 1980. E.g. 37 for 2017 */
|
||||
};
|
||||
uint16_t as_int;
|
||||
} fat_date_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint16_t sec : 5; /* Seconds divided by 2. E.g. 21 for 42 seconds */
|
||||
uint16_t min : 6; /* Minutes, 0 - 59 */
|
||||
uint16_t hour : 5; /* Hour, 0 - 23 */
|
||||
};
|
||||
uint16_t as_int;
|
||||
} fat_time_t;
|
||||
|
||||
static const char* TAG = "vfs_fat";
|
||||
|
||||
static ssize_t vfs_fat_write(void* p, int fd, const void * data, size_t size);
|
||||
static off_t vfs_fat_lseek(void* p, int fd, off_t size, int mode);
|
||||
static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size);
|
||||
static ssize_t vfs_fat_pread(void *ctx, int fd, void *dst, size_t size, off_t offset);
|
||||
static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, off_t offset);
|
||||
static int vfs_fat_open(void* ctx, const char * path, int flags, int mode);
|
||||
static int vfs_fat_close(void* ctx, int fd);
|
||||
static int vfs_fat_fstat(void* ctx, int fd, struct stat * st);
|
||||
static int vfs_fat_stat(void* ctx, const char * path, struct stat * st);
|
||||
static int vfs_fat_fsync(void* ctx, int fd);
|
||||
static int vfs_fat_link(void* ctx, const char* n1, const char* n2);
|
||||
static int vfs_fat_unlink(void* ctx, const char *path);
|
||||
static int vfs_fat_rename(void* ctx, const char *src, const char *dst);
|
||||
static DIR* vfs_fat_opendir(void* ctx, const char* name);
|
||||
static struct dirent* vfs_fat_readdir(void* ctx, DIR* pdir);
|
||||
static int vfs_fat_readdir_r(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
|
||||
static long vfs_fat_telldir(void* ctx, DIR* pdir);
|
||||
static void vfs_fat_seekdir(void* ctx, DIR* pdir, long offset);
|
||||
static int vfs_fat_closedir(void* ctx, DIR* pdir);
|
||||
static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode);
|
||||
static int vfs_fat_rmdir(void* ctx, const char* name);
|
||||
static int vfs_fat_access(void* ctx, const char *path, int amode);
|
||||
static int vfs_fat_truncate(void* ctx, const char *path, off_t length);
|
||||
static int vfs_fat_utime(void* ctx, const char *path, const struct utimbuf *times);
|
||||
|
||||
static vfs_fat_ctx_t* s_fat_ctxs[FF_VOLUMES] = { NULL, NULL };
|
||||
//backwards-compatibility with esp_vfs_fat_unregister()
|
||||
static vfs_fat_ctx_t* s_fat_ctx = NULL;
|
||||
|
||||
static size_t find_context_index_by_path(const char* base_path)
|
||||
{
|
||||
for(size_t i=0; i<FF_VOLUMES; i++) {
|
||||
if (s_fat_ctxs[i] && !strcmp(s_fat_ctxs[i]->base_path, base_path)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return FF_VOLUMES;
|
||||
}
|
||||
|
||||
static size_t find_unused_context_index(void)
|
||||
{
|
||||
for(size_t i=0; i<FF_VOLUMES; i++) {
|
||||
if (!s_fat_ctxs[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return FF_VOLUMES;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, size_t max_files, FATFS** out_fs)
|
||||
{
|
||||
size_t ctx = find_context_index_by_path(base_path);
|
||||
if (ctx < FF_VOLUMES) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
ctx = find_unused_context_index();
|
||||
if (ctx == FF_VOLUMES) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
const esp_vfs_t vfs = {
|
||||
.flags = ESP_VFS_FLAG_CONTEXT_PTR,
|
||||
.write_p = &vfs_fat_write,
|
||||
.lseek_p = &vfs_fat_lseek,
|
||||
.read_p = &vfs_fat_read,
|
||||
.pread_p = &vfs_fat_pread,
|
||||
.pwrite_p = &vfs_fat_pwrite,
|
||||
.open_p = &vfs_fat_open,
|
||||
.close_p = &vfs_fat_close,
|
||||
.fstat_p = &vfs_fat_fstat,
|
||||
.stat_p = &vfs_fat_stat,
|
||||
.fsync_p = &vfs_fat_fsync,
|
||||
.link_p = &vfs_fat_link,
|
||||
.unlink_p = &vfs_fat_unlink,
|
||||
.rename_p = &vfs_fat_rename,
|
||||
.opendir_p = &vfs_fat_opendir,
|
||||
.closedir_p = &vfs_fat_closedir,
|
||||
.readdir_p = &vfs_fat_readdir,
|
||||
.readdir_r_p = &vfs_fat_readdir_r,
|
||||
.seekdir_p = &vfs_fat_seekdir,
|
||||
.telldir_p = &vfs_fat_telldir,
|
||||
.mkdir_p = &vfs_fat_mkdir,
|
||||
.rmdir_p = &vfs_fat_rmdir,
|
||||
.access_p = &vfs_fat_access,
|
||||
.truncate_p = &vfs_fat_truncate,
|
||||
.utime_p = &vfs_fat_utime,
|
||||
};
|
||||
size_t ctx_size = sizeof(vfs_fat_ctx_t) + max_files * sizeof(FIL);
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ff_memalloc(ctx_size);
|
||||
if (fat_ctx == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memset(fat_ctx, 0, ctx_size);
|
||||
fat_ctx->o_append = ff_memalloc(max_files * sizeof(bool));
|
||||
if (fat_ctx->o_append == NULL) {
|
||||
free(fat_ctx);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memset(fat_ctx->o_append, 0, max_files * sizeof(bool));
|
||||
fat_ctx->max_files = max_files;
|
||||
strlcpy(fat_ctx->fat_drive, fat_drive, sizeof(fat_ctx->fat_drive) - 1);
|
||||
strlcpy(fat_ctx->base_path, base_path, sizeof(fat_ctx->base_path) - 1);
|
||||
|
||||
esp_err_t err = esp_vfs_register(base_path, &vfs, fat_ctx);
|
||||
if (err != ESP_OK) {
|
||||
free(fat_ctx->o_append);
|
||||
free(fat_ctx);
|
||||
return err;
|
||||
}
|
||||
|
||||
_lock_init(&fat_ctx->lock);
|
||||
s_fat_ctxs[ctx] = fat_ctx;
|
||||
|
||||
//compatibility
|
||||
s_fat_ctx = fat_ctx;
|
||||
|
||||
*out_fs = &fat_ctx->fs;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_unregister_path(const char* base_path)
|
||||
{
|
||||
size_t ctx = find_context_index_by_path(base_path);
|
||||
if (ctx == FF_VOLUMES) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
vfs_fat_ctx_t* fat_ctx = s_fat_ctxs[ctx];
|
||||
esp_err_t err = esp_vfs_unregister(fat_ctx->base_path);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
_lock_close(&fat_ctx->lock);
|
||||
free(fat_ctx->o_append);
|
||||
free(fat_ctx);
|
||||
s_fat_ctxs[ctx] = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int get_next_fd(vfs_fat_ctx_t* fat_ctx)
|
||||
{
|
||||
for (size_t i = 0; i < fat_ctx->max_files; ++i) {
|
||||
if (fat_ctx->files[i].obj.fs == NULL) {
|
||||
return (int) i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int fat_mode_conv(int m)
|
||||
{
|
||||
int res = 0;
|
||||
int acc_mode = m & O_ACCMODE;
|
||||
if (acc_mode == O_RDONLY) {
|
||||
res |= FA_READ;
|
||||
} else if (acc_mode == O_WRONLY) {
|
||||
res |= FA_WRITE;
|
||||
} else if (acc_mode == O_RDWR) {
|
||||
res |= FA_READ | FA_WRITE;
|
||||
}
|
||||
if ((m & O_CREAT) && (m & O_EXCL)) {
|
||||
res |= FA_CREATE_NEW;
|
||||
} else if ((m & O_CREAT) && (m & O_TRUNC)) {
|
||||
res |= FA_CREATE_ALWAYS;
|
||||
} else if (m & O_APPEND) {
|
||||
res |= FA_OPEN_ALWAYS;
|
||||
} else {
|
||||
res |= FA_OPEN_EXISTING;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fresult_to_errno(FRESULT fr)
|
||||
{
|
||||
switch(fr) {
|
||||
case FR_DISK_ERR: return EIO;
|
||||
case FR_INT_ERR: return EIO;
|
||||
case FR_NOT_READY: return ENODEV;
|
||||
case FR_NO_FILE: return ENOENT;
|
||||
case FR_NO_PATH: return ENOENT;
|
||||
case FR_INVALID_NAME: return EINVAL;
|
||||
case FR_DENIED: return EACCES;
|
||||
case FR_EXIST: return EEXIST;
|
||||
case FR_INVALID_OBJECT: return EBADF;
|
||||
case FR_WRITE_PROTECTED: return EACCES;
|
||||
case FR_INVALID_DRIVE: return ENXIO;
|
||||
case FR_NOT_ENABLED: return ENODEV;
|
||||
case FR_NO_FILESYSTEM: return ENODEV;
|
||||
case FR_MKFS_ABORTED: return EINTR;
|
||||
case FR_TIMEOUT: return ETIMEDOUT;
|
||||
case FR_LOCKED: return EACCES;
|
||||
case FR_NOT_ENOUGH_CORE: return ENOMEM;
|
||||
case FR_TOO_MANY_OPEN_FILES: return ENFILE;
|
||||
case FR_INVALID_PARAMETER: return EINVAL;
|
||||
case FR_OK: return 0;
|
||||
}
|
||||
assert(0 && "unhandled FRESULT");
|
||||
return ENOTSUP;
|
||||
}
|
||||
|
||||
static void file_cleanup(vfs_fat_ctx_t* ctx, int fd)
|
||||
{
|
||||
memset(&ctx->files[fd], 0, sizeof(FIL));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepend drive letters to path names
|
||||
* This function returns new path path pointers, pointing to a temporary buffer
|
||||
* inside ctx.
|
||||
* @note Call this function with ctx->lock acquired. Paths are valid while the
|
||||
* lock is held.
|
||||
* @param ctx vfs_fat_ctx_t context
|
||||
* @param[inout] path as input, pointer to the path; as output, pointer to the new path
|
||||
* @param[inout] path2 as input, pointer to the path; as output, pointer to the new path
|
||||
*/
|
||||
static void prepend_drive_to_path(vfs_fat_ctx_t * ctx, const char ** path, const char ** path2){
|
||||
snprintf(ctx->tmp_path_buf, sizeof(ctx->tmp_path_buf), "%s%s", ctx->fat_drive, *path);
|
||||
*path = ctx->tmp_path_buf;
|
||||
if(path2){
|
||||
snprintf(ctx->tmp_path_buf2, sizeof(ctx->tmp_path_buf2), "%s%s", ((vfs_fat_ctx_t*)ctx)->fat_drive, *path2);
|
||||
*path2 = ctx->tmp_path_buf2;
|
||||
}
|
||||
}
|
||||
|
||||
static int vfs_fat_open(void* ctx, const char * path, int flags, int mode)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s: path=\"%s\", flags=%x, mode=%x", __func__, path, flags, mode);
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &path, NULL);
|
||||
int fd = get_next_fd(fat_ctx);
|
||||
if (fd < 0) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGE(TAG, "open: no free file descriptors");
|
||||
errno = ENFILE;
|
||||
return -1;
|
||||
}
|
||||
FRESULT res = f_open(&fat_ctx->files[fd], path, fat_mode_conv(flags));
|
||||
if (res != FR_OK) {
|
||||
file_cleanup(fat_ctx, fd);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
// O_APPEND need to be stored because it is not compatible with FA_OPEN_APPEND:
|
||||
// - FA_OPEN_APPEND means to jump to the end of file only after open()
|
||||
// - O_APPEND means to jump to the end only before each write()
|
||||
// Other VFS drivers handles O_APPEND well (to the best of my knowledge),
|
||||
// therefore this flag is stored here (at this VFS level) in order to save
|
||||
// memory.
|
||||
fat_ctx->o_append[fd] = (flags & O_APPEND) == O_APPEND;
|
||||
_lock_release(&fat_ctx->lock);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static ssize_t vfs_fat_write(void* ctx, int fd, const void * data, size_t size)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
FIL* file = &fat_ctx->files[fd];
|
||||
FRESULT res;
|
||||
if (fat_ctx->o_append[fd]) {
|
||||
if ((res = f_lseek(file, f_size(file))) != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
unsigned written = 0;
|
||||
res = f_write(file, data, size, &written);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
if (written == 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
FIL* file = &fat_ctx->files[fd];
|
||||
unsigned read = 0;
|
||||
FRESULT res = f_read(file, dst, size, &read);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
if (read == 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
static ssize_t vfs_fat_pread(void *ctx, int fd, void *dst, size_t size, off_t offset)
|
||||
{
|
||||
ssize_t ret = -1;
|
||||
vfs_fat_ctx_t *fat_ctx = (vfs_fat_ctx_t *) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
FIL *file = &fat_ctx->files[fd];
|
||||
const off_t prev_pos = f_tell(file);
|
||||
|
||||
FRESULT f_res = f_lseek(file, offset);
|
||||
if (f_res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
errno = fresult_to_errno(f_res);
|
||||
goto pread_release;
|
||||
}
|
||||
|
||||
unsigned read = 0;
|
||||
f_res = f_read(file, dst, size, &read);
|
||||
if (f_res == FR_OK) {
|
||||
ret = read;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
errno = fresult_to_errno(f_res);
|
||||
// No return yet - need to restore previous position
|
||||
}
|
||||
|
||||
f_res = f_lseek(file, prev_pos);
|
||||
if (f_res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
if (ret >= 0) {
|
||||
errno = fresult_to_errno(f_res);
|
||||
} // else f_read failed so errno shouldn't be overwritten
|
||||
ret = -1; // in case the read was successful but the seek wasn't
|
||||
}
|
||||
|
||||
pread_release:
|
||||
_lock_release(&fat_ctx->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, off_t offset)
|
||||
{
|
||||
ssize_t ret = -1;
|
||||
vfs_fat_ctx_t *fat_ctx = (vfs_fat_ctx_t *) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
FIL *file = &fat_ctx->files[fd];
|
||||
const off_t prev_pos = f_tell(file);
|
||||
|
||||
FRESULT f_res = f_lseek(file, offset);
|
||||
if (f_res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
errno = fresult_to_errno(f_res);
|
||||
goto pwrite_release;
|
||||
}
|
||||
|
||||
unsigned wr = 0;
|
||||
f_res = f_write(file, src, size, &wr);
|
||||
if (f_res == FR_OK) {
|
||||
ret = wr;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
errno = fresult_to_errno(f_res);
|
||||
// No return yet - need to restore previous position
|
||||
}
|
||||
|
||||
f_res = f_lseek(file, prev_pos);
|
||||
if (f_res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
if (ret >= 0) {
|
||||
errno = fresult_to_errno(f_res);
|
||||
} // else f_write failed so errno shouldn't be overwritten
|
||||
ret = -1; // in case the write was successful but the seek wasn't
|
||||
}
|
||||
|
||||
pwrite_release:
|
||||
_lock_release(&fat_ctx->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vfs_fat_fsync(void* ctx, int fd)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
FIL* file = &fat_ctx->files[fd];
|
||||
FRESULT res = f_sync(file);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
int rc = 0;
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
rc = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vfs_fat_close(void* ctx, int fd)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
FIL* file = &fat_ctx->files[fd];
|
||||
FRESULT res = f_close(file);
|
||||
file_cleanup(fat_ctx, fd);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
int rc = 0;
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
rc = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static off_t vfs_fat_lseek(void* ctx, int fd, off_t offset, int mode)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
FIL* file = &fat_ctx->files[fd];
|
||||
off_t new_pos;
|
||||
if (mode == SEEK_SET) {
|
||||
new_pos = offset;
|
||||
} else if (mode == SEEK_CUR) {
|
||||
off_t cur_pos = f_tell(file);
|
||||
new_pos = cur_pos + offset;
|
||||
} else if (mode == SEEK_END) {
|
||||
off_t size = f_size(file);
|
||||
new_pos = size + offset;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
FRESULT res = f_lseek(file, new_pos);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
return new_pos;
|
||||
}
|
||||
|
||||
static int vfs_fat_fstat(void* ctx, int fd, struct stat * st)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
FIL* file = &fat_ctx->files[fd];
|
||||
st->st_size = f_size(file);
|
||||
st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
|
||||
st->st_mtime = 0;
|
||||
st->st_atime = 0;
|
||||
st->st_ctime = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline mode_t get_stat_mode(bool is_dir)
|
||||
{
|
||||
return S_IRWXU | S_IRWXG | S_IRWXO |
|
||||
((is_dir) ? S_IFDIR : S_IFREG);
|
||||
}
|
||||
|
||||
static int vfs_fat_stat(void* ctx, const char * path, struct stat * st)
|
||||
{
|
||||
if (strcmp(path, "/") == 0) {
|
||||
/* FatFS f_stat function does not work for the drive root.
|
||||
* Just pretend that this is a directory.
|
||||
*/
|
||||
memset(st, 0, sizeof(*st));
|
||||
st->st_mode = get_stat_mode(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &path, NULL);
|
||||
FILINFO info;
|
||||
FRESULT res = f_stat(path, &info);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(st, 0, sizeof(*st));
|
||||
st->st_size = info.fsize;
|
||||
st->st_mode = get_stat_mode((info.fattrib & AM_DIR) != 0);
|
||||
fat_date_t fdate = { .as_int = info.fdate };
|
||||
fat_time_t ftime = { .as_int = info.ftime };
|
||||
struct tm tm = {
|
||||
.tm_mday = fdate.mday,
|
||||
.tm_mon = fdate.mon - 1, /* unlike tm_mday, tm_mon is zero-based */
|
||||
.tm_year = fdate.year + 80,
|
||||
.tm_sec = ftime.sec * 2,
|
||||
.tm_min = ftime.min,
|
||||
.tm_hour = ftime.hour
|
||||
};
|
||||
st->st_mtime = mktime(&tm);
|
||||
st->st_atime = 0;
|
||||
st->st_ctime = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfs_fat_unlink(void* ctx, const char *path)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &path, NULL);
|
||||
FRESULT res = f_unlink(path);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfs_fat_link(void* ctx, const char* n1, const char* n2)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &n1, &n2);
|
||||
const size_t copy_buf_size = fat_ctx->fs.csize;
|
||||
FRESULT res;
|
||||
FIL* pf1 = (FIL*) ff_memalloc(sizeof(FIL));
|
||||
FIL* pf2 = (FIL*) ff_memalloc(sizeof(FIL));
|
||||
void* buf = ff_memalloc(copy_buf_size);
|
||||
if (buf == NULL || pf1 == NULL || pf2 == NULL) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "alloc failed, pf1=%p, pf2=%p, buf=%p", pf1, pf2, buf);
|
||||
free(pf1);
|
||||
free(pf2);
|
||||
free(buf);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
memset(pf1, 0, sizeof(*pf1));
|
||||
memset(pf2, 0, sizeof(*pf2));
|
||||
res = f_open(pf1, n1, FA_READ | FA_OPEN_EXISTING);
|
||||
if (res != FR_OK) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
goto fail1;
|
||||
}
|
||||
res = f_open(pf2, n2, FA_WRITE | FA_CREATE_NEW);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
if (res != FR_OK) {
|
||||
goto fail2;
|
||||
}
|
||||
size_t size_left = f_size(pf1);
|
||||
while (size_left > 0) {
|
||||
size_t will_copy = (size_left < copy_buf_size) ? size_left : copy_buf_size;
|
||||
size_t read;
|
||||
res = f_read(pf1, buf, will_copy, &read);
|
||||
if (res != FR_OK) {
|
||||
goto fail3;
|
||||
} else if (read != will_copy) {
|
||||
res = FR_DISK_ERR;
|
||||
goto fail3;
|
||||
}
|
||||
size_t written;
|
||||
res = f_write(pf2, buf, will_copy, &written);
|
||||
if (res != FR_OK) {
|
||||
goto fail3;
|
||||
} else if (written != will_copy) {
|
||||
res = FR_DISK_ERR;
|
||||
goto fail3;
|
||||
}
|
||||
size_left -= will_copy;
|
||||
}
|
||||
fail3:
|
||||
f_close(pf2);
|
||||
free(pf2);
|
||||
fail2:
|
||||
f_close(pf1);
|
||||
free(pf1);
|
||||
fail1:
|
||||
free(buf);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfs_fat_rename(void* ctx, const char *src, const char *dst)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &src, &dst);
|
||||
FRESULT res = f_rename(src, dst);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DIR* vfs_fat_opendir(void* ctx, const char* name)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &name, NULL);
|
||||
vfs_fat_dir_t* fat_dir = ff_memalloc(sizeof(vfs_fat_dir_t));
|
||||
if (!fat_dir) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
memset(fat_dir, 0, sizeof(*fat_dir));
|
||||
|
||||
FRESULT res = f_opendir(&fat_dir->ffdir, name);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
if (res != FR_OK) {
|
||||
free(fat_dir);
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return NULL;
|
||||
}
|
||||
return (DIR*) fat_dir;
|
||||
}
|
||||
|
||||
static int vfs_fat_closedir(void* ctx, DIR* pdir)
|
||||
{
|
||||
assert(pdir);
|
||||
vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir;
|
||||
FRESULT res = f_closedir(&fat_dir->ffdir);
|
||||
free(pdir);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dirent* vfs_fat_readdir(void* ctx, DIR* pdir)
|
||||
{
|
||||
vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir;
|
||||
struct dirent* out_dirent;
|
||||
int err = vfs_fat_readdir_r(ctx, pdir, &fat_dir->cur_dirent, &out_dirent);
|
||||
if (err != 0) {
|
||||
errno = err;
|
||||
return NULL;
|
||||
}
|
||||
return out_dirent;
|
||||
}
|
||||
|
||||
static int vfs_fat_readdir_r(void* ctx, DIR* pdir,
|
||||
struct dirent* entry, struct dirent** out_dirent)
|
||||
{
|
||||
assert(pdir);
|
||||
vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir;
|
||||
FRESULT res = f_readdir(&fat_dir->ffdir, &fat_dir->filinfo);
|
||||
if (res != FR_OK) {
|
||||
*out_dirent = NULL;
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
return fresult_to_errno(res);
|
||||
}
|
||||
if (fat_dir->filinfo.fname[0] == 0) {
|
||||
// end of directory
|
||||
*out_dirent = NULL;
|
||||
return 0;
|
||||
}
|
||||
entry->d_ino = 0;
|
||||
if (fat_dir->filinfo.fattrib & AM_DIR) {
|
||||
entry->d_type = DT_DIR;
|
||||
} else {
|
||||
entry->d_type = DT_REG;
|
||||
}
|
||||
strlcpy(entry->d_name, fat_dir->filinfo.fname,
|
||||
sizeof(entry->d_name));
|
||||
fat_dir->offset++;
|
||||
*out_dirent = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long vfs_fat_telldir(void* ctx, DIR* pdir)
|
||||
{
|
||||
assert(pdir);
|
||||
vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir;
|
||||
return fat_dir->offset;
|
||||
}
|
||||
|
||||
static void vfs_fat_seekdir(void* ctx, DIR* pdir, long offset)
|
||||
{
|
||||
assert(pdir);
|
||||
vfs_fat_dir_t* fat_dir = (vfs_fat_dir_t*) pdir;
|
||||
FRESULT res;
|
||||
if (offset < fat_dir->offset) {
|
||||
res = f_rewinddir(&fat_dir->ffdir);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: rewinddir fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return;
|
||||
}
|
||||
fat_dir->offset = 0;
|
||||
}
|
||||
while (fat_dir->offset < offset) {
|
||||
res = f_readdir(&fat_dir->ffdir, &fat_dir->filinfo);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: f_readdir fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return;
|
||||
}
|
||||
fat_dir->offset++;
|
||||
}
|
||||
}
|
||||
|
||||
static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode)
|
||||
{
|
||||
(void) mode;
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &name, NULL);
|
||||
FRESULT res = f_mkdir(name);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfs_fat_rmdir(void* ctx, const char* name)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &name, NULL);
|
||||
FRESULT res = f_unlink(name);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfs_fat_access(void* ctx, const char *path, int amode)
|
||||
{
|
||||
FILINFO info;
|
||||
int ret = 0;
|
||||
FRESULT res;
|
||||
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &path, NULL);
|
||||
res = f_stat(path, &info);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
|
||||
if (res == FR_OK) {
|
||||
if (((amode & W_OK) == W_OK) && ((info.fattrib & AM_RDO) == AM_RDO)) {
|
||||
ret = -1;
|
||||
errno = EACCES;
|
||||
}
|
||||
// There is no flag to test readable or executable: we assume that if
|
||||
// it exists then it is readable and executable
|
||||
} else {
|
||||
ret = -1;
|
||||
errno = ENOENT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vfs_fat_truncate(void* ctx, const char *path, off_t length)
|
||||
{
|
||||
FRESULT res;
|
||||
FIL* file;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &path, NULL);
|
||||
|
||||
file = (FIL*) ff_memalloc(sizeof(FIL));
|
||||
if (file == NULL) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "truncate alloc failed");
|
||||
errno = ENOMEM;
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
memset(file, 0, sizeof(*file));
|
||||
|
||||
res = f_open(file, path, FA_WRITE);
|
||||
|
||||
if (res != FR_OK) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = f_size(file);
|
||||
|
||||
if (res < length) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "truncate does not support extending size");
|
||||
errno = EPERM;
|
||||
ret = -1;
|
||||
goto close;
|
||||
}
|
||||
|
||||
res = f_lseek(file, length);
|
||||
if (res != FR_OK) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
ret = -1;
|
||||
goto close;
|
||||
}
|
||||
|
||||
res = f_truncate(file);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
close:
|
||||
res = f_close(file);
|
||||
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGE(TAG, "closing file opened for truncate failed");
|
||||
// Overwrite previous errors, since not being able to close
|
||||
// an opened file is a more critical issue.
|
||||
errno = fresult_to_errno(res);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
free(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vfs_fat_utime(void *ctx, const char *path, const struct utimbuf *times)
|
||||
{
|
||||
FILINFO filinfo_time;
|
||||
|
||||
{
|
||||
struct tm tm_time;
|
||||
|
||||
if (times) {
|
||||
localtime_r(×->modtime, &tm_time);
|
||||
} else {
|
||||
// use current time
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
localtime_r(&tv.tv_sec, &tm_time);
|
||||
}
|
||||
|
||||
if (tm_time.tm_year < 80) {
|
||||
// FATFS cannot handle years before 1980
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fat_date_t fdate;
|
||||
fat_time_t ftime;
|
||||
|
||||
// this time transformation is esentially the reverse of the one in vfs_fat_stat()
|
||||
fdate.mday = tm_time.tm_mday;
|
||||
fdate.mon = tm_time.tm_mon + 1; // January in fdate.mon is 1, and 0 in tm_time.tm_mon
|
||||
fdate.year = tm_time.tm_year - 80; // tm_time.tm_year=0 is 1900, tm_time.tm_year=0 is 1980
|
||||
ftime.sec = tm_time.tm_sec / 2, // ftime.sec counts seconds by 2
|
||||
ftime.min = tm_time.tm_min;
|
||||
ftime.hour = tm_time.tm_hour;
|
||||
|
||||
filinfo_time.fdate = fdate.as_int;
|
||||
filinfo_time.ftime = ftime.as_int;
|
||||
}
|
||||
|
||||
vfs_fat_ctx_t *fat_ctx = (vfs_fat_ctx_t *) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
prepend_drive_to_path(fat_ctx, &path, NULL);
|
||||
FRESULT res = f_utime(path, &filinfo_time);
|
||||
_lock_release(&fat_ctx->lock);
|
||||
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
30
components/fatfs/vfs/vfs_fat_internal.h
Normal file
30
components/fatfs/vfs/vfs_fat_internal.h
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_vfs_fat.h"
|
||||
#include <sys/param.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static inline size_t esp_vfs_fat_get_allocation_unit_size(
|
||||
size_t sector_size, size_t requested_size)
|
||||
{
|
||||
size_t alloc_unit_size = requested_size;
|
||||
const size_t max_sectors_per_cylinder = 128;
|
||||
const size_t max_size = sector_size * max_sectors_per_cylinder;
|
||||
alloc_unit_size = MAX(alloc_unit_size, sector_size);
|
||||
alloc_unit_size = MIN(alloc_unit_size, max_size);
|
||||
return alloc_unit_size;
|
||||
}
|
186
components/fatfs/vfs/vfs_fat_sdmmc.c
Normal file
186
components/fatfs/vfs/vfs_fat_sdmmc.c
Normal file
@ -0,0 +1,186 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "vfs_fat_internal.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "diskio_impl.h"
|
||||
#include "diskio_sdmmc.h"
|
||||
|
||||
static const char* TAG = "vfs_fat_sdmmc";
|
||||
static sdmmc_card_t* s_card = NULL;
|
||||
static uint8_t s_pdrv = 0;
|
||||
static char * s_base_path = NULL;
|
||||
|
||||
esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path,
|
||||
const sdmmc_host_t* host_config,
|
||||
const void* slot_config,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
sdmmc_card_t** out_card)
|
||||
{
|
||||
const size_t workbuf_size = 4096;
|
||||
void* workbuf = NULL;
|
||||
FATFS* fs = NULL;
|
||||
|
||||
if (s_card != NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// connect SDMMC driver to FATFS
|
||||
BYTE pdrv = 0xFF;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == 0xFF) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
s_base_path = strdup(base_path);
|
||||
if(!s_base_path){
|
||||
ESP_LOGD(TAG, "could not copy base_path");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
esp_err_t err = ESP_OK;
|
||||
// not using ff_memalloc here, as allocation in internal RAM is preferred
|
||||
s_card = malloc(sizeof(sdmmc_card_t));
|
||||
if (s_card == NULL) {
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = (*host_config->init)();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "host init returned rc=0x%x", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// configure SD slot
|
||||
if (host_config->flags == SDMMC_HOST_FLAG_SPI) {
|
||||
err = sdspi_host_init_slot(host_config->slot,
|
||||
(const sdspi_slot_config_t*) slot_config);
|
||||
} else {
|
||||
err = sdmmc_host_init_slot(host_config->slot,
|
||||
(const sdmmc_slot_config_t*) slot_config);
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "slot_config returned rc=0x%x", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// probe and initialize card
|
||||
err = sdmmc_card_init(host_config, s_card);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "sdmmc_card_init failed 0x(%x)", err);
|
||||
goto fail;
|
||||
}
|
||||
if (out_card != NULL) {
|
||||
*out_card = s_card;
|
||||
}
|
||||
|
||||
ff_diskio_register_sdmmc(pdrv, s_card);
|
||||
s_pdrv = pdrv;
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
// connect FATFS to VFS
|
||||
err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT res = f_mount(fs, drv, 1);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGW(TAG, "failed to mount card (%d)", res);
|
||||
if (!((res == FR_NO_FILESYSTEM || res == FR_INT_ERR)
|
||||
&& mount_config->format_if_mount_failed)) {
|
||||
goto fail;
|
||||
}
|
||||
ESP_LOGW(TAG, "partitioning card");
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
if (workbuf == NULL) {
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
DWORD plist[] = {100, 0, 0, 0};
|
||||
res = f_fdisk(s_pdrv, plist, workbuf);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_fdisk failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
|
||||
s_card->csd.sector_size,
|
||||
mount_config->allocation_unit_size);
|
||||
ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size);
|
||||
res = f_mkfs(drv, FM_ANY, alloc_unit_size, workbuf, workbuf_size);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
free(workbuf);
|
||||
workbuf = NULL;
|
||||
ESP_LOGW(TAG, "mounting again");
|
||||
res = f_mount(fs, drv, 0);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
host_config->deinit();
|
||||
free(workbuf);
|
||||
if (fs) {
|
||||
f_mount(NULL, drv, 0);
|
||||
}
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
free(s_card);
|
||||
s_card = NULL;
|
||||
free(s_base_path);
|
||||
s_base_path = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdmmc_unmount(void)
|
||||
{
|
||||
if (s_card == NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
// unmount
|
||||
char drv[3] = {(char)('0' + s_pdrv), ':', 0};
|
||||
f_mount(0, drv, 0);
|
||||
// release SD driver
|
||||
esp_err_t (*host_deinit)(void) = s_card->host.deinit;
|
||||
ff_diskio_unregister(s_pdrv);
|
||||
free(s_card);
|
||||
s_card = NULL;
|
||||
(*host_deinit)();
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(s_base_path);
|
||||
free(s_base_path);
|
||||
s_base_path = NULL;
|
||||
return err;
|
||||
}
|
209
components/fatfs/vfs/vfs_fat_spiflash.c
Normal file
209
components/fatfs/vfs/vfs_fat_spiflash.c
Normal file
@ -0,0 +1,209 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "vfs_fat_internal.h"
|
||||
#include "diskio_impl.h"
|
||||
|
||||
#include "diskio_rawflash.h"
|
||||
|
||||
#include "wear_levelling.h"
|
||||
#include "diskio_wl.h"
|
||||
|
||||
static const char *TAG = "vfs_fat_spiflash";
|
||||
esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
const size_t workbuf_size = 4096;
|
||||
void *workbuf = NULL;
|
||||
|
||||
esp_partition_subtype_t subtype = partition_label ?
|
||||
ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_FAT;
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
subtype, partition_label);
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
result = wl_mount(data_partition, wl_handle);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to mount wear levelling layer. result = %i", result);
|
||||
return result;
|
||||
}
|
||||
// connect driver to FATFS
|
||||
BYTE pdrv = 0xFF;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
result = ff_diskio_register_wl_partition(pdrv, *wl_handle);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ff_diskio_register_wl_partition failed pdrv=%i, error - 0x(%x)", pdrv, result);
|
||||
goto fail;
|
||||
}
|
||||
FATFS *fs;
|
||||
result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
if (result == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (result != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT fresult = f_mount(fs, drv, 1);
|
||||
if (fresult != FR_OK) {
|
||||
ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
|
||||
if (!((fresult == FR_NO_FILESYSTEM || fresult == FR_INT_ERR)
|
||||
&& mount_config->format_if_mount_failed)) {
|
||||
result = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
if (workbuf == NULL) {
|
||||
result = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
|
||||
CONFIG_WL_SECTOR_SIZE,
|
||||
mount_config->allocation_unit_size);
|
||||
ESP_LOGI(TAG, "Formatting FATFS partition, allocation unit size=%d", alloc_unit_size);
|
||||
fresult = f_mkfs(drv, FM_ANY | FM_SFD, alloc_unit_size, workbuf, workbuf_size);
|
||||
if (fresult != FR_OK) {
|
||||
result = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "f_mkfs failed (%d)", fresult);
|
||||
goto fail;
|
||||
}
|
||||
free(workbuf);
|
||||
workbuf = NULL;
|
||||
ESP_LOGI(TAG, "Mounting again");
|
||||
fresult = f_mount(fs, drv, 0);
|
||||
if (fresult != FR_OK) {
|
||||
result = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "f_mount failed after formatting (%d)", fresult);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
free(workbuf);
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount(const char *base_path, wl_handle_t wl_handle)
|
||||
{
|
||||
BYTE pdrv = ff_diskio_get_pdrv_wl(wl_handle);
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
f_mount(0, drv, 0);
|
||||
ff_diskio_unregister(pdrv);
|
||||
ff_diskio_clear_pdrv_wl(wl_handle);
|
||||
// release partition driver
|
||||
esp_err_t err_drv = wl_unmount(wl_handle);
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||
if (err == ESP_OK) err = err_drv;
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_rawflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label);
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// connect driver to FATFS
|
||||
BYTE pdrv = 0xFF;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
result = ff_diskio_register_raw_partition(pdrv, data_partition);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ff_diskio_register_raw_partition failed pdrv=%i, error - 0x(%x)", pdrv, result);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
FATFS *fs;
|
||||
result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
if (result == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (result != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT fresult = f_mount(fs, drv, 1);
|
||||
if (fresult != FR_OK) {
|
||||
ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
|
||||
result = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_vfs_fat_rawflash_unmount(const char *base_path, const char* partition_label)
|
||||
{
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label);
|
||||
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
BYTE pdrv = ff_diskio_get_pdrv_raw(data_partition);
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
f_mount(0, drv, 0);
|
||||
ff_diskio_unregister(pdrv);
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||
return err;
|
||||
}
|
@ -11,7 +11,7 @@ else ifdef CONFIG_NEWLIB_LIBRARY_LEVEL_FLOAT_NANO
|
||||
LIB_PATH := $(COMPONENT_PATH)/newlib/lib/libc_fnano.a $(COMPONENT_PATH)/newlib/lib/libm.a
|
||||
endif
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS += newlib/port/include newlib/include
|
||||
COMPONENT_ADD_INCLUDEDIRS := newlib/port/include newlib/include
|
||||
COMPONENT_SRCDIRS += newlib/port
|
||||
COMPONENT_ADD_LDFLAGS := $(LIB_PATH) -lnewlib
|
||||
COMPONENT_ADD_LINKER_DEPS := $(LIB_PATH)
|
||||
|
@ -110,7 +110,7 @@ typedef _fpos64_t fpos64_t;
|
||||
#ifdef __FOPEN_MAX__
|
||||
#define FOPEN_MAX __FOPEN_MAX__
|
||||
#else
|
||||
#define FOPEN_MAX 20
|
||||
#define FOPEN_MAX 10
|
||||
#endif
|
||||
|
||||
#ifdef __FILENAME_MAX__
|
||||
|
31
components/newlib/newlib/port/include/sys/unistd.h
Normal file
31
components/newlib/newlib/port/include/sys/unistd.h
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
#ifndef _ESP_SYS_UNISTD_H
|
||||
#define _ESP_SYS_UNISTD_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include_next <sys/unistd.h>
|
||||
|
||||
int truncate(const char *, off_t __length);
|
||||
int gethostname(char *__name, size_t __len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _SYS_UNISTD_H */
|
35
components/newlib/newlib/port/include/sys/utime.h
Normal file
35
components/newlib/newlib/port/include/sys/utime.h
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _UTIME_H_
|
||||
#define _UTIME_H_
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct utimbuf {
|
||||
time_t actime; // access time
|
||||
time_t modtime; // modification time
|
||||
};
|
||||
|
||||
int utime(const char *path, const struct utimbuf *times);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* _UTIME_H_ */
|
21
components/newlib/newlib/port/pread.c
Normal file
21
components/newlib/newlib/port/pread.c
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <unistd.h>
|
||||
#include "esp_vfs.h"
|
||||
|
||||
ssize_t pread(int fd, void *dst, size_t size, off_t offset)
|
||||
{
|
||||
return esp_vfs_pread(fd, dst, size, offset);
|
||||
}
|
21
components/newlib/newlib/port/pwrite.c
Normal file
21
components/newlib/newlib/port/pwrite.c
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <unistd.h>
|
||||
#include "esp_vfs.h"
|
||||
|
||||
ssize_t pwrite(int fd, const void *src, size_t size, off_t offset)
|
||||
{
|
||||
return esp_vfs_pwrite(fd, src, size, offset);
|
||||
}
|
21
components/newlib/newlib/port/utime.c
Normal file
21
components/newlib/newlib/port/utime.c
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <utime.h>
|
||||
#include "esp_vfs.h"
|
||||
|
||||
int utime(const char *path, const struct utimbuf *times)
|
||||
{
|
||||
return esp_vfs_utime(path, times);
|
||||
}
|
@ -9,6 +9,6 @@ factory, 0, 0, 0x10000, 0xF0000
|
||||
ota_0, 0, ota_0, , 64K
|
||||
ota_1, 0, ota_1, , 64K
|
||||
# flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests
|
||||
flash_test, data, fat, , 528K
|
||||
flash_test, data, fat, , 896K
|
||||
|
||||
# Note: still 1MB of a 4MB flash left free for some other purpose
|
||||
|
|
Reference in New Issue
Block a user