├── .codespellrc ├── .github ├── dependabot.yml └── workflows │ ├── check-arduino.yml │ ├── compile-examples.yml │ ├── report-size-deltas.yml │ ├── spell-check.yml │ └── sync-labels.yml ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── RawFlashAccess │ └── RawFlashAccess.ino ├── SPIFFSDirectories │ └── SPIFFSDirectories.ino ├── SPIFFSFormat │ └── SPIFFSFormat.ino └── SPIFFSUsage │ └── SPIFFSUsage.ino ├── keywords.txt ├── library.properties └── src ├── Arduino_MKRMEM.h ├── Arduino_SPIFFS.cpp ├── Arduino_SPIFFS.h ├── Arduino_SPIFFS_Directory.cpp ├── Arduino_SPIFFS_Directory.h ├── Arduino_SPIFFS_DirectoryEntry.cpp ├── Arduino_SPIFFS_DirectoryEntry.h ├── Arduino_SPIFFS_File.cpp ├── Arduino_SPIFFS_File.h ├── Arduino_W25Q16DV.cpp ├── Arduino_W25Q16DV.h ├── spiffs.h ├── spiffs_cache.c ├── spiffs_check.c ├── spiffs_config.h ├── spiffs_gc.c ├── spiffs_hydrogen.c ├── spiffs_nucleus.c └── spiffs_nucleus.h /.codespellrc: -------------------------------------------------------------------------------- 1 | # See: https://github.com/codespell-project/codespell#using-a-config-file 2 | [codespell] 3 | # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: 4 | ignore-words-list = thru,creat,pixes,objec,conjuction,indicies,wel,allo 5 | check-filenames = 6 | check-hidden = 7 | skip = ./.git 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file 2 | version: 2 3 | 4 | updates: 5 | # Configure check for outdated GitHub Actions actions in workflows. 6 | # See: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot 7 | - package-ecosystem: github-actions 8 | directory: / # Check the repository's workflows under /.github/workflows/ 9 | labels: 10 | - "topic: infrastructure" 11 | schedule: 12 | interval: daily 13 | -------------------------------------------------------------------------------- /.github/workflows/check-arduino.yml: -------------------------------------------------------------------------------- 1 | name: Check Arduino 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | pull_request: 7 | schedule: 8 | # Run every Tuesday at 8 AM UTC to catch breakage caused by new rules added to Arduino Lint. 9 | - cron: "0 8 * * TUE" 10 | workflow_dispatch: 11 | repository_dispatch: 12 | 13 | jobs: 14 | lint: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Arduino Lint 22 | uses: arduino/arduino-lint-action@v2 23 | with: 24 | compliance: strict 25 | library-manager: update 26 | # Always use this setting for official repositories. Remove for 3rd party projects. 27 | official: true 28 | project-type: library 29 | -------------------------------------------------------------------------------- /.github/workflows/compile-examples.yml: -------------------------------------------------------------------------------- 1 | name: Compile Examples 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | paths: 7 | - ".github/workflows/compile-examples.yml" 8 | - "examples/**" 9 | - "src/**" 10 | pull_request: 11 | paths: 12 | - ".github/workflows/compile-examples.yml" 13 | - "examples/**" 14 | - "src/**" 15 | schedule: 16 | # Run every Tuesday at 8 AM UTC to catch breakage caused by changes to external resources (libraries, platforms). 17 | - cron: "0 8 * * TUE" 18 | workflow_dispatch: 19 | repository_dispatch: 20 | 21 | jobs: 22 | build: 23 | name: ${{ matrix.board.fqbn }} 24 | runs-on: ubuntu-latest 25 | 26 | env: 27 | SKETCHES_REPORTS_PATH: sketches-reports 28 | 29 | strategy: 30 | fail-fast: false 31 | 32 | matrix: 33 | board: 34 | - fqbn: arduino:samd:mkr1000 35 | platforms: | 36 | - name: arduino:samd 37 | artifact-name-suffix: arduino-samd-mkr1000 38 | - fqbn: arduino:samd:mkrzero 39 | platforms: | 40 | - name: arduino:samd 41 | artifact-name-suffix: arduino-samd-mkrzero 42 | - fqbn: arduino:samd:mkrwifi1010 43 | platforms: | 44 | - name: arduino:samd 45 | artifact-name-suffix: arduino-samd-mkrwifi1010 46 | - fqbn: arduino:samd:mkrfox1200 47 | platforms: | 48 | - name: arduino:samd 49 | artifact-name-suffix: arduino-samd-mkrfox1200 50 | - fqbn: arduino:samd:mkrwan1300 51 | platforms: | 52 | - name: arduino:samd 53 | artifact-name-suffix: arduino-samd-mkrwan1300 54 | - fqbn: arduino:samd:mkrwan1310 55 | platforms: | 56 | - name: arduino:samd 57 | artifact-name-suffix: arduino-samd-mkrwan1310 58 | - fqbn: arduino:samd:mkrgsm1400 59 | platforms: | 60 | - name: arduino:samd 61 | artifact-name-suffix: arduino-samd-mkrgsm1400 62 | - fqbn: arduino:samd:mkrnb1500 63 | platforms: | 64 | - name: arduino:samd 65 | artifact-name-suffix: arduino-samd-mkrnb1500 66 | - fqbn: arduino:samd:mkrvidor4000 67 | platforms: | 68 | - name: arduino:samd 69 | artifact-name-suffix: arduino-samd-mkrvidor4000 70 | - fqbn: arduino:mbed_portenta:envie_m7:target_core=cm4 71 | platforms: | 72 | - name: arduino:mbed_portenta 73 | artifact-name-suffix: arduino-mbed_portenta-envie_m7-target_core-cm4 74 | - fqbn: arduino:mbed_portenta:envie_m7 75 | platforms: | 76 | - name: arduino:mbed_portenta 77 | artifact-name-suffix: arduino-mbed_portenta-envie_m7 78 | 79 | steps: 80 | - name: Checkout repository 81 | uses: actions/checkout@v4 82 | 83 | - name: Compile examples 84 | uses: arduino/compile-sketches@v1 85 | with: 86 | github-token: ${{ secrets.GITHUB_TOKEN }} 87 | fqbn: ${{ matrix.board.fqbn }} 88 | platforms: ${{ matrix.board.platforms }} 89 | libraries: | 90 | # Install the library from the local path. 91 | - source-path: ./ 92 | # Additional library dependencies can be listed here. 93 | # See: https://github.com/arduino/compile-sketches#libraries 94 | sketch-paths: | 95 | - examples 96 | enable-deltas-report: true 97 | sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} 98 | 99 | - name: Save sketches report as workflow artifact 100 | uses: actions/upload-artifact@v4 101 | with: 102 | if-no-files-found: error 103 | path: ${{ env.SKETCHES_REPORTS_PATH }} 104 | name: sketches-report-${{ matrix.board.artifact-name-suffix }} 105 | -------------------------------------------------------------------------------- /.github/workflows/report-size-deltas.yml: -------------------------------------------------------------------------------- 1 | name: Report Size Deltas 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | paths: 7 | - ".github/workflows/report-size-deltas.yml" 8 | schedule: 9 | # Run at the minimum interval allowed by GitHub Actions. 10 | # Note: GitHub Actions periodically has outages which result in workflow failures. 11 | # In this event, the workflows will start passing again once the service recovers. 12 | - cron: "*/5 * * * *" 13 | workflow_dispatch: 14 | repository_dispatch: 15 | 16 | jobs: 17 | report: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Comment size deltas reports to PRs 21 | uses: arduino/report-size-deltas@v1 22 | with: 23 | # Regex matching the names of the workflow artifacts created by the "Compile Examples" workflow 24 | sketches-reports-source: ^sketches-report-.+ 25 | -------------------------------------------------------------------------------- /.github/workflows/spell-check.yml: -------------------------------------------------------------------------------- 1 | name: Spell Check 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | pull_request: 7 | schedule: 8 | # Run every Tuesday at 8 AM UTC to catch new misspelling detections resulting from dictionary updates. 9 | - cron: "0 8 * * TUE" 10 | workflow_dispatch: 11 | repository_dispatch: 12 | 13 | jobs: 14 | spellcheck: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Spell check 22 | uses: codespell-project/actions-codespell@master 23 | -------------------------------------------------------------------------------- /.github/workflows/sync-labels.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md 2 | name: Sync Labels 3 | 4 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows 5 | on: 6 | push: 7 | paths: 8 | - ".github/workflows/sync-labels.ya?ml" 9 | - ".github/label-configuration-files/*.ya?ml" 10 | pull_request: 11 | paths: 12 | - ".github/workflows/sync-labels.ya?ml" 13 | - ".github/label-configuration-files/*.ya?ml" 14 | schedule: 15 | # Run daily at 8 AM UTC to sync with changes to shared label configurations. 16 | - cron: "0 8 * * *" 17 | workflow_dispatch: 18 | repository_dispatch: 19 | 20 | env: 21 | CONFIGURATIONS_FOLDER: .github/label-configuration-files 22 | CONFIGURATIONS_ARTIFACT: label-configuration-files 23 | 24 | jobs: 25 | check: 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v4 31 | 32 | - name: Download JSON schema for labels configuration file 33 | id: download-schema 34 | uses: carlosperate/download-file-action@v2 35 | with: 36 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json 37 | location: ${{ runner.temp }}/label-configuration-schema 38 | 39 | - name: Install JSON schema validator 40 | run: | 41 | sudo npm install \ 42 | --global \ 43 | ajv-cli \ 44 | ajv-formats 45 | 46 | - name: Validate local labels configuration 47 | run: | 48 | # See: https://github.com/ajv-validator/ajv-cli#readme 49 | ajv validate \ 50 | --all-errors \ 51 | -c ajv-formats \ 52 | -s "${{ steps.download-schema.outputs.file-path }}" \ 53 | -d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}" 54 | 55 | download: 56 | needs: check 57 | runs-on: ubuntu-latest 58 | 59 | strategy: 60 | matrix: 61 | filename: 62 | # Filenames of the shared configurations to apply to the repository in addition to the local configuration. 63 | # https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels 64 | - universal.yml 65 | 66 | steps: 67 | - name: Download 68 | uses: carlosperate/download-file-action@v2 69 | with: 70 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} 71 | 72 | - name: Pass configuration files to next job via workflow artifact 73 | uses: actions/upload-artifact@v4 74 | with: 75 | path: | 76 | *.yaml 77 | *.yml 78 | if-no-files-found: error 79 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 80 | 81 | sync: 82 | needs: download 83 | runs-on: ubuntu-latest 84 | 85 | steps: 86 | - name: Set environment variables 87 | run: | 88 | # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable 89 | echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV" 90 | 91 | - name: Determine whether to dry run 92 | id: dry-run 93 | if: > 94 | github.event_name == 'pull_request' || 95 | ( 96 | ( 97 | github.event_name == 'push' || 98 | github.event_name == 'workflow_dispatch' 99 | ) && 100 | github.ref != format('refs/heads/{0}', github.event.repository.default_branch) 101 | ) 102 | run: | 103 | # Use of this flag in the github-label-sync command will cause it to only check the validity of the 104 | # configuration. 105 | echo "::set-output name=flag::--dry-run" 106 | 107 | - name: Checkout repository 108 | uses: actions/checkout@v4 109 | 110 | - name: Download configuration files artifact 111 | uses: actions/download-artifact@v4 112 | with: 113 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 114 | path: ${{ env.CONFIGURATIONS_FOLDER }} 115 | 116 | - name: Remove unneeded artifact 117 | uses: geekyeggo/delete-artifact@v5 118 | with: 119 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 120 | 121 | - name: Merge label configuration files 122 | run: | 123 | # Merge all configuration files 124 | shopt -s extglob 125 | cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}" 126 | 127 | - name: Install github-label-sync 128 | run: sudo npm install --global github-label-sync 129 | 130 | - name: Sync labels 131 | env: 132 | GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} 133 | run: | 134 | # See: https://github.com/Financial-Times/github-label-sync 135 | github-label-sync \ 136 | --labels "${{ env.MERGED_CONFIGURATION_PATH }}" \ 137 | ${{ steps.dry-run.outputs.flag }} \ 138 | ${{ github.repository }} 139 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | **/sketch.json 3 | **/*.bin 4 | **/*.elf 5 | **/*.hex 6 | **/*.map 7 | build/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `Arduino_MKRMEM` 2 | ================ 3 | 4 | [![Check Arduino status](https://github.com/arduino-libraries/Arduino_MKRMEM/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_MKRMEM/actions/workflows/check-arduino.yml) 5 | [![Compile Examples status](https://github.com/arduino-libraries/Arduino_MKRMEM/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_MKRMEM/actions/workflows/compile-examples.yml) 6 | [![Spell Check status](https://github.com/arduino-libraries/Arduino_MKRMEM/actions/workflows/spell-check.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_MKRMEM/actions/workflows/spell-check.yml) 7 | 8 | This library provides a driver for the [Arduino MKR MEM Shield](https://store.arduino.cc/arduino-mkr-mem-shield) W25Q16DV SPI flash (with a capacity of 2 MByte) complete with integration for the [SPIFFS](https://github.com/pellepl/spiffs) embedded flash file system (EFFS). 9 | 10 | **Attention**: Before you can use SPIFFS you need to erase and format the flash memory chip using [SPIFFSFormat.ino](examples/SPIFFSFormat/SPIFFSFormat.ino). 11 | 12 | ## Why use an EFFS? 13 | The usage of semiconductor based flash memories differs fundamentally from other devices such as magnetic based hard discs. In flash terminology you don't write to memory but you **program** it. When a flash memory is programmed the default state of the flash memory (all bits equal to 1) is changed to the desired value. Note that you can only change bits from 1 to 0, in order to change a bit to 1 again an **erase operation** has to be performed. Such an erase operation can not be performed for each individual bit, only a bit block of bits and bytes can be "erased" together. The smallest possible erase size for the W25Q16DV is 4 kByte (sector erase). Also you can not just program any address on the flash, programming is usually performed **page-by-page**. The W25Q16DV page size is 256 bytes. 14 | 15 | If data stored on the flash memory needs to be changed the data has to be read, modified and written to another address while the the old entry has to be marked as as invalid. Since this tends to consume the available space fairly quickly, a process known as **garbage collection** has to be performed periodically. During garbage collection all valid data are copied to fresh blocks and the old blocks are erased. 16 | 17 | When a maximum number of program / erase cycles (≥ 10^5) are exceeded the flash memory will start to wear down, causing the flash memory to no longer function reliably. As a countermeasure **wear leveling** techniques are used which distribute the data evenly across all sectors of the flash memory to minimize the number of erase cycles per sector and thus extend the life of the flash memory. **Dynamic** Wear Leveling refers to a wear-leveling strategy which works only with those data. **Static** Wear Leveling refers to a wear-leveling strategy that looks at all data, including those already written to the flash memory. 18 | 19 | The goals of garbage collection (maximizing the free sectors) and wear-leveling (even utilization of all sectors) are in conflict with one another. To get both sufficient performance and endurance, a good trade-off must be found between these two tasks. 20 | 21 | So while the usage of an embedded flash file system comes with a bit of overhead, it is the only way to reasonably ensure successful long-term operation of flash memories. 22 | 23 | ## How to use 24 | 25 | ```C++ 26 | #include 27 | /* ... */ 28 | static char const PANGRAM[] = "The quick brown fox jumps over the lazy dog."; 29 | /* ... */ 30 | void setup() 31 | { 32 | Serial.begin(); 33 | flash.begin(); 34 | /* ... */ 35 | if(SPIFFS_OK != filesystem.mount()) { 36 | Serial.println("mount() failed with error code "); Serial.println(filesystem.err()); return; 37 | } 38 | /* ... */ 39 | File file = filesystem.open("fox.txt", CREATE | READ_WRITE| TRUNCATE); 40 | /* ... */ 41 | file.write((void *)PANGRAM, strlen(PANGRAM)); 42 | /* ... */ 43 | file.lseek(0, START); /* Rewind file pointer to the start */ 44 | char buf[64] = {0}; 45 | int const bytes_read = file.read(buf, sizeof(buf)); 46 | buf[bytes_read] = '\0'; 47 | Serial.println(buf); 48 | /* ... */ 49 | file.close(); 50 | filesystem.unmount(); 51 | } 52 | ``` 53 | -------------------------------------------------------------------------------- /examples/RawFlashAccess/RawFlashAccess.ino: -------------------------------------------------------------------------------- 1 | /* RawFlashAccess.ino 2 | * 3 | * This sketch demonstrates the raw API of the W25Q16DV class 4 | * which allows for low level flash memory control. 5 | * 6 | * Alexander Entinger 7 | */ 8 | 9 | /************************************************************************************** 10 | * INCLUDE 11 | **************************************************************************************/ 12 | 13 | #include 14 | 15 | #undef max 16 | #undef min 17 | #include 18 | #include 19 | 20 | /************************************************************************************** 21 | * SETUP/LOOP 22 | **************************************************************************************/ 23 | 24 | void setup() 25 | { 26 | Serial.begin(9600); 27 | 28 | unsigned long const start = millis(); 29 | for(unsigned long now = millis(); !Serial && ((now - start) < 5000); now = millis()) { }; 30 | 31 | flash.begin(); 32 | 33 | W25Q16DV_Id const id = flash.readId(); 34 | 35 | char msg[32] = {0}; 36 | snprintf(msg, sizeof(msg), "ID: %02X %02X %02X", id.manufacturer_id, id.memory_type, id.capacity); 37 | Serial.println(msg); 38 | 39 | 40 | std::array data_write = {0}, 41 | data_read = {0}; 42 | 43 | /************************************************************************************** 44 | * CHIP ERASE 45 | **************************************************************************************/ 46 | 47 | Serial.println("Erasing chip"); 48 | 49 | flash.eraseChip(); 50 | 51 | flash.read(0x000100, data_read.data(), data_read.size()); 52 | 53 | if(std::all_of(data_read.begin(), data_read.end(), [](uint8_t const elem) { return (elem == 0xFF); })) { 54 | Serial.println("Comparison OK"); 55 | } else { 56 | Serial.println("Comparison FAIL"); 57 | } 58 | printArray("RD: ", data_read); 59 | 60 | /************************************************************************************** 61 | * PAGE PROGRAM 62 | **************************************************************************************/ 63 | 64 | Serial.println("Programming page"); 65 | 66 | /* Initialize data */ 67 | std::transform(data_write.begin(), data_write.end(), data_write.begin(), 68 | [](uint8_t const elem) 69 | { 70 | static uint8_t i = 0; 71 | return i++; 72 | }); 73 | 74 | flash.programPage(0x000100, data_write.data(), data_write.size()); 75 | flash.read (0x000100, data_read.data(), data_read.size()); 76 | 77 | printArray("WR: ", data_write); 78 | printArray("RD: ", data_read); 79 | 80 | if(std::equal(data_write.begin(), data_write.end(), data_read.begin())) { 81 | Serial.println("Comparison OK"); 82 | } else { 83 | Serial.println("Comparison FAIL"); 84 | } 85 | 86 | /************************************************************************************** 87 | * SECTOR ERASE 88 | **************************************************************************************/ 89 | 90 | Serial.println("Sector erase"); 91 | 92 | /* Erase the whole first sector (4 kB) */ 93 | flash.eraseSector(0x000000); 94 | 95 | /* Set the comparison buffer to 0xFF since we now need to compare if every value is 0xFF */ 96 | std::fill(data_write.begin(), data_write.end(), 0xFF); 97 | 98 | /* Read the data */ 99 | flash.read(0x000100, data_read.data(), data_read.size()); 100 | printArray("RD: ", data_read); 101 | 102 | /* Compare the two data buffers */ 103 | if(std::all_of(data_read.begin(), data_read.end(), [](uint8_t const elem) { return (elem == 0xFF); })) { 104 | Serial.println("Comparison OK"); 105 | } else { 106 | Serial.println("Comparison FAIL"); 107 | } 108 | } 109 | 110 | void loop() 111 | { 112 | 113 | } 114 | 115 | /************************************************************************************** 116 | * HELPER 117 | **************************************************************************************/ 118 | 119 | void printArray(char const * desc, std::array arr) 120 | { 121 | Serial.print(desc); 122 | 123 | std::for_each(arr.begin(), arr.end(), 124 | [](uint8_t const elem) 125 | { 126 | Serial.print(elem, HEX); 127 | Serial.print(" "); 128 | }); 129 | 130 | Serial.println(); 131 | } 132 | -------------------------------------------------------------------------------- /examples/SPIFFSDirectories/SPIFFSDirectories.ino: -------------------------------------------------------------------------------- 1 | /* SPIFFSDirectories.ino 2 | * 3 | * This sketch demonstrates how to use directories (as much 4 | * as is supported by SPIFFS). 5 | * 6 | * Alexander Entinger 7 | */ 8 | 9 | /************************************************************************************** 10 | * INCLUDE 11 | **************************************************************************************/ 12 | 13 | #include 14 | 15 | /************************************************************************************** 16 | * SETUP/LOOP 17 | **************************************************************************************/ 18 | 19 | void setup() 20 | { 21 | Serial.begin(9600); 22 | 23 | unsigned long const start = millis(); 24 | for(unsigned long now = millis(); !Serial && ((now - start) < 5000); now = millis()) { }; 25 | 26 | flash.begin(); 27 | 28 | Serial.println("Mounting ..."); 29 | int res = filesystem.mount(); 30 | if(res != SPIFFS_OK && res != SPIFFS_ERR_NOT_A_FS) { 31 | Serial.println("mount() failed with error code "); Serial.println(res); return; 32 | } 33 | 34 | /* Note: SPIFFS is a flat file system; it doesn't have directories. */ 35 | File file_A = filesystem.open("/testfile_A.txt", CREATE | WRITE_ONLY | TRUNCATE); 36 | File file_B = filesystem.open("/testdir/testfile_B.txt", CREATE | WRITE_ONLY | TRUNCATE); 37 | 38 | Serial.println("opendir('/')"); 39 | Directory dir = filesystem.opendir("/"); 40 | DirEntry entry; 41 | while(dir.readdir(entry)) { 42 | if (entry.isFile()) Serial.print(" F "); 43 | else if(entry.isDirectory()) Serial.print(" D "); 44 | Serial.print(entry.name()); 45 | Serial.println(); 46 | } 47 | dir.closedir(); 48 | 49 | Serial.println("Unmounting ..."); 50 | filesystem.unmount(); 51 | } 52 | 53 | void loop() 54 | { 55 | 56 | } 57 | -------------------------------------------------------------------------------- /examples/SPIFFSFormat/SPIFFSFormat.ino: -------------------------------------------------------------------------------- 1 | /* SPIFFSFormat.ino 2 | * 3 | * This sketch erases the complete flash and formats it for 4 | * usage with the SPIFFS (SPI Flash File System). 5 | * 6 | * Alexander Entinger 7 | */ 8 | 9 | /************************************************************************************** 10 | * INCLUDE 11 | **************************************************************************************/ 12 | 13 | #include 14 | 15 | /************************************************************************************** 16 | * SETUP/LOOP 17 | **************************************************************************************/ 18 | 19 | void setup() 20 | { 21 | Serial.begin(9600); 22 | 23 | unsigned long const start = millis(); 24 | for(unsigned long now = millis(); !Serial && ((now - start) < 5000); now = millis()) { }; 25 | 26 | flash.begin(); 27 | 28 | Serial.println("Erasing chip ..."); 29 | flash.eraseChip(); 30 | 31 | Serial.println("Mounting ..."); 32 | int res = filesystem.mount(); 33 | if(res != SPIFFS_OK && res != SPIFFS_ERR_NOT_A_FS) { 34 | Serial.println("mount() failed with error code "); Serial.println(res); return; 35 | } 36 | 37 | Serial.println("Unmounting ..."); 38 | filesystem.unmount(); 39 | 40 | Serial.println("Formatting ..."); 41 | res = filesystem.format(); 42 | if(res != SPIFFS_OK) { 43 | Serial.println("format() failed with error code "); Serial.println(res); return; 44 | } 45 | 46 | Serial.println("Mounting ..."); 47 | res = filesystem.mount(); 48 | if(res != SPIFFS_OK) { 49 | Serial.println("mount() failed with error code "); Serial.println(res); return; 50 | } 51 | 52 | Serial.println("Checking ..."); 53 | res = filesystem.check(); 54 | if(res != SPIFFS_OK) { 55 | Serial.println("check() failed with error code "); Serial.println(res); return; 56 | } 57 | 58 | Serial.println("Retrieving filesystem info ..."); 59 | unsigned int bytes_total = 0, 60 | bytes_used = 0; 61 | res = filesystem.info(bytes_total, bytes_used); 62 | if(res != SPIFFS_OK) { 63 | Serial.println("check() failed with error code "); Serial.println(res); return; 64 | } else { 65 | char msg[64] = {0}; 66 | snprintf(msg, sizeof(msg), "SPIFFS Info:\nBytes Total: %d\nBytes Used: %d", bytes_total, bytes_used); 67 | Serial.println(msg); 68 | } 69 | 70 | Serial.println("Unmounting ..."); 71 | filesystem.unmount(); 72 | } 73 | 74 | void loop() 75 | { 76 | 77 | } 78 | -------------------------------------------------------------------------------- /examples/SPIFFSUsage/SPIFFSUsage.ino: -------------------------------------------------------------------------------- 1 | /* SPIFFSUsage.ino 2 | * 3 | * This sketch demonstrates various file operations utilizing 4 | * the Arduino MKR MEM Shield port for the SPIFFS. 5 | * 6 | * Alexander Entinger 7 | */ 8 | 9 | /************************************************************************************** 10 | * INCLUDE 11 | **************************************************************************************/ 12 | 13 | #include 14 | 15 | /************************************************************************************** 16 | * CONSTANTS 17 | **************************************************************************************/ 18 | 19 | /* A pangram is a sentence using every letter of a given alphabet at least once. */ 20 | static char const PANGRAM[] = "The quick brown fox jumps over the lazy dog."; 21 | 22 | /************************************************************************************** 23 | * SETUP/LOOP 24 | **************************************************************************************/ 25 | 26 | void setup() { 27 | Serial.begin(9600); 28 | 29 | unsigned long const start = millis(); 30 | for(unsigned long now = millis(); !Serial && ((now - start) < 5000); now = millis()) { }; 31 | 32 | flash.begin(); 33 | 34 | Serial.println("Mounting ..."); 35 | if(SPIFFS_OK != filesystem.mount()) { 36 | Serial.println("mount() failed with error code "); Serial.println(filesystem.err()); return; 37 | } 38 | 39 | 40 | Serial.println("Checking ..."); 41 | if(SPIFFS_OK != filesystem.check()) { 42 | Serial.println("check() failed with error code "); Serial.println(filesystem.err()); return; 43 | } 44 | 45 | Serial.print("Checking for file ... "); 46 | File fnf = filesystem.open("404.txt", READ_ONLY); 47 | if (!fnf) { 48 | Serial.println(" 404.txt does not exist."); 49 | } 50 | 51 | Serial.print("Checking for file ... "); 52 | File fox = filesystem.open("fox.txt", READ_ONLY); 53 | if (fox) { 54 | Serial.println(" fox.txt exists. It will be overwritten."); 55 | } 56 | 57 | Serial.println("Writing ..."); 58 | /* Create file if it doesn't exist (SPIFFS_CREAT) and open in 59 | * write only mode (SPIFFS_WRONLY). If the file does exist 60 | * delete the existing content (SPIFFS_TRUNC). 61 | */ 62 | File file = filesystem.open("fox.txt", CREATE | READ_WRITE| TRUNCATE); 63 | 64 | int const bytes_to_write = strlen(PANGRAM); 65 | int const bytes_written = file.write((void *)PANGRAM, bytes_to_write); 66 | 67 | if(bytes_written != bytes_to_write) { 68 | Serial.println("write() failed with error code "); Serial.println(filesystem.err()); return; 69 | } else { 70 | Serial.print(bytes_written); 71 | Serial.println(" bytes written"); 72 | } 73 | 74 | 75 | Serial.println("Retrieving filesystem info ..."); 76 | unsigned int bytes_total = 0, 77 | bytes_used = 0; 78 | if(SPIFFS_OK != filesystem.info(bytes_total, bytes_used)) { 79 | Serial.println("check() failed with error code "); Serial.println(filesystem.err()); return; 80 | } else { 81 | char msg[64] = {0}; 82 | snprintf(msg, sizeof(msg), "SPIFFS Info:\nBytes Total: %d\nBytes Used: %d", bytes_total, bytes_used); 83 | Serial.println(msg); 84 | } 85 | 86 | 87 | Serial.println("Reading ..."); 88 | file.lseek(0, START); /* Rewind file pointer to the start */ 89 | 90 | char buf[64] = {0}; 91 | int const bytes_read = file.read(buf, sizeof(buf)); 92 | buf[bytes_read] = '\0'; 93 | 94 | file.close(); 95 | Serial.print("["); Serial.print(bytes_read); Serial.print("] "); 96 | Serial.println(buf); 97 | 98 | 99 | Serial.println("Unmounting ..."); 100 | filesystem.unmount(); 101 | } 102 | 103 | void loop() { 104 | 105 | } 106 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Arduino_MKRMEM 3 | ####################################### 4 | 5 | ####################################### 6 | # Class (KEYWORD1) 7 | ####################################### 8 | 9 | Arduino_SPIFFS KEYWORD1 10 | Arduino_W25Q16DV KEYWORD1 11 | File KEYWORD1 12 | Directory KEYWORD1 13 | DirEntry KEYWORD1 14 | 15 | ####################################### 16 | # Methods and Functions (KEYWORD2) 17 | ####################################### 18 | 19 | ####################################### 20 | # Arduino_SPIFFS 21 | ####################################### 22 | mount KEYWORD2 23 | mounted KEYWORD2 24 | unmount KEYWORD2 25 | format KEYWORD2 26 | check KEYWORD2 27 | info KEYWORD2 28 | err KEYWORD2 29 | clearerr KEYWORD2 30 | create KEYWORD2 31 | open KEYWORD2 32 | read KEYWORD2 33 | write KEYWORD2 34 | lseek KEYWORD2 35 | eof KEYWORD2 36 | tell KEYWORD2 37 | close KEYWORD2 38 | remove KEYWORD2 39 | fremove KEYWORD2 40 | stat KEYWORD2 41 | fstat KEYWORD2 42 | fflush KEYWORD2 43 | rename KEYWORD2 44 | opendir KEYWORD2 45 | closedir KEYWORD2 46 | readdir KEYWORD2 47 | 48 | ####################################### 49 | # Arduino_W25Q16DV 50 | ####################################### 51 | readId KEYWORD2 52 | isBusy KEYWORD2 53 | read KEYWORD2 54 | programPage KEYWORD2 55 | eraseSector KEYWORD2 56 | eraseChip KEYWORD2 57 | 58 | ####################################### 59 | # Constants (LITERAL1) 60 | ####################################### 61 | 62 | APPEND LITERAL1 63 | TRUNCATE LITERAL1 64 | CREATE LITERAL1 65 | READ_ONLY LITERAL1 66 | WRITE_ONLY LITERAL1 67 | READ_WRITE LITERAL1 68 | DIRECT LITERAL1 69 | EXCLUSIVE LITERAL1 70 | 71 | START LITERAL1 72 | CURRENT LITERAL1 73 | END LITERAL1 74 | 75 | SPIFFS_APPEND LITERAL1 76 | SPIFFS_TRUNC LITERAL1 77 | SPIFFS_CREAT LITERAL1 78 | SPIFFS_RDONLY LITERAL1 79 | SPIFFS_WRONLY LITERAL1 80 | SPIFFS_RDWR LITERAL1 81 | SPIFFS_DIRECT LITERAL1 82 | SPIFFS_EXCL LITERAL1 83 | 84 | SPIFFS_SEEK_SET LITERAL1 85 | SPIFFS_SEEK_CUR LITERAL1 86 | SPIFFS_SEEK_END LITERAL1 87 | 88 | SPIFFS_TYPE_FILE LITERAL1 89 | SPIFFS_TYPE_DIR LITERAL1 90 | SPIFFS_TYPE_HARD_LINK LITERAL1 91 | SPIFFS_TYPE_SOFT_LINK LITERAL1 92 | 93 | SPIFFS_OK LITERAL1 94 | SPIFFS_ERR_NOT_MOUNTED LITERAL1 95 | SPIFFS_ERR_FULL LITERAL1 96 | SPIFFS_ERR_NOT_FOUND LITERAL1 97 | SPIFFS_ERR_END_OF_OBJECT LITERAL1 98 | SPIFFS_ERR_DELETED LITERAL1 99 | SPIFFS_ERR_NOT_FINALIZED LITERAL1 100 | SPIFFS_ERR_NOT_INDEX LITERAL1 101 | SPIFFS_ERR_OUT_OF_FILE_DESCS LITERAL1 102 | SPIFFS_ERR_FILE_CLOSED LITERAL1 103 | SPIFFS_ERR_FILE_DELETED LITERAL1 104 | SPIFFS_ERR_BAD_DESCRIPTOR LITERAL1 105 | SPIFFS_ERR_IS_INDEX LITERAL1 106 | SPIFFS_ERR_IS_FREE LITERAL1 107 | SPIFFS_ERR_INDEX_SPAN_MISMATCH LITERAL1 108 | SPIFFS_ERR_DATA_SPAN_MISMATCH LITERAL1 109 | SPIFFS_ERR_INDEX_REF_FREE LITERAL1 110 | SPIFFS_ERR_INDEX_REF_LU LITERAL1 111 | SPIFFS_ERR_INDEX_REF_INVALID LITERAL1 112 | SPIFFS_ERR_INDEX_FREE LITERAL1 113 | SPIFFS_ERR_INDEX_LU LITERAL1 114 | SPIFFS_ERR_INDEX_INVALID LITERAL1 115 | SPIFFS_ERR_NOT_WRITABLE LITERAL1 116 | SPIFFS_ERR_NOT_READABLE LITERAL1 117 | SPIFFS_ERR_CONFLICTING_NAME LITERAL1 118 | SPIFFS_ERR_NOT_CONFIGURED LITERAL1 119 | SPIFFS_ERR_NOT_A_FS LITERAL1 120 | SPIFFS_ERR_MOUNTED LITERAL1 121 | SPIFFS_ERR_ERASE_FAIL LITERAL1 122 | SPIFFS_ERR_MAGIC_NOT_POSSIBLE LITERAL1 123 | SPIFFS_ERR_NO_DELETED_BLOCKS LITERAL1 124 | SPIFFS_ERR_FILE_EXISTS LITERAL1 125 | SPIFFS_ERR_NOT_A_FILE LITERAL1 126 | SPIFFS_ERR_RO_NOT_IMPL LITERAL1 127 | SPIFFS_ERR_RO_ABORTED_OPERATION LITERAL1 128 | SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS LITERAL1 129 | SPIFFS_ERR_PROBE_NOT_A_FS LITERAL1 130 | SPIFFS_ERR_NAME_TOO_LONG LITERAL1 131 | SPIFFS_ERR_IX_MAP_UNMAPPED LITERAL1 132 | SPIFFS_ERR_IX_MAP_MAPPED LITERAL1 133 | SPIFFS_ERR_IX_MAP_BAD_RANGE LITERAL1 134 | SPIFFS_ERR_SEEK_BOUNDS LITERAL1 135 | SPIFFS_ERR_INTERNAL LITERAL1 136 | SPIFFS_ERR_TEST LITERAL1 137 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Arduino_MKRMEM 2 | version=1.1.0 3 | author=Alexander Entinger 4 | maintainer=Arduino 5 | sentence=SPIFFS on W25Q16DV for Arduino MKR MEM Shield. 6 | paragraph=Arduino library for the W25Q16DV flash on the MKR MEM Shield utilizing the SPIFFS flash file system. 7 | category=Communication 8 | url=https://github.com/arduino-libraries/Arduino_MKRMEM 9 | architectures=samd 10 | -------------------------------------------------------------------------------- /src/Arduino_MKRMEM.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef MKRMEM_H_ 20 | #define MKRMEM_H_ 21 | 22 | /************************************************************************************** 23 | * INCLUDE 24 | **************************************************************************************/ 25 | 26 | #include "Arduino_SPIFFS.h" 27 | #include "Arduino_W25Q16DV.h" 28 | 29 | #endif /* MKRMEM_H_ */ 30 | -------------------------------------------------------------------------------- /src/Arduino_SPIFFS.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | /************************************************************************************** 20 | * INCLUDE 21 | **************************************************************************************/ 22 | 23 | #include "Arduino_SPIFFS.h" 24 | 25 | #include "Arduino_W25Q16DV.h" 26 | 27 | /************************************************************************************** 28 | * GLOBAL VARIABLES 29 | **************************************************************************************/ 30 | 31 | static spiffs_config cfg; 32 | 33 | /************************************************************************************** 34 | * CTOR/DTOR 35 | **************************************************************************************/ 36 | 37 | Arduino_SPIFFS::Arduino_SPIFFS(spiffs_read read_func, spiffs_write write_func, spiffs_erase erase_func) 38 | { 39 | cfg.hal_read_f = read_func; 40 | cfg.hal_write_f = write_func; 41 | cfg.hal_erase_f = erase_func; 42 | } 43 | 44 | /************************************************************************************** 45 | * PUBLIC MEMBER FUNCTIONS 46 | **************************************************************************************/ 47 | 48 | int Arduino_SPIFFS::mount() 49 | { 50 | return SPIFFS_mount(&_fs, 51 | &cfg, 52 | _spiffs_work_buf, 53 | _spiffs_fds, 54 | sizeof(_spiffs_fds), 55 | _spiffs_cache_buf, 56 | sizeof(_spiffs_cache_buf), 57 | 0); 58 | } 59 | 60 | File Arduino_SPIFFS::open(const char *path, uint16_t const flags) 61 | { 62 | spiffs_file const fh = SPIFFS_open(&_fs, path, flags, 0); 63 | return File::create(fh); 64 | } 65 | 66 | Directory Arduino_SPIFFS::opendir(const char *name) 67 | { 68 | spiffs_DIR d; 69 | SPIFFS_opendir(&_fs, name, &d); 70 | return Directory::create(&d); 71 | } 72 | 73 | /************************************************************************************** 74 | * EXTERN DECLARATION 75 | **************************************************************************************/ 76 | 77 | Arduino_SPIFFS filesystem( 78 | #if defined(SPIFFS_USE_W25Q16DV_FLASH) 79 | w25q16_spi_read, w25q16_spi_write, w25q16_spi_erase 80 | #endif 81 | ); 82 | -------------------------------------------------------------------------------- /src/Arduino_SPIFFS.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef ARDUINO_SPIFFS_H_ 20 | #define ARDUINO_SPIFFS_H_ 21 | 22 | /************************************************************************************** 23 | * INCLUDE 24 | **************************************************************************************/ 25 | 26 | #include 27 | 28 | #include "Arduino_SPIFFS_File.h" 29 | #include "Arduino_SPIFFS_Directory.h" 30 | 31 | extern "C" 32 | { 33 | #include "spiffs.h" 34 | } 35 | 36 | /************************************************************************************** 37 | * CONFIGURATION 38 | **************************************************************************************/ 39 | 40 | #define SPIFFS_USE_W25Q16DV_FLASH 41 | 42 | /************************************************************************************** 43 | * SANITY CHECK 44 | **************************************************************************************/ 45 | 46 | #if !defined(SPIFFS_USE_W25Q16DV_FLASH) 47 | #error "Enable at least one flash chip for usage with Arduino_SPIFFS" 48 | #endif 49 | 50 | /************************************************************************************** 51 | * STATIC ASSERTIONS 52 | **************************************************************************************/ 53 | 54 | static_assert(sizeof(u8_t) == sizeof(byte), "Arduino SPIFFS Wrapper - u8_t != byte - possible loss of data"); 55 | static_assert(sizeof(s32_t) == sizeof(int), "Arduino SPIFFS Wrapper - s32_t != int32_t - possible loss of data"); 56 | static_assert(sizeof(u32_t) == sizeof(unsigned int), "Arduino SPIFFS Wrapper - s32_t != int32_t - possible loss of data"); 57 | 58 | /************************************************************************************** 59 | * CONSTANTS 60 | **************************************************************************************/ 61 | 62 | static uint16_t constexpr APPEND = SPIFFS_O_APPEND; 63 | static uint16_t constexpr TRUNCATE = SPIFFS_O_TRUNC; 64 | static uint16_t constexpr CREATE = SPIFFS_O_CREAT; 65 | static uint16_t constexpr READ_ONLY = SPIFFS_O_RDONLY; 66 | static uint16_t constexpr WRITE_ONLY = SPIFFS_O_WRONLY; 67 | static uint16_t constexpr READ_WRITE = SPIFFS_O_RDWR; 68 | #ifdef DIRECT 69 | #undef DIRECT 70 | #endif 71 | static uint16_t constexpr DIRECT = SPIFFS_O_DIRECT; /* Any writes to the filehandle will never be cached but flushed directly */ 72 | static uint16_t constexpr EXCLUSIVE = SPIFFS_O_EXCL; 73 | 74 | /************************************************************************************** 75 | * CLASS DECLARATION 76 | **************************************************************************************/ 77 | 78 | class Arduino_SPIFFS 79 | { 80 | public: 81 | 82 | Arduino_SPIFFS(spiffs_read read_func, spiffs_write write_func, spiffs_erase erase_func); 83 | 84 | 85 | int mount (); 86 | inline byte mounted () { return SPIFFS_mounted(&_fs); } 87 | inline void unmount () { SPIFFS_unmount(&_fs); } 88 | 89 | inline int format () { return SPIFFS_format(&_fs); } 90 | inline int check () { return SPIFFS_check(&_fs); } 91 | inline int info (unsigned int & total, unsigned int & used) { return SPIFFS_info(&_fs, reinterpret_cast(&total), reinterpret_cast(&used)); } 92 | 93 | inline int err () { return SPIFFS_errno(&_fs); } 94 | inline void clearerr() { SPIFFS_clearerr(&_fs); } 95 | 96 | inline int create (const char * path) { return SPIFFS_creat(&_fs, path, 0); } 97 | File open (const char * path, uint16_t const flags); 98 | inline int remove (const char * path) { return SPIFFS_remove(&_fs, path); } 99 | inline int rename (const char * old, const char * newPath) { return SPIFFS_rename(&_fs, old, newPath); } 100 | 101 | inline int create (String const & path) { return create(path.c_str()); } 102 | inline File open (String const & path, uint16_t const flags) { return open(path.c_str(), flags); } 103 | inline int remove (String const & path) { return remove(path.c_str()); } 104 | inline int rename (String const & old, String const & newPath) { return rename(old.c_str(), newPath.c_str()); } 105 | 106 | Directory opendir (const char * name); 107 | inline Directory opendir (String const & name) { return opendir(name.c_str()); } 108 | 109 | 110 | /* In order to allow access to the private functions 111 | * read, write, lseek, eof, tell, close, remove, flush 112 | * we allow Arduino_SPIFFS_File to be a 'friend' of 113 | * 'Arduino_SPIFFS'. Another alternative would be to 114 | * make those functions public but what would users do 115 | * without the availability of a file handle? 116 | */ 117 | friend class Arduino_SPIFFS_File; 118 | 119 | 120 | private: 121 | 122 | spiffs _fs; 123 | 124 | u8_t _spiffs_work_buf[SPIFFS_CFG_LOG_PAGE_SZ(0)*2]; 125 | u8_t _spiffs_fds[32*4]; 126 | u8_t _spiffs_cache_buf[(SPIFFS_CFG_LOG_PAGE_SZ(0)+32)*4]; 127 | 128 | 129 | inline int read (spiffs_file fh, void * buf, int len) { return SPIFFS_read(&_fs, fh, buf, len); } 130 | inline int write (spiffs_file fh, void * buf, int len) { return SPIFFS_write(&_fs, fh, buf, len); } 131 | inline int lseek (spiffs_file fh, int offs, int whence) { return SPIFFS_lseek(&_fs, fh, offs, whence); } 132 | inline int eof (spiffs_file fh) { return SPIFFS_eof(&_fs, fh); } 133 | inline int tell (spiffs_file fh) { return SPIFFS_tell(&_fs, fh); } 134 | inline int close (spiffs_file fh) { return SPIFFS_close(&_fs, fh); } 135 | inline int remove(spiffs_file fh) { return SPIFFS_fremove(&_fs, fh); } 136 | inline int flush (spiffs_file fh) { return SPIFFS_fflush(&_fs, fh); } 137 | 138 | }; 139 | 140 | /************************************************************************************** 141 | * EXTERN DEFINITION 142 | **************************************************************************************/ 143 | 144 | extern Arduino_SPIFFS filesystem; 145 | 146 | #endif /* ARDUINO_SPIFFS_H_ */ 147 | -------------------------------------------------------------------------------- /src/Arduino_SPIFFS_Directory.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | /************************************************************************************** 20 | * INCLUDE 21 | **************************************************************************************/ 22 | 23 | #include "Arduino_SPIFFS_Directory.h" 24 | 25 | /************************************************************************************** 26 | * CTOR/DTOR 27 | **************************************************************************************/ 28 | 29 | Arduino_SPIFFS_Directory::Arduino_SPIFFS_Directory(spiffs_DIR const * dir) 30 | { 31 | memcpy(&_dir, dir, sizeof(_dir)); 32 | } 33 | 34 | Arduino_SPIFFS_Directory::~Arduino_SPIFFS_Directory() 35 | { 36 | closedir(); 37 | } 38 | 39 | /************************************************************************************** 40 | * PUBLIC MEMBER FUNCTIONS 41 | **************************************************************************************/ 42 | 43 | bool Arduino_SPIFFS_Directory::closedir() 44 | { 45 | return (SPIFFS_OK == SPIFFS_closedir(&_dir)); 46 | } 47 | 48 | bool Arduino_SPIFFS_Directory::readdir(DirEntry & dir_entry) 49 | { 50 | spiffs_dirent de; 51 | 52 | if(SPIFFS_readdir(&_dir, &de)) { 53 | dir_entry = DirEntry::create(&de); 54 | return true; 55 | } else { 56 | return false; 57 | } 58 | } 59 | 60 | Arduino_SPIFFS_Directory Arduino_SPIFFS_Directory::operator = (Arduino_SPIFFS_Directory const & other) 61 | { 62 | memcpy(&_dir, other.getDir(), sizeof(_dir)); 63 | } 64 | 65 | Arduino_SPIFFS_Directory Arduino_SPIFFS_Directory::create(spiffs_DIR const * dir) 66 | { 67 | return Arduino_SPIFFS_Directory(dir); 68 | } 69 | -------------------------------------------------------------------------------- /src/Arduino_SPIFFS_Directory.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef ARDUINO_SPIFFS_DIRECTORY_H_ 20 | #define ARDUINO_SPIFFS_DIRECTORY_H_ 21 | 22 | /************************************************************************************** 23 | * INCLUDE 24 | **************************************************************************************/ 25 | 26 | #include "Arduino_SPIFFS_DirectoryEntry.h" 27 | 28 | extern "C" 29 | { 30 | #include "spiffs.h" 31 | } 32 | 33 | /************************************************************************************** 34 | * CLASS DECLARATION 35 | **************************************************************************************/ 36 | 37 | class Arduino_SPIFFS_Directory 38 | { 39 | 40 | public: 41 | 42 | ~Arduino_SPIFFS_Directory(); 43 | 44 | 45 | bool closedir(); 46 | bool readdir (DirEntry & dir_entry); 47 | 48 | 49 | static Arduino_SPIFFS_Directory create(spiffs_DIR const * dir); 50 | 51 | 52 | Arduino_SPIFFS_Directory operator = (Arduino_SPIFFS_Directory const & other); 53 | inline spiffs_DIR const * getDir() const { return &_dir; } 54 | 55 | 56 | private: 57 | 58 | spiffs_DIR _dir; 59 | 60 | Arduino_SPIFFS_Directory(spiffs_DIR const * dir); 61 | 62 | }; 63 | 64 | /************************************************************************************** 65 | * TYPEDEF 66 | **************************************************************************************/ 67 | 68 | typedef Arduino_SPIFFS_Directory Directory; /* Shorten the class name down because 'Arduino_SPIFFS_Directory' makes for a very long type name */ 69 | 70 | #endif /* ARDUINO_SPIFFS_DIRECTORY_H_ */ 71 | -------------------------------------------------------------------------------- /src/Arduino_SPIFFS_DirectoryEntry.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | /************************************************************************************** 20 | * INCLUDE 21 | **************************************************************************************/ 22 | 23 | #include "Arduino_SPIFFS_DirectoryEntry.h" 24 | 25 | /************************************************************************************** 26 | * CTOR/DTOR 27 | **************************************************************************************/ 28 | 29 | Arduino_SPIFFS_DirectoryEntry::Arduino_SPIFFS_DirectoryEntry(spiffs_dirent const * dirent) 30 | { 31 | if(dirent) { 32 | memcpy(&_dirent, dirent, sizeof(_dirent)); 33 | } 34 | } 35 | 36 | /************************************************************************************** 37 | * PUBLIC MEMBER FUNCTIONS 38 | **************************************************************************************/ 39 | 40 | String Arduino_SPIFFS_DirectoryEntry::name() 41 | { 42 | return String(reinterpret_cast(&(_dirent.name))); 43 | } 44 | 45 | bool Arduino_SPIFFS_DirectoryEntry::isFile() 46 | { 47 | return (_dirent.type == SPIFFS_TYPE_FILE); 48 | } 49 | 50 | bool Arduino_SPIFFS_DirectoryEntry::isDirectory() 51 | { 52 | return (_dirent.type == SPIFFS_TYPE_DIR); 53 | } 54 | 55 | Arduino_SPIFFS_DirectoryEntry Arduino_SPIFFS_DirectoryEntry::create(spiffs_dirent const * dirent) 56 | { 57 | return Arduino_SPIFFS_DirectoryEntry(dirent); 58 | } 59 | 60 | Arduino_SPIFFS_DirectoryEntry Arduino_SPIFFS_DirectoryEntry::operator = (Arduino_SPIFFS_DirectoryEntry const & other) 61 | { 62 | memcpy(&_dirent, other.getDirEnt(), sizeof(_dirent)); 63 | } 64 | -------------------------------------------------------------------------------- /src/Arduino_SPIFFS_DirectoryEntry.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef ARDUINO_SPIFFS_DIRECTORYENTRY_H_ 20 | #define ARDUINO_SPIFFS_DIRECTORYENTRY_H_ 21 | 22 | /************************************************************************************** 23 | * INCLUDE 24 | **************************************************************************************/ 25 | 26 | #include 27 | 28 | extern "C" 29 | { 30 | #include "spiffs.h" 31 | } 32 | 33 | /************************************************************************************** 34 | * CLASS DECLARATION 35 | **************************************************************************************/ 36 | 37 | class Arduino_SPIFFS_DirectoryEntry 38 | { 39 | 40 | public: 41 | 42 | Arduino_SPIFFS_DirectoryEntry() : Arduino_SPIFFS_DirectoryEntry(0) { } 43 | Arduino_SPIFFS_DirectoryEntry(spiffs_dirent const * dirent); 44 | 45 | 46 | String name(); 47 | bool isFile(); 48 | bool isDirectory(); 49 | 50 | 51 | static Arduino_SPIFFS_DirectoryEntry create(spiffs_dirent const * dirent); 52 | 53 | 54 | Arduino_SPIFFS_DirectoryEntry operator = (Arduino_SPIFFS_DirectoryEntry const & other); 55 | inline spiffs_dirent const * getDirEnt() const { return &_dirent; } 56 | 57 | 58 | private: 59 | 60 | spiffs_dirent _dirent; 61 | 62 | }; 63 | 64 | /************************************************************************************** 65 | * TYPEDEF 66 | **************************************************************************************/ 67 | 68 | typedef Arduino_SPIFFS_DirectoryEntry DirEntry; /* Shorten the class name down because 'Arduino_SPIFFS_DirectoryEntry' makes for a very long type name */ 69 | 70 | #endif /* ARDUINO_SPIFFS_DIRECTORYENTRY_H_ */ 71 | -------------------------------------------------------------------------------- /src/Arduino_SPIFFS_File.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | /************************************************************************************** 20 | * INCLUDE 21 | **************************************************************************************/ 22 | 23 | #include "Arduino_SPIFFS_File.h" 24 | 25 | #include 26 | 27 | /************************************************************************************** 28 | * EXTERN DEFINITION 29 | **************************************************************************************/ 30 | 31 | extern Arduino_SPIFFS filesystem; 32 | 33 | /************************************************************************************** 34 | * CTOR/DTOR 35 | **************************************************************************************/ 36 | 37 | Arduino_SPIFFS_File::Arduino_SPIFFS_File(spiffs_file const fh) 38 | : _fh(fh) 39 | { 40 | 41 | } 42 | 43 | Arduino_SPIFFS_File::~Arduino_SPIFFS_File() 44 | { 45 | close(); 46 | } 47 | 48 | /************************************************************************************** 49 | * OPERATOR OVERRIDES 50 | **************************************************************************************/ 51 | 52 | Arduino_SPIFFS_File::operator bool() 53 | { 54 | return _fh >= 0; 55 | } 56 | 57 | /************************************************************************************** 58 | * PUBLIC MEMBER FUNCTIONS 59 | **************************************************************************************/ 60 | 61 | int Arduino_SPIFFS_File::read(void * buf, int len) 62 | { 63 | return filesystem.read(_fh, buf, len); 64 | } 65 | 66 | int Arduino_SPIFFS_File::write(void * buf, int len) 67 | { 68 | return filesystem.write(_fh, buf, len); 69 | } 70 | 71 | int Arduino_SPIFFS_File::lseek(int offs, int whence) 72 | { 73 | return filesystem.lseek(_fh, offs, whence); 74 | } 75 | 76 | int Arduino_SPIFFS_File::eof() 77 | { 78 | return filesystem.eof(_fh); 79 | } 80 | 81 | int Arduino_SPIFFS_File::tell() 82 | { 83 | return filesystem.tell(_fh); 84 | } 85 | 86 | int Arduino_SPIFFS_File::close() 87 | { 88 | return filesystem.close(_fh); 89 | } 90 | 91 | int Arduino_SPIFFS_File::remove() 92 | { 93 | return filesystem.remove(_fh); 94 | } 95 | 96 | int Arduino_SPIFFS_File::flush() 97 | { 98 | return filesystem.flush(_fh); 99 | } 100 | 101 | Arduino_SPIFFS_File Arduino_SPIFFS_File::create(spiffs_file const fh) 102 | { 103 | return Arduino_SPIFFS_File(fh); 104 | } 105 | 106 | Arduino_SPIFFS_File Arduino_SPIFFS_File::operator = (Arduino_SPIFFS_File const & other) 107 | { 108 | _fh = other.getFh(); 109 | } 110 | -------------------------------------------------------------------------------- /src/Arduino_SPIFFS_File.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef ARDUINO_SPIFFS_FILE_H_ 20 | #define ARDUINO_SPIFFS_FILE_H_ 21 | 22 | /************************************************************************************** 23 | * INCLUDE 24 | **************************************************************************************/ 25 | 26 | #include 27 | 28 | extern "C" 29 | { 30 | #include "spiffs.h" 31 | } 32 | 33 | /************************************************************************************** 34 | * CONSTANTS 35 | **************************************************************************************/ 36 | 37 | static int constexpr START = SPIFFS_SEEK_SET; /* Start to seek from the beginning of the file */ 38 | static int constexpr CURRENT = SPIFFS_SEEK_CUR; /* Start to seek from the current position of the file pointer */ 39 | static int constexpr END = SPIFFS_SEEK_END; /* Start to seek from the end of the file */ 40 | 41 | /************************************************************************************** 42 | * CLASS DECLARATION 43 | **************************************************************************************/ 44 | 45 | class Arduino_SPIFFS_File 46 | { 47 | 48 | public: 49 | 50 | ~Arduino_SPIFFS_File(); 51 | 52 | 53 | int read (void * buf, int len); 54 | int write (void * buf, int len); 55 | int lseek (int offs, int whence); 56 | int eof (); 57 | int tell (); 58 | int close (); 59 | int remove(); 60 | int flush (); 61 | 62 | 63 | static Arduino_SPIFFS_File create(spiffs_file const fh); 64 | 65 | 66 | Arduino_SPIFFS_File operator = (Arduino_SPIFFS_File const & other); 67 | operator bool (); 68 | inline spiffs_file getFh() const { return _fh; } 69 | 70 | 71 | private: 72 | 73 | spiffs_file _fh; 74 | 75 | Arduino_SPIFFS_File(spiffs_file const fh); 76 | 77 | }; 78 | 79 | /************************************************************************************** 80 | * TYPEDEF 81 | **************************************************************************************/ 82 | 83 | typedef Arduino_SPIFFS_File File; /* Shorten the class name down because 'Arduino_SPIFFS_File' makes for a very long type name */ 84 | 85 | #endif /* ARDUINO_SPIFFS_FILE_H_ */ 86 | -------------------------------------------------------------------------------- /src/Arduino_W25Q16DV.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | /************************************************************************************** 20 | * INCLUDE 21 | **************************************************************************************/ 22 | 23 | #include "Arduino_W25Q16DV.h" 24 | 25 | /************************************************************************************** 26 | * CONSTANTS 27 | **************************************************************************************/ 28 | 29 | static uint32_t const W25Q16DV_MAX_SPI_CLK = 104 * 1000 * 1000; /* 104 MHz */ 30 | static SPISettings const W25Q16DV_SPI_SETTINGS{W25Q16DV_MAX_SPI_CLK, MSBFIRST, SPI_MODE0}; 31 | 32 | 33 | static uint32_t const W25Q16DV_MAXREAD_SPI_CLK = 50 * 1000 * 1000; /* 50 MHz Max Read */ 34 | static SPISettings const W25Q16DV_SPIREAD_SETTINGS{W25Q16DV_MAXREAD_SPI_CLK, MSBFIRST, SPI_MODE0}; 35 | 36 | /************************************************************************************** 37 | * CTOR/DTOR 38 | **************************************************************************************/ 39 | 40 | Arduino_W25Q16DV::Arduino_W25Q16DV(SPIClass & spi, int const cs_pin) 41 | : _spi (spi) 42 | , _cs_pin(cs_pin) 43 | { 44 | 45 | } 46 | 47 | /************************************************************************************** 48 | * PUBLIC MEMBER FUNCTIONS 49 | **************************************************************************************/ 50 | 51 | void Arduino_W25Q16DV::begin() 52 | { 53 | _spi.begin(); 54 | pinMode(_cs_pin, OUTPUT); 55 | deselect(); 56 | } 57 | 58 | W25Q16DV_Id Arduino_W25Q16DV::readId() 59 | { 60 | W25Q16DV_Id id; 61 | 62 | select(); 63 | _spi.beginTransaction(W25Q16DV_SPI_SETTINGS); 64 | _spi.transfer(static_cast(W25Q16DV_Command::ReadJedecId)); 65 | id.manufacturer_id = _spi.transfer(0); 66 | id.memory_type = _spi.transfer(0); 67 | id.capacity = _spi.transfer(0); 68 | deselect(); 69 | 70 | return id; 71 | } 72 | 73 | bool Arduino_W25Q16DV::isBusy() 74 | { 75 | W25Q16DV_StatusReg1 status_reg_1; 76 | status_reg_1.byte = readStatusReg1(); 77 | return (status_reg_1.bit.BUSY == 1); 78 | } 79 | 80 | void Arduino_W25Q16DV::read(uint32_t const addr, uint8_t * buf, uint32_t const size) 81 | { 82 | while(isBusy()) { delayMicroseconds(1); } 83 | 84 | select(); 85 | _spi.beginTransaction(W25Q16DV_SPIREAD_SETTINGS); 86 | /* Command */ 87 | _spi.transfer(static_cast(W25Q16DV_Command::ReadData)); 88 | /* Address */ 89 | _spi.transfer(static_cast(addr >> 16)); 90 | _spi.transfer(static_cast(addr >> 8)); 91 | _spi.transfer(static_cast(addr >> 0)); 92 | /* Data */ 93 | for(uint32_t bytes_read = 0; bytes_read < size; bytes_read++) 94 | { 95 | buf[bytes_read] = _spi.transfer(0); 96 | } 97 | deselect(); 98 | } 99 | 100 | void Arduino_W25Q16DV::programPage(uint32_t const addr, uint8_t const * buf, uint32_t const size) 101 | { 102 | while(isBusy()) { delayMicroseconds(1); } 103 | 104 | enableWrite(); 105 | 106 | select(); 107 | _spi.beginTransaction(W25Q16DV_SPI_SETTINGS); 108 | /* Command */ 109 | _spi.transfer(static_cast(W25Q16DV_Command::PageProgram)); 110 | /* Address */ 111 | _spi.transfer(static_cast(addr >> 16)); 112 | _spi.transfer(static_cast(addr >> 8)); 113 | _spi.transfer(static_cast(addr >> 0)); 114 | /* Data */ 115 | for(uint32_t bytes_written = 0; bytes_written < size; bytes_written++) 116 | { 117 | _spi.transfer(buf[bytes_written]); 118 | } 119 | deselect(); 120 | } 121 | 122 | void Arduino_W25Q16DV::eraseSector(uint32_t const addr) 123 | { 124 | while(isBusy()) { delayMicroseconds(1); } 125 | 126 | enableWrite(); 127 | 128 | select(); 129 | _spi.beginTransaction(W25Q16DV_SPI_SETTINGS); 130 | /* Command */ 131 | _spi.transfer(static_cast(W25Q16DV_Command::SectorErase)); 132 | /* Address */ 133 | _spi.transfer(static_cast(addr >> 16)); 134 | _spi.transfer(static_cast(addr >> 8)); 135 | _spi.transfer(static_cast(addr >> 0)); 136 | deselect(); 137 | } 138 | 139 | void Arduino_W25Q16DV::eraseChip() 140 | { 141 | while(isBusy()) { delayMicroseconds(1); } 142 | 143 | enableWrite(); 144 | 145 | select(); 146 | _spi.beginTransaction(W25Q16DV_SPI_SETTINGS); 147 | _spi.transfer(static_cast(W25Q16DV_Command::ChipErase)); 148 | deselect(); 149 | 150 | /* In this instance wait within this function since it's so time consuming */ 151 | while(isBusy()) { delayMicroseconds(1); } 152 | } 153 | 154 | /************************************************************************************** 155 | * PRIVATE MEMBER FUNCTIONS 156 | **************************************************************************************/ 157 | 158 | void Arduino_W25Q16DV::select() 159 | { 160 | digitalWrite(_cs_pin, LOW); 161 | } 162 | 163 | void Arduino_W25Q16DV::deselect() 164 | { 165 | digitalWrite(_cs_pin, HIGH); 166 | } 167 | 168 | uint8_t Arduino_W25Q16DV::readStatusReg1() 169 | { 170 | select(); 171 | _spi.beginTransaction(W25Q16DV_SPI_SETTINGS); 172 | /* Command */ 173 | _spi.transfer(static_cast(W25Q16DV_Command::ReadStatusReg1)); 174 | /* Read Status Reg 1 */ 175 | uint8_t const status_reg_1 = _spi.transfer(0); 176 | deselect(); 177 | 178 | return status_reg_1; 179 | } 180 | 181 | void Arduino_W25Q16DV::enableWrite() 182 | { 183 | select(); 184 | _spi.beginTransaction(W25Q16DV_SPI_SETTINGS); 185 | _spi.transfer(static_cast(W25Q16DV_Command::WriteEnable)); 186 | deselect(); 187 | } 188 | 189 | /************************************************************************************** 190 | * EXTERN DECLARATION 191 | **************************************************************************************/ 192 | 193 | Arduino_W25Q16DV flash(SPI, 194 | #if defined(ARDUINO_EDGE_CONTROL) 195 | EDGE_CONTROL_W25Q16DV_CS_PIN 196 | #else 197 | MKRMEM_W25Q16DV_CS_PIN 198 | #endif 199 | ); 200 | 201 | /************************************************************************************** 202 | * FREE FUNCTION DEFINITION 203 | **************************************************************************************/ 204 | 205 | s32_t w25q16_spi_read(u32_t addr, u32_t size, u8_t * buf) 206 | { 207 | flash.read(addr, buf, size); 208 | return SPIFFS_OK; 209 | } 210 | 211 | s32_t w25q16_spi_write(u32_t addr, u32_t size, u8_t * buf) 212 | { 213 | flash.programPage(addr, buf, size); 214 | return SPIFFS_OK; 215 | } 216 | 217 | s32_t w25q16_spi_erase(u32_t addr, u32_t size) 218 | { 219 | flash.eraseSector(addr); 220 | return SPIFFS_OK; 221 | } 222 | -------------------------------------------------------------------------------- /src/Arduino_W25Q16DV.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Arduino. All right reserved. 3 | 4 | This library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | This library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | See the GNU Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with this library; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #ifndef ARDUINO_W25Q16DV_H_ 20 | #define ARDUINO_W25Q16DV_H_ 21 | 22 | /************************************************************************************** 23 | * INCLUDE 24 | **************************************************************************************/ 25 | 26 | #include 27 | 28 | #include 29 | 30 | extern "C" 31 | { 32 | #include "spiffs.h" 33 | } 34 | 35 | /************************************************************************************** 36 | * TYPEDEF 37 | **************************************************************************************/ 38 | 39 | enum class W25Q16DV_Command : uint8_t 40 | { 41 | PageProgram = 0x02, 42 | ReadData = 0x03, 43 | ReadStatusReg1 = 0x05, 44 | WriteEnable = 0x06, 45 | SectorErase = 0x20, 46 | ChipErase = 0x60, 47 | ReadJedecId = 0x9F, 48 | }; 49 | 50 | typedef struct 51 | { 52 | uint8_t manufacturer_id; 53 | uint8_t memory_type; 54 | uint8_t capacity; 55 | } W25Q16DV_Id; 56 | 57 | typedef union 58 | { 59 | struct { 60 | uint8_t BUSY : 1; 61 | uint8_t WEL : 1; 62 | uint8_t BP0 : 1; 63 | uint8_t BP1 : 1; 64 | uint8_t BP2 : 1; 65 | uint8_t TB : 1; 66 | uint8_t SEC : 1; 67 | uint8_t SRP0 : 1; 68 | } bit; 69 | uint8_t byte; 70 | } W25Q16DV_StatusReg1; 71 | 72 | /************************************************************************************** 73 | * CONSTANTS 74 | **************************************************************************************/ 75 | #if defined(ARDUINO_EDGE_CONTROL) 76 | static int const EDGE_CONTROL_W25Q16DV_CS_PIN = 28; 77 | #else 78 | static int const MKRMEM_W25Q16DV_CS_PIN = 5; 79 | #endif 80 | 81 | /************************************************************************************** 82 | * CLASS DECLARATION 83 | **************************************************************************************/ 84 | 85 | class Arduino_W25Q16DV 86 | { 87 | 88 | public: 89 | 90 | Arduino_W25Q16DV(SPIClass & spi, int const cs_pin); 91 | 92 | void begin(); 93 | 94 | W25Q16DV_Id readId(); 95 | 96 | bool isBusy (); 97 | void read (uint32_t const addr, uint8_t * buf, uint32_t const size); 98 | void programPage(uint32_t const addr, uint8_t const * buf, uint32_t const size); 99 | void eraseSector(uint32_t const addr); 100 | void eraseChip (); 101 | 102 | 103 | static uint32_t const PAGE_SIZE = 256; 104 | static uint32_t const SECTOR_SIZE = 4096; 105 | static uint32_t const MEMORY_SIZE = 4096 * 16 * 32; /* 16 sectors per block / 32 blocks per chip */ 106 | 107 | private: 108 | 109 | SPIClass & _spi; 110 | int const _cs_pin; 111 | 112 | void select(); 113 | void deselect(); 114 | 115 | uint8_t readStatusReg1(); 116 | void enableWrite(); 117 | 118 | }; 119 | 120 | /************************************************************************************** 121 | * EXTERN DEFINITION 122 | **************************************************************************************/ 123 | 124 | extern Arduino_W25Q16DV flash; 125 | 126 | /************************************************************************************** 127 | * FREE FUNCTION DECLARATION 128 | **************************************************************************************/ 129 | 130 | s32_t w25q16_spi_read (u32_t addr, u32_t size, u8_t * buf); 131 | s32_t w25q16_spi_write(u32_t addr, u32_t size, u8_t * buf); 132 | s32_t w25q16_spi_erase(u32_t addr, u32_t size); 133 | 134 | #endif /* ARDUINO_W25Q16DV_H_ */ 135 | -------------------------------------------------------------------------------- /src/spiffs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spiffs.h 3 | * 4 | * Created on: May 26, 2013 5 | * Author: petera 6 | */ 7 | 8 | #ifndef SPIFFS_H_ 9 | #define SPIFFS_H_ 10 | #if defined(__cplusplus) 11 | extern "C" { 12 | #endif 13 | 14 | #include "spiffs_config.h" 15 | 16 | #define SPIFFS_OK 0 17 | #define SPIFFS_ERR_NOT_MOUNTED -10000 18 | #define SPIFFS_ERR_FULL -10001 19 | #define SPIFFS_ERR_NOT_FOUND -10002 20 | #define SPIFFS_ERR_END_OF_OBJECT -10003 21 | #define SPIFFS_ERR_DELETED -10004 22 | #define SPIFFS_ERR_NOT_FINALIZED -10005 23 | #define SPIFFS_ERR_NOT_INDEX -10006 24 | #define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007 25 | #define SPIFFS_ERR_FILE_CLOSED -10008 26 | #define SPIFFS_ERR_FILE_DELETED -10009 27 | #define SPIFFS_ERR_BAD_DESCRIPTOR -10010 28 | #define SPIFFS_ERR_IS_INDEX -10011 29 | #define SPIFFS_ERR_IS_FREE -10012 30 | #define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013 31 | #define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014 32 | #define SPIFFS_ERR_INDEX_REF_FREE -10015 33 | #define SPIFFS_ERR_INDEX_REF_LU -10016 34 | #define SPIFFS_ERR_INDEX_REF_INVALID -10017 35 | #define SPIFFS_ERR_INDEX_FREE -10018 36 | #define SPIFFS_ERR_INDEX_LU -10019 37 | #define SPIFFS_ERR_INDEX_INVALID -10020 38 | #define SPIFFS_ERR_NOT_WRITABLE -10021 39 | #define SPIFFS_ERR_NOT_READABLE -10022 40 | #define SPIFFS_ERR_CONFLICTING_NAME -10023 41 | #define SPIFFS_ERR_NOT_CONFIGURED -10024 42 | 43 | #define SPIFFS_ERR_NOT_A_FS -10025 44 | #define SPIFFS_ERR_MOUNTED -10026 45 | #define SPIFFS_ERR_ERASE_FAIL -10027 46 | #define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028 47 | 48 | #define SPIFFS_ERR_NO_DELETED_BLOCKS -10029 49 | 50 | #define SPIFFS_ERR_FILE_EXISTS -10030 51 | 52 | #define SPIFFS_ERR_NOT_A_FILE -10031 53 | #define SPIFFS_ERR_RO_NOT_IMPL -10032 54 | #define SPIFFS_ERR_RO_ABORTED_OPERATION -10033 55 | #define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034 56 | #define SPIFFS_ERR_PROBE_NOT_A_FS -10035 57 | #define SPIFFS_ERR_NAME_TOO_LONG -10036 58 | 59 | #define SPIFFS_ERR_IX_MAP_UNMAPPED -10037 60 | #define SPIFFS_ERR_IX_MAP_MAPPED -10038 61 | #define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039 62 | 63 | #define SPIFFS_ERR_SEEK_BOUNDS -10040 64 | 65 | 66 | #define SPIFFS_ERR_INTERNAL -10050 67 | 68 | #define SPIFFS_ERR_TEST -10100 69 | 70 | 71 | // spiffs file descriptor index type. must be signed 72 | typedef s16_t spiffs_file; 73 | // spiffs file descriptor flags 74 | typedef u16_t spiffs_flags; 75 | // spiffs file mode 76 | typedef u16_t spiffs_mode; 77 | // object type 78 | typedef u8_t spiffs_obj_type; 79 | 80 | struct spiffs_t; 81 | 82 | #if SPIFFS_HAL_CALLBACK_EXTRA 83 | 84 | /* spi read call function type */ 85 | typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst); 86 | /* spi write call function type */ 87 | typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src); 88 | /* spi erase call function type */ 89 | typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size); 90 | 91 | #else // SPIFFS_HAL_CALLBACK_EXTRA 92 | 93 | /* spi read call function type */ 94 | typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); 95 | /* spi write call function type */ 96 | typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); 97 | /* spi erase call function type */ 98 | typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); 99 | #endif // SPIFFS_HAL_CALLBACK_EXTRA 100 | 101 | /* file system check callback report operation */ 102 | typedef enum { 103 | SPIFFS_CHECK_LOOKUP = 0, 104 | SPIFFS_CHECK_INDEX, 105 | SPIFFS_CHECK_PAGE 106 | } spiffs_check_type; 107 | 108 | /* file system check callback report type */ 109 | typedef enum { 110 | SPIFFS_CHECK_PROGRESS = 0, 111 | SPIFFS_CHECK_ERROR, 112 | SPIFFS_CHECK_FIX_INDEX, 113 | SPIFFS_CHECK_FIX_LOOKUP, 114 | SPIFFS_CHECK_DELETE_ORPHANED_INDEX, 115 | SPIFFS_CHECK_DELETE_PAGE, 116 | SPIFFS_CHECK_DELETE_BAD_FILE 117 | } spiffs_check_report; 118 | 119 | /* file system check callback function */ 120 | #if SPIFFS_HAL_CALLBACK_EXTRA 121 | typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report, 122 | u32_t arg1, u32_t arg2); 123 | #else // SPIFFS_HAL_CALLBACK_EXTRA 124 | typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, 125 | u32_t arg1, u32_t arg2); 126 | #endif // SPIFFS_HAL_CALLBACK_EXTRA 127 | 128 | /* file system listener callback operation */ 129 | typedef enum { 130 | /* the file has been created */ 131 | SPIFFS_CB_CREATED = 0, 132 | /* the file has been updated or moved to another page */ 133 | SPIFFS_CB_UPDATED, 134 | /* the file has been deleted */ 135 | SPIFFS_CB_DELETED 136 | } spiffs_fileop_type; 137 | 138 | /* file system listener callback function */ 139 | typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix); 140 | 141 | #ifndef SPIFFS_DBG 142 | #define SPIFFS_DBG(...) \ 143 | printf(__VA_ARGS__) 144 | #endif 145 | #ifndef SPIFFS_GC_DBG 146 | #define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) 147 | #endif 148 | #ifndef SPIFFS_CACHE_DBG 149 | #define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) 150 | #endif 151 | #ifndef SPIFFS_CHECK_DBG 152 | #define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__) 153 | #endif 154 | 155 | /* Any write to the filehandle is appended to end of the file */ 156 | #define SPIFFS_APPEND (1<<0) 157 | #define SPIFFS_O_APPEND SPIFFS_APPEND 158 | /* If the opened file exists, it will be truncated to zero length before opened */ 159 | #define SPIFFS_TRUNC (1<<1) 160 | #define SPIFFS_O_TRUNC SPIFFS_TRUNC 161 | /* If the opened file does not exist, it will be created before opened */ 162 | #define SPIFFS_CREAT (1<<2) 163 | #define SPIFFS_O_CREAT SPIFFS_CREAT 164 | /* The opened file may only be read */ 165 | #define SPIFFS_RDONLY (1<<3) 166 | #define SPIFFS_O_RDONLY SPIFFS_RDONLY 167 | /* The opened file may only be written */ 168 | #define SPIFFS_WRONLY (1<<4) 169 | #define SPIFFS_O_WRONLY SPIFFS_WRONLY 170 | /* The opened file may be both read and written */ 171 | #define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY) 172 | #define SPIFFS_O_RDWR SPIFFS_RDWR 173 | /* Any writes to the filehandle will never be cached but flushed directly */ 174 | #define SPIFFS_DIRECT (1<<5) 175 | #define SPIFFS_O_DIRECT SPIFFS_DIRECT 176 | /* If SPIFFS_O_CREAT and SPIFFS_O_EXCL are set, SPIFFS_open() shall fail if the file exists */ 177 | #define SPIFFS_EXCL (1<<6) 178 | #define SPIFFS_O_EXCL SPIFFS_EXCL 179 | 180 | #define SPIFFS_SEEK_SET (0) 181 | #define SPIFFS_SEEK_CUR (1) 182 | #define SPIFFS_SEEK_END (2) 183 | 184 | #define SPIFFS_TYPE_FILE (1) 185 | #define SPIFFS_TYPE_DIR (2) 186 | #define SPIFFS_TYPE_HARD_LINK (3) 187 | #define SPIFFS_TYPE_SOFT_LINK (4) 188 | 189 | #ifndef SPIFFS_LOCK 190 | #define SPIFFS_LOCK(fs) 191 | #endif 192 | 193 | #ifndef SPIFFS_UNLOCK 194 | #define SPIFFS_UNLOCK(fs) 195 | #endif 196 | 197 | // phys structs 198 | 199 | // spiffs spi configuration struct 200 | typedef struct { 201 | // physical read function 202 | spiffs_read hal_read_f; 203 | // physical write function 204 | spiffs_write hal_write_f; 205 | // physical erase function 206 | spiffs_erase hal_erase_f; 207 | #if SPIFFS_SINGLETON == 0 208 | // physical size of the spi flash 209 | u32_t phys_size; 210 | // physical offset in spi flash used for spiffs, 211 | // must be on block boundary 212 | u32_t phys_addr; 213 | // physical size when erasing a block 214 | u32_t phys_erase_block; 215 | 216 | // logical size of a block, must be on physical 217 | // block size boundary and must never be less than 218 | // a physical block 219 | u32_t log_block_size; 220 | // logical size of a page, must be at least 221 | // log_block_size / 8 222 | u32_t log_page_size; 223 | 224 | #endif 225 | #if SPIFFS_FILEHDL_OFFSET 226 | // an integer offset added to each file handle 227 | u16_t fh_ix_offset; 228 | #endif 229 | } spiffs_config; 230 | 231 | typedef struct spiffs_t { 232 | // file system configuration 233 | spiffs_config cfg; 234 | // number of logical blocks 235 | u32_t block_count; 236 | 237 | // cursor for free blocks, block index 238 | spiffs_block_ix free_cursor_block_ix; 239 | // cursor for free blocks, entry index 240 | int free_cursor_obj_lu_entry; 241 | // cursor when searching, block index 242 | spiffs_block_ix cursor_block_ix; 243 | // cursor when searching, entry index 244 | int cursor_obj_lu_entry; 245 | 246 | // primary work buffer, size of a logical page 247 | u8_t *lu_work; 248 | // secondary work buffer, size of a logical page 249 | u8_t *work; 250 | // file descriptor memory area 251 | u8_t *fd_space; 252 | // available file descriptors 253 | u32_t fd_count; 254 | 255 | // last error 256 | s32_t err_code; 257 | 258 | // current number of free blocks 259 | u32_t free_blocks; 260 | // current number of busy pages 261 | u32_t stats_p_allocated; 262 | // current number of deleted pages 263 | u32_t stats_p_deleted; 264 | // flag indicating that garbage collector is cleaning 265 | u8_t cleaning; 266 | // max erase count amongst all blocks 267 | spiffs_obj_id max_erase_count; 268 | 269 | #if SPIFFS_GC_STATS 270 | u32_t stats_gc_runs; 271 | #endif 272 | 273 | #if SPIFFS_CACHE 274 | // cache memory 275 | void *cache; 276 | // cache size 277 | u32_t cache_size; 278 | #if SPIFFS_CACHE_STATS 279 | u32_t cache_hits; 280 | u32_t cache_misses; 281 | #endif 282 | #endif 283 | 284 | // check callback function 285 | spiffs_check_callback check_cb_f; 286 | // file callback function 287 | spiffs_file_callback file_cb_f; 288 | // mounted flag 289 | u8_t mounted; 290 | // user data 291 | void *user_data; 292 | // config magic 293 | u32_t config_magic; 294 | } spiffs; 295 | 296 | /* spiffs file status struct */ 297 | typedef struct { 298 | spiffs_obj_id obj_id; 299 | u32_t size; 300 | spiffs_obj_type type; 301 | spiffs_page_ix pix; 302 | u8_t name[SPIFFS_OBJ_NAME_LEN]; 303 | #if SPIFFS_OBJ_META_LEN 304 | u8_t meta[SPIFFS_OBJ_META_LEN]; 305 | #endif 306 | } spiffs_stat; 307 | 308 | struct spiffs_dirent { 309 | spiffs_obj_id obj_id; 310 | u8_t name[SPIFFS_OBJ_NAME_LEN]; 311 | spiffs_obj_type type; 312 | u32_t size; 313 | spiffs_page_ix pix; 314 | #if SPIFFS_OBJ_META_LEN 315 | u8_t meta[SPIFFS_OBJ_META_LEN]; 316 | #endif 317 | }; 318 | 319 | typedef struct { 320 | spiffs *fs; 321 | spiffs_block_ix block; 322 | int entry; 323 | } spiffs_DIR; 324 | 325 | #if SPIFFS_IX_MAP 326 | 327 | typedef struct { 328 | // buffer with looked up data pixes 329 | spiffs_page_ix *map_buf; 330 | // precise file byte offset 331 | u32_t offset; 332 | // start data span index of lookup buffer 333 | spiffs_span_ix start_spix; 334 | // end data span index of lookup buffer 335 | spiffs_span_ix end_spix; 336 | } spiffs_ix_map; 337 | 338 | #endif 339 | 340 | // functions 341 | 342 | #if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 343 | /** 344 | * Special function. This takes a spiffs config struct and returns the number 345 | * of blocks this file system was formatted with. This function relies on 346 | * that following info is set correctly in given config struct: 347 | * 348 | * phys_addr, log_page_size, and log_block_size. 349 | * 350 | * Also, hal_read_f must be set in the config struct. 351 | * 352 | * One must be sure of the correct page size and that the physical address is 353 | * correct in the probed file system when calling this function. It is not 354 | * checked if the phys_addr actually points to the start of the file system, 355 | * so one might get a false positive if entering a phys_addr somewhere in the 356 | * middle of the file system at block boundary. In addition, it is not checked 357 | * if the page size is actually correct. If it is not, weird file system sizes 358 | * will be returned. 359 | * 360 | * If this function detects a file system it returns the assumed file system 361 | * size, which can be used to set the phys_size. 362 | * 363 | * Otherwise, it returns an error indicating why it is not regarded as a file 364 | * system. 365 | * 366 | * Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK 367 | * macros. It returns the error code directly, instead of as read by 368 | * SPIFFS_errno. 369 | * 370 | * @param config essential parts of the physical and logical 371 | * configuration of the file system. 372 | */ 373 | s32_t SPIFFS_probe_fs(spiffs_config *config); 374 | #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 375 | 376 | /** 377 | * Initializes the file system dynamic parameters and mounts the filesystem. 378 | * If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS 379 | * if the flash does not contain a recognizable file system. 380 | * In this case, SPIFFS_format must be called prior to remounting. 381 | * @param fs the file system struct 382 | * @param config the physical and logical configuration of the file system 383 | * @param work a memory work buffer comprising 2*config->log_page_size 384 | * bytes used throughout all file system operations 385 | * @param fd_space memory for file descriptors 386 | * @param fd_space_size memory size of file descriptors 387 | * @param cache memory for cache, may be null 388 | * @param cache_size memory size of cache 389 | * @param check_cb_f callback function for reporting during consistency checks 390 | */ 391 | s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, 392 | u8_t *fd_space, u32_t fd_space_size, 393 | void *cache, u32_t cache_size, 394 | spiffs_check_callback check_cb_f); 395 | 396 | /** 397 | * Unmounts the file system. All file handles will be flushed of any 398 | * cached writes and closed. 399 | * @param fs the file system struct 400 | */ 401 | void SPIFFS_unmount(spiffs *fs); 402 | 403 | /** 404 | * Creates a new file. 405 | * @param fs the file system struct 406 | * @param path the path of the new file 407 | * @param mode ignored, for posix compliance 408 | */ 409 | s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode); 410 | 411 | /** 412 | * Opens/creates a file. 413 | * @param fs the file system struct 414 | * @param path the path of the new file 415 | * @param flags the flags for the open command, can be combinations of 416 | * SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY, 417 | * SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL 418 | * @param mode ignored, for posix compliance 419 | */ 420 | spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode); 421 | 422 | /** 423 | * Opens a file by given dir entry. 424 | * Optimization purposes, when traversing a file system with SPIFFS_readdir 425 | * a normal SPIFFS_open would need to traverse the filesystem again to find 426 | * the file, whilst SPIFFS_open_by_dirent already knows where the file resides. 427 | * @param fs the file system struct 428 | * @param e the dir entry to the file 429 | * @param flags the flags for the open command, can be combinations of 430 | * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, 431 | * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. 432 | * SPIFFS_CREAT will have no effect in this case. 433 | * @param mode ignored, for posix compliance 434 | */ 435 | spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode); 436 | 437 | /** 438 | * Opens a file by given page index. 439 | * Optimization purposes, opens a file by directly pointing to the page 440 | * index in the spi flash. 441 | * If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE 442 | * is returned. 443 | * @param fs the file system struct 444 | * @param page_ix the page index 445 | * @param flags the flags for the open command, can be combinations of 446 | * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, 447 | * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. 448 | * SPIFFS_CREAT will have no effect in this case. 449 | * @param mode ignored, for posix compliance 450 | */ 451 | spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode); 452 | 453 | /** 454 | * Reads from given filehandle. 455 | * @param fs the file system struct 456 | * @param fh the filehandle 457 | * @param buf where to put read data 458 | * @param len how much to read 459 | * @returns number of bytes read, or -1 if error 460 | */ 461 | s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); 462 | 463 | /** 464 | * Writes to given filehandle. 465 | * @param fs the file system struct 466 | * @param fh the filehandle 467 | * @param buf the data to write 468 | * @param len how much to write 469 | * @returns number of bytes written, or -1 if error 470 | */ 471 | s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); 472 | 473 | /** 474 | * Moves the read/write file offset. Resulting offset is returned or negative if error. 475 | * lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset. 476 | * @param fs the file system struct 477 | * @param fh the filehandle 478 | * @param offs how much/where to move the offset 479 | * @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes 480 | * if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset 481 | * if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative 482 | */ 483 | s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); 484 | 485 | /** 486 | * Removes a file by path 487 | * @param fs the file system struct 488 | * @param path the path of the file to remove 489 | */ 490 | s32_t SPIFFS_remove(spiffs *fs, const char *path); 491 | 492 | /** 493 | * Removes a file by filehandle 494 | * @param fs the file system struct 495 | * @param fh the filehandle of the file to remove 496 | */ 497 | s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh); 498 | 499 | /** 500 | * Gets file status by path 501 | * @param fs the file system struct 502 | * @param path the path of the file to stat 503 | * @param s the stat struct to populate 504 | */ 505 | s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s); 506 | 507 | /** 508 | * Gets file status by filehandle 509 | * @param fs the file system struct 510 | * @param fh the filehandle of the file to stat 511 | * @param s the stat struct to populate 512 | */ 513 | s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s); 514 | 515 | /** 516 | * Flushes all pending write operations from cache for given file 517 | * @param fs the file system struct 518 | * @param fh the filehandle of the file to flush 519 | */ 520 | s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); 521 | 522 | /** 523 | * Closes a filehandle. If there are pending write operations, these are finalized before closing. 524 | * @param fs the file system struct 525 | * @param fh the filehandle of the file to close 526 | */ 527 | s32_t SPIFFS_close(spiffs *fs, spiffs_file fh); 528 | 529 | /** 530 | * Renames a file 531 | * @param fs the file system struct 532 | * @param old path of file to rename 533 | * @param newPath new path of file 534 | */ 535 | s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath); 536 | 537 | #if SPIFFS_OBJ_META_LEN 538 | /** 539 | * Updates file's metadata 540 | * @param fs the file system struct 541 | * @param path path to the file 542 | * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. 543 | */ 544 | s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta); 545 | 546 | /** 547 | * Updates file's metadata 548 | * @param fs the file system struct 549 | * @param fh file handle of the file 550 | * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. 551 | */ 552 | s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta); 553 | #endif 554 | 555 | /** 556 | * Returns last error of last file operation. 557 | * @param fs the file system struct 558 | */ 559 | s32_t SPIFFS_errno(spiffs *fs); 560 | 561 | /** 562 | * Clears last error. 563 | * @param fs the file system struct 564 | */ 565 | void SPIFFS_clearerr(spiffs *fs); 566 | 567 | /** 568 | * Opens a directory stream corresponding to the given name. 569 | * The stream is positioned at the first entry in the directory. 570 | * On hydrogen builds the name argument is ignored as hydrogen builds always correspond 571 | * to a flat file structure - no directories. 572 | * @param fs the file system struct 573 | * @param name the name of the directory 574 | * @param d pointer the directory stream to be populated 575 | */ 576 | spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); 577 | 578 | /** 579 | * Closes a directory stream 580 | * @param d the directory stream to close 581 | */ 582 | s32_t SPIFFS_closedir(spiffs_DIR *d); 583 | 584 | /** 585 | * Reads a directory into given spifs_dirent struct. 586 | * @param d pointer to the directory stream 587 | * @param e the dirent struct to be populated 588 | * @returns null if error or end of stream, else given dirent is returned 589 | */ 590 | struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); 591 | 592 | /** 593 | * Runs a consistency check on given filesystem. 594 | * @param fs the file system struct 595 | */ 596 | s32_t SPIFFS_check(spiffs *fs); 597 | 598 | /** 599 | * Returns number of total bytes available and number of used bytes. 600 | * This is an estimation, and depends on if there a many files with little 601 | * data or few files with much data. 602 | * NB: If used number of bytes exceeds total bytes, a SPIFFS_check should 603 | * run. This indicates a power loss in midst of things. In worst case 604 | * (repeated powerlosses in mending or gc) you might have to delete some files. 605 | * 606 | * @param fs the file system struct 607 | * @param total total number of bytes in filesystem 608 | * @param used used number of bytes in filesystem 609 | */ 610 | s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used); 611 | 612 | /** 613 | * Formats the entire file system. All data will be lost. 614 | * The filesystem must not be mounted when calling this. 615 | * 616 | * NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount 617 | * MUST be called prior to formatting in order to configure the filesystem. 618 | * If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling 619 | * SPIFFS_format. 620 | * If SPIFFS_mount fails, SPIFFS_format can be called directly without calling 621 | * SPIFFS_unmount first. 622 | * 623 | * @param fs the file system struct 624 | */ 625 | s32_t SPIFFS_format(spiffs *fs); 626 | 627 | /** 628 | * Returns nonzero if spiffs is mounted, or zero if unmounted. 629 | * @param fs the file system struct 630 | */ 631 | u8_t SPIFFS_mounted(spiffs *fs); 632 | 633 | /** 634 | * Tries to find a block where most or all pages are deleted, and erase that 635 | * block if found. Does not care for wear levelling. Will not move pages 636 | * around. 637 | * If parameter max_free_pages are set to 0, only blocks with only deleted 638 | * pages will be selected. 639 | * 640 | * NB: the garbage collector is automatically called when spiffs needs free 641 | * pages. The reason for this function is to give possibility to do background 642 | * tidying when user knows the system is idle. 643 | * 644 | * Use with care. 645 | * 646 | * Setting max_free_pages to anything larger than zero will eventually wear 647 | * flash more as a block containing free pages can be erased. 648 | * 649 | * Will set err_no to SPIFFS_OK if a block was found and erased, 650 | * SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found, 651 | * or other error. 652 | * 653 | * @param fs the file system struct 654 | * @param max_free_pages maximum number allowed free pages in block 655 | */ 656 | s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages); 657 | 658 | /** 659 | * Will try to make room for given amount of bytes in the filesystem by moving 660 | * pages and erasing blocks. 661 | * If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If 662 | * there already is this amount (or more) of free space, SPIFFS_gc will 663 | * silently return. It is recommended to call SPIFFS_info before invoking 664 | * this method in order to determine what amount of bytes to give. 665 | * 666 | * NB: the garbage collector is automatically called when spiffs needs free 667 | * pages. The reason for this function is to give possibility to do background 668 | * tidying when user knows the system is idle. 669 | * 670 | * Use with care. 671 | * 672 | * @param fs the file system struct 673 | * @param size amount of bytes that should be freed 674 | */ 675 | s32_t SPIFFS_gc(spiffs *fs, u32_t size); 676 | 677 | /** 678 | * Check if EOF reached. 679 | * @param fs the file system struct 680 | * @param fh the filehandle of the file to check 681 | */ 682 | s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); 683 | 684 | /** 685 | * Get position in file. 686 | * @param fs the file system struct 687 | * @param fh the filehandle of the file to check 688 | */ 689 | s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); 690 | 691 | /** 692 | * Registers a callback function that keeps track on operations on file 693 | * headers. Do note, that this callback is called from within internal spiffs 694 | * mechanisms. Any operations on the actual file system being callbacked from 695 | * in this callback will mess things up for sure - do not do this. 696 | * This can be used to track where files are and move around during garbage 697 | * collection, which in turn can be used to build location tables in ram. 698 | * Used in conjuction with SPIFFS_open_by_page this may improve performance 699 | * when opening a lot of files. 700 | * Must be invoked after mount. 701 | * 702 | * @param fs the file system struct 703 | * @param cb_func the callback on file operations 704 | */ 705 | s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func); 706 | 707 | #if SPIFFS_IX_MAP 708 | 709 | /** 710 | * Maps the first level index lookup to a given memory map. 711 | * This will make reading big files faster, as the memory map will be used for 712 | * looking up data pages instead of searching for the indices on the physical 713 | * medium. When mapping, all affected indicies are found and the information is 714 | * copied to the array. 715 | * Whole file or only parts of it may be mapped. The index map will cover file 716 | * contents from argument offset until and including arguments (offset+len). 717 | * It is valid to map a longer range than the current file size. The map will 718 | * then be populated when the file grows. 719 | * On garbage collections and file data page movements, the map array will be 720 | * automatically updated. Do not tamper with the map array, as this contains 721 | * the references to the data pages. Modifying it from outside will corrupt any 722 | * future readings using this file descriptor. 723 | * The map will no longer be used when the file descriptor closed or the file 724 | * is unmapped. 725 | * This can be useful to get faster and more deterministic timing when reading 726 | * large files, or when seeking and reading a lot within a file. 727 | * @param fs the file system struct 728 | * @param fh the file handle of the file to map 729 | * @param map a spiffs_ix_map struct, describing the index map 730 | * @param offset absolute file offset where to start the index map 731 | * @param len length of the mapping in actual file bytes 732 | * @param map_buf the array buffer for the look up data - number of required 733 | * elements in the array can be derived from function 734 | * SPIFFS_bytes_to_ix_map_entries given the length 735 | */ 736 | s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, 737 | u32_t offset, u32_t len, spiffs_page_ix *map_buf); 738 | 739 | /** 740 | * Unmaps the index lookup from this filehandle. All future readings will 741 | * proceed as normal, requiring reading of the first level indices from 742 | * physical media. 743 | * The map and map buffer given in function SPIFFS_ix_map will no longer be 744 | * referenced by spiffs. 745 | * It is not strictly necessary to unmap a file before closing it, as closing 746 | * a file will automatically unmap it. 747 | * @param fs the file system struct 748 | * @param fh the file handle of the file to unmap 749 | */ 750 | s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh); 751 | 752 | /** 753 | * Moves the offset for the index map given in function SPIFFS_ix_map. Parts or 754 | * all of the map buffer will repopulated. 755 | * @param fs the file system struct 756 | * @param fh the mapped file handle of the file to remap 757 | * @param offset new absolute file offset where to start the index map 758 | */ 759 | s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs); 760 | 761 | /** 762 | * Utility function to get number of spiffs_page_ix entries a map buffer must 763 | * contain on order to map given amount of file data in bytes. 764 | * See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes. 765 | * @param fs the file system struct 766 | * @param bytes number of file data bytes to map 767 | * @return needed number of elements in a spiffs_page_ix array needed to 768 | * map given amount of bytes in a file 769 | */ 770 | s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes); 771 | 772 | /** 773 | * Utility function to amount of file data bytes that can be mapped when 774 | * mapping a file with buffer having given number of spiffs_page_ix entries. 775 | * See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries. 776 | * @param fs the file system struct 777 | * @param map_page_ix_entries number of entries in a spiffs_page_ix array 778 | * @return amount of file data in bytes that can be mapped given a map 779 | * buffer having given amount of spiffs_page_ix entries 780 | */ 781 | s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries); 782 | 783 | #endif // SPIFFS_IX_MAP 784 | 785 | 786 | #if SPIFFS_TEST_VISUALISATION 787 | /** 788 | * Prints out a visualization of the filesystem. 789 | * @param fs the file system struct 790 | */ 791 | s32_t SPIFFS_vis(spiffs *fs); 792 | #endif 793 | 794 | #if SPIFFS_BUFFER_HELP 795 | /** 796 | * Returns number of bytes needed for the filedescriptor buffer given 797 | * amount of file descriptors. 798 | */ 799 | u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs); 800 | 801 | #if SPIFFS_CACHE 802 | /** 803 | * Returns number of bytes needed for the cache buffer given 804 | * amount of cache pages. 805 | */ 806 | u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages); 807 | #endif 808 | #endif 809 | 810 | #if SPIFFS_CACHE 811 | #endif 812 | #if defined(__cplusplus) 813 | } 814 | #endif 815 | 816 | #endif /* SPIFFS_H_ */ 817 | -------------------------------------------------------------------------------- /src/spiffs_cache.c: -------------------------------------------------------------------------------- 1 | /* 2 | * spiffs_cache.c 3 | * 4 | * Created on: Jun 23, 2013 5 | * Author: petera 6 | */ 7 | 8 | #include "spiffs.h" 9 | #include "spiffs_nucleus.h" 10 | 11 | #if SPIFFS_CACHE 12 | 13 | // returns cached page for give page index, or null if no such cached page 14 | static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) { 15 | spiffs_cache *cache = spiffs_get_cache(fs); 16 | if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0; 17 | int i; 18 | for (i = 0; i < cache->cpage_count; i++) { 19 | spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); 20 | if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && 22 | cp->pix == pix ) { 23 | //SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix); 24 | cp->last_access = cache->last_access; 25 | return cp; 26 | } 27 | } 28 | //SPIFFS_CACHE_DBG("CACHE_GET: no cache for "_SPIPRIpg"\n", pix); 29 | return 0; 30 | } 31 | 32 | // frees cached page 33 | static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) { 34 | s32_t res = SPIFFS_OK; 35 | spiffs_cache *cache = spiffs_get_cache(fs); 36 | spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix); 37 | if (cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && 40 | (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { 41 | u8_t *mem = spiffs_get_cache_page(fs, cache, ix); 42 | SPIFFS_CACHE_DBG("CACHE_FREE: write cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); 43 | res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); 44 | } 45 | 46 | #if SPIFFS_CACHE_WR 47 | if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) { 48 | SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id); 49 | } else 50 | #endif 51 | { 52 | SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); 53 | } 54 | cache->cpage_use_map &= ~(1 << ix); 55 | cp->flags = 0; 56 | } 57 | 58 | return res; 59 | } 60 | 61 | // removes the oldest accessed cached page 62 | static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) { 63 | s32_t res = SPIFFS_OK; 64 | spiffs_cache *cache = spiffs_get_cache(fs); 65 | 66 | if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) { 67 | // at least one free cpage 68 | return SPIFFS_OK; 69 | } 70 | 71 | // all busy, scan thru all to find the cpage which has oldest access 72 | int i; 73 | int cand_ix = -1; 74 | u32_t oldest_val = 0; 75 | for (i = 0; i < cache->cpage_count; i++) { 76 | spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); 77 | if ((cache->last_access - cp->last_access) > oldest_val && 78 | (cp->flags & flag_mask) == flags) { 79 | oldest_val = cache->last_access - cp->last_access; 80 | cand_ix = i; 81 | } 82 | } 83 | 84 | if (cand_ix >= 0) { 85 | res = spiffs_cache_page_free(fs, cand_ix, 1); 86 | } 87 | 88 | return res; 89 | } 90 | 91 | // allocates a new cached page and returns it, or null if all cache pages are busy 92 | static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) { 93 | spiffs_cache *cache = spiffs_get_cache(fs); 94 | if (cache->cpage_use_map == 0xffffffff) { 95 | // out of cache memory 96 | return 0; 97 | } 98 | int i; 99 | for (i = 0; i < cache->cpage_count; i++) { 100 | if ((cache->cpage_use_map & (1<cpage_use_map |= (1<last_access = cache->last_access; 104 | //SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i); 105 | return cp; 106 | } 107 | } 108 | // out of cache entries 109 | return 0; 110 | } 111 | 112 | // drops the cache page for give page index 113 | void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) { 114 | spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); 115 | if (cp) { 116 | spiffs_cache_page_free(fs, cp->ix, 0); 117 | } 118 | } 119 | 120 | // ------------------------------ 121 | 122 | // reads from spi flash or the cache 123 | s32_t spiffs_phys_rd( 124 | spiffs *fs, 125 | u8_t op, 126 | spiffs_file fh, 127 | u32_t addr, 128 | u32_t len, 129 | u8_t *dst) { 130 | (void)fh; 131 | s32_t res = SPIFFS_OK; 132 | spiffs_cache *cache = spiffs_get_cache(fs); 133 | spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr)); 134 | cache->last_access++; 135 | if (cp) { 136 | // we've already got one, you see 137 | #if SPIFFS_CACHE_STATS 138 | fs->cache_hits++; 139 | #endif 140 | cp->last_access = cache->last_access; 141 | u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); 142 | _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); 143 | } else { 144 | if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) { 145 | // for second layer lookup functions, we do not cache in order to prevent shredding 146 | return SPIFFS_HAL_READ(fs, addr, len, dst); 147 | } 148 | #if SPIFFS_CACHE_STATS 149 | fs->cache_misses++; 150 | #endif 151 | // this operation will always free one cache page (unless all already free), 152 | // the result code stems from the write operation of the possibly freed cache page 153 | res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); 154 | 155 | cp = spiffs_cache_page_allocate(fs); 156 | if (cp) { 157 | cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; 158 | cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr); 159 | SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for pix "_SPIPRIpg "\n", cp->ix, cp->pix); 160 | 161 | s32_t res2 = SPIFFS_HAL_READ(fs, 162 | addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), 163 | SPIFFS_CFG_LOG_PAGE_SZ(fs), 164 | spiffs_get_cache_page(fs, cache, cp->ix)); 165 | if (res2 != SPIFFS_OK) { 166 | // honor read failure before possible write failure (bad idea?) 167 | res = res2; 168 | } 169 | u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); 170 | _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); 171 | } else { 172 | // this will never happen, last resort for sake of symmetry 173 | s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst); 174 | if (res2 != SPIFFS_OK) { 175 | // honor read failure before possible write failure (bad idea?) 176 | res = res2; 177 | } 178 | } 179 | } 180 | return res; 181 | } 182 | 183 | // writes to spi flash and/or the cache 184 | s32_t spiffs_phys_wr( 185 | spiffs *fs, 186 | u8_t op, 187 | spiffs_file fh, 188 | u32_t addr, 189 | u32_t len, 190 | u8_t *src) { 191 | (void)fh; 192 | spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr); 193 | spiffs_cache *cache = spiffs_get_cache(fs); 194 | spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); 195 | 196 | if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) { 197 | // have a cache page 198 | // copy in data to cache page 199 | 200 | if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE && 201 | (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) { 202 | // page is being deleted, wipe from cache - unless it is a lookup page 203 | spiffs_cache_page_free(fs, cp->ix, 0); 204 | return SPIFFS_HAL_WRITE(fs, addr, len, src); 205 | } 206 | 207 | u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); 208 | _SPIFFS_MEMCPY(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); 209 | 210 | cache->last_access++; 211 | cp->last_access = cache->last_access; 212 | 213 | if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) { 214 | // page is being updated, no write-cache, just pass thru 215 | return SPIFFS_HAL_WRITE(fs, addr, len, src); 216 | } else { 217 | return SPIFFS_OK; 218 | } 219 | } else { 220 | // no cache page, no write cache - just write thru 221 | return SPIFFS_HAL_WRITE(fs, addr, len, src); 222 | } 223 | } 224 | 225 | #if SPIFFS_CACHE_WR 226 | // returns the cache page that this fd refers, or null if no cache page 227 | spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) { 228 | spiffs_cache *cache = spiffs_get_cache(fs); 229 | 230 | if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) { 231 | // all cpages free, no cpage cannot be assigned to obj_id 232 | return 0; 233 | } 234 | 235 | int i; 236 | for (i = 0; i < cache->cpage_count; i++) { 237 | spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); 238 | if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) && 240 | cp->obj_id == fd->obj_id) { 241 | return cp; 242 | } 243 | } 244 | 245 | return 0; 246 | } 247 | 248 | // allocates a new cache page and refers this to given fd - flushes an old cache 249 | // page if all cache is busy 250 | spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) { 251 | // before this function is called, it is ensured that there is no already existing 252 | // cache page with same object id 253 | spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); 254 | spiffs_cache_page *cp = spiffs_cache_page_allocate(fs); 255 | if (cp == 0) { 256 | // could not get cache page 257 | return 0; 258 | } 259 | 260 | cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR; 261 | cp->obj_id = fd->obj_id; 262 | fd->cache_page = cp; 263 | SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for fd "_SPIPRIfd ":"_SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id); 264 | return cp; 265 | } 266 | 267 | // unrefers all fds that this cache page refers to and releases the cache page 268 | void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) { 269 | if (cp == 0) return; 270 | u32_t i; 271 | spiffs_fd *fds = (spiffs_fd *)fs->fd_space; 272 | for (i = 0; i < fs->fd_count; i++) { 273 | spiffs_fd *cur_fd = &fds[i]; 274 | if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) { 275 | cur_fd->cache_page = 0; 276 | } 277 | } 278 | spiffs_cache_page_free(fs, cp->ix, 0); 279 | 280 | cp->obj_id = 0; 281 | } 282 | 283 | #endif 284 | 285 | // initializes the cache 286 | void spiffs_cache_init(spiffs *fs) { 287 | if (fs->cache == 0) return; 288 | u32_t sz = fs->cache_size; 289 | u32_t cache_mask = 0; 290 | int i; 291 | int cache_entries = 292 | (sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs)); 293 | if (cache_entries <= 0) return; 294 | 295 | for (i = 0; i < cache_entries; i++) { 296 | cache_mask <<= 1; 297 | cache_mask |= 1; 298 | } 299 | 300 | spiffs_cache cache; 301 | memset(&cache, 0, sizeof(spiffs_cache)); 302 | cache.cpage_count = cache_entries; 303 | cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache)); 304 | 305 | cache.cpage_use_map = 0xffffffff; 306 | cache.cpage_use_mask = cache_mask; 307 | _SPIFFS_MEMCPY(fs->cache, &cache, sizeof(spiffs_cache)); 308 | 309 | spiffs_cache *c = spiffs_get_cache(fs); 310 | 311 | memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs)); 312 | 313 | c->cpage_use_map &= ~(c->cpage_use_mask); 314 | for (i = 0; i < cache.cpage_count; i++) { 315 | spiffs_get_cache_page_hdr(fs, c, i)->ix = i; 316 | } 317 | } 318 | 319 | #endif // SPIFFS_CACHE 320 | -------------------------------------------------------------------------------- /src/spiffs_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spiffs_config.h 3 | * 4 | * Created on: Jul 3, 2013 5 | * Author: petera 6 | */ 7 | 8 | #ifndef SPIFFS_CONFIG_H_ 9 | #define SPIFFS_CONFIG_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | typedef int32_t s32_t; 17 | typedef uint32_t u32_t; 18 | typedef int16_t s16_t; 19 | typedef uint16_t u16_t; 20 | typedef int8_t s8_t; 21 | typedef uint8_t u8_t; 22 | 23 | // compile time switches 24 | 25 | // Set generic spiffs debug output call. 26 | #ifndef SPIFFS_DBG 27 | #define SPIFFS_DBG(_f, ...) //printf(_f, ## __VA_ARGS__) 28 | #endif 29 | // Set spiffs debug output call for garbage collecting. 30 | #ifndef SPIFFS_GC_DBG 31 | #define SPIFFS_GC_DBG(_f, ...) //printf(_f, ## __VA_ARGS__) 32 | #endif 33 | // Set spiffs debug output call for caching. 34 | #ifndef SPIFFS_CACHE_DBG 35 | #define SPIFFS_CACHE_DBG(_f, ...) //printf(_f, ## __VA_ARGS__) 36 | #endif 37 | // Set spiffs debug output call for system consistency checks. 38 | #ifndef SPIFFS_CHECK_DBG 39 | #define SPIFFS_CHECK_DBG(_f, ...) //printf(_f, ## __VA_ARGS__) 40 | #endif 41 | // Set spiffs debug output call for all api invocations. 42 | #ifndef SPIFFS_API_DBG 43 | #define SPIFFS_API_DBG(_f, ...) //printf(_f, ## __VA_ARGS__) 44 | #endif 45 | 46 | // Defines spiffs debug print formatters 47 | // some general signed number 48 | #ifndef _SPIPRIi 49 | #define _SPIPRIi "%d" 50 | #endif 51 | // address 52 | #ifndef _SPIPRIad 53 | #define _SPIPRIad "%08x" 54 | #endif 55 | // block 56 | #ifndef _SPIPRIbl 57 | #define _SPIPRIbl "%04x" 58 | #endif 59 | // page 60 | #ifndef _SPIPRIpg 61 | #define _SPIPRIpg "%04x" 62 | #endif 63 | // span index 64 | #ifndef _SPIPRIsp 65 | #define _SPIPRIsp "%04x" 66 | #endif 67 | // file descriptor 68 | #ifndef _SPIPRIfd 69 | #define _SPIPRIfd "%d" 70 | #endif 71 | // file object id 72 | #ifndef _SPIPRIid 73 | #define _SPIPRIid "%04x" 74 | #endif 75 | // file flags 76 | #ifndef _SPIPRIfl 77 | #define _SPIPRIfl "%02x" 78 | #endif 79 | 80 | 81 | // Enable/disable API functions to determine exact number of bytes 82 | // for filedescriptor and cache buffers. Once decided for a configuration, 83 | // this can be disabled to reduce flash. 84 | #ifndef SPIFFS_BUFFER_HELP 85 | #define SPIFFS_BUFFER_HELP 0 86 | #endif 87 | 88 | // Enables/disable memory read caching of nucleus file system operations. 89 | // If enabled, memory area must be provided for cache in SPIFFS_mount. 90 | #ifndef SPIFFS_CACHE 91 | #define SPIFFS_CACHE 1 92 | #endif 93 | #if SPIFFS_CACHE 94 | // Enables memory write caching for file descriptors in hydrogen 95 | #ifndef SPIFFS_CACHE_WR 96 | #define SPIFFS_CACHE_WR 1 97 | #endif 98 | 99 | // Enable/disable statistics on caching. Debug/test purpose only. 100 | #ifndef SPIFFS_CACHE_STATS 101 | #define SPIFFS_CACHE_STATS 0 102 | #endif 103 | #endif 104 | 105 | // Always check header of each accessed page to ensure consistent state. 106 | // If enabled it will increase number of reads, will increase flash. 107 | #ifndef SPIFFS_PAGE_CHECK 108 | #define SPIFFS_PAGE_CHECK 1 109 | #endif 110 | 111 | // Define maximum number of gc runs to perform to reach desired free pages. 112 | #ifndef SPIFFS_GC_MAX_RUNS 113 | #define SPIFFS_GC_MAX_RUNS 5 114 | #endif 115 | 116 | // Enable/disable statistics on gc. Debug/test purpose only. 117 | #ifndef SPIFFS_GC_STATS 118 | #define SPIFFS_GC_STATS 0 119 | #endif 120 | 121 | // Garbage collecting examines all pages in a block which and sums up 122 | // to a block score. Deleted pages normally gives positive score and 123 | // used pages normally gives a negative score (as these must be moved). 124 | // To have a fair wear-leveling, the erase age is also included in score, 125 | // whose factor normally is the most positive. 126 | // The larger the score, the more likely it is that the block will 127 | // picked for garbage collection. 128 | 129 | // Garbage collecting heuristics - weight used for deleted pages. 130 | #ifndef SPIFFS_GC_HEUR_W_DELET 131 | #define SPIFFS_GC_HEUR_W_DELET (5) 132 | #endif 133 | // Garbage collecting heuristics - weight used for used pages. 134 | #ifndef SPIFFS_GC_HEUR_W_USED 135 | #define SPIFFS_GC_HEUR_W_USED (-1) 136 | #endif 137 | // Garbage collecting heuristics - weight used for time between 138 | // last erased and erase of this block. 139 | #ifndef SPIFFS_GC_HEUR_W_ERASE_AGE 140 | #define SPIFFS_GC_HEUR_W_ERASE_AGE (50) 141 | #endif 142 | 143 | // Object name maximum length. Note that this length include the 144 | // zero-termination character, meaning maximum string of characters 145 | // can at most be SPIFFS_OBJ_NAME_LEN - 1. 146 | #ifndef SPIFFS_OBJ_NAME_LEN 147 | #define SPIFFS_OBJ_NAME_LEN (32) 148 | #endif 149 | 150 | // Maximum length of the metadata associated with an object. 151 | // Setting to non-zero value enables metadata-related API but also 152 | // changes the on-disk format, so the change is not backward-compatible. 153 | // 154 | // Do note: the meta length must never exceed 155 | // logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64) 156 | // 157 | // This is derived from following: 158 | // logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) + 159 | // spiffs_object_ix_header fields + at least some LUT entries) 160 | #ifndef SPIFFS_OBJ_META_LEN 161 | #define SPIFFS_OBJ_META_LEN (0) 162 | #endif 163 | 164 | // Size of buffer allocated on stack used when copying data. 165 | // Lower value generates more read/writes. No meaning having it bigger 166 | // than logical page size. 167 | #ifndef SPIFFS_COPY_BUFFER_STACK 168 | #define SPIFFS_COPY_BUFFER_STACK (64) 169 | #endif 170 | 171 | // Enable this to have an identifiable spiffs filesystem. This will look for 172 | // a magic in all sectors to determine if this is a valid spiffs system or 173 | // not on mount point. If not, SPIFFS_format must be called prior to mounting 174 | // again. 175 | #ifndef SPIFFS_USE_MAGIC 176 | #define SPIFFS_USE_MAGIC (1) 177 | #endif 178 | 179 | #if SPIFFS_USE_MAGIC 180 | // Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is 181 | // enabled, the magic will also be dependent on the length of the filesystem. 182 | // For example, a filesystem configured and formatted for 4 megabytes will not 183 | // be accepted for mounting with a configuration defining the filesystem as 2 184 | // megabytes. 185 | #ifndef SPIFFS_USE_MAGIC_LENGTH 186 | #define SPIFFS_USE_MAGIC_LENGTH (1) 187 | #endif 188 | #endif 189 | 190 | // SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level 191 | // These should be defined on a multithreaded system 192 | 193 | // define this to enter a mutex if you're running on a multithreaded system 194 | #ifndef SPIFFS_LOCK 195 | #define SPIFFS_LOCK(fs) 196 | #endif 197 | // define this to exit a mutex if you're running on a multithreaded system 198 | #ifndef SPIFFS_UNLOCK 199 | #define SPIFFS_UNLOCK(fs) 200 | #endif 201 | 202 | // Enable if only one spiffs instance with constant configuration will exist 203 | // on the target. This will reduce calculations, flash and memory accesses. 204 | // Parts of configuration must be defined below instead of at time of mount. 205 | #ifndef SPIFFS_SINGLETON 206 | #define SPIFFS_SINGLETON 1 207 | #endif 208 | 209 | #if SPIFFS_SINGLETON 210 | // Instead of giving parameters in config struct, singleton build must 211 | // give parameters in defines below. 212 | #ifndef SPIFFS_CFG_PHYS_SZ 213 | #define SPIFFS_CFG_PHYS_SZ(ignore) (4096 * 16 * 32) 214 | #endif 215 | #ifndef SPIFFS_CFG_PHYS_ERASE_SZ 216 | #define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4096) 217 | #endif 218 | #ifndef SPIFFS_CFG_PHYS_ADDR 219 | #define SPIFFS_CFG_PHYS_ADDR(ignore) (0) 220 | #endif 221 | #ifndef SPIFFS_CFG_LOG_PAGE_SZ 222 | #define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) 223 | #endif 224 | #ifndef SPIFFS_CFG_LOG_BLOCK_SZ 225 | #define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (4096) 226 | #endif 227 | #endif 228 | 229 | // Enable this if your target needs aligned data for index tables 230 | #ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 231 | #define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1 232 | #endif 233 | 234 | // Enable this if you want the HAL callbacks to be called with the spiffs struct 235 | #ifndef SPIFFS_HAL_CALLBACK_EXTRA 236 | #define SPIFFS_HAL_CALLBACK_EXTRA 0 237 | #endif 238 | 239 | // Enable this if you want to add an integer offset to all file handles 240 | // (spiffs_file). This is useful if running multiple instances of spiffs on 241 | // same target, in order to recognise to what spiffs instance a file handle 242 | // belongs. 243 | // NB: This adds config field fh_ix_offset in the configuration struct when 244 | // mounting, which must be defined. 245 | #ifndef SPIFFS_FILEHDL_OFFSET 246 | #define SPIFFS_FILEHDL_OFFSET 0 247 | #endif 248 | 249 | // Enable this to compile a read only version of spiffs. 250 | // This will reduce binary size of spiffs. All code comprising modification 251 | // of the file system will not be compiled. Some config will be ignored. 252 | // HAL functions for erasing and writing to spi-flash may be null. Cache 253 | // can be disabled for even further binary size reduction (and ram savings). 254 | // Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL. 255 | // If the file system cannot be mounted due to aborted erase operation and 256 | // SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be 257 | // returned. 258 | // Might be useful for e.g. bootloaders and such. 259 | #ifndef SPIFFS_READ_ONLY 260 | #define SPIFFS_READ_ONLY 0 261 | #endif 262 | 263 | // Enable this to add a temporal file cache using the fd buffer. 264 | // The effects of the cache is that SPIFFS_open will find the file faster in 265 | // certain cases. It will make it a lot easier for spiffs to find files 266 | // opened frequently, reducing number of readings from the spi flash for 267 | // finding those files. 268 | // This will grow each fd by 6 bytes. If your files are opened in patterns 269 | // with a degree of temporal locality, the system is optimized. 270 | // Examples can be letting spiffs serve web content, where one file is the css. 271 | // The css is accessed for each html file that is opened, meaning it is 272 | // accessed almost every second time a file is opened. Another example could be 273 | // a log file that is often opened, written, and closed. 274 | // The size of the cache is number of given file descriptors, as it piggybacks 275 | // on the fd update mechanism. The cache lives in the closed file descriptors. 276 | // When closed, the fd know the whereabouts of the file. Instead of forgetting 277 | // this, the temporal cache will keep handling updates to that file even if the 278 | // fd is closed. If the file is opened again, the location of the file is found 279 | // directly. If all available descriptors become opened, all cache memory is 280 | // lost. 281 | #ifndef SPIFFS_TEMPORAL_FD_CACHE 282 | #define SPIFFS_TEMPORAL_FD_CACHE 1 283 | #endif 284 | 285 | // Temporal file cache hit score. Each time a file is opened, all cached files 286 | // will lose one point. If the opened file is found in cache, that entry will 287 | // gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this 288 | // value for the specific access patterns of the application. However, it must 289 | // be between 1 (no gain for hitting a cached entry often) and 255. 290 | #ifndef SPIFFS_TEMPORAL_CACHE_HIT_SCORE 291 | #define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 4 292 | #endif 293 | 294 | // Enable to be able to map object indices to memory. 295 | // This allows for faster and more deterministic reading if cases of reading 296 | // large files and when changing file offset by seeking around a lot. 297 | // When mapping a file's index, the file system will be scanned for index pages 298 | // and the info will be put in memory provided by user. When reading, the 299 | // memory map can be looked up instead of searching for index pages on the 300 | // medium. This way, user can trade memory against performance. 301 | // Whole, parts of, or future parts not being written yet can be mapped. The 302 | // memory array will be owned by spiffs and updated accordingly during garbage 303 | // collecting or when modifying the indices. The latter is invoked by when the 304 | // file is modified in some way. The index buffer is tied to the file 305 | // descriptor. 306 | #ifndef SPIFFS_IX_MAP 307 | #define SPIFFS_IX_MAP 1 308 | #endif 309 | 310 | // By default SPIFFS in some cases relies on the property of NOR flash that bits 311 | // cannot be set from 0 to 1 by writing and that controllers will ignore such 312 | // bit changes. This results in fewer reads as SPIFFS can in some cases perform 313 | // blind writes, with all bits set to 1 and only those it needs reset set to 0. 314 | // Most of the chips and controllers allow this behavior, so the default is to 315 | // use this technique. If your controller is one of the rare ones that don't, 316 | // turn this option on and SPIFFS will perform a read-modify-write instead. 317 | #ifndef SPIFFS_NO_BLIND_WRITES 318 | #define SPIFFS_NO_BLIND_WRITES 0 319 | #endif 320 | 321 | // Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function 322 | // in the api. This function will visualize all filesystem using given printf 323 | // function. 324 | #ifndef SPIFFS_TEST_VISUALISATION 325 | #define SPIFFS_TEST_VISUALISATION 1 326 | #endif 327 | #if SPIFFS_TEST_VISUALISATION 328 | #ifndef spiffs_printf 329 | #define spiffs_printf(...) printf(__VA_ARGS__) 330 | #endif 331 | // spiffs_printf argument for a free page 332 | #ifndef SPIFFS_TEST_VIS_FREE_STR 333 | #define SPIFFS_TEST_VIS_FREE_STR "_" 334 | #endif 335 | // spiffs_printf argument for a deleted page 336 | #ifndef SPIFFS_TEST_VIS_DELE_STR 337 | #define SPIFFS_TEST_VIS_DELE_STR "/" 338 | #endif 339 | // spiffs_printf argument for an index page for given object id 340 | #ifndef SPIFFS_TEST_VIS_INDX_STR 341 | #define SPIFFS_TEST_VIS_INDX_STR(id) "i" 342 | #endif 343 | // spiffs_printf argument for a data page for given object id 344 | #ifndef SPIFFS_TEST_VIS_DATA_STR 345 | #define SPIFFS_TEST_VIS_DATA_STR(id) "d" 346 | #endif 347 | #endif 348 | 349 | #ifndef SPIFFS_SECURE_ERASE 350 | #define SPIFFS_SECURE_ERASE 0 351 | #endif 352 | 353 | // Types depending on configuration such as the amount of flash bytes 354 | // given to spiffs file system in total (spiffs_file_system_size), 355 | // the logical block size (log_block_size), and the logical page size 356 | // (log_page_size) 357 | 358 | // Block index type. Make sure the size of this type can hold 359 | // the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size 360 | typedef u16_t spiffs_block_ix; 361 | // Page index type. Make sure the size of this type can hold 362 | // the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size 363 | typedef u16_t spiffs_page_ix; 364 | // Object id type - most significant bit is reserved for index flag. Make sure the 365 | // size of this type can hold the highest object id on a full system, 366 | // i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2 367 | typedef u16_t spiffs_obj_id; 368 | // Object span index type. Make sure the size of this type can 369 | // hold the largest possible span index on the system - 370 | // i.e. (spiffs_file_system_size / log_page_size) - 1 371 | typedef u16_t spiffs_span_ix; 372 | 373 | #endif /* SPIFFS_CONFIG_H_ */ 374 | -------------------------------------------------------------------------------- /src/spiffs_gc.c: -------------------------------------------------------------------------------- 1 | #include "spiffs.h" 2 | #include "spiffs_nucleus.h" 3 | 4 | #if !SPIFFS_READ_ONLY 5 | 6 | // Erases a logical block and updates the erase counter. 7 | // If cache is enabled, all pages that might be cached in this block 8 | // is dropped. 9 | static s32_t spiffs_gc_erase_block( 10 | spiffs *fs, 11 | spiffs_block_ix bix) { 12 | s32_t res; 13 | 14 | SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix); 15 | res = spiffs_erase_block(fs, bix); 16 | SPIFFS_CHECK_RES(res); 17 | 18 | #if SPIFFS_CACHE 19 | { 20 | u32_t i; 21 | for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) { 22 | spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i); 23 | } 24 | } 25 | #endif 26 | return res; 27 | } 28 | 29 | // Searches for blocks where all entries are deleted - if one is found, 30 | // the block is erased. Compared to the non-quick gc, the quick one ensures 31 | // that no updates are needed on existing objects on pages that are erased. 32 | s32_t spiffs_gc_quick( 33 | spiffs *fs, u16_t max_free_pages) { 34 | s32_t res = SPIFFS_OK; 35 | u32_t blocks = fs->block_count; 36 | spiffs_block_ix cur_block = 0; 37 | u32_t cur_block_addr = 0; 38 | int cur_entry = 0; 39 | spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; 40 | 41 | SPIFFS_GC_DBG("gc_quick: running\n"); 42 | #if SPIFFS_GC_STATS 43 | fs->stats_gc_runs++; 44 | #endif 45 | 46 | int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); 47 | 48 | // find fully deleted blocks 49 | // check each block 50 | while (res == SPIFFS_OK && blocks--) { 51 | u16_t deleted_pages_in_block = 0; 52 | u16_t free_pages_in_block = 0; 53 | 54 | int obj_lookup_page = 0; 55 | // check each object lookup page 56 | while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { 57 | int entry_offset = obj_lookup_page * entries_per_page; 58 | res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, 59 | 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); 60 | // check each entry 61 | while (res == SPIFFS_OK && 62 | cur_entry - entry_offset < entries_per_page && 63 | cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { 64 | spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; 65 | if (obj_id == SPIFFS_OBJ_ID_DELETED) { 66 | deleted_pages_in_block++; 67 | } else if (obj_id == SPIFFS_OBJ_ID_FREE) { 68 | // kill scan, go for next block 69 | free_pages_in_block++; 70 | if (free_pages_in_block > max_free_pages) { 71 | obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); 72 | res = 1; // kill object lu loop 73 | break; 74 | } 75 | } else { 76 | // kill scan, go for next block 77 | obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); 78 | res = 1; // kill object lu loop 79 | break; 80 | } 81 | cur_entry++; 82 | } // per entry 83 | obj_lookup_page++; 84 | } // per object lookup page 85 | if (res == 1) res = SPIFFS_OK; 86 | 87 | if (res == SPIFFS_OK && 88 | deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) && 89 | free_pages_in_block <= max_free_pages) { 90 | // found a fully deleted block 91 | fs->stats_p_deleted -= deleted_pages_in_block; 92 | res = spiffs_gc_erase_block(fs, cur_block); 93 | return res; 94 | } 95 | 96 | cur_entry = 0; 97 | cur_block++; 98 | cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); 99 | } // per block 100 | 101 | if (res == SPIFFS_OK) { 102 | res = SPIFFS_ERR_NO_DELETED_BLOCKS; 103 | } 104 | return res; 105 | } 106 | 107 | // Checks if garbage collecting is necessary. If so a candidate block is found, 108 | // cleansed and erased 109 | s32_t spiffs_gc_check( 110 | spiffs *fs, 111 | u32_t len) { 112 | s32_t res; 113 | s32_t free_pages = 114 | (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2) 115 | - fs->stats_p_allocated - fs->stats_p_deleted; 116 | int tries = 0; 117 | 118 | if (fs->free_blocks > 3 && 119 | (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { 120 | return SPIFFS_OK; 121 | } 122 | 123 | u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); 124 | // if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { 125 | // SPIFFS_GC_DBG("gc: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); 126 | // return SPIFFS_ERR_FULL; 127 | // } 128 | if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) { 129 | SPIFFS_GC_DBG("gc_check: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); 130 | return SPIFFS_ERR_FULL; 131 | } 132 | 133 | do { 134 | SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n", 135 | tries, 136 | fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted), 137 | len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); 138 | 139 | spiffs_block_ix *cands; 140 | int count; 141 | spiffs_block_ix cand; 142 | s32_t prev_free_pages = free_pages; 143 | // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state 144 | res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0); 145 | SPIFFS_CHECK_RES(res); 146 | if (count == 0) { 147 | SPIFFS_GC_DBG("gc_check: no candidates, return\n"); 148 | return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL; 149 | } 150 | #if SPIFFS_GC_STATS 151 | fs->stats_gc_runs++; 152 | #endif 153 | cand = cands[0]; 154 | fs->cleaning = 1; 155 | //SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand); 156 | res = spiffs_gc_clean(fs, cand); 157 | fs->cleaning = 0; 158 | if (res < 0) { 159 | SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); 160 | } else { 161 | SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); 162 | } 163 | SPIFFS_CHECK_RES(res); 164 | 165 | res = spiffs_gc_erase_page_stats(fs, cand); 166 | SPIFFS_CHECK_RES(res); 167 | 168 | res = spiffs_gc_erase_block(fs, cand); 169 | SPIFFS_CHECK_RES(res); 170 | 171 | free_pages = 172 | (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) 173 | - fs->stats_p_allocated - fs->stats_p_deleted; 174 | 175 | if (prev_free_pages <= 0 && prev_free_pages == free_pages) { 176 | // abort early to reduce wear, at least tried once 177 | SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n"); 178 | break; 179 | } 180 | 181 | } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || 182 | (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); 183 | 184 | free_pages = 185 | (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) 186 | - fs->stats_p_allocated - fs->stats_p_deleted; 187 | if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { 188 | res = SPIFFS_ERR_FULL; 189 | } 190 | 191 | SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n", 192 | fs->stats_p_allocated + fs->stats_p_deleted, 193 | fs->free_blocks, free_pages, tries, res); 194 | 195 | return res; 196 | } 197 | 198 | // Updates page statistics for a block that is about to be erased 199 | s32_t spiffs_gc_erase_page_stats( 200 | spiffs *fs, 201 | spiffs_block_ix bix) { 202 | s32_t res = SPIFFS_OK; 203 | int obj_lookup_page = 0; 204 | int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); 205 | spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; 206 | int cur_entry = 0; 207 | u32_t dele = 0; 208 | u32_t allo = 0; 209 | 210 | // check each object lookup page 211 | while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { 212 | int entry_offset = obj_lookup_page * entries_per_page; 213 | res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, 214 | 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); 215 | // check each entry 216 | while (res == SPIFFS_OK && 217 | cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { 218 | spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; 219 | if (obj_id == SPIFFS_OBJ_ID_FREE) { 220 | } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { 221 | dele++; 222 | } else { 223 | allo++; 224 | } 225 | cur_entry++; 226 | } // per entry 227 | obj_lookup_page++; 228 | } // per object lookup page 229 | SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele); 230 | fs->stats_p_allocated -= allo; 231 | fs->stats_p_deleted -= dele; 232 | return res; 233 | } 234 | 235 | // Finds block candidates to erase 236 | s32_t spiffs_gc_find_candidate( 237 | spiffs *fs, 238 | spiffs_block_ix **block_candidates, 239 | int *candidate_count, 240 | char fs_crammed) { 241 | s32_t res = SPIFFS_OK; 242 | u32_t blocks = fs->block_count; 243 | spiffs_block_ix cur_block = 0; 244 | u32_t cur_block_addr = 0; 245 | spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; 246 | int cur_entry = 0; 247 | 248 | // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score 249 | int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t))); 250 | *candidate_count = 0; 251 | memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); 252 | 253 | // divide up work area into block indices and scores 254 | spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; 255 | s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); 256 | 257 | // align cand_scores on s32_t boundary 258 | cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1)); 259 | 260 | *block_candidates = cand_blocks; 261 | 262 | int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); 263 | 264 | // check each block 265 | while (res == SPIFFS_OK && blocks--) { 266 | u16_t deleted_pages_in_block = 0; 267 | u16_t used_pages_in_block = 0; 268 | 269 | int obj_lookup_page = 0; 270 | // check each object lookup page 271 | while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { 272 | int entry_offset = obj_lookup_page * entries_per_page; 273 | res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, 274 | 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); 275 | // check each entry 276 | while (res == SPIFFS_OK && 277 | cur_entry - entry_offset < entries_per_page && 278 | cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { 279 | spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; 280 | if (obj_id == SPIFFS_OBJ_ID_FREE) { 281 | // when a free entry is encountered, scan logic ensures that all following entries are free also 282 | res = 1; // kill object lu loop 283 | break; 284 | } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { 285 | deleted_pages_in_block++; 286 | } else { 287 | used_pages_in_block++; 288 | } 289 | cur_entry++; 290 | } // per entry 291 | obj_lookup_page++; 292 | } // per object lookup page 293 | if (res == 1) res = SPIFFS_OK; 294 | 295 | // calculate score and insert into candidate table 296 | // stoneage sort, but probably not so many blocks 297 | if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) { 298 | // read erase count 299 | spiffs_obj_id erase_count; 300 | res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, 301 | SPIFFS_ERASE_COUNT_PADDR(fs, cur_block), 302 | sizeof(spiffs_obj_id), (u8_t *)&erase_count); 303 | SPIFFS_CHECK_RES(res); 304 | 305 | spiffs_obj_id erase_age; 306 | if (fs->max_erase_count > erase_count) { 307 | erase_age = fs->max_erase_count - erase_count; 308 | } else { 309 | erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count); 310 | } 311 | 312 | s32_t score = 313 | deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + 314 | used_pages_in_block * SPIFFS_GC_HEUR_W_USED + 315 | erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); 316 | int cand_ix = 0; 317 | SPIFFS_GC_DBG("gc_check: bix:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); 318 | while (cand_ix < max_candidates) { 319 | if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { 320 | cand_blocks[cand_ix] = cur_block; 321 | cand_scores[cand_ix] = score; 322 | break; 323 | } else if (cand_scores[cand_ix] < score) { 324 | int reorder_cand_ix = max_candidates - 2; 325 | while (reorder_cand_ix >= cand_ix) { 326 | cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix]; 327 | cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix]; 328 | reorder_cand_ix--; 329 | } 330 | cand_blocks[cand_ix] = cur_block; 331 | cand_scores[cand_ix] = score; 332 | break; 333 | } 334 | cand_ix++; 335 | } 336 | (*candidate_count)++; 337 | } 338 | 339 | cur_entry = 0; 340 | cur_block++; 341 | cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); 342 | } // per block 343 | 344 | return res; 345 | } 346 | 347 | typedef enum { 348 | FIND_OBJ_DATA, 349 | MOVE_OBJ_DATA, 350 | MOVE_OBJ_IX, 351 | FINISHED 352 | } spiffs_gc_clean_state; 353 | 354 | typedef struct { 355 | spiffs_gc_clean_state state; 356 | spiffs_obj_id cur_obj_id; 357 | spiffs_span_ix cur_objix_spix; 358 | spiffs_page_ix cur_objix_pix; 359 | spiffs_page_ix cur_data_pix; 360 | int stored_scan_entry_index; 361 | u8_t obj_id_found; 362 | } spiffs_gc; 363 | 364 | // Empties given block by moving all data into free pages of another block 365 | // Strategy: 366 | // loop: 367 | // scan object lookup for object data pages 368 | // for first found id, check spix and load corresponding object index page to memory 369 | // push object scan lookup entry index 370 | // rescan object lookup, find data pages with same id and referenced by same object index 371 | // move data page, update object index in memory 372 | // when reached end of lookup, store updated object index 373 | // pop object scan lookup entry index 374 | // repeat loop until end of object lookup 375 | // scan object lookup again for remaining object index pages, move to new page in other block 376 | // 377 | s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { 378 | s32_t res = SPIFFS_OK; 379 | const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); 380 | // this is the global localizer being pushed and popped 381 | int cur_entry = 0; 382 | spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; 383 | spiffs_gc gc; // our stack frame/state 384 | spiffs_page_ix cur_pix = 0; 385 | spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; 386 | spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; 387 | 388 | SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix); 389 | 390 | memset(&gc, 0, sizeof(spiffs_gc)); 391 | gc.state = FIND_OBJ_DATA; 392 | 393 | if (fs->free_cursor_block_ix == bix) { 394 | // move free cursor to next block, cannot use free pages from the block we want to clean 395 | fs->free_cursor_block_ix = (bix+1)%fs->block_count; 396 | fs->free_cursor_obj_lu_entry = 0; 397 | SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix); 398 | } 399 | 400 | while (res == SPIFFS_OK && gc.state != FINISHED) { 401 | SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry); 402 | gc.obj_id_found = 0; // reset (to no found data page) 403 | 404 | // scan through lookup pages 405 | int obj_lookup_page = cur_entry / entries_per_page; 406 | u8_t scan = 1; 407 | // check each object lookup page 408 | while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { 409 | int entry_offset = obj_lookup_page * entries_per_page; 410 | res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, 411 | 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), 412 | SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); 413 | // check each object lookup entry 414 | while (scan && res == SPIFFS_OK && 415 | cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { 416 | spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; 417 | cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry); 418 | 419 | // act upon object id depending on gc state 420 | switch (gc.state) { 421 | case FIND_OBJ_DATA: 422 | // find a data page 423 | if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && 424 | ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) { 425 | // found a data page, stop scanning and handle in switch case below 426 | SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id); 427 | gc.obj_id_found = 1; 428 | gc.cur_obj_id = obj_id; 429 | gc.cur_data_pix = cur_pix; 430 | scan = 0; 431 | } 432 | break; 433 | case MOVE_OBJ_DATA: 434 | // evacuate found data pages for corresponding object index we have in memory, 435 | // update memory representation 436 | if (obj_id == gc.cur_obj_id) { 437 | spiffs_page_header p_hdr; 438 | res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 439 | 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); 440 | SPIFFS_CHECK_RES(res); 441 | SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); 442 | if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) { 443 | SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n"); 444 | } else { 445 | spiffs_page_ix new_data_pix; 446 | if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { 447 | // move page 448 | res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix); 449 | SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); 450 | SPIFFS_CHECK_RES(res); 451 | // move wipes obj_lu, reload it 452 | res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, 453 | 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), 454 | SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); 455 | SPIFFS_CHECK_RES(res); 456 | } else { 457 | // page is deleted but not deleted in lookup, scrap it - 458 | // might seem unnecessary as we will erase this block, but 459 | // we might get aborted 460 | SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); 461 | res = spiffs_page_delete(fs, cur_pix); 462 | SPIFFS_CHECK_RES(res); 463 | new_data_pix = SPIFFS_OBJ_ID_FREE; 464 | } 465 | // update memory representation of object index page with new data page 466 | if (gc.cur_objix_spix == 0) { 467 | // update object index header page 468 | ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; 469 | SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); 470 | } else { 471 | // update object index page 472 | ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; 473 | SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); 474 | } 475 | } 476 | } 477 | break; 478 | case MOVE_OBJ_IX: 479 | // find and evacuate object index pages 480 | if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && 481 | (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { 482 | // found an index object id 483 | spiffs_page_header p_hdr; 484 | spiffs_page_ix new_pix; 485 | // load header 486 | res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 487 | 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); 488 | SPIFFS_CHECK_RES(res); 489 | if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { 490 | // move page 491 | res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix); 492 | SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); 493 | SPIFFS_CHECK_RES(res); 494 | spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr, 495 | SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0); 496 | // move wipes obj_lu, reload it 497 | res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, 498 | 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), 499 | SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); 500 | SPIFFS_CHECK_RES(res); 501 | } else { 502 | // page is deleted but not deleted in lookup, scrap it - 503 | // might seem unnecessary as we will erase this block, but 504 | // we might get aborted 505 | SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); 506 | res = spiffs_page_delete(fs, cur_pix); 507 | if (res == SPIFFS_OK) { 508 | spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, 509 | SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); 510 | } 511 | } 512 | SPIFFS_CHECK_RES(res); 513 | } 514 | break; 515 | default: 516 | scan = 0; 517 | break; 518 | } // switch gc state 519 | cur_entry++; 520 | } // per entry 521 | obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop 522 | } // per object lookup page 523 | if (res != SPIFFS_OK) break; 524 | 525 | // state finalization and switch 526 | switch (gc.state) { 527 | case FIND_OBJ_DATA: 528 | if (gc.obj_id_found) { 529 | // handle found data page - 530 | // find out corresponding obj ix page and load it to memory 531 | spiffs_page_header p_hdr; 532 | spiffs_page_ix objix_pix; 533 | gc.stored_scan_entry_index = cur_entry; // push cursor 534 | cur_entry = 0; // restart scan from start 535 | gc.state = MOVE_OBJ_DATA; 536 | res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 537 | 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); 538 | SPIFFS_CHECK_RES(res); 539 | gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); 540 | SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix); 541 | res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix); 542 | if (res == SPIFFS_ERR_NOT_FOUND) { 543 | // on borked systems we might get an ERR_NOT_FOUND here - 544 | // this is handled by simply deleting the page as it is not referenced 545 | // from anywhere 546 | SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix); 547 | res = spiffs_page_delete(fs, gc.cur_data_pix); 548 | SPIFFS_CHECK_RES(res); 549 | // then we restore states and continue scanning for data pages 550 | cur_entry = gc.stored_scan_entry_index; // pop cursor 551 | gc.state = FIND_OBJ_DATA; 552 | break; // done 553 | } 554 | SPIFFS_CHECK_RES(res); 555 | SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix); 556 | res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 557 | 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); 558 | SPIFFS_CHECK_RES(res); 559 | // cannot allow a gc if the presumed index in fact is no index, a 560 | // check must run or lot of data may be lost 561 | SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix); 562 | gc.cur_objix_pix = objix_pix; 563 | } else { 564 | // no more data pages found, passed thru all block, start evacuating object indices 565 | gc.state = MOVE_OBJ_IX; 566 | cur_entry = 0; // restart entry scan index 567 | } 568 | break; 569 | case MOVE_OBJ_DATA: { 570 | // store modified objix (hdr) page residing in memory now that all 571 | // data pages belonging to this object index and residing in the block 572 | // we want to evacuate 573 | spiffs_page_ix new_objix_pix; 574 | gc.state = FIND_OBJ_DATA; 575 | cur_entry = gc.stored_scan_entry_index; // pop cursor 576 | if (gc.cur_objix_spix == 0) { 577 | // store object index header page 578 | res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix); 579 | SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0); 580 | SPIFFS_CHECK_RES(res); 581 | } else { 582 | // store object index page 583 | res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix); 584 | SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix); 585 | SPIFFS_CHECK_RES(res); 586 | spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, 587 | SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); 588 | } 589 | } 590 | break; 591 | case MOVE_OBJ_IX: 592 | // scanned thru all block, no more object indices found - our work here is done 593 | gc.state = FINISHED; 594 | break; 595 | default: 596 | cur_entry = 0; 597 | break; 598 | } // switch gc.state 599 | SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state); 600 | } // while state != FINISHED 601 | 602 | 603 | return res; 604 | } 605 | 606 | #endif // !SPIFFS_READ_ONLY 607 | -------------------------------------------------------------------------------- /src/spiffs_nucleus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spiffs_nucleus.h 3 | * 4 | * Created on: Jun 15, 2013 5 | * Author: petera 6 | */ 7 | 8 | /* SPIFFS layout 9 | * 10 | * spiffs is designed for following spi flash characteristics: 11 | * - only big areas of data (blocks) can be erased 12 | * - erasing resets all bits in a block to ones 13 | * - writing pulls ones to zeroes 14 | * - zeroes cannot be pulled to ones, without erase 15 | * - wear leveling 16 | * 17 | * spiffs is also meant to be run on embedded, memory constraint devices. 18 | * 19 | * Entire area is divided in blocks. Entire area is also divided in pages. 20 | * Each block contains same number of pages. A page cannot be erased, but a 21 | * block can be erased. 22 | * 23 | * Entire area must be block_size * x 24 | * page_size must be block_size / (2^y) where y > 2 25 | * 26 | * ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes 27 | * 28 | * BLOCK 0 PAGE 0 object lookup 1 29 | * PAGE 1 object lookup 2 30 | * ... 31 | * PAGE n-1 object lookup n 32 | * PAGE n object data 1 33 | * PAGE n+1 object data 2 34 | * ... 35 | * PAGE n+m-1 object data m 36 | * 37 | * BLOCK 1 PAGE n+m object lookup 1 38 | * PAGE n+m+1 object lookup 2 39 | * ... 40 | * PAGE 2n+m-1 object lookup n 41 | * PAGE 2n+m object data 1 42 | * PAGE 2n+m object data 2 43 | * ... 44 | * PAGE 2n+2m-1 object data m 45 | * ... 46 | * 47 | * n is number of object lookup pages, which is number of pages needed to index all pages 48 | * in a block by object id 49 | * : block_size / page_size * sizeof(obj_id) / page_size 50 | * m is number data pages, which is number of pages in block minus number of lookup pages 51 | * : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size 52 | * thus, n+m is total number of pages in a block 53 | * : block_size / page_size 54 | * 55 | * ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256 56 | * 57 | * Object lookup pages contain object id entries. Each entry represent the corresponding 58 | * data page. 59 | * Assuming a 16 bit object id, an object id being 0xffff represents a free page. 60 | * An object id being 0x0000 represents a deleted page. 61 | * 62 | * ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff .. 63 | * page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff .. 64 | * page 2 : data : data for object id 0008 65 | * page 3 : data : data for object id 0001 66 | * page 4 : data : data for object id 0aaa 67 | * ... 68 | * 69 | * 70 | * Object data pages can be either object index pages or object content. 71 | * All object data pages contains a data page header, containing object id and span index. 72 | * The span index denotes the object page ordering amongst data pages with same object id. 73 | * This applies to both object index pages (when index spans more than one page of entries), 74 | * and object data pages. 75 | * An object index page contains page entries pointing to object content page. The entry index 76 | * in a object index page correlates to the span index in the actual object data page. 77 | * The first object index page (span index 0) is called object index header page, and also 78 | * contains object flags (directory/file), size, object name etc. 79 | * 80 | * ex: 81 | * BLOCK 1 82 | * PAGE 256: objectl lookup page 1 83 | * [*123] [ 123] [ 123] [ 123] 84 | * [ 123] [*123] [ 123] [ 123] 85 | * [free] [free] [free] [free] ... 86 | * PAGE 257: objectl lookup page 2 87 | * [free] [free] [free] [free] ... 88 | * PAGE 258: object index page (header) 89 | * obj.id:0123 span.ix:0000 flags:INDEX 90 | * size:1600 name:ex.txt type:file 91 | * [259] [260] [261] [262] 92 | * PAGE 259: object data page 93 | * obj.id:0123 span.ix:0000 flags:DATA 94 | * PAGE 260: object data page 95 | * obj.id:0123 span.ix:0001 flags:DATA 96 | * PAGE 261: object data page 97 | * obj.id:0123 span.ix:0002 flags:DATA 98 | * PAGE 262: object data page 99 | * obj.id:0123 span.ix:0003 flags:DATA 100 | * PAGE 263: object index page 101 | * obj.id:0123 span.ix:0001 flags:INDEX 102 | * [264] [265] [fre] [fre] 103 | * [fre] [fre] [fre] [fre] 104 | * PAGE 264: object data page 105 | * obj.id:0123 span.ix:0004 flags:DATA 106 | * PAGE 265: object data page 107 | * obj.id:0123 span.ix:0005 flags:DATA 108 | * 109 | */ 110 | #ifndef SPIFFS_NUCLEUS_H_ 111 | #define SPIFFS_NUCLEUS_H_ 112 | 113 | #define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1) 114 | #define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1) 115 | #define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2) 116 | #define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3) 117 | #define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4) 118 | 119 | // visitor result, continue searching 120 | #define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20) 121 | // visitor result, continue searching after reloading lu buffer 122 | #define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21) 123 | // visitor result, stop searching 124 | #define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22) 125 | 126 | // updating an object index contents 127 | #define SPIFFS_EV_IX_UPD (0) 128 | // creating a new object index 129 | #define SPIFFS_EV_IX_NEW (1) 130 | // deleting an object index 131 | #define SPIFFS_EV_IX_DEL (2) 132 | // moving an object index without updating contents 133 | #define SPIFFS_EV_IX_MOV (3) 134 | // updating an object index header data only, not the table itself 135 | #define SPIFFS_EV_IX_UPD_HDR (4) 136 | 137 | #define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1))) 138 | 139 | #define SPIFFS_UNDEFINED_LEN (u32_t)(-1) 140 | 141 | #define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) 142 | #define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) 143 | 144 | 145 | 146 | #if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__) 147 | /* For GCC, clang and TI compilers */ 148 | #define SPIFFS_PACKED __attribute__((packed)) 149 | #elif defined(__ICCARM__) || defined(__CC_ARM) 150 | /* For IAR ARM and Keil MDK-ARM compilers */ 151 | #define SPIFFS_PACKED 152 | 153 | #else 154 | /* Unknown compiler */ 155 | #define SPIFFS_PACKED 156 | #endif 157 | 158 | 159 | 160 | #if SPIFFS_USE_MAGIC 161 | #if !SPIFFS_USE_MAGIC_LENGTH 162 | #define SPIFFS_MAGIC(fs, bix) \ 163 | ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) 164 | #else // SPIFFS_USE_MAGIC_LENGTH 165 | #define SPIFFS_MAGIC(fs, bix) \ 166 | ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) 167 | #endif // SPIFFS_USE_MAGIC_LENGTH 168 | #endif // SPIFFS_USE_MAGIC 169 | 170 | #define SPIFFS_CONFIG_MAGIC (0x20090315) 171 | 172 | #if SPIFFS_SINGLETON == 0 173 | #define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ 174 | ((fs)->cfg.log_page_size) 175 | #define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \ 176 | ((fs)->cfg.log_block_size) 177 | #define SPIFFS_CFG_PHYS_SZ(fs) \ 178 | ((fs)->cfg.phys_size) 179 | #define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \ 180 | ((fs)->cfg.phys_erase_block) 181 | #define SPIFFS_CFG_PHYS_ADDR(fs) \ 182 | ((fs)->cfg.phys_addr) 183 | #endif 184 | 185 | // total number of pages 186 | #define SPIFFS_MAX_PAGES(fs) \ 187 | ( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) 188 | // total number of pages per block, including object lookup pages 189 | #define SPIFFS_PAGES_PER_BLOCK(fs) \ 190 | ( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) 191 | // number of object lookup pages per block 192 | #define SPIFFS_OBJ_LOOKUP_PAGES(fs) \ 193 | (MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) ) 194 | // checks if page index belongs to object lookup 195 | #define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \ 196 | (((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) 197 | // number of object lookup entries in all object lookup pages 198 | #define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \ 199 | (SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) 200 | // converts a block to physical address 201 | #define SPIFFS_BLOCK_TO_PADDR(fs, block) \ 202 | ( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) ) 203 | // converts a object lookup entry to page index 204 | #define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \ 205 | ((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry)) 206 | // converts a object lookup entry to physical address of corresponding page 207 | #define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \ 208 | (SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) 209 | // converts a page to physical address 210 | #define SPIFFS_PAGE_TO_PADDR(fs, page) \ 211 | ( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) 212 | // converts a physical address to page 213 | #define SPIFFS_PADDR_TO_PAGE(fs, addr) \ 214 | ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) ) 215 | // gives index in page for a physical address 216 | #define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \ 217 | ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) ) 218 | // returns containing block for given page 219 | #define SPIFFS_BLOCK_FOR_PAGE(fs, page) \ 220 | ( (page) / SPIFFS_PAGES_PER_BLOCK(fs) ) 221 | // returns starting page for block 222 | #define SPIFFS_PAGE_FOR_BLOCK(fs, block) \ 223 | ( (block) * SPIFFS_PAGES_PER_BLOCK(fs) ) 224 | // converts page to entry in object lookup page 225 | #define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \ 226 | ( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) ) 227 | // returns data size in a data page 228 | #define SPIFFS_DATA_PAGE_SIZE(fs) \ 229 | ( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) 230 | // returns physical address for block's erase count, 231 | // always in the physical last entry of the last object lookup page 232 | #define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \ 233 | ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) 234 | // returns physical address for block's magic, 235 | // always in the physical second last entry of the last object lookup page 236 | #define SPIFFS_MAGIC_PADDR(fs, bix) \ 237 | ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 ) 238 | // checks if there is any room for magic in the object luts 239 | #define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \ 240 | ( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \ 241 | <= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) ) 242 | 243 | // define helpers object 244 | 245 | // entries in an object header page index 246 | #define SPIFFS_OBJ_HDR_IX_LEN(fs) \ 247 | ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) 248 | // entries in an object page index 249 | #define SPIFFS_OBJ_IX_LEN(fs) \ 250 | ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) 251 | // object index entry for given data span index 252 | #define SPIFFS_OBJ_IX_ENTRY(fs, spix) \ 253 | ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs))) 254 | // object index span index number for given data span index or entry 255 | #define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \ 256 | ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) 257 | // get data span index for object index span index 258 | #define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \ 259 | ( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) ) 260 | 261 | #if SPIFFS_FILEHDL_OFFSET 262 | #define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0) 263 | #define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0) 264 | #else 265 | #define SPIFFS_FH_OFFS(fs, fh) ((spiffs_file)(fh)) 266 | #define SPIFFS_FH_UNOFFS(fs, fh) ((spiffs_file)(fh)) 267 | #endif 268 | 269 | 270 | #define SPIFFS_OP_T_OBJ_LU (0<<0) 271 | #define SPIFFS_OP_T_OBJ_LU2 (1<<0) 272 | #define SPIFFS_OP_T_OBJ_IX (2<<0) 273 | #define SPIFFS_OP_T_OBJ_DA (3<<0) 274 | #define SPIFFS_OP_C_DELE (0<<2) 275 | #define SPIFFS_OP_C_UPDT (1<<2) 276 | #define SPIFFS_OP_C_MOVS (2<<2) 277 | #define SPIFFS_OP_C_MOVD (3<<2) 278 | #define SPIFFS_OP_C_FLSH (4<<2) 279 | #define SPIFFS_OP_C_READ (5<<2) 280 | #define SPIFFS_OP_C_WRTHRU (6<<2) 281 | 282 | #define SPIFFS_OP_TYPE_MASK (3<<0) 283 | #define SPIFFS_OP_COM_MASK (7<<2) 284 | 285 | 286 | // if 0, this page is written to, else clean 287 | #define SPIFFS_PH_FLAG_USED (1<<0) 288 | // if 0, writing is finalized, else under modification 289 | #define SPIFFS_PH_FLAG_FINAL (1<<1) 290 | // if 0, this is an index page, else a data page 291 | #define SPIFFS_PH_FLAG_INDEX (1<<2) 292 | // if 0, page is deleted, else valid 293 | #define SPIFFS_PH_FLAG_DELET (1<<7) 294 | // if 0, this index header is being deleted 295 | #define SPIFFS_PH_FLAG_IXDELE (1<<6) 296 | 297 | 298 | #define SPIFFS_CHECK_MOUNT(fs) \ 299 | ((fs)->mounted != 0) 300 | 301 | #define SPIFFS_CHECK_CFG(fs) \ 302 | ((fs)->config_magic == SPIFFS_CONFIG_MAGIC) 303 | 304 | #define SPIFFS_CHECK_RES(res) \ 305 | do { \ 306 | if ((res) < SPIFFS_OK) return (res); \ 307 | } while (0); 308 | 309 | #define SPIFFS_API_CHECK_MOUNT(fs) \ 310 | if (!SPIFFS_CHECK_MOUNT((fs))) { \ 311 | (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ 312 | return SPIFFS_ERR_NOT_MOUNTED; \ 313 | } 314 | 315 | #define SPIFFS_API_CHECK_CFG(fs) \ 316 | if (!SPIFFS_CHECK_CFG((fs))) { \ 317 | (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ 318 | return SPIFFS_ERR_NOT_CONFIGURED; \ 319 | } 320 | 321 | #define SPIFFS_API_CHECK_RES(fs, res) \ 322 | if ((res) < SPIFFS_OK) { \ 323 | (fs)->err_code = (res); \ 324 | return (res); \ 325 | } 326 | 327 | #define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ 328 | if ((res) < SPIFFS_OK) { \ 329 | (fs)->err_code = (res); \ 330 | SPIFFS_UNLOCK(fs); \ 331 | return (res); \ 332 | } 333 | 334 | #define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ 335 | if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ 336 | if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ 337 | if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ 338 | if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \ 339 | if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \ 340 | if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH; 341 | //if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED; 342 | 343 | #define SPIFFS_VALIDATE_DATA(ph, objid, spix) \ 344 | if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ 345 | if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ 346 | if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ 347 | if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \ 348 | if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \ 349 | if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; 350 | 351 | 352 | // check id, only visit matching objec ids 353 | #define SPIFFS_VIS_CHECK_ID (1<<0) 354 | // report argument object id to visitor - else object lookup id is reported 355 | #define SPIFFS_VIS_CHECK_PH (1<<1) 356 | // stop searching at end of all look up pages 357 | #define SPIFFS_VIS_NO_WRAP (1<<2) 358 | 359 | #if SPIFFS_HAL_CALLBACK_EXTRA 360 | 361 | #define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ 362 | (_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src)) 363 | #define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ 364 | (_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst)) 365 | #define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ 366 | (_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len)) 367 | 368 | #else // SPIFFS_HAL_CALLBACK_EXTRA 369 | 370 | #define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ 371 | (_fs)->cfg.hal_write_f((_paddr), (_len), (_src)) 372 | #define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ 373 | (_fs)->cfg.hal_read_f((_paddr), (_len), (_dst)) 374 | #define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ 375 | (_fs)->cfg.hal_erase_f((_paddr), (_len)) 376 | 377 | #endif // SPIFFS_HAL_CALLBACK_EXTRA 378 | 379 | #if SPIFFS_CACHE 380 | 381 | #define SPIFFS_CACHE_FLAG_DIRTY (1<<0) 382 | #define SPIFFS_CACHE_FLAG_WRTHRU (1<<1) 383 | #define SPIFFS_CACHE_FLAG_OBJLU (1<<2) 384 | #define SPIFFS_CACHE_FLAG_OBJIX (1<<3) 385 | #define SPIFFS_CACHE_FLAG_DATA (1<<4) 386 | #define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7) 387 | 388 | #define SPIFFS_CACHE_PAGE_SIZE(fs) \ 389 | (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)) 390 | 391 | #define spiffs_get_cache(fs) \ 392 | ((spiffs_cache *)((fs)->cache)) 393 | 394 | #define spiffs_get_cache_page_hdr(fs, c, ix) \ 395 | ((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)]))) 396 | 397 | #define spiffs_get_cache_page(fs, c, ix) \ 398 | ((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page)) 399 | 400 | // cache page struct 401 | typedef struct { 402 | // cache flags 403 | u8_t flags; 404 | // cache page index 405 | u8_t ix; 406 | // last access of this cache page 407 | u32_t last_access; 408 | union { 409 | // type read cache 410 | struct { 411 | // read cache page index 412 | spiffs_page_ix pix; 413 | }; 414 | #if SPIFFS_CACHE_WR 415 | // type write cache 416 | struct { 417 | // write cache 418 | spiffs_obj_id obj_id; 419 | // offset in cache page 420 | u32_t offset; 421 | // size of cache page 422 | u16_t size; 423 | }; 424 | #endif 425 | }; 426 | } spiffs_cache_page; 427 | 428 | // cache struct 429 | typedef struct { 430 | u8_t cpage_count; 431 | u32_t last_access; 432 | u32_t cpage_use_map; 433 | u32_t cpage_use_mask; 434 | u8_t *cpages; 435 | } spiffs_cache; 436 | 437 | #endif 438 | 439 | 440 | // spiffs nucleus file descriptor 441 | typedef struct { 442 | // the filesystem of this descriptor 443 | spiffs *fs; 444 | // number of file descriptor - if 0, the file descriptor is closed 445 | spiffs_file file_nbr; 446 | // object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted 447 | spiffs_obj_id obj_id; 448 | // size of the file 449 | u32_t size; 450 | // cached object index header page index 451 | spiffs_page_ix objix_hdr_pix; 452 | // cached offset object index page index 453 | spiffs_page_ix cursor_objix_pix; 454 | // cached offset object index span index 455 | spiffs_span_ix cursor_objix_spix; 456 | // current absolute offset 457 | u32_t offset; 458 | // current file descriptor offset (cached) 459 | u32_t fdoffset; 460 | // fd flags 461 | spiffs_flags flags; 462 | #if SPIFFS_CACHE_WR 463 | spiffs_cache_page *cache_page; 464 | #endif 465 | #if SPIFFS_TEMPORAL_FD_CACHE 466 | // djb2 hash of filename 467 | u32_t name_hash; 468 | // hit score (score == 0 indicates never used fd) 469 | u16_t score; 470 | #endif 471 | #if SPIFFS_IX_MAP 472 | // spiffs index map, if 0 it means unmapped 473 | spiffs_ix_map *ix_map; 474 | #endif 475 | } spiffs_fd; 476 | 477 | 478 | // object structs 479 | 480 | // page header, part of each page except object lookup pages 481 | // NB: this is always aligned when the data page is an object index, 482 | // as in this case struct spiffs_page_object_ix is used 483 | typedef struct SPIFFS_PACKED { 484 | // object id 485 | spiffs_obj_id obj_id; 486 | // object span index 487 | spiffs_span_ix span_ix; 488 | // flags 489 | u8_t flags; 490 | } spiffs_page_header; 491 | 492 | // object index header page header 493 | typedef struct SPIFFS_PACKED 494 | #if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 495 | __attribute(( aligned(sizeof(spiffs_page_ix)) )) 496 | #endif 497 | { 498 | // common page header 499 | spiffs_page_header p_hdr; 500 | // alignment 501 | u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; 502 | // size of object 503 | u32_t size; 504 | // type of object 505 | spiffs_obj_type type; 506 | // name of object 507 | u8_t name[SPIFFS_OBJ_NAME_LEN]; 508 | #if SPIFFS_OBJ_META_LEN 509 | // metadata. not interpreted by SPIFFS in any way. 510 | u8_t meta[SPIFFS_OBJ_META_LEN]; 511 | #endif 512 | } spiffs_page_object_ix_header; 513 | 514 | // object index page header 515 | typedef struct SPIFFS_PACKED { 516 | spiffs_page_header p_hdr; 517 | u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; 518 | } spiffs_page_object_ix; 519 | 520 | // callback func for object lookup visitor 521 | typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, 522 | const void *user_const_p, void *user_var_p); 523 | 524 | 525 | #if SPIFFS_CACHE 526 | #define _spiffs_rd(fs, op, fh, addr, len, dst) \ 527 | spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst)) 528 | #define _spiffs_wr(fs, op, fh, addr, len, src) \ 529 | spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src)) 530 | #else 531 | #define _spiffs_rd(fs, op, fh, addr, len, dst) \ 532 | spiffs_phys_rd((fs), (addr), (len), (dst)) 533 | #define _spiffs_wr(fs, op, fh, addr, len, src) \ 534 | spiffs_phys_wr((fs), (addr), (len), (src)) 535 | #endif 536 | 537 | #ifndef MIN 538 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) 539 | #endif 540 | #ifndef MAX 541 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) 542 | #endif 543 | 544 | // --------------- 545 | 546 | s32_t spiffs_phys_rd( 547 | spiffs *fs, 548 | #if SPIFFS_CACHE 549 | u8_t op, 550 | spiffs_file fh, 551 | #endif 552 | u32_t addr, 553 | u32_t len, 554 | u8_t *dst); 555 | 556 | s32_t spiffs_phys_wr( 557 | spiffs *fs, 558 | #if SPIFFS_CACHE 559 | u8_t op, 560 | spiffs_file fh, 561 | #endif 562 | u32_t addr, 563 | u32_t len, 564 | u8_t *src); 565 | 566 | s32_t spiffs_phys_cpy( 567 | spiffs *fs, 568 | spiffs_file fh, 569 | u32_t dst, 570 | u32_t src, 571 | u32_t len); 572 | 573 | s32_t spiffs_phys_count_free_blocks( 574 | spiffs *fs); 575 | 576 | s32_t spiffs_obj_lu_find_entry_visitor( 577 | spiffs *fs, 578 | spiffs_block_ix starting_block, 579 | int starting_lu_entry, 580 | u8_t flags, 581 | spiffs_obj_id obj_id, 582 | spiffs_visitor_f v, 583 | const void *user_const_p, 584 | void *user_var_p, 585 | spiffs_block_ix *block_ix, 586 | int *lu_entry); 587 | 588 | s32_t spiffs_erase_block( 589 | spiffs *fs, 590 | spiffs_block_ix bix); 591 | 592 | #if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH 593 | s32_t spiffs_probe( 594 | spiffs_config *cfg); 595 | #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH 596 | 597 | // --------------- 598 | 599 | s32_t spiffs_obj_lu_scan( 600 | spiffs *fs); 601 | 602 | s32_t spiffs_obj_lu_find_free_obj_id( 603 | spiffs *fs, 604 | spiffs_obj_id *obj_id, 605 | const u8_t *conflicting_name); 606 | 607 | s32_t spiffs_obj_lu_find_free( 608 | spiffs *fs, 609 | spiffs_block_ix starting_block, 610 | int starting_lu_entry, 611 | spiffs_block_ix *block_ix, 612 | int *lu_entry); 613 | 614 | s32_t spiffs_obj_lu_find_id( 615 | spiffs *fs, 616 | spiffs_block_ix starting_block, 617 | int starting_lu_entry, 618 | spiffs_obj_id obj_id, 619 | spiffs_block_ix *block_ix, 620 | int *lu_entry); 621 | 622 | s32_t spiffs_obj_lu_find_id_and_span( 623 | spiffs *fs, 624 | spiffs_obj_id obj_id, 625 | spiffs_span_ix spix, 626 | spiffs_page_ix exclusion_pix, 627 | spiffs_page_ix *pix); 628 | 629 | s32_t spiffs_obj_lu_find_id_and_span_by_phdr( 630 | spiffs *fs, 631 | spiffs_obj_id obj_id, 632 | spiffs_span_ix spix, 633 | spiffs_page_ix exclusion_pix, 634 | spiffs_page_ix *pix); 635 | 636 | // --------------- 637 | 638 | s32_t spiffs_page_allocate_data( 639 | spiffs *fs, 640 | spiffs_obj_id obj_id, 641 | spiffs_page_header *ph, 642 | u8_t *data, 643 | u32_t len, 644 | u32_t page_offs, 645 | u8_t finalize, 646 | spiffs_page_ix *pix); 647 | 648 | s32_t spiffs_page_move( 649 | spiffs *fs, 650 | spiffs_file fh, 651 | u8_t *page_data, 652 | spiffs_obj_id obj_id, 653 | spiffs_page_header *page_hdr, 654 | spiffs_page_ix src_pix, 655 | spiffs_page_ix *dst_pix); 656 | 657 | s32_t spiffs_page_delete( 658 | spiffs *fs, 659 | spiffs_page_ix pix); 660 | 661 | // --------------- 662 | 663 | s32_t spiffs_object_create( 664 | spiffs *fs, 665 | spiffs_obj_id obj_id, 666 | const u8_t name[], 667 | const u8_t meta[], 668 | spiffs_obj_type type, 669 | spiffs_page_ix *objix_hdr_pix); 670 | 671 | s32_t spiffs_object_update_index_hdr( 672 | spiffs *fs, 673 | spiffs_fd *fd, 674 | spiffs_obj_id obj_id, 675 | spiffs_page_ix objix_hdr_pix, 676 | u8_t *new_objix_hdr_data, 677 | const u8_t name[], 678 | const u8_t meta[], 679 | u32_t size, 680 | spiffs_page_ix *new_pix); 681 | 682 | #if SPIFFS_IX_MAP 683 | 684 | s32_t spiffs_populate_ix_map( 685 | spiffs *fs, 686 | spiffs_fd *fd, 687 | u32_t vec_entry_start, 688 | u32_t vec_entry_end); 689 | 690 | #endif 691 | 692 | void spiffs_cb_object_event( 693 | spiffs *fs, 694 | spiffs_page_object_ix *objix, 695 | int ev, 696 | spiffs_obj_id obj_id, 697 | spiffs_span_ix spix, 698 | spiffs_page_ix new_pix, 699 | u32_t new_size); 700 | 701 | s32_t spiffs_object_open_by_id( 702 | spiffs *fs, 703 | spiffs_obj_id obj_id, 704 | spiffs_fd *f, 705 | spiffs_flags flags, 706 | spiffs_mode mode); 707 | 708 | s32_t spiffs_object_open_by_page( 709 | spiffs *fs, 710 | spiffs_page_ix pix, 711 | spiffs_fd *f, 712 | spiffs_flags flags, 713 | spiffs_mode mode); 714 | 715 | s32_t spiffs_object_append( 716 | spiffs_fd *fd, 717 | u32_t offset, 718 | u8_t *data, 719 | u32_t len); 720 | 721 | s32_t spiffs_object_modify( 722 | spiffs_fd *fd, 723 | u32_t offset, 724 | u8_t *data, 725 | u32_t len); 726 | 727 | s32_t spiffs_object_read( 728 | spiffs_fd *fd, 729 | u32_t offset, 730 | u32_t len, 731 | u8_t *dst); 732 | 733 | s32_t spiffs_object_truncate( 734 | spiffs_fd *fd, 735 | u32_t new_len, 736 | u8_t remove_object); 737 | 738 | s32_t spiffs_object_find_object_index_header_by_name( 739 | spiffs *fs, 740 | const u8_t name[SPIFFS_OBJ_NAME_LEN], 741 | spiffs_page_ix *pix); 742 | 743 | // --------------- 744 | 745 | s32_t spiffs_gc_check( 746 | spiffs *fs, 747 | u32_t len); 748 | 749 | s32_t spiffs_gc_erase_page_stats( 750 | spiffs *fs, 751 | spiffs_block_ix bix); 752 | 753 | s32_t spiffs_gc_find_candidate( 754 | spiffs *fs, 755 | spiffs_block_ix **block_candidate, 756 | int *candidate_count, 757 | char fs_crammed); 758 | 759 | s32_t spiffs_gc_clean( 760 | spiffs *fs, 761 | spiffs_block_ix bix); 762 | 763 | s32_t spiffs_gc_quick( 764 | spiffs *fs, u16_t max_free_pages); 765 | 766 | // --------------- 767 | 768 | s32_t spiffs_fd_find_new( 769 | spiffs *fs, 770 | spiffs_fd **fd, 771 | const char *name); 772 | 773 | s32_t spiffs_fd_return( 774 | spiffs *fs, 775 | spiffs_file f); 776 | 777 | s32_t spiffs_fd_get( 778 | spiffs *fs, 779 | spiffs_file f, 780 | spiffs_fd **fd); 781 | 782 | #if SPIFFS_TEMPORAL_FD_CACHE 783 | void spiffs_fd_temporal_cache_rehash( 784 | spiffs *fs, 785 | const char *old_path, 786 | const char *new_path); 787 | #endif 788 | 789 | #if SPIFFS_CACHE 790 | void spiffs_cache_init( 791 | spiffs *fs); 792 | 793 | void spiffs_cache_drop_page( 794 | spiffs *fs, 795 | spiffs_page_ix pix); 796 | 797 | #if SPIFFS_CACHE_WR 798 | spiffs_cache_page *spiffs_cache_page_allocate_by_fd( 799 | spiffs *fs, 800 | spiffs_fd *fd); 801 | 802 | void spiffs_cache_fd_release( 803 | spiffs *fs, 804 | spiffs_cache_page *cp); 805 | 806 | spiffs_cache_page *spiffs_cache_page_get_by_fd( 807 | spiffs *fs, 808 | spiffs_fd *fd); 809 | #endif 810 | #endif 811 | 812 | s32_t spiffs_lookup_consistency_check( 813 | spiffs *fs, 814 | u8_t check_all_objects); 815 | 816 | s32_t spiffs_page_consistency_check( 817 | spiffs *fs); 818 | 819 | s32_t spiffs_object_index_consistency_check( 820 | spiffs *fs); 821 | 822 | // memcpy macro, 823 | // checked in test builds, otherwise plain memcpy (unless already defined) 824 | #ifdef _SPIFFS_TEST 825 | #define _SPIFFS_MEMCPY(__d, __s, __l) do { \ 826 | intptr_t __a1 = (intptr_t)((u8_t*)(__s)); \ 827 | intptr_t __a2 = (intptr_t)((u8_t*)(__s)+(__l)); \ 828 | intptr_t __b1 = (intptr_t)((u8_t*)(__d)); \ 829 | intptr_t __b2 = (intptr_t)((u8_t*)(__d)+(__l)); \ 830 | if (__a1 <= __b2 && __b1 <= __a2) { \ 831 | printf("FATAL OVERLAP: memcpy from %lx..%lx to %lx..%lx\n", __a1, __a2, __b1, __b2); \ 832 | ERREXIT(); \ 833 | } \ 834 | memcpy((__d),(__s),(__l)); \ 835 | } while (0) 836 | #else 837 | #ifndef _SPIFFS_MEMCPY 838 | #define _SPIFFS_MEMCPY(__d, __s, __l) do{memcpy((__d),(__s),(__l));}while(0) 839 | #endif 840 | #endif //_SPIFFS_TEST 841 | 842 | #endif /* SPIFFS_NUCLEUS_H_ */ 843 | --------------------------------------------------------------------------------