From 924860a78f389a8ff4c912d3eb8f6e5181d1059c Mon Sep 17 00:00:00 2001
From: sheinz <shein@bk.ru>
Date: Fri, 15 Jul 2016 15:22:03 +0300
Subject: [PATCH] SPIFFS: Update example, README.md

Separate method to initialize SPIFFS memory buffers.
REDME.md for spiffs component.
Simplify spiffs example.
---
 examples/posix_fs/posix_fs_example.c |   3 +-
 examples/spiffs/Makefile             |   2 +-
 examples/spiffs/spiffs_example.c     | 194 ++++++++-------------------
 extras/spiffs/README.md              | 153 +++++++++++++++++++++
 extras/spiffs/esp_spiffs.c           |  71 ++++++----
 extras/spiffs/esp_spiffs.h           |  25 +++-
 6 files changed, 277 insertions(+), 171 deletions(-)
 create mode 100644 extras/spiffs/README.md

diff --git a/examples/posix_fs/posix_fs_example.c b/examples/posix_fs/posix_fs_example.c
index 4525be3..2f5ad14 100644
--- a/examples/posix_fs/posix_fs_example.c
+++ b/examples/posix_fs/posix_fs_example.c
@@ -18,8 +18,9 @@ static fs_time_t get_current_time()
 
 void test_task(void *pvParameters)
 {
+    esp_spiffs_init();
     esp_spiffs_mount();
-    esp_spiffs_unmount();  // FS must be unmounted before formating
+    SPIFFS_unmount(&fs);  // FS must be unmounted before formating
     if (SPIFFS_format(&fs) == SPIFFS_OK) {
         printf("Format complete\n");
     } else {
diff --git a/examples/spiffs/Makefile b/examples/spiffs/Makefile
index b0be23f..7327707 100644
--- a/examples/spiffs/Makefile
+++ b/examples/spiffs/Makefile
@@ -4,7 +4,7 @@ FLASH_SIZE = 32
 
 # spiffs configuration
 SPIFFS_BASE_ADDR = 0x200000
-SPIFFS_SIZE = 0x100000
+SPIFFS_SIZE = 0x010000
 
 include ../../common.mk
 
diff --git a/examples/spiffs/spiffs_example.c b/examples/spiffs/spiffs_example.c
index 226ee51..98decaf 100644
--- a/examples/spiffs/spiffs_example.c
+++ b/examples/spiffs/spiffs_example.c
@@ -4,171 +4,95 @@
 #include "task.h"
 #include "esp8266.h"
 
+#include "fcntl.h"
+#include "unistd.h"
+
 #include "spiffs.h"
 #include "esp_spiffs.h"
 
 
-#define TEST_FILE_NAME_LEN  16
-#define TEST_FILES          32
-#define TEST_FILE_MAX_SIZE  8192
-
-typedef struct {
-    char name[TEST_FILE_NAME_LEN];
-    uint16_t size;
-    uint8_t first_data_byte;
-} TestFile;
-
-static TestFile test_files[TEST_FILES];
-
-inline static void fill_test_data(uint8_t *src, uint16_t size, uint8_t first_byte)
+static void example_read_file_posix()
 {
-    while (size--) {
-        *src++ = first_byte++;
-    }
-}
+    const int buf_size = 0xFF;
+    uint8_t buf[buf_size];
 
-static bool write_test_files()
-{
-    uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE);
-    bool result = true;
-
-    for (uint8_t i = 0; i < TEST_FILES; i++) {
-        sprintf(test_files[i].name, "file_%d.dat", i);
-        spiffs_file f = SPIFFS_open(&fs, test_files[i].name,
-                SPIFFS_CREAT|SPIFFS_RDWR|SPIFFS_TRUNC, 0);
-        if (f < 0) {
-            printf("Open file operation failed\n");
-            result = false;
-            break;
-        }
-        test_files[i].size = rand() % TEST_FILE_MAX_SIZE;
-        test_files[i].first_data_byte = rand() % 256;
-        fill_test_data(buf, test_files[i].size, test_files[i].first_data_byte);
-
-        printf("Writing file %s size=%d\n", test_files[i].name,
-                test_files[i].size);
-        int32_t written = SPIFFS_write(&fs, f, buf, test_files[i].size);
-        if (written != test_files[i].size) {
-            printf("Write file operation failed, written=%d\n", written);
-            result = false;
-            break;
-        }
-        SPIFFS_close(&fs, f);
-    }
-    free(buf);
-    return result;
-}
-
-inline static bool verify_test_data(uint8_t *data, uint16_t size,
-        uint8_t first_byte)
-{
-    while (size--) {
-        if (*data++ != first_byte++) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static bool verify_test_files()
-{
-    uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE);
-    bool result = true;
-
-    for (uint8_t i = 0; i < TEST_FILES; i++) {
-        printf("Verifying file %s\n", test_files[i].name);
-        spiffs_file f = SPIFFS_open(&fs, test_files[i].name, SPIFFS_RDONLY, 0);
-        if (f < 0) {
-            printf("Open file operation failed\n");
-            result = false;
-            break;
-        }
-
-        int32_t n = SPIFFS_read(&fs, f, buf, test_files[i].size);
-        if (n != test_files[i].size) {
-            printf("Read file operation failed\n");
-            result = false;
-            break;
-        }
-
-        if (!verify_test_data(buf, test_files[i].size,
-                    test_files[i].first_data_byte)) {
-            printf("Data verification failed\n");
-            result = false;
-            break;
-        }
-
-        SPIFFS_close(&fs, f);
+    int fd = open("test.txt", O_RDONLY);
+    if (fd < 0) {
+        printf("Error opening file\n");
+        return;
     }
 
-    free(buf);
-    return result;
+    int read_bytes = read(fd, buf, buf_size);
+    printf("Read %d bytes\n", read_bytes);
+
+    buf[read_bytes] = '\0';    // zero terminate string
+    printf("Data: %s\n", buf);
+
+    close(fd);
 }
 
-static bool cleanup_test_files()
+static void example_read_file_spiffs()
 {
-    bool result = true;
+    const int buf_size = 0xFF;
+    uint8_t buf[buf_size];
 
-    for (uint8_t i = 0; i < TEST_FILES; i++) {
-        printf("Removing file %s\n", test_files[i].name);
-        if (SPIFFS_remove(&fs, test_files[i].name) != SPIFFS_OK) {
-            printf("Remove file operation failed\n");
-            result = false;
-            break;
-        }
+    spiffs_file fd = SPIFFS_open(&fs, "other.txt", SPIFFS_RDONLY, 0);
+    if (fd < 0) {
+        printf("Error opening file\n");
+        return;
     }
-    return result;
+
+    int read_bytes = SPIFFS_read(&fs, fd, buf, buf_size);
+    printf("Read %d bytes\n", read_bytes);
+
+    buf[read_bytes] = '\0';    // zero terminate string
+    printf("Data: %s\n", buf);
+
+    SPIFFS_close(&fs, fd);
 }
 
-inline static void print_info()
+static void example_write_file()
+{
+    uint8_t buf[] = "Example data, written by ESP8266";
+
+    int fd = open("other.txt", O_WRONLY|O_CREAT, 0);
+    if (fd < 0) {
+        printf("Error opening file\n");
+        return;
+    }
+
+    int written = write(fd, buf, sizeof(buf));
+    printf("Written %d bytes\n", written);
+
+    close(fd);
+}
+
+static void example_fs_info()
 {
     uint32_t total, used;
-
     SPIFFS_info(&fs, &total, &used);
-
-    printf("FS total=%d bytes, used=%d bytes\n", total, used);
-    printf("FS %d %% used\n", 100 * used/total);
-
-    // File system structure visualisation
-    // SPIFFS_vis(&fs);
+    printf("Total: %d bytes, used: %d bytes", total, used);
 }
 
 void test_task(void *pvParameters)
 {
-    bool result = true;
-
-    esp_spiffs_mount();
-    esp_spiffs_unmount();  // FS must be unmounted before formating
-    if (SPIFFS_format(&fs) == SPIFFS_OK) {
-        printf("Format complete\n");
-    } else {
-        printf("Format failed\n");
+    esp_spiffs_init();
+    if (esp_spiffs_mount() != SPIFFS_OK) {
+        printf("Error mount SPIFFS\n");
     }
-    esp_spiffs_mount();
 
     while (1) {
-        vTaskDelay(5000 / portTICK_RATE_MS);
+        vTaskDelay(2000 / portTICK_RATE_MS);
 
-        result = write_test_files();
+        example_write_file();
 
-        if (result) {
-            result = verify_test_files();
-        }
+        example_read_file_posix();
 
-        print_info();
+        example_read_file_spiffs();
 
-        if (result) {
-            result = cleanup_test_files();
-        }
+        example_fs_info();
 
-        if (result) {
-            printf("Test passed!\n");
-        } else {
-            printf("Test failed!\n");
-            while (1) {
-                vTaskDelay(1);
-            }
-        }
+        printf("\n\n");
     }
 }
 
diff --git a/extras/spiffs/README.md b/extras/spiffs/README.md
new file mode 100644
index 0000000..76d2081
--- /dev/null
+++ b/extras/spiffs/README.md
@@ -0,0 +1,153 @@
+# SPIFFS ESP8266 File system
+
+This component adds file system support for ESP8266. File system of choice
+for ESP8266 is [SPIFFS](https://github.com/pellepl/spiffs). 
+It was specifically designed to use with SPI NOR flash on embedded systems.
+The main advantage of SPIFFS is wear leveling, which prolongs life time 
+of a flash memory.
+
+## Features
+
+ * SPIFFS - embedded file system for NOR flash memory.
+ * POSIX file operations.
+ * Static files upload to ESP8266 file system within build process.
+ * SPIFFS singleton configuration. Only one instance of FS on a device.
+
+## Usage
+
+In order to use file system in a project the following steps should be made:
+ * Add SPIFFS component in a project Makefile `EXTRA_COMPONENTS = extras/spiffs`
+ * Specify your flash size in the Makefile `FLASH_SIZE = 32`
+ * Specify the start address of file system region on the flash memory
+`SPIFFS_BASE_ADDR = 0x200000`
+ * If you want to upload files to a file system during flash process specify
+the directory with files `$(eval $(call make_spiffs_image,files))`
+
+In the end the Makefile should look like:
+
+```
+PROGRAM=spiffs_example
+EXTRA_COMPONENTS = extras/spiffs
+FLASH_SIZE = 32
+
+SPIFFS_BASE_ADDR = 0x200000
+SPIFFS_SIZE = 0x100000
+
+include ../../common.mk
+
+$(eval $(call make_spiffs_image,files))
+```
+
+Note: Macro call to prepare SPIFFS image for flashing should go after
+`include common.mk`
+
+### Files upload
+
+To upload files to a file system during flash process the following macro is
+used:
+
+```
+$(eval $(call make_spiffs_image,files))
+```
+
+It enables the build of a helper utility **mkspiffs**. This utility creates
+an SPIFFS image with files in the specified directory.
+
+The SPIFFS image is created during build stage, after `make` is run.
+The image is flashed into the device along with firmware during flash stage,
+after `make flash` is run.
+
+**mkspiffs** utility uses the same SPIFFS source code and the same
+configuration as ESP8266. So the created image should always be compatible
+with SPIFFS on a device.
+
+The build process will catch any changes in files directory and rebuild the
+image each time `make` is run.
+The build process will handle SPIFFS_SIZE change and rebuild **mkspiffs**
+utility and the image.
+
+## Example
+
+### Mount
+
+```
+esp_spiffs_init();   // allocate memory buffers
+if (esp_spiffs_mount() != SPIFFS_OK) {
+    printf("Error mounting SPIFFS\n");
+}
+```
+
+### Format
+
+Formatting SPIFFS is a little bit awkward. Before formatting SPIFFS must be
+mounted and unmounted.
+```
+esp_spiffs_init();
+if (esp_spiffs_mount() != SPIFFS_OK) {
+    printf("Error mount SPIFFS\n");
+}
+SPIFFS_unmount(&fs);  // FS must be unmounted before formating
+if (SPIFFS_format(&fs) == SPIFFS_OK) {
+    printf("Format complete\n");
+} else {
+    printf("Format failed\n");
+}
+esp_spiffs_mount();
+```
+
+### POSIX read
+
+Nothing special here.
+
+```
+const int buf_size = 0xFF;
+uint8_t buf[buf_size];
+
+int fd = open("test.txt", O_RDONLY);
+if (fd < 0) {
+    printf("Error opening file\n");
+}
+
+read(fd, buf, buf_size);
+printf("Data: %s\n", buf);
+
+close(fd);
+```
+
+### SPIFFS read
+
+SPIFFS interface is intended to be as close to POSIX as possible.
+
+```
+const int buf_size = 0xFF;
+uint8_t buf[buf_size];
+
+spiffs_file fd = SPIFFS_open(&fs, "other.txt", SPIFFS_RDONLY, 0);
+if (fd < 0) {
+    printf("Error opening file\n");
+}
+
+SPIFFS_read(&fs, fd, buf, buf_size);
+printf("Data: %s\n", buf);
+
+SPIFFS_close(&fs, fd);
+```
+
+### POSIX write
+
+```
+uint8_t buf[] = "Example data, written by ESP8266";
+
+int fd = open("other.txt", O_WRONLY|O_CREAT, 0);
+if (fd < 0) {
+    printf("Error opening file\n");
+}
+
+write(fd, buf, sizeof(buf));
+
+close(fd);
+```
+
+## Resources
+
+[SPIFFS](https://github.com/pellepl/spiffs)
diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c
index 72419a1..11a84d5 100644
--- a/extras/spiffs/esp_spiffs.c
+++ b/extras/spiffs/esp_spiffs.c
@@ -14,9 +14,21 @@
 
 spiffs fs;
 
-static void *work_buf = 0;
-static void *fds_buf = 0;
-static void *cache_buf = 0;
+typedef struct {
+    void *buf;
+    uint32_t size;
+} fs_buf_t;
+
+static fs_buf_t work_buf = {0};
+static fs_buf_t fds_buf = {0};
+static fs_buf_t cache_buf = {0};
+
+/**
+ * Number of file descriptors opened at the same time
+ */
+#define ESP_SPIFFS_FD_NUMBER       5
+
+#define ESP_SPIFFS_CACHE_PAGES     5
 
 /*
  * Flash addresses and size alignment is a rip-off of Arduino implementation.
@@ -147,6 +159,29 @@ static s32_t esp_spiffs_erase(u32_t addr, u32_t size)
     return SPIFFS_OK;
 }
 
+void esp_spiffs_init()
+{
+    work_buf.size = 2 * SPIFFS_CFG_LOG_PAGE_SZ();
+    fds_buf.size = SPIFFS_buffer_bytes_for_filedescs(&fs, ESP_SPIFFS_FD_NUMBER);
+    cache_buf.size= SPIFFS_buffer_bytes_for_cache(&fs, ESP_SPIFFS_CACHE_PAGES);
+
+    work_buf.buf = malloc(work_buf.size);
+    fds_buf.buf = malloc(fds_buf.size);
+    cache_buf.buf = malloc(cache_buf.size);
+}
+
+void esp_spiffs_deinit()
+{
+    free(work_buf.buf);
+    work_buf.buf = 0;
+
+    free(fds_buf.buf);
+    fds_buf.buf = 0;
+
+    free(cache_buf.buf);
+    cache_buf.buf = 0;
+}
+
 int32_t esp_spiffs_mount()
 {
     spiffs_config config = {0};
@@ -155,18 +190,13 @@ int32_t esp_spiffs_mount()
     config.hal_write_f = esp_spiffs_write;
     config.hal_erase_f = esp_spiffs_erase;
 
-    size_t workBufSize = 2 * SPIFFS_CFG_LOG_PAGE_SZ();
-    size_t fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&fs, 5);
-    size_t cacheBufSize = SPIFFS_buffer_bytes_for_cache(&fs, 5);
+    printf("SPIFFS size: %d\n", SPIFFS_SIZE);
+    printf("SPIFFS memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n",
+            work_buf.size, fds_buf.size, cache_buf.size);
 
-    work_buf = malloc(workBufSize);
-    fds_buf = malloc(fdsBufSize);
-    cache_buf = malloc(cacheBufSize);
-    printf("spiffs memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n",
-            workBufSize, fdsBufSize, cacheBufSize);
-
-    int32_t err = SPIFFS_mount(&fs, &config, work_buf, fds_buf, fdsBufSize,
-            cache_buf, cacheBufSize, 0);
+    int32_t err = SPIFFS_mount(&fs, &config, (uint8_t*)work_buf.buf, 
+            (uint8_t*)fds_buf.buf, fds_buf.size, 
+            cache_buf.buf, cache_buf.size, 0);
 
     if (err != SPIFFS_OK) {
         printf("Error spiffs mount: %d\n", err);
@@ -175,19 +205,6 @@ int32_t esp_spiffs_mount()
     return err;
 }
 
-void esp_spiffs_unmount()
-{
-    SPIFFS_unmount(&fs);
-
-    free(work_buf);
-    free(fds_buf);
-    free(cache_buf);
-
-    work_buf = 0;
-    fds_buf = 0;
-    cache_buf = 0;
-}
-
 #define FD_OFFSET 3
 
 // This implementation replaces implementation in core/newlib_syscals.c
diff --git a/extras/spiffs/esp_spiffs.h b/extras/spiffs/esp_spiffs.h
index 3022d51..f074017 100644
--- a/extras/spiffs/esp_spiffs.h
+++ b/extras/spiffs/esp_spiffs.h
@@ -13,16 +13,27 @@
 extern spiffs fs;
 
 /**
- * Provide SPIFFS with all necessary configuration, allocate memory buffers
- * and mount SPIFFS.
+ * Prepare for SPIFFS mount.
+ * 
+ * The function allocates all the necessary buffers.
+ */
+void esp_spiffs_init();
+
+/**
+ * Free all memory buffers that were used by SPIFFS.
+ *
+ * The function should be called after SPIFFS unmount if the file system is not
+ * going to need any more.
+ */
+void esp_spiffs_deinit();
+
+/**
+ * Mount SPIFFS.
+ *
+ * esp_spiffs_init must be called first.
  *
  * Return SPIFFS return code.
  */
 int32_t esp_spiffs_mount();
 
-/**
- * Unmount SPIFFS and free all allocated buffers.
- */
-void esp_spiffs_unmount();
-
 #endif  // __ESP_SPIFFS_H__