diff options
| author | bhgv <bhgv.empire@gmail.com> | 2020-05-12 12:50:49 +0300 |
|---|---|---|
| committer | bhgv <bhgv.empire@gmail.com> | 2020-05-12 12:50:49 +0300 |
| commit | 9b5d5f8a4640dbecdc87e5b6e7e95f71018632cf (patch) | |
| tree | d3135c3861ef93ed2523642d3c5f64c7819b7def /libiot/vfs | |
| parent | 73c13e732072c17f3e584e11a51d1f7dc8d88e32 (diff) | |
| parent | 31b4edc67b75658ce5e2d41f2fc87331f4b26d49 (diff) | |
Merge branch 'master' of https://github.com/bhgv/Inferno-OS-bhgv
Diffstat (limited to 'libiot/vfs')
| -rw-r--r-- | libiot/vfs/fat.c | 453 | ||||
| -rw-r--r-- | libiot/vfs/include/esp_vfs.h | 424 | ||||
| -rw-r--r-- | libiot/vfs/include/sys/dirent.h | 56 | ||||
| -rw-r--r-- | libiot/vfs/lfs.c | 949 | ||||
| -rw-r--r-- | libiot/vfs/mkfile | 7 | ||||
| -rw-r--r-- | libiot/vfs/ramfs.c | 634 | ||||
| -rw-r--r-- | libiot/vfs/spiffs.c | 1423 | ||||
| -rw-r--r-- | libiot/vfs/vfs.h | 128 |
8 files changed, 4074 insertions, 0 deletions
diff --git a/libiot/vfs/fat.c b/libiot/vfs/fat.c new file mode 100644 index 0000000..d612277 --- /dev/null +++ b/libiot/vfs/fat.c @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L. + * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the <organization> nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * * The WHITECAT logotype cannot be changed, you can remove it, but you + * cannot change it in any way. The WHITECAT logotype is: + * + * /\ /\ + * / \_____/ \ + * /_____________\ + * W H I T E C A T + * + * * Redistributions in binary form must retain all copyright notices printed + * to any local or remote output device. This include any reference to + * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may + * appear in the future. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Lua RTOS fat vfs + * + */ + +#include "sdkconfig.h" + +#if (CONFIG_SD_CARD_MMC || CONFIG_SD_CARD_SPI) && CONFIG_LUA_RTOS_USE_FAT + +#include <stdio.h> + +#include "esp_vfs.h" + +#include "esp_err.h" +#include "esp_log.h" +#include "esp_vfs_fat.h" +#include "driver/sdmmc_host.h" +#include "driver/sdspi_host.h" +#include "sdmmc_cmd.h" +#include "driver/sdmmc_defs.h" + +#include <drivers/gpio.h> +#include <drivers/spi.h> +#include <drivers/power_bus.h> + +#include <sys/driver.h> +#include <sys/mount.h> +#include <sys/syslog.h> + +#include <sys/vfs/vfs.h> + +int vfs_fat_mount(const char *target) { +#if CONFIG_SD_CARD_SPI + spi_bus_t *spi_bus = get_spi_info(); + + #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + // Lock resources + if (spi_lock_bus_resources(CONFIG_LUA_RTOS_SD_SPI, DRIVER_ALL_FLAGS)) { + return -1; + } + + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_SD_CS, DRIVER_ALL_FLAGS, "SD Card - CS")) { + return -1; + } + #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + + sdmmc_host_t host = SDSPI_HOST_DEFAULT(); + sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT(); + + #if (CONFIG_LUA_RTOS_SD_SPI == 2) + host.slot = HSPI_HOST; + + slot_config.gpio_miso = CONFIG_LUA_RTOS_SPI2_MISO; + slot_config.gpio_mosi = CONFIG_LUA_RTOS_SPI2_MOSI; + slot_config.gpio_sck = CONFIG_LUA_RTOS_SPI2_CLK; + slot_config.gpio_cs = CONFIG_LUA_RTOS_SD_CS; + #endif // (CONFIG_LUA_RTOS_SD_SPI == 2) + + #if (CONFIG_LUA_RTOS_SD_SPI == 3) + host.slot = VSPI_HOST; + + slot_config.gpio_miso = CONFIG_LUA_RTOS_SPI3_MISO; + slot_config.gpio_mosi = CONFIG_LUA_RTOS_SPI3_MOSI; + slot_config.gpio_sck = CONFIG_LUA_RTOS_SPI3_CLK; + slot_config.gpio_cs = CONFIG_LUA_RTOS_SD_CS; + #endif // (CONFIG_LUA_RTOS_SD_SPI == 3) + + spi_bus[spi_idx(CONFIG_LUA_RTOS_SD_SPI)].setup |= SPI_DMA_SETUP; +#endif // CONFIG_SD_CARD_SPI + +#if CONFIG_SD_CARD_MMC + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + slot_config.gpio_cd = CONFIG_LUA_RTOS_MCC_CD; + slot_config.gpio_wp = CONFIG_LUA_RTOS_MCC_WP; + + #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + // Lock resources + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 15, DRIVER_ALL_FLAGS, "SD Card - CMD")) { + return -1; + } + + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 14, DRIVER_ALL_FLAGS, "SD Card - CLK")) { + return -1; + } + + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 2, DRIVER_ALL_FLAGS, "SD Card - DAT0")) { + return -1; + } + #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + + gpio_pin_pullup(2); + gpio_pin_pullup(14); + gpio_pin_pullup(15); + + #if CONFIG_LUA_RTOS_MCC_1_LINE + host.flags = SDMMC_HOST_FLAG_1BIT; + slot_config.width = 1; + #endif // CONFIG_LUA_RTOS_MCC_1_LINE + + #if CONFIG_LUA_RTOS_MCC_4_LINE + host.flags = SDMMC_HOST_FLAG_4BIT; + slot_config.width = 4; + + gpio_pin_pullup(4); + gpio_pin_pullup(12); + gpio_pin_pullup(13); + + #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 4, DRIVER_ALL_FLAGS, "SD Card - DAT1")) { + return -1; + } + + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 12, DRIVER_ALL_FLAGS, "SD Card - DAT2")) { + return -1; + } + + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 13, DRIVER_ALL_FLAGS, "SD Card - DAT3")) { + return -1; + } + #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + #endif // CONFIG_LUA_RTOS_MCC_4_LINE + + #if CONFIG_LUA_RTOS_MCC_CD != -1 + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_CD, DRIVER_ALL_FLAGS, "SD Card - CD")) { + return -1; + } + #endif // CONFIG_LUA_RTOS_MCC_CD + + #if CONFIG_LUA_RTOS_MCC_WP != -1 + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_WP, DRIVER_ALL_FLAGS, "SD Card - WP")) { + return -1; + } + #endif // CONFIG_LUA_RTOS_MCC_WP +#endif // CONFIG_SD_CARD_MMC + +#if CONFIG_LUA_RTOS_SD_CONNECTED_TO_POWER_BUS + pwbus_on(); +#endif + + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5 + }; + + #if CONFIG_SD_CARD_SPI + syslog(LOG_INFO, "sd is at spi%d, cs=%s%d", + CONFIG_LUA_RTOS_SD_SPI, + gpio_portname(CONFIG_LUA_RTOS_SD_CS), gpio_name(CONFIG_LUA_RTOS_SD_CS) + ); + #endif + + #if CONFIG_SD_CARD_MMC + syslog(LOG_INFO, "sd is at mmc0"); + #endif + + sdmmc_card_t* card; + esp_err_t ret = esp_vfs_fat_sdmmc_mount("/fat", &host, &slot_config, &mount_config, &card); + if (ret != ESP_OK) { + syslog(LOG_INFO, "fat can't mounted (error %d)", ret); + vfs_fat_umount(target); + return -1; + } + + syslog(LOG_INFO, "sd name %s", card->cid.name); + syslog(LOG_INFO, "sd type %s", (card->ocr & SD_OCR_SDHC_CAP)?"SDHC/SDXC":"SDSC"); + syslog(LOG_INFO, "sd working at %s", (card->csd.tr_speed > 25000000)?"high speed":"default speed"); + syslog(LOG_INFO, "sd size %.2f GB", + ((((double)card->csd.capacity) * ((double)card->csd.sector_size)) / 1073741824.0) + ); + + syslog(LOG_INFO, "sd CSD ver=%d, sector_size=%d, capacity=%d read_bl_len=%d", + card->csd.csd_ver, + card->csd.sector_size, card->csd.capacity, card->csd.read_block_len + ); + + syslog(LOG_INFO, "sd SCR sd_spec=%d, bus_width=%d", card->scr.sd_spec, card->scr.bus_width); + + syslog(LOG_INFO, "fat mounted on %s", target); + + return 0; +} + +int vfs_fat_umount(const char *target) { + // Unmount + esp_vfs_fat_sdmmc_unmount(); + +#if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + #if CONFIG_SD_CARD_SPI + spi_bus_t *spi_bus = get_spi_info(); + + // Unlock resources + spi_unlock_bus_resources(CONFIG_LUA_RTOS_SD_SPI); + driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_SD_CS); + spi_bus[spi_idx(CONFIG_LUA_RTOS_SD_SPI)].setup &= ~SPI_DMA_SETUP; + #endif // CONFIG_SD_CARD_SPI + + #if CONFIG_SD_CARD_MMC + // Unlock resources + driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 15); + driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 14); + driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 2); + + #if CONFIG_LUA_RTOS_MCC_4_LINE + driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 14); + driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 12); + driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 13); + #endif + + #if CONFIG_LUA_RTOS_MCC_CD != -1 + driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_CD); + #endif + + #if CONFIG_LUA_RTOS_MCC_WP != -1 + driver_unlock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_WP); + #endif + #endif // CONFIG_SD_CARD_MMC +#endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + +#if CONFIG_LUA_RTOS_SD_CONNECTED_TO_POWER_BUS + pwbus_off(); +#endif + + syslog(LOG_INFO, "fat unmounted"); + + return 0; +} + +int vfs_fat_format(const char *target) { + vfs_fat_umount(target); + +#if CONFIG_SD_CARD_SPI + spi_bus_t *spi_bus = get_spi_info(); + + #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + // Lock resources + if (spi_lock_bus_resources(CONFIG_LUA_RTOS_SD_SPI, DRIVER_ALL_FLAGS)) { + return -1; + } + + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_SD_CS, DRIVER_ALL_FLAGS, "SD Card - CS")) { + return -1; + } + #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + + sdmmc_host_t host = SDSPI_HOST_DEFAULT(); + sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT(); + + #if (CONFIG_LUA_RTOS_SD_SPI == 2) + host.slot = HSPI_HOST; + + slot_config.gpio_miso = CONFIG_LUA_RTOS_SPI2_MISO; + slot_config.gpio_mosi = CONFIG_LUA_RTOS_SPI2_MOSI; + slot_config.gpio_sck = CONFIG_LUA_RTOS_SPI2_CLK; + slot_config.gpio_cs = CONFIG_LUA_RTOS_SD_CS; + #endif // (CONFIG_LUA_RTOS_SD_SPI == 2) + + #if (CONFIG_LUA_RTOS_SD_SPI == 3) + host.slot = VSPI_HOST; + + slot_config.gpio_miso = CONFIG_LUA_RTOS_SPI3_MISO; + slot_config.gpio_mosi = CONFIG_LUA_RTOS_SPI3_MOSI; + slot_config.gpio_sck = CONFIG_LUA_RTOS_SPI3_CLK; + slot_config.gpio_cs = CONFIG_LUA_RTOS_SD_CS; + #endif // (CONFIG_LUA_RTOS_SD_SPI == 3) + + spi_bus[spi_idx(CONFIG_LUA_RTOS_SD_SPI)].setup |= SPI_DMA_SETUP; +#endif // CONFIG_SD_CARD_SPI + +#if CONFIG_SD_CARD_MMC + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + slot_config.gpio_cd = CONFIG_LUA_RTOS_MCC_CD; + slot_config.gpio_wp = CONFIG_LUA_RTOS_MCC_WP; + + #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + // Lock resources + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 15, DRIVER_ALL_FLAGS, "SD Card - CMD")) { + return -1; + } + + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 14, DRIVER_ALL_FLAGS, "SD Card - CLK")) { + return -1; + } + + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 2, DRIVER_ALL_FLAGS, "SD Card - DAT0")) { + return -1; + } + #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + + gpio_pin_pullup(2); + gpio_pin_pullup(14); + gpio_pin_pullup(15); + + #if CONFIG_LUA_RTOS_MCC_1_LINE + host.flags = SDMMC_HOST_FLAG_1BIT; + slot_config.width = 1; + #endif // CONFIG_LUA_RTOS_MCC_1_LINE + + #if CONFIG_LUA_RTOS_MCC_4_LINE + host.flags = SDMMC_HOST_FLAG_4BIT; + slot_config.width = 4; + + gpio_pin_pullup(4); + gpio_pin_pullup(12); + gpio_pin_pullup(13); + + #if CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 4, DRIVER_ALL_FLAGS, "SD Card - DAT1")) { + return -1; + } + + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 12, DRIVER_ALL_FLAGS, "SD Card - DAT2")) { + return -1; + } + + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, 13, DRIVER_ALL_FLAGS, "SD Card - DAT3")) { + return -1; + } + #endif // CONFIG_LUA_RTOS_USE_HARDWARE_LOCKS + #endif // CONFIG_LUA_RTOS_MCC_4_LINE + + #if CONFIG_LUA_RTOS_MCC_CD != -1 + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_CD, DRIVER_ALL_FLAGS, "SD Card - CD")) { + return -1; + } + #endif // CONFIG_LUA_RTOS_MCC_CD + + #if CONFIG_LUA_RTOS_MCC_WP != -1 + if (driver_lock(SYSTEM_DRIVER, 0, GPIO_DRIVER, CONFIG_LUA_RTOS_MCC_WP, DRIVER_ALL_FLAGS, "SD Card - WP")) { + return -1; + } + #endif // CONFIG_LUA_RTOS_MCC_WP +#endif // CONFIG_SD_CARD_MMC + + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5 + }; + + #if CONFIG_SD_CARD_SPI + syslog(LOG_INFO, "sd is at spi%d, cs=%s%d", + CONFIG_LUA_RTOS_SD_SPI, + gpio_portname(CONFIG_LUA_RTOS_SD_CS), gpio_name(CONFIG_LUA_RTOS_SD_CS) + ); + #endif + + #if CONFIG_SD_CARD_MMC + syslog(LOG_INFO, "sd is at mmc0"); + #endif + + sdmmc_card_t* card; + esp_err_t ret = esp_vfs_fat_sdmmc_format("/fat", &host, &slot_config, &mount_config, &card); + if (ret != ESP_OK) { + syslog(LOG_INFO, "fat can't mounted (error %d)", ret); + vfs_fat_umount(target); + return -1; + } + + syslog(LOG_INFO, "sd name %s", card->cid.name); + syslog(LOG_INFO, "sd type %s", (card->ocr & SD_OCR_SDHC_CAP)?"SDHC/SDXC":"SDSC"); + syslog(LOG_INFO, "sd working at %s", (card->csd.tr_speed > 25000000)?"high speed":"default speed"); + syslog(LOG_INFO, "sd size %.2f GB", + ((((double)card->csd.capacity) * ((double)card->csd.sector_size)) / 1073741824.0) + ); + + syslog(LOG_INFO, "sd CSD ver=%d, sector_size=%d, capacity=%d read_bl_len=%d", + card->csd.csd_ver, + card->csd.sector_size, card->csd.capacity, card->csd.read_block_len + ); + + syslog(LOG_INFO, "sd SCR sd_spec=%d, bus_width=%d", card->scr.sd_spec, card->scr.bus_width); + + syslog(LOG_INFO, "fat mounted on %s", target); + + vfs_fat_umount(target); + vfs_fat_mount(target); + + return 0; +} + +int vfs_fat_fsstat(const char *target, u32_t *total, u32_t *used) { + DWORD nclst = 0; + FATFS* fs = NULL; + FRESULT err = f_getfree (target, &nclst, &fs); + + if (err != FR_OK) { + syslog(LOG_ERR, "fat get fs info of '%s' (%i)", target, err); + return -1; + } + + //fs->free_clst and nclst both are equal and give the number of free clusters + //fs->csize = Cluster size [sectors] + //fs->ssize = Sector size (512, 1024, 2048 or 4096) + //fs->fsize = Size of an FAT [sectors] + //fs->n_fatent = nclst + 2 + + DWORD tclst = fs->n_fatent - 2; + + if (total) { + *total = tclst * fs->csize * fs->ssize; + } + + if (used) { + *used = (tclst - nclst) * fs->csize * fs->ssize; + } + + return 0; +} + +#endif diff --git a/libiot/vfs/include/esp_vfs.h b/libiot/vfs/include/esp_vfs.h new file mode 100644 index 0000000..a0762d3 --- /dev/null +++ b/libiot/vfs/include/esp_vfs.h @@ -0,0 +1,424 @@ +// Copyright 2015-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_VFS_H__ +#define __ESP_VFS_H__ + +#include <stdint.h> +#include <stddef.h> +#include <stdarg.h> +#include <unistd.h> +#include <utime.h> +#include <FreeRTOS.h> +#include <semphr.h> +#include <esp_err.h> +#include <sys/types.h> +#include <sys/reent.h> +#include <sys/stat.h> +#include <sys/time.h> +//#include <sys/termios.h> +//#include <../platform_include/sys/termios.h> +//#include <sys/poll.h> +#include <dirent.h> +#include <string.h> +//#include "sdkconfig.h" + +#include "lwip/sockets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _SYS_TYPES_FD_SET +#error "VFS should be used with FD_SETSIZE and FD_SET from sys/types.h" +#endif + +/** + * Maximum number of (global) file descriptors. + */ +#define MAX_FDS FD_SETSIZE /* for compatibility with fd_set and select() */ + +/** + * Maximum length of path prefix (not including zero terminator) + */ +#define ESP_VFS_PATH_MAX 15 + +/** + * Default value of flags member in esp_vfs_t structure. + */ +#define ESP_VFS_FLAG_DEFAULT 0 + +/** + * Flag which indicates that FS needs extra context pointer in syscalls. + */ +#define ESP_VFS_FLAG_CONTEXT_PTR 1 + +/* + * @brief VFS identificator used for esp_vfs_register_with_id() + */ +typedef int esp_vfs_id_t; + +/** + * @brief VFS definition structure + * + * This structure should be filled with pointers to corresponding + * FS driver functions. + * + * VFS component will translate all FDs so that the filesystem implementation + * sees them starting at zero. The caller sees a global FD which is prefixed + * with an pre-filesystem-implementation. + * + * Some FS implementations expect some state (e.g. pointer to some structure) + * to be passed in as a first argument. For these implementations, + * populate the members of this structure which have _p suffix, set + * flags member to ESP_VFS_FLAG_CONTEXT_PTR and provide the context pointer + * to esp_vfs_register function. + * If the implementation doesn't use this extra argument, populate the + * members without _p suffix and set flags member to ESP_VFS_FLAG_DEFAULT. + * + * If the FS driver doesn't provide some of the functions, set corresponding + * members to NULL. + */ +typedef struct +{ + int flags; /*!< ESP_VFS_FLAG_CONTEXT_PTR or ESP_VFS_FLAG_DEFAULT */ + union { + ssize_t (*write_p)(void* p, int fd, const void * data, size_t size); + ssize_t (*write)(int fd, const void * data, size_t size); + }; + union { + off_t (*lseek_p)(void* p, int fd, off_t size, int mode); + off_t (*lseek)(int fd, off_t size, int mode); + }; + union { + ssize_t (*read_p)(void* ctx, int fd, void * dst, size_t size); + ssize_t (*read)(int fd, void * dst, size_t size); + }; + union { + int (*open_p)(void* ctx, const char * path, int flags, int mode); + int (*open)(const char * path, int flags, int mode); + }; + union { + int (*close_p)(void* ctx, int fd); + int (*close)(int fd); + }; + union { + int (*fstat_p)(void* ctx, int fd, struct stat * st); + int (*fstat)(int fd, struct stat * st); + }; + union { + int (*stat_p)(void* ctx, const char * path, struct stat * st); + int (*stat)(const char * path, struct stat * st); + }; + union { + int (*link_p)(void* ctx, const char* n1, const char* n2); + int (*link)(const char* n1, const char* n2); + }; + union { + int (*unlink_p)(void* ctx, const char *path); + int (*unlink)(const char *path); + }; + union { + int (*rename_p)(void* ctx, const char *src, const char *dst); + int (*rename)(const char *src, const char *dst); + }; + union { + DIR* (*opendir_p)(void* ctx, const char* name); + DIR* (*opendir)(const char* name); + }; + union { + struct dirent* (*readdir_p)(void* ctx, DIR* pdir); + struct dirent* (*readdir)(DIR* pdir); + }; + union { + int (*readdir_r_p)(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent); + int (*readdir_r)(DIR* pdir, struct dirent* entry, struct dirent** out_dirent); + }; + union { + long (*telldir_p)(void* ctx, DIR* pdir); + long (*telldir)(DIR* pdir); + }; + union { + void (*seekdir_p)(void* ctx, DIR* pdir, long offset); + void (*seekdir)(DIR* pdir, long offset); + }; + union { + int (*closedir_p)(void* ctx, DIR* pdir); + int (*closedir)(DIR* pdir); + }; + union { + int (*mkdir_p)(void* ctx, const char* name, mode_t mode); + int (*mkdir)(const char* name, mode_t mode); + }; + union { + int (*rmdir_p)(void* ctx, const char* name); + int (*rmdir)(const char* name); + }; + union { + int (*fcntl_p)(void* ctx, int fd, int cmd, va_list args); + int (*fcntl)(int fd, int cmd, va_list args); + }; + union { + int (*ioctl_p)(void* ctx, int fd, int cmd, va_list args); + int (*ioctl)(int fd, int cmd, va_list args); + }; + union { + int (*fsync_p)(void* ctx, int fd); + int (*fsync)(int fd); + }; + union { + int (*access_p)(void* ctx, const char *path, int amode); + int (*access)(const char *path, int amode); + }; + union { + int (*truncate_p)(void* ctx, const char *path, off_t length); + int (*truncate)(const char *path, off_t length); + }; + union { + int (*utime_p)(void* ctx, const char *path, const struct utimbuf *times); + int (*utime)(const char *path, const struct utimbuf *times); + }; + union { + int (*ftruncate_p)(void* ctx, int fd, off_t length); + int (*ftruncate)(int fd, off_t length); + }; + union { + int (*writev_p)(void* ctx, int fd, const struct iovec *iov, int iovcnt); + int (*writev)(int fd, const struct iovec *iov, int iovcnt); + }; + union { + int (*select_p)(void* ctx, int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout); + int (*select)(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout); + }; +#ifdef CONFIG_SUPPORT_TERMIOS + union { + int (*tcsetattr_p)(void *ctx, int fd, int optional_actions, const struct termios *p); + int (*tcsetattr)(int fd, int optional_actions, const struct termios *p); + }; + union { + int (*tcgetattr_p)(void *ctx, int fd, struct termios *p); + int (*tcgetattr)(int fd, struct termios *p); + }; + union { + int (*tcdrain_p)(void *ctx, int fd); + int (*tcdrain)(int fd); + }; + union { + int (*tcflush_p)(void *ctx, int fd, int select); + int (*tcflush)(int fd, int select); + }; + union { + int (*tcflow_p)(void *ctx, int fd, int action); + int (*tcflow)(int fd, int action); + }; + union { + pid_t (*tcgetsid_p)(void *ctx, int fd); + pid_t (*tcgetsid)(int fd); + }; + union { + int (*tcsendbreak_p)(void *ctx, int fd, int duration); + int (*tcsendbreak)(int fd, int duration); + }; +#endif // CONFIG_SUPPORT_TERMIOS + + /** start_select is called for setting up synchronous I/O multiplexing of the desired file descriptors in the given VFS */ + esp_err_t (*start_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, SemaphoreHandle_t *signal_sem); + /** socket select function for socket FDs with the functionality of POSIX select(); this should be set only for the socket VFS */ + int (*socket_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout); + /** called by VFS to interrupt the socket_select call when select is activated from a non-socket VFS driver; set only for the socket driver */ + void (*stop_socket_select)(); + /** stop_socket_select which can be called from ISR; set only for the socket driver */ + void (*stop_socket_select_isr)(BaseType_t *woken); + /** end_select is called to stop the I/O multiplexing and deinitialize the environment created by start_select for the given VFS */ + void (*end_select)(); +} esp_vfs_t; + + +/** + * Register a virtual filesystem for given path prefix. + * + * @param base_path file path prefix associated with the filesystem. + * Must be a zero-terminated C string, up to ESP_VFS_PATH_MAX + * characters long, and at least 2 characters long. + * Name must start with a "/" and must not end with "/". + * For example, "/data" or "/dev/spi" are valid. + * These VFSes would then be called to handle file paths such as + * "/data/myfile.txt" or "/dev/spi/0". + * @param vfs Pointer to esp_vfs_t, a structure which maps syscalls to + * the filesystem driver functions. VFS component doesn't + * assume ownership of this pointer. + * @param ctx If vfs->flags has ESP_VFS_FLAG_CONTEXT_PTR set, a pointer + * which should be passed to VFS functions. Otherwise, NULL. + * + * @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are + * registered. + */ +esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx); + + +/** + * Special case function for registering a VFS that uses a method other than + * open() to open new file descriptors from the interval <min_fd; max_fd). + * + * This is a special-purpose function intended for registering LWIP sockets to VFS. + * + * @param vfs Pointer to esp_vfs_t. Meaning is the same as for esp_vfs_register(). + * @param ctx Pointer to context structure. Meaning is the same as for esp_vfs_register(). + * @param min_fd The smallest file descriptor this VFS will use. + * @param max_fd Upper boundary for file descriptors this VFS will use (the biggest file descriptor plus one). + * + * @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are + * registered, ESP_ERR_INVALID_ARG if the file descriptor boundaries + * are incorrect. + */ +esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, int max_fd); + +/** + * Special case function for registering a VFS that uses a method other than + * open() to open new file descriptors. In comparison with + * esp_vfs_register_fd_range, this function doesn't pre-registers an interval + * of file descriptors. File descriptors can be registered later, by using + * esp_vfs_register_fd. + * + * @param vfs Pointer to esp_vfs_t. Meaning is the same as for esp_vfs_register(). + * @param ctx Pointer to context structure. Meaning is the same as for esp_vfs_register(). + * @param vfs_id Here will be written the VFS ID which can be passed to + * esp_vfs_register_fd for registering file descriptors. + * + * @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are + * registered, ESP_ERR_INVALID_ARG if the file descriptor boundaries + * are incorrect. + */ +esp_err_t esp_vfs_register_with_id(const esp_vfs_t *vfs, void *ctx, esp_vfs_id_t *vfs_id); + +/** + * Unregister a virtual filesystem for given path prefix + * + * @param base_path file prefix previously used in esp_vfs_register call + * @return ESP_OK if successful, ESP_ERR_INVALID_STATE if VFS for given prefix + * hasn't been registered + */ +esp_err_t esp_vfs_unregister(const char* base_path); + +/** + * Special function for registering another file descriptor for a VFS registered + * by esp_vfs_register_with_id. + * + * @param vfs_id VFS identificator returned by esp_vfs_register_with_id. + * @param fd The registered file descriptor will be written to this address. + * + * @return ESP_OK if the registration is successful, + * ESP_ERR_NO_MEM if too many file descriptors are registered, + * ESP_ERR_INVALID_ARG if the arguments are incorrect. + */ +esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd); + +/** + * Special function for unregistering a file descriptor belonging to a VFS + * registered by esp_vfs_register_with_id. + * + * @param vfs_id VFS identificator returned by esp_vfs_register_with_id. + * @param fd File descriptor which should be unregistered. + * + * @return ESP_OK if the registration is successful, + * ESP_ERR_INVALID_ARG if the arguments are incorrect. + */ +esp_err_t esp_vfs_unregister_fd(esp_vfs_id_t vfs_id, int fd); + +/** + * These functions are to be used in newlib syscall table. They will be called by + * newlib when it needs to use any of the syscalls. + */ +/**@{*/ +ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size); +off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode); +ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size); +int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode); +int esp_vfs_close(struct _reent *r, int fd); +int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st); +int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st); +int esp_vfs_link(struct _reent *r, const char* n1, const char* n2); +int esp_vfs_unlink(struct _reent *r, const char *path); +int esp_vfs_rename(struct _reent *r, const char *src, const char *dst); +int esp_vfs_utime(const char *path, const struct utimbuf *times); +/**@}*/ + +/** + * @brief Synchronous I/O multiplexing which implements the functionality of POSIX select() for VFS + * @param nfds Specifies the range of descriptors which should be checked. + * The first nfds descriptors will be checked in each set. + * @param readfds If not NULL, then points to a descriptor set that on input + * specifies which descriptors should be checked for being + * ready to read, and on output indicates which descriptors + * are ready to read. + * @param writefds If not NULL, then points to a descriptor set that on input + * specifies which descriptors should be checked for being + * ready to write, and on output indicates which descriptors + * are ready to write. + * @param errorfds If not NULL, then points to a descriptor set that on input + * specifies which descriptors should be checked for error + * conditions, and on output indicates which descriptors + * have error conditions. + * @param timeout If not NULL, then points to timeval structure which + * specifies the time period after which the functions should + * time-out and return. If it is NULL, then the function will + * not time-out. + * + * @return The number of descriptors set in the descriptor sets, or -1 + * when an error (specified by errno) have occurred. + */ +int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout); + +/** + * @brief Notification from a VFS driver about a read/write/error condition + * + * This function is called when the VFS driver detects a read/write/error + * condition as it was requested by the previous call to start_select. + * + * @param signal_sem semaphore handle which was passed to the driver by the start_select call + */ +void esp_vfs_select_triggered(SemaphoreHandle_t *signal_sem); + +/** + * @brief Notification from a VFS driver about a read/write/error condition (ISR version) + * + * This function is called when the VFS driver detects a read/write/error + * condition as it was requested by the previous call to start_select. + * + * @param signal_sem semaphore handle which was passed to the driver by the start_select call + * @param woken is set to pdTRUE if the function wakes up a task with higher priority + */ +void esp_vfs_select_triggered_isr(SemaphoreHandle_t *signal_sem, BaseType_t *woken); + +/** + * @brief Implements the VFS layer for synchronous I/O multiplexing by poll() + * + * The implementation is based on esp_vfs_select. The parameters and return values are compatible with POSIX poll(). + * + * @param fds Pointer to the array containing file descriptors and events poll() should consider. + * @param nfds Number of items in the array fds. + * @param timeout Poll() should wait at least timeout milliseconds. If the value is 0 then it should return + * immediately. If the value is -1 then it should wait (block) until the event occurs. + * + * @return A positive return value indicates the number of file descriptors that have been selected. The 0 + * return value indicates a timed-out poll. -1 is return on failure and errno is set accordingly. + * + */ +int esp_vfs_poll(struct pollfd *fds, nfds_t nfds, int timeout); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //__ESP_VFS_H__ diff --git a/libiot/vfs/include/sys/dirent.h b/libiot/vfs/include/sys/dirent.h new file mode 100644 index 0000000..ffc764c --- /dev/null +++ b/libiot/vfs/include/sys/dirent.h @@ -0,0 +1,56 @@ +// 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 <stdint.h> + +/** + * This header file provides POSIX-compatible definitions of directory + * access functions and related data types. + * See http://pubs.opengroup.org/onlinepubs/7908799/xsh/dirent.h.html + * for reference. + */ + +/** + * @brief Opaque directory structure + */ +typedef struct { + uint16_t dd_vfs_idx; /*!< VFS index, not to be used by applications */ + uint16_t dd_rsv; /*!< field reserved for future extension */ + /* remaining fields are defined by VFS implementation */ +} DIR; + +/** + * @brief Directory entry structure + */ +struct dirent { + int d_ino; /*!< file number */ + uint8_t d_type; /*!< not defined in POSIX, but present in BSD and Linux */ +#define DT_UNKNOWN 0 +#define DT_REG 1 +#define DT_DIR 2 + char d_name[256]; /*!< zero-terminated file name */ + uint32_t d_fsize; +}; + +DIR* opendir(const char* name); +struct dirent* readdir(DIR* pdir); +long telldir(DIR* pdir); +void seekdir(DIR* pdir, long loc); +void rewinddir(DIR* pdir); +int closedir(DIR* pdir); +int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent); + diff --git a/libiot/vfs/lfs.c b/libiot/vfs/lfs.c new file mode 100644 index 0000000..c008e5e --- /dev/null +++ b/libiot/vfs/lfs.c @@ -0,0 +1,949 @@ +/* + * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L. + * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the <organization> nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * * The WHITECAT logotype cannot be changed, you can remove it, but you + * cannot change it in any way. The WHITECAT logotype is: + * + * /\ /\ + * / \_____/ \ + * /_____________\ + * W H I T E C A T + * + * * Redistributions in binary form must retain all copyright notices printed + * to any local or remote output device. This include any reference to + * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may + * appear in the future. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Lua RTOS lfs vfs + * + */ + +#include "sdkconfig.h" + +#if CONFIG_LUA_RTOS_USE_LFS + +#include "rom/spi_flash.h" +#include "esp_partition.h" + +#include <freertos/FreeRTOS.h> + +#include <string.h> +#include <stdio.h> +#include <limits.h> +#include <dirent.h> + +#include <sys/stat.h> + +#include "esp_vfs.h" +#include <errno.h> + +#include "lfs.h" + +#include <sys/syslog.h> +#include <sys/mount.h> +#include <sys/mutex.h> +#include <sys/list.h> +#include <sys/fcntl.h> +#include <sys/vfs/vfs.h> +#include <dirent.h> + +static int vfs_lfs_open(const char *path, int flags, int mode); +static ssize_t vfs_lfs_write(int fd, const void *data, size_t size); +static ssize_t vfs_lfs_read(int fd, void * dst, size_t size); +static int vfs_lfs_fstat(int fd, struct stat * st); +static int vfs_lfs_close(int fd); +static off_t vfs_lfs_lseek(int fd, off_t size, int mode); +static int vfs_lfs_access(const char *path, int amode); +static long vfs_lfs_telldir(DIR *dirp); + +static struct list files; +static lfs_t lfs; + +struct vfs_lfs_context { + uint32_t base_addr; + struct mtx lock; +}; + +/* + * This function translate error codes from lfs to errno error codes + * + */ +static int lfs_to_errno(int res) { + switch (res) { + case LFS_ERR_OK: + return 0; + + case LFS_ERR_IO: + case LFS_ERR_CORRUPT: + return EIO; + + case LFS_ERR_NOENT: + return ENOENT; + + case LFS_ERR_EXIST: + return EEXIST; + + case LFS_ERR_NOTDIR: + return ENOTDIR; + + case LFS_ERR_ISDIR: + return EISDIR; + + case LFS_ERR_NOTEMPTY: + return ENOTEMPTY; + + case LFS_ERR_BADF: + return EBADF; + + case LFS_ERR_NOMEM: + return ENOMEM; + + case LFS_ERR_NOSPC: + return ENOSPC; + + case LFS_ERR_INVAL: + return EINVAL; + + default: + return res; + } +} + +static int vfs_lfs_open(const char *path, int flags, int mode) { + int fd; + int result; + + // Allocate new file + vfs_file_t *file = calloc(1, sizeof(vfs_file_t)); + if (!file) { + errno = ENOMEM; + return -1; + } + + file->fs_file = (void *)calloc(1, sizeof(lfs_file_t)); + if (!file->fs_file) { + free(file); + errno = ENOMEM; + return -1; + } + + file->path = strdup(path); + if (!file->path) { + free(file->fs_file); + free(file); + errno = ENOMEM; + return -1; + } + + // Add file to file list and get the file descriptor + int res = lstadd(&files, file, &fd); + if (res) { + free(file->fs_file); + free(file->path); + free(file); + errno = res; + return -1; + } + + // Translate flags to lfs flags + int lfs_flags = 0; + + if (flags == O_APPEND) + lfs_flags |= LFS_O_APPEND; + + if (flags == O_RDONLY) + lfs_flags |= LFS_O_RDONLY; + + if (flags & O_WRONLY) + lfs_flags |= LFS_O_WRONLY; + + if (flags & O_RDWR) + lfs_flags |= LFS_O_RDWR; + + if (flags & O_EXCL) + lfs_flags |= LFS_O_EXCL; + + if (flags & O_CREAT) + lfs_flags |= LFS_O_CREAT; + + if (flags & O_TRUNC) + lfs_flags |= LFS_O_TRUNC; + + if (*path == '/') { + path++; + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + if ((result = lfs_file_open(&lfs, file->fs_file, path, lfs_flags)) < 0) { + errno = lfs_to_errno(result); + lstremove(&files, fd, 0); + + free(file->fs_file); + free(file->path); + free(file); + + mtx_unlock(&ctx->lock); + + return -1; + } + + mtx_unlock(&ctx->lock); + + return fd; +} + +static ssize_t vfs_lfs_write(int fd, const void *data, size_t size) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + // Write to file + result = lfs_file_write(&lfs, file->fs_file, (void *)data, size); + if (result < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + return result; +} + +static ssize_t vfs_lfs_read(int fd, void *dst, size_t size) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + // Read from file + result = lfs_file_read(&lfs, file->fs_file, dst, size); + if (result < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + return result; +} + +static int vfs_lfs_fstat(int fd, struct stat *st) { + vfs_file_t *file; + struct lfs_info info; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + // Init stats + memset(st, 0, sizeof(struct stat)); + + // Set block size for this file system + st->st_blksize = lfs.cfg->block_size; + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + // Get the file stats + result = lfs_stat(&lfs, file->path, &info); + if (result < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + st->st_size = info.size; + st->st_mode = ((info.type==LFS_TYPE_REG)?S_IFREG:S_IFDIR); + + return 0; +} + +static int vfs_lfs_close(int fd) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + // Close file + result = lfs_file_close(&lfs, file->fs_file); + if (result < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + // Remove file from file list + lstremove(&files, fd, 0); + + free(file->fs_file); + free(file->path); + free(file); + + mtx_unlock(&ctx->lock); + + return 0; +} + +static off_t vfs_lfs_lseek(int fd, off_t size, int mode) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + int whence = LFS_SEEK_CUR; + + switch (mode) { + case SEEK_SET: + whence = LFS_SEEK_SET; + break; + case SEEK_CUR: + whence = LFS_SEEK_CUR; + break; + case SEEK_END: + whence = LFS_SEEK_END; + break; + default: + errno = EINVAL; + return -1; + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + result = lfs_file_seek(&lfs, file->fs_file, size, whence); + if (result < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + return result; +} + +static int vfs_lfs_stat(const char *path, struct stat *st) { + struct lfs_info info; + int result; + + // Init stats + memset(st, 0, sizeof(struct stat)); + + // Set block size for this file system + st->st_blksize = lfs.cfg->block_size; + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + // Get the file stats + result = lfs_stat(&lfs, path, &info); + if (result < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + st->st_size = info.size; + st->st_mode = ((info.type==LFS_TYPE_REG)?S_IFREG:S_IFDIR); + + return 0; +} + +static int vfs_lfs_access(const char *path, int amode) { +#if 0 + struct stat s; + + if (vfs_lfs_stat(path, &s) < 0) { + return -1; + } + + if (s.st_mode != S_IFREG) { + errno = EACCES; + return -1; + } + + return 0; +#endif + return 0; + +} + +static int vfs_lfs_unlink(const char *path) { + struct lfs_info info; + int result; + + // Sanity checks + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + // Is not a directory + result = lfs_stat(&lfs, path, &info); + if (result < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + if (info.type == LFS_TYPE_DIR) { + mtx_unlock(&ctx->lock); + errno = EPERM; + return -1; + } + + // Unlink + if ((result = lfs_remove(&lfs, path)) < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + return 0; +} + +static int vfs_lfs_rename(const char *src, const char *dst) { + int result; + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + if ((result = lfs_rename(&lfs, src, dst)) < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + return 0; +} + +static DIR* vfs_lfs_opendir(const char *name) { + int result; + + vfs_dir_t *dir; + + dir = vfs_allocate_dir("lfs", name); + if (!dir) { + return NULL; + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + // Open directory + if ((result = lfs_dir_open(&lfs, dir->fs_dir, name)) < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + vfs_free_dir(dir); + + return NULL; + } + + mtx_unlock(&ctx->lock); + + return (DIR *)dir; +} + +static int vfs_lfs_rmdir(const char *name) { + struct lfs_info info; + int result; + + // Sanity checks + if (strcmp(name,"/") == 0) { + errno = EBUSY; + return -1; + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + // Is a directory + result = lfs_stat(&lfs, name, &info); + if (result < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + if (info.type != LFS_TYPE_DIR) { + mtx_unlock(&ctx->lock); + errno = ENOTDIR; + return -1; + } + + // Unlink + if ((result = lfs_remove(&lfs, name)) < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + return 0; +} + +static struct dirent *vfs_lfs_readdir(DIR *pdir) { + vfs_dir_t *dir = (vfs_dir_t *)pdir; + struct dirent *ent = &dir->ent; + int result; + + // Clear current entry + memset(ent, 0, sizeof(struct dirent)); + + // If there are mount points to read, read them first + if (dir->mount) { + struct dirent *ment = mount_readdir((DIR *)dir); + if (ment) { + return ment; + } + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + +again: + // Read next directory entry + if ((result = lfs_dir_read(&lfs, ((vfs_dir_t *)pdir)->fs_dir, (struct lfs_info *)dir->fs_info)) < 0) { + errno = lfs_to_errno(result); + } else if (result > 0) { + if ((strcmp(((struct lfs_info *)dir->fs_info)->name,".") == 0) || (strcmp(((struct lfs_info *)dir->fs_info)->name,"..") == 0)) { + goto again; + } + + ent->d_type = (((struct lfs_info *)dir->fs_info)->type == LFS_TYPE_REG)?DT_REG:DT_DIR; + ent->d_fsize = ((struct lfs_info *)dir->fs_info)->size; + strlcpy(ent->d_name, ((struct lfs_info *)dir->fs_info)->name, MAXNAMLEN); + + mtx_unlock(&ctx->lock); + + return ent; + } + + mtx_unlock(&ctx->lock); + + return NULL; +} + +static long vfs_lfs_telldir(DIR *dirp) { + vfs_dir_t *dir = (vfs_dir_t *)dirp; + + lfs_soff_t offset; + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + offset = lfs_dir_tell(&lfs, dir->fs_dir); + if (offset < 0) { + mtx_unlock(&ctx->lock); + errno = EBADF; + return -1; + } + + mtx_unlock(&ctx->lock); + + return (long)offset; +} + +static int vfs_piffs_closedir(DIR *pdir) { + vfs_dir_t *dir = ((vfs_dir_t *)pdir); + int result; + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + if ((result = lfs_dir_close(&lfs, dir->fs_dir)) < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + vfs_free_dir(dir); + + mtx_unlock(&ctx->lock); + + return 0; +} + +static int vfs_lfs_mkdir(const char *path, mode_t mode) { + int result; + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + if ((result = lfs_mkdir(&lfs, path)) < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + return 0; +} + +static int vfs_lfs_fsync(int fd) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + result = lfs_file_sync(&lfs, file->fs_file); + if (result < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + return 0; +} + +static int vfs_lfs_ftruncate(int fd, off_t length) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)lfs.cfg->context; + + mtx_lock(&ctx->lock); + + result = lfs_file_truncate(&lfs, file->fs_file, length); + if (result < 0) { + mtx_unlock(&ctx->lock); + errno = lfs_to_errno(result); + return -1; + } + + mtx_unlock(&ctx->lock); + + return 0; +} + +static int lfs_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) { + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)c->context; + + if (spi_flash_read(ctx->base_addr + (block * c->block_size) + off, buffer, size) != 0) { + return LFS_ERR_IO; + } + + return 0; +} + +static int lfs_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) { + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)c->context; + + if (spi_flash_write(ctx->base_addr + (block * c->block_size) + off, buffer, size) != 0) { + return LFS_ERR_IO; + } + + return 0; +} + +static int lfs_erase(const struct lfs_config *c, lfs_block_t block) { + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)c->context; + + if (spi_flash_erase_sector((ctx->base_addr + (block * c->block_size)) >> 12) != 0) { + return LFS_ERR_IO; + } + + return 0; +} + +static int lfs_sync(const struct lfs_config *c) { + return 0; +} + +static struct lfs_config *lfs_config() { + // Find a partition + uint32_t base_address = 0; + uint32_t fs_size = 0; + + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, LUA_RTOS_LFS_PART, NULL); + + if (!partition) { + syslog(LOG_ERR, "lfs can't find a valid partition"); + return NULL; + } else { + base_address = partition->address; + fs_size = partition->size; + } + + // Allocate file system configuration data + struct lfs_config *cfg = calloc(1, sizeof(struct lfs_config)); + if (!cfg) { + syslog(LOG_ERR, "lfs not enough memory"); + return NULL; + } + + struct vfs_lfs_context *ctx = calloc(1, sizeof(struct vfs_lfs_context)); + if (!ctx) { + free(cfg); + + syslog(LOG_ERR, "lfs not enough memory"); + return NULL; + } + + // Configure the file system + cfg->read = lfs_read; + cfg->prog = lfs_prog; + cfg->erase = lfs_erase; + cfg->sync = lfs_sync; + + cfg->block_size = CONFIG_LUA_RTOS_LFS_BLOCK_SIZE; + cfg->read_size = CONFIG_LUA_RTOS_LFS_READ_SIZE; + cfg->prog_size = CONFIG_LUA_RTOS_LFS_PROG_SIZE; + cfg->block_count = fs_size / cfg->block_size; + cfg->lookahead = cfg->block_count; + + cfg->context = ctx; + ctx->base_addr = base_address; + + return cfg; +} + +int vfs_lfs_mount(const char *target) { + esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &vfs_lfs_write, + .open = &vfs_lfs_open, + .fstat = &vfs_lfs_fstat, + .close = &vfs_lfs_close, + .read = &vfs_lfs_read, + .lseek = &vfs_lfs_lseek, + .stat = &vfs_lfs_stat, + .link = NULL, + .unlink = &vfs_lfs_unlink, + .rename = &vfs_lfs_rename, + .mkdir = &vfs_lfs_mkdir, + .opendir = &vfs_lfs_opendir, + .readdir = &vfs_lfs_readdir, + .closedir = &vfs_piffs_closedir, + .rmdir = &vfs_lfs_rmdir, + .fsync = &vfs_lfs_fsync, + .access = &vfs_lfs_access, + .ftruncate = &vfs_lfs_ftruncate, + .telldir = &vfs_lfs_telldir, + }; + + // Get configuration + struct lfs_config *cfg = lfs_config(); + if (!cfg) { + return -1; + } + + struct vfs_lfs_context *ctx = (struct vfs_lfs_context *)(cfg->context); + + mtx_init(&ctx->lock, NULL, NULL, 0); + + syslog(LOG_INFO, + "lfs start address at 0x%x, size %d Kb", + ctx->base_addr, (cfg->block_count * cfg->block_size) / 1024); + + syslog(LOG_INFO, "lfs %d blocks, %d bytes/block, %d bytes/read, %d bytes/write",cfg->block_count,cfg->block_size, cfg->read_size, cfg->prog_size); + + // Mount file system + int err = lfs_mount(&lfs, cfg); + if (err < 0) { + syslog(LOG_INFO, "lfs formatting ..."); + + int block; + for(block = 0;block < cfg->block_count;block++) { + lfs_erase(cfg, block); + } + + lfs_format(&lfs, cfg); + err = lfs_mount(&lfs, cfg); + } + + if (err == LFS_ERR_OK) { + lstinit(&files, 0, LIST_DEFAULT); + + // Register the file system + ESP_ERROR_CHECK(esp_vfs_register("/lfs", &vfs, NULL)); + + syslog(LOG_INFO, "lfs mounted on %s", target); + + return 0; + } else { + free(cfg); + free(ctx); + + syslog(LOG_INFO, "lfs mount error"); + } + + return -1; +} + +int vfs_lfs_umount(const char *target) { + // Unmount + lfs_umount(&lfs); + + // Free resources + if (lfs.cfg) { + if (lfs.cfg->context) { + mtx_destroy(&((struct vfs_lfs_context *)lfs.cfg->context)->lock); + free(lfs.cfg->context); + } + + free((struct lfs_config *)lfs.cfg); + } + + lstdestroy(&files, 1); + + // Unregister vfs + esp_vfs_unregister("/lfs"); + + syslog(LOG_INFO, "lfs unmounted"); + + return 0; +} + +int vfs_lfs_format(const char *target) { + // Unmount first + vfs_lfs_umount(target); + + // Get configuration + struct lfs_config *cfg = lfs_config(); + if (!cfg) { + return -1; + } + + // Format + int block; + for(block = 0;block < cfg->block_count;block++) { + lfs_erase(cfg, block); + } + + int err = lfs_format(&lfs, cfg); + + // Free resources + if (lfs.cfg) { + if (lfs.cfg->context) { + free(lfs.cfg->context); + } + + free((struct lfs_config *)lfs.cfg); + } + + if (err == LFS_ERR_OK) { + // Mount again + vfs_lfs_mount(target); + } + + return -1; +} + +int vfs_lfs_fsstat(const char *target, u32_t *total, u32_t *used) { + + int err = lfs_info(&lfs, total, used); + if (err != 0) { + syslog(LOG_ERR, "lfs get fs info of '%s' (%i)", target, err); + return -1; + } + + return 0; +} + +#endif diff --git a/libiot/vfs/mkfile b/libiot/vfs/mkfile new file mode 100644 index 0000000..600ebc7 --- /dev/null +++ b/libiot/vfs/mkfile @@ -0,0 +1,7 @@ +OFILES=\ + $OFILES\ + vfs/spiffs.$O\ + +CFLAGS=\ + $CFLAGS\ + -Ivfs/include\ diff --git a/libiot/vfs/ramfs.c b/libiot/vfs/ramfs.c new file mode 100644 index 0000000..270209c --- /dev/null +++ b/libiot/vfs/ramfs.c @@ -0,0 +1,634 @@ +/* + * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L. + * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the <organization> nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * * The WHITECAT logotype cannot be changed, you can remove it, but you + * cannot change it in any way. The WHITECAT logotype is: + * + * /\ /\ + * / \_____/ \ + * /_____________\ + * W H I T E C A T + * + * * Redistributions in binary form must retain all copyright notices printed + * to any local or remote output device. This include any reference to + * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may + * appear in the future. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Lua RTOS, RAM file system + * + */ + +#include "sdkconfig.h" + +#if CONFIG_LUA_RTOS_USE_RAM_FS + +#include "esp_partition.h" + +#include <freertos/FreeRTOS.h> + +#include <string.h> +#include <stdio.h> +#include <limits.h> +#include <dirent.h> + +#include <sys/stat.h> + +#include "esp_vfs.h" + +#include <errno.h> + +#include "ramfs.h" + +#include <sys/syslog.h> +#include <sys/mount.h> +#include <sys/mutex.h> +#include <sys/list.h> +#include <sys/fcntl.h> +#include <sys/vfs/vfs.h> +#include <dirent.h> + +static int vfs_ramfs_open(const char *path, int flags, int mode); +static ssize_t vfs_ramfs_write(int fd, const void *data, size_t size); +static ssize_t vfs_ramfs_read(int fd, void * dst, size_t size); +static int vfs_ramfs_fstat(int fd, struct stat * st); +static int vfs_ramfs_close(int fd); +static off_t vfs_ramfs_lseek(int fd, off_t size, int mode); +static int vfs_ramfs_access(const char *path, int amode); + +static struct list files; +static ramfs_t fs; + +/* + * This function translate file system errors to POSIX errors + * + */ +static int ramfs_to_errno(int res) { + switch (res) { + case RAMFS_ERR_OK: + return 0; + case RAMFS_ERR_NOMEM: + return ENOMEM; + case RAMFS_ERR_NOENT: + return ENOENT; + case RAMFS_ERR_EXIST: + return EEXIST; + case RAMFS_ERR_NOTDIR: + return ENOTDIR; + case RAMFS_ERR_BADF: + return EBADF; + case RAMFS_ERR_ACCESS: + return EACCES; + case RAMFS_ERR_NOSPC: + return ENOSPC; + case RAMFS_ERR_INVAL: + return EINVAL; + case RAMFS_ERR_ISDIR: + return EISDIR; + case RAMFS_ERR_NOTEMPTY: + return ENOTEMPTY; + case RAMFS_ERR_BUSY: + return EBUSY; + case RAMFS_ERR_PERM: + return EPERM; + case RAMFS_ERR_NAMETOOLONG: + return ENAMETOOLONG; + } + + return ENOTSUP; +} + +static int vfs_ramfs_open(const char *path, int flags, int mode) { + int fd; + int result; + + // Allocate new file + vfs_file_t *file = calloc(1, sizeof(vfs_file_t)); + if (!file) { + errno = ENOMEM; + return -1; + } + + file->fs_file = (void *)calloc(1, sizeof(ramfs_file_t)); + if (!file->fs_file) { + free(file); + errno = ENOMEM; + return -1; + } + + // Add file to file list and get the file descriptor + int res = lstadd(&files, file, &fd); + if (res) { + free(file->fs_file); + free(file); + errno = res; + return -1; + } + + // Translate POSIX flags to file system flags + int ramfs_flags = 0; + + // Access mode + int access_mode = flags & O_ACCMODE; + + if (access_mode == O_RDONLY) { + ramfs_flags |= RAMFS_O_RDONLY; + } else if (access_mode == O_WRONLY) { + ramfs_flags |= RAMFS_O_WRONLY; + } else if (access_mode == O_RDWR) { + ramfs_flags |= RAMFS_O_RDWR; + } + + // File status + if (flags & O_CREAT) { + ramfs_flags |= RAMFS_O_CREAT; + } + + if (flags & O_EXCL) { + ramfs_flags |= RAMFS_O_EXCL; + } + + if (flags & O_TRUNC) { + ramfs_flags |= RAMFS_O_TRUNC; + } + + if (flags & O_APPEND) { + ramfs_flags |= RAMFS_O_APPEND; + } + + if ((result = ramfs_file_open(&fs, file->fs_file, path, ramfs_flags)) != RAMFS_ERR_OK) { + errno = ramfs_to_errno(result); + lstremove(&files, fd, 0); + + free(file->fs_file); + free(file); + + return -1; + } + + return fd; +} + +static ssize_t vfs_ramfs_write(int fd, const void *data, size_t size) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + // Write to file + result = ramfs_file_write(&fs, file->fs_file, (void *)data, size); + if (result < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + return result; +} + +static ssize_t vfs_ramfs_read(int fd, void *dst, size_t size) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + // Read from file + result = ramfs_file_read(&fs, file->fs_file, dst, size); + if (result < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + return result; +} + +static int vfs_ramfs_fstat(int fd, struct stat *st) { + vfs_file_t *file; + ramfs_info_t info; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + // Init stats + memset(st, 0, sizeof(struct stat)); + + // Get the file stats + result = ramfs_file_stat(&fs, file->fs_file, &info); + if (result < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + st->st_size = info.size; + st->st_mode = ((info.type==RAMFS_FILE)?S_IFREG:S_IFDIR); + st->st_blksize = fs.block_size; + + return 0; +} + +static int vfs_ramfs_close(int fd) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + // Close file + result = ramfs_file_close(&fs, file->fs_file); + if (result < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + // Remove file from file list + lstremove(&files, fd, 0); + + free(file->fs_file); + free(file); + + return 0; +} + +static off_t vfs_ramfs_lseek(int fd, off_t size, int mode) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + // Convert POSIX whence to file system whence + int whence = RAMFS_SEEK_CUR; + + switch (mode) { + case SEEK_SET: + whence = RAMFS_SEEK_SET; + break; + case SEEK_CUR: + whence = RAMFS_SEEK_CUR; + break; + case SEEK_END: + whence = RAMFS_SEEK_END; + break; + default: + errno = EINVAL; + return -1; + } + + result = ramfs_file_seek(&fs, file->fs_file, size, whence); + if (result < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + return result; +} + +static int vfs_ramfs_stat(const char *path, struct stat *st) { + ramfs_info_t info; + int result; + + // Init stats + memset(st, 0, sizeof(struct stat)); + + // Get the file stats + result = ramfs_stat(&fs, path, &info); + if (result < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + st->st_size = info.size; + st->st_mode = ((info.type==RAMFS_FILE)?S_IFREG:S_IFDIR); + st->st_blksize = fs.block_size; + + return 0; +} + +static int vfs_ramfs_access(const char *path, int amode) { +#if 0 + struct stat s; + + if (vfs_ramfs_stat(path, &s) < 0) { + return -1; + } + + if (s.st_mode != S_IFREG) { + errno = EACCES; + return -1; + } + + return 0; +#endif + return 0; + +} + +static int vfs_ramfs_unlink(const char *path) { + int result; + + if ((result = ramfs_unlink(&fs, path)) < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + return 0; +} + +static int vfs_ramfs_rename(const char *src, const char *dst) { + int result; + + if ((result = ramfs_rename(&fs, src, dst)) < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + return 0; +} + +static DIR* vfs_ramfs_opendir(const char *name) { + int result; + + vfs_dir_t *dir = vfs_allocate_dir("ramfs", name); + if (!dir) { + return NULL; + } + + // Open directory + if ((result = ramfs_dir_open(&fs, dir->fs_dir, name)) < 0) { + errno = ramfs_to_errno(result); + vfs_free_dir(dir); + + return NULL; + } + + return (DIR *)dir; +} + +static int vfs_ramfs_rmdir(const char *name) { + int result; + + if ((result = ramfs_rmdir(&fs, name)) < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + return 0; +} + +static struct dirent *vfs_ramfs_readdir(DIR *pdir) { + vfs_dir_t *dir = (vfs_dir_t *)pdir; + struct dirent *ent = &dir->ent; + int result; + + // Clear current entry + memset(ent, 0, sizeof(struct dirent)); + + // If there are mount points to read, read them first + if (dir->mount) { + struct dirent *ment = mount_readdir((DIR *)dir); + if (ment) { + return ment; + } + } + + // Read next directory entry + if ((result = ramfs_dir_read(&fs, ((vfs_dir_t *)pdir)->fs_dir, (ramfs_info_t *)dir->fs_info)) == RAMFS_ERR_OK) { + ent->d_type = (((ramfs_info_t *)dir->fs_info)->type == RAMFS_FILE)?DT_REG:DT_DIR; + ent->d_fsize = ((ramfs_info_t *)dir->fs_info)->size; + strlcpy(ent->d_name, ((ramfs_info_t *)dir->fs_info)->name, MAXNAMLEN); + + return ent; + } + + if (result != RAMFS_ERR_NOENT) { + errno = ramfs_to_errno(result); + } + + return NULL; +} + +static long vfs_ramfs_telldir(DIR *dirp) { + vfs_dir_t *dir = (vfs_dir_t *)dirp; + ramfs_off_t offset; + + offset = ramfs_telldir(&fs, dir->fs_dir); + if (offset < 0) { + errno = ramfs_to_errno(offset); + return -1; + } + + return (long)offset; +} + +static int vfs_ramfs_closedir(DIR *pdir) { + vfs_dir_t *dir = ((vfs_dir_t *)pdir); + int result; + + if ((result = ramfs_dir_close(&fs, dir->fs_dir)) < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + vfs_free_dir(dir); + + return 0; +} + +static int vfs_ramfs_mkdir(const char *path, mode_t mode) { + int result; + + if ((result = ramfs_mkdir(&fs, path)) < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + return 0; +} + +static int vfs_ramfs_fsync(int fd) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + result = ramfs_file_sync(&fs, file->fs_file); + if (result < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + return 0; +} + +static int vfs_ramfs_ftruncate(int fd, off_t length) { + vfs_file_t *file; + int result; + + // Get file from file list + result = lstget(&files, fd, (void **) &file); + if (result) { + errno = EBADF; + return -1; + } + + if ((result = ramfs_file_truncate(&fs, file->fs_file, length)) != RAMFS_ERR_OK) { + errno = ramfs_to_errno(result); + return -1; + } + + return 0; +} + +static int vfs_ramfs_truncate(const char *path, off_t length) { + ramfs_file_t file; + int result; + + if ((result = ramfs_file_open(&fs, &file, path, RAMFS_O_RDWR)) != RAMFS_ERR_OK) { + errno = ramfs_to_errno(result); + return -1; + } + + if ((result = ramfs_file_truncate(&fs, &file, length)) != RAMFS_ERR_OK) { + errno = ramfs_to_errno(result); + return -1; + } + + result = ramfs_file_close(&fs, &file); + if (result < 0) { + errno = ramfs_to_errno(result); + return -1; + } + + return 0; +} + +int vfs_ramfs_mount(const char *target) { + esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &vfs_ramfs_write, + .open = &vfs_ramfs_open, + .fstat = &vfs_ramfs_fstat, + .close = &vfs_ramfs_close, + .read = &vfs_ramfs_read, + .lseek = &vfs_ramfs_lseek, + .stat = &vfs_ramfs_stat, + .link = NULL, + .unlink = &vfs_ramfs_unlink, + .rename = &vfs_ramfs_rename, + .mkdir = &vfs_ramfs_mkdir, + .opendir = &vfs_ramfs_opendir, + .readdir = &vfs_ramfs_readdir, + .closedir = &vfs_ramfs_closedir, + .rmdir = &vfs_ramfs_rmdir, + .fsync = &vfs_ramfs_fsync, + .access = &vfs_ramfs_access, + .telldir = &vfs_ramfs_telldir, + .truncate = &vfs_ramfs_truncate, + .ftruncate = &vfs_ramfs_ftruncate, + }; + + int res; + ramfs_config_t config; + + config.size = CONFIG_LUA_RTOS_RAM_FS_SIZE; + config.block_size = CONFIG_LUA_RTOS_RAM_FS_BLOCK_SIZE; + + syslog(LOG_INFO, "ramfs size %d Kb, block size %d bytes", config.size / 1024, config.block_size); + + if ((res = ramfs_mount(&fs, &config)) == RAMFS_ERR_OK) { + lstinit(&files, 0, LIST_DEFAULT); + + // Register the file system + ESP_ERROR_CHECK(esp_vfs_register("/ramfs", &vfs, NULL)); + + syslog(LOG_INFO, "ramfs mounted on %s", target); + + return 0; + } else { + syslog(LOG_INFO, "ramfs mount error"); + } + + return -1; +} + +int vfs_ramfs_umount(const char *target) { + ramfs_umount(&fs); + esp_vfs_unregister("/ramfs"); + + syslog(LOG_INFO, "ramfs unmounted"); + + return 0; +} + +int vfs_ramfs_format(const char *target) { + vfs_ramfs_umount(target); + vfs_ramfs_mount(target); + + return 0; +} + +int vfs_ramfs_fsstat(const char *target, u32_t *total, u32_t *used) { + + if (total) { + *total = fs.size; + } + + if (used) { + *used = fs.current_size; + } + + return 0; +} + +#endif diff --git a/libiot/vfs/spiffs.c b/libiot/vfs/spiffs.c new file mode 100644 index 0000000..cdd445a --- /dev/null +++ b/libiot/vfs/spiffs.c @@ -0,0 +1,1423 @@ +/* + * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L. + * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the <organization> nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * * The WHITECAT logotype cannot be changed, you can remove it, but you + * cannot change it in any way. The WHITECAT logotype is: + * + * /\ /\ + * / \_____/ \ + * /_____________\ + * W H I T E C A T + * + * * Redistributions in binary form must retain all copyright notices printed + * to any local or remote output device. This include any reference to + * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may + * appear in the future. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Lua RTOS spiffs vfs + * + */ + +//#include "sdkconfig.h" + +//#if CONFIG_LUA_RTOS_USE_SPIFFS + +//#include "esp_partition.h" + +#include <FreeRTOS.h> + +#include <string.h> +#include <stdio.h> +#include <limits.h> + +#include <sys/stat.h> + +#include "esp_vfs.h" +//#include "esp_attr.h" +#include <errno.h> + +#include "../spiffs/spiffs.h" +#include "../spiffs/k210_spiffs.h" +#include "../spiffs/spiffs_nucleus.h" +//#include <sys/syslog.h> +//#include <sys/mount.h> +#include "../sys/mutex.h" +#include "../sys/list.h" +#include <sys/fcntl.h> +#include "vfs.h" +#include <dirent.h> + +#include "../hal/w25qxx.h" + +#define syslog(a, ...) printf(__VA_ARGS__) + +static int vfs_spiffs_open(const char *path, int flags, int mode); +static ssize_t vfs_spiffs_write(int fdi, const void *data, size_t size); +static ssize_t vfs_spiffs_read(int fdi, void * dst, size_t size); +static int vfs_spiffs_fstat(int fdi, struct stat * st); +static int vfs_spiffs_close(int fdi); +static off_t vfs_spiffs_lseek(int fdi, off_t size, int mode); +static int vfs_spiffs_access(const char *path, int amode); + +#if !defined(max) +#define max(A,B) ( (A) > (B) ? (A):(B)) +#endif + +#if !defined(min) +#define min(A,B) ( (A) < (B) ? (A):(B)) +#endif + +#define VFS_SPIFFS_FLAGS_READONLY (1 << 0) + +static spiffs fs; +static struct list files; + +static u8_t *my_spiffs_work_buf = NULL; +static u8_t *my_spiffs_fds = NULL; +static u8_t *my_spiffs_cache = NULL; + +static struct mtx vfs_mtx; +static struct mtx ll_mtx; + + +typedef struct fd_itm fd_itm; + +struct fd_itm { + long fd; + fd_itm* next; +}; + +fd_itm* fds_root = NULL; +fd_itm** fds_root_last = &fds_root; +int fds_cnt = 0; + + +static int new_fd_itm(long fd){ + fd_itm *f = malloc(sizeof(fd_itm)); + + f->next = NULL; + f->fd = fd; + + *fds_root_last = f; + fds_root_last = &f->next; + + fds_cnt++; + + return fds_cnt - 1; +} + +static long del_fd_itm(int fdi){ + long fd = -1; + int i; + fd_itm *f = fds_root; + fd_itm **pref = &fds_root; + + if(fdi < 0 || fdi >= fds_cnt || f == NULL) + return -1; + + for(i = 0; i < fdi; i++){ + pref = &f->next; + f = f->next; + } + *pref = f->next; + + fd = f->fd; + + free(f); + + fds_cnt--; + + return fd; +} + +static long get_fd_itm(int fdi){ + long fd = -1; + int i; + fd_itm *f = fds_root; + + if(fdi < 0 || fdi >= fds_cnt || f == NULL) + return -1; + + for(i = 0; i < fdi; i++){ + f = f->next; + } + fd = f->fd; + + return fd; +} + + + +static void dir_path(char *npath, uint8_t base) { + int len = strlen(npath); + + if (base) { + char *c; + + c = &npath[len - 1]; + while (c >= npath) { + if (*c == '/') { + break; + } + + len--; + c--; + } + } + + if (len > 1) { + if (npath[len - 1] == '.') { + npath[len - 1] = '\0'; + + if (npath[len - 2] == '/') { + npath[len - 2] = '\0'; + } + } else { + if (npath[len - 1] == '/') { + npath[len - 1] = '\0'; + } + } + } else { + if ((npath[len - 1] == '/') || (npath[len - 1] == '.')) { + npath[len - 1] = '\0'; + } + } + + strlcat(npath, "/.", PATH_MAX); +} + +static void check_path(const char *path, uint8_t *base_is_dir, + uint8_t *full_is_dir, uint8_t *is_file, int *filenum) { + char bpath[PATH_MAX + 1]; // Base path + char fpath[PATH_MAX + 1]; // Full path + struct spiffs_dirent e; + spiffs_DIR d; + int file_num = 0; + + *filenum = 0; + *base_is_dir = 0; + *full_is_dir = 0; + *is_file = 0; + + // Get base directory name + strlcpy(bpath, path, PATH_MAX); + dir_path(bpath, 1); + + // Get full directory name + strlcpy(fpath, path, PATH_MAX); + dir_path(fpath, 0); + + SPIFFS_opendir(&fs, "/", &d); + while (SPIFFS_readdir(&d, &e)) { + if (!strcmp(bpath, (const char *) e.name)) { + *base_is_dir = 1; + } + + if (!strcmp(fpath, (const char *) e.name)) { + *full_is_dir = 1; + } + + if (!strcmp(path, (const char *) e.name)) { + *is_file = 1; + } + + if (!strncmp(fpath, (const char *) e.name, min(strlen((char * )e.name), strlen(fpath) - 1))) { + if (strlen((const char *) e.name) >= strlen(fpath) && strcmp(fpath, (const char *) e.name)) { + file_num++; + } + } + } + SPIFFS_closedir(&d); + + *filenum = file_num; +} + +/* + * This function translate error codes from SPIFFS to errno error codes + * + */ +static int spiffs_result(int res) { + switch(res) { + case SPIFFS_OK : + case SPIFFS_ERR_END_OF_OBJECT: + return 0; + case SPIFFS_ERR_NOT_WRITABLE: + case SPIFFS_ERR_NOT_READABLE: + return EACCES; + case SPIFFS_ERR_NOT_MOUNTED : + case SPIFFS_ERR_NOT_A_FS : + return ENODEV; + case SPIFFS_ERR_FULL : + return ENOSPC; + case SPIFFS_ERR_BAD_DESCRIPTOR : + return EBADF; + case SPIFFS_ERR_MOUNTED : + return EEXIST; + case SPIFFS_ERR_FILE_EXISTS : + return EEXIST; + case SPIFFS_ERR_NOT_FOUND : + case SPIFFS_ERR_CONFLICTING_NAME: + case SPIFFS_ERR_NOT_A_FILE : + case SPIFFS_ERR_DELETED : + case SPIFFS_ERR_FILE_DELETED : + return ENOENT; + case SPIFFS_ERR_NAME_TOO_LONG : + return ENAMETOOLONG; + case SPIFFS_ERR_RO_NOT_IMPL : + case SPIFFS_ERR_RO_ABORTED_OPERATION : + return EROFS; + default: + return EIO; + } + + return ENOTSUP; +} + +static int vfs_spiffs_traverse(const char *pathp, int *pis_dir, int *filenum, int *valid_prefix) { + char path[PATH_MAX + 1]; + char current[PATH_MAX + 3]; // Current path + char *dir; // Current directory + spiffs_stat stat; + int res; + + int is_dir = 0; + + if (pis_dir) { + *pis_dir = 0; + } + + if (filenum) { + *filenum = 0; + } + + if (valid_prefix) { + *valid_prefix = 1; + } + + if (strlen(pathp) > PATH_MAX - 3) { + return ENAMETOOLONG; + } + + // Copy original path, because the strtok function changes the original string + strlcpy(path, pathp, PATH_MAX); + + // Current directory is empty + strcpy(current, ""); + + // Get the first directory in path + dir = strtok((char *)path, "/"); + while (dir) { + // Append current directory to the current path + strncat(current, "/", PATH_MAX); + strncat(current, dir, PATH_MAX); + + // To check if the current path is a directory + // we must append /. + strncat(current, "/.", PATH_MAX); + + res = SPIFFS_stat(&fs, current, &stat); + + // Remove /. from the current path to check if the current path + // corresponds to a file in case that is required later + *(current + strlen(current) - 2) = '\0'; + + // Get next directory in path + dir = strtok(NULL, "/"); + + if (res != SPIFFS_OK) { + // Current path is not a directory, then check if it is a file + res = SPIFFS_stat(&fs, current, &stat); + if (res != SPIFFS_OK) { + // Current path is not a directory, and it is not a file. + if (dir) { + if (valid_prefix) { + *valid_prefix = 0; + } + } + + // Error: A component in pathname does not exist + return ENOENT; + } else { + // Current path is a file + if (dir) { + // There are more directories in path to check. + + if (valid_prefix) { + *valid_prefix = 0; + } + + // Error: a component used as a directory in pathname is not, + // in fact, a directory + return ENOTDIR; + } + + is_dir = 0; + } + } else { + // Current path is a directory + is_dir = 1; + } + } + + if (is_dir && filenum) { + // Count files in directory + struct spiffs_dirent e; + spiffs_DIR d; + + // Get full directory name + char fpath[PATH_MAX + 1]; + + strlcpy(fpath, pathp, PATH_MAX); + dir_path(fpath, 0); + + SPIFFS_opendir(&fs, "/", &d); + while (SPIFFS_readdir(&d, &e)) { + if (!strncmp(fpath, (const char *) e.name, min(strlen((char * )e.name), strlen(fpath) - 1))) { + if (strlen((const char *) e.name) >= strlen(fpath) && strcmp(fpath, (const char *) e.name)) { + *filenum = *filenum + 1; + } + } + } + SPIFFS_closedir(&d); + } + + if (pis_dir) { + *pis_dir = (is_dir || (strcmp(pathp, "/") == 0)); + } + + return EEXIST; +} + +static int vfs_spiffs_open(const char *path, int flags, int mode) { + char npath[PATH_MAX + 1]; + long fd; + int result = 0; + + // Allocate new file + vfs_file_t *file = calloc(1, sizeof(vfs_file_t)); + if (!file) { + errno = ENOMEM; + return -1; + } + + file->fs_file = (void *)calloc(1, sizeof(spiffs_file)); + if (!file->fs_file) { + free(file); + errno = ENOMEM; + return -1; + } + + // Add file to file list. List index is file descriptor. + int res = lstadd(&files, file, &fd); + if (res) { + free(file->fs_file); + free(file); + errno = res; + return -1; + } + + // Open file + spiffs_flags spiffs_flgs = 0; + + // Translate flags to SPIFFS flags + if (flags == O_RDONLY) + spiffs_flgs |= SPIFFS_RDONLY; + + if (flags & O_WRONLY) + spiffs_flgs |= SPIFFS_WRONLY; + + if (flags & O_RDWR) + spiffs_flgs = SPIFFS_RDWR; + + if (flags & O_EXCL) + spiffs_flgs |= SPIFFS_EXCL; + + if (flags & O_CREAT) + spiffs_flgs |= SPIFFS_CREAT; + + if (flags & O_TRUNC) + spiffs_flgs |= SPIFFS_TRUNC; + + // Check path + uint8_t base_is_dir = 0; + uint8_t full_is_dir = 0; + uint8_t is_file = 0; + int file_num = 0; + + mtx_lock(&vfs_mtx); + + check_path(path, &base_is_dir, &full_is_dir, &is_file, &file_num); + + if (full_is_dir) { + // We want to open a directory. + // If in flags are set some write access mode this is an error, because we only + // can open a directory in read mode. + if (spiffs_flgs & (SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC)) { + lstremove(&files, fd, 0); + free(file->fs_file); + free(file); + mtx_unlock(&vfs_mtx); + errno = EISDIR; + return -1; + } + + // Open the directory + strlcpy(npath, path, PATH_MAX); + dir_path((char *) npath, 0); + + // Open SPIFFS file + *((spiffs_file *)file->fs_file) = SPIFFS_open(&fs, npath, SPIFFS_RDONLY, 0); + if (*((spiffs_file *)file->fs_file) < 0) { + result = spiffs_result(fs.err_code); + } + + file->is_dir = 1; + } else { + if (!base_is_dir) { + // If base path is not a directory we return an error + lstremove(&files, fd, 0); + free(file->fs_file); + free(file); + mtx_unlock(&vfs_mtx); + errno = ENOENT; + return -1; + } else { + // Open SPIFFS file + *((spiffs_file *)file->fs_file) = SPIFFS_open(&fs, path, spiffs_flgs, 0); + if (*((spiffs_file *)file->fs_file) < 0) { + result = spiffs_result(fs.err_code); + } + } + } + + if (result != 0) { + lstremove(&files, fd, 0); + free(file->fs_file); + free(file); + mtx_unlock(&vfs_mtx); + errno = result; + return -1; + } + + int fdi = new_fd_itm(fd); + + mtx_unlock(&vfs_mtx); + + return fdi; //fd; +} + +static ssize_t vfs_spiffs_write(int fdi, const void *data, size_t size) { + vfs_file_t *file; + int res; + long fd = get_fd_itm(fdi); + + res = lstget(&files, fd, (void **) &file); + if (res || file->is_dir) { + errno = EBADF; + return -1; + } + + mtx_lock(&vfs_mtx); + + // Write SPIFFS file + res = SPIFFS_write(&fs, *((spiffs_file *)file->fs_file), (void *) data, size); + if (res >= 0) { + mtx_unlock(&vfs_mtx); + return res; + } else { + res = spiffs_result(fs.err_code); + if (res != 0) { + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + } + + mtx_unlock(&vfs_mtx); + + return -1; +} + +static ssize_t vfs_spiffs_read(int fdi, void * dst, size_t size) { + vfs_file_t *file; + int res; + long fd = get_fd_itm(fdi); + + res = lstget(&files, fd, (void **) &file); + if (res || file->is_dir) { + errno = EBADF; + return -1; + } + + mtx_lock(&vfs_mtx); + + // Read SPIFFS file + res = SPIFFS_read(&fs, *((spiffs_file *)file->fs_file), dst, size); + if (res >= 0) { + mtx_unlock(&vfs_mtx); + return res; + } else { + res = spiffs_result(fs.err_code); + if (res != 0) { + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + + // EOF + mtx_unlock(&vfs_mtx); + return 0; + } + + mtx_unlock(&vfs_mtx); + + return -1; +} + +static int vfs_spiffs_fstat(int fdi, struct stat * st) { + vfs_file_t *file; + spiffs_stat stat; + int res; + long fd = get_fd_itm(fdi); + + res = lstget(&files, fd, (void **) &file); + if (res) { + errno = EBADF; + return -1; + } + + // We have not time in SPIFFS + st->st_mtime = 0; + st->st_atime = 0; + st->st_ctime = 0; + + // Set block size for this file system + st->st_blksize = w25qxx_FLASH_PAGE_SIZE; //{} CONFIG_LUA_RTOS_SPIFFS_LOG_PAGE_SIZE; + + // First test if it's a directory entry + if (file->is_dir) { + st->st_mode = S_IFDIR; + st->st_size = 0; + return 0; + } + + mtx_lock(&vfs_mtx); + + // If is not a directory get file statistics + res = SPIFFS_fstat(&fs, *((spiffs_file *)file->fs_file), &stat); + if (res == SPIFFS_OK) { + st->st_size = stat.size; + } else { + st->st_size = 0; + res = spiffs_result(fs.err_code); + } + + st->st_mode = S_IFREG; + + if (res < 0) { + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + + mtx_unlock(&vfs_mtx); + + return 0; +} + +static int vfs_spiffs_close(int fdi) { + vfs_file_t *file; + int res; + long fd = del_fd_itm(fdi); + + res = lstget(&files, fd, (void **) &file); + if (res) { + errno = EBADF; + return -1; + } + + mtx_lock(&vfs_mtx); + + res = SPIFFS_close(&fs, *((spiffs_file *)file->fs_file)); + if (res) { + res = spiffs_result(fs.err_code); + } + + if (res < 0) { + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + + lstremove(&files, fd, 0); + + free(file->fs_file); + free(file); + + mtx_unlock(&vfs_mtx); + + return 0; +} + +static off_t vfs_spiffs_lseek(int fdi, off_t size, int mode) { + vfs_file_t *file; + int res; + long fd = get_fd_itm(fdi); + + res = lstget(&files, fd, (void **) &file); + if (res) { + errno = EBADF; + return -1; + } + + if (file->is_dir) { + errno = EBADF; + return -1; + } + + int whence = SPIFFS_SEEK_CUR; + + switch (mode) { + case SEEK_SET: + whence = SPIFFS_SEEK_SET; + break; + case SEEK_CUR: + whence = SPIFFS_SEEK_CUR; + break; + case SEEK_END: + whence = SPIFFS_SEEK_END; + break; + } + + mtx_lock(&vfs_mtx); + + res = SPIFFS_lseek(&fs, *((spiffs_file *)file->fs_file), size, whence); + if (res < 0) { + res = spiffs_result(fs.err_code); + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + + mtx_unlock(&vfs_mtx); + + return res; +} + +static int vfs_spiffs_stat(const char * path, struct stat * st) { + int fd; + int res; + + mtx_lock(&vfs_mtx); + + fd = vfs_spiffs_open(path, 0, 0); + if (fd < 0) { + mtx_unlock(&vfs_mtx); + errno = spiffs_result(fs.err_code); + return -1; + } + + res = vfs_spiffs_fstat(fd, st); + if (fd < 0) { + mtx_unlock(&vfs_mtx); + errno = spiffs_result(fs.err_code); + return -1; + } + + vfs_spiffs_close(fd); + + mtx_unlock(&vfs_mtx); + + return res; +} + +static int vfs_spiffs_access(const char *path, int amode) { + struct stat s; + + mtx_lock(&vfs_mtx); + + if (vfs_spiffs_stat(path, &s) < 0) { + mtx_unlock(&vfs_mtx); + return -1; + } + + if (s.st_mode != S_IFREG) { + mtx_unlock(&vfs_mtx); + errno = EACCES; + return -1; + } + + mtx_unlock(&vfs_mtx); + + return 0; +} + +static int vfs_spiffs_unlink(const char *path) { + int is_dir; + + mtx_lock(&vfs_mtx); + + int res = vfs_spiffs_traverse(path, &is_dir, NULL, NULL); + if ((res != 0) && (res != EEXIST)) { + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + + if (is_dir) { + mtx_unlock(&vfs_mtx); + errno = EPERM; + return -1; + } + + res = SPIFFS_remove(&fs, path); + if (res) { + mtx_unlock(&vfs_mtx); + errno = spiffs_result(fs.err_code); + return -1; + } + + mtx_unlock(&vfs_mtx); + + return 0; +} + +static int vfs_spiffs_rename(const char *src, const char *dst) { + int src_is_dir; + int dst_is_dir; + int dst_files; + + mtx_lock(&vfs_mtx); + + int res = vfs_spiffs_traverse(src, &src_is_dir, NULL, NULL); + if ((res != 0) && (res != EEXIST) && (res != ENOENT)) { + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + + res = vfs_spiffs_traverse(dst, &dst_is_dir, &dst_files, NULL); + if ((res != 0) && (res != EEXIST) && (res != ENOENT)) { + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + + if (dst_is_dir && !src_is_dir) { + mtx_unlock(&vfs_mtx); + errno = EISDIR; + return -1; + } + + if (dst_is_dir && (dst_files > 0)) { + mtx_unlock(&vfs_mtx); + errno = ENOTEMPTY; + return -1; + } + + char dpath[PATH_MAX + 1]; + char *csrc; + char *cname; + + if (src_is_dir) { + // We need to rename all tree + struct spiffs_dirent e; + spiffs_DIR d; + + SPIFFS_opendir(&fs, "/", &d); + while (SPIFFS_readdir(&d, &e)) { + if (!strncmp(src, (const char *) e.name, strlen(src)) && e.name[strlen(src)] == '/') { + strlcpy(dpath, dst, PATH_MAX); + csrc = (char *) src; + cname = (char *) e.name; + + while (*csrc && *cname && (*csrc == *cname)) { + ++csrc; + ++cname; + } + + strlcat(dpath, cname, PATH_MAX); + + if (SPIFFS_rename(&fs, (char *) e.name, dpath) != SPIFFS_OK) { + if (fs.err_code != SPIFFS_ERR_CONFLICTING_NAME) { + mtx_unlock(&vfs_mtx); + errno = spiffs_result(fs.err_code); + return -1; + } else { + // This only happens when e.name and dpath are directories. In this case + // remove e.name + if (SPIFFS_remove(&fs, (char *) e.name) != SPIFFS_OK) { + mtx_unlock(&vfs_mtx); + errno = spiffs_result(fs.err_code); + return -1; + } + } + } + } + } + SPIFFS_closedir(&d); + } else { + if (SPIFFS_rename(&fs, src, dst) < 0) { + mtx_unlock(&vfs_mtx); + errno = spiffs_result(fs.err_code); + return -1; + } + } + + mtx_unlock(&vfs_mtx); + + return 0; +} + +static DIR* vfs_spiffs_opendir(const char* name) { + int is_dir; + + vfs_dir_t *dir = vfs_allocate_dir("spiffs", name); + if (!dir) { + return NULL; + } + + mtx_lock(&vfs_mtx); + + int res = vfs_spiffs_traverse(name, &is_dir, NULL, NULL); + if ((res != 0) && (res != EEXIST)) { + vfs_free_dir(dir); + mtx_unlock(&vfs_mtx); + errno = res; + return NULL; + } + + if ((res == EEXIST) && !is_dir) { + vfs_free_dir(dir); + mtx_unlock(&vfs_mtx); + errno = ENOTDIR; + return NULL; + } + + if (!is_dir) { + vfs_free_dir(dir); + mtx_unlock(&vfs_mtx); + errno = ENOENT; + return NULL; + } + + if (!SPIFFS_opendir(&fs, name, (spiffs_DIR *)dir->fs_dir)) { + vfs_free_dir(dir); + mtx_unlock(&vfs_mtx); + errno = spiffs_result(fs.err_code); + return NULL; + } + + mtx_unlock(&vfs_mtx); + + return (DIR *) dir; +} + +static int vfs_spiffs_rmdir(const char *path) { + int is_dir; + int filenum; + + if (strcmp(path,"/") == 0) { + errno = EBUSY; + return -1; + } + + mtx_lock(&vfs_mtx); + + int res = vfs_spiffs_traverse(path, &is_dir, &filenum, NULL); + if ((res != 0) && (res != EEXIST)) { + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + + if (!is_dir) { + mtx_unlock(&vfs_mtx); + errno = ENOTDIR; + return -1; + } + + if (filenum > 0) { + mtx_unlock(&vfs_mtx); + errno = ENOTEMPTY; + return -1; + } + + // Remove directory + char npath[PATH_MAX + 1]; + + strlcpy(npath, path, PATH_MAX); + dir_path(npath, 0); + + res = SPIFFS_remove(&fs, npath); + if (res) { + mtx_unlock(&vfs_mtx); + errno = spiffs_result(fs.err_code); + return -1; + } + + mtx_unlock(&vfs_mtx); + + return 0; +} + +static struct dirent* vfs_spiffs_readdir(DIR* pdir) { + int res = 0, len = 0, entries = 0; + vfs_dir_t *dir = (vfs_dir_t *) pdir; + + struct dirent *ent = &dir->ent; + + char *fn; + + // Clear the current dirent + memset(ent, 0, sizeof(struct dirent)); + + // If there are mount points to read, read them first + if (dir->mount) { + struct dirent *ment = mount_readdir((DIR *)dir); + if (ment) { + return ment; + } + } + + mtx_lock(&vfs_mtx); + + // Search for next entry + for (;;) { + // Read directory + dir->fs_info = (void *)SPIFFS_readdir((spiffs_DIR *)dir->fs_dir, (struct spiffs_dirent *)dir->fs_info); + if (!dir->fs_info) { + if (fs.err_code != SPIFFS_VIS_END) { + res = spiffs_result(fs.err_code); + errno = res; + } + + break; + } + + // Break condition + if (((struct spiffs_dirent *)(dir->fs_info))->name[0] == 0) + break; + + // Get name and length + fn = (char *)(((struct spiffs_dirent *)(dir->fs_info))->name); + len = strlen(fn); + + // Get entry type and size + ent->d_type = DT_REG; + + if (len >= 2) { + if (fn[len - 1] == '.') { + if (fn[len - 2] == '/') { + ent->d_type = DT_DIR; + + fn[len - 2] = '\0'; + + len = strlen(fn); + + // Skip root dir + if (len == 0) { + continue; + } + } + } + } + + // Skip entries not belonged to path + if (strncmp(fn, dir->path, strlen(dir->path)) != 0) { + continue; + } + + if (strlen(dir->path) > 1) { + if (*(fn + strlen(dir->path)) != '/') { + continue; + } + } + + // Skip root directory + fn = fn + strlen(dir->path); + len = strlen(fn); + if (len == 0) { + continue; + } + + // Skip initial / + if (len > 1) { + if (*fn == '/') { + fn = fn + 1; + len--; + } + } + + // Skip subdirectories + if (strchr(fn, '/')) { + continue; + } + + ent->d_fsize = ((struct spiffs_dirent *)(dir->fs_info))->size; + + strlcpy(ent->d_name, fn, MAXNAMLEN); + + entries++; + dir->offset++; + + break; + } + + mtx_unlock(&vfs_mtx); + + if (entries > 0) { + return ent; + } else { + return NULL; + } +} + +static long vfs_spiffs_telldir(DIR *dirp) { + vfs_dir_t *dir = (vfs_dir_t *)dirp; + + if (dir->offset < 0) { + errno = EBADF; + return -1; + } + + return (long)dir->offset; +} + +static int vfs_spiffs_closedir(DIR* pdir) { + vfs_dir_t *dir = (vfs_dir_t *) pdir; + int res; + + if (!pdir) { + errno = EBADF; + return -1; + } + + mtx_lock(&vfs_mtx); + + if ((res = SPIFFS_closedir((spiffs_DIR *)dir->fs_dir)) < 0) { + mtx_unlock(&vfs_mtx); + errno = spiffs_result(fs.err_code); + return -1; + } + + vfs_free_dir(dir); + + mtx_unlock(&vfs_mtx); + + return 0; +} + +static int vfs_spiffs_mkdir(const char *path, mode_t mode) { + char npath[PATH_MAX + 1]; + int res; + int valid_prefix; + + mtx_lock(&vfs_mtx); + + res = vfs_spiffs_traverse(path, NULL, NULL, &valid_prefix); + if ((res != 0) && ((res != ENOENT) || (!valid_prefix))) { + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + + // Create directory + strlcpy(npath, path, PATH_MAX); + dir_path(npath, 0); + + spiffs_file fd = SPIFFS_open(&fs, npath, SPIFFS_CREAT | SPIFFS_RDWR, 0); + if (fd < 0) { + mtx_unlock(&vfs_mtx); + res = spiffs_result(fs.err_code); + errno = res; + return -1; + } + + if (SPIFFS_close(&fs, fd) < 0) { + mtx_unlock(&vfs_mtx); + res = spiffs_result(fs.err_code); + errno = res; + return -1; + } + + mtx_unlock(&vfs_mtx); + + return 0; +} + +static int vfs_spiffs_fsync(int fdi) { + vfs_file_t *file; + int res; + long fd = get_fd_itm(fdi); + + res = lstget(&files, fd, (void **) &file); + if (res) { + errno = EBADF; + return -1; + } + + if (file->is_dir) { + errno = EBADF; + return -1; + } + + mtx_lock(&vfs_mtx); + + res = SPIFFS_fflush(&fs, *((spiffs_file *)file->fs_file)); + if (res >= 0) { + mtx_unlock(&vfs_mtx); + return res; + } else { + res = spiffs_result(fs.err_code); + if (res != 0) { + mtx_unlock(&vfs_mtx); + errno = res; + return -1; + } + + mtx_unlock(&vfs_mtx); + errno = EIO; + return -1; + } + + mtx_unlock(&vfs_mtx); + + return 0; +} + +static void vfs_spiffs_free_resources() { + if (my_spiffs_work_buf) free(my_spiffs_work_buf); + if (my_spiffs_fds) free(my_spiffs_fds); + if (my_spiffs_cache) free(my_spiffs_cache); + + mtx_destroy(&vfs_mtx); + mtx_destroy(&ll_mtx); +} + +int vfs_spiffs_mount(const char *target) { + esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &vfs_spiffs_write, + .open = &vfs_spiffs_open, + .fstat = &vfs_spiffs_fstat, + .close = &vfs_spiffs_close, + .read = &vfs_spiffs_read, + .lseek = &vfs_spiffs_lseek, + .stat = &vfs_spiffs_stat, + .link = NULL, + .unlink = &vfs_spiffs_unlink, + .rename = &vfs_spiffs_rename, + .mkdir = &vfs_spiffs_mkdir, + .opendir = &vfs_spiffs_opendir, + .readdir = &vfs_spiffs_readdir, + .closedir = &vfs_spiffs_closedir, + .rmdir = &vfs_spiffs_rmdir, + .fsync = &vfs_spiffs_fsync, + .access = &vfs_spiffs_access, + .telldir = &vfs_spiffs_telldir, + }; + + // Mount spiffs file system + spiffs_config cfg; + int res = 0; + int retries = 0; + + // Find partition + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, LUA_RTOS_SPIFFS_PART, NULL); + + if (!partition) { + vfs_spiffs_free_resources(); + syslog(LOG_ERR, "spiffs can't find a valid partition"); + return -1; + } else { + cfg.phys_addr = partition->address; + cfg.phys_size = partition->size; + } + + cfg.phys_erase_block = w25qxx_FLASH_SECTOR_SIZE; //{} CONFIG_LUA_RTOS_SPIFFS_ERASE_SIZE; + cfg.log_page_size = w25qxx_FLASH_PAGE_SIZE; //{} CONFIG_LUA_RTOS_SPIFFS_LOG_PAGE_SIZE; + cfg.log_block_size = w25qxx_FLASH_SECTOR_SIZE; //{} CONFIG_LUA_RTOS_SPIFFS_LOG_BLOCK_SIZE; + + if (partition) { + syslog(LOG_INFO, + "spiffs start address at 0x%x, size %d Kb", + cfg.phys_addr, cfg.phys_size / 1024); + } else { + syslog(LOG_INFO, "spiffs start address at 0x%x, size %d Kb", + cfg.phys_addr, cfg.phys_size / 1024); + } + + cfg.hal_read_f = (spiffs_read) low_spiffs_read; + cfg.hal_write_f = (spiffs_write) low_spiffs_write; + cfg.hal_erase_f = (spiffs_erase) low_spiffs_erase; + + my_spiffs_work_buf = malloc(cfg.log_page_size * 2); + if (!my_spiffs_work_buf) { + vfs_spiffs_free_resources(); + syslog(LOG_ERR, "spiffs can't allocate memory for file system"); + return -1; + } + + int fds_len = sizeof(spiffs_fd) * 5; + my_spiffs_fds = malloc(fds_len); + if (!my_spiffs_fds) { + vfs_spiffs_free_resources(); + syslog(LOG_ERR, "spiffs can't allocate memory for file system"); + return -1; + } + + int cache_len = cfg.log_page_size * 5; + my_spiffs_cache = malloc(cache_len); + if (!my_spiffs_cache) { + vfs_spiffs_free_resources(); + syslog(LOG_ERR, "spiffs can't allocate memory for file system"); + return -1; + } + + // Init mutex + mtx_init(&vfs_mtx, NULL, NULL, MTX_RECURSE); + + mtx_init(&ll_mtx, NULL, NULL, 0); + fs.user_data = &ll_mtx; + + while (retries < 2) { + res = SPIFFS_mount(&fs, &cfg, my_spiffs_work_buf, my_spiffs_fds, + fds_len, my_spiffs_cache, cache_len, NULL); + + if (res < 0) { + if (fs.err_code == SPIFFS_ERR_NOT_A_FS) { + syslog(LOG_ERR, "spiffs no file system detected, formating..."); + SPIFFS_unmount(&fs); + res = SPIFFS_format(&fs); + if (res < 0) { + vfs_spiffs_free_resources(); + syslog(LOG_ERR, "spiffs format error"); + return -1; + } + } else { + vfs_spiffs_free_resources(); + syslog(LOG_ERR, "spiff can't mount file system (%s)", + strerror(spiffs_result(fs.err_code))); + return -1; + } + } else { + break; + } + + retries++; + } + + if (retries > 0) { + syslog(LOG_INFO, "spiffs creating root folder"); + + // Create the root folder + spiffs_file fd = SPIFFS_open(&fs, "/.", SPIFFS_CREAT | SPIFFS_RDWR, 0); + if (fd < 0) { + vfs_spiffs_umount(target); + syslog(LOG_ERR, "spiffs can't create root folder (%s)", + strerror(spiffs_result(fs.err_code))); + return -1; + } + + if (SPIFFS_close(&fs, fd) < 0) { + vfs_spiffs_umount(target); + syslog(LOG_ERR, "spiffs can't create root folder (%s)", + strerror(spiffs_result(fs.err_code))); + return -1; + } + } + + lstinit(&files, 0, LIST_DEFAULT); + + ESP_ERROR_CHECK(esp_vfs_register("/spiffs", &vfs, NULL)); + + syslog(LOG_INFO, "spiffs mounted on %s", target); + + return 0; +} + +int vfs_spiffs_umount(const char *target) { + esp_vfs_unregister("/spiffs"); + SPIFFS_unmount(&fs); + + vfs_spiffs_free_resources(); + + syslog(LOG_INFO, "spiffs unmounted"); + + return 0; +} + +int vfs_spiffs_format(const char *target) { + vfs_spiffs_umount(target); + + mtx_init(&ll_mtx, NULL, NULL, 0); + + int res = SPIFFS_format(&fs); + if (res < 0) { + syslog(LOG_ERR, "spiffs format error"); + return -1; + } + + mtx_destroy(&ll_mtx); + + vfs_spiffs_mount(target); + + syslog(LOG_INFO, "spiffs creating root folder"); + + // Create the root folder + spiffs_file fd = SPIFFS_open(&fs, "/.", SPIFFS_CREAT | SPIFFS_RDWR, 0); + if (fd < 0) { + vfs_spiffs_umount(target); + syslog(LOG_ERR, "spiffs can't create root folder (%s)", + strerror(spiffs_result(fs.err_code))); + return -1; + } + + if (SPIFFS_close(&fs, fd) < 0) { + vfs_spiffs_umount(target); + syslog(LOG_ERR, "spiffs can't create root folder (%s)", + strerror(spiffs_result(fs.err_code))); + return -1; + } + + return 0; +} + +int vfs_spiffs_fsstat(const char *target, u32_t *total, u32_t *used) { + + if (SPIFFS_info(&fs, total, used) != SPIFFS_OK) { + syslog(LOG_ERR, "spiffs get fs info of '%s' (%s)", target, + strerror(spiffs_result(fs.err_code))); + return -1; + } + + return 0; +} + +//#endif diff --git a/libiot/vfs/vfs.h b/libiot/vfs/vfs.h new file mode 100644 index 0000000..eb743fd --- /dev/null +++ b/libiot/vfs/vfs.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L. + * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the <organization> nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * * The WHITECAT logotype cannot be changed, you can remove it, but you + * cannot change it in any way. The WHITECAT logotype is: + * + * /\ /\ + * / \_____/ \ + * /_____________\ + * W H I T E C A T + * + * * Redistributions in binary form must retain all copyright notices printed + * to any local or remote output device. This include any reference to + * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may + * appear in the future. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Lua RTOS common vfs functions + * + */ + +#include "esp_vfs.h" + +#include <stdarg.h> +#include <unistd.h> + +//#include <sys/mount.h> + +#define LUA_RTOS_SPIFFS_PART 0x40 +#define LUA_RTOS_LFS_PART 0x41 + +typedef struct { + void *fs_file; + char *path; + uint8_t is_dir; +} vfs_file_t; + +typedef struct { + DIR dir; + long offset; + void *fs_dir; + void *fs_info; + char *path; + struct dirent ent; + const struct mount_pt *mount; +} vfs_dir_t; + +typedef struct { + int flags; // FD flags +} vfs_fd_local_storage_t; + +// Return if there are available bytes for read from the file descriptor. +// This function is blocking. +typedef int(*vfs_has_bytes)(int, int); + +// Return the bytes available for write to the file descriptor. +// This function is non blocking. +typedef int(*vfs_free_bytes)(int); + +// Get one byte from the file descriptor. +// This functions is blocking. +typedef int(*vfs_get_byte)(int,char *); + +// Put one byte to the file descriptor. +// This function is blocking. +typedef void(*vfs_put_byte)(int,char *); + +/* +int vfs_fat_mount(const char *target); +int vfs_fat_umount(const char *target); +int vfs_fat_format(const char *target); +int vfs_fat_fsstat(const char *target, u32_t *total, u32_t *used); +*/ + +int vfs_spiffs_mount(const char *target); +int vfs_spiffs_umount(const char *target); +int vfs_spiffs_format(const char *target); +int vfs_spiffs_fsstat(const char *target, u32_t *total, u32_t *used); + +/* +int vfs_lfs_mount(const char *target); +int vfs_lfs_umount(const char *target); +int vfs_lfs_format(const char *target); +int vfs_lfs_fsstat(const char *target, u32_t *total, u32_t *used); + +int vfs_ramfs_mount(const char *target); +int vfs_ramfs_umount(const char *target); +int vfs_ramfs_format(const char *target); +int vfs_ramfs_fsstat(const char *target, u32_t *total, u32_t *used); + +int vfs_romfs_mount(const char *target); +int vfs_romfs_umount(const char *target); +int vfs_romfs_fsstat(const char *target, u32_t *total, u32_t *used); + +int vfs_generic_fcntl(vfs_fd_local_storage_t *local_storage, int fd, int cmd, va_list args); +ssize_t vfs_generic_read(vfs_fd_local_storage_t *local_storage, vfs_has_bytes has_bytes, vfs_get_byte get, int fd, void * dst, size_t size); +ssize_t vfs_generic_write(vfs_fd_local_storage_t *local_storage, vfs_put_byte put, int fd, const void *data, size_t size); +ssize_t vfs_generic_writev(vfs_fd_local_storage_t *local_storage, vfs_put_byte put, int fd, const struct iovec *iov, int iovcnt); +int vfs_generic_select(vfs_fd_local_storage_t *local_storage, vfs_has_bytes has_bytes, vfs_free_bytes free, int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout); + +vfs_dir_t *vfs_allocate_dir(const char *vfs, const char *name); +void vfs_free_dir(vfs_dir_t *dir); +vfs_fd_local_storage_t *vfs_create_fd_local_storage(int num); +*/ |
