├── .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 | [](https://github.com/arduino-libraries/Arduino_MKRMEM/actions/workflows/check-arduino.yml)
5 | [](https://github.com/arduino-libraries/Arduino_MKRMEM/actions/workflows/compile-examples.yml)
6 | [](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 |
--------------------------------------------------------------------------------