]> granicus.if.org Git - esp-idf/commitdiff
Merge branch 'feature/fail_build_on_sphinx_warning' into 'master'
authorIvan Grokhotkov <ivan@espressif.com>
Tue, 12 Sep 2017 05:49:11 +0000 (13:49 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Tue, 12 Sep 2017 05:49:11 +0000 (13:49 +0800)
Fail CI build on Sphinx warnings

See merge request !1228

36 files changed:
.gitmodules
components/bt/bluedroid/hci/hci_layer.c
components/bt/bluedroid/hci/include/hci_layer.h
components/bt/bluedroid/stack/gatt/gatt_utils.c
components/driver/include/driver/uart.h
components/driver/uart.c
components/fatfs/src/vfs_fat_spiflash.c
components/freertos/include/freertos/FreeRTOSConfig.h
components/freertos/tasks.c
components/freertos/test/test_tasks_snapshot.c [new file with mode: 0644]
components/lwip/apps/dhcpserver.c
components/lwip/include/lwip/apps/dhcpserver.h
components/lwip/include/lwip/apps/dhcpserver_options.h [new file with mode: 0644]
components/nvs_flash/src/nvs_api.cpp
components/nvs_flash/test_nvs_host/test_nvs.cpp
components/spiffs/Kconfig [new file with mode: 0644]
components/spiffs/component.mk [new file with mode: 0644]
components/spiffs/esp_spiffs.c [new file with mode: 0644]
components/spiffs/include/esp_spiffs.h [new file with mode: 0644]
components/spiffs/include/spiffs_config.h [new file with mode: 0755]
components/spiffs/spiffs [new submodule]
components/spiffs/test/component.mk [new file with mode: 0644]
components/spiffs/test/test_spiffs.c [new file with mode: 0644]
components/tcpip_adapter/tcpip_adapter_lwip.c
docs/Doxyfile
docs/api-reference/storage/index.rst
docs/api-reference/storage/spiffs.rst [new file with mode: 0644]
examples/bluetooth/gatt_server/main/gatts_demo.c
examples/storage/spiffs/Makefile [new file with mode: 0644]
examples/storage/spiffs/README.md [new file with mode: 0644]
examples/storage/spiffs/main/component.mk [new file with mode: 0644]
examples/storage/spiffs/main/spiffs_example_main.c [new file with mode: 0644]
examples/storage/spiffs/partitions_example.csv [new file with mode: 0644]
examples/storage/spiffs/sdkconfig.defaults [new file with mode: 0644]
tools/ci/mirror-list.txt
tools/unit-test-app/partition_table_unit_test_app.csv

index c54435224f6bb54fce57e90834bea1ebbdac0337..e118821b73c51d2c51d68a9abd720c2ccea37389 100644 (file)
@@ -29,3 +29,7 @@
 [submodule "components/libsodium/libsodium"]
        path = components/libsodium/libsodium
        url = https://github.com/jedisct1/libsodium.git
+
+[submodule "components/spiffs/spiffs"]
+       path = components/spiffs/spiffs
+       url = https://github.com/pellepl/spiffs.git
index 5b5205dd9a5d0faab461005ccc04c998482f4972..1c5117c7f25046582b9dab1c0d33a1987e38f0ae 100644 (file)
@@ -32,6 +32,7 @@
 #include "alarm.h"
 #include "thread.h"
 #include "mutex.h"
+#include "fixed_queue.h"
 
 typedef struct {
     uint16_t opcode;
index 76f93638ad375e9490c61c9ae1eef83432c9dcc9..5e9b8c695b50c2da918ff28f691d3e5922236a63 100644 (file)
@@ -21,7 +21,6 @@
 
 #include "bt_types.h"
 #include "allocator.h"
-#include "fixed_queue.h"
 #include "osi.h"
 #include "future.h"
 ///// LEGACY DEFINITIONS /////
index 30b618001b214c66411b0964d234e6fe2fceb246..0266a212d5ad85aa4bb9e0eed06d79f25e2ca5c3 100644 (file)
@@ -2216,9 +2216,10 @@ void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport)
         gatt_free_pending_ind(p_tcb);
         gatt_free_pending_enc_queue(p_tcb);
         gatt_free_pending_prepare_write_queue(p_tcb);
+#if (GATTS_INCLUDED == TRUE)
         fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, osi_free_func);
         p_tcb->sr_cmd.multi_rsp_q = NULL;
-
+#endif  ///GATTS_INCLUDED == TRUE
         for (i = 0; i < GATT_MAX_APPS; i ++) {
             p_reg = &gatt_cb.cl_rcb[i];
             if (p_reg->in_use && p_reg->app_cb.p_conn_cb) {
index 229685b7c3c3399ac9a6842b2cae5f370647c088..de1b619f93059b9164fd4ae078bf786e2b4324f8 100644 (file)
@@ -269,6 +269,20 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask);
  */
 esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh);
 
+/**
+ * @brief Set software flow control.
+ *
+ * @param uart_num   UART_NUM_0, UART_NUM_1 or UART_NUM_2
+ * @param enable     switch on or off
+ * @param rx_thresh_xon  low water mark
+ * @param rx_thresh_xoff high water mark
+ *
+ * @return
+ *     - ESP_OK   Success
+ *     - ESP_FAIL Parameter error
+ */
+ esp_err_t uart_set_sw_flow_ctrl(uart_port_t uart_num, bool enable, uint8_t rx_thresh_xon,  uint8_t rx_thresh_xoff);
+
 /**
  * @brief Get hardware flow control mode
  *
index 5371a447c8feb40371b9f2276b0ad05b250a5d64..fffae17344cd24f88bca85de4551ea482bf3fd31 100644 (file)
@@ -29,6 +29,9 @@
 #include "driver/uart.h"
 #include "driver/gpio.h"
 
+#define XOFF (char)0x13
+#define XON (char)0x11
+
 static const char* UART_TAG = "uart";
 #define UART_CHECK(a, str, ret_val) \
     if (!(a)) { \
@@ -199,6 +202,22 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask)
     return ESP_OK;
 }
 
+esp_err_t uart_set_sw_flow_ctrl(uart_port_t uart_num, bool enable,  uint8_t rx_thresh_xon,  uint8_t rx_thresh_xoff)
+{
+    UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL);
+    UART_CHECK((rx_thresh_xon < UART_FIFO_LEN), "rx flow xon thresh error", ESP_FAIL);
+    UART_CHECK((rx_thresh_xoff < UART_FIFO_LEN), "rx flow xon thresh error", ESP_FAIL);
+    UART_ENTER_CRITICAL(&uart_spinlock[uart_num]);
+    UART[uart_num]->flow_conf.sw_flow_con_en = enable? 1:0;
+    UART[uart_num]->flow_conf.xonoff_del = enable?1:0;
+    UART[uart_num]->swfc_conf.xon_threshold =  rx_thresh_xon;
+    UART[uart_num]->swfc_conf.xoff_threshold =  rx_thresh_xoff;
+    UART[uart_num]->swfc_conf.xon_char = XON;
+    UART[uart_num]->swfc_conf.xoff_char = XOFF;
+    UART_EXIT_CRITICAL(&uart_spinlock[uart_num]);
+    return ESP_OK;
+}
+
 //only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set.
 esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh)
 {
index b9f80354a14fcad755de8f93240ccba525005914..2e90468d2b0dcac62c81fe73ed0f0be5abcedd6c 100644 (file)
@@ -32,7 +32,10 @@ esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path,
     const size_t workbuf_size = 4096;
     void *workbuf = NULL;
 
-    esp_partition_t *data_partition = (esp_partition_t *)esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label);
+    esp_partition_subtype_t subtype = partition_label ?
+            ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_FAT;
+    const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
+                                                subtype, partition_label);
     if (data_partition == NULL) {
         ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
         return ESP_ERR_NOT_FOUND;
index 81e8a7734719987e673e41a0f93d2f487c54d230..3ccd3fda695f3032a24f44185caad6ae005545f4 100644 (file)
@@ -269,9 +269,7 @@ extern void vPortCleanUpTCB ( void *pxTCB );
 #define configXT_BOARD                      1   /* Board mode */
 #define configXT_SIMULATOR                                     0
 
-#if CONFIG_ESP32_ENABLE_COREDUMP
 #define configENABLE_TASK_SNAPSHOT                     1
-#endif
 
 #if CONFIG_SYSVIEW_ENABLE
 #ifndef __ASSEMBLER__
index 0433deb2af4da5707c0a0d0c5e602ebc09389d2c..9126f862e0e2ba42091812a829f9029a1586f899 100644 (file)
@@ -4991,22 +4991,24 @@ TickType_t uxReturn;
 #endif /* configUSE_TASK_NOTIFICATIONS */
 
 #if ( configENABLE_TASK_SNAPSHOT == 1 )
-
-    static void prvTaskGetSnapshot( TaskSnapshot_t *pxTaskSnapshotArray, UBaseType_t *uxTask, TCB_t *pxTCB )
-    {
-        pxTaskSnapshotArray[ *uxTask ].pxTCB = pxTCB;
-        pxTaskSnapshotArray[ *uxTask ].pxTopOfStack = (StackType_t *)pxTCB->pxTopOfStack;
-        #if( portSTACK_GROWTH < 0 )
-        {
-            pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxTCB->pxEndOfStack;
-        }
-        #else
-        {
-            pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxTCB->pxStack;
-        }
-        #endif
-        (*uxTask)++;
-    }
+       static void prvTaskGetSnapshot( TaskSnapshot_t *pxTaskSnapshotArray, UBaseType_t *uxTask, TCB_t *pxTCB )
+       {
+               if (pxTCB == NULL) {
+                       return;
+               }
+               pxTaskSnapshotArray[ *uxTask ].pxTCB = pxTCB;
+               pxTaskSnapshotArray[ *uxTask ].pxTopOfStack = (StackType_t *)pxTCB->pxTopOfStack;
+               #if( portSTACK_GROWTH < 0 )
+               {
+                       pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxTCB->pxEndOfStack;
+               }
+               #else
+               {
+                       pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxTCB->pxStack;
+               }
+               #endif
+               (*uxTask)++;
+       }
 
        static void prvTaskGetSnapshotsFromList( TaskSnapshot_t *pxTaskSnapshotArray, UBaseType_t *uxTask, const UBaseType_t uxArraySize, List_t *pxList )
        {
@@ -5017,12 +5019,11 @@ TickType_t uxReturn;
                        listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList );
                        do
                        {
-                               listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList );
-
                                if( *uxTask >= uxArraySize )
                                        break;
 
-                prvTaskGetSnapshot( pxTaskSnapshotArray, uxTask, pxNextTCB );
+                               listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList );
+                               prvTaskGetSnapshot( pxTaskSnapshotArray, uxTask, pxNextTCB );
                        } while( pxNextTCB != pxFirstTCB );
                }
                else
@@ -5035,8 +5036,6 @@ TickType_t uxReturn;
        {
                UBaseType_t uxTask = 0, i = 0;
 
-PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB[ portNUM_PROCESSORS ] = { NULL };
-
 
                *pxTcbSz = sizeof(TCB_t);
                /* Fill in an TaskStatus_t structure with information on each
@@ -5052,12 +5051,11 @@ PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB[ portNUM_PROCESSORS ] = { NULL };
                task in the Blocked state. */
                prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, ( List_t * ) pxDelayedTaskList );
                prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, ( List_t * ) pxOverflowDelayedTaskList );
-        for (i = 0; i < portNUM_PROCESSORS; i++) {
-            if( uxTask >= uxArraySize )
-                    break;
-            prvTaskGetSnapshot( pxTaskSnapshotArray, &uxTask, pxCurrentTCB[ i ] );
-            prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &( xPendingReadyList[ i ] ) );
-        }
+               for (i = 0; i < portNUM_PROCESSORS; i++) {
+                       if( uxTask >= uxArraySize )
+                               break;
+                       prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &( xPendingReadyList[ i ] ) );
+               }
 
                #if( INCLUDE_vTaskDelete == 1 )
                {
diff --git a/components/freertos/test/test_tasks_snapshot.c b/components/freertos/test/test_tasks_snapshot.c
new file mode 100644 (file)
index 0000000..c9364cc
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ Test FreeRTOS support for core dump.
+*/
+
+#include <stdio.h>
+#include "soc/cpu.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "unity.h"
+
+#define TEST_MAX_TASKS_NUM     32
+
+/* simple test to check that in normal conditions uxTaskGetSnapshotAll does not generate exception */
+TEST_CASE("Tasks snapshot", "[freertos]")
+{
+    TaskSnapshot_t tasks[TEST_MAX_TASKS_NUM];
+    UBaseType_t tcb_sz;
+    int other_core_id = xPortGetCoreID() == 0 ? 1 : 0;
+
+    // uxTaskGetSnapshotAll is supposed to be called when all tasks on both CPUs are 
+    // inactive and can not alter FreeRTOS internal tasks lists, e.g. from panic handler
+    unsigned state = portENTER_CRITICAL_NESTED();
+#if CONFIG_FREERTOS_UNICORE == 0
+    esp_cpu_stall(other_core_id);
+#endif
+    UBaseType_t task_num = uxTaskGetSnapshotAll(tasks, TEST_MAX_TASKS_NUM, &tcb_sz);
+#if CONFIG_FREERTOS_UNICORE == 0
+    esp_cpu_unstall(other_core_id);
+#endif
+    portEXIT_CRITICAL_NESTED(state);
+
+    printf("Dumped %d tasks. TCB size %d\n", task_num, tcb_sz);
+    TEST_ASSERT_NOT_EQUAL(0, task_num);
+    TEST_ASSERT_NOT_EQUAL(0, tcb_sz);
+}
index 44ddd33681285378db0180bb96d950a3a95e8cd0..c5ce7a8adebaa0c66684c8960bcb244fd3c8e526 100644 (file)
@@ -23,6 +23,7 @@
 #include "tcpip_adapter.h"
 
 #include "apps/dhcpserver.h"
+#include "apps/dhcpserver_options.h"
 
 #if ESP_DHCP
 
 #define DHCPS_STATE_IDLE 5
 #define DHCPS_STATE_RELEASE 6
 
+typedef struct _list_node {
+       void *pnode;
+       struct _list_node *pnext;
+} list_node;
+
 ////////////////////////////////////////////////////////////////////////////////////
 
 static const u32_t magic_cookie  = 0x63538263;
@@ -135,7 +141,7 @@ void *dhcps_option_info(u8_t op_id, u32_t opt_len)
  *                pinsert -- the insert node of the list
  * Returns      : none
 *******************************************************************************/
-void node_insert_to_list(list_node **phead, list_node *pinsert)
+static void node_insert_to_list(list_node **phead, list_node *pinsert)
 {
     list_node *plist = NULL;
     struct dhcps_pool *pdhcps_pool = NULL;
index 9e361833d3de0dda5c73d67006f5eae8be2b7967..a0024ab5f05354fb72368d50b13566411d3cfb8f 100644 (file)
@@ -36,123 +36,6 @@ typedef struct dhcps_msg {
         u8_t options[312];
 }dhcps_msg;
 
-/** DHCP OPTIONS CODE **/
-typedef enum
-{
-    /* RFC 1497 Vendor Extensions */
-
-    PAD = 0,
-    END = 255,
-
-    SUBNET_MASK = 1,
-    TIME_OFFSET = 2,
-    ROUTER = 3,
-    TIME_SERVER = 4,
-    NAME_SERVER = 5,
-    DOMAIN_NAME_SERVER = 6,
-    LOG_SERVER = 7,
-    COOKIE_SERVER = 8,
-    LPR_SERVER = 9,
-    IMPRESS_SERVER = 10,
-    RESOURCE_LOCATION_SERVER = 11,
-    HOST_NAME = 12,
-    BOOT_FILE_SIZE = 13,
-    MERIT_DUMP_FILE = 14,
-    DOMAIN_NAME = 15,
-    SWAP_SERVER = 16,
-    ROOT_PATH = 17,
-    EXTENSIONS_PATH = 18,
-
-    /* IP Layer Parameters per Host */
-
-    IP_FORWARDING = 19,
-    NON_LOCAL_SOURCE_ROUTING = 20,
-    POLICY_FILTER = 21,
-    MAXIMUM_DATAGRAM_REASSEMBLY_SIZE = 22,
-    DEFAULT_IP_TIME_TO_LIVE = 23,
-    PATH_MTU_AGING_TIMEOUT = 24,
-    PATH_MTU_PLATEAU_TABLE = 25,
-
-    /* IP Layer Parameters per Interface */
-
-    INTERFACE_MTU = 26,
-    ALL_SUBNETS_ARE_LOCAL = 27,
-    BROADCAST_ADDRESS = 28,
-    PERFORM_MASK_DISCOVERY = 29,
-    MASK_SUPPLIER = 30,
-    PERFORM_ROUTER_DISCOVERY = 31,
-    ROUTER_SOLICITATION_ADDRESS = 32,
-    STATIC_ROUTE = 33,
-
-    /* Link Layer Parameters per Interface */
-
-    TRAILER_ENCAPSULATION = 34,
-    ARP_CACHE_TIMEOUT = 35,
-    ETHERNET_ENCAPSULATION = 36,
-
-    /* TCP Parameters */
-
-    TCP_DEFAULT_TTL = 37,
-    TCP_KEEPALIVE_INTERVAL = 38,
-    TCP_KEEPALIVE_GARBAGE = 39,
-
-    /* Application and Service Parameters */
-
-    NETWORK_INFORMATION_SERVICE_DOMAIN = 40,
-    NETWORK_INFORMATION_SERVERS = 41,
-    NETWORK_TIME_PROTOCOL_SERVERS = 42,
-    VENDOR_SPECIFIC_INFORMATION = 43,
-    NETBIOS_OVER_TCP_IP_NAME_SERVER = 44,
-    NETBIOS_OVER_TCP_IP_DATAGRAM_DISTRIBUTION_SERVER = 45,
-    NETBIOS_OVER_TCP_IP_NODE_TYPE = 46,
-    NETBIOS_OVER_TCP_IP_SCOPE = 47,
-    X_WINDOW_SYSTEM_FONT_SERVER = 48,
-    X_WINDOW_SYSTEM_DISPLAY_MANAGER = 49,
-    NETWORK_INFORMATION_SERVICE_PLUS_DOMAIN = 64,
-    NETWORK_INFORMATION_SERVICE_PLUS_SERVERS = 65,
-    MOBILE_IP_HOME_AGENT = 68,
-    SMTP_SERVER = 69,
-    POP3_SERVER = 70,
-    NNTP_SERVER = 71,
-    DEFAULT_WWW_SERVER = 72,
-    DEFAULT_FINGER_SERVER = 73,
-    DEFAULT_IRC_SERVER = 74,
-    STREETTALK_SERVER = 75,
-    STREETTALK_DIRECTORY_ASSISTANCE_SERVER = 76,
-
-    /* DHCP Extensions */
-
-    REQUESTED_IP_ADDRESS = 50,
-    IP_ADDRESS_LEASE_TIME = 51,
-    OPTION_OVERLOAD = 52,
-    TFTP_SERVER_NAME = 66,
-    BOOTFILE_NAME = 67,
-    DHCP_MESSAGE_TYPE = 53,
-    SERVER_IDENTIFIER = 54,
-    PARAMETER_REQUEST_LIST = 55,
-    MESSAGE = 56,
-    MAXIMUM_DHCP_MESSAGE_SIZE = 57,
-    RENEWAL_T1_TIME_VALUE = 58,
-    REBINDING_T2_TIME_VALUE = 59,
-    VENDOR_CLASS_IDENTIFIER = 60,
-    CLIENT_IDENTIFIER = 61,
-
-    USER_CLASS = 77,
-    FQDN = 81,
-    DHCP_AGENT_OPTIONS = 82,
-    NDS_SERVERS = 85,
-    NDS_TREE_NAME = 86,
-    NDS_CONTEXT = 87,
-    CLIENT_LAST_TRANSACTION_TIME = 91,
-    ASSOCIATED_IP = 92,
-    USER_AUTHENTICATION_PROTOCOL = 98,
-    AUTO_CONFIGURE = 116,
-    NAME_SERVICE_SEARCH = 117,
-    SUBNET_SELECTION = 118,
-    DOMAIN_SEARCH = 119,
-    CLASSLESS_ROUTE = 121,
-} dhcp_msg_option;
-
 /*   Defined in esp_misc.h */
 typedef struct {
        bool enable;
@@ -176,11 +59,6 @@ struct dhcps_pool{
        u32_t lease_timer;
 };
 
-typedef struct _list_node{
-       void *pnode;
-       struct _list_node *pnext;
-}list_node;
-
 typedef u32_t dhcps_time_t;
 typedef u8_t dhcps_offer_t;
 
diff --git a/components/lwip/include/lwip/apps/dhcpserver_options.h b/components/lwip/include/lwip/apps/dhcpserver_options.h
new file mode 100644 (file)
index 0000000..38d46f6
--- /dev/null
@@ -0,0 +1,134 @@
+// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
+
+/** DHCP Options
+
+    This macros are not part of the public dhcpserver.h interface.
+ **/
+typedef enum
+{
+    /* RFC 1497 Vendor Extensions */
+
+    PAD = 0,
+    END = 255,
+
+    SUBNET_MASK = 1,
+    TIME_OFFSET = 2,
+    ROUTER = 3,
+    TIME_SERVER = 4,
+    NAME_SERVER = 5,
+    DOMAIN_NAME_SERVER = 6,
+    LOG_SERVER = 7,
+    COOKIE_SERVER = 8,
+    LPR_SERVER = 9,
+    IMPRESS_SERVER = 10,
+    RESOURCE_LOCATION_SERVER = 11,
+    HOST_NAME = 12,
+    BOOT_FILE_SIZE = 13,
+    MERIT_DUMP_FILE = 14,
+    DOMAIN_NAME = 15,
+    SWAP_SERVER = 16,
+    ROOT_PATH = 17,
+    EXTENSIONS_PATH = 18,
+
+    /* IP Layer Parameters per Host */
+
+    IP_FORWARDING = 19,
+    NON_LOCAL_SOURCE_ROUTING = 20,
+    POLICY_FILTER = 21,
+    MAXIMUM_DATAGRAM_REASSEMBLY_SIZE = 22,
+    DEFAULT_IP_TIME_TO_LIVE = 23,
+    PATH_MTU_AGING_TIMEOUT = 24,
+    PATH_MTU_PLATEAU_TABLE = 25,
+
+    /* IP Layer Parameters per Interface */
+
+    INTERFACE_MTU = 26,
+    ALL_SUBNETS_ARE_LOCAL = 27,
+    BROADCAST_ADDRESS = 28,
+    PERFORM_MASK_DISCOVERY = 29,
+    MASK_SUPPLIER = 30,
+    PERFORM_ROUTER_DISCOVERY = 31,
+    ROUTER_SOLICITATION_ADDRESS = 32,
+    STATIC_ROUTE = 33,
+
+    /* Link Layer Parameters per Interface */
+
+    TRAILER_ENCAPSULATION = 34,
+    ARP_CACHE_TIMEOUT = 35,
+    ETHERNET_ENCAPSULATION = 36,
+
+    /* TCP Parameters */
+
+    TCP_DEFAULT_TTL = 37,
+    TCP_KEEPALIVE_INTERVAL = 38,
+    TCP_KEEPALIVE_GARBAGE = 39,
+
+    /* Application and Service Parameters */
+
+    NETWORK_INFORMATION_SERVICE_DOMAIN = 40,
+    NETWORK_INFORMATION_SERVERS = 41,
+    NETWORK_TIME_PROTOCOL_SERVERS = 42,
+    VENDOR_SPECIFIC_INFORMATION = 43,
+    NETBIOS_OVER_TCP_IP_NAME_SERVER = 44,
+    NETBIOS_OVER_TCP_IP_DATAGRAM_DISTRIBUTION_SERVER = 45,
+    NETBIOS_OVER_TCP_IP_NODE_TYPE = 46,
+    NETBIOS_OVER_TCP_IP_SCOPE = 47,
+    X_WINDOW_SYSTEM_FONT_SERVER = 48,
+    X_WINDOW_SYSTEM_DISPLAY_MANAGER = 49,
+    NETWORK_INFORMATION_SERVICE_PLUS_DOMAIN = 64,
+    NETWORK_INFORMATION_SERVICE_PLUS_SERVERS = 65,
+    MOBILE_IP_HOME_AGENT = 68,
+    SMTP_SERVER = 69,
+    POP3_SERVER = 70,
+    NNTP_SERVER = 71,
+    DEFAULT_WWW_SERVER = 72,
+    DEFAULT_FINGER_SERVER = 73,
+    DEFAULT_IRC_SERVER = 74,
+    STREETTALK_SERVER = 75,
+    STREETTALK_DIRECTORY_ASSISTANCE_SERVER = 76,
+
+    /* DHCP Extensions */
+
+    REQUESTED_IP_ADDRESS = 50,
+    IP_ADDRESS_LEASE_TIME = 51,
+    OPTION_OVERLOAD = 52,
+    TFTP_SERVER_NAME = 66,
+    BOOTFILE_NAME = 67,
+    DHCP_MESSAGE_TYPE = 53,
+    SERVER_IDENTIFIER = 54,
+    PARAMETER_REQUEST_LIST = 55,
+    MESSAGE = 56,
+    MAXIMUM_DHCP_MESSAGE_SIZE = 57,
+    RENEWAL_T1_TIME_VALUE = 58,
+    REBINDING_T2_TIME_VALUE = 59,
+    VENDOR_CLASS_IDENTIFIER = 60,
+    CLIENT_IDENTIFIER = 61,
+
+    USER_CLASS = 77,
+    FQDN = 81,
+    DHCP_AGENT_OPTIONS = 82,
+    NDS_SERVERS = 85,
+    NDS_TREE_NAME = 86,
+    NDS_CONTEXT = 87,
+    CLIENT_LAST_TRANSACTION_TIME = 91,
+    ASSOCIATED_IP = 92,
+    USER_AUTHENTICATION_PROTOCOL = 98,
+    AUTO_CONFIGURE = 116,
+    NAME_SERVICE_SEARCH = 117,
+    SUBNET_SELECTION = 118,
+    DOMAIN_SEARCH = 119,
+    CLASSLESS_ROUTE = 121,
+} dhcp_msg_option;
index 57759ecd83798fda1280f998e70c9076cdad0408..0f06def07ea0cd18e58cfb4831c1af590f86f6d9 100644 (file)
@@ -402,6 +402,7 @@ static esp_err_t nvs_get_str_or_blob(nvs_handle handle, nvs::ItemType type, cons
         return ESP_ERR_NVS_INVALID_LENGTH;
     }
 
+    *length = dataSize;
     return entry.mStoragePtr->readItem(entry.mNsIndex, type, key, out_value, dataSize);
 }
 
index 5a35c11f4a12ad094a6e8c90f5248baab449b5c8..f419256cf334b9b8c7db3cd0f025c96e7cd53bce 100644 (file)
@@ -530,6 +530,10 @@ TEST_CASE("nvs api tests", "[nvs]")
     TEST_ESP_ERR(ESP_ERR_NVS_INVALID_LENGTH, nvs_get_str(handle_2, "key", buf, &buf_len_short));
     CHECK(buf_len_short == buf_len);
     
+    size_t buf_len_long = buf_len + 1;
+    TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len_long));
+    CHECK(buf_len_long == buf_len);
+
     TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len));
 
     CHECK(0 == strcmp(buf, str));
diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig
new file mode 100644 (file)
index 0000000..d82ceec
--- /dev/null
@@ -0,0 +1,139 @@
+menu "SPIFFS Configuration"
+
+config SPIFFS_MAX_PARTITIONS
+    int "Maximum Number of Partitions"
+    default 3
+    range 1 10
+    help
+        Define maximum number of partitions 
+        that can be mounted.
+
+menu "SPIFFS Cache Configuration"
+config SPIFFS_CACHE
+    bool "Enable SPIFFS Cache"
+    default "y"
+    help
+        Enables/disable memory read 
+        caching of nucleus file system 
+        operations.
+
+config SPIFFS_CACHE_WR
+    bool "Enable SPIFFS Write Caching"
+    default "y"
+    depends on SPIFFS_CACHE
+    help
+        Enables memory write caching for 
+        file descriptors in hydrogen.
+
+config SPIFFS_CACHE_STATS
+    bool "Enable SPIFFS Cache Statistics"
+    default "n"
+    depends on SPIFFS_CACHE
+    help
+        Enable/disable statistics on caching. 
+        Debug/test purpose only.
+
+endmenu
+
+config SPIFFS_PAGE_CHECK
+    bool "Enable SPIFFS Page Check"
+    default "y"
+    help
+        Always check header of each 
+        accessed page to ensure consistent state.
+        If enabled it will increase number 
+        of reads, will increase flash.
+
+config SPIFFS_GC_MAX_RUNS
+    int "Set Maximum GC Runs"
+    default 10
+    range 1 255
+    help
+        Define maximum number of gc runs to 
+        perform to reach desired free pages.
+
+config SPIFFS_GC_STATS
+    bool "Enable SPIFFS GC Statistics"
+    default "n"
+    help
+        Enable/disable statistics on gc. 
+        Debug/test purpose only.
+
+config SPIFFS_OBJ_NAME_LEN
+    int "Set SPIFFS Maximum Name Length"
+    default 32
+    range 1 256
+    help
+        Object name maximum length. Note that this length 
+        include the zero-termination character, 
+        meaning maximum string of characters can at most be 
+        SPIFFS_OBJ_NAME_LEN - 1.
+
+config SPIFFS_USE_MAGIC
+    bool "Enable SPIFFS Filesystem Magic"
+    default "y"
+    help
+        Enable this to have an identifiable spiffs filesystem. 
+        This will look for a magic in all sectors 
+        to determine if this is a valid spiffs system 
+        or not on mount point.
+
+config SPIFFS_USE_MAGIC_LENGTH
+    bool "Enable SPIFFS Filesystem Length Magic"
+    default "y"
+    depends on SPIFFS_USE_MAGIC
+    help
+        If this option is enabled, the magic will also be dependent 
+        on the length of the filesystem. For example, a filesystem 
+        configured and formatted for 4 megabytes will not be accepted 
+        for mounting with a configuration defining the filesystem as 2 megabytes.
+
+menu "Debug Configuration"
+
+config SPIFFS_DBG
+    bool "Enable general SPIFFS debug"
+    default "n"
+    help
+        Enabling this option will print 
+        general debug mesages to the console
+
+config SPIFFS_API_DBG
+    bool "Enable SPIFFS API debug"
+    default "n"
+    help
+        Enabling this option will print 
+        API debug mesages to the console
+
+config SPIFFS_GC_DBG
+    bool "Enable SPIFFS Garbage Cleaner debug"
+    default "n"
+    help
+        Enabling this option will print 
+        GC debug mesages to the console
+
+config SPIFFS_CACHE_DBG
+    bool "Enable SPIFFS Cache debug"
+    default "n"
+    depends on SPIFFS_CACHE
+    help
+        Enabling this option will print 
+        Cache debug mesages to the console
+
+config SPIFFS_CHECK_DBG
+    bool "Enable SPIFFS Filesystem Check debug"
+    default "n"
+    help
+        Enabling this option will print 
+        Filesystem Check debug mesages 
+        to the console
+
+config SPIFFS_TEST_VISUALISATION
+    bool "Enable SPIFFS Filesystem Visualization"
+    default "n"
+    help
+        Enable this option to enable SPIFFS_vis function 
+        in the api.
+
+endmenu
+
+endmenu
diff --git a/components/spiffs/component.mk b/components/spiffs/component.mk
new file mode 100644 (file)
index 0000000..624b219
--- /dev/null
@@ -0,0 +1,3 @@
+COMPONENT_ADD_INCLUDEDIRS := include
+COMPONENT_PRIV_INCLUDEDIRS := spiffs/src
+COMPONENT_SRCDIRS := . spiffs/src
diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c
new file mode 100644 (file)
index 0000000..f731f5b
--- /dev/null
@@ -0,0 +1,766 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp_spiffs.h"
+#include "spiffs.h"
+#include "spiffs_nucleus.h"
+#include "esp_log.h"
+#include "esp_partition.h"
+#include "esp_spi_flash.h"
+#include "esp_image_format.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/semphr.h"
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/lock.h>
+#include "esp_vfs.h"
+#include "esp_err.h"
+#include "rom/spi_flash.h"
+
+static const char * TAG = "SPIFFS";
+
+/**
+ * @brief SPIFFS definition structure
+ */
+typedef struct {
+    spiffs *fs;                             /*!< Handle to the underlying SPIFFS */
+    SemaphoreHandle_t lock;                 /*!< FS lock */
+    const esp_partition_t* partition;       /*!< The partition on which SPIFFS is located */
+    char base_path[ESP_VFS_PATH_MAX+1];     /*!< Mount point */
+    bool by_label;                          /*!< Partition was mounted by label */
+    spiffs_config cfg;                      /*!< SPIFFS Mount configuration */
+    uint8_t *work;                          /*!< Work Buffer */
+    uint8_t *fds;                           /*!< File Descriptor Buffer */
+    uint32_t fds_sz;                        /*!< File Descriptor Buffer Length */
+    uint8_t *cache;                         /*!< Cache Buffer */
+    uint32_t cache_sz;                      /*!< Cache Buffer Length */
+} esp_spiffs_t;
+
+/**
+ * @brief SPIFFS DIR structure
+ */
+typedef struct {
+    DIR dir;            /*!< VFS DIR struct */
+    spiffs_DIR d;       /*!< SPIFFS DIR struct */
+    struct dirent e;    /*!< Last open dirent */
+    long offset;        /*!< Offset of the current dirent */
+    char path[SPIFFS_OBJ_NAME_LEN]; /*!< Requested directory name */
+} vfs_spiffs_dir_t;
+
+static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode);
+static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size);
+static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size);
+static int vfs_spiffs_close(void* ctx, int fd);
+static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode);
+static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st);
+static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st);
+static int vfs_spiffs_unlink(void* ctx, const char *path);
+static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2);
+static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst);
+static DIR* vfs_spiffs_opendir(void* ctx, const char* name);
+static int vfs_spiffs_closedir(void* ctx, DIR* pdir);
+static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir);
+static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir,
+                                struct dirent* entry, struct dirent** out_dirent);
+static long vfs_spiffs_telldir(void* ctx, DIR* pdir);
+static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset);
+static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode);
+static int vfs_spiffs_rmdir(void* ctx, const char* name);
+
+static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS];
+
+void spiffs_api_lock(spiffs *fs)
+{
+    xSemaphoreTake(((esp_spiffs_t *)(fs->user_data))->lock, portMAX_DELAY);
+}
+
+void spiffs_api_unlock(spiffs *fs)
+{
+    xSemaphoreGive(((esp_spiffs_t *)(fs->user_data))->lock);
+}
+
+static s32_t spiffs_api_read(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *dst)
+{
+    esp_err_t err = esp_partition_read(((esp_spiffs_t *)(fs->user_data))->partition, 
+                                        addr, dst, size);
+    if (err) {
+        ESP_LOGE(TAG, "failed to read addr %08x, size %08x, err %d", addr, size, err);
+        return -1;
+    }
+    return 0;
+}
+
+static s32_t spiffs_api_write(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *src)
+{
+    esp_err_t err = esp_partition_write(((esp_spiffs_t *)(fs->user_data))->partition, 
+                                        addr, src, size);
+    if (err) {
+        ESP_LOGE(TAG, "failed to write addr %08x, size %08x, err %d", addr, size, err);
+        return -1;
+    }
+    return 0;
+}
+
+static s32_t spiffs_api_erase(spiffs *fs, uint32_t addr, uint32_t size)
+{
+    esp_err_t err = esp_partition_erase_range(((esp_spiffs_t *)(fs->user_data))->partition, 
+                                        addr, size);
+    if (err) {
+        ESP_LOGE(TAG, "failed to erase addr %08x, size %08x, err %d", addr, size, err);
+        return -1;
+    }
+    return 0;
+}
+
+static void spiffs_api_check(spiffs *fs, spiffs_check_type type, 
+                            spiffs_check_report report, uint32_t arg1, uint32_t arg2)
+{
+    static const char * spiffs_check_type_str[3] = {
+        "LOOKUP",
+        "INDEX",
+        "PAGE"
+    };
+
+    static const char * spiffs_check_report_str[7] = {
+        "PROGRESS",
+        "ERROR",
+        "FIX INDEX",
+        "FIX LOOKUP",
+        "DELETE ORPHANED INDEX",
+        "DELETE PAGE",
+        "DELETE BAD FILE"
+    };
+
+    if (report != SPIFFS_CHECK_PROGRESS) {
+        ESP_LOGE(TAG, "CHECK: type:%s, report:%s, %x:%x", spiffs_check_type_str[type], 
+                              spiffs_check_report_str[report], arg1, arg2);
+    } else {
+        ESP_LOGV(TAG, "CHECK PROGRESS: report:%s, %x:%x", 
+                              spiffs_check_report_str[report], arg1, arg2);
+    }
+}
+
+static void esp_spiffs_free(esp_spiffs_t ** efs)
+{
+    esp_spiffs_t * e = *efs;
+    if (*efs == NULL) {
+        return;
+    }
+    *efs = NULL;
+
+    if (e->fs) {
+        SPIFFS_unmount(e->fs);
+        free(e->fs);
+    }
+    vSemaphoreDelete(e->lock);
+    free(e->fds);
+    free(e->cache);
+    free(e->work);
+    free(e);
+}
+
+static esp_err_t esp_spiffs_by_label(const char* label, int * index){
+    int i;
+    esp_spiffs_t * p;
+    for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) {
+        p = _efs[i];
+        if (p) {
+            if (!label && !p->by_label) {
+                *index = i;
+                return ESP_OK;
+            }
+            if (label && p->by_label && strncmp(label, p->partition->label, 17) == 0) {
+                *index = i;
+                return ESP_OK;
+            }
+        }
+    }
+    return ESP_ERR_NOT_FOUND;
+}
+
+static esp_err_t esp_spiffs_get_empty(int * index){
+    int i;
+    for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) {
+        if (_efs[i] == NULL) {
+            *index = i;
+            return ESP_OK;
+        }
+    }
+    return ESP_ERR_NOT_FOUND;
+}
+
+static esp_err_t esp_spiffs_init(const esp_vfs_spiffs_conf_t* conf)
+{
+    int index;
+    //find if such partition is already mounted
+    if (esp_spiffs_by_label(conf->partition_label, &index) == ESP_OK) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    if (esp_spiffs_get_empty(&index) != ESP_OK) {
+        ESP_LOGE(TAG, "max mounted partitions reached");
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    esp_partition_subtype_t subtype = conf->partition_label ?
+            ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_SPIFFS;
+    const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, 
+                                      subtype, conf->partition_label);
+    if (!partition) {
+        ESP_LOGE(TAG, "spiffs partition could not be found");
+        return ESP_ERR_NOT_FOUND;
+    }
+
+    if (partition->encrypted) {
+        ESP_LOGE(TAG, "spiffs can not run on encrypted partition");
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    esp_spiffs_t * efs = malloc(sizeof(esp_spiffs_t));
+    if (efs == NULL) {
+        ESP_LOGE(TAG, "esp_spiffs could not be malloced");
+        return ESP_ERR_NO_MEM;
+    }
+    memset(efs, 0, sizeof(esp_spiffs_t));
+
+    efs->cfg.hal_erase_f       = spiffs_api_erase;
+    efs->cfg.hal_read_f        = spiffs_api_read;
+    efs->cfg.hal_write_f       = spiffs_api_write;
+    efs->cfg.log_block_size    = g_rom_flashchip.sector_size;
+    efs->cfg.log_page_size     = g_rom_flashchip.page_size;
+    efs->cfg.phys_addr         = 0;
+    efs->cfg.phys_erase_block  = g_rom_flashchip.sector_size;
+    efs->cfg.phys_size         = partition->size;
+
+    efs->by_label = conf->partition_label != NULL;
+
+    efs->lock = xSemaphoreCreateMutex();
+    if (efs->lock == NULL) {
+        ESP_LOGE(TAG, "mutex lock could not be created");
+        esp_spiffs_free(&efs);
+        return ESP_ERR_NO_MEM;
+    }
+
+    efs->fds_sz = conf->max_files * sizeof(spiffs_fd);
+    efs->fds = malloc(efs->fds_sz);
+    if (efs->fds == NULL) {
+        ESP_LOGE(TAG, "fd buffer could not be malloced");
+        esp_spiffs_free(&efs);
+        return ESP_ERR_NO_MEM;
+    }
+    memset(efs->fds, 0, efs->fds_sz);
+
+#if SPIFFS_CACHE
+    efs->cache_sz = sizeof(spiffs_cache) + conf->max_files * (sizeof(spiffs_cache_page)
+                          + efs->cfg.log_page_size);
+    efs->cache = malloc(efs->cache_sz);
+    if (efs->cache == NULL) {
+        ESP_LOGE(TAG, "cache buffer could not be malloced");
+        esp_spiffs_free(&efs);
+        return ESP_ERR_NO_MEM;
+    }
+    memset(efs->cache, 0, efs->cache_sz);
+#endif
+
+    const uint32_t work_sz = efs->cfg.log_page_size * 2;
+    efs->work = malloc(work_sz);
+    if (efs->work == NULL) {
+        ESP_LOGE(TAG, "work buffer could not be malloced");
+        esp_spiffs_free(&efs);
+        return ESP_ERR_NO_MEM;
+    }
+    memset(efs->work, 0, work_sz);
+
+    efs->fs = malloc(sizeof(spiffs));
+    if (efs->fs == NULL) {
+        ESP_LOGE(TAG, "spiffs could not be malloced");
+        esp_spiffs_free(&efs);
+        return ESP_ERR_NO_MEM;
+    }
+    memset(efs->fs, 0, sizeof(spiffs));
+
+    efs->fs->user_data = (void *)efs;
+    efs->partition = partition;
+
+    s32_t res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz, 
+                            efs->cache, efs->cache_sz, spiffs_api_check);
+
+    if (conf->format_if_mount_failed && res != SPIFFS_OK) {
+        ESP_LOGW(TAG, "mount failed, %i. formatting...", SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        res = SPIFFS_format(efs->fs);
+        if (res != SPIFFS_OK) {
+            ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(efs->fs));
+            SPIFFS_clearerr(efs->fs);
+            esp_spiffs_free(&efs);
+            return ESP_FAIL;
+        }
+        res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz, 
+                            efs->cache, efs->cache_sz, spiffs_api_check);
+    }
+    if (res != SPIFFS_OK) {
+        ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        esp_spiffs_free(&efs);
+        return ESP_FAIL;
+    }
+    _efs[index] = efs;
+    return ESP_OK;
+}
+
+bool esp_spiffs_mounted(const char* partition_label)
+{
+    int index;
+    if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
+        return false;
+    }
+    return (SPIFFS_mounted(_efs[index]->fs));
+}
+
+esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes)
+{
+    int index;
+    if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
+        return ESP_ERR_INVALID_STATE;
+    }
+    SPIFFS_info(_efs[index]->fs, total_bytes, used_bytes);
+    return ESP_OK;
+}
+
+esp_err_t esp_spiffs_format(const char* partition_label)
+{
+    bool mount_on_success = false;
+    int index;
+    esp_err_t err = esp_spiffs_by_label(partition_label, &index);
+    if (err != ESP_OK) {
+        esp_vfs_spiffs_conf_t conf = {
+                .format_if_mount_failed = true,
+                .partition_label = partition_label,
+                .max_files = 1
+        };
+        err = esp_spiffs_init(&conf);
+        if (err != ESP_OK) {
+            return err;
+        }
+        err = esp_spiffs_by_label(partition_label, &index);
+        if (err != ESP_OK) {
+            return err;
+        }
+        esp_spiffs_free(&_efs[index]);
+        return ESP_OK;
+    } else if (SPIFFS_mounted(_efs[index]->fs)) {
+        SPIFFS_unmount(_efs[index]->fs);
+        mount_on_success = true;
+    }
+    s32_t res = SPIFFS_format(_efs[index]->fs);
+    if (res != SPIFFS_OK) {
+        ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(_efs[index]->fs));
+        SPIFFS_clearerr(_efs[index]->fs);
+        return ESP_FAIL;
+    }
+
+    if (mount_on_success) {
+        res = SPIFFS_mount(_efs[index]->fs, &_efs[index]->cfg, _efs[index]->work,
+                            _efs[index]->fds, _efs[index]->fds_sz, _efs[index]->cache,
+                            _efs[index]->cache_sz, spiffs_api_check);
+        if (res != SPIFFS_OK) {
+            ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(_efs[index]->fs));
+            SPIFFS_clearerr(_efs[index]->fs);
+            return ESP_FAIL;
+        }
+    }
+    return ESP_OK;
+}
+
+esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)
+{
+    assert(conf->base_path);
+    const esp_vfs_t vfs = {
+        .flags = ESP_VFS_FLAG_CONTEXT_PTR,
+        .write_p = &vfs_spiffs_write,
+        .lseek_p = &vfs_spiffs_lseek,
+        .read_p = &vfs_spiffs_read,
+        .open_p = &vfs_spiffs_open,
+        .close_p = &vfs_spiffs_close,
+        .fstat_p = &vfs_spiffs_fstat,
+        .stat_p = &vfs_spiffs_stat,
+        .link_p = &vfs_spiffs_link,
+        .unlink_p = &vfs_spiffs_unlink,
+        .rename_p = &vfs_spiffs_rename,
+        .opendir_p = &vfs_spiffs_opendir,
+        .closedir_p = &vfs_spiffs_closedir,
+        .readdir_p = &vfs_spiffs_readdir,
+        .readdir_r_p = &vfs_spiffs_readdir_r,
+        .seekdir_p = &vfs_spiffs_seekdir,
+        .telldir_p = &vfs_spiffs_telldir,
+        .mkdir_p = &vfs_spiffs_mkdir,
+        .rmdir_p = &vfs_spiffs_rmdir
+    };
+
+    esp_err_t err = esp_spiffs_init(conf);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    int index;
+    if (esp_spiffs_by_label(conf->partition_label, &index) != ESP_OK) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    strlcat(_efs[index]->base_path, conf->base_path, ESP_VFS_PATH_MAX + 1);
+    err = esp_vfs_register(conf->base_path, &vfs, _efs[index]);
+    if (err != ESP_OK) {
+        esp_spiffs_free(&_efs[index]);
+        return err;
+    }
+
+    return ESP_OK;
+}
+
+esp_err_t esp_vfs_spiffs_unregister(const char* partition_label)
+{
+    int index;
+    if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
+        return ESP_ERR_INVALID_STATE;
+    }
+    esp_err_t err = esp_vfs_unregister(_efs[index]->base_path);
+    if (err != ESP_OK) {
+        return err;
+    }
+    esp_spiffs_free(&_efs[index]);
+    return ESP_OK;
+}
+
+static int spiffs_res_to_errno(s32_t fr)
+{
+    switch(fr) {
+    case SPIFFS_OK :
+        return 0;
+    case SPIFFS_ERR_NOT_MOUNTED :
+        return ENODEV;
+    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 :
+        return ENOENT;
+    case SPIFFS_ERR_NOT_A_FILE :
+        return ENOENT;
+    case SPIFFS_ERR_DELETED :
+        return ENOENT;
+    case SPIFFS_ERR_FILE_DELETED :
+        return ENOENT;
+    case SPIFFS_ERR_NAME_TOO_LONG :
+        return ENAMETOOLONG;
+    case SPIFFS_ERR_RO_NOT_IMPL :
+        return EROFS;
+    case SPIFFS_ERR_RO_ABORTED_OPERATION :
+        return EROFS;
+    default :
+        return EIO;
+    }
+    return ENOTSUP;
+}
+
+static int spiffs_mode_conv(int m)
+{
+    int res = 0;
+    int acc_mode = m & O_ACCMODE;
+    if (acc_mode == O_RDONLY) {
+        res |= SPIFFS_O_RDONLY;
+    } else if (acc_mode == O_WRONLY) {
+        res |= SPIFFS_O_WRONLY;
+    } else if (acc_mode == O_RDWR) {
+        res |= SPIFFS_O_RDWR;
+    }
+    if ((m & O_CREAT) && (m & O_EXCL)) {
+        res |= SPIFFS_O_CREAT | SPIFFS_O_EXCL;
+    } else if ((m & O_CREAT) && (m & O_TRUNC)) {
+        res |= SPIFFS_O_CREAT | SPIFFS_O_TRUNC;
+    } else if (m & O_APPEND) {
+        res |= SPIFFS_O_APPEND;
+    }
+    return res;
+}
+
+static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode)
+{
+    assert(path);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    int fd = SPIFFS_open(efs->fs, path, spiffs_mode_conv(flags), mode);
+    if (fd < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return fd;
+}
+
+static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size)
+{
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    ssize_t res = SPIFFS_write(efs->fs, fd, (void *)data, size);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size)
+{
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    ssize_t res = SPIFFS_read(efs->fs, fd, dst, size);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static int vfs_spiffs_close(void* ctx, int fd)
+{
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    int res = SPIFFS_close(efs->fs, fd);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode)
+{
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    off_t res = SPIFFS_lseek(efs->fs, fd, offset, mode);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+
+}
+
+static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st)
+{
+    assert(st);
+    spiffs_stat s;
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    off_t res = SPIFFS_fstat(efs->fs, fd, &s);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    st->st_size = s.size;
+    st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
+    return res;
+}
+
+static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st)
+{
+    assert(path);
+    assert(st);
+    spiffs_stat s;
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    off_t res = SPIFFS_stat(efs->fs, path, &s);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+
+    st->st_size = s.size;
+    st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
+    st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG;
+    return res;
+}
+
+static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst)
+{
+    assert(src);
+    assert(dst);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    int res = SPIFFS_rename(efs->fs, src, dst);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static int vfs_spiffs_unlink(void* ctx, const char *path)
+{
+    assert(path);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    int res = SPIFFS_remove(efs->fs, path);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static DIR* vfs_spiffs_opendir(void* ctx, const char* name)
+{
+    assert(name);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    vfs_spiffs_dir_t * dir = calloc(1, sizeof(vfs_spiffs_dir_t));
+    if (!dir) {
+        errno = ENOMEM;
+        return NULL;
+    }
+    if (!SPIFFS_opendir(efs->fs, name, &dir->d)) {
+        free(dir);
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return NULL;
+    }
+    dir->offset = 0;
+    strlcpy(dir->path, name, SPIFFS_OBJ_NAME_LEN);
+    return (DIR*) dir;
+}
+
+static int vfs_spiffs_closedir(void* ctx, DIR* pdir)
+{
+    assert(pdir);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
+    int res = SPIFFS_closedir(&dir->d);
+    free(dir);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir)
+{
+    assert(pdir);
+    vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
+    struct dirent* out_dirent;
+    int err = vfs_spiffs_readdir_r(ctx, pdir, &dir->e, &out_dirent);
+    if (err != 0) {
+        errno = err;
+        return NULL;
+    }
+    return out_dirent;
+}
+
+static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir, struct dirent* entry, 
+                                struct dirent** out_dirent)
+{
+    assert(pdir);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
+    struct spiffs_dirent out;
+    if (SPIFFS_readdir(&dir->d, &out) == 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        if (!errno) {
+            *out_dirent = NULL;
+        }
+        return errno;
+    }
+    const char * item_name = (const char *)out.name;
+    size_t plen = strlen(dir->path);
+    if (plen > 1) {
+        if (strncasecmp(dir->path, (const char *)out.name, plen) || out.name[plen] != '/' || !out.name[plen+1]) {
+            return vfs_spiffs_readdir_r(ctx, pdir, entry, out_dirent);
+        }
+        item_name += plen + 1;
+    } else if (item_name[0] == '/') {
+        item_name++;
+    }
+    entry->d_ino = 0;
+    entry->d_type = out.type;
+    snprintf(entry->d_name, SPIFFS_OBJ_NAME_LEN, "%s", item_name);
+    dir->offset++;
+    *out_dirent = entry;
+    return 0;
+}
+
+static long vfs_spiffs_telldir(void* ctx, DIR* pdir)
+{
+    assert(pdir);
+    vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
+    return dir->offset;
+}
+
+static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset)
+{
+    assert(pdir);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
+    struct spiffs_dirent tmp;
+    if (offset < dir->offset) {
+        //rewind dir
+        SPIFFS_closedir(&dir->d);
+        if (!SPIFFS_opendir(efs->fs, NULL, &dir->d)) {
+            errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+            SPIFFS_clearerr(efs->fs);
+            return;
+        }
+        dir->offset = 0;
+    }
+    while (dir->offset < offset) {
+        if (SPIFFS_readdir(&dir->d, &tmp) == 0) {
+            errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+            SPIFFS_clearerr(efs->fs);
+            return;
+        }
+        size_t plen = strlen(dir->path);
+        if (plen > 1) {
+            if (strncasecmp(dir->path, (const char *)tmp.name, plen) || tmp.name[plen] != '/' || !tmp.name[plen+1]) {
+                continue;
+            }
+        }
+        dir->offset++;
+    }
+}
+
+static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+static int vfs_spiffs_rmdir(void* ctx, const char* name)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2)
+{
+    errno = ENOTSUP;
+    return -1;
+}
diff --git a/components/spiffs/include/esp_spiffs.h b/components/spiffs/include/esp_spiffs.h
new file mode 100644 (file)
index 0000000..9a1f12c
--- /dev/null
@@ -0,0 +1,94 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef _ESP_SPIFFS_H_
+#define _ESP_SPIFFS_H_
+
+#include <stdbool.h>
+#include "esp_err.h"
+
+/**
+ * @brief Configuration structure for esp_vfs_spiffs_register
+ */
+typedef struct {
+        const char* base_path;          /*!< File path prefix associated with the filesystem. */
+        const char* partition_label;    /*!< Optional, label of SPIFFS partition to use. If set to NULL, first partition with subtype=spiffs will be used. */
+        size_t max_files;               /*!< Maximum files that could be open at the same time. */
+        bool format_if_mount_failed;    /*!< If true, it will format the file system if it fails to mount. */
+} esp_vfs_spiffs_conf_t;
+
+/**
+ * Register and mount SPIFFS to VFS with given path prefix.
+ *
+ * @param   conf                      Pointer to esp_vfs_spiffs_conf_t configuration structure
+ *
+ * @return  
+ *          - ESP_OK                  if success
+ *          - ESP_ERR_NO_MEM          if objects could not be allocated
+ *          - ESP_ERR_INVALID_STATE   if already mounted or partition is encrypted
+ *          - ESP_ERR_NOT_FOUND       if partition for SPIFFS was not found
+ *          - ESP_FAIL                if mount or format fails
+ */
+esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf);
+
+/**
+ * Unregister and unmount SPIFFS from VFS
+ *
+ * @param partition_label  Optional, label of the partition to unregister.
+ *                         If not specified, first partition with subtype=spiffs is used.
+ *
+ * @return  
+ *          - ESP_OK if successful
+ *          - ESP_ERR_INVALID_STATE already unregistered
+ */
+esp_err_t esp_vfs_spiffs_unregister(const char* partition_label);
+
+/**
+ * Check if SPIFFS is mounted
+ *
+ * @param partition_label  Optional, label of the partition to check.
+ *                         If not specified, first partition with subtype=spiffs is used.
+ *
+ * @return  
+ *          - true    if mounted
+ *          - false   if not mounted
+ */
+bool esp_spiffs_mounted(const char* partition_label);
+
+/**
+ * Format the SPIFFS partition
+ *
+ * @param partition_label  Optional, label of the partition to format.
+ *                         If not specified, first partition with subtype=spiffs is used.
+ * @return  
+ *          - ESP_OK      if successful
+ *          - ESP_FAIL    on error
+ */
+esp_err_t esp_spiffs_format(const char* partition_label);
+
+/**
+ * Get information for SPIFFS
+ *
+ * @param partition_label           Optional, label of the partition to get info for.
+ *                                  If not specified, first partition with subtype=spiffs is used.
+ * @param[out] total_bytes          Size of the file system
+ * @param[out] used_bytes           Current used bytes in the file system
+ *
+ * @return  
+ *          - ESP_OK                  if success
+ *          - ESP_ERR_INVALID_STATE   if not mounted
+ */
+esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes);
+
+#endif /* _ESP_SPIFFS_H_ */
diff --git a/components/spiffs/include/spiffs_config.h b/components/spiffs/include/spiffs_config.h
new file mode 100755 (executable)
index 0000000..e0c9d7f
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * spiffs_config.h
+ *
+ *  Created on: Jul 3, 2013
+ *      Author: petera
+ */
+
+#ifndef SPIFFS_CONFIG_H_
+#define SPIFFS_CONFIG_H_
+
+// ----------- 8< ------------
+// Following includes are for the linux test build of spiffs
+// These may/should/must be removed/altered/replaced in your target
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sdkconfig.h>
+#include <esp_log.h>
+
+// compile time switches
+#define SPIFFS_TAG "SPIFFS"
+
+// Set generic spiffs debug output call.
+#if CONGIG_SPIFFS_DBG
+#define SPIFFS_DBG(...)             ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#else
+#define SPIFFS_DBG(...)
+#endif
+#if CONGIG_SPIFFS_API_DBG
+#define SPIFFS_API_DBG(...)         ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#else
+#define SPIFFS_API_DBG(...)
+#endif
+#if CONGIG_SPIFFS_DBG
+#define SPIFFS_GC_DBG(...)          ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#else
+#define SPIFFS_GC_DBG(...)
+#endif
+#if CONGIG_SPIFFS_CACHE_DBG
+#define SPIFFS_CACHE_DBG(...)       ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#else
+#define SPIFFS_CACHE_DBG(...)
+#endif
+#if CONGIG_SPIFFS_CHECK_DBG
+#define SPIFFS_CHECK_DBG(...)       ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#else
+#define SPIFFS_CHECK_DBG(...)
+#endif
+
+// needed types
+typedef signed int s32_t;
+typedef unsigned int u32_t;
+typedef signed short s16_t;
+typedef unsigned short u16_t;
+typedef signed char s8_t;
+typedef unsigned char u8_t;
+
+struct spiffs_t;
+extern void spiffs_api_lock(struct spiffs_t *fs);
+extern void spiffs_api_unlock(struct spiffs_t *fs);
+
+// Defines spiffs debug print formatters
+// some general signed number
+#define _SPIPRIi   "%d"
+// address
+#define _SPIPRIad  "%08x"
+// block
+#define _SPIPRIbl  "%04x"
+// page
+#define _SPIPRIpg  "%04x"
+// span index
+#define _SPIPRIsp  "%04x"
+// file descriptor
+#define _SPIPRIfd  "%d"
+// file object id
+#define _SPIPRIid  "%04x"
+// file flags
+#define _SPIPRIfl  "%02x"
+
+
+// Enable/disable API functions to determine exact number of bytes
+// for filedescriptor and cache buffers. Once decided for a configuration,
+// this can be disabled to reduce flash.
+#define SPIFFS_BUFFER_HELP              0
+
+// Enables/disable memory read caching of nucleus file system operations.
+// If enabled, memory area must be provided for cache in SPIFFS_mount.
+#ifdef CONFIG_SPIFFS_CACHE
+#define SPIFFS_CACHE                (1)
+#else
+#define SPIFFS_CACHE                (0)
+#endif
+#if SPIFFS_CACHE
+// Enables memory write caching for file descriptors in hydrogen
+#ifdef CONFIG_SPIFFS_CACHE_WR
+#define SPIFFS_CACHE_WR             (1)
+#else
+#define SPIFFS_CACHE_WR             (0)
+#endif
+
+// Enable/disable statistics on caching. Debug/test purpose only.
+#ifdef CONFIG_SPIFFS_CACHE_STATS
+#define SPIFFS_CACHE_STATS          (1)
+#else
+#define SPIFFS_CACHE_STATS          (0)
+#endif
+#endif
+
+// Always check header of each accessed page to ensure consistent state.
+// If enabled it will increase number of reads, will increase flash.
+#ifdef CONFIG_SPIFFS_PAGE_CHECK
+#define SPIFFS_PAGE_CHECK           (1)
+#else
+#define SPIFFS_PAGE_CHECK           (0)
+#endif
+
+// Define maximum number of gc runs to perform to reach desired free pages.
+#define SPIFFS_GC_MAX_RUNS              CONFIG_SPIFFS_GC_MAX_RUNS
+
+// Enable/disable statistics on gc. Debug/test purpose only.
+#ifdef CONFIG_SPIFFS_GC_STATS
+#define SPIFFS_GC_STATS             (1)
+#else
+#define SPIFFS_GC_STATS             (0)
+#endif
+
+// Garbage collecting examines all pages in a block which and sums up
+// to a block score. Deleted pages normally gives positive score and
+// used pages normally gives a negative score (as these must be moved).
+// To have a fair wear-leveling, the erase age is also included in score,
+// whose factor normally is the most positive.
+// The larger the score, the more likely it is that the block will
+// picked for garbage collection.
+
+// Garbage collecting heuristics - weight used for deleted pages.
+#define SPIFFS_GC_HEUR_W_DELET          (5)
+// Garbage collecting heuristics - weight used for used pages.
+#define SPIFFS_GC_HEUR_W_USED           (-1)
+// Garbage collecting heuristics - weight used for time between
+// last erased and erase of this block.
+#define SPIFFS_GC_HEUR_W_ERASE_AGE      (50)
+
+// Object name maximum length. Note that this length include the
+// zero-termination character, meaning maximum string of characters
+// can at most be SPIFFS_OBJ_NAME_LEN - 1.
+#define SPIFFS_OBJ_NAME_LEN             (CONFIG_SPIFFS_OBJ_NAME_LEN)
+
+// Maximum length of the metadata associated with an object.
+// Setting to non-zero value enables metadata-related API but also
+// changes the on-disk format, so the change is not backward-compatible.
+//
+// Do note: the meta length must never exceed
+// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64)
+//
+// This is derived from following:
+// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
+// spiffs_object_ix_header fields + at least some LUT entries)
+#define SPIFFS_OBJ_META_LEN             (0)
+
+// Size of buffer allocated on stack used when copying data.
+// Lower value generates more read/writes. No meaning having it bigger
+// than logical page size.
+#define SPIFFS_COPY_BUFFER_STACK        (256)
+
+// Enable this to have an identifiable spiffs filesystem. This will look for
+// a magic in all sectors to determine if this is a valid spiffs system or
+// not on mount point. If not, SPIFFS_format must be called prior to mounting
+// again.
+#ifdef CONFIG_SPIFFS_USE_MAGIC
+#define SPIFFS_USE_MAGIC                (1)
+#else
+#define SPIFFS_USE_MAGIC                (0)
+#endif
+
+#if SPIFFS_USE_MAGIC
+// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is
+// enabled, the magic will also be dependent on the length of the filesystem.
+// For example, a filesystem configured and formatted for 4 megabytes will not
+// be accepted for mounting with a configuration defining the filesystem as 2
+// megabytes.
+#ifdef CONFIG_SPIFFS_USE_MAGIC_LENGTH
+#define SPIFFS_USE_MAGIC_LENGTH         (1)
+#else
+#define SPIFFS_USE_MAGIC_LENGTH         (0)
+#endif
+#endif
+
+// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
+// These should be defined on a multithreaded system
+
+// define this to enter a mutex if you're running on a multithreaded system
+#define SPIFFS_LOCK(fs)   spiffs_api_lock(fs)
+// define this to exit a mutex if you're running on a multithreaded system
+#define SPIFFS_UNLOCK(fs) spiffs_api_unlock(fs)
+
+// Enable if only one spiffs instance with constant configuration will exist
+// on the target. This will reduce calculations, flash and memory accesses.
+// Parts of configuration must be defined below instead of at time of mount.
+#define SPIFFS_SINGLETON 0
+
+// Enable this if your target needs aligned data for index tables
+#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES      0
+
+// Enable this if you want the HAL callbacks to be called with the spiffs struct
+#define SPIFFS_HAL_CALLBACK_EXTRA               1
+
+// Enable this if you want to add an integer offset to all file handles
+// (spiffs_file). This is useful if running multiple instances of spiffs on
+// same target, in order to recognise to what spiffs instance a file handle
+// belongs.
+// NB: This adds config field fh_ix_offset in the configuration struct when
+// mounting, which must be defined.
+#define SPIFFS_FILEHDL_OFFSET                   0
+
+// Enable this to compile a read only version of spiffs.
+// This will reduce binary size of spiffs. All code comprising modification
+// of the file system will not be compiled. Some config will be ignored.
+// HAL functions for erasing and writing to spi-flash may be null. Cache
+// can be disabled for even further binary size reduction (and ram savings).
+// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL.
+// If the file system cannot be mounted due to aborted erase operation and
+// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be
+// returned.
+// Might be useful for e.g. bootloaders and such.
+#define SPIFFS_READ_ONLY                        0
+
+// Enable this to add a temporal file cache using the fd buffer.
+// The effects of the cache is that SPIFFS_open will find the file faster in
+// certain cases. It will make it a lot easier for spiffs to find files
+// opened frequently, reducing number of readings from the spi flash for
+// finding those files.
+// This will grow each fd by 6 bytes. If your files are opened in patterns
+// with a degree of temporal locality, the system is optimized.
+// Examples can be letting spiffs serve web content, where one file is the css.
+// The css is accessed for each html file that is opened, meaning it is
+// accessed almost every second time a file is opened. Another example could be
+// a log file that is often opened, written, and closed.
+// The size of the cache is number of given file descriptors, as it piggybacks
+// on the fd update mechanism. The cache lives in the closed file descriptors.
+// When closed, the fd know the whereabouts of the file. Instead of forgetting
+// this, the temporal cache will keep handling updates to that file even if the
+// fd is closed. If the file is opened again, the location of the file is found
+// directly. If all available descriptors become opened, all cache memory is
+// lost.
+#define SPIFFS_TEMPORAL_FD_CACHE                1
+
+// Temporal file cache hit score. Each time a file is opened, all cached files
+// will lose one point. If the opened file is found in cache, that entry will
+// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this
+// value for the specific access patterns of the application. However, it must
+// be between 1 (no gain for hitting a cached entry often) and 255.
+#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE         4
+
+// Enable to be able to map object indices to memory.
+// This allows for faster and more deterministic reading if cases of reading
+// large files and when changing file offset by seeking around a lot.
+// When mapping a file's index, the file system will be scanned for index pages
+// and the info will be put in memory provided by user. When reading, the
+// memory map can be looked up instead of searching for index pages on the
+// medium. This way, user can trade memory against performance.
+// Whole, parts of, or future parts not being written yet can be mapped. The
+// memory array will be owned by spiffs and updated accordingly during garbage
+// collecting or when modifying the indices. The latter is invoked by when the
+// file is modified in some way. The index buffer is tied to the file
+// descriptor.
+#define SPIFFS_IX_MAP                           1
+
+// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
+// in the api. This function will visualize all filesystem using given printf
+// function.
+#ifdef CONFIG_SPIFFS_TEST_VISUALISATION
+#define SPIFFS_TEST_VISUALISATION               1
+#else
+#define SPIFFS_TEST_VISUALISATION               0
+#endif
+#if SPIFFS_TEST_VISUALISATION
+#ifndef spiffs_printf
+#define spiffs_printf(...)                ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#endif
+// spiffs_printf argument for a free page
+#define SPIFFS_TEST_VIS_FREE_STR          "_"
+// spiffs_printf argument for a deleted page
+#define SPIFFS_TEST_VIS_DELE_STR          "/"
+// spiffs_printf argument for an index page for given object id
+#define SPIFFS_TEST_VIS_INDX_STR(id)      "i"
+// spiffs_printf argument for a data page for given object id
+#define SPIFFS_TEST_VIS_DATA_STR(id)      "d"
+#endif
+
+// Types depending on configuration such as the amount of flash bytes
+// given to spiffs file system in total (spiffs_file_system_size),
+// the logical block size (log_block_size), and the logical page size
+// (log_page_size)
+
+// Block index type. Make sure the size of this type can hold
+// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size
+typedef u16_t spiffs_block_ix;
+// Page index type. Make sure the size of this type can hold
+// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size
+typedef u16_t spiffs_page_ix;
+// Object id type - most significant bit is reserved for index flag. Make sure the
+// size of this type can hold the highest object id on a full system,
+// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2
+typedef u16_t spiffs_obj_id;
+// Object span index type. Make sure the size of this type can
+// hold the largest possible span index on the system -
+// i.e. (spiffs_file_system_size / log_page_size) - 1
+typedef u16_t spiffs_span_ix;
+
+#endif /* SPIFFS_CONFIG_H_ */
diff --git a/components/spiffs/spiffs b/components/spiffs/spiffs
new file mode 160000 (submodule)
index 0000000..794f047
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 794f0478d2aa9c978c3844da6e97f14239a1e061
diff --git a/components/spiffs/test/component.mk b/components/spiffs/test/component.mk
new file mode 100644 (file)
index 0000000..ce464a2
--- /dev/null
@@ -0,0 +1 @@
+COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
diff --git a/components/spiffs/test/test_spiffs.c b/components/spiffs/test/test_spiffs.c
new file mode 100644 (file)
index 0000000..d60e4a5
--- /dev/null
@@ -0,0 +1,506 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/unistd.h>
+#include "unity.h"
+#include "test_utils.h"
+#include "esp_log.h"
+#include "esp_system.h"
+#include "esp_vfs.h"
+#include "esp_spiffs.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "freertos/semphr.h"
+#include "esp_partition.h"
+
+const char* spiffs_test_hello_str = "Hello, World!\n";
+const char* spiffs_test_partition_label = "flash_test";
+
+void test_spiffs_create_file_with_text(const char* name, const char* text)
+{
+    FILE* f = fopen(name, "wb");
+    TEST_ASSERT_NOT_NULL(f);
+    TEST_ASSERT_TRUE(fputs(text, f) != EOF);
+    TEST_ASSERT_EQUAL(0, fclose(f));
+}
+
+void test_spiffs_overwrite_append(const char* filename)
+{
+    /* Create new file with 'aaaa' */
+    test_spiffs_create_file_with_text(filename, "aaaa");
+
+    /* Append 'bbbb' to file */
+    FILE *f_a = fopen(filename, "a");
+    TEST_ASSERT_NOT_NULL(f_a);
+    TEST_ASSERT_NOT_EQUAL(EOF, fputs("bbbb", f_a));
+    TEST_ASSERT_EQUAL(0, fclose(f_a));
+
+    /* Read back 8 bytes from file, verify it's 'aaaabbbb' */
+    char buf[10] = { 0 };
+    FILE *f_r = fopen(filename, "r");
+    TEST_ASSERT_NOT_NULL(f_r);
+    TEST_ASSERT_EQUAL(8, fread(buf, 1, 8, f_r));
+    TEST_ASSERT_EQUAL_STRING_LEN("aaaabbbb", buf, 8);
+
+    /* Be sure we're at end of file */
+    TEST_ASSERT_EQUAL(0, fread(buf, 1, 8, f_r));
+
+    TEST_ASSERT_EQUAL(0, fclose(f_r));
+
+    /* Overwrite file with 'cccc' */
+    test_spiffs_create_file_with_text(filename, "cccc");
+
+    /* Verify file now only contains 'cccc' */
+    f_r = fopen(filename, "r");
+    TEST_ASSERT_NOT_NULL(f_r);
+    bzero(buf, sizeof(buf));
+    TEST_ASSERT_EQUAL(4, fread(buf, 1, 8, f_r)); // trying to read 8 bytes, only expecting 4
+    TEST_ASSERT_EQUAL_STRING_LEN("cccc", buf, 4);
+    TEST_ASSERT_EQUAL(0, fclose(f_r));
+}
+
+void test_spiffs_read_file(const char* filename)
+{
+    FILE* f = fopen(filename, "r");
+    TEST_ASSERT_NOT_NULL(f);
+    char buf[32] = { 0 };
+    int cb = fread(buf, 1, sizeof(buf), f);
+    TEST_ASSERT_EQUAL(strlen(spiffs_test_hello_str), cb);
+    TEST_ASSERT_EQUAL(0, strcmp(spiffs_test_hello_str, buf));
+    TEST_ASSERT_EQUAL(0, fclose(f));
+}
+
+void test_spiffs_open_max_files(const char* filename_prefix, size_t files_count)
+{
+    FILE** files = calloc(files_count, sizeof(FILE*));
+    for (size_t i = 0; i < files_count; ++i) {
+        char name[32];
+        snprintf(name, sizeof(name), "%s_%d.txt", filename_prefix, i);
+        files[i] = fopen(name, "w");
+        TEST_ASSERT_NOT_NULL(files[i]);
+    }
+    /* close everything and clean up */
+    for (size_t i = 0; i < files_count; ++i) {
+        fclose(files[i]);
+    }
+    free(files);
+}
+
+void test_spiffs_lseek(const char* filename)
+{
+    FILE* f = fopen(filename, "wb+");
+    TEST_ASSERT_NOT_NULL(f);
+    TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n"));
+    TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR));
+    TEST_ASSERT_EQUAL('9', fgetc(f));
+    TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET));
+    TEST_ASSERT_EQUAL('3', fgetc(f));
+    TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END));
+    TEST_ASSERT_EQUAL('8', fgetc(f));
+    TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
+    TEST_ASSERT_EQUAL(11, ftell(f));
+    TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n"));
+    TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
+    TEST_ASSERT_EQUAL(15, ftell(f));
+    TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
+    char buf[20];
+    TEST_ASSERT_EQUAL(15, fread(buf, 1, sizeof(buf), f));
+    const char ref_buf[] = "0123456789\nabc\n";
+    TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
+
+    TEST_ASSERT_EQUAL(0, fclose(f));
+}
+
+void test_spiffs_stat(const char* filename)
+{
+    test_spiffs_create_file_with_text(filename, "foo\n");
+    struct stat st;
+    TEST_ASSERT_EQUAL(0, stat(filename, &st));
+    TEST_ASSERT(st.st_mode & S_IFREG);
+    TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
+}
+
+void test_spiffs_unlink(const char* filename)
+{
+    test_spiffs_create_file_with_text(filename, "unlink\n");
+
+    TEST_ASSERT_EQUAL(0, unlink(filename));
+
+    TEST_ASSERT_NULL(fopen(filename, "r"));
+}
+
+void test_spiffs_rename(const char* filename_prefix)
+{
+    char name_dst[64];
+    char name_src[64];
+    snprintf(name_dst, sizeof(name_dst), "%s_dst.txt", filename_prefix);
+    snprintf(name_src, sizeof(name_src), "%s_src.txt", filename_prefix);
+
+    unlink(name_dst);
+    unlink(name_src);
+
+    FILE* f = fopen(name_src, "w+");
+    TEST_ASSERT_NOT_NULL(f);
+    char* str = "0123456789";
+    for (int i = 0; i < 400; ++i) {
+        TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f));
+    }
+    TEST_ASSERT_EQUAL(0, fclose(f));
+    TEST_ASSERT_EQUAL(0, rename(name_src, name_dst));
+    TEST_ASSERT_NULL(fopen(name_src, "r"));
+    FILE* fdst = fopen(name_dst, "r");
+    TEST_ASSERT_NOT_NULL(fdst);
+    TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END));
+    TEST_ASSERT_EQUAL(4000, ftell(fdst));
+    TEST_ASSERT_EQUAL(0, fclose(fdst));
+}
+
+void test_spiffs_can_opendir(const char* path)
+{
+    char name_dir_file[64];
+    const char * file_name = "test_opd.txt";
+    snprintf(name_dir_file, sizeof(name_dir_file), "%s/%s", path, file_name);
+    unlink(name_dir_file);
+    test_spiffs_create_file_with_text(name_dir_file, "test_opendir\n");
+    DIR* dir = opendir(path);
+    TEST_ASSERT_NOT_NULL(dir);
+    bool found = false;
+    while (true) {
+        struct dirent* de = readdir(dir);
+        if (!de) {
+            break;
+        }
+        if (strcasecmp(de->d_name, file_name) == 0) {
+            found = true;
+            break;
+        }
+    }
+    TEST_ASSERT_TRUE(found);
+    TEST_ASSERT_EQUAL(0, closedir(dir));
+    unlink(name_dir_file);
+}
+
+void test_spiffs_opendir_readdir_rewinddir(const char* dir_prefix)
+{
+    char name_dir_inner_file[64];
+    char name_dir_inner[64];
+    char name_dir_file3[64];
+    char name_dir_file2[64];
+    char name_dir_file1[64];
+
+    snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/inner/3.txt", dir_prefix);
+    snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/inner", dir_prefix);
+    snprintf(name_dir_file3, sizeof(name_dir_file2), "%s/boo.bin", dir_prefix);
+    snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/2.txt", dir_prefix);
+    snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/1.txt", dir_prefix);
+
+    unlink(name_dir_inner_file);
+    rmdir(name_dir_inner);
+    unlink(name_dir_file1);
+    unlink(name_dir_file2);
+    unlink(name_dir_file3);
+    rmdir(dir_prefix);
+
+    test_spiffs_create_file_with_text(name_dir_file1, "1\n");
+    test_spiffs_create_file_with_text(name_dir_file2, "2\n");
+    test_spiffs_create_file_with_text(name_dir_file3, "\01\02\03");
+    test_spiffs_create_file_with_text(name_dir_inner_file, "3\n");
+
+    DIR* dir = opendir(dir_prefix);
+    TEST_ASSERT_NOT_NULL(dir);
+    int count = 0;
+    const char* names[4];
+    while(count < 4) {
+        struct dirent* de = readdir(dir);
+        if (!de) {
+            break;
+        }
+        printf("found '%s'\n", de->d_name);
+        if (strcasecmp(de->d_name, "1.txt") == 0) {
+            TEST_ASSERT_TRUE(de->d_type == DT_REG);
+            names[count] = "1.txt";
+            ++count;
+        } else if (strcasecmp(de->d_name, "2.txt") == 0) {
+            TEST_ASSERT_TRUE(de->d_type == DT_REG);
+            names[count] = "2.txt";
+            ++count;
+        } else if (strcasecmp(de->d_name, "inner/3.txt") == 0) {
+            TEST_ASSERT_TRUE(de->d_type == DT_REG);
+            names[count] = "inner/3.txt";
+            ++count;
+        } else if (strcasecmp(de->d_name, "boo.bin") == 0) {
+            TEST_ASSERT_TRUE(de->d_type == DT_REG);
+            names[count] = "boo.bin";
+            ++count;
+        } else {
+            TEST_FAIL_MESSAGE("unexpected directory entry");
+        }
+    }
+    TEST_ASSERT_EQUAL(count, 4);
+
+    rewinddir(dir);
+    struct dirent* de = readdir(dir);
+    TEST_ASSERT_NOT_NULL(de);
+    TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
+    seekdir(dir, 3);
+    de = readdir(dir);
+    TEST_ASSERT_NOT_NULL(de);
+    TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
+    seekdir(dir, 1);
+    de = readdir(dir);
+    TEST_ASSERT_NOT_NULL(de);
+    TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
+    seekdir(dir, 2);
+    de = readdir(dir);
+    TEST_ASSERT_NOT_NULL(de);
+    TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
+
+    TEST_ASSERT_EQUAL(0, closedir(dir));
+}
+
+
+typedef struct {
+    const char* filename;
+    bool write;
+    size_t word_count;
+    int seed;
+    SemaphoreHandle_t done;
+    int result;
+} read_write_test_arg_t;
+
+#define READ_WRITE_TEST_ARG_INIT(name, seed_) \
+        { \
+            .filename = name, \
+            .seed = seed_, \
+            .word_count = 4096, \
+            .write = true, \
+            .done = xSemaphoreCreateBinary() \
+        }
+
+static void read_write_task(void* param)
+{
+    read_write_test_arg_t* args = (read_write_test_arg_t*) param;
+    FILE* f = fopen(args->filename, args->write ? "wb" : "rb");
+    if (f == NULL) {
+        args->result = ESP_ERR_NOT_FOUND;
+        goto done;
+    }
+
+    srand(args->seed);
+    for (size_t i = 0; i < args->word_count; ++i) {
+        uint32_t val = rand();
+        if (args->write) {
+            int cnt = fwrite(&val, sizeof(val), 1, f);
+            if (cnt != 1) {
+                ets_printf("E(w): i=%d, cnt=%d val=%d\n\n", i, cnt, val);
+                args->result = ESP_FAIL;
+                goto close;
+            }
+        } else {
+            uint32_t rval;
+            int cnt = fread(&rval, sizeof(rval), 1, f);
+            if (cnt != 1) {
+                ets_printf("E(r): i=%d, cnt=%d rval=%d\n\n", i, cnt, rval);
+                args->result = ESP_FAIL;
+                goto close;
+            }
+        }
+    }
+    args->result = ESP_OK;
+
+close:
+    fclose(f);
+
+done:
+    xSemaphoreGive(args->done);
+    vTaskDelay(1);
+    vTaskDelete(NULL);
+}
+
+void test_spiffs_concurrent(const char* filename_prefix)
+{
+    char names[4][64];
+    for (size_t i = 0; i < 4; ++i) {
+        snprintf(names[i], sizeof(names[i]), "%s%d", filename_prefix, i + 1);
+        unlink(names[i]);
+    }
+
+    read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT(names[0], 1);
+    read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT(names[1], 2);
+
+    printf("writing f1 and f2\n");
+
+    xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, 0);
+    xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, 1);
+
+    xSemaphoreTake(args1.done, portMAX_DELAY);
+    printf("f1 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args1.result);
+    xSemaphoreTake(args2.done, portMAX_DELAY);
+    printf("f2 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args2.result);
+
+    args1.write = false;
+    args2.write = false;
+    read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT(names[2], 3);
+    read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT(names[3], 4);
+
+    printf("reading f1 and f2, writing f3 and f4\n");
+
+    xTaskCreatePinnedToCore(&read_write_task, "rw3", 2048, &args3, 3, NULL, 1);
+    xTaskCreatePinnedToCore(&read_write_task, "rw4", 2048, &args4, 3, NULL, 0);
+    xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, 0);
+    xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, 1);
+
+    xSemaphoreTake(args1.done, portMAX_DELAY);
+    printf("f1 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args1.result);
+    xSemaphoreTake(args2.done, portMAX_DELAY);
+    printf("f2 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args2.result);
+    xSemaphoreTake(args3.done, portMAX_DELAY);
+    printf("f3 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args3.result);
+    xSemaphoreTake(args4.done, portMAX_DELAY);
+    printf("f4 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args4.result);
+
+    vSemaphoreDelete(args1.done);
+    vSemaphoreDelete(args2.done);
+    vSemaphoreDelete(args3.done);
+    vSemaphoreDelete(args4.done);
+}
+
+
+static void test_setup()
+{
+    esp_vfs_spiffs_conf_t conf = {
+      .base_path = "/spiffs",
+      .partition_label = spiffs_test_partition_label,
+      .max_files = 5,
+      .format_if_mount_failed = true
+    };
+
+    TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
+}
+
+static void test_teardown()
+{
+    TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
+}
+
+TEST_CASE("can format partition", "[spiffs]")
+{
+    const esp_partition_t* part = get_test_data_partition();
+    TEST_ASSERT_NOT_NULL(part);
+    TEST_ESP_OK(esp_partition_erase_range(part, 0, part->size));
+    test_setup();
+    size_t total = 0, used = 0;
+    TEST_ESP_OK(esp_spiffs_info(spiffs_test_partition_label, &total, &used));
+    printf("total: %d, used: %d\n", total, used);
+    TEST_ASSERT_EQUAL(0, used);
+    test_teardown();
+}
+
+TEST_CASE("can create and write file", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str);
+    test_teardown();
+}
+
+TEST_CASE("can read file", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str);
+    test_spiffs_read_file("/spiffs/hello.txt");
+    test_teardown();
+}
+
+TEST_CASE("can open maximum number of files", "[spiffs]")
+{
+    size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */
+    esp_vfs_spiffs_conf_t conf = {
+        .base_path = "/spiffs",
+        .partition_label = spiffs_test_partition_label,
+        .format_if_mount_failed = true,
+        .max_files = max_files
+    };
+    TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
+    test_spiffs_open_max_files("/spiffs/f", max_files);
+    TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
+}
+
+TEST_CASE("overwrite and append file", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_overwrite_append("/spiffs/hello.txt");
+    test_teardown();
+}
+
+TEST_CASE("can lseek", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_lseek("/spiffs/seek.txt");
+    test_teardown();
+}
+
+
+TEST_CASE("stat returns correct values", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_stat("/spiffs/stat.txt");
+    test_teardown();
+}
+
+TEST_CASE("unlink removes a file", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_unlink("/spiffs/unlink.txt");
+    test_teardown();
+}
+
+TEST_CASE("rename moves a file", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_rename("/spiffs/move");
+    test_teardown();
+}
+
+TEST_CASE("can opendir root directory of FS", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_can_opendir("/spiffs");
+    test_teardown();
+}
+
+TEST_CASE("opendir, readdir, rewinddir, seekdir work as expected", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_opendir_readdir_rewinddir("/spiffs/dir");
+    test_teardown();
+}
+
+TEST_CASE("multiple tasks can use same volume", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_concurrent("/spiffs/f");
+    test_teardown();
+}
index 25676266ac420ce82898d2245dba5e6930d01476..f9d7afe6bca8060e242e65dfe85213b45cfec480 100644 (file)
@@ -33,6 +33,7 @@
 #include "netif/ethernetif.h"
 
 #include "apps/dhcpserver.h"
+#include "apps/dhcpserver_options.h"
 
 #include "esp_event.h"
 #include "esp_log.h"
index fbc15cd2679e02046be5632839b2f64374573781..628db925acff9e267c01fddc10ecabbdae5f2890 100644 (file)
@@ -91,6 +91,8 @@ INPUT = \
     ../components/spi_flash/include/esp_spi_flash.h \
     ../components/spi_flash/include/esp_partition.h \
     ../components/bootloader_support/include/esp_flash_encrypt.h \
+    ## SPIFFS
+    ../components/spiffs/include/esp_spiffs.h \
     ## SD/MMC Card Host
     ## NOTE: for three lines below header_file.inc is not used
     ../components/sdmmc/include/sdmmc_cmd.h \
index 9c4394bd5a25534cadb471b7f63b17d4a4184c57..9c9215b09f7c221cbc6ec2d457a46b49e88217f4 100644 (file)
@@ -10,6 +10,7 @@ Storage API
    Virtual Filesystem <vfs>
    FAT Filesystem <fatfs>
    Wear Levelling <wear-levelling>
+   SPIFFS Filesystem <spiffs>
 
 
 Example code for this API section is provided in :example:`storage` directory of ESP-IDF examples.
diff --git a/docs/api-reference/storage/spiffs.rst b/docs/api-reference/storage/spiffs.rst
new file mode 100644 (file)
index 0000000..d463024
--- /dev/null
@@ -0,0 +1,53 @@
+SPIFFS Filesystem\r
+=================\r
+\r
+Overview\r
+--------\r
+\r
+SPIFFS is a file system intended for SPI NOR flash devices on embedded targets.\r
+It supports wear leveling, file system consistency checks and more.\r
+\r
+Notes\r
+-----\r
+\r
+ - Presently, spiffs does not support directories. It produces a flat structure. If SPIFFS is mounted under ``/spiffs`` creating a file with path ``/spiffs/tmp/myfile.txt`` will create a file called ``/tmp/myfile.txt`` in SPIFFS, instead of ``myfile.txt`` under directory ``/spiffs/tmp``. \r
+ - It is not a realtime stack. One write operation might last much longer than another.\r
+ - Presently, it does not detect or handle bad blocks.\r
+\r
+Tools\r
+-----\r
+\r
+Host-Side tools for creating SPIFS partition images exist and one such tool is `mkspiffs <https://github.com/igrr/mkspiffs>`_.\r
+You can use it to create image from a given folder and then flash that image with ``esptool.py``\r
+\r
+To do that you need to obtain some parameters:\r
+\r
+- Block Size: 4096 (standard for SPI Flash)\r
+- Page Size: 256 (standard for SPI Flash)\r
+- Image Size: Size of the partition in bytes (can be obtained from partition table)\r
+- Partition Offset: Starting address of the partition (can be obtained from partition table)\r
+\r
+To pack a folder into 1 Megabyte image::\r
+\r
+    mkspiffs -c [src_folder] -b 4096 -p 256 -s 0x100000 spiffs.bin\r
+\r
+To flash the image to ESP32 at offset 0x110000::\r
+\r
+    python esptool.py --chip esp32 --port [port] --baud [baud] write_flash -z 0x110000 spiffs.bin\r
+\r
+See also\r
+--------\r
+\r
+- :doc:`Partition Table documentation <../../api-guides/partition-tables>`\r
+\r
+Application Example\r
+-------------------\r
+\r
+An example for using SPIFFS is provided in :example:`storage/spiffs` directory. This example initializes and mounts SPIFFS partition, and writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information.\r
+\r
+High level API Reference\r
+------------------------\r
+\r
+* :component_file:`spiffs/include/esp_spiffs.h`\r
+\r
+.. include:: /_build/inc/esp_spiffs.inc\r
index edbe74394726c29e812d5beac6bcde0724ff0d3a..8c30fd326e269e6330cee2711634946e4f25bad2 100644 (file)
@@ -455,9 +455,9 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
         break;
     }
     case ESP_GATTS_ADD_CHAR_DESCR_EVT:
-        gl_profile_tab[PROFILE_A_APP_ID].descr_handle = param->add_char.attr_handle;
+        gl_profile_tab[PROFILE_A_APP_ID].descr_handle = param->add_char_descr.attr_handle;
         ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n",
-                 param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle);
+                 param->add_char_descr.status, param->add_char_descr.attr_handle, param->add_char_descr.service_handle);
         break;
     case ESP_GATTS_DELETE_EVT:
         break;
@@ -608,9 +608,9 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
                                      NULL, NULL);
         break;
     case ESP_GATTS_ADD_CHAR_DESCR_EVT:
-        gl_profile_tab[PROFILE_B_APP_ID].descr_handle = param->add_char.attr_handle;
+        gl_profile_tab[PROFILE_B_APP_ID].descr_handle = param->add_char_descr.attr_handle;
         ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n",
-                 param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle);
+                 param->add_char_descr.status, param->add_char_descr.attr_handle, param->add_char_descr.service_handle);
         break;
     case ESP_GATTS_DELETE_EVT:
         break;
diff --git a/examples/storage/spiffs/Makefile b/examples/storage/spiffs/Makefile
new file mode 100644 (file)
index 0000000..4523423
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := spiffs
+
+include $(IDF_PATH)/make/project.mk
+
diff --git a/examples/storage/spiffs/README.md b/examples/storage/spiffs/README.md
new file mode 100644 (file)
index 0000000..8c048fa
--- /dev/null
@@ -0,0 +1,27 @@
+# SPIFFS example
+
+This example demonstrates how to use SPIFFS with ESP32. Example does the following steps:
+
+1. Use an "all-in-one" `esp_vfs_spiffs_register` function to:
+    - initialize SPIFFS,
+    - mount SPIFFS filesystem using SPIFFS library (and format, if the filesystem can not be mounted),
+    - register SPIFFS filesystem in VFS, enabling C standard library and POSIX functions to be used.
+2. Create a file using `fopen` and write to it using `fprintf`.
+3. Rename the file. Before renaming, check if destination file already exists using `stat` function, and remove it using `unlink` function.
+4. Open renamed file for reading, read back the line, and print it to the terminal.
+
+## Example output
+
+Here is an example console output. In this case `format_if_mount_failed` parameter was set to `true` in the source code. SPIFFS was unformatted, so the initial mount has failed. SPIFFS was then formatted, and mounted again.
+
+```
+I (195) example: Initializing SPIFFS
+E (195) SPIFFS: mount failed, -10025. formatting...
+I (4525) example: Opening file
+I (4635) example: File written
+I (4685) example: Renaming file
+I (4735) example: Reading file
+I (4735) example: Read from file: 'Hello World!'
+I (4735) example: SPIFFS unmounted
+```
+
diff --git a/examples/storage/spiffs/main/component.mk b/examples/storage/spiffs/main/component.mk
new file mode 100644 (file)
index 0000000..a98f634
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
diff --git a/examples/storage/spiffs/main/spiffs_example_main.c b/examples/storage/spiffs/main/spiffs_example_main.c
new file mode 100644 (file)
index 0000000..d2ead73
--- /dev/null
@@ -0,0 +1,99 @@
+/* SPIFFS filesystem example.
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_spiffs.h"
+
+static const char *TAG = "example";
+
+void app_main(void)
+{
+    ESP_LOGI(TAG, "Initializing SPIFFS");
+    
+    esp_vfs_spiffs_conf_t conf = {
+      .base_path = "/spiffs",
+      .partition_label = NULL,
+      .max_files = 5,
+      .format_if_mount_failed = true
+    };
+    
+    // Use settings defined above to initialize and mount SPIFFS filesystem.
+    // Note: esp_vfs_spiffs_register is an all-in-one convenience function.
+    esp_err_t ret = esp_vfs_spiffs_register(&conf);
+
+    if (ret != ESP_OK) {
+        if (ret == ESP_FAIL) {
+            ESP_LOGE(TAG, "Failed to mount or format filesystem");
+        } else if (ret == ESP_ERR_NOT_FOUND) {
+            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
+        } else {
+            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%d)", ret);
+        }
+        return;
+    }
+    
+    size_t total = 0, used = 0;
+    ret = esp_spiffs_info(NULL, &total, &used);
+    if (ret != ESP_OK) {
+        ESP_LOGE(TAG, "Failed to get SPIFFS partition information");
+    } else {
+        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
+    }
+
+    // Use POSIX and C standard library functions to work with files.
+    // First create a file.
+    ESP_LOGI(TAG, "Opening file");
+    FILE* f = fopen("/spiffs/hello.txt", "w");
+    if (f == NULL) {
+        ESP_LOGE(TAG, "Failed to open file for writing");
+        return;
+    }
+    fprintf(f, "Hello World!\n");
+    fclose(f);
+    ESP_LOGI(TAG, "File written");
+
+    // Check if destination file exists before renaming
+    struct stat st;
+    if (stat("/spiffs/foo.txt", &st) == 0) {
+        // Delete it if it exists
+        unlink("/spiffs/foo.txt");
+    }
+
+    // Rename original file
+    ESP_LOGI(TAG, "Renaming file");
+    if (rename("/spiffs/hello.txt", "/spiffs/foo.txt") != 0) {
+        ESP_LOGE(TAG, "Rename failed");
+        return;
+    }
+
+    // Open renamed file for reading
+    ESP_LOGI(TAG, "Reading file");
+    f = fopen("/spiffs/foo.txt", "r");
+    if (f == NULL) {
+        ESP_LOGE(TAG, "Failed to open file for reading");
+        return;
+    }
+    char line[64];
+    fgets(line, sizeof(line), f);
+    fclose(f);
+    // strip newline
+    char* pos = strchr(line, '\n');
+    if (pos) {
+        *pos = '\0';
+    }
+    ESP_LOGI(TAG, "Read from file: '%s'", line);
+
+    // All done, unmount partition and disable SPIFFS
+    esp_vfs_spiffs_unregister(NULL);
+    ESP_LOGI(TAG, "SPIFFS unmounted");
+}
diff --git a/examples/storage/spiffs/partitions_example.csv b/examples/storage/spiffs/partitions_example.csv
new file mode 100644 (file)
index 0000000..6d9ee2e
--- /dev/null
@@ -0,0 +1,6 @@
+# Name,   Type, SubType, Offset,  Size, Flags
+# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
+nvs,      data, nvs,     0x9000,  0x6000,
+phy_init, data, phy,     0xf000,  0x1000,
+factory,  app,  factory, 0x10000, 1M,
+storage,  data, spiffs,  ,        0xF0000, 
diff --git a/examples/storage/spiffs/sdkconfig.defaults b/examples/storage/spiffs/sdkconfig.defaults
new file mode 100644 (file)
index 0000000..f30f322
--- /dev/null
@@ -0,0 +1,5 @@
+CONFIG_PARTITION_TABLE_CUSTOM=y
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
+CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000
+CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
+CONFIG_APP_OFFSET=0x10000
index 549359f60e175bd2d5365d994f450aa3d14ff9bc..d3972aa5d5a4ac3e6e3882e2e55ec844da6542f3 100644 (file)
@@ -6,5 +6,6 @@ components/esptool_py/esptool                       @GENERAL_MIRROR_SERVER@/idf/
 components/libsodium/libsodium                      @GENERAL_MIRROR_SERVER@/idf/libsodium.git                       ALLOW_TO_SYNC_FROM_PUBLIC
 components/micro-ecc/micro-ecc                      @GENERAL_MIRROR_SERVER@/idf/micro-ecc.git                       ALLOW_TO_SYNC_FROM_PUBLIC
 components/nghttp/nghttp2                           @GENERAL_MIRROR_SERVER@/idf/nghttp2.git                         ALLOW_TO_SYNC_FROM_PUBLIC
+components/spiffs/spiffs                            @GENERAL_MIRROR_SERVER@/idf/spiffs.git                          ALLOW_TO_SYNC_FROM_PUBLIC
 third-party/mruby                                   @GENERAL_MIRROR_SERVER@/idf/mruby.git                           ALLOW_TO_SYNC_FROM_PUBLIC
 third-party/neverbleed                              @GENERAL_MIRROR_SERVER@/idf/neverbleed.git                      ALLOW_TO_SYNC_FROM_PUBLIC
index ea43d0791cbd9873f69a7395088548d8e105e9c6..46173719b5826c8dfd84ae31b359928d2710895d 100644 (file)
@@ -10,7 +10,5 @@ factory,    0,    0,       0x10000, 0x140000
 # (done this way so tests can run in 2MB of flash.)
 ota_0,      0,    ota_0,   ,        64K
 ota_1,      0,    ota_1,   ,        64K
-# flash_test partition used for SPI flash tests and WL FAT partition
-# 528K is the minimal size needed to create a FAT partition
-# (128 sectors for FAT + 4 sectors for WL)
+# flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests
 flash_test, data, fat,     ,        528K