Compare commits
428 commits
filesystem
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4336fbe17a | ||
|
|
fa599780ee | ||
|
|
91fc6766da | ||
|
|
28ce73b8a6 | ||
|
|
54ee662201 | ||
|
|
36c78d4d4a | ||
|
|
81c521d286 | ||
|
|
40b28eae7e | ||
|
|
662b31ebc2 | ||
|
|
74f8e1f93a | ||
|
|
07096cd812 | ||
|
|
82324914fe | ||
|
|
7f17ca5f40 | ||
|
|
f7c4cde6b9 | ||
|
|
5a6b5b3451 | ||
|
|
1ad988ac30 | ||
|
|
a85404884e | ||
|
|
4d89dd36c2 | ||
|
|
74339a4809 | ||
|
|
309a3ae589 | ||
|
|
fa09a76bf7 | ||
|
|
85d1d6d8ea | ||
|
|
633451610d | ||
|
|
e1fcb6cbfc | ||
|
|
0f3d2efb4f | ||
|
|
0cd6ae693f | ||
|
|
1d83f417ed | ||
|
|
1e8cfaad0d | ||
|
|
a08e8cbb36 | ||
|
|
d6cc9859ce | ||
|
|
14a5007afc | ||
|
|
dfc0c29971 | ||
|
|
27407c65db | ||
|
|
c432f797d6 | ||
|
|
8638e99752 | ||
|
|
9b75975e8e | ||
|
|
086a0a6c19 | ||
|
|
215ece3283 | ||
|
|
43816f100d | ||
|
|
d7fe1b5d8c | ||
|
|
0ea40d89db | ||
|
|
9bb4034f53 | ||
|
|
bc73e26780 | ||
|
|
ca322bcb40 | ||
|
|
320ad469bb | ||
|
|
88dac12ddb | ||
|
|
338408fc78 | ||
|
|
b1145b466d | ||
|
|
fc3c7f770b | ||
|
|
3e6ff08544 | ||
|
|
c895779e4a | ||
|
|
42f4766ab3 | ||
|
|
a399c40f53 | ||
|
|
052e25993a | ||
|
|
f808187589 | ||
|
|
fee5612088 | ||
|
|
feca16003a | ||
|
|
7a5695dc68 | ||
|
|
dffbd447bd | ||
|
|
d0d1c19b9a | ||
|
|
3e529cf82f | ||
|
|
21205e4005 | ||
|
|
dfa76d4d6f | ||
|
|
45cade91b4 | ||
|
|
e38b13bea7 | ||
|
|
c84aa34067 | ||
|
|
77d4881763 | ||
|
|
af252c24be | ||
|
|
016e71eaae | ||
|
|
bdb8b39a0d | ||
|
|
e8c31635e5 | ||
|
|
cca05d5cf0 | ||
|
|
495425a9b0 | ||
|
|
90e1715e3d | ||
|
|
d73fe315cc | ||
|
|
8b20b3e2f4 | ||
|
|
da5aca87cf | ||
|
|
63823f9eb3 | ||
|
|
77ac756366 | ||
|
|
e303e391d1 | ||
|
|
e4040df857 | ||
|
|
fc6bd42754 | ||
|
|
09eb519cf4 | ||
|
|
a33dc6d489 | ||
|
|
a7f3bc2951 | ||
|
|
0d7c4628e7 | ||
|
|
8cc5a433f5 | ||
|
|
224ef65d83 | ||
|
|
756309ec4d | ||
|
|
0e267bc885 | ||
|
|
535ddb0d7b | ||
|
|
104c48cb48 | ||
|
|
613015ae3f | ||
|
|
70ec99069c | ||
|
|
e1385807c8 | ||
|
|
6fc263c282 | ||
|
|
9b4c4355cb | ||
|
|
de7f0db6a5 | ||
|
|
f857f944cd | ||
|
|
8713b0938d | ||
|
|
c6000b22dc | ||
|
|
3832dae91b | ||
|
|
51acbd91c0 | ||
|
|
ac95d6a459 | ||
|
|
d4075376ac | ||
|
|
6f82133a90 | ||
|
|
eeefeb598f | ||
|
|
054edb8798 | ||
|
|
bed5b6268a | ||
|
|
ae9c8535b5 | ||
|
|
a323261c55 | ||
|
|
0f9f831086 | ||
|
|
ec9e20f508 | ||
|
|
0c19f316c3 | ||
|
|
2c3edb8d82 | ||
|
|
6782e16f47 | ||
|
|
d0074840ab | ||
|
|
4a7e461534 | ||
|
|
197c50ffe5 | ||
|
|
516f3feb9e | ||
|
|
d2726992d8 | ||
|
|
6bb97b6a89 | ||
|
|
92b38f6b39 | ||
|
|
c3e699de00 | ||
|
|
443042362d | ||
|
|
a44bbbb89d | ||
|
|
b5d9a387b5 | ||
|
|
27ecd662c4 | ||
|
|
00a6c79255 | ||
|
|
55c21a4b88 | ||
|
|
cd843d8ebd | ||
|
|
3e7590d3e9 | ||
|
|
70b08d85f8 | ||
|
|
f2a0e601b2 | ||
|
|
3960966446 | ||
|
|
200f09d1d5 | ||
|
|
d1a03dd26d | ||
|
|
0365542bc1 | ||
|
|
cc7006833f | ||
|
|
be90d36495 | ||
|
|
f2a0cfddb3 | ||
|
|
8f986c1892 | ||
|
|
7b7bd471e2 | ||
|
|
93925ac61c | ||
|
|
1b7b7f78fc | ||
|
|
e2577c267b | ||
|
|
e6c4dd60e8 | ||
|
|
f5ae0c855e | ||
|
|
05da3b1ef5 | ||
|
|
f7b76d6721 | ||
|
|
7cdef7c3e7 | ||
|
|
991d4cda7b | ||
|
|
6bac6fb182 | ||
|
|
b2ce6df1c8 | ||
|
|
34b7097936 | ||
|
|
cf26475c64 | ||
|
|
70fdc0d575 | ||
|
|
f82ccd851c | ||
|
|
2768bc43ac | ||
|
|
8632328d95 | ||
|
|
768fd8ed84 | ||
|
|
03dbeacd4a | ||
|
|
8fed17bd4f | ||
|
|
9b0289b6c4 | ||
|
|
ad851ea424 | ||
|
|
0b5b3649ca | ||
|
|
28d6ced436 | ||
|
|
1f97e6eef9 | ||
|
|
f005e15c56 | ||
|
|
0542d5507f | ||
|
|
7020ad26fa | ||
|
|
d6e0e3ea7b | ||
|
|
a67d4f0957 | ||
|
|
4b244e7559 | ||
|
|
731718b6d8 | ||
|
|
20f66f10d5 | ||
|
|
1961926613 | ||
|
|
2c3a8e2230 | ||
|
|
07a98a6da8 | ||
|
|
18064ffd67 | ||
|
|
6b0137b7aa | ||
|
|
d4a2118db2 | ||
|
|
ec7c1c82ce | ||
|
|
6e0c3d2939 | ||
|
|
44db420fed | ||
|
|
83b0d101d2 | ||
|
|
701b3c0a8f | ||
|
|
2fe9f0cfff | ||
|
|
759397ba57 | ||
|
|
1a5604f3bc | ||
|
|
65d6e1aacf | ||
|
|
56d7b740ed | ||
|
|
43731b8da5 | ||
|
|
240fef2ca8 | ||
|
|
e19252de3f | ||
|
|
76b9fc6339 | ||
|
|
abb02a3cdc | ||
|
|
885eae2292 | ||
|
|
5de9bb0a6c | ||
|
|
c666e0cc9b | ||
|
|
0957a82aca | ||
|
|
00521cd59b | ||
|
|
3ba538f27e | ||
|
|
a2cf3b9797 | ||
|
|
f500453578 | ||
|
|
0a386927f3 | ||
|
|
de4371626b | ||
|
|
2cff6632ee | ||
|
|
2361e23881 | ||
|
|
175c192f2b | ||
|
|
b569157e09 | ||
|
|
22c0f001ee | ||
|
|
2390b88243 | ||
|
|
a4ac932c3f | ||
|
|
1c72c5ae71 | ||
|
|
6ae580d917 | ||
|
|
6809f38aa8 | ||
|
|
1c9a07eff4 | ||
|
|
999710e92d | ||
|
|
9f02cb294b | ||
|
|
497e8332c4 | ||
|
|
613da21bb3 | ||
|
|
16708753b4 | ||
|
|
fa6a438981 | ||
|
|
d68d708739 | ||
|
|
8a15b9e195 | ||
|
|
fa5f7a5486 | ||
|
|
687498c981 | ||
|
|
6bb17bb179 | ||
|
|
b9c66fa1b1 | ||
|
|
5687201ec9 | ||
|
|
b90bbd6349 | ||
|
|
e813fbe664 | ||
|
|
cd594a1617 | ||
|
|
08bdc03880 | ||
|
|
97b8ce167d | ||
|
|
133f48dd74 | ||
|
|
2934d32d1f | ||
|
|
d900dae127 | ||
|
|
c0e30f4192 | ||
|
|
8be4f3dbed | ||
|
|
33bfa15fc8 | ||
|
|
5256ea5247 | ||
|
|
7217c83ed1 | ||
|
|
3bd209446a | ||
|
|
cce35ecb74 | ||
|
|
0d6137a8a9 | ||
|
|
ac197818d3 | ||
|
|
54939b773e | ||
|
|
3be514d04d | ||
|
|
369381ecf0 | ||
|
|
40b46f1329 | ||
|
|
c697543815 | ||
|
|
58bfe02bcb | ||
|
|
872f1cd1f3 | ||
|
|
eeb579fb9c | ||
|
|
fb9f9cc3bb | ||
|
|
5c75800a83 | ||
|
|
d2fa1ba9cc | ||
|
|
ab949fe922 | ||
|
|
a7efa04365 | ||
|
|
5dbda1a8be | ||
|
|
3b9427b282 | ||
|
|
ae8a059e89 | ||
|
|
beb3a98b05 | ||
|
|
e2761767e7 | ||
|
|
4b30ab4284 | ||
|
|
0b558d2ea3 | ||
|
|
d7fb4c556e | ||
|
|
f3cbd721cb | ||
|
|
ffbca9c1e3 | ||
|
|
e26d792713 | ||
|
|
ae2b0660ca | ||
|
|
38a1cd7ee8 | ||
|
|
bd00a04d30 | ||
|
|
65b4f7dd56 | ||
|
|
95066a7d29 | ||
|
|
449e8ddd29 | ||
|
|
5451041458 | ||
|
|
dfcb32b1f3 | ||
|
|
072354249d | ||
|
|
a567034480 | ||
|
|
ad93421f36 | ||
|
|
c14d921460 | ||
|
|
f63b70aa3d | ||
|
|
00b93d86c0 | ||
|
|
17d506e81f | ||
|
|
bb31ec8102 | ||
|
|
4bf4a4f5dc | ||
|
|
47875b775a | ||
|
|
f28554d94d | ||
|
|
a693f84cb9 | ||
|
|
b665dc1fdd | ||
|
|
e2df4fdb4e | ||
|
|
70ab489d2a | ||
|
|
3c55153ed6 | ||
|
|
f8756c882b | ||
|
|
81f91c1e26 | ||
|
|
67980cf636 | ||
|
|
93b4959250 | ||
|
|
b08837e4f5 | ||
|
|
e427aad6be | ||
|
|
bb06eb887a | ||
|
|
2d853b81a4 | ||
|
|
0e5b41e329 | ||
|
|
9861c11f85 | ||
|
|
c8fd53f096 | ||
|
|
f01ce17d3b | ||
|
|
4ec78c64ce | ||
|
|
06e362c454 | ||
|
|
b13dcbafec | ||
|
|
63c42dd176 | ||
|
|
c31790876b | ||
|
|
79ff5cf761 | ||
|
|
b3257ddd55 | ||
|
|
71cef5e3a0 | ||
|
|
35966a57f9 | ||
|
|
a5fcd5b435 | ||
|
|
38dffb0ee9 | ||
|
|
db81c87f59 | ||
|
|
2b31ee733a | ||
|
|
4cbaae83b0 | ||
|
|
6179c0981d | ||
|
|
cd73b9d222 | ||
|
|
98350c7157 | ||
|
|
453a5f9340 | ||
|
|
fd8ef8d3e6 | ||
|
|
abdd3fe467 | ||
|
|
703d84f707 | ||
|
|
61054d17e0 | ||
|
|
fa772f3905 | ||
|
|
69c2e4a696 | ||
|
|
9e76479a7e | ||
|
|
ee167aba00 | ||
|
|
c4f8990430 | ||
|
|
3e8d790b17 | ||
|
|
1bb0a1fcf8 | ||
|
|
093c3ee3c0 | ||
|
|
c6a06c9ff1 | ||
|
|
1158be469e | ||
|
|
37e262d437 | ||
|
|
70cb98f1e0 | ||
|
|
f4efa29c9c | ||
|
|
61649995d3 | ||
|
|
1fce504d28 | ||
|
|
a745b06920 | ||
|
|
a55c66afb4 | ||
|
|
35d4a3c84b | ||
|
|
c9f1c51063 | ||
|
|
8d43a385b9 | ||
|
|
bd556e24e4 | ||
|
|
bd477c1d6b | ||
|
|
f83faa4fd2 | ||
|
|
25b73d5196 | ||
|
|
7481928992 | ||
|
|
d718b1f539 | ||
|
|
21b60cf657 | ||
|
|
e84cf1854b | ||
|
|
2aed17fc1e | ||
|
|
7e35e717f3 | ||
|
|
53580a082b | ||
|
|
eab54f15e1 | ||
|
|
06b9b9e641 | ||
|
|
a1fbf4b2ce | ||
|
|
a75b35c6f8 | ||
|
|
fa268db77f | ||
|
|
be79c97d9f | ||
|
|
5f3beee6d8 | ||
|
|
073fd6da96 | ||
|
|
09b3794e25 | ||
|
|
ba178ce226 | ||
|
|
6ab105020c | ||
|
|
70d40bd8ee | ||
|
|
cbdd42debe | ||
|
|
6090cd8d16 | ||
|
|
aea0616fa7 | ||
|
|
dfa149df19 | ||
|
|
6906aab802 | ||
|
|
5e32ca078d | ||
|
|
3e04f381e4 | ||
|
|
5ea1f9d76f | ||
|
|
17114aaa3f | ||
|
|
620fcf0a45 | ||
|
|
8fbd956ff0 | ||
|
|
4130cffc0a | ||
|
|
31c0586275 | ||
|
|
b044a0fae7 | ||
|
|
0b194f457b | ||
|
|
0ee51f2def | ||
|
|
39dddfdc37 | ||
|
|
e13ae73f4a | ||
|
|
5ca368e466 | ||
|
|
0d7c48739b | ||
|
|
94b751b3a5 | ||
|
|
a5b2d0ea10 | ||
|
|
d5dbfea2dc | ||
|
|
00c8c761c9 | ||
|
|
3b3c5b8329 | ||
|
|
22494beefd | ||
|
|
3811b04182 | ||
|
|
7da22663e0 | ||
|
|
f2510bd575 | ||
|
|
e3cf838246 | ||
|
|
387b0f4ad7 | ||
|
|
ce5a2735fb | ||
|
|
31db912528 | ||
|
|
87f1ac8789 | ||
|
|
28f4764c25 | ||
|
|
f9df9dcccb | ||
|
|
355101b145 | ||
|
|
c8e656dd42 | ||
|
|
aaebc17f5e | ||
|
|
c214077d66 | ||
|
|
3a101ff39f | ||
|
|
11f71124ae | ||
|
|
2394029037 | ||
|
|
6221177919 | ||
|
|
6bae28c91b | ||
|
|
2b14fa45c4 | ||
|
|
f48ae8f065 | ||
|
|
93c6f4e635 | ||
|
|
d290019bdc | ||
|
|
c43365abbd | ||
|
|
3f4a6e1ae9 | ||
|
|
a541a40036 | ||
|
|
8e51386a7e | ||
|
|
47836f74e3 | ||
|
|
68ff762ebd |
174 changed files with 5773 additions and 43237 deletions
67
.github/workflows/build.yml
vendored
Normal file
67
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
name: Build
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
push:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
IDF_VERSION: v5.5
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
board:
|
||||
- 'esp32'
|
||||
- 'fruitjam_c6'
|
||||
build_type:
|
||||
- 'Release'
|
||||
- 'Debug'
|
||||
steps:
|
||||
- name: Pull ESP-IDF docker
|
||||
run: docker pull espressif/idf:$IDF_VERSION
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout submodules
|
||||
run: git submodule update --init --depth 1 certificates components/arduino-esp32
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
docker run --rm -v $PWD:/project -w /project espressif/idf:$IDF_VERSION /bin/bash -c "git config --global --add safe.directory /project && idf.py -DBOARD=${{ matrix.board }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} build"
|
||||
# combine.py is auto run as postbuild
|
||||
if [ "${{ matrix.build_type }}" == "Debug" ]; then
|
||||
# add debug suffix to the bin file
|
||||
mv NINA_ADAFRUIT*.bin "$(echo NINA_ADAFRUIT*.bin | sed 's/.bin/_Debug.bin/')"
|
||||
fi
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
#name: nina-fw-${{ matrix.board }}${{ matrix.build_type == 'Debug' && format('-{0}', matrix.build_type) || '' }}
|
||||
name: NINA_ADAFRUIT-${{ matrix.board }}-${{ matrix.build_type }}
|
||||
path: |
|
||||
NINA_ADAFRUIT*.bin
|
||||
|
||||
# - name: Prepare Release Asset
|
||||
# if: github.event_name == 'release' && matrix.build_type == 'Release'
|
||||
# run: cp build/nina-fw.bin nina-fw-${{ matrix.board }}-${{ github.event.release.tag_name }}-${{ matrix.build_type }}.bin
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
NINA_ADAFRUIT*.bin
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -1 +1,8 @@
|
|||
build/
|
||||
*.bin
|
||||
# Regenerate sdkconfig from the sdkconfig.defaults* files.
|
||||
sdkconfig.esp32
|
||||
sdkconfig.esp32c6
|
||||
sdkconfig.*.old
|
||||
sdkconfig.old
|
||||
managed_components/
|
||||
|
|
|
|||
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[submodule "certificates"]
|
||||
path = certificates
|
||||
url = https://github.com/adafruit/certificates
|
||||
[submodule "arduino-esp32"]
|
||||
path = components/arduino-esp32
|
||||
url = https://github.com/espressif/arduino-esp32
|
||||
45
.travis.yml
Normal file
45
.travis.yml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# .travis.yml for nina-fw
|
||||
# adapted from https://github.com/igrr/esp32-cam-demo/blob/master/.travis.yml
|
||||
sudo: false
|
||||
language: bash
|
||||
os:
|
||||
- linux
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- python3.5
|
||||
- gperf
|
||||
|
||||
before_install:
|
||||
# Save path to the git respository
|
||||
- PROJECT_PATH=$(pwd)
|
||||
|
||||
install:
|
||||
- mkdir -p ~/esp
|
||||
- cd ~/esp
|
||||
# Download binary toolchain for the ESP32
|
||||
- wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
|
||||
- tar -xzf xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
|
||||
# Make xtensa-esp32-elf available for all terminal sessions
|
||||
- export PATH=$PATH:$HOME/esp/xtensa-esp32-elf/bin
|
||||
# Get ESP-IDF v3.3.1
|
||||
- git clone --branch v3.3.1 --recursive https://github.com/espressif/esp-idf.git
|
||||
# Set the path to ESP-IDF directory
|
||||
- export IDF_PATH=~/esp/esp-idf
|
||||
# Install Required Python Packages
|
||||
- python -m pip install --user -r $IDF_PATH/requirements.txt
|
||||
|
||||
|
||||
script:
|
||||
- cd $PROJECT_PATH
|
||||
- make firmware
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key: "$GITHUB_TOKEN"
|
||||
file_glob: true
|
||||
file: "NINA_W102-*.bin"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
168
CHANGELOG
Normal file
168
CHANGELOG
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
# Changelog
|
||||
|
||||
## Adafruit firmware
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.7.7 - 2023.09.20
|
||||
|
||||
* Change to use adafruit/certificates repo for roots.pem
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.7.5 - 2023.07.21
|
||||
|
||||
* Fix for bug in NINA-W102 firmware 1.7.5 not allowing uploads on Windows/Linux
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.7.5 - 2023.07.21
|
||||
|
||||
* Fixed Adafruit IO Root SSL Certificate
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.7.4 - 2021.06.03
|
||||
|
||||
* Fixed support for custom hostname in WiFi client mode
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.7.3 - 2021.03.26
|
||||
|
||||
* Changed Analog Write to use full PWM range.
|
||||
* Added support to Pin Mode for INPUT_PULLUP.
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.7.2 - 2021.03.05
|
||||
|
||||
* Replace peek() with available() during connection status check to avoid losing messages.
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.7.1 - 2020.10.24
|
||||
|
||||
* Enable HCI BLE for AirLift boards and breakouts.
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.6.0 - 2019.12.28
|
||||
|
||||
* Updated to build with ESP-IDF 3.3.1
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.5.0 - 2019.10.21
|
||||
|
||||
* Added Digital Read and Analog Read
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.4.0 - 2019.10.09
|
||||
|
||||
* Updated to use ESP-IDF 3.3 (LTS)
|
||||
* Updated WiFiSSLClient
|
||||
* Workflow improvements in Makefile
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.3.1 - 2019.04.24
|
||||
|
||||
* Added WPA2 Enterprise
|
||||
* Updated README.md to indicate this is Adafruit's fork
|
||||
* Updated README.md with current, reproducible build instructions
|
||||
* Updated CHANGELOG with various Adafruit changes
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.3.0 - 2019.04.04
|
||||
|
||||
* Rev'd version in CommandHandler.cpp
|
||||
* Added version number to combine.py script
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.2.2 - 2019.04.04
|
||||
|
||||
* Added support for WPA2 Enterprise
|
||||
|
||||
Adafruit's Arduino NINA-W102 firmware 1.2.2 - 2019.02.28
|
||||
|
||||
* First official Adafruit release
|
||||
|
||||
## Arduino firmware
|
||||
|
||||
Arduino NINA-W102 firmware 1.5.0 - 2022.07.09
|
||||
|
||||
* Add BSD-like sockets APIs (on command range 0x70 -> 0x7f)
|
||||
* Fix ADC readings range (fuill scale range is now ~2.7V)
|
||||
|
||||
Arduino NINA-W102 firmware 1.4.8 - 2021.07.29
|
||||
|
||||
* Fix issue due to repeated status()/connected() call (#73)
|
||||
|
||||
Arduino NINA-W102 firmware 1.4.7 - 2021.06.18
|
||||
|
||||
Functionally identical with 1.4.6, except version number.
|
||||
|
||||
This release was made necessary because bad arguments were used with the FirmwareUploader tool
|
||||
on Arduino Cloud for updating NINA-FWs (effectively deleting all certificates except the one
|
||||
fetched by the tool). Now all Cloud-Connected NINA-FWs need to be updated once more which is
|
||||
only possible by incrementing the firmware version number.
|
||||
|
||||
Arduino NINA-W102 firmware 1.4.6 - 2021.06.09
|
||||
|
||||
* Add a new root certificate list including detailed instructions how to update it (#71)
|
||||
|
||||
Arduino NINA-W102 firmware 1.4.5 - 2021.05.05
|
||||
|
||||
* Adding support for Arduino RP2040 Nano Connect (#67)
|
||||
* Added ISRG Root X1 for LetsEncrypt (#68)
|
||||
|
||||
Arduino NINA-W102 firmware 1.4.4 - 2021.04.13
|
||||
|
||||
* Adding support for Arduino RP2040 Nano Connect (#64)
|
||||
|
||||
Arduino NINA-W102 firmware 1.4.3 - 2021.01.29
|
||||
|
||||
* Do not immediately close connection after 1 failed write (#62)
|
||||
|
||||
Arduino NINA-W102 firmware 1.4.2 - 2021.01.28
|
||||
|
||||
Port BearSSL + crypto integration to NINA (#57). This allows to offload BearSSL for Arduino IoT Cloud applications to the nina module, drastically saving on flash/RAM.
|
||||
|
||||
Arduino NINA-W102 firmware 1.4.1 - 2020.08.17
|
||||
|
||||
* Direct download of OTA binary to Nina storage and verifying both CRC and length (#53)
|
||||
|
||||
Arduino NINA-W102 firmware 1.4.0 - 2020.07.13
|
||||
|
||||
* Adding API which allows to utilize the nina file system as an external storage module for the host CPU (#32)
|
||||
* Fixing bug leading to packet corruption in situation of high UDP traffic (#49)
|
||||
* Fixing bug leading to missing packets and connection drop (#50)
|
||||
|
||||
Arduino NINA-W102 firmware 1.3.0 - 2019.12.23
|
||||
|
||||
* Fixed channel's assignement for WiFi.beginAP(...)
|
||||
* Fixed set host name API begin connecting to AP
|
||||
* Updated to IDF v3.3.1
|
||||
* Added WPA2 Enterprise support (PEAP/MSCHAPv2 + EAP-TLS)
|
||||
|
||||
Arduino NINA-W102 firmware 1.2.4 - 2019.06.14
|
||||
|
||||
* Disabled CONFIG_LWIP_DHCP_DOES_ARP_CHECK
|
||||
* Added small delay to WiFiUDP.parsePacket() allow other tasks to run
|
||||
* Updated to esp-idf v3.3 branch
|
||||
|
||||
Arduino NINA-W102 firmware 1.2.3 - 2018.04.09
|
||||
|
||||
* Added reason code command to retrieve the disconnect reason code
|
||||
* WiFiSSLClient: enable SNI via mbedtls_ssl_set_hostname when using hostname
|
||||
|
||||
Arduino NINA-W102 firmware 1.2.2 - 2018.03.26
|
||||
|
||||
* Changed connections to use WIFI_FAST_SCAN instead of WIFI_ALL_CHANNEL_SCAN
|
||||
* Changed ASSOC_FAIL error handling, to now try to re-connect
|
||||
* Added phy_init partition with u-blox recommended phy init data and use
|
||||
* Fixed socket connections hanging when WiFi is disconnected
|
||||
* Fixed issues where DNS server could be reset to 0.0.0.0 on WiFi re-connections
|
||||
* Updated to use esp-idf 3.1.3
|
||||
|
||||
Arduino NINA-W102 firmware 1.2.1 - 2018.11.16
|
||||
|
||||
* Fixed factory SSL certificates being incorrectly parsed due to OOM caused by Bluetooth inclusion
|
||||
|
||||
Arduino NINA-W102 firmware 1.2.0 - 2018.11.13
|
||||
|
||||
* Fixed DNS servers not sticking when static IP address is used
|
||||
* Added HCI UART mode on boot, enabled if SS is LOW
|
||||
* Erase NVS flash on init failure
|
||||
|
||||
Arduino NINA-W102 firmware 1.1.0 - 2018.10.4
|
||||
|
||||
* WiFi is now stopped before connecting to AP
|
||||
* Changed default STA hostname to "arduino-XXYY"
|
||||
* Switched to all channel scan when connecting to AP in STA mode
|
||||
* Fixed static IP's support in STA + AP modes
|
||||
* Return failure on bad AP params
|
||||
* Moved certificates to separate mmapped partition, added a larger set of built-in root CA's
|
||||
* Updated to use esp-idf v3.1
|
||||
|
||||
Arduino NINA-W102 firmware 1.0.0 - 2018.06.28
|
||||
|
||||
* Initial release
|
||||
41
CMakeLists.txt
Normal file
41
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
if (BOARD STREQUAL "fruitjam_c6")
|
||||
set(IDF_TARGET esp32c6 CACHE STRING "Target ESP-IDF board")
|
||||
elseif (BOARD STREQUAL "esp32")
|
||||
set(IDF_TARGET esp32 CACHE STRING "Target ESP-IDF board")
|
||||
else ()
|
||||
message(FATAL_ERROR "Unsupported BOARD: ${BOARD}. Supported boards are: fruitjam_c6, esp32")
|
||||
endif ()
|
||||
|
||||
set(SDKCONFIG ${CMAKE_BINARY_DIR}/sdkconfig)
|
||||
set(SDKCONFIG_DEFAULTS sdkconfig.defaults ${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/sdkconfig)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
list(APPEND SDKCONFIG_DEFAULTS sdkconfig.debug)
|
||||
endif ()
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_compile_definitions(CMAKE_BUILD_TYPE_DEBUG)
|
||||
endif ()
|
||||
|
||||
STRING(TOUPPER ${BOARD} BOARD_UPPER)
|
||||
add_compile_definitions(
|
||||
BOARD_${BOARD_UPPER}
|
||||
)
|
||||
set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_LIST_DIR}/boards)
|
||||
|
||||
# required when upgrading to esp-idf v5.5 and arduino-esp32 v3.3.0
|
||||
add_link_options("-Wl,-u,__wrap_esp_log_writev")
|
||||
|
||||
project(nina-fw)
|
||||
|
||||
# Post build to run combine.py
|
||||
add_custom_command(TARGET app POST_BUILD
|
||||
COMMAND python ${CMAKE_CURRENT_LIST_DIR}/combine.py -b ${CMAKE_BINARY_DIR} NINA_ADAFRUIT-${BOARD}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
VERBATIM
|
||||
)
|
||||
44
Makefile
44
Makefile
|
|
@ -1,15 +1,57 @@
|
|||
PROJECT_NAME := nina-fw
|
||||
|
||||
# Passthrough Board Port
|
||||
M4_PORT := /dev/cu.usbmodem14121301
|
||||
UPLOAD_BAUD = 115200
|
||||
# ESP32 USB Serial Port
|
||||
ESP_PORT := /dev/cu.usbserial-AH03B302
|
||||
|
||||
# Directories and Files
|
||||
BOOT_VOLUME := /Volumes/FEATHERBOOT/.
|
||||
CIRCUITPYTHON_UF2 := circuitpython.uf2
|
||||
|
||||
EXTRA_COMPONENT_DIRS := $(PWD)/arduino
|
||||
|
||||
CPPFLAGS += -DARDUINO
|
||||
|
||||
ifeq ($(RELEASE),1)
|
||||
CFLAGS += -DNDEBUG -DCONFIG_FREERTOS_ASSERT_DISABLE -Os -DLOG_LOCAL_LEVEL=0
|
||||
CPPFLAGS += -DNDEBUG -Os
|
||||
endif
|
||||
|
||||
ifeq ($(UNO_WIFI_REV2),1)
|
||||
CFLAGS += -DUNO_WIFI_REV2
|
||||
CPPFLAGS += -DUNO_WIFI_REV2
|
||||
endif
|
||||
|
||||
ifeq ($(NANO_RP2040_CONNECT),1)
|
||||
CFLAGS += -DNANO_RP2040_CONNECT
|
||||
CPPFLAGS += -DNANO_RP2040_CONNECT
|
||||
endif
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
firmware: all makefs
|
||||
load-passthrough:
|
||||
cp passthrough.UF2 $(BOOT_VOLUME)
|
||||
|
||||
load-nina:
|
||||
esptool.py --port $(M4_PORT) --before no_reset --baud $(UPLOAD_BAUD) write_flash 0 $(wildcard NINA_W102-*.bin)
|
||||
|
||||
load-circuitpython:
|
||||
cp $(CIRCUITPYTHON_UF2) $(BOOT_VOLUME)
|
||||
|
||||
serial:
|
||||
miniterm.py $(ESP_PORT) $(UPLOAD_BAUD)
|
||||
|
||||
firmware: all
|
||||
python combine.py
|
||||
|
||||
.PHONY: firmware
|
||||
|
||||
.PHONY: load-passthrough
|
||||
|
||||
.PHONY: load-nina
|
||||
|
||||
.PHONY: load-circuitpython
|
||||
|
||||
.PHONY: serial
|
||||
|
|
|
|||
63
README.md
63
README.md
|
|
@ -1,20 +1,69 @@
|
|||
# Arduino NINA-W102 firmware
|
||||
# Adafruit fork of the Arduino NINA-W102 firmware
|
||||
|
||||
[](https://travis-ci.com/adafruit/nina-fw)
|
||||
|
||||
This is the Adafruit fork of the Arduino NINA-W102 firmware. The original
|
||||
repository is located at https://github.com/arduino/nina-fw
|
||||
|
||||
This firmware uses [Espressif's IDF](https://github.com/espressif/esp-idf)
|
||||
|
||||
## Contributing to nina-fw
|
||||
|
||||
Please be aware that by contributing to this project
|
||||
you are agreeing to the [Code of Conduct](https://github.com/adafruit/nina-fw/blob/master/code-of-conduct.md).
|
||||
Contributors who follow the [Code of Conduct](https://github.com/adafruit/nina-fw/blob/master/code-of-conduct.md)
|
||||
are welcome to submit pull requests and they will be promptly
|
||||
reviewed by project admins. Please join the [Discord](https://adafru.it/discord) too.
|
||||
|
||||
The NINA firmware version needs to be updated in two places in this repo:
|
||||
1. CommandHandler.cpp
|
||||
1. CHANGELOG
|
||||
|
||||
## Building
|
||||
|
||||
1. [Download the ESP32 toolchain](http://esp-idf.readthedocs.io/en/v3.0/get-started/index.html#setup-toolchain)
|
||||
The firmware shipped in Adafruit's products is compiled following these
|
||||
instructions. These may differ from the instructions included in the
|
||||
original Arduino firmware repository.
|
||||
|
||||
1. [Download the ESP32 toolchain](https://docs.espressif.com/projects/esp-idf/en/v3.3.1/get-started/index.html#setup-toolchain)
|
||||
1. Extract it and add it to your `PATH`: `export PATH=$PATH:<path/to/toolchain>/bin`
|
||||
1. Clone **v3.0** of the IDF: `git clone --branch v3.0 --recursive https://github.com/espressif/esp-idf.git`
|
||||
1. Clone **v5.5** of the IDF: `git clone --branch v5.5 --recursive https://github.com/espressif/esp-idf.git`
|
||||
1. Set the `IDF_PATH` environment variable: `export IDF_PATH=<path/to/idf>`
|
||||
1. Run `make` to build the firmware (in the directory of this read me)
|
||||
1. Load the `WiFiNINAFirmwareUpdater` example sketch on to the board
|
||||
1. Use `esptool` to flash the compiled firmware
|
||||
1. `git submodule update --init` to fetch the `certificates` submodule.
|
||||
1. Run `make firmware` to build the firmware (in the directory of this read me)
|
||||
1. You may need to set up a python3 `venv` to avoid Python library version issues.
|
||||
1. You should have a file named `NINA_W102-x.x.x.bin` in the top directory
|
||||
1. Use appropriate tools (`esptool.py`, appropriate pass-through firmware etc)
|
||||
to load this binary file onto your board.
|
||||
a. If you do not know how to do this, [we have an excellent guide on the Adafruit Learning System for upgrading your ESP32's firmware](https://learn.adafruit.com/upgrading-esp32-firmware)
|
||||
|
||||
## Packaging
|
||||
The `make` command produces a bunch of binary files that must be flashed at very precise locations, making `esptool` commandline quite complicated.
|
||||
Instead, once the firmware has been compiled, you can invoke `combine.py` script to produce a monolithic binary that can be flashed at 0x0.
|
||||
```
|
||||
make
|
||||
python combine.py
|
||||
```
|
||||
This produces `NINA_W102.bin-{version}` file (a different name can be specified as parameter). To flash this file you can use https://learn.adafruit.com/upgrading-esp32-firmware
|
||||
|
||||
## Build a new certificate list (based on the Google Android root CA list)
|
||||
```bash
|
||||
git clone https://android.googlesource.com/platform/system/ca-certificates
|
||||
cp nina-fw/tools/nina-fw-create-roots.sh ca-certificates/files
|
||||
cd ca-certificates/files
|
||||
./nina-fw-create-roots.sh
|
||||
cp roots.pem ../../nina-fw/data/roots.pem
|
||||
```
|
||||
|
||||
## Check certificate list against URL list
|
||||
```bash
|
||||
cd tools
|
||||
./sslcheck.sh -c ../data/roots.pem -l url_lists/url_list_moz.com.txt -e
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
Copyright (c) 2018-2019 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
COMPONENT_ADD_INCLUDEDIRS := cores/esp32 libraries/SPIS/src libraries/WiFi/src
|
||||
|
||||
COMPONENT_SRCDIRS := cores/esp32 libraries/SPIS/src libraries/WiFi/src
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef ARDUINO_H
|
||||
#define ARDUINO_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif // __cplusplus
|
||||
|
||||
// system functions
|
||||
void init(void);
|
||||
|
||||
// sketch
|
||||
void setup(void);
|
||||
void loop(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "WMath.h"
|
||||
#endif
|
||||
#include "delay.h"
|
||||
|
||||
#include "wiring_digital.h"
|
||||
#include "wiring_analog.h"
|
||||
|
||||
#endif // ARDUINO_H
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#include "wiring_digital.h"
|
||||
|
||||
#include "WInterrupts.h"
|
||||
|
||||
static voidFuncPtr callbacksInt[GPIO_NUM_MAX] = { NULL };
|
||||
|
||||
void IRAM_ATTR gpioInterruptHandler(void* arg)
|
||||
{
|
||||
uint32_t pin = (uint32_t)arg;
|
||||
|
||||
if (callbacksInt[pin]) {
|
||||
callbacksInt[pin]();
|
||||
}
|
||||
}
|
||||
|
||||
void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode)
|
||||
{
|
||||
callbacksInt[pin] = callback;
|
||||
|
||||
switch (mode) {
|
||||
case LOW:
|
||||
gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_LOW_LEVEL);
|
||||
gpio_wakeup_enable((gpio_num_t)pin, GPIO_INTR_LOW_LEVEL);
|
||||
break;
|
||||
|
||||
case HIGH:
|
||||
gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_HIGH_LEVEL);
|
||||
gpio_wakeup_enable((gpio_num_t)pin, GPIO_INTR_HIGH_LEVEL);
|
||||
break;
|
||||
|
||||
case CHANGE:
|
||||
gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_ANYEDGE);
|
||||
break;
|
||||
|
||||
case FALLING:
|
||||
gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_NEGEDGE);
|
||||
break;
|
||||
|
||||
case RISING:
|
||||
gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_POSEDGE);
|
||||
break;
|
||||
|
||||
default:
|
||||
gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_DISABLE);
|
||||
break;
|
||||
}
|
||||
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3);
|
||||
|
||||
gpio_isr_handler_add((gpio_num_t)pin, gpioInterruptHandler, (void*)pin);
|
||||
|
||||
gpio_intr_enable((gpio_num_t)pin);
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _WIRING_INTERRUPTS_
|
||||
#define _WIRING_INTERRUPTS_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// #define LOW 0
|
||||
// #define HIGH 1
|
||||
#define CHANGE 2
|
||||
#define FALLING 3
|
||||
#define RISING 4
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
|
||||
void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <esp_system.h>
|
||||
}
|
||||
|
||||
#include "WMath.h"
|
||||
|
||||
extern long random(long howbig)
|
||||
{
|
||||
if(howbig == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return esp_random() % howbig;
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _WIRING_MATH_
|
||||
#define _WIRING_MATH_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern long random(long);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _WIRING_MATH_ */
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include "delay.h"
|
||||
|
||||
unsigned long millis()
|
||||
{
|
||||
return xTaskGetTickCount() * portTICK_PERIOD_MS;
|
||||
}
|
||||
|
||||
void delay(uint32_t ms)
|
||||
{
|
||||
vTaskDelay(ms / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef DELAY_H
|
||||
#define DELAY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern unsigned long millis();
|
||||
|
||||
extern void delay(uint32_t ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // DELAY_H
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#define ARDUINO_MAIN
|
||||
#include "Arduino.h"
|
||||
|
||||
void arduino_main(void*) {
|
||||
init();
|
||||
|
||||
setup();
|
||||
|
||||
while (1) {
|
||||
loop();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void app_main() {
|
||||
xTaskCreatePinnedToCore(arduino_main, "arduino", 8192, NULL, 1, NULL, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <nvs_flash.h>
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
void init() {
|
||||
nvs_flash_init();
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <driver/ledc.h>
|
||||
|
||||
#include "wiring_analog.h"
|
||||
|
||||
void analogWrite(uint32_t pin, uint32_t value)
|
||||
{
|
||||
periph_module_enable(PERIPH_LEDC_MODULE);
|
||||
|
||||
ledc_timer_config_t timerConf = {
|
||||
.bit_num = LEDC_TIMER_10_BIT,
|
||||
.freq_hz = 1000,
|
||||
.speed_mode = LEDC_HIGH_SPEED_MODE,
|
||||
.timer_num = (pin / 7),
|
||||
};
|
||||
ledc_timer_config(&timerConf);
|
||||
|
||||
ledc_channel_config_t ledc_conf = {
|
||||
.channel = (pin % 7),
|
||||
.duty = (value << 2),
|
||||
.gpio_num = pin,
|
||||
.intr_type = LEDC_INTR_DISABLE,
|
||||
.speed_mode = LEDC_HIGH_SPEED_MODE,
|
||||
.timer_sel = (pin / 7)
|
||||
};
|
||||
ledc_channel_config(&ledc_conf);
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef WIRING_ANALOG_H
|
||||
#define WIRING_ANALOG_H
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void analogWrite(uint32_t pin, uint32_t value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WIRING_ANALOG_H
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#include "wiring_digital.h"
|
||||
|
||||
void pinMode(uint32_t pin, uint32_t mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case INPUT:
|
||||
gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode((gpio_num_t)pin, GPIO_FLOATING);
|
||||
break;
|
||||
|
||||
case OUTPUT:
|
||||
gpio_set_direction((gpio_num_t)pin, GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode((gpio_num_t)pin, GPIO_FLOATING);
|
||||
break;
|
||||
}
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[pin], PIN_FUNC_GPIO);
|
||||
}
|
||||
|
||||
void digitalWrite(uint32_t pin, uint32_t val)
|
||||
{
|
||||
gpio_set_level((gpio_num_t)pin, val);
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef WIRING_DIGITAL_H
|
||||
#define WIRING_DIGITAL_H
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LOW 0x00
|
||||
#define HIGH 0x01
|
||||
|
||||
#define INPUT 0x00
|
||||
#define OUTPUT 0x01
|
||||
|
||||
extern void pinMode(uint32_t pin, uint32_t mode);
|
||||
|
||||
extern void digitalWrite(uint32_t pin, uint32_t val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WIRING_DIGITAL_H
|
||||
|
|
@ -1,682 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <esp_wifi.h>
|
||||
#include <tcpip_adapter.h>
|
||||
|
||||
#include <apps/sntp/sntp.h>
|
||||
|
||||
#include <lwip/dns.h>
|
||||
#include <lwip/netdb.h>
|
||||
#include <lwip/raw.h>
|
||||
#include <lwip/icmp.h>
|
||||
#include <lwip/sockets.h>
|
||||
#include <lwip/ip_addr.h>
|
||||
#include <lwip/inet_chksum.h>
|
||||
|
||||
#include "WiFi.h"
|
||||
|
||||
WiFiClass::WiFiClass() :
|
||||
_initialized(false),
|
||||
_status(WL_NO_SHIELD),
|
||||
_interface(ESP_IF_WIFI_STA),
|
||||
_onReceiveCallback(NULL)
|
||||
{
|
||||
_eventGroup = xEventGroupCreate();
|
||||
memset(&_apRecord, 0x00, sizeof(_apRecord));
|
||||
memset(&_ipInfo, 0x00, sizeof(_ipInfo));
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::status()
|
||||
{
|
||||
if (!_initialized) {
|
||||
_initialized = true;
|
||||
init();
|
||||
}
|
||||
|
||||
return _status;
|
||||
}
|
||||
|
||||
int WiFiClass::hostByName(const char* hostname, /*IPAddress*/uint32_t& result)
|
||||
{
|
||||
result = 0xffffffff;
|
||||
|
||||
struct hostent* hostEntry = lwip_gethostbyname(hostname);
|
||||
|
||||
if (hostEntry == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&result, hostEntry->h_addr_list[0], sizeof(result));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int WiFiClass::ping(/*IPAddress*/uint32_t host, uint8_t ttl)
|
||||
{
|
||||
uint32_t timeout = 5000;
|
||||
|
||||
int s = socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP);
|
||||
|
||||
struct timeval timeoutVal;
|
||||
timeoutVal.tv_sec = (timeout / 1000);
|
||||
timeoutVal.tv_usec = (timeout % 1000) * 1000;
|
||||
|
||||
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeoutVal, sizeof(timeoutVal));
|
||||
setsockopt(s, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct icmp_echo_hdr header;
|
||||
uint8_t data[32];
|
||||
} request;
|
||||
|
||||
ICMPH_TYPE_SET(&request.header, ICMP_ECHO);
|
||||
ICMPH_CODE_SET(&request.header, 0);
|
||||
request.header.chksum = 0;
|
||||
request.header.id = 0xAFAF;
|
||||
request.header.seqno = random(0xffff);
|
||||
|
||||
for (size_t i = 0; i < sizeof(request.data); i++) {
|
||||
request.data[i] = i;
|
||||
}
|
||||
|
||||
request.header.chksum = inet_chksum(&request, sizeof(request));
|
||||
|
||||
ip_addr_t addr;
|
||||
addr.type = IPADDR_TYPE_V4;
|
||||
addr.u_addr.ip4.addr = host;
|
||||
// IP_ADDR4(&addr, ip[0], ip[1], ip[2], ip[3]);
|
||||
|
||||
struct sockaddr_in to;
|
||||
struct sockaddr_in from;
|
||||
|
||||
to.sin_len = sizeof(to);
|
||||
to.sin_family = AF_INET;
|
||||
inet_addr_from_ipaddr(&to.sin_addr, ip_2_ip4(&addr));
|
||||
|
||||
sendto(s, &request, sizeof(request), 0, (struct sockaddr*)&to, sizeof(to));
|
||||
unsigned long sendTime = millis();
|
||||
unsigned long recvTime = 0;
|
||||
|
||||
do {
|
||||
socklen_t fromlen = sizeof(from);
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct ip_hdr ipHeader;
|
||||
struct icmp_echo_hdr header;
|
||||
} response;
|
||||
|
||||
int rxSize = recvfrom(s, &response, sizeof(response), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen);
|
||||
if (rxSize == -1) {
|
||||
// time out
|
||||
break;
|
||||
}
|
||||
|
||||
if (rxSize < sizeof(response)) {
|
||||
// too short
|
||||
continue;
|
||||
}
|
||||
|
||||
if (from.sin_family != AF_INET) {
|
||||
// not IPv4
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((response.header.id == request.header.id) && (response.header.seqno == request.header.seqno)) {
|
||||
recvTime = millis();
|
||||
}
|
||||
} while (recvTime == 0);
|
||||
|
||||
close(s);
|
||||
|
||||
if (recvTime == 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return (recvTime - sendTime);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::begin(const char* ssid)
|
||||
{
|
||||
return begin(ssid, "");
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::begin(const char* ssid, uint8_t key_idx, const char* key)
|
||||
{
|
||||
return begin(ssid, key);
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::begin(const char* ssid, const char* key)
|
||||
{
|
||||
wifi_config_t wifiConfig;
|
||||
|
||||
memset(&wifiConfig, 0x00, sizeof(wifiConfig));
|
||||
strncpy((char*)wifiConfig.sta.ssid, ssid, sizeof(wifiConfig.sta.ssid));
|
||||
strncpy((char*)wifiConfig.sta.password, key, sizeof(wifiConfig.sta.password));
|
||||
wifiConfig.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
|
||||
_status = WL_NO_SSID_AVAIL;
|
||||
|
||||
_interface = ESP_IF_WIFI_STA;
|
||||
|
||||
esp_wifi_stop();
|
||||
esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
esp_wifi_start();
|
||||
xEventGroupWaitBits(_eventGroup, BIT0, false, true, portMAX_DELAY);
|
||||
|
||||
if (esp_wifi_set_config(ESP_IF_WIFI_STA, &wifiConfig) != ESP_OK) {
|
||||
_status = WL_CONNECT_FAILED;
|
||||
}
|
||||
|
||||
esp_wifi_connect();
|
||||
|
||||
return _status;
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::beginAP(const char *ssid, uint8_t channel)
|
||||
{
|
||||
wifi_config_t wifiConfig;
|
||||
|
||||
memset(&wifiConfig, 0x00, sizeof(wifiConfig));
|
||||
strncpy((char*)wifiConfig.ap.ssid, ssid, sizeof(wifiConfig.sta.ssid));
|
||||
wifiConfig.ap.channel = 0;
|
||||
wifiConfig.ap.authmode = WIFI_AUTH_OPEN;
|
||||
wifiConfig.ap.max_connection = 4;
|
||||
|
||||
_status = WL_NO_SSID_AVAIL;
|
||||
|
||||
_interface = ESP_IF_WIFI_AP;
|
||||
|
||||
esp_wifi_stop();
|
||||
esp_wifi_set_mode(WIFI_MODE_AP);
|
||||
|
||||
if (esp_wifi_set_config(ESP_IF_WIFI_AP, &wifiConfig) != ESP_OK) {
|
||||
_status = WL_AP_FAILED;
|
||||
}
|
||||
|
||||
esp_wifi_start();
|
||||
xEventGroupWaitBits(_eventGroup, BIT1, false, true, portMAX_DELAY);
|
||||
|
||||
return _status;
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::beginAP(const char *ssid, uint8_t key_idx, const char* key, uint8_t channel)
|
||||
{
|
||||
wifi_config_t wifiConfig;
|
||||
|
||||
memset(&wifiConfig, 0x00, sizeof(wifiConfig));
|
||||
strncpy((char*)wifiConfig.ap.ssid, ssid, sizeof(wifiConfig.sta.ssid));
|
||||
strncpy((char*)wifiConfig.ap.password, key, sizeof(wifiConfig.sta.password));
|
||||
wifiConfig.ap.channel = 0;
|
||||
wifiConfig.ap.authmode = WIFI_AUTH_WEP;
|
||||
wifiConfig.ap.max_connection = 4;
|
||||
|
||||
_status = WL_NO_SSID_AVAIL;
|
||||
|
||||
_interface = ESP_IF_WIFI_AP;
|
||||
|
||||
esp_wifi_stop();
|
||||
esp_wifi_set_mode(WIFI_MODE_AP);
|
||||
|
||||
if (esp_wifi_set_config(ESP_IF_WIFI_AP, &wifiConfig) != ESP_OK) {
|
||||
_status = WL_AP_FAILED;
|
||||
}
|
||||
|
||||
esp_wifi_start();
|
||||
xEventGroupWaitBits(_eventGroup, BIT1, false, true, portMAX_DELAY);
|
||||
|
||||
return _status;
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::beginAP(const char *ssid, const char* key, uint8_t channel)
|
||||
{
|
||||
wifi_config_t wifiConfig;
|
||||
|
||||
memset(&wifiConfig, 0x00, sizeof(wifiConfig));
|
||||
strncpy((char*)wifiConfig.ap.ssid, ssid, sizeof(wifiConfig.sta.ssid));
|
||||
strncpy((char*)wifiConfig.ap.password, key, sizeof(wifiConfig.sta.password));
|
||||
wifiConfig.ap.channel = 0;
|
||||
wifiConfig.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
|
||||
wifiConfig.ap.max_connection = 4;
|
||||
|
||||
_status = WL_NO_SSID_AVAIL;
|
||||
|
||||
_interface = ESP_IF_WIFI_AP;
|
||||
|
||||
esp_wifi_stop();
|
||||
esp_wifi_set_mode(WIFI_MODE_AP);
|
||||
|
||||
if (esp_wifi_set_config(ESP_IF_WIFI_AP, &wifiConfig) != ESP_OK) {
|
||||
_status = WL_AP_FAILED;
|
||||
}
|
||||
|
||||
esp_wifi_start();
|
||||
xEventGroupWaitBits(_eventGroup, BIT1, false, true, portMAX_DELAY);
|
||||
|
||||
return _status;
|
||||
}
|
||||
|
||||
void WiFiClass::config(/*IPAddress*/uint32_t local_ip, /*IPAddress*/uint32_t gateway, /*IPAddress*/uint32_t subnet)
|
||||
{
|
||||
dns_clear_servers(true);
|
||||
|
||||
_ipInfo.ip.addr = local_ip;
|
||||
_ipInfo.gw.addr = gateway;
|
||||
_ipInfo.netmask.addr = subnet;
|
||||
tcpip_adapter_set_ip_info(_interface == ESP_IF_WIFI_AP ? TCPIP_ADAPTER_IF_AP : TCPIP_ADAPTER_IF_STA, &_ipInfo);
|
||||
}
|
||||
|
||||
void WiFiClass::setDNS(/*IPAddress*/uint32_t dns_server1, /*IPAddress*/uint32_t dns_server2)
|
||||
{
|
||||
ip_addr_t d;
|
||||
d.type = IPADDR_TYPE_V4;
|
||||
|
||||
if (dns_server1) {
|
||||
d.u_addr.ip4.addr = static_cast<uint32_t>(dns_server1);
|
||||
dns_setserver(0, &d);
|
||||
}
|
||||
|
||||
if (dns_server2) {
|
||||
d.u_addr.ip4.addr = static_cast<uint32_t>(dns_server2);
|
||||
dns_setserver(1, &d);
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiClass::hostname(const char* name)
|
||||
{
|
||||
tcpip_adapter_set_hostname(_interface == ESP_IF_WIFI_AP ? TCPIP_ADAPTER_IF_AP : TCPIP_ADAPTER_IF_STA, name);
|
||||
}
|
||||
|
||||
void WiFiClass::disconnect()
|
||||
{
|
||||
esp_wifi_disconnect();
|
||||
esp_wifi_stop();
|
||||
}
|
||||
|
||||
void WiFiClass::end()
|
||||
{
|
||||
esp_wifi_stop();
|
||||
}
|
||||
|
||||
uint8_t* WiFiClass::macAddress(uint8_t* mac)
|
||||
{
|
||||
uint8_t macTemp[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
esp_wifi_get_mac(_interface, macTemp);
|
||||
|
||||
mac[0] = macTemp[5];
|
||||
mac[1] = macTemp[4];
|
||||
mac[2] = macTemp[3];
|
||||
mac[3] = macTemp[2];
|
||||
mac[4] = macTemp[1];
|
||||
mac[5] = macTemp[0];
|
||||
|
||||
return mac;
|
||||
}
|
||||
|
||||
uint32_t WiFiClass::localIP()
|
||||
{
|
||||
return _ipInfo.ip.addr;
|
||||
}
|
||||
|
||||
uint32_t WiFiClass::subnetMask()
|
||||
{
|
||||
return _ipInfo.netmask.addr;
|
||||
}
|
||||
|
||||
uint32_t WiFiClass::gatewayIP()
|
||||
{
|
||||
return _ipInfo.gw.addr;
|
||||
}
|
||||
|
||||
char* WiFiClass::SSID()
|
||||
{
|
||||
return (char*)_apRecord.ssid;
|
||||
}
|
||||
|
||||
int32_t WiFiClass::RSSI()
|
||||
{
|
||||
if (_interface == ESP_IF_WIFI_AP) {
|
||||
return 0;
|
||||
} else {
|
||||
esp_wifi_sta_get_ap_info(&_apRecord);
|
||||
|
||||
return _apRecord.rssi;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::encryptionType()
|
||||
{
|
||||
uint8_t encryptionType = _apRecord.authmode;
|
||||
|
||||
if (encryptionType == WIFI_AUTH_OPEN) {
|
||||
encryptionType = 7;
|
||||
} else if (encryptionType == WIFI_AUTH_WEP) {
|
||||
encryptionType = 5;
|
||||
} else if (encryptionType == WIFI_AUTH_WPA_PSK) {
|
||||
encryptionType = 2;
|
||||
} else if (encryptionType == WIFI_AUTH_WPA2_PSK || encryptionType == WIFI_AUTH_WPA_WPA2_PSK) {
|
||||
encryptionType = 4;
|
||||
} else {
|
||||
// unknown?
|
||||
encryptionType = 255;
|
||||
}
|
||||
|
||||
return encryptionType;
|
||||
}
|
||||
|
||||
uint8_t* WiFiClass::BSSID(uint8_t* bssid)
|
||||
{
|
||||
if (_interface == ESP_IF_WIFI_AP) {
|
||||
return macAddress(bssid);
|
||||
} else {
|
||||
bssid[0] = _apRecord.bssid[5];
|
||||
bssid[1] = _apRecord.bssid[4];
|
||||
bssid[2] = _apRecord.bssid[3];
|
||||
bssid[3] = _apRecord.bssid[2];
|
||||
bssid[4] = _apRecord.bssid[1];
|
||||
bssid[5] = _apRecord.bssid[0];
|
||||
|
||||
return bssid;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t WiFiClass::scanNetworks()
|
||||
{
|
||||
esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
esp_wifi_start();
|
||||
xEventGroupWaitBits(_eventGroup, BIT0, false, true, portMAX_DELAY);
|
||||
|
||||
wifi_scan_config_t config;
|
||||
|
||||
config.ssid = 0;
|
||||
config.bssid = 0;
|
||||
config.channel = 0;
|
||||
config.show_hidden = false;
|
||||
config.scan_type = WIFI_SCAN_TYPE_ACTIVE;
|
||||
config.scan_time.active.min = 100;
|
||||
config.scan_time.active.max = 300;
|
||||
|
||||
xEventGroupClearBits(_eventGroup, BIT2);
|
||||
|
||||
if (esp_wifi_scan_start(&config, false) != ESP_OK) {
|
||||
_status = WL_NO_SSID_AVAIL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
xEventGroupWaitBits(_eventGroup, BIT2, false, true, portMAX_DELAY);
|
||||
|
||||
uint16_t numNetworks;
|
||||
esp_wifi_scan_get_ap_num(&numNetworks);
|
||||
|
||||
if (numNetworks > MAX_SCAN_RESULTS) {
|
||||
numNetworks = MAX_SCAN_RESULTS;
|
||||
}
|
||||
|
||||
esp_wifi_scan_get_ap_records(&numNetworks, _scanResults);
|
||||
|
||||
_status = WL_SCAN_COMPLETED;
|
||||
|
||||
return numNetworks;
|
||||
}
|
||||
|
||||
char* WiFiClass::SSID(uint8_t pos)
|
||||
{
|
||||
return (char*)_scanResults[pos].ssid;
|
||||
}
|
||||
|
||||
int32_t WiFiClass::RSSI(uint8_t pos)
|
||||
{
|
||||
return _scanResults[pos].rssi;
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::encryptionType(uint8_t pos)
|
||||
{
|
||||
uint8_t encryptionType = _scanResults[pos].authmode;
|
||||
|
||||
if (encryptionType == WIFI_AUTH_OPEN) {
|
||||
encryptionType = 7;
|
||||
} else if (encryptionType == WIFI_AUTH_WEP) {
|
||||
encryptionType = 5;
|
||||
} else if (encryptionType == WIFI_AUTH_WPA_PSK) {
|
||||
encryptionType = 2;
|
||||
} else if (encryptionType == WIFI_AUTH_WPA2_PSK || encryptionType == WIFI_AUTH_WPA_WPA2_PSK) {
|
||||
encryptionType = 4;
|
||||
} else {
|
||||
// unknown?
|
||||
encryptionType = 255;
|
||||
}
|
||||
|
||||
return encryptionType;
|
||||
}
|
||||
|
||||
uint8_t* WiFiClass::BSSID(uint8_t pos, uint8_t* bssid)
|
||||
{
|
||||
const uint8_t* tempBssid = _scanResults[pos].bssid;
|
||||
|
||||
bssid[0] = tempBssid[5];
|
||||
bssid[1] = tempBssid[4];
|
||||
bssid[2] = tempBssid[3];
|
||||
bssid[3] = tempBssid[2];
|
||||
bssid[4] = tempBssid[1];
|
||||
bssid[5] = tempBssid[0];
|
||||
|
||||
return bssid;
|
||||
}
|
||||
|
||||
uint8_t WiFiClass::channel(uint8_t pos)
|
||||
{
|
||||
return _scanResults[pos].primary;
|
||||
}
|
||||
|
||||
unsigned long WiFiClass::getTime()
|
||||
{
|
||||
time_t now;
|
||||
|
||||
time(&now);
|
||||
|
||||
if (now < 946684800) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return now;
|
||||
}
|
||||
|
||||
void WiFiClass::lowPowerMode()
|
||||
{
|
||||
esp_wifi_set_ps(WIFI_PS_MODEM);
|
||||
}
|
||||
|
||||
void WiFiClass::noLowPowerMode()
|
||||
{
|
||||
esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
}
|
||||
|
||||
void WiFiClass::onReceive(void(*callback)(void))
|
||||
{
|
||||
_onReceiveCallback = callback;
|
||||
}
|
||||
|
||||
err_t WiFiClass::staNetifInputHandler(struct pbuf* p, struct netif* inp)
|
||||
{
|
||||
return WiFi.handleStaNetifInput(p, inp);
|
||||
}
|
||||
|
||||
err_t WiFiClass::apNetifInputHandler(struct pbuf* p, struct netif* inp)
|
||||
{
|
||||
return WiFi.handleApNetifInput(p, inp);
|
||||
}
|
||||
|
||||
err_t WiFiClass::handleStaNetifInput(struct pbuf* p, struct netif* inp)
|
||||
{
|
||||
err_t result = _staNetifInput(p, inp);
|
||||
|
||||
if (_onReceiveCallback) {
|
||||
_onReceiveCallback();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
err_t WiFiClass::handleApNetifInput(struct pbuf* p, struct netif* inp)
|
||||
{
|
||||
err_t result = _apNetifInput(p, inp);
|
||||
|
||||
if (_onReceiveCallback) {
|
||||
_onReceiveCallback();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void WiFiClass::init()
|
||||
{
|
||||
tcpip_adapter_init();
|
||||
esp_event_loop_init(WiFiClass::systemEventHandler, this);
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
esp_wifi_init(&cfg);
|
||||
esp_wifi_set_storage(WIFI_STORAGE_RAM);
|
||||
|
||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
sntp_setservername(0, (char*)"0.pool.ntp.org");
|
||||
sntp_setservername(1, (char*)"1.pool.ntp.org");
|
||||
sntp_setservername(2, (char*)"2.pool.ntp.org");
|
||||
sntp_init();
|
||||
|
||||
_status = WL_IDLE_STATUS;
|
||||
}
|
||||
|
||||
esp_err_t WiFiClass::systemEventHandler(void* ctx, system_event_t* event)
|
||||
{
|
||||
((WiFiClass*)ctx)->handleSystemEvent(event);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void WiFiClass::handleSystemEvent(system_event_t* event)
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case SYSTEM_EVENT_SCAN_DONE:
|
||||
xEventGroupSetBits(_eventGroup, BIT2);
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_START: {
|
||||
struct netif* staNetif;
|
||||
uint8_t mac[6];
|
||||
char defaultHostname[13];
|
||||
|
||||
esp_wifi_get_mac(ESP_IF_WIFI_STA, mac);
|
||||
sprintf(defaultHostname, "arduino-%.2x%.2x", mac[4], mac[5]);
|
||||
tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, defaultHostname);
|
||||
|
||||
if (tcpip_adapter_get_netif(TCPIP_ADAPTER_IF_STA, (void**)&staNetif) == ESP_OK) {
|
||||
if (staNetif->input != WiFiClass::staNetifInputHandler) {
|
||||
_staNetifInput = staNetif->input;
|
||||
|
||||
staNetif->input = WiFiClass::staNetifInputHandler;
|
||||
}
|
||||
}
|
||||
|
||||
xEventGroupSetBits(_eventGroup, BIT0);
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSTEM_EVENT_STA_STOP:
|
||||
xEventGroupClearBits(_eventGroup, BIT0);
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_CONNECTED:
|
||||
esp_wifi_sta_get_ap_info(&_apRecord);
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
memcpy(&_ipInfo, &event->event_info.got_ip.ip_info, sizeof(_ipInfo));
|
||||
_status = WL_CONNECTED;
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED: {
|
||||
uint8_t reason = event->event_info.disconnected.reason;
|
||||
|
||||
memset(&_apRecord, 0x00, sizeof(_apRecord));
|
||||
|
||||
if (reason == 201/*NO_AP_FOUND*/ || reason == 202/*AUTH_FAIL*/ || reason == 203/*ASSOC_FAIL*/) {
|
||||
_status = WL_CONNECT_FAILED;
|
||||
} else {
|
||||
_status = WL_DISCONNECTED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSTEM_EVENT_STA_LOST_IP:
|
||||
memset(&_ipInfo, 0x00, sizeof(_ipInfo));
|
||||
_status = WL_CONNECTION_LOST;
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_AP_START: {
|
||||
struct netif* apNetif;
|
||||
|
||||
if (tcpip_adapter_get_netif(TCPIP_ADAPTER_IF_AP, (void**)&apNetif) == ESP_OK) {
|
||||
if (apNetif->input != WiFiClass::apNetifInputHandler) {
|
||||
_apNetifInput = apNetif->input;
|
||||
|
||||
apNetif->input = WiFiClass::apNetifInputHandler;
|
||||
}
|
||||
}
|
||||
|
||||
wifi_config_t config;
|
||||
|
||||
esp_wifi_get_config(ESP_IF_WIFI_AP, &config);
|
||||
memcpy(_apRecord.ssid, config.ap.ssid, sizeof(config.ap.ssid));
|
||||
_apRecord.authmode = config.ap.authmode;
|
||||
|
||||
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &_ipInfo);
|
||||
_status = WL_AP_LISTENING;
|
||||
xEventGroupSetBits(_eventGroup, BIT1);
|
||||
break;
|
||||
}
|
||||
|
||||
case SYSTEM_EVENT_AP_STOP:
|
||||
_status = WL_IDLE_STATUS;
|
||||
memset(&_apRecord, 0x00, sizeof(_apRecord));
|
||||
memset(&_ipInfo, 0x00, sizeof(_ipInfo));
|
||||
xEventGroupClearBits(_eventGroup, BIT1);
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_AP_STACONNECTED:
|
||||
_status = WL_AP_CONNECTED;
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_AP_STADISCONNECTED:
|
||||
wifi_sta_list_t staList;
|
||||
|
||||
esp_wifi_ap_get_sta_list(&staList);
|
||||
|
||||
if (staList.num == 0) {
|
||||
_status = WL_AP_LISTENING;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WiFiClass WiFi;
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef WIFI_H
|
||||
#define WIFI_H
|
||||
|
||||
#include <esp_event_loop.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/event_groups.h>
|
||||
|
||||
#include <lwip/netif.h>
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
typedef enum {
|
||||
WL_NO_SHIELD = 255,
|
||||
WL_IDLE_STATUS = 0,
|
||||
WL_NO_SSID_AVAIL,
|
||||
WL_SCAN_COMPLETED,
|
||||
WL_CONNECTED,
|
||||
WL_CONNECT_FAILED,
|
||||
WL_CONNECTION_LOST,
|
||||
WL_DISCONNECTED,
|
||||
WL_AP_LISTENING,
|
||||
WL_AP_CONNECTED,
|
||||
WL_AP_FAILED,
|
||||
} wl_status_t;
|
||||
|
||||
#define MAX_SCAN_RESULTS 10
|
||||
|
||||
class WiFiClass
|
||||
{
|
||||
public:
|
||||
WiFiClass();
|
||||
|
||||
uint8_t begin(const char* ssid);
|
||||
uint8_t begin(const char* ssid, uint8_t key_idx, const char* key);
|
||||
uint8_t begin(const char* ssid, const char* key);
|
||||
|
||||
uint8_t beginAP(const char *ssid, uint8_t channel);
|
||||
uint8_t beginAP(const char *ssid, uint8_t key_idx, const char* key, uint8_t channel);
|
||||
uint8_t beginAP(const char *ssid, const char* key, uint8_t channel);
|
||||
|
||||
|
||||
void config(/*IPAddress*/uint32_t local_ip, /*IPAddress*/uint32_t gateway, /*IPAddress*/uint32_t subnet);
|
||||
|
||||
void setDNS(/*IPAddress*/uint32_t dns_server1, /*IPAddress*/uint32_t dns_server2);
|
||||
|
||||
void hostname(const char* name);
|
||||
|
||||
void disconnect();
|
||||
void end();
|
||||
|
||||
uint8_t* macAddress(uint8_t* mac);
|
||||
|
||||
uint32_t localIP();
|
||||
uint32_t subnetMask();
|
||||
uint32_t gatewayIP();
|
||||
char* SSID();
|
||||
int32_t RSSI();
|
||||
uint8_t encryptionType();
|
||||
uint8_t* BSSID(uint8_t* bssid);
|
||||
int8_t scanNetworks();
|
||||
char* SSID(uint8_t pos);
|
||||
int32_t RSSI(uint8_t pos);
|
||||
uint8_t encryptionType(uint8_t pos);
|
||||
uint8_t* BSSID(uint8_t pos, uint8_t* bssid);
|
||||
uint8_t channel(uint8_t pos);
|
||||
|
||||
uint8_t status();
|
||||
|
||||
int hostByName(const char* hostname, /*IPAddress*/uint32_t& result);
|
||||
|
||||
int ping(/*IPAddress*/uint32_t host, uint8_t ttl);
|
||||
|
||||
unsigned long getTime();
|
||||
|
||||
void lowPowerMode();
|
||||
void noLowPowerMode();
|
||||
|
||||
void onReceive(void(*)(void));
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
static esp_err_t systemEventHandler(void* ctx, system_event_t* event);
|
||||
void handleSystemEvent(system_event_t* event);
|
||||
|
||||
static err_t staNetifInputHandler(struct pbuf* p, struct netif* inp);
|
||||
static err_t apNetifInputHandler(struct pbuf* p, struct netif* inp);
|
||||
err_t handleStaNetifInput(struct pbuf* p, struct netif* inp);
|
||||
err_t handleApNetifInput(struct pbuf* p, struct netif* inp);
|
||||
|
||||
private:
|
||||
bool _initialized;
|
||||
volatile uint8_t _status;
|
||||
EventGroupHandle_t _eventGroup;
|
||||
esp_interface_t _interface;
|
||||
|
||||
wifi_ap_record_t _scanResults[MAX_SCAN_RESULTS];
|
||||
wifi_ap_record_t _apRecord;
|
||||
tcpip_adapter_ip_info_t _ipInfo;
|
||||
|
||||
netif_input_fn _staNetifInput;
|
||||
netif_input_fn _apNetifInput;
|
||||
|
||||
void (*_onReceiveCallback)(void);
|
||||
};
|
||||
|
||||
extern WiFiClass WiFi;
|
||||
|
||||
#endif // WIFI_H
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <lwip/netdb.h>
|
||||
#include <lwip/sockets.h>
|
||||
|
||||
#include "WiFiClient.h"
|
||||
|
||||
|
||||
WiFiClient::WiFiClient() :
|
||||
WiFiClient(-1)
|
||||
{
|
||||
}
|
||||
|
||||
WiFiClient::WiFiClient(int socket) :
|
||||
_socket(socket)
|
||||
{
|
||||
}
|
||||
|
||||
int WiFiClient::connect(const char* host, uint16_t port)
|
||||
{
|
||||
struct hostent* server = gethostbyname(host);
|
||||
|
||||
if (server == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return connect(server->h_addr, port);
|
||||
}
|
||||
|
||||
int WiFiClient::connect(/*IPAddress*/uint32_t ip, uint16_t port)
|
||||
{
|
||||
_socket = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (_socket < 0) {
|
||||
_socket = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0x00, sizeof(addr));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = (uint32_t)ip;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if (lwip_connect_r(_socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
lwip_close_r(_socket);
|
||||
_socket = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nonBlocking = 1;
|
||||
lwip_ioctl_r(_socket, FIONBIO, &nonBlocking);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t WiFiClient::write(uint8_t b)
|
||||
{
|
||||
return write(&b, 1);
|
||||
}
|
||||
|
||||
size_t WiFiClient::write(const uint8_t *buf, size_t size)
|
||||
{
|
||||
if (_socket == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = lwip_send_r(_socket, (void*)buf, size, MSG_DONTWAIT);
|
||||
|
||||
if (result < 0) {
|
||||
lwip_close_r(_socket);
|
||||
_socket = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int WiFiClient::available()
|
||||
{
|
||||
if (_socket == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
|
||||
if (lwip_ioctl_r(_socket, FIONREAD, &result) < 0) {
|
||||
lwip_close_r(_socket);
|
||||
_socket = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int WiFiClient::read()
|
||||
{
|
||||
uint8_t b;
|
||||
|
||||
if (read(&b, sizeof(b)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
int WiFiClient::read(uint8_t* buf, size_t size)
|
||||
{
|
||||
if (!available()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = lwip_recv_r(_socket, buf, size, MSG_DONTWAIT);
|
||||
|
||||
if (result <= 0 && errno != EWOULDBLOCK) {
|
||||
lwip_close_r(_socket);
|
||||
_socket = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
result = -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int WiFiClient::peek()
|
||||
{
|
||||
uint8_t b;
|
||||
|
||||
if (recv(_socket, &b, sizeof(b), MSG_PEEK | MSG_DONTWAIT) <= 0) {
|
||||
if (errno != EWOULDBLOCK) {
|
||||
lwip_close_r(_socket);
|
||||
_socket = -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void WiFiClient::flush()
|
||||
{
|
||||
}
|
||||
|
||||
void WiFiClient::stop()
|
||||
{
|
||||
if (_socket != -1) {
|
||||
lwip_close_r(_socket);
|
||||
_socket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t WiFiClient::connected()
|
||||
{
|
||||
if (_socket != -1) {
|
||||
// use peek to update socket state
|
||||
peek();
|
||||
}
|
||||
|
||||
return (_socket != -1);
|
||||
}
|
||||
|
||||
WiFiClient::operator bool()
|
||||
{
|
||||
return (_socket != -1);
|
||||
}
|
||||
|
||||
bool WiFiClient::operator==(const WiFiClient &other) const
|
||||
{
|
||||
return (_socket == other._socket);
|
||||
}
|
||||
|
||||
/*IPAddress*/uint32_t WiFiClient::remoteIP()
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
|
||||
getpeername(_socket, (struct sockaddr*)&addr, &len);
|
||||
|
||||
return ((struct sockaddr_in *)&addr)->sin_addr.s_addr;
|
||||
}
|
||||
|
||||
uint16_t WiFiClient::remotePort()
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
|
||||
getpeername(_socket, (struct sockaddr*)&addr, &len);
|
||||
|
||||
return ntohs(((struct sockaddr_in *)&addr)->sin_port);
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef WIFICLIENT_H
|
||||
#define WIFICLIENT_H
|
||||
|
||||
#include <Arduino.h>
|
||||
// #include <Client.h>
|
||||
// #include <IPAddress.h>
|
||||
|
||||
class WiFiServer;
|
||||
|
||||
class WiFiClient /*: public Client*/ {
|
||||
|
||||
public:
|
||||
WiFiClient();
|
||||
|
||||
uint8_t status();
|
||||
|
||||
virtual int connect(/*IPAddress*/uint32_t ip, uint16_t port);
|
||||
virtual int connect(const char* host, uint16_t port);
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const uint8_t *buf, size_t size);
|
||||
virtual int available();
|
||||
virtual int read();
|
||||
virtual int read(uint8_t *buf, size_t size);
|
||||
virtual int peek();
|
||||
virtual void flush();
|
||||
virtual void stop();
|
||||
virtual uint8_t connected();
|
||||
virtual operator bool();
|
||||
bool operator==(const WiFiClient &other) const;
|
||||
|
||||
virtual /*IPAddress*/uint32_t remoteIP();
|
||||
virtual uint16_t remotePort();
|
||||
|
||||
// using Print::write;
|
||||
|
||||
protected:
|
||||
friend class WiFiServer;
|
||||
|
||||
WiFiClient(int socket);
|
||||
|
||||
private:
|
||||
int _socket;
|
||||
};
|
||||
|
||||
#endif // WIFICLIENT_H
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <lwip/netdb.h>
|
||||
#include <lwip/sockets.h>
|
||||
#include "esp_partition.h"
|
||||
|
||||
#include "WiFiSSLClient.h"
|
||||
|
||||
class __Guard {
|
||||
public:
|
||||
__Guard(SemaphoreHandle_t handle) {
|
||||
_handle = handle;
|
||||
|
||||
xSemaphoreTakeRecursive(_handle, portMAX_DELAY);
|
||||
}
|
||||
|
||||
~__Guard() {
|
||||
xSemaphoreGiveRecursive(_handle);
|
||||
}
|
||||
|
||||
private:
|
||||
SemaphoreHandle_t _handle;
|
||||
};
|
||||
|
||||
#define synchronized __Guard __guard(_mbedMutex);
|
||||
|
||||
WiFiSSLClient::WiFiSSLClient() :
|
||||
_connected(false),
|
||||
_peek(-1)
|
||||
{
|
||||
_netContext.fd = -1;
|
||||
|
||||
_mbedMutex = xSemaphoreCreateRecursiveMutex();
|
||||
}
|
||||
|
||||
int WiFiSSLClient::connect(const char* host, uint16_t port)
|
||||
{
|
||||
synchronized {
|
||||
_netContext.fd = -1;
|
||||
_connected = false;
|
||||
|
||||
mbedtls_ssl_init(&_sslContext);
|
||||
mbedtls_ctr_drbg_init(&_ctrDrbgContext);
|
||||
mbedtls_ssl_config_init(&_sslConfig);
|
||||
mbedtls_entropy_init(&_entropyContext);
|
||||
mbedtls_x509_crt_init(&_caCrt);
|
||||
mbedtls_net_init(&_netContext);
|
||||
|
||||
if (mbedtls_ctr_drbg_seed(&_ctrDrbgContext, mbedtls_entropy_func, &_entropyContext, NULL, 0) != 0) {
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mbedtls_ssl_config_defaults(&_sslConfig, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_authmode(&_sslConfig, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||
|
||||
spi_flash_mmap_handle_t handle;
|
||||
const unsigned char* certs_data = {};
|
||||
|
||||
const esp_partition_t* part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "certs");
|
||||
if (part == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret = esp_partition_mmap(part, 0, part->size, SPI_FLASH_MMAP_DATA, (const void**)&certs_data, &handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = mbedtls_x509_crt_parse(&_caCrt, certs_data, strlen((char*)certs_data) + 1);
|
||||
if (ret < 0) {
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_ca_chain(&_sslConfig, &_caCrt, NULL);
|
||||
|
||||
mbedtls_ssl_conf_rng(&_sslConfig, mbedtls_ctr_drbg_random, &_ctrDrbgContext);
|
||||
|
||||
if (mbedtls_ssl_setup(&_sslContext, &_sslConfig) != 0) {
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
char portStr[6];
|
||||
itoa(port, portStr, 10);
|
||||
|
||||
if (mbedtls_net_connect(&_netContext, host, portStr, MBEDTLS_NET_PROTO_TCP) != 0) {
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
mbedtls_ssl_set_bio(&_sslContext, &_netContext, mbedtls_net_send, mbedtls_net_recv, NULL);
|
||||
|
||||
int result;
|
||||
|
||||
do {
|
||||
result = mbedtls_ssl_handshake(&_sslContext);
|
||||
} while (result == MBEDTLS_ERR_SSL_WANT_READ || result == MBEDTLS_ERR_SSL_WANT_WRITE);
|
||||
|
||||
if (result != 0) {
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
mbedtls_net_set_nonblock(&_netContext);
|
||||
_connected = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int WiFiSSLClient::connect(/*IPAddress*/uint32_t ip, uint16_t port)
|
||||
{
|
||||
char ipStr[16];
|
||||
|
||||
sprintf(ipStr, "%d.%d.%d.%d", ((ip & 0xff000000) >> 24), ((ip & 0x00ff0000) >> 16), ((ip & 0x0000ff00) >> 8), ((ip & 0x000000ff) >> 0)/*ip[0], ip[1], ip[2], ip[3]*/);
|
||||
|
||||
return connect(ipStr, port);
|
||||
}
|
||||
|
||||
size_t WiFiSSLClient::write(uint8_t b)
|
||||
{
|
||||
return write(&b, 1);
|
||||
}
|
||||
|
||||
size_t WiFiSSLClient::write(const uint8_t *buf, size_t size)
|
||||
{
|
||||
synchronized {
|
||||
int written = mbedtls_ssl_write(&_sslContext, buf, size);
|
||||
|
||||
if (written < 0) {
|
||||
written = 0;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
}
|
||||
|
||||
int WiFiSSLClient::available()
|
||||
{
|
||||
synchronized {
|
||||
int result = mbedtls_ssl_read(&_sslContext, NULL, 0);
|
||||
|
||||
int n = mbedtls_ssl_get_bytes_avail(&_sslContext);
|
||||
|
||||
if (n == 0 && result != 0 && result != MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
stop();
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
int WiFiSSLClient::read()
|
||||
{
|
||||
uint8_t b;
|
||||
|
||||
if (_peek != -1) {
|
||||
b = _peek;
|
||||
_peek = -1;
|
||||
} else if (read(&b, sizeof(b)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
int WiFiSSLClient::read(uint8_t* buf, size_t size)
|
||||
{
|
||||
synchronized {
|
||||
if (!available()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = mbedtls_ssl_read(&_sslContext, buf, size);
|
||||
|
||||
if (result < 0) {
|
||||
if (result != MBEDTLS_ERR_SSL_WANT_READ && result != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
stop();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
int WiFiSSLClient::peek()
|
||||
{
|
||||
if (_peek == -1) {
|
||||
_peek = read();
|
||||
}
|
||||
|
||||
return _peek;
|
||||
}
|
||||
|
||||
void WiFiSSLClient::flush()
|
||||
{
|
||||
}
|
||||
|
||||
void WiFiSSLClient::stop()
|
||||
{
|
||||
synchronized {
|
||||
if (_netContext.fd > 0) {
|
||||
mbedtls_ssl_session_reset(&_sslContext);
|
||||
|
||||
mbedtls_net_free(&_netContext);
|
||||
mbedtls_x509_crt_free(&_caCrt);
|
||||
mbedtls_entropy_free(&_entropyContext);
|
||||
mbedtls_ssl_config_free(&_sslConfig);
|
||||
mbedtls_ctr_drbg_free(&_ctrDrbgContext);
|
||||
mbedtls_ssl_free(&_sslContext);
|
||||
}
|
||||
|
||||
_connected = false;
|
||||
_netContext.fd = -1;
|
||||
}
|
||||
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
uint8_t WiFiSSLClient::connected()
|
||||
{
|
||||
synchronized {
|
||||
if (!_connected) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (available()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
WiFiSSLClient::operator bool()
|
||||
{
|
||||
return ((_netContext.fd != -1) && _connected);
|
||||
}
|
||||
|
||||
/*IPAddress*/uint32_t WiFiSSLClient::remoteIP()
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
|
||||
getpeername(_netContext.fd, (struct sockaddr*)&addr, &len);
|
||||
|
||||
return ((struct sockaddr_in *)&addr)->sin_addr.s_addr;
|
||||
}
|
||||
|
||||
uint16_t WiFiSSLClient::remotePort()
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
|
||||
getpeername(_netContext.fd, (struct sockaddr*)&addr, &len);
|
||||
|
||||
return ntohs(((struct sockaddr_in *)&addr)->sin_port);
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef WIFISSLCLIENT_H
|
||||
#define WIFISSLCLIENT_H
|
||||
|
||||
#include <mbedtls/net.h>
|
||||
#include <mbedtls/ssl.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/error.h>
|
||||
|
||||
#include <Arduino.h>
|
||||
// #include <Client.h>
|
||||
// #include <IPAddress.h>
|
||||
|
||||
class WiFiSSLClient /*: public Client*/ {
|
||||
|
||||
public:
|
||||
WiFiSSLClient();
|
||||
|
||||
uint8_t status();
|
||||
|
||||
virtual int connect(/*IPAddress*/uint32_t ip, uint16_t port);
|
||||
virtual int connect(const char* host, uint16_t port);
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const uint8_t *buf, size_t size);
|
||||
virtual int available();
|
||||
virtual int read();
|
||||
virtual int read(uint8_t *buf, size_t size);
|
||||
virtual int peek();
|
||||
virtual void flush();
|
||||
virtual void stop();
|
||||
virtual uint8_t connected();
|
||||
virtual operator bool();
|
||||
|
||||
// using Print::write;
|
||||
|
||||
virtual /*IPAddress*/uint32_t remoteIP();
|
||||
virtual uint16_t remotePort();
|
||||
|
||||
private:
|
||||
static const char* ROOT_CAs;
|
||||
|
||||
mbedtls_entropy_context _entropyContext;
|
||||
mbedtls_ctr_drbg_context _ctrDrbgContext;
|
||||
mbedtls_ssl_context _sslContext;
|
||||
mbedtls_ssl_config _sslConfig;
|
||||
mbedtls_net_context _netContext;
|
||||
mbedtls_x509_crt _caCrt;
|
||||
bool _connected;
|
||||
int _peek;
|
||||
|
||||
SemaphoreHandle_t _mbedMutex;
|
||||
};
|
||||
|
||||
#endif /* WIFISSLCLIENT_H */
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <lwip/sockets.h>
|
||||
|
||||
#include "WiFiClient.h"
|
||||
#include "WiFiServer.h"
|
||||
|
||||
WiFiServer::WiFiServer() :
|
||||
WiFiServer(0)
|
||||
{
|
||||
}
|
||||
|
||||
WiFiServer::WiFiServer(uint16_t port) :
|
||||
_port(port),
|
||||
_socket(-1)
|
||||
{
|
||||
for (int i = 0; i < CONFIG_LWIP_MAX_SOCKETS; i++) {
|
||||
_spawnedSockets[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiServer::begin()
|
||||
{
|
||||
_socket = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (_socket < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0x00, sizeof(addr));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = (uint32_t)0;
|
||||
addr.sin_port = htons(_port);
|
||||
|
||||
if (lwip_bind(_socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
lwip_close_r(_socket);
|
||||
_socket = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lwip_listen(_socket, 1) < 0) {
|
||||
lwip_close_r(_socket);
|
||||
_socket = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
int nonBlocking = 1;
|
||||
lwip_ioctl_r(_socket, FIONBIO, &nonBlocking);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
WiFiClient WiFiServer::available(uint8_t* status)
|
||||
{
|
||||
int result = lwip_accept(_socket, NULL, 0);
|
||||
|
||||
if (status) {
|
||||
*status = (result != -1);
|
||||
}
|
||||
|
||||
if (result != -1) {
|
||||
// store the connected socket
|
||||
for (int i = 0; i < CONFIG_LWIP_MAX_SOCKETS; i++) {
|
||||
if (_spawnedSockets[i] == -1) {
|
||||
_spawnedSockets[i] = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = -1;
|
||||
|
||||
// find an existing socket with data
|
||||
for (int i = 0; i < CONFIG_LWIP_MAX_SOCKETS; i++) {
|
||||
if (_spawnedSockets[i] != -1) {
|
||||
WiFiClient c(_spawnedSockets[i]);
|
||||
|
||||
if (!c.connected()) {
|
||||
// socket not connected, clear from book keeping
|
||||
_spawnedSockets[i] = -1;
|
||||
} else if (c.available()) {
|
||||
result = _spawnedSockets[i];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return WiFiClient(result);
|
||||
}
|
||||
|
||||
uint8_t WiFiServer::status() {
|
||||
// Deprecated.
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t WiFiServer::write(uint8_t b)
|
||||
{
|
||||
return write(&b, 1);
|
||||
}
|
||||
|
||||
size_t WiFiServer::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
size_t written = 0;
|
||||
|
||||
for (int i = 0; i < CONFIG_LWIP_MAX_SOCKETS; i++) {
|
||||
if (_spawnedSockets[i] != -1) {
|
||||
WiFiClient c(_spawnedSockets[i]);
|
||||
|
||||
written += c.write(buffer, size);
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
WiFiServer::operator bool()
|
||||
{
|
||||
return (_port != 0 && _socket != -1);
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef WIFISERVER_H
|
||||
#define WIFISERVER_H
|
||||
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#include <Arduino.h>
|
||||
// #include <Server.h>
|
||||
|
||||
class WiFiClient;
|
||||
|
||||
class WiFiServer /*: public Server*/ {
|
||||
public:
|
||||
WiFiServer();
|
||||
WiFiServer(uint16_t);
|
||||
WiFiClient available(uint8_t* status = NULL);
|
||||
void begin();
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const uint8_t *buf, size_t size);
|
||||
uint8_t status();
|
||||
|
||||
// using Print::write;
|
||||
|
||||
virtual operator bool();
|
||||
|
||||
private:
|
||||
uint16_t _port;
|
||||
int _socket;
|
||||
int _spawnedSockets[CONFIG_LWIP_MAX_SOCKETS];
|
||||
};
|
||||
|
||||
#endif // WIFISERVER_H
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <lwip/netdb.h>
|
||||
#include <lwip/sockets.h>
|
||||
|
||||
#include "WiFiUdp.h"
|
||||
|
||||
WiFiUDP::WiFiUDP() :
|
||||
_socket(-1),
|
||||
_remoteIp(0),
|
||||
_remotePort(0),
|
||||
_rcvIndex(0),
|
||||
_rcvSize(0),
|
||||
_sndSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t WiFiUDP::begin(uint16_t port)
|
||||
{
|
||||
_socket = lwip_socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
if (_socket < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0x00, sizeof(addr));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = (uint32_t)0;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if (lwip_bind(_socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
lwip_close_r(_socket);
|
||||
_socket = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nonBlocking = 1;
|
||||
lwip_ioctl_r(_socket, FIONBIO, &nonBlocking);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t WiFiUDP::beginMulticast(/*IPAddress*/uint32_t ip, uint16_t port)
|
||||
{
|
||||
if (!begin(port)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ip_mreq multi;
|
||||
|
||||
multi.imr_multiaddr.s_addr = (uint32_t)ip;
|
||||
multi.imr_interface.s_addr = (uint32_t)0;
|
||||
|
||||
lwip_setsockopt_r(_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &multi, sizeof(multi));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* return number of bytes available in the current packet,
|
||||
will return zero if parsePacket hasn't been called yet */
|
||||
int WiFiUDP::available()
|
||||
{
|
||||
return (_rcvSize - _rcvIndex);
|
||||
}
|
||||
|
||||
/* Release any resources being used by this WiFiUDP instance */
|
||||
void WiFiUDP::stop()
|
||||
{
|
||||
lwip_close_r(_socket);
|
||||
_socket = -1;
|
||||
}
|
||||
|
||||
int WiFiUDP::beginPacket(const char *host, uint16_t port)
|
||||
{
|
||||
struct hostent* server = gethostbyname(host);
|
||||
|
||||
if (server == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return beginPacket(server->h_addr, port);
|
||||
}
|
||||
|
||||
int WiFiUDP::beginPacket(/*IPAddress*/uint32_t ip, uint16_t port)
|
||||
{
|
||||
_remoteIp = ip;
|
||||
_remotePort = port;
|
||||
|
||||
_sndSize = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int WiFiUDP::endPacket()
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0x00, sizeof(addr));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = _remoteIp;
|
||||
addr.sin_port = htons(_remotePort);
|
||||
|
||||
if (lwip_sendto(_socket, _sndBuffer, _sndSize, 0, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t WiFiUDP::write(uint8_t byte)
|
||||
{
|
||||
return write(&byte, 1);
|
||||
}
|
||||
|
||||
size_t WiFiUDP::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
size_t written = size;
|
||||
|
||||
if ((_sndSize + size) > sizeof(_sndBuffer)) {
|
||||
written = sizeof(_sndBuffer) - _sndSize;
|
||||
}
|
||||
|
||||
memcpy(&_sndBuffer[_sndSize], buffer, size);
|
||||
|
||||
_sndSize += written;
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
int WiFiUDP::parsePacket()
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrLen = sizeof(addr);
|
||||
|
||||
_rcvIndex = 0;
|
||||
_rcvSize = 0;
|
||||
|
||||
int result = lwip_recvfrom_r(_socket, _rcvBuffer, sizeof(_rcvBuffer), MSG_DONTWAIT, (struct sockaddr*)&addr, &addrLen);
|
||||
|
||||
if (result <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_rcvSize = result;
|
||||
_remoteIp = addr.sin_addr.s_addr;
|
||||
_remotePort = ntohs(addr.sin_port);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int WiFiUDP::read()
|
||||
{
|
||||
uint8_t b;
|
||||
|
||||
if (read(&b, sizeof(b)) < 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
int WiFiUDP::read(unsigned char* buf, size_t size)
|
||||
{
|
||||
if (available() < (int)size) {
|
||||
size = available();
|
||||
}
|
||||
|
||||
memcpy(buf, &_rcvBuffer[_rcvIndex], size);
|
||||
|
||||
_rcvIndex += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int WiFiUDP::peek()
|
||||
{
|
||||
if (!available()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _rcvBuffer[_rcvIndex];
|
||||
}
|
||||
|
||||
void WiFiUDP::flush()
|
||||
{
|
||||
}
|
||||
|
||||
/*IPAddress*/uint32_t WiFiUDP::remoteIP()
|
||||
{
|
||||
return _remoteIp;
|
||||
}
|
||||
|
||||
uint16_t WiFiUDP::remotePort()
|
||||
{
|
||||
return _remotePort;
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
This file is part of the Arduino NINA firmware.
|
||||
Copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef WIFIUDP_H
|
||||
#define WIFIUDP_H
|
||||
|
||||
// #include <Udp.h>
|
||||
|
||||
class WiFiUDP /*: public UDP*/ {
|
||||
|
||||
public:
|
||||
WiFiUDP();
|
||||
virtual uint8_t begin(uint16_t);
|
||||
virtual uint8_t beginMulticast(/*IPAddress*/uint32_t, uint16_t);
|
||||
virtual void stop();
|
||||
|
||||
virtual int beginPacket(/*IPAddress*/uint32_t ip, uint16_t port);
|
||||
virtual int beginPacket(const char *host, uint16_t port);
|
||||
virtual int endPacket();
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
|
||||
// using Print::write;
|
||||
|
||||
virtual int parsePacket();
|
||||
virtual int available();
|
||||
virtual int read();
|
||||
virtual int read(unsigned char* buffer, size_t len);
|
||||
virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); };
|
||||
virtual int peek();
|
||||
virtual void flush();
|
||||
|
||||
virtual /*IPAddress*/ uint32_t remoteIP();
|
||||
virtual uint16_t remotePort();
|
||||
|
||||
virtual operator bool() { return _socket != -1; }
|
||||
|
||||
private:
|
||||
int _socket;
|
||||
uint32_t _remoteIp;
|
||||
uint16_t _remotePort;
|
||||
|
||||
uint8_t _rcvBuffer[1500];
|
||||
uint16_t _rcvIndex;
|
||||
uint16_t _rcvSize;
|
||||
uint8_t _sndBuffer[1500];
|
||||
uint16_t _sndSize;
|
||||
};
|
||||
|
||||
#endif // WIFIUDP_H
|
||||
3
boards/CMakeLists.txt
Normal file
3
boards/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
idf_component_register(
|
||||
INCLUDE_DIRS . ${BOARD}
|
||||
)
|
||||
18
boards/esp32/board.h
Normal file
18
boards/esp32/board.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef BOARD_H
|
||||
#define BOARD_H
|
||||
|
||||
// SPIS for WiFi
|
||||
#define AIRLIFT_MOSI 14
|
||||
#define AIRLIFT_MISO 23
|
||||
#define AIRLIFT_SCK 18
|
||||
#define AIRLIFT_CS 5
|
||||
#define AIRLIFT_BUSY 33 // ready
|
||||
|
||||
// UART for BLE HCI
|
||||
#define AIRLIFT_RTS AIRLIFT_BUSY
|
||||
#define AIRLIFT_CTS 0 // BOOT PIN
|
||||
|
||||
// #define CONFIG_BT_LE_HCI_UART_RTS_PIN 33 // ESP_BUSY (ready)
|
||||
// #define CONFIG_BT_LE_HCI_UART_CTS_PIN 0 // GPIO0
|
||||
|
||||
#endif
|
||||
2
boards/esp32/sdkconfig
Normal file
2
boards/esp32/sdkconfig
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
CONFIG_BTDM_CTRL_HCI_MODE_UART_H4=y
|
||||
CONFIG_BTDM_CTRL_HCI_UART_BAUDRATE=115200
|
||||
11
boards/fruitjam_c6/board.h
Normal file
11
boards/fruitjam_c6/board.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef BOARD_H
|
||||
#define BOARD_H
|
||||
|
||||
// SPIS for WiFi
|
||||
#define AIRLIFT_MOSI 21
|
||||
#define AIRLIFT_MISO 6
|
||||
#define AIRLIFT_SCK 22
|
||||
#define AIRLIFT_CS 7
|
||||
#define AIRLIFT_BUSY CONFIG_BT_LE_HCI_UART_RTS_PIN // 18, aka ready
|
||||
|
||||
#endif
|
||||
9
boards/fruitjam_c6/sdkconfig
Normal file
9
boards/fruitjam_c6/sdkconfig
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# BT_LE_HCI
|
||||
CONFIG_BT_LE_HCI_INTERFACE_USE_UART=y
|
||||
CONFIG_BT_LE_HCI_UART_FLOWCTRL=y
|
||||
CONFIG_BT_LE_HCI_UART_TX_PIN=16
|
||||
CONFIG_BT_LE_HCI_UART_RX_PIN=17
|
||||
CONFIG_BT_LE_HCI_UART_RTS_PIN=18
|
||||
CONFIG_BT_LE_HCI_UART_CTS_PIN=9
|
||||
CONFIG_BT_LE_HCI_UART_BAUD=115200
|
||||
CONFIG_BT_LE_CONTROLLER_LOG_ENABLED=y
|
||||
BIN
build_mkspiffs
BIN
build_mkspiffs
Binary file not shown.
1
certificates
Submodule
1
certificates
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 96174bf8bafd24c6b215fa065460a582a7624037
|
||||
127
code-of-conduct.md
Normal file
127
code-of-conduct.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Adafruit Community Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and leaders pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level or type of
|
||||
experience, education, socio-economic status, nationality, personal appearance,
|
||||
race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
We are committed to providing a friendly, safe and welcoming environment for
|
||||
all.
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Be kind and courteous to others
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Collaborating with other community members
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and sexual attention or advances
|
||||
* The use of inappropriate images, including in a community member's avatar
|
||||
* The use of inappropriate language, including in a community member's nickname
|
||||
* Any spamming, flaming, baiting or other attention-stealing behavior
|
||||
* Excessive or unwelcome helping; answering outside the scope of the question
|
||||
asked
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate
|
||||
|
||||
The goal of the standards and moderation guidelines outlined here is to build
|
||||
and maintain a respectful community. We ask that you don’t just aim to be
|
||||
"technically unimpeachable", but rather try to be your best self.
|
||||
|
||||
We value many things beyond technical expertise, including collaboration and
|
||||
supporting others within our community. Providing a positive experience for
|
||||
other community members can have a much more significant impact than simply
|
||||
providing the correct answer.
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project leaders are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project leaders have the right and responsibility to remove, edit, or
|
||||
reject messages, comments, commits, code, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any community member for other behaviors that they deem
|
||||
inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Moderation
|
||||
|
||||
Instances of behaviors that violate the Adafruit Community Code of Conduct
|
||||
may be reported by any member of the community. Community members are
|
||||
encouraged to report these situations, including situations they witness
|
||||
involving other community members.
|
||||
|
||||
You may report in the following ways:
|
||||
|
||||
In any situation, you may send an email to <support@adafruit.com>.
|
||||
|
||||
On the Adafruit Discord, you may send an open message from any channel
|
||||
to all Community Helpers by tagging @community helpers. You may also send an
|
||||
open message from any channel, or a direct message to @kattni#1507,
|
||||
@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or
|
||||
@Andon#8175.
|
||||
|
||||
Email and direct message reports will be kept confidential.
|
||||
|
||||
In situations on Discord where the issue is particularly egregious, possibly
|
||||
illegal, requires immediate action, or violates the Discord terms of service,
|
||||
you should also report the message directly to Discord.
|
||||
|
||||
These are the steps for upholding our community’s standards of conduct.
|
||||
|
||||
1. Any member of the community may report any situation that violates the
|
||||
Adafruit Community Code of Conduct. All reports will be reviewed and
|
||||
investigated.
|
||||
2. If the behavior is an egregious violation, the community member who
|
||||
committed the violation may be banned immediately, without warning.
|
||||
3. Otherwise, moderators will first respond to such behavior with a warning.
|
||||
4. Moderators follow a soft "three strikes" policy - the community member may
|
||||
be given another chance, if they are receptive to the warning and change their
|
||||
behavior.
|
||||
5. If the community member is unreceptive or unreasonable when warned by a
|
||||
moderator, or the warning goes unheeded, they may be banned for a first or
|
||||
second offense. Repeated offenses will result in the community member being
|
||||
banned.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct and the enforcement policies listed above apply to all
|
||||
Adafruit Community venues. This includes but is not limited to any community
|
||||
spaces (both public and private), the entire Adafruit Discord server, and
|
||||
Adafruit GitHub repositories. Examples of Adafruit Community spaces include
|
||||
but are not limited to meet-ups, audio chats on the Adafruit Discord, or
|
||||
interaction at a conference.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. As a community
|
||||
member, you are representing our community, and are expected to behave
|
||||
accordingly.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 1.4, available at
|
||||
<https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>,
|
||||
and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html).
|
||||
|
||||
For other projects adopting the Adafruit Community Code of
|
||||
Conduct, please contact the maintainers of those projects for enforcement.
|
||||
If you wish to use this code of conduct for your own project, consider
|
||||
explicitly mentioning your moderation policy or making a copy with your
|
||||
own moderation policy so as to avoid confusion.
|
||||
114
combine.py
114
combine.py
|
|
@ -1,39 +1,97 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
booloaderData = open("build/bootloader/bootloader.bin", "rb").read()
|
||||
partitionData = open("build/partitions.bin", "rb").read()
|
||||
appData = open("build/nina-fw.bin", "rb").read()
|
||||
certsData = open("data/roots.pem", "rb").read()
|
||||
storageData = open("build/spiffs_image.img", "rb").read()
|
||||
import json
|
||||
import sys
|
||||
import argparse
|
||||
import os
|
||||
|
||||
# calculate the output binary size, app offset
|
||||
outputSize = 0x1B0000 + len(storageData)
|
||||
if (outputSize % 1024):
|
||||
outputSize += 1024 - (outputSize % 1024)
|
||||
def extract_firmware_version():
|
||||
with open('main/CommandHandler.cpp', 'r') as file:
|
||||
for line in file:
|
||||
if 'const char FIRMWARE_VERSION[] = ' in line:
|
||||
# The line format is `const char FIRMWARE_VERSION[] = "2.0.0-adafruit";`
|
||||
# Split by double quote and get the second element
|
||||
version = line.split('"')[1]
|
||||
return version
|
||||
raise RuntimeError("FIRMWARE_VERSION not found in CommandHandler.cpp")
|
||||
|
||||
# allocate and init to 0xff
|
||||
outputData = bytearray(b'\xff') * outputSize
|
||||
def get_idf_target(build_dir):
|
||||
with open(f"{build_dir}/config.env") as file:
|
||||
config = json.load(file)
|
||||
return config["IDF_TARGET"]
|
||||
|
||||
# copy data: bootloader, partitions, app
|
||||
for i in range(0, len(booloaderData)):
|
||||
outputData[0x1000 + i] = booloaderData[i]
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('outfile', help='output file')
|
||||
parser.add_argument('-b', '--build_dir', default='build', help='build directory')
|
||||
|
||||
for i in range(0, len(partitionData)):
|
||||
outputData[0x8000 + i] = partitionData[i]
|
||||
args = parser.parse_args()
|
||||
outfile = args.outfile
|
||||
build_dir = os.path.normpath(args.build_dir)
|
||||
|
||||
for i in range(0, len(appData)):
|
||||
outputData[0x10000 + i] = appData[i]
|
||||
bootloaderData = open(f"{build_dir}/bootloader/bootloader.bin", "rb").read()
|
||||
partitionData = open(f"{build_dir}/partition_table/partition-table.bin", "rb").read()
|
||||
#phyData = open("data/phy.bin", "rb").read()
|
||||
appData = open(f"{build_dir}/nina-fw.bin", "rb").read()
|
||||
|
||||
for i in range(0, len(certsData)):
|
||||
outputData[0x190000 + i] = certsData[i]
|
||||
# remove everything between certificate markers to save space. There might be comments and other information.
|
||||
certsData = b""
|
||||
with open("certificates/data/roots.pem", "rb") as certs_file:
|
||||
in_cert = False
|
||||
for line in certs_file:
|
||||
if line.startswith(b"-----BEGIN CERTIFICATE-----"):
|
||||
in_cert = True
|
||||
if in_cert:
|
||||
certsData += line
|
||||
if line.startswith(b"-----END CERTIFICATE-----"):
|
||||
in_cert = False
|
||||
|
||||
# zero terminate the pem file
|
||||
outputData[0x190000 + len(certsData)] = 0
|
||||
# calculate the output binary size, app offset
|
||||
outputSize = 0x30000 + len(appData)
|
||||
if outputSize % 1024:
|
||||
outputSize += 1024 - (outputSize % 1024)
|
||||
|
||||
for i in range(0, len(storageData)):
|
||||
outputData[0x1B0000 + i] = storageData[i]
|
||||
# allocate and init to 0xff
|
||||
outputData = bytearray(b"\xff") * outputSize
|
||||
|
||||
# write out
|
||||
with open("NINA_W102.bin","w+b") as f:
|
||||
f.seek(0)
|
||||
f.write(outputData)
|
||||
# copy data: bootloader, partitions, app
|
||||
BOOTLOADER_OFFSET = {
|
||||
"esp32" : 0x1000,
|
||||
"esp32c6" : 0x0000,
|
||||
}
|
||||
|
||||
try:
|
||||
idf_target = get_idf_target(build_dir)
|
||||
bootloader_offset = BOOTLOADER_OFFSET[idf_target]
|
||||
except KeyError:
|
||||
raise RuntimeError(f"unsupported IDF_TARGET: {idf_target}")
|
||||
|
||||
for i in range(0, len(bootloaderData)):
|
||||
outputData[bootloader_offset + i] = bootloaderData[i]
|
||||
|
||||
for i in range(0, len(partitionData)):
|
||||
outputData[0x8000 + i] = partitionData[i]
|
||||
|
||||
#for i in range(0, len(phyData)):
|
||||
# outputData[0xf000 + i] = phyData[i]
|
||||
|
||||
for i in range(0, len(certsData)):
|
||||
outputData[0x10000 + i] = certsData[i]
|
||||
|
||||
# zero terminate the pem file
|
||||
outputData[0x10000 + len(certsData)] = 0
|
||||
|
||||
for i in range(0, len(appData)):
|
||||
outputData[0x30000 + i] = appData[i]
|
||||
|
||||
version = extract_firmware_version()
|
||||
outputFilename = f"{outfile}-{version}.bin"
|
||||
|
||||
# write out
|
||||
with open(outputFilename, "w+b") as f:
|
||||
f.seek(0)
|
||||
f.write(outputData)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
4
components/SPIS/CMakeLists.txt
Normal file
4
components/SPIS/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
idf_component_register(SRCS src/SPIS.cpp
|
||||
INCLUDE_DIRS src
|
||||
REQUIRES arduino-esp32
|
||||
)
|
||||
|
|
@ -22,8 +22,7 @@
|
|||
#include <driver/gpio.h>
|
||||
#include <driver/spi_slave.h>
|
||||
|
||||
#include "wiring_digital.h"
|
||||
#include "WInterrupts.h"
|
||||
#include "esp32-hal-gpio.h"
|
||||
|
||||
#include "SPIS.h"
|
||||
|
||||
|
|
@ -113,4 +112,5 @@ void SPISClass::handleSetupComplete()
|
|||
xSemaphoreGiveFromISR(_readySemaphore, NULL);
|
||||
}
|
||||
|
||||
SPISClass SPIS(VSPI_HOST, 1, 12, 23, 18, 5, 33);
|
||||
// Move definition to sketch.ino.cpp
|
||||
// SPISClass SPIS(VSPI_HOST, 1, 12, 23, 18, 5, 33);
|
||||
1
components/arduino-esp32
Submodule
1
components/arduino-esp32
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit dbaf6a3226317a7c5e452e7b8a15e54c86bc2b6a
|
||||
10
components/mkspiffs/.gitignore
vendored
10
components/mkspiffs/.gitignore
vendored
|
|
@ -1,10 +0,0 @@
|
|||
*.o
|
||||
mkspiffs
|
||||
mkspiffs.exe
|
||||
out.*
|
||||
*.tar.gz
|
||||
|
||||
# eclipse wokspace files and dirs
|
||||
.project
|
||||
.cproject
|
||||
.settings
|
||||
3
components/mkspiffs/.gitmodules
vendored
3
components/mkspiffs/.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "spiffs"]
|
||||
path = spiffs
|
||||
url = https://github.com/pellepl/spiffs.git
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2017 Ivan Grokhotkov <ivan@espressif.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
BUILD_CONFIG_NAME ?=
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
TARGET_OS := WINDOWS
|
||||
DIST_SUFFIX := windows
|
||||
ARCHIVE_CMD := 7z a
|
||||
ARCHIVE_EXTENSION := zip
|
||||
TARGET := mkspiffs.exe
|
||||
TARGET_CFLAGS := -mno-ms-bitfields
|
||||
TARGET_LDFLAGS := -Wl,-static -static-libgcc
|
||||
CC=gcc
|
||||
CXX=g++
|
||||
else
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
TARGET_OS := LINUX
|
||||
UNAME_P := $(shell uname -p)
|
||||
ifeq ($(UNAME_P),x86_64)
|
||||
DIST_SUFFIX := linux64
|
||||
endif
|
||||
ifneq ($(filter %86,$(UNAME_P)),)
|
||||
DIST_SUFFIX := linux32
|
||||
endif
|
||||
endif
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
TARGET_OS := OSX
|
||||
DIST_SUFFIX := osx
|
||||
CC=clang
|
||||
CXX=clang++
|
||||
TARGET_CFLAGS = -mmacosx-version-min=10.7 -arch i386 -arch x86_64
|
||||
TARGET_CXXFLAGS = -mmacosx-version-min=10.7 -arch i386 -arch x86_64 -stdlib=libc++
|
||||
TARGET_LDFLAGS = -arch i386 -arch x86_64 -stdlib=libc++
|
||||
endif
|
||||
ARCHIVE_CMD := tar czf
|
||||
ARCHIVE_EXTENSION := tar.gz
|
||||
TARGET := mkspiffs
|
||||
endif
|
||||
|
||||
VERSION ?= $(shell git describe --always)
|
||||
|
||||
OBJ := main.o \
|
||||
spiffs/src/spiffs_cache.o \
|
||||
spiffs/src/spiffs_check.o \
|
||||
spiffs/src/spiffs_gc.o \
|
||||
spiffs/src/spiffs_hydrogen.o \
|
||||
spiffs/src/spiffs_nucleus.o \
|
||||
|
||||
INCLUDES := -Itclap -Iinclude -Ispiffs/src -I.
|
||||
|
||||
override CFLAGS := -std=gnu99 -Os -Wall $(TARGET_CFLAGS) $(CFLAGS)
|
||||
override CXXFLAGS := -std=gnu++11 -Os -Wall $(TARGET_CXXFLAGS) $(CXXFLAGS)
|
||||
override LDFLAGS := $(TARGET_LDFLAGS) $(LDFLAGS)
|
||||
override CPPFLAGS := $(INCLUDES) -D$(TARGET_OS) -DVERSION=\"$(VERSION)\" -D__NO_INLINE__ $(CPPFLAGS)
|
||||
|
||||
DIST_NAME := mkspiffs-$(VERSION)$(BUILD_CONFIG_NAME)-$(DIST_SUFFIX)
|
||||
DIST_DIR := $(DIST_NAME)
|
||||
DIST_ARCHIVE := $(DIST_NAME).$(ARCHIVE_EXTENSION)
|
||||
|
||||
.PHONY: all clean dist
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
dist: test $(DIST_ARCHIVE)
|
||||
|
||||
$(DIST_ARCHIVE): $(TARGET) $(DIST_DIR)
|
||||
cp $(TARGET) $(DIST_DIR)/
|
||||
$(ARCHIVE_CMD) $(DIST_ARCHIVE) $(DIST_DIR)
|
||||
|
||||
$(TARGET): $(OBJ)
|
||||
$(CXX) $^ -o $@ $(LDFLAGS)
|
||||
strip $(TARGET)
|
||||
|
||||
$(DIST_DIR):
|
||||
@mkdir -p $@
|
||||
|
||||
clean:
|
||||
@rm -f $(TARGET) $(OBJ)
|
||||
|
||||
SPIFFS_TEST_FS_CONFIG := -s 0x100000 -p 512 -b 0x2000
|
||||
|
||||
test: $(TARGET)
|
||||
mkdir -p spiffs_t
|
||||
cp spiffs/src/*.h spiffs_t/
|
||||
cp spiffs/src/*.c spiffs_t/
|
||||
rm -rf spiffs_t/.git
|
||||
rm -f spiffs_t/.DS_Store
|
||||
ls -1 spiffs_t > out.list0
|
||||
touch spiffs_t/.DS_Store
|
||||
mkdir -p spiffs_t/.git
|
||||
touch spiffs_t/.git/foo
|
||||
./mkspiffs -c spiffs_t $(SPIFFS_TEST_FS_CONFIG) out.spiffs_t | sort | sed s/^\\/// > out.list1
|
||||
./mkspiffs -u spiffs_u $(SPIFFS_TEST_FS_CONFIG) out.spiffs_t | sort | sed s/^\\/// > out.list_u
|
||||
./mkspiffs -l $(SPIFFS_TEST_FS_CONFIG) out.spiffs_t | cut -f 2 | sort | sed s/^\\/// > out.list2
|
||||
diff --strip-trailing-cr out.list0 out.list1
|
||||
diff --strip-trailing-cr out.list0 out.list2
|
||||
rm -rf spiffs_t/.git
|
||||
rm -f spiffs_t/.DS_Store
|
||||
diff spiffs_t spiffs_u
|
||||
rm -f out.{list0,list1,list2,list_u,spiffs_t}
|
||||
rm -R spiffs_u spiffs_t
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
# mkspiffs
|
||||
Tool to build and unpack [SPIFFS](https://github.com/pellepl/spiffs) images.
|
||||
|
||||
## **IMPORTANT** - building for ESP32 esp-idf spiffs
|
||||
---
|
||||
The **sdkconfig.h** from your application using spiffs has to be used in build process!
|
||||
Before building **mkspiffs**, copy the **sdkconfig.h** from your application **build/include** directory to **mkspiffs/include**
|
||||
After every change to spiffs configuration **rebuild mkspiffs**
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
|
||||
mkspiffs {-c <pack_dir>|-u <dest_dir>|-l|-i} [-d <0-5>] [-b <number>]
|
||||
[-p <number>] [-s <number>] [--] [--version] [-h]
|
||||
<image_file>
|
||||
|
||||
|
||||
Where:
|
||||
|
||||
-c <pack_dir>, --create <pack_dir>
|
||||
(OR required) create spiffs image from a directory
|
||||
-- OR --
|
||||
-u <dest_dir>, --unpack <dest_dir>
|
||||
(OR required) unpack spiffs image to a directory
|
||||
-- OR --
|
||||
-l, --list
|
||||
(OR required) list files in spiffs image
|
||||
-- OR --
|
||||
-i, --visualize
|
||||
(OR required) visualize spiffs image
|
||||
|
||||
|
||||
-d <0-5>, --debug <0-5>
|
||||
Debug level. 0 means no debug output.
|
||||
|
||||
-b <number>, --block <number>
|
||||
fs block size, in bytes
|
||||
|
||||
-p <number>, --page <number>
|
||||
fs page size, in bytes
|
||||
|
||||
-s <number>, --size <number>
|
||||
fs image size, in bytes
|
||||
|
||||
--, --ignore_rest
|
||||
Ignores the rest of the labeled arguments following this flag.
|
||||
|
||||
--version
|
||||
Displays version information and exits.
|
||||
|
||||
-h, --help
|
||||
Displays usage information and exits.
|
||||
|
||||
<image_file>
|
||||
(required) spiffs image file
|
||||
|
||||
|
||||
```
|
||||
## Build
|
||||
|
||||
You need gcc (≥4.8) or clang(≥600.0.57), and make. On Windows, use MinGW.
|
||||
|
||||
Run:
|
||||
```bash
|
||||
$ make dist
|
||||
```
|
||||
|
||||
### Build status
|
||||
|
||||
Linux | Windows
|
||||
------|-------
|
||||
[](https://travis-ci.org/igrr/mkspiffs) | [](https://ci.appveyor.com/project/igrr/mkspiffs)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## To do
|
||||
|
||||
- [ ] Add more debug output and print SPIFFS debug output
|
||||
- [ ] Error handling
|
||||
- [ ] Determine the image size automatically when opening a file
|
||||
- [ ] Code cleanup
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#define CONFIG_SPIFFS_USE_MAGIC_LENGTH 1
|
||||
#define CONFIG_SPIFFS_CACHE_WR 1
|
||||
#define CONFIG_SPIFFS_CACHE 1
|
||||
#define CONFIG_SPIFFS_USE_DIR 1
|
||||
#define CONFIG_SPIFFS_META_LENGTH 4
|
||||
#define CONFIG_SPIFFS_USE_MAGIC 1
|
||||
#define CONFIG_SPIFFS_PAGE_CHECK 1
|
||||
#define CONFIG_SPIFFS_GC_MAX_RUNS 10
|
||||
#define CONFIG_SPIFFS_MAX_PARTITIONS 3
|
||||
#define CONFIG_SPIFFS_OBJ_NAME_LEN 32
|
||||
#define CONFIG_SPIFFS_PAGE_SIZE 256
|
||||
|
|
@ -1,342 +0,0 @@
|
|||
/*
|
||||
* spiffs_config.h
|
||||
*
|
||||
* Created on: Jul 3, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#ifndef SPIFFS_CONFIG_H_
|
||||
#define SPIFFS_CONFIG_H_
|
||||
|
||||
// ----------- 8< ------------
|
||||
// Following includes are for the linux test build of spiffs
|
||||
// These may/should/must be removed/altered/replaced in your target
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <sdkconfig.h>
|
||||
|
||||
// Set generic spiffs debug output call.
|
||||
#ifndef SPIFFS_DBG
|
||||
#define SPIFFS_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for garbage collecting.
|
||||
#ifndef SPIFFS_GC_DBG
|
||||
#define SPIFFS_GC_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for caching.
|
||||
#ifndef SPIFFS_CACHE_DBG
|
||||
#define SPIFFS_CACHE_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for system consistency checks.
|
||||
#ifndef SPIFFS_CHECK_DBG
|
||||
#define SPIFFS_CHECK_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for all api invocations.
|
||||
#ifndef SPIFFS_API_DBG
|
||||
#define SPIFFS_API_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
// needed types
|
||||
typedef int16_t file_t;
|
||||
typedef int32_t s32_t;
|
||||
typedef uint32_t u32_t;
|
||||
typedef int16_t s16_t;
|
||||
typedef uint16_t u16_t;
|
||||
typedef int8_t s8_t;
|
||||
typedef uint8_t u8_t;
|
||||
|
||||
// Defines spiffs debug print formatters
|
||||
// some general signed number
|
||||
#define _SPIPRIi "%d"
|
||||
// address
|
||||
#define _SPIPRIad "%08x"
|
||||
// block
|
||||
#define _SPIPRIbl "%04x"
|
||||
// page
|
||||
#define _SPIPRIpg "%04x"
|
||||
// span index
|
||||
#define _SPIPRIsp "%04x"
|
||||
// file descriptor
|
||||
#define _SPIPRIfd "%d"
|
||||
// file object id
|
||||
#define _SPIPRIid "%04x"
|
||||
// file flags
|
||||
#define _SPIPRIfl "%02x"
|
||||
|
||||
|
||||
// Enable/disable API functions to determine exact number of bytes
|
||||
// for filedescriptor and cache buffers. Once decided for a configuration,
|
||||
// this can be disabled to reduce flash.
|
||||
#ifndef SPIFFS_BUFFER_HELP
|
||||
#define SPIFFS_BUFFER_HELP 0
|
||||
#endif
|
||||
|
||||
// Enables/disable memory read caching of nucleus file system operations.
|
||||
// If enabled, memory area must be provided for cache in SPIFFS_mount.
|
||||
#ifndef SPIFFS_CACHE
|
||||
#define SPIFFS_CACHE (1)
|
||||
#endif
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
// Enables memory write caching for file descriptors in hydrogen
|
||||
#ifndef SPIFFS_CACHE_WR
|
||||
#ifndef CONFIG_SPIFFS_CACHE_WR
|
||||
#define SPIFFS_CACHE_WR (0)
|
||||
#else
|
||||
#define SPIFFS_CACHE_WR (1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Enable/disable statistics on caching. Debug/test purpose only.
|
||||
#ifndef SPIFFS_CACHE_STATS
|
||||
#ifdef CONFIG_SPIFFS_CACHE_STATS
|
||||
#define SPIFFS_CACHE_STATS (1)
|
||||
#else
|
||||
#define SPIFFS_CACHE_STATS (0)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Always check header of each accessed page to ensure consistent state.
|
||||
// If enabled it will increase number of reads, will increase flash.
|
||||
#ifndef SPIFFS_PAGE_CHECK
|
||||
#ifndef CONFIG_SPIFFS_PAGE_CHECK
|
||||
#define SPIFFS_PAGE_CHECK (0)
|
||||
#else
|
||||
#define SPIFFS_PAGE_CHECK (1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Define maximum number of gc runs to perform to reach desired free pages.
|
||||
#ifdef CONFIG_SPIFFS_GC_MAX_RUNS
|
||||
#define SPIFFS_GC_MAX_RUNS CONFIG_SPIFFS_GC_MAX_RUNS
|
||||
#else
|
||||
#define SPIFFS_GC_MAX_RUNS 10
|
||||
#endif
|
||||
|
||||
// Enable/disable statistics on gc. Debug/test purpose only.
|
||||
#ifndef SPIFFS_GC_STATS
|
||||
#ifdef CONFIG_SPIFFS_GC_STATS
|
||||
#define SPIFFS_GC_STATS (1)
|
||||
#else
|
||||
#define SPIFFS_GC_STATS (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Garbage collecting examines all pages in a block which and sums up
|
||||
// to a block score. Deleted pages normally gives positive score and
|
||||
// used pages normally gives a negative score (as these must be moved).
|
||||
// To have a fair wear-leveling, the erase age is also included in score,
|
||||
// whose factor normally is the most positive.
|
||||
// The larger the score, the more likely it is that the block will
|
||||
// picked for garbage collection.
|
||||
|
||||
// Garbage collecting heuristics - weight used for deleted pages.
|
||||
#define SPIFFS_GC_HEUR_W_DELET (5)
|
||||
// Garbage collecting heuristics - weight used for used pages.
|
||||
#define SPIFFS_GC_HEUR_W_USED (-1)
|
||||
// Garbage collecting heuristics - weight used for time between
|
||||
// last erased and erase of this block.
|
||||
#define SPIFFS_GC_HEUR_W_ERASE_AGE (50)
|
||||
|
||||
// Object name maximum length. Note that this length include the
|
||||
// zero-termination character, meaning maximum string of characters
|
||||
// can at most be SPIFFS_OBJ_NAME_LEN - 1.
|
||||
#ifdef CONFIG_SPIFFS_OBJ_NAME_LEN
|
||||
#define SPIFFS_OBJ_NAME_LEN (CONFIG_SPIFFS_OBJ_NAME_LEN)
|
||||
#else
|
||||
#define SPIFFS_OBJ_NAME_LEN (32)
|
||||
#endif
|
||||
// Maximum length of the metadata associated with an object.
|
||||
// Setting to non-zero value enables metadata-related API but also
|
||||
// changes the on-disk format, so the change is not backward-compatible.
|
||||
//
|
||||
// Do note: the meta length must never exceed
|
||||
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64)
|
||||
//
|
||||
// This is derived from following:
|
||||
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
|
||||
// spiffs_object_ix_header fields + at least some LUT entries)
|
||||
#ifdef CONFIG_SPIFFS_META_LENGTH
|
||||
#define SPIFFS_OBJ_META_LEN (CONFIG_SPIFFS_META_LENGTH)
|
||||
#else
|
||||
#define SPIFFS_OBJ_META_LEN (0)
|
||||
#endif
|
||||
|
||||
// Size of buffer allocated on stack used when copying data.
|
||||
// Lower value generates more read/writes. No meaning having it bigger
|
||||
// than logical page size.
|
||||
#ifndef SPIFFS_COPY_BUFFER_STACK
|
||||
#define SPIFFS_COPY_BUFFER_STACK (256)
|
||||
#endif
|
||||
|
||||
// Enable this to have an identifiable spiffs filesystem. This will look for
|
||||
// a magic in all sectors to determine if this is a valid spiffs system or
|
||||
// not on mount point. If not, SPIFFS_format must be called prior to mounting
|
||||
// again.
|
||||
#ifndef SPIFFS_USE_MAGIC
|
||||
#ifndef CONFIG_SPIFFS_USE_MAGIC
|
||||
#define SPIFFS_USE_MAGIC (0)
|
||||
#else
|
||||
#define SPIFFS_USE_MAGIC (1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if SPIFFS_USE_MAGIC
|
||||
// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is
|
||||
// enabled, the magic will also be dependent on the length of the filesystem.
|
||||
// For example, a filesystem configured and formatted for 4 megabytes will not
|
||||
// be accepted for mounting with a configuration defining the filesystem as 2
|
||||
// megabytes.
|
||||
#ifndef SPIFFS_USE_MAGIC_LENGTH
|
||||
#ifndef CONFIG_SPIFFS_USE_MAGIC_LENGTH
|
||||
#define SPIFFS_USE_MAGIC_LENGTH (0)
|
||||
#else
|
||||
#define SPIFFS_USE_MAGIC_LENGTH (1)
|
||||
#endif
|
||||
#endif
|
||||
#endif // SPIFFS_USE_MAGIC
|
||||
|
||||
// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
|
||||
// These should be defined on a multithreaded system
|
||||
|
||||
// define this to enter a mutex if you're running on a multithreaded system
|
||||
#define SPIFFS_LOCK(fs)
|
||||
// define this to exit a mutex if you're running on a multithreaded system
|
||||
#define SPIFFS_UNLOCK(fs)
|
||||
|
||||
// Enable if only one spiffs instance with constant configuration will exist
|
||||
// on the target. This will reduce calculations, flash and memory accesses.
|
||||
// Parts of configuration must be defined below instead of at time of mount.
|
||||
#define SPIFFS_SINGLETON 0
|
||||
|
||||
// Enable this if your target needs aligned data for index tables
|
||||
#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
|
||||
#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 0
|
||||
#endif
|
||||
|
||||
// Enable this if you want the HAL callbacks to be called with the spiffs struct
|
||||
#ifndef SPIFFS_HAL_CALLBACK_EXTRA
|
||||
#define SPIFFS_HAL_CALLBACK_EXTRA 1
|
||||
#endif
|
||||
|
||||
// Enable this if you want to add an integer offset to all file handles
|
||||
// (spiffs_file). This is useful if running multiple instances of spiffs on
|
||||
// same target, in order to recognise to what spiffs instance a file handle
|
||||
// belongs.
|
||||
// NB: This adds config field fh_ix_offset in the configuration struct when
|
||||
// mounting, which must be defined.
|
||||
#define SPIFFS_FILEHDL_OFFSET 0
|
||||
|
||||
// Enable this to compile a read only version of spiffs.
|
||||
// This will reduce binary size of spiffs. All code comprising modification
|
||||
// of the file system will not be compiled. Some config will be ignored.
|
||||
// HAL functions for erasing and writing to spi-flash may be null. Cache
|
||||
// can be disabled for even further binary size reduction (and ram savings).
|
||||
// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL.
|
||||
// If the file system cannot be mounted due to aborted erase operation and
|
||||
// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be
|
||||
// returned.
|
||||
// Might be useful for e.g. bootloaders and such.
|
||||
#ifndef SPIFFS_READ_ONLY
|
||||
#define SPIFFS_READ_ONLY 0
|
||||
#endif
|
||||
|
||||
// Enable this to add a temporal file cache using the fd buffer.
|
||||
// The effects of the cache is that SPIFFS_open will find the file faster in
|
||||
// certain cases. It will make it a lot easier for spiffs to find files
|
||||
// opened frequently, reducing number of readings from the spi flash for
|
||||
// finding those files.
|
||||
// This will grow each fd by 6 bytes. If your files are opened in patterns
|
||||
// with a degree of temporal locality, the system is optimized.
|
||||
// Examples can be letting spiffs serve web content, where one file is the css.
|
||||
// The css is accessed for each html file that is opened, meaning it is
|
||||
// accessed almost every second time a file is opened. Another example could be
|
||||
// a log file that is often opened, written, and closed.
|
||||
// The size of the cache is number of given file descriptors, as it piggybacks
|
||||
// on the fd update mechanism. The cache lives in the closed file descriptors.
|
||||
// When closed, the fd know the whereabouts of the file. Instead of forgetting
|
||||
// this, the temporal cache will keep handling updates to that file even if the
|
||||
// fd is closed. If the file is opened again, the location of the file is found
|
||||
// directly. If all available descriptors become opened, all cache memory is
|
||||
// lost.
|
||||
#ifndef SPIFFS_TEMPORAL_FD_CACHE
|
||||
#define SPIFFS_TEMPORAL_FD_CACHE 1
|
||||
#endif
|
||||
|
||||
// Temporal file cache hit score. Each time a file is opened, all cached files
|
||||
// will lose one point. If the opened file is found in cache, that entry will
|
||||
// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this
|
||||
// value for the specific access patterns of the application. However, it must
|
||||
// be between 1 (no gain for hitting a cached entry often) and 255.
|
||||
#ifndef SPIFFS_TEMPORAL_CACHE_HIT_SCORE
|
||||
#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 4
|
||||
#endif
|
||||
|
||||
// Enable to be able to map object indices to memory.
|
||||
// This allows for faster and more deterministic reading if cases of reading
|
||||
// large files and when changing file offset by seeking around a lot.
|
||||
// When mapping a file's index, the file system will be scanned for index pages
|
||||
// and the info will be put in memory provided by user. When reading, the
|
||||
// memory map can be looked up instead of searching for index pages on the
|
||||
// medium. This way, user can trade memory against performance.
|
||||
// Whole, parts of, or future parts not being written yet can be mapped. The
|
||||
// memory array will be owned by spiffs and updated accordingly during garbage
|
||||
// collecting or when modifying the indices. The latter is invoked by when the
|
||||
// file is modified in some way. The index buffer is tied to the file
|
||||
// descriptor.
|
||||
#ifndef SPIFFS_IX_MAP
|
||||
#define SPIFFS_IX_MAP 1
|
||||
#endif
|
||||
|
||||
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
|
||||
// in the api. This function will visualize all filesystem using given printf
|
||||
// function.
|
||||
#ifndef SPIFFS_TEST_VISUALISATION
|
||||
#ifndef CONFIG_SPIFFS_TEST_VISUALISATION
|
||||
#define SPIFFS_TEST_VISUALISATION 0
|
||||
#else
|
||||
#define SPIFFS_TEST_VISUALISATION 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
#ifndef spiffs_printf
|
||||
#define spiffs_printf(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
// spiffs_printf argument for a free page
|
||||
#define SPIFFS_TEST_VIS_FREE_STR "_"
|
||||
// spiffs_printf argument for a deleted page
|
||||
#define SPIFFS_TEST_VIS_DELE_STR "/"
|
||||
// spiffs_printf argument for an index page for given object id
|
||||
#define SPIFFS_TEST_VIS_INDX_STR(id) "i"
|
||||
// spiffs_printf argument for a data page for given object id
|
||||
#define SPIFFS_TEST_VIS_DATA_STR(id) "d"
|
||||
#endif
|
||||
|
||||
// Types depending on configuration such as the amount of flash bytes
|
||||
// given to spiffs file system in total (spiffs_file_system_size),
|
||||
// the logical block size (log_block_size), and the logical page size
|
||||
// (log_page_size)
|
||||
|
||||
// Block index type. Make sure the size of this type can hold
|
||||
// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size
|
||||
typedef u16_t spiffs_block_ix;
|
||||
// Page index type. Make sure the size of this type can hold
|
||||
// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size
|
||||
typedef u16_t spiffs_page_ix;
|
||||
// Object id type - most significant bit is reserved for index flag. Make sure the
|
||||
// size of this type can hold the highest object id on a full system,
|
||||
// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2
|
||||
typedef u16_t spiffs_obj_id;
|
||||
// Object span index type. Make sure the size of this type can
|
||||
// hold the largest possible span index on the system -
|
||||
// i.e. (spiffs_file_system_size / log_page_size) - 1
|
||||
typedef u16_t spiffs_span_ix;
|
||||
|
||||
#endif /* SPIFFS_CONFIG_H_ */
|
||||
|
|
@ -1,734 +0,0 @@
|
|||
//
|
||||
// main.cpp
|
||||
// make_spiffs
|
||||
//
|
||||
// Created by Ivan Grokhotkov on 13/05/15.
|
||||
// Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
//
|
||||
#define TCLAP_SETBASE_ZERO 1
|
||||
|
||||
#include <iostream>
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdlib>
|
||||
#include "tclap/CmdLine.h"
|
||||
#include "tclap/UnlabeledValueArg.h"
|
||||
|
||||
#if defined (CONFIG_SPIFFS_USE_MTIME) || defined (CONFIG_SPIFFS_USE_DIR)
|
||||
/**
|
||||
* @brief SPIFFS metadata structure
|
||||
*/
|
||||
typedef struct {
|
||||
#ifdef CONFIG_SPIFFS_USE_MTIME
|
||||
s32_t mtime; /*!< file modification time */
|
||||
#endif
|
||||
#ifdef CONFIG_SPIFFS_USE_DIR
|
||||
u8_t type; /*!< file type */
|
||||
#endif
|
||||
} __attribute__((packed, aligned(1))) spiffs_meta_t;
|
||||
#endif
|
||||
|
||||
|
||||
static std::vector<uint8_t> s_flashmem;
|
||||
|
||||
static std::string s_dirName;
|
||||
static std::string s_imageName;
|
||||
static int s_imageSize;
|
||||
static int s_pageSize;
|
||||
static int s_blockSize;
|
||||
|
||||
enum Action { ACTION_NONE, ACTION_PACK, ACTION_UNPACK, ACTION_LIST, ACTION_VISUALIZE };
|
||||
static Action s_action = ACTION_NONE;
|
||||
|
||||
static spiffs s_fs;
|
||||
|
||||
static std::vector<uint8_t> s_spiffsWorkBuf;
|
||||
static std::vector<uint8_t> s_spiffsFds;
|
||||
static std::vector<uint8_t> s_spiffsCache;
|
||||
|
||||
static int s_debugLevel = 0;
|
||||
static bool s_addAllFiles;
|
||||
|
||||
// Unless -a flag is given, these files/directories will not be included into the image
|
||||
static const char* ignored_file_names[] = {
|
||||
".DS_Store",
|
||||
".git",
|
||||
".gitignore",
|
||||
".gitmodules"
|
||||
};
|
||||
|
||||
#if SPIFFS_HAL_CALLBACK_EXTRA
|
||||
static s32_t api_spiffs_read(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst){
|
||||
memcpy(dst, &s_flashmem[0] + addr, size);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
static s32_t api_spiffs_write(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src){
|
||||
memcpy(&s_flashmem[0] + addr, src, size);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
static s32_t api_spiffs_erase(struct spiffs_t *fs, u32_t addr, u32_t size){
|
||||
memset(&s_flashmem[0] + addr, 0xff, size);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
#else
|
||||
static s32_t api_spiffs_read(u32_t addr, u32_t size, u8_t *dst){
|
||||
memcpy(dst, &s_flashmem[0] + addr, size);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
static s32_t api_spiffs_write(u32_t addr, u32_t size, u8_t *src){
|
||||
memcpy(&s_flashmem[0] + addr, src, size);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
static s32_t api_spiffs_erase(u32_t addr, u32_t size){
|
||||
memset(&s_flashmem[0] + addr, 0xff, size);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//implementation
|
||||
|
||||
int spiffsTryMount(){
|
||||
spiffs_config cfg = {0};
|
||||
|
||||
cfg.phys_addr = 0x0000;
|
||||
cfg.phys_size = (u32_t) s_flashmem.size();
|
||||
|
||||
cfg.phys_erase_block = s_blockSize;
|
||||
cfg.log_block_size = s_blockSize;
|
||||
cfg.log_page_size = s_pageSize;
|
||||
|
||||
cfg.hal_read_f = api_spiffs_read;
|
||||
cfg.hal_write_f = api_spiffs_write;
|
||||
cfg.hal_erase_f = api_spiffs_erase;
|
||||
|
||||
const int maxOpenFiles = 4;
|
||||
s_spiffsWorkBuf.resize(s_pageSize * 2);
|
||||
s_spiffsFds.resize(sizeof(spiffs_fd) * maxOpenFiles);
|
||||
s_spiffsCache.resize(sizeof(spiffs_cache) + maxOpenFiles * (sizeof(spiffs_cache_page) + cfg.log_page_size));
|
||||
|
||||
return SPIFFS_mount(&s_fs, &cfg,
|
||||
&s_spiffsWorkBuf[0],
|
||||
&s_spiffsFds[0], s_spiffsFds.size(),
|
||||
&s_spiffsCache[0], s_spiffsCache.size(),
|
||||
NULL);
|
||||
}
|
||||
|
||||
bool spiffsMount(){
|
||||
if(SPIFFS_mounted(&s_fs))
|
||||
return true;
|
||||
int res = spiffsTryMount();
|
||||
return (res == SPIFFS_OK);
|
||||
}
|
||||
|
||||
bool spiffsFormat(){
|
||||
spiffsMount();
|
||||
SPIFFS_unmount(&s_fs);
|
||||
int formated = SPIFFS_format(&s_fs);
|
||||
if(formated != SPIFFS_OK)
|
||||
return false;
|
||||
return (spiffsTryMount() == SPIFFS_OK);
|
||||
}
|
||||
|
||||
void spiffsUnmount(){
|
||||
if(SPIFFS_mounted(&s_fs))
|
||||
SPIFFS_unmount(&s_fs);
|
||||
}
|
||||
|
||||
static void spiffs_update_meta(spiffs *fs, spiffs_file fd, u8_t type)
|
||||
{
|
||||
#if defined (CONFIG_SPIFFS_USE_MTIME) || defined (CONFIG_SPIFFS_USE_DIR)
|
||||
spiffs_meta_t meta;
|
||||
#ifdef CONFIG_SPIFFS_USE_MTIME
|
||||
meta.mtime = time(NULL);
|
||||
#endif //CONFIG_SPIFFS_USE_MTIME
|
||||
|
||||
#ifdef CONFIG_SPIFFS_USE_DIR
|
||||
// Add file type (directory or regular file) to the last byte of metadata
|
||||
meta.type = type;
|
||||
#endif
|
||||
int ret = SPIFFS_fupdate_meta(fs, fd, (uint8_t *)&meta);
|
||||
if (ret != SPIFFS_OK) {
|
||||
std::cerr << "error: Failed to update metadata: " << ret << std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
static time_t spiffs_get_mtime(const spiffs_stat* s)
|
||||
{
|
||||
time_t t = 0;
|
||||
#ifdef CONFIG_SPIFFS_USE_MTIME
|
||||
spiffs_meta_t meta;
|
||||
memcpy(&meta, s->meta, sizeof(meta));
|
||||
t = meta.mtime;
|
||||
#endif
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
static int get_spiffs_stat(const char * path, struct stat * st)
|
||||
{
|
||||
spiffs_stat s;
|
||||
off_t res = SPIFFS_stat(&s_fs, path, &s);
|
||||
if (res < 0) {
|
||||
SPIFFS_clearerr(&s_fs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
st->st_size = s.size;
|
||||
#ifdef CONFIG_SPIFFS_USE_DIR
|
||||
spiffs_meta_t *meta = (spiffs_meta_t *)&s.meta;
|
||||
if (meta->type == SPIFFS_TYPE_DIR) st->st_mode = S_IFDIR;
|
||||
else st->st_mode = S_IFREG;
|
||||
#else
|
||||
st->st_mode = (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG;
|
||||
#endif
|
||||
st->st_mtime = spiffs_get_mtime(&s);
|
||||
st->st_atime = 0;
|
||||
st->st_ctime = 0;
|
||||
return res;
|
||||
}
|
||||
*/
|
||||
|
||||
int addFile(char* name, const char* path) {
|
||||
FILE* src = fopen(path, "rb");
|
||||
if (!src) {
|
||||
std::cerr << "error: failed to open " << path << " for reading" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
spiffs_file dst = SPIFFS_open(&s_fs, name, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
|
||||
spiffs_update_meta(&s_fs, dst, SPIFFS_TYPE_FILE);
|
||||
|
||||
// read file size
|
||||
fseek(src, 0, SEEK_END);
|
||||
size_t size = ftell(src);
|
||||
fseek(src, 0, SEEK_SET);
|
||||
|
||||
if (s_debugLevel > 0) {
|
||||
std::cout << "file size: " << size << std::endl;
|
||||
}
|
||||
|
||||
size_t left = size;
|
||||
uint8_t data_byte;
|
||||
while (left > 0){
|
||||
if (1 != fread(&data_byte, 1, 1, src)) {
|
||||
std::cerr << "fread error!" << std::endl;
|
||||
|
||||
fclose(src);
|
||||
SPIFFS_close(&s_fs, dst);
|
||||
return 1;
|
||||
}
|
||||
int res = SPIFFS_write(&s_fs, dst, &data_byte, 1);
|
||||
if (res < 0) {
|
||||
std::cerr << "SPIFFS_write error(" << s_fs.err_code << "): ";
|
||||
|
||||
if (s_fs.err_code == SPIFFS_ERR_FULL) {
|
||||
std::cerr << "File system is full." << std::endl;
|
||||
} else {
|
||||
std::cerr << "unknown";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
if (s_debugLevel > 0) {
|
||||
std::cout << "data left: " << left << std::endl;
|
||||
}
|
||||
|
||||
fclose(src);
|
||||
SPIFFS_close(&s_fs, dst);
|
||||
return 1;
|
||||
}
|
||||
left -= 1;
|
||||
}
|
||||
|
||||
SPIFFS_close(&s_fs, dst);
|
||||
fclose(src);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int addFiles(const char* dirname, const char* subPath) {
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
bool error = false;
|
||||
std::string dirPath = dirname;
|
||||
dirPath += subPath;
|
||||
|
||||
// Open directory
|
||||
if ((dir = opendir (dirPath.c_str())) != NULL) {
|
||||
|
||||
// Read files from directory.
|
||||
while ((ent = readdir (dir)) != NULL) {
|
||||
|
||||
// Ignore dir itself.
|
||||
if ((strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!s_addAllFiles) {
|
||||
bool skip = false;
|
||||
size_t ignored_file_names_count = sizeof(ignored_file_names) / sizeof(ignored_file_names[0]);
|
||||
for (size_t i = 0; i < ignored_file_names_count; ++i) {
|
||||
if (strcmp(ent->d_name, ignored_file_names[i]) == 0) {
|
||||
std::cerr << "skipping " << ent->d_name << std::endl;
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::string fullpath = dirPath;
|
||||
fullpath += ent->d_name;
|
||||
struct stat path_stat;
|
||||
stat (fullpath.c_str(), &path_stat);
|
||||
|
||||
if (!S_ISREG(path_stat.st_mode)) {
|
||||
// Check if path is a directory.
|
||||
if (S_ISDIR(path_stat.st_mode)) {
|
||||
#ifdef CONFIG_SPIFFS_USE_DIR
|
||||
std::string dirpath = subPath;
|
||||
dirpath += ent->d_name;
|
||||
std::cout << dirpath << " [D]" << std::endl;
|
||||
spiffs_file dst = SPIFFS_open(&s_fs, (char*)dirpath.c_str(), SPIFFS_CREAT | SPIFFS_WRONLY, 0);
|
||||
if (dst < 0) {
|
||||
std::cerr << "error adding directory (open)!" << std::endl;
|
||||
error = true;
|
||||
if (s_debugLevel > 0) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
spiffs_update_meta(&s_fs, dst, SPIFFS_TYPE_DIR);
|
||||
if (SPIFFS_close(&s_fs, dst) < 0) {
|
||||
std::cerr << "error adding directory (close)!" << std::endl;
|
||||
error = true;
|
||||
if (s_debugLevel > 0) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
// Prepare new sub path.
|
||||
std::string newSubPath = subPath;
|
||||
newSubPath += ent->d_name;
|
||||
newSubPath += "/";
|
||||
|
||||
if (addFiles(dirname, newSubPath.c_str()) != 0)
|
||||
{
|
||||
std::cerr << "Error for adding content from " << ent->d_name << "!" << std::endl;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "skipping " << ent->d_name << std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Filepath with dirname as root folder.
|
||||
std::string filepath = subPath;
|
||||
filepath += ent->d_name;
|
||||
std::cout << filepath << std::endl;
|
||||
|
||||
// Add File to image.
|
||||
if (addFile((char*)filepath.c_str(), fullpath.c_str()) != 0) {
|
||||
std::cerr << "error adding file!" << std::endl;
|
||||
error = true;
|
||||
if (s_debugLevel > 0) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} // end while
|
||||
closedir (dir);
|
||||
} else {
|
||||
std::cerr << "warning: can't read source directory" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (error) ? 1 : 0;
|
||||
}
|
||||
|
||||
void listFiles() {
|
||||
spiffs_DIR dir;
|
||||
spiffs_dirent ent;
|
||||
//struct stat sb = {0};
|
||||
|
||||
SPIFFS_opendir(&s_fs, 0, &dir);
|
||||
spiffs_dirent* it;
|
||||
while (true) {
|
||||
it = SPIFFS_readdir(&dir, &ent);
|
||||
if (!it)
|
||||
break;
|
||||
|
||||
//get_spiffs_stat((const char *)it->name, &sb);
|
||||
//std::cout << sb.st_mode << '\t' << it->size << '\t' << it->name << std::endl;
|
||||
std::cout << it->size << '\t' << it->name << std::endl;
|
||||
}
|
||||
SPIFFS_closedir(&dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if directory exists.
|
||||
* @param path Directory path.
|
||||
* @return True if exists otherwise false.
|
||||
*
|
||||
* @author Pascal Gollor (http://www.pgollor.de/cms/)
|
||||
*/
|
||||
bool dirExists(const char* path) {
|
||||
DIR *d = opendir(path);
|
||||
|
||||
if (d) {
|
||||
closedir(d);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create directory if it not exists.
|
||||
* @param path Directory path.
|
||||
* @return True or false.
|
||||
*
|
||||
* @author Pascal Gollor (http://www.pgollor.de/cms/)
|
||||
*/
|
||||
bool dirCreate(const char* path) {
|
||||
// Check if directory also exists.
|
||||
if (dirExists(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// platform stuff...
|
||||
#if defined(_WIN32)
|
||||
if (_mkdir(path) != 0) {
|
||||
#else
|
||||
if (mkdir(path, S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH) != 0) {
|
||||
#endif
|
||||
std::cerr << "Can not create directory!!!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpack file from file system.
|
||||
* @param spiffsFile SPIFFS dir entry pointer.
|
||||
* @param destPath Destination file path path.
|
||||
* @return True or false.
|
||||
*
|
||||
* @author Pascal Gollor (http://www.pgollor.de/cms/)
|
||||
*/
|
||||
bool unpackFile(spiffs_dirent *spiffsFile, const char *destPath) {
|
||||
u8_t buffer[spiffsFile->size];
|
||||
std::string filename = (const char*)(spiffsFile->name);
|
||||
|
||||
// Open file from spiffs file system.
|
||||
spiffs_file src = SPIFFS_open(&s_fs, (char *)(filename.c_str()), SPIFFS_RDONLY, 0);
|
||||
|
||||
// read content into buffer
|
||||
SPIFFS_read(&s_fs, src, buffer, spiffsFile->size);
|
||||
|
||||
// Close spiffs file.
|
||||
SPIFFS_close(&s_fs, src);
|
||||
|
||||
// Open file.
|
||||
FILE* dst = fopen(destPath, "wb");
|
||||
|
||||
// Write content into file.
|
||||
fwrite(buffer, sizeof(u8_t), sizeof(buffer), dst);
|
||||
|
||||
// Close file.
|
||||
fclose(dst);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpack files from file system.
|
||||
* @param sDest Directory path as std::string.
|
||||
* @return True or false.
|
||||
*
|
||||
* @author Pascal Gollor (http://www.pgollor.de/cms/)
|
||||
*
|
||||
* todo: Do unpack stuff for directories.
|
||||
*/
|
||||
bool unpackFiles(std::string sDest) {
|
||||
spiffs_DIR dir;
|
||||
spiffs_dirent ent;
|
||||
|
||||
// Add "./" to path if is not given.
|
||||
if (sDest.find("./") == std::string::npos && sDest.find("/") == std::string::npos) {
|
||||
sDest = "./" + sDest;
|
||||
}
|
||||
|
||||
// Check if directory exists. If it does not then try to create it with permissions 755.
|
||||
if (! dirExists(sDest.c_str())) {
|
||||
std::cout << "Directory " << sDest << " does not exists. Try to create it." << std::endl;
|
||||
|
||||
// Try to create directory.
|
||||
if (! dirCreate(sDest.c_str())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Open directory.
|
||||
SPIFFS_opendir(&s_fs, 0, &dir);
|
||||
|
||||
// Read content from directory.
|
||||
spiffs_dirent* it = SPIFFS_readdir(&dir, &ent);
|
||||
while (it) {
|
||||
// Check if content is a file.
|
||||
if ((int)(it->type) == 1) {
|
||||
std::string name = (const char*)(it->name);
|
||||
std::string sDestFilePath = sDest + name;
|
||||
size_t pos = name.find_first_of("/", 1);
|
||||
|
||||
// If file is in sub directories?
|
||||
while (pos != std::string::npos) {
|
||||
// Subdir path.
|
||||
std::string path = sDest;
|
||||
path += name.substr(0, pos);
|
||||
|
||||
// Create subddir if subdir not exists.
|
||||
if (!dirExists(path.c_str())) {
|
||||
if (!dirCreate(path.c_str())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pos = name.find_first_of("/", pos + 1);
|
||||
}
|
||||
|
||||
// Unpack file to destination directory.
|
||||
if (! unpackFile(it, sDestFilePath.c_str()) ) {
|
||||
std::cout << "Can not unpack " << it->name << "!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Output stuff.
|
||||
std::cout
|
||||
<< it->name
|
||||
<< '\t'
|
||||
<< " > " << sDestFilePath
|
||||
<< '\t'
|
||||
<< "size: " << it->size << " Bytes"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// Get next file handle.
|
||||
it = SPIFFS_readdir(&dir, &ent);
|
||||
} // end while
|
||||
|
||||
// Close directory.
|
||||
SPIFFS_closedir(&dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Actions
|
||||
|
||||
int actionPack() {
|
||||
if (!dirExists(s_dirName.c_str())) {
|
||||
std::cerr << "error: can't read source directory" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
s_flashmem.resize(s_imageSize, 0xff);
|
||||
|
||||
FILE* fdres = fopen(s_imageName.c_str(), "wb");
|
||||
if (!fdres) {
|
||||
std::cerr << "error: failed to open image file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
spiffsFormat();
|
||||
int result = addFiles(s_dirName.c_str(), "/");
|
||||
//listFiles();
|
||||
spiffsUnmount();
|
||||
|
||||
fwrite(&s_flashmem[0], 4, s_flashmem.size()/4, fdres);
|
||||
fclose(fdres);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpack action.
|
||||
* @return 0 success, 1 error
|
||||
*
|
||||
* @author Pascal Gollor (http://www.pgollor.de/cms/)
|
||||
*/
|
||||
int actionUnpack(void) {
|
||||
int ret = 0;
|
||||
s_flashmem.resize(s_imageSize, 0xff);
|
||||
|
||||
// open spiffs image
|
||||
FILE* fdsrc = fopen(s_imageName.c_str(), "rb");
|
||||
if (!fdsrc) {
|
||||
std::cerr << "error: failed to open image file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// read content into s_flashmem
|
||||
ret = fread(&s_flashmem[0], 4, s_flashmem.size()/4, fdsrc);
|
||||
|
||||
// close fiel handle
|
||||
fclose(fdsrc);
|
||||
|
||||
// mount file system
|
||||
spiffsMount();
|
||||
|
||||
// unpack files
|
||||
if (! unpackFiles(s_dirName)) {
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
// unmount file system
|
||||
spiffsUnmount();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int actionList() {
|
||||
s_flashmem.resize(s_imageSize, 0xff);
|
||||
|
||||
FILE* fdsrc = fopen(s_imageName.c_str(), "rb");
|
||||
if (!fdsrc) {
|
||||
std::cerr << "error: failed to open image file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ret = fread(&s_flashmem[0], 4, s_flashmem.size()/4, fdsrc);
|
||||
if (ret < 0) {
|
||||
std::cerr << "error: failed to read from image file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
fclose(fdsrc);
|
||||
spiffsMount();
|
||||
listFiles();
|
||||
spiffsUnmount();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int actionVisualize() {
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
s_flashmem.resize(s_imageSize, 0xff);
|
||||
|
||||
FILE* fdsrc = fopen(s_imageName.c_str(), "rb");
|
||||
if (!fdsrc) {
|
||||
std::cerr << "error: failed to open image file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
fread(&s_flashmem[0], 4, s_flashmem.size()/4, fdsrc);
|
||||
fclose(fdsrc);
|
||||
|
||||
spiffsMount();
|
||||
SPIFFS_vis(&s_fs);
|
||||
uint32_t total, used;
|
||||
SPIFFS_info(&s_fs, &total, &used);
|
||||
std::cout << "total: " << total << std::endl << "used: " << used << std::endl;
|
||||
spiffsUnmount();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void processArgs(int argc, const char** argv) {
|
||||
TCLAP::CmdLine cmd("", ' ', VERSION);
|
||||
TCLAP::ValueArg<std::string> packArg( "c", "create", "create spiffs image from a directory", true, "", "pack_dir");
|
||||
TCLAP::ValueArg<std::string> unpackArg( "u", "unpack", "unpack spiffs image to a directory", true, "", "dest_dir");
|
||||
TCLAP::SwitchArg listArg( "l", "list", "list files in spiffs image", false);
|
||||
TCLAP::SwitchArg visualizeArg( "i", "visualize", "visualize spiffs image", false);
|
||||
TCLAP::UnlabeledValueArg<std::string> outNameArg( "image_file", "spiffs image file", true, "", "image_file" );
|
||||
TCLAP::ValueArg<int> imageSizeArg( "s", "size", "fs image size, in bytes", false, 0x10000, "number" );
|
||||
TCLAP::ValueArg<int> pageSizeArg( "p", "page", "fs page size, in bytes", false, 256, "number" );
|
||||
TCLAP::ValueArg<int> blockSizeArg( "b", "block", "fs block size, in bytes", false, 4096, "number" );
|
||||
TCLAP::SwitchArg addAllFilesArg( "a", "all-files", "when creating an image, include files which are normally ignored; currently only applies to '.DS_Store' files and '.git' directories", false);
|
||||
TCLAP::ValueArg<int> debugArg( "d", "debug", "Debug level. 0 means no debug output.", false, 0, "0-5" );
|
||||
|
||||
cmd.add( imageSizeArg );
|
||||
cmd.add( pageSizeArg );
|
||||
cmd.add( blockSizeArg );
|
||||
cmd.add( addAllFilesArg );
|
||||
cmd.add( debugArg );
|
||||
std::vector<TCLAP::Arg*> args = {&packArg, &unpackArg, &listArg, &visualizeArg};
|
||||
cmd.xorAdd( args );
|
||||
cmd.add( outNameArg );
|
||||
cmd.parse( argc, argv );
|
||||
|
||||
if (debugArg.getValue() > 0) {
|
||||
std::cout << "Debug output enabled" << std::endl;
|
||||
s_debugLevel = debugArg.getValue();
|
||||
}
|
||||
|
||||
if (packArg.isSet()) {
|
||||
s_dirName = packArg.getValue();
|
||||
s_action = ACTION_PACK;
|
||||
} else if (unpackArg.isSet()) {
|
||||
s_dirName = unpackArg.getValue();
|
||||
s_action = ACTION_UNPACK;
|
||||
} else if (listArg.isSet()) {
|
||||
s_action = ACTION_LIST;
|
||||
} else if (visualizeArg.isSet()) {
|
||||
s_action = ACTION_VISUALIZE;
|
||||
}
|
||||
|
||||
s_imageName = outNameArg.getValue();
|
||||
s_imageSize = imageSizeArg.getValue();
|
||||
s_pageSize = pageSizeArg.getValue();
|
||||
s_blockSize = blockSizeArg.getValue();
|
||||
s_addAllFiles = addAllFilesArg.isSet();
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
|
||||
try {
|
||||
processArgs(argc, argv);
|
||||
} catch(...) {
|
||||
std::cerr << "Invalid arguments" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (s_action) {
|
||||
case ACTION_PACK:
|
||||
return actionPack();
|
||||
break;
|
||||
case ACTION_UNPACK:
|
||||
return actionUnpack();
|
||||
break;
|
||||
case ACTION_LIST:
|
||||
return actionList();
|
||||
break;
|
||||
case ACTION_VISUALIZE:
|
||||
return actionVisualize();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
language: c
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
|
||||
before_script:
|
||||
|
||||
script: make all && make clean && make test && make build-all && make clean test FLAGS=-DSPIFFS_OBJ_META_LEN=8
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
# Fuzzing SPIFFS
|
||||
|
||||
The SPIFFS test suite includes a test program designed for fuzzing with
|
||||
[AFL](http://lcamtuf.coredump.cx/afl/). This automatically exercises the
|
||||
SPIFFS API and verifies that the file system does not crash or interact incorrectly
|
||||
with the flash chip.
|
||||
|
||||
There are two steps to fuzzing. The first is to build the test suite with
|
||||
the AFL version of gcc. The CC variable should point to your copy of afl-gcc.
|
||||
|
||||
```
|
||||
make clean test CC=/usr/local/bin/afl-gcc
|
||||
```
|
||||
|
||||
There is a new test `afl_test` that reads from stdin a list of commands
|
||||
and arguments. These are interpreted and executed on the API. The `afltests`
|
||||
directory contains a number of test cases that can be fed to the `afl_test` test.
|
||||
|
||||
|
||||
The second is to run this test suite under afl as follows (where findings is
|
||||
the output directory):
|
||||
|
||||
```
|
||||
afl-fuzz -i afltests -o findings ./build/linux_spiffs_test -f afl_test
|
||||
```
|
||||
|
||||
This run will take hours (or days) and will (hopefully) not find any crashes.
|
||||
If a crash (or hang) is found, then the input file that caused the crash is
|
||||
saved. This allows the specific test case to be debugged.
|
||||
|
||||
## Reducing the size of the file
|
||||
|
||||
AFL comes with `afl-tmin` which can reduce the size of the test input file to
|
||||
make it easier to debug.
|
||||
|
||||
```
|
||||
afl-tmin -i findings/crashes/<somefile> -o smalltest -- build/linux_spiffs_test -f afl_test
|
||||
```
|
||||
|
||||
This will write a short version of the testcase file to `smalltest`. This can then be
|
||||
fed into the test program for debugging:
|
||||
|
||||
```
|
||||
build/linux_spiffs_test -f afl_test < smalltest
|
||||
```
|
||||
|
||||
This should still crash, but allows it to be run under a debugger.
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976<at>gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
# SPIFFS (SPI Flash File System)
|
||||
**V0.3.7**
|
||||
|
||||
[](https://travis-ci.org/pellepl/spiffs)
|
||||
|
||||
Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976 at gmail.com)
|
||||
|
||||
For legal stuff, see [LICENSE](https://github.com/pellepl/spiffs/blob/master/LICENSE). Basically, you may do whatever you want with the source. Use, modify, sell, print it out, roll it and smoke it - as long as I won't be held responsible.
|
||||
|
||||
Love to hear feedback though!
|
||||
|
||||
|
||||
## INTRODUCTION
|
||||
|
||||
Spiffs is a file system intended for SPI NOR flash devices on embedded targets.
|
||||
|
||||
Spiffs is designed with following characteristics in mind:
|
||||
- Small (embedded) targets, sparse RAM without heap
|
||||
- Only big areas of data (blocks) can be erased
|
||||
- An erase will reset all bits in block to ones
|
||||
- Writing pulls one to zeroes
|
||||
- Zeroes can only be pulled to ones by erase
|
||||
- Wear leveling
|
||||
|
||||
|
||||
## BUILDING
|
||||
|
||||
`mkdir build; make`
|
||||
|
||||
Otherwise, configure the `builddir` variable towards the top of `makefile` as something opposed to the default `build`. Sanity check on the host via `make test` and refer to `.travis.yml` for the official in-depth testing procedure. See the wiki for [integrating](https://github.com/pellepl/spiffs/wiki/Integrate-spiffs) spiffs into projects and [spiffsimg](https://github.com/nodemcu/nodemcu-firmware/tree/master/tools/spiffsimg) from [nodemcu](https://github.com/nodemcu) is a good example on the subject.
|
||||
|
||||
|
||||
## FEATURES
|
||||
|
||||
What spiffs does:
|
||||
- Specifically designed for low ram usage
|
||||
- Uses statically sized ram buffers, independent of number of files
|
||||
- Posix-like api: open, close, read, write, seek, stat, etc
|
||||
- It can run on any NOR flash, not only SPI flash - theoretically also on embedded flash of a microprocessor
|
||||
- Multiple spiffs configurations can run on same target - and even on same SPI flash device
|
||||
- Implements static wear leveling
|
||||
- Built in file system consistency checks
|
||||
- Highly configurable
|
||||
|
||||
What spiffs does not:
|
||||
- Presently, spiffs does not support directories. It produces a flat structure. Creating a file with path *tmp/myfile.txt* will create a file called *tmp/myfile.txt* instead of a *myfile.txt* under directory *tmp*.
|
||||
- It is not a realtime stack. One write operation might last much longer than another.
|
||||
- Poor scalability. Spiffs is intended for small memory devices - the normal sizes for SPI flashes. Going beyond ~128Mbyte is probably a bad idea. This is a side effect of the design goal to use as little ram as possible.
|
||||
- Presently, it does not detect or handle bad blocks.
|
||||
- One configuration, one binary. There's no generic spiffs binary that handles all types of configurations.
|
||||
|
||||
|
||||
## MORE INFO
|
||||
|
||||
See the [wiki](https://github.com/pellepl/spiffs/wiki) for [configuring](https://github.com/pellepl/spiffs/wiki/Configure-spiffs), [integrating](https://github.com/pellepl/spiffs/wiki/Integrate-spiffs), [using](https://github.com/pellepl/spiffs/wiki/Using-spiffs), and [optimizing](https://github.com/pellepl/spiffs/wiki/Performance-and-Optimizing) spiffs.
|
||||
|
||||
For design, see [docs/TECH_SPEC](https://github.com/pellepl/spiffs/blob/master/docs/TECH_SPEC).
|
||||
|
||||
For a generic spi flash driver, see [this](https://github.com/pellepl/spiflash_driver).
|
||||
|
||||
## HISTORY
|
||||
|
||||
### 0.3.7
|
||||
- fixed prevent seeking to negative offsets #158
|
||||
- fixed file descriptor offsets not updated for multiple fds on same file #157
|
||||
- fixed cache page not closed for removed files #156
|
||||
- fixed a lseek bug when seeking exactly to end of a fully indexed first level LUT #148
|
||||
- fixed wear leveling issue #145
|
||||
- fixed attempt to write out of bounds in flash #130,
|
||||
- set file offset when seeking over end #121 (thanks @sensslen)
|
||||
- fixed seeking in virgin files #120 (thanks @sensslen)
|
||||
- Optional file metadata #128 (thanks @cesanta)
|
||||
- AFL testing framework #100 #143 (thanks @pjsg)
|
||||
- Testframe updates
|
||||
|
||||
New API functions:
|
||||
- `SPIFFS_update_meta, SPIFFS_fupdate_meta` - updates metadata for a file
|
||||
|
||||
New config defines:
|
||||
- `SPIFFS_OBJ_META_LEN` - enable possibility to add extra metadata to files
|
||||
|
||||
### 0.3.6
|
||||
- Fix range bug in index memory mapping #98
|
||||
- Add index memory mapping #97
|
||||
- Optimize SPIFFS_read for large files #96
|
||||
- Add temporal cache for opening files #95
|
||||
- More robust gc #93 (thanks @dismirlian)
|
||||
- Fixed a double write of same data in certain cache situations
|
||||
- Fixed an open bug in READ_ONLY builds
|
||||
- File not visible in SPIFFS_readdir #90 (thanks @benpicco-tmp)
|
||||
- Cache load code cleanup #92 (thanks @niclash)
|
||||
- Fixed lock/unlock asymmetry #88 #87 (thanks @JackJefferson, @dpruessner)
|
||||
- Testframe updates
|
||||
|
||||
New API functions:
|
||||
- `SPIFFS_ix_map` - map index meta data to memory for a file
|
||||
- `SPIFFS_ix_unmap` - unmaps index meta data for a file
|
||||
- `SPIFFS_ix_remap` - changes file offset for index metadata map
|
||||
- `SPIFFS_bytes_to_ix_map_entries` - utility, get length of needed vector for given amount of bytes
|
||||
- `SPIFFS_ix_map_entries_to_bytes` - utility, get number of bytes a vector can represent given length
|
||||
|
||||
New config defines:
|
||||
- `SPIFFS_IX_MAP` - enable possibility to map index meta data to memory for reading faster
|
||||
- `SPIFFS_TEMPORAL_FD_CACHE` - enable temporal cache for opening files faster
|
||||
- `SPIFFS_TEMPORAL_CACHE_HIT_SCORE` - for tuning the temporal cache
|
||||
|
||||
### 0.3.5
|
||||
- Fixed a bug in fs check
|
||||
- API returns actual error codes #84) (thanks @Nails)
|
||||
- Fix compiler warnings for non-gcc #83 #81 (thanks @Nails)
|
||||
- Unable to recover from full fs #82 (thanks @rojer)
|
||||
- Define SPIFFS_O_* flags #80
|
||||
- Problem with long filenames #79 (thanks @psjg)
|
||||
- Duplicate file name bug fix #74 (thanks @igrr)
|
||||
- SPIFFS_eof and SPIFFS_tell return wrong value #72 (thanks @ArtemPisarenko)
|
||||
- Bunch of testframe updates #77 #78 #86 (thanks @dpreussner, @psjg a.o)
|
||||
|
||||
### 0.3.4
|
||||
- Added user callback file func.
|
||||
- Fixed a stat bug with obj id.
|
||||
- SPIFFS_probe_fs added
|
||||
- Add possibility to compile a read-only version of spiffs
|
||||
- Make magic dependent on fs length, if needed (see #59 & #66) (thanks @hreintke)
|
||||
- Exposed SPIFFS_open_by_page_function
|
||||
- Zero-size file cannot be seek #57 (thanks @lishen2)
|
||||
- Add tell and eof functions #54 (thanks @raburton)
|
||||
- Make api string params const #53 (thanks @raburton)
|
||||
- Preserve user_data during mount() #51 (thanks @rojer)
|
||||
|
||||
New API functions:
|
||||
- `SPIFFS_set_file_callback_func` - register a callback informing about file events
|
||||
- `SPIFFS_probe_fs` - probe a spi flash trying to figure out size of fs
|
||||
- `SPIFFS_open_by_page` - open a file by page index
|
||||
- `SPIFFS_eof` - checks if end of file is reached
|
||||
- `SPIFFS_tell` - returns current file offset
|
||||
|
||||
New config defines:
|
||||
- `SPIFFS_READ_ONLY`
|
||||
- `SPIFFS_USE_MAGIC_LENGTH`
|
||||
|
||||
### 0.3.3
|
||||
**Might not be compatible with 0.3.2 structures. See issue #40**
|
||||
- Possibility to add integer offset to file handles
|
||||
- Truncate function presumes too few free pages #49
|
||||
- Bug in truncate function #48 (thanks @PawelDefee)
|
||||
- Update spiffs_gc.c - remove unnecessary parameter (thanks @PawelDefee)
|
||||
- Update INTEGRATION docs (thanks @PawelDefee)
|
||||
- Fix pointer truncation in 64-bit platforms (thanks @igrr)
|
||||
- Zero-sized files cannot be read #44 (thanks @rojer)
|
||||
- (More) correct calculation of max_id in obj_lu_find #42 #41 (thanks @lishen2)
|
||||
- Check correct error code in obj_lu_find_free #41 (thanks @lishen2)
|
||||
- Moar comments for SPIFFS_lseek (thanks @igrr)
|
||||
- Fixed padding in spiffs_page_object_ix #40 (thanks @jmattsson @lishen2)
|
||||
- Fixed gc_quick test (thanks @jmattsson)
|
||||
- Add SPIFFS_EXCL flag #36
|
||||
- SPIFFS_close may fail silently if cache is enabled #37
|
||||
- User data in callbacks #34
|
||||
- Ignoring SINGLETON build in cache setup (thanks Luca)
|
||||
- Compilation error fixed #32 (thanks @chotasanjiv)
|
||||
- Align cand_scores (thanks @hefloryd)
|
||||
- Fix build warnings when SPIFFS_CACHE is 0 (thanks @ajaybhargav)
|
||||
|
||||
New config defines:
|
||||
- `SPIFFS_FILEHDL_OFFSET`
|
||||
|
||||
### 0.3.2
|
||||
- Limit cache size if too much cache is given (thanks pgeiem)
|
||||
- New feature - Controlled erase. #23
|
||||
- SPIFFS_rename leaks file descriptors #28 (thanks benpicco)
|
||||
- moved dbg print defines in test framework to params_test.h
|
||||
- lseek should return the resulting offset (thanks hefloryd)
|
||||
- fixed type on dbg ifdefs
|
||||
- silence warning about signed/unsigned comparison when spiffs_obj_id is 32 bit (thanks benpicco)
|
||||
- Possible error in test_spiffs.c #21 (thanks yihcdaso-yeskela)
|
||||
- Cache might writethrough too often #16
|
||||
- even moar testrunner updates
|
||||
- Test framework update and some added tests
|
||||
- Some thoughts for next gen
|
||||
- Test sigsevs when having too many sectors #13 (thanks alonewolfx2)
|
||||
- GC might be suboptimal #11
|
||||
- Fix eternal readdir when objheader at last block, last entry
|
||||
|
||||
New API functions:
|
||||
- `SPIFFS_gc_quick` - call a nonintrusive gc
|
||||
- `SPIFFS_gc` - call a full-scale intrusive gc
|
||||
|
||||
### 0.3.1
|
||||
- Removed two return warnings, was too triggerhappy on release
|
||||
|
||||
### 0.3.0
|
||||
- Added existing namecheck when creating files
|
||||
- Lots of static analysis bugs #6
|
||||
- Added rename func
|
||||
- Fix SPIFFS_read length when reading beyond file size
|
||||
- Added reading beyond file length testcase
|
||||
- Made build a bit more configurable
|
||||
- Changed name in spiffs from "errno" to "err_code" due to conflicts compiling in mingw
|
||||
- Improved GC checks, fixed an append bug, more robust truncate for very special case
|
||||
- GC checks preempts GC, truncate even less picky
|
||||
- Struct alignment needed for some targets, define in spiffs config #10
|
||||
- Spiffs filesystem magic, definable in config
|
||||
|
||||
New config defines:
|
||||
- `SPIFFS_USE_MAGIC` - enable or disable magic check upon mount
|
||||
- `SPIFFS_ALIGNED_OBJECT_INDEX_TABLES` - alignment for certain targets
|
||||
|
||||
New API functions:
|
||||
- `SPIFFS_rename` - rename files
|
||||
- `SPIFFS_clearerr` - clears last errno
|
||||
- `SPIFFS_info` - returns info on used and total bytes in fs
|
||||
- `SPIFFS_format` - formats the filesystem
|
||||
- `SPIFFS_mounted` - checks if filesystem is mounted
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
‰5S-C4
|
||||
d5rh
|
||||
OlWkR#C4
|
||||
d5rh
|
||||
O4W4R4O4W4êC4#d5rh
|
||||
O4d5rh
|
||||
OlWkRh
|
||||
O4Y5rh
|
||||
OlWkR4C44R45ŠË
|
||||
O4W4ê4C4C4
|
||||
O4O4W4R4O4W4êC4#d5rh
|
||||
O4d5rh
|
||||
W4R45rË
|
||||
O4W4ê4#d5rh
|
||||
rz
|
||||
Binary file not shown.
|
|
@ -1,18 +0,0 @@
|
|||
b55
|
||||
O4W4R4C4D4
|
||||
b45
|
||||
d5rh
|
||||
O4W4R4f4C4
|
||||
baaU
|
||||
d5rh
|
||||
OaWaRafaCa
|
||||
cd5rh
|
||||
OaWaRafaCa
|
||||
O4S4W4R4C4
|
||||
d5rh
|
||||
O4W4S4R4C4
|
||||
d5rh
|
||||
O4W4R4S4C4
|
||||
d5rh
|
||||
O4W4R4C4
|
||||
d5rh
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
b55
|
||||
O4
|
||||
W?W?W?W?W?f4
|
||||
W<W=W>W:W;f4
|
||||
C4
|
||||
b45
|
||||
d5rh
|
||||
O4W?R4f4C4
|
||||
baa
|
||||
d5rh
|
||||
OaWaRafaCa
|
||||
d5rh
|
||||
OaWaRafaCa
|
||||
O4W?R4C4
|
||||
d5rh
|
||||
|
|
@ -1,239 +0,0 @@
|
|||
* USING SPIFFS
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
* SPIFFS DESIGN
|
||||
|
||||
Spiffs is inspired by YAFFS. However, YAFFS is designed for NAND flashes, and
|
||||
for bigger targets with much more ram. Nevertheless, many wise thoughts have
|
||||
been borrowed from YAFFS when writing spiffs. Kudos!
|
||||
|
||||
The main complication writing spiffs was that it cannot be assumed the target
|
||||
has a heap. Spiffs must go along only with the work ram buffer given to it.
|
||||
This forces extra implementation on many areas of spiffs.
|
||||
|
||||
|
||||
** SPI flash devices using NOR technology
|
||||
|
||||
Below is a small description of how SPI flashes work internally. This is to
|
||||
give an understanding of the design choices made in spiffs.
|
||||
|
||||
SPI flash devices are physically divided in blocks. On some SPI flash devices,
|
||||
blocks are further divided into sectors. Datasheets sometimes name blocks as
|
||||
sectors and vice versa.
|
||||
|
||||
Common memory capacaties for SPI flashes are 512kB up to 8MB of data, where
|
||||
blocks may be 64kB. Sectors can be e.g. 4kB, if supported. Many SPI flashes
|
||||
have uniform block sizes, whereas others have non-uniform - the latter meaning
|
||||
that e.g. the first 16 blocks are 4kB big, and the rest are 64kB.
|
||||
|
||||
The entire memory is linear and can be read and written in random access.
|
||||
Erasing can only be done block- or sectorwise; or by mass erase.
|
||||
|
||||
SPI flashes can normally be erased from 100.000 up to 1.000.000 cycles before
|
||||
they fail.
|
||||
|
||||
A clean SPI flash from factory have all bits in entire memory set to one. A
|
||||
mass erase will reset the device to this state. Block or sector erasing will
|
||||
put the all bits in the area given by the sector or block to ones. Writing to a
|
||||
NOR flash pulls ones to zeroes. Writing 0xFF to an address is simply a no-op.
|
||||
|
||||
Writing 0b10101010 to a flash address holding 0b00001111 will yield 0b00001010.
|
||||
|
||||
This way of "write by nand" is used considerably in spiffs.
|
||||
|
||||
Common characteristics of NOR flashes are quick reads, but slow writes.
|
||||
|
||||
And finally, unlike NAND flashes, NOR flashes seem to not need any error
|
||||
correction. They always write correctly I gather.
|
||||
|
||||
|
||||
** Spiffs logical structure
|
||||
|
||||
Some terminology before proceeding. Physical blocks/sectors means sizes stated
|
||||
in the datasheet. Logical blocks and pages is something the integrator choose.
|
||||
|
||||
|
||||
** Blocks and pages
|
||||
|
||||
Spiffs is allocated to a part or all of the memory of the SPI flash device.
|
||||
This area is divided into logical blocks, which in turn are divided into
|
||||
logical pages. The boundary of a logical block must coincide with one or more
|
||||
physical blocks. The sizes for logical blocks and logical pages always remain
|
||||
the same, they are uniform.
|
||||
|
||||
Example: non-uniform flash mapped to spiffs with 128kB logical blocks
|
||||
|
||||
PHYSICAL FLASH BLOCKS SPIFFS LOGICAL BLOCKS: 128kB
|
||||
|
||||
+-----------------------+ - - - +-----------------------+
|
||||
| Block 1 : 16kB | | Block 1 : 128kB |
|
||||
+-----------------------+ | |
|
||||
| Block 2 : 16kB | | |
|
||||
+-----------------------+ | |
|
||||
| Block 3 : 16kB | | |
|
||||
+-----------------------+ | |
|
||||
| Block 4 : 16kB | | |
|
||||
+-----------------------+ | |
|
||||
| Block 5 : 64kB | | |
|
||||
+-----------------------+ - - - +-----------------------+
|
||||
| Block 6 : 64kB | | Block 2 : 128kB |
|
||||
+-----------------------+ | |
|
||||
| Block 7 : 64kB | | |
|
||||
+-----------------------+ - - - +-----------------------+
|
||||
| Block 8 : 64kB | | Block 3 : 128kB |
|
||||
+-----------------------+ | |
|
||||
| Block 9 : 64kB | | |
|
||||
+-----------------------+ - - - +-----------------------+
|
||||
| ... | | ... |
|
||||
|
||||
A logical block is divided further into a number of logical pages. A page
|
||||
defines the smallest data holding element known to spiffs. Hence, if a file
|
||||
is created being one byte big, it will occupy one page for index and one page
|
||||
for data - it will occupy 2 x size of a logical page on flash.
|
||||
So it seems it is good to select a small page size.
|
||||
|
||||
Each page has a metadata header being normally 5 to 9 bytes. This said, a very
|
||||
small page size will make metadata occupy a lot of the memory on the flash. A
|
||||
page size of 64 bytes will waste 8-14% on metadata, while 256 bytes 2-4%.
|
||||
So it seems it is good to select a big page size.
|
||||
|
||||
Also, spiffs uses a ram buffer being two times the page size. This ram buffer
|
||||
is used for loading and manipulating pages, but it is also used for algorithms
|
||||
to find free file ids, scanning the file system, etc. Having too small a page
|
||||
size means less work buffer for spiffs, ending up in more reads operations and
|
||||
eventually gives a slower file system.
|
||||
|
||||
Choosing the page size for the system involves many factors:
|
||||
- How big is the logical block size
|
||||
- What is the normal size of most files
|
||||
- How much ram can be spent
|
||||
- How much data (vs metadata) must be crammed into the file system
|
||||
- How fast must spiffs be
|
||||
- Other things impossible to find out
|
||||
|
||||
So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't
|
||||
fret - there is no optimal page size. This varies from how the target will use
|
||||
spiffs. Use the golden rule:
|
||||
|
||||
~~~ Logical Page Size = Logical Block Size / 256 ~~~
|
||||
|
||||
This is a good starting point. The final page size can then be derived through
|
||||
heuristical experimenting for us non-analytical minds.
|
||||
|
||||
|
||||
** Objects, indices and look-ups
|
||||
|
||||
A file, or an object as called in spiffs, is identified by an object id.
|
||||
Another YAFFS rip-off. This object id is a part of the page header. So, all
|
||||
pages know to which object/file they belong - not counting the free pages.
|
||||
|
||||
An object is made up of two types of pages: object index pages and data pages.
|
||||
Data pages contain the data written by user. Index pages contain metadata about
|
||||
the object, more specifically what data pages are part of the object.
|
||||
|
||||
The page header also includes something called a span index. Let's say a file
|
||||
is written covering three data pages. The first data page will then have span
|
||||
index 0, the second span index 1, and the last data page will have span index
|
||||
2. Simple as that.
|
||||
|
||||
Finally, each page header contain flags, telling if the page is used,
|
||||
deleted, finalized, holds index or data, and more.
|
||||
|
||||
Object indices also have span indices, where an object index with span index 0
|
||||
is referred to as the object index header. This page does not only contain
|
||||
references to data pages, but also extra info such as object name, object size
|
||||
in bytes, flags for file or directory, etc.
|
||||
|
||||
If one were to create a file covering three data pages, named e.g.
|
||||
"spandex-joke.txt", given object id 12, it could look like this:
|
||||
|
||||
PAGE 0 <things to be unveiled soon>
|
||||
|
||||
PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA]
|
||||
<first data page of joke>
|
||||
|
||||
PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA]
|
||||
<second data page of joke>
|
||||
|
||||
PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA]
|
||||
<some data belonging to object 545, probably not very amusing>
|
||||
|
||||
PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA]
|
||||
<third data page of joke>
|
||||
|
||||
PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX]
|
||||
obj ix header: [name:spandex-joke.txt size:600 bytes flags:FILE]
|
||||
obj ix: [1 2 4]
|
||||
|
||||
Looking in detail at page 5, the object index header page, the object index
|
||||
array refers to each data page in order, as mentioned before. The index of the
|
||||
object index array correlates with the data page span index.
|
||||
|
||||
entry ix: 0 1 2
|
||||
obj ix: [1 2 4]
|
||||
| | |
|
||||
PAGE 1, DATA, SPAN_IX 0 --------/ | |
|
||||
PAGE 2, DATA, SPAN_IX 1 --------/ |
|
||||
PAGE 4, DATA, SPAN_IX 2 --------/
|
||||
|
||||
Things to be unveiled in page 0 - well.. Spiffs is designed for systems low on
|
||||
ram. We cannot keep a dynamic list on the whereabouts of each object index
|
||||
header so we can find a file fast. There might not even be a heap! But, we do
|
||||
not want to scan all page headers on the flash to find the object index header.
|
||||
|
||||
The first page(s) of each block contains the so called object look-up. These
|
||||
are not normal pages, they do not have a header. Instead, they are arrays
|
||||
pointing out what object-id the rest of all pages in the block belongs to.
|
||||
|
||||
By this look-up, only the first page(s) in each block must to scanned to find
|
||||
the actual page which contains the object index header of the desired object.
|
||||
|
||||
The object lookup is redundant metadata. The assumption is that it presents
|
||||
less overhead reading a full page of data to memory from each block and search
|
||||
that, instead of reading a small amount of data from each page (i.e. the page
|
||||
header) in all blocks. Each read operation from SPI flash normally contains
|
||||
extra data as the read command itself and the flash address. Also, depending on
|
||||
the underlying implementation, other criterions may need to be passed for each
|
||||
read transaction, like mutexes and such.
|
||||
|
||||
The veiled example unveiled would look like this, with some extra pages:
|
||||
|
||||
PAGE 0 [ 12 12 545 12 12 34 34 4 0 0 0 0 ...]
|
||||
PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] ...
|
||||
PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] ...
|
||||
PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] ...
|
||||
PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] ...
|
||||
PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] ...
|
||||
PAGE 6 page header: [obj_id:34 span_ix:0 flags:USED|DATA] ...
|
||||
PAGE 7 page header: [obj_id:34 span_ix:1 flags:USED|DATA] ...
|
||||
PAGE 8 page header: [obj_id:4 span_ix:1 flags:USED|INDEX] ...
|
||||
PAGE 9 page header: [obj_id:23 span_ix:0 flags:DELETED|INDEX] ...
|
||||
PAGE 10 page header: [obj_id:23 span_ix:0 flags:DELETED|DATA] ...
|
||||
PAGE 11 page header: [obj_id:23 span_ix:1 flags:DELETED|DATA] ...
|
||||
PAGE 12 page header: [obj_id:23 span_ix:2 flags:DELETED|DATA] ...
|
||||
...
|
||||
|
||||
Ok, so why are page 9 to 12 marked as 0 when they belong to object id 23? These
|
||||
pages are deleted, so this is marked both in page header flags and in the look
|
||||
up. This is an example where spiffs uses NOR flashes "nand-way" of writing.
|
||||
|
||||
As a matter of fact, there are two object id's which are special:
|
||||
|
||||
obj id 0 (all bits zeroes) - indicates a deleted page in object look up
|
||||
obj id 0xff.. (all bits ones) - indicates a free page in object look up
|
||||
|
||||
Actually, the object id's have another quirk: if the most significant bit is
|
||||
set, this indicates an object index page. If the most significant bit is zero,
|
||||
this indicates a data page. So to be fully correct, page 0 in above example
|
||||
would look like this:
|
||||
|
||||
PAGE 0 [ 12 12 545 12 *12 34 34 *4 0 0 0 0 ...]
|
||||
|
||||
where the asterisk means the msb of the object id is set.
|
||||
|
||||
This is another way to speed up the searches when looking for object indices.
|
||||
By looking on the object id's msb in the object lookup, it is also possible
|
||||
to find out whether the page is an object index page or a data page.
|
||||
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
* When mending lost pages, also see if they fit into length specified in object index header
|
||||
|
||||
SPIFFS2 thoughts
|
||||
|
||||
* Instead of exact object id:s in the object lookup tables, use a hash of span index and object id.
|
||||
Eg. object id xor:ed with bit-reversed span index.
|
||||
This should decrease number of actual pages that needs to be visited when looking thru the obj lut.
|
||||
|
||||
* Logical number of each block. When moving stuff in a garbage collected page, the free
|
||||
page is assigned the same number as the garbage collected. Thus, object index pages do not have to
|
||||
be rewritten.
|
||||
|
||||
* Steal one page, use as a bit parity page. When starting an fs modification operation, write one bit
|
||||
as zero. When ending, write another bit as zero. On mount, if number of zeroes in page is uneven, a
|
||||
check is automatically run.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
ifndef spiffs
|
||||
$(warn defaulting path to generic spiffs module, spiffs variable not set)
|
||||
spiffs = ../generic/spiffs
|
||||
endif
|
||||
FLAGS += -DCONFIG_BUILD_SPIFFS
|
||||
INC += -I${spiffs}/src
|
||||
CPATH += ${spiffs}/src
|
||||
CFILES += spiffs_nucleus.c
|
||||
CFILES += spiffs_gc.c
|
||||
CFILES += spiffs_hydrogen.c
|
||||
CFILES += spiffs_cache.c
|
||||
CFILES += spiffs_check.c
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
BINARY = linux_spiffs_test
|
||||
|
||||
############
|
||||
#
|
||||
# Paths
|
||||
#
|
||||
############
|
||||
|
||||
sourcedir = src
|
||||
builddir = build
|
||||
|
||||
|
||||
#############
|
||||
#
|
||||
# Build tools
|
||||
#
|
||||
#############
|
||||
|
||||
CC ?= gcc
|
||||
LD ?= ld
|
||||
GDB ?= gdb
|
||||
OBJCOPY ?= objcopy
|
||||
OBJDUMP ?= objdump
|
||||
MKDIR ?= mkdir -p
|
||||
|
||||
###############
|
||||
#
|
||||
# Files and libs
|
||||
#
|
||||
###############
|
||||
|
||||
NO_TEST ?= 0
|
||||
CFLAGS = $(FLAGS)
|
||||
ifeq (1, $(strip $(NO_TEST)))
|
||||
CFILES_TEST = main.c
|
||||
CFLAGS += -DNO_TEST -Werror
|
||||
else
|
||||
CFILES_TEST = main.c \
|
||||
test_spiffs.c \
|
||||
test_dev.c \
|
||||
test_check.c \
|
||||
test_hydrogen.c \
|
||||
test_bugreports.c \
|
||||
testsuites.c \
|
||||
testrunner.c
|
||||
CFLAGS += -D_SPIFFS_TEST
|
||||
endif
|
||||
include files.mk
|
||||
INCLUDE_DIRECTIVES = -I./${sourcedir} -I./${sourcedir}/default -I./${sourcedir}/test
|
||||
COMPILEROPTIONS = $(INCLUDE_DIRECTIVES)
|
||||
|
||||
COMPILEROPTIONS_APP = $(INCLUDE_DIRECTIVES) \
|
||||
-Wall -Wno-format-y2k -W -Wstrict-prototypes -Wmissing-prototypes \
|
||||
-Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch \
|
||||
-Wshadow -Wcast-align -Wchar-subscripts -Winline -Wnested-externs\
|
||||
-Wredundant-decls
|
||||
|
||||
############
|
||||
#
|
||||
# Tasks
|
||||
#
|
||||
############
|
||||
|
||||
vpath %.c ${sourcedir} ${sourcedir}/default ${sourcedir}/test
|
||||
|
||||
OBJFILES = $(CFILES:%.c=${builddir}/%.o)
|
||||
OBJFILES_TEST = $(CFILES_TEST:%.c=${builddir}/%.o)
|
||||
|
||||
DEPFILES = $(CFILES:%.c=${builddir}/%.d) $(CFILES_TEST:%.c=${builddir}/%.d)
|
||||
|
||||
ALLOBJFILES += $(OBJFILES) $(OBJFILES_TEST)
|
||||
|
||||
DEPENDENCIES = $(DEPFILES)
|
||||
|
||||
# link object files, create binary
|
||||
$(BINARY): $(ALLOBJFILES)
|
||||
@echo "... linking"
|
||||
@${CC} $(LINKEROPTIONS) -o ${builddir}/$(BINARY) $(ALLOBJFILES) $(LIBS)
|
||||
ifeq (1, $(strip $(NO_TEST)))
|
||||
@echo "size: `du -b ${builddir}/${BINARY} | sed 's/\([0-9]*\).*/\1/g '` bytes"
|
||||
endif
|
||||
|
||||
|
||||
-include $(DEPENDENCIES)
|
||||
|
||||
# compile c files
|
||||
$(OBJFILES) : ${builddir}/%.o:%.c
|
||||
@echo "... compile $@"
|
||||
@${CC} $(COMPILEROPTIONS_APP) $(CFLAGS) -g -c -o $@ $<
|
||||
|
||||
$(OBJFILES_TEST) : ${builddir}/%.o:%.c
|
||||
@echo "... compile $@"
|
||||
@${CC} ${COMPILEROPTIONS} $(CFLAGS) -g -c -o $@ $<
|
||||
|
||||
# make dependencies
|
||||
# @echo "... depend $@";
|
||||
$(DEPFILES) : ${builddir}/%.d:%.c
|
||||
@rm -f $@; \
|
||||
${CC} $(COMPILEROPTIONS) -M $< > $@.$$$$; \
|
||||
sed 's,\($*\)\.o[ :]*, ${builddir}/\1.o $@ : ,g' < $@.$$$$ > $@; \
|
||||
rm -f $@.$$$$
|
||||
|
||||
all: mkdirs $(BINARY)
|
||||
|
||||
mkdirs:
|
||||
-@${MKDIR} ${builddir}
|
||||
-@${MKDIR} test_data
|
||||
|
||||
FILTER ?=
|
||||
|
||||
test: $(BINARY)
|
||||
ifdef $(FILTER)
|
||||
./build/$(BINARY)
|
||||
else
|
||||
./build/$(BINARY) -f $(FILTER)
|
||||
endif
|
||||
|
||||
test_failed: $(BINARY)
|
||||
./build/$(BINARY) _tests_fail
|
||||
|
||||
clean:
|
||||
@echo ... removing build files in ${builddir}
|
||||
@rm -f ${builddir}/*.o
|
||||
@rm -f ${builddir}/*.d
|
||||
@rm -f ${builddir}/*.elf
|
||||
|
||||
ONOFF = 1 0
|
||||
OFFON = 0 1
|
||||
build-all:
|
||||
@for rdonly in $(ONOFF); do \
|
||||
for singleton in $(ONOFF); do \
|
||||
for hal_cb_xtra in $(OFFON); do \
|
||||
for cache in $(OFFON); do \
|
||||
for magic in $(OFFON); do \
|
||||
for temporal_cache in $(OFFON); do \
|
||||
for ix_map in $(OFFON); do \
|
||||
echo; \
|
||||
echo ============================================================; \
|
||||
echo SPIFFS_READ_ONLY=$$rdonly; \
|
||||
echo SPIFFS_SINGLETON=$$singleton; \
|
||||
echo SPIFFS_HAL_CALLBACK_EXTRA=$$hal_cb_xtra; \
|
||||
echo SPIFFS_CACHE, SPIFFS_CACHE_WR=$$cache; \
|
||||
echo SPIFFS_USE_MAGIC, SPIFFS_USE_MAGIC_LENGTH=$$magic; \
|
||||
echo SPIFFS_TEMPORAL_FD_CACHE=$$temporal_cache; \
|
||||
echo SPIFFS_IX_MAP=$$ix_map; \
|
||||
$(MAKE) clean && $(MAKE) FLAGS="\
|
||||
-DSPIFFS_HAL_CALLBACK_EXTRA=$$hal_cb_xtra \
|
||||
-DSPIFFS_SINGLETON=$$singleton \
|
||||
-DSPIFFS_CACHE=$$cache \
|
||||
-DSPIFFS_CACHE_WR=$$cache \
|
||||
-DSPIFFS_READ_ONLY=$$rdonly \
|
||||
-DSPIFFS_USE_MAGIC=$$magic \
|
||||
-DSPIFFS_USE_MAGIC_LENGTH=$$magic \
|
||||
-DSPIFFS_TEMPORAL_FD_CACHE=$$temporal_cache \
|
||||
-DSPIFFS_IX_MAP=$$ix_map \
|
||||
" NO_TEST=1; \
|
||||
done || exit 1; \
|
||||
done \
|
||||
done \
|
||||
done \
|
||||
done \
|
||||
done \
|
||||
done
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
/*
|
||||
* spiffs_config.h
|
||||
*
|
||||
* Created on: Jul 3, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#ifndef SPIFFS_CONFIG_H_
|
||||
#define SPIFFS_CONFIG_H_
|
||||
|
||||
// ----------- 8< ------------
|
||||
// Following includes are for the linux test build of spiffs
|
||||
// These may/should/must be removed/altered/replaced in your target
|
||||
#include "params_test.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#ifdef _SPIFFS_TEST
|
||||
#include "testrunner.h"
|
||||
#endif
|
||||
// ----------- >8 ------------
|
||||
|
||||
// compile time switches
|
||||
|
||||
// Set generic spiffs debug output call.
|
||||
#ifndef SPIFFS_DBG
|
||||
#define SPIFFS_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for garbage collecting.
|
||||
#ifndef SPIFFS_GC_DBG
|
||||
#define SPIFFS_GC_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for caching.
|
||||
#ifndef SPIFFS_CACHE_DBG
|
||||
#define SPIFFS_CACHE_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for system consistency checks.
|
||||
#ifndef SPIFFS_CHECK_DBG
|
||||
#define SPIFFS_CHECK_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for all api invocations.
|
||||
#ifndef SPIFFS_API_DBG
|
||||
#define SPIFFS_API_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Defines spiffs debug print formatters
|
||||
// some general signed number
|
||||
#ifndef _SPIPRIi
|
||||
#define _SPIPRIi "%d"
|
||||
#endif
|
||||
// address
|
||||
#ifndef _SPIPRIad
|
||||
#define _SPIPRIad "%08x"
|
||||
#endif
|
||||
// block
|
||||
#ifndef _SPIPRIbl
|
||||
#define _SPIPRIbl "%04x"
|
||||
#endif
|
||||
// page
|
||||
#ifndef _SPIPRIpg
|
||||
#define _SPIPRIpg "%04x"
|
||||
#endif
|
||||
// span index
|
||||
#ifndef _SPIPRIsp
|
||||
#define _SPIPRIsp "%04x"
|
||||
#endif
|
||||
// file descriptor
|
||||
#ifndef _SPIPRIfd
|
||||
#define _SPIPRIfd "%d"
|
||||
#endif
|
||||
// file object id
|
||||
#ifndef _SPIPRIid
|
||||
#define _SPIPRIid "%04x"
|
||||
#endif
|
||||
// file flags
|
||||
#ifndef _SPIPRIfl
|
||||
#define _SPIPRIfl "%02x"
|
||||
#endif
|
||||
|
||||
|
||||
// Enable/disable API functions to determine exact number of bytes
|
||||
// for filedescriptor and cache buffers. Once decided for a configuration,
|
||||
// this can be disabled to reduce flash.
|
||||
#ifndef SPIFFS_BUFFER_HELP
|
||||
#define SPIFFS_BUFFER_HELP 0
|
||||
#endif
|
||||
|
||||
// Enables/disable memory read caching of nucleus file system operations.
|
||||
// If enabled, memory area must be provided for cache in SPIFFS_mount.
|
||||
#ifndef SPIFFS_CACHE
|
||||
#define SPIFFS_CACHE 1
|
||||
#endif
|
||||
#if SPIFFS_CACHE
|
||||
// Enables memory write caching for file descriptors in hydrogen
|
||||
#ifndef SPIFFS_CACHE_WR
|
||||
#define SPIFFS_CACHE_WR 1
|
||||
#endif
|
||||
|
||||
// Enable/disable statistics on caching. Debug/test purpose only.
|
||||
#ifndef SPIFFS_CACHE_STATS
|
||||
#define SPIFFS_CACHE_STATS 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Always check header of each accessed page to ensure consistent state.
|
||||
// If enabled it will increase number of reads, will increase flash.
|
||||
#ifndef SPIFFS_PAGE_CHECK
|
||||
#define SPIFFS_PAGE_CHECK 1
|
||||
#endif
|
||||
|
||||
// Define maximum number of gc runs to perform to reach desired free pages.
|
||||
#ifndef SPIFFS_GC_MAX_RUNS
|
||||
#define SPIFFS_GC_MAX_RUNS 5
|
||||
#endif
|
||||
|
||||
// Enable/disable statistics on gc. Debug/test purpose only.
|
||||
#ifndef SPIFFS_GC_STATS
|
||||
#define SPIFFS_GC_STATS 1
|
||||
#endif
|
||||
|
||||
// Garbage collecting examines all pages in a block which and sums up
|
||||
// to a block score. Deleted pages normally gives positive score and
|
||||
// used pages normally gives a negative score (as these must be moved).
|
||||
// To have a fair wear-leveling, the erase age is also included in score,
|
||||
// whose factor normally is the most positive.
|
||||
// The larger the score, the more likely it is that the block will
|
||||
// picked for garbage collection.
|
||||
|
||||
// Garbage collecting heuristics - weight used for deleted pages.
|
||||
#ifndef SPIFFS_GC_HEUR_W_DELET
|
||||
#define SPIFFS_GC_HEUR_W_DELET (5)
|
||||
#endif
|
||||
// Garbage collecting heuristics - weight used for used pages.
|
||||
#ifndef SPIFFS_GC_HEUR_W_USED
|
||||
#define SPIFFS_GC_HEUR_W_USED (-1)
|
||||
#endif
|
||||
// Garbage collecting heuristics - weight used for time between
|
||||
// last erased and erase of this block.
|
||||
#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE
|
||||
#define SPIFFS_GC_HEUR_W_ERASE_AGE (50)
|
||||
#endif
|
||||
|
||||
// Object name maximum length. Note that this length include the
|
||||
// zero-termination character, meaning maximum string of characters
|
||||
// can at most be SPIFFS_OBJ_NAME_LEN - 1.
|
||||
#ifndef SPIFFS_OBJ_NAME_LEN
|
||||
#define SPIFFS_OBJ_NAME_LEN (32)
|
||||
#endif
|
||||
|
||||
// Maximum length of the metadata associated with an object.
|
||||
// Setting to non-zero value enables metadata-related API but also
|
||||
// changes the on-disk format, so the change is not backward-compatible.
|
||||
//
|
||||
// Do note: the meta length must never exceed
|
||||
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64)
|
||||
//
|
||||
// This is derived from following:
|
||||
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
|
||||
// spiffs_object_ix_header fields + at least some LUT entries)
|
||||
#ifndef SPIFFS_OBJ_META_LEN
|
||||
#define SPIFFS_OBJ_META_LEN (0)
|
||||
#endif
|
||||
|
||||
// Size of buffer allocated on stack used when copying data.
|
||||
// Lower value generates more read/writes. No meaning having it bigger
|
||||
// than logical page size.
|
||||
#ifndef SPIFFS_COPY_BUFFER_STACK
|
||||
#define SPIFFS_COPY_BUFFER_STACK (64)
|
||||
#endif
|
||||
|
||||
// Enable this to have an identifiable spiffs filesystem. This will look for
|
||||
// a magic in all sectors to determine if this is a valid spiffs system or
|
||||
// not on mount point. If not, SPIFFS_format must be called prior to mounting
|
||||
// again.
|
||||
#ifndef SPIFFS_USE_MAGIC
|
||||
#define SPIFFS_USE_MAGIC (0)
|
||||
#endif
|
||||
|
||||
#if SPIFFS_USE_MAGIC
|
||||
// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is
|
||||
// enabled, the magic will also be dependent on the length of the filesystem.
|
||||
// For example, a filesystem configured and formatted for 4 megabytes will not
|
||||
// be accepted for mounting with a configuration defining the filesystem as 2
|
||||
// megabytes.
|
||||
#ifndef SPIFFS_USE_MAGIC_LENGTH
|
||||
#define SPIFFS_USE_MAGIC_LENGTH (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
|
||||
// These should be defined on a multithreaded system
|
||||
|
||||
// define this to enter a mutex if you're running on a multithreaded system
|
||||
#ifndef SPIFFS_LOCK
|
||||
#define SPIFFS_LOCK(fs)
|
||||
#endif
|
||||
// define this to exit a mutex if you're running on a multithreaded system
|
||||
#ifndef SPIFFS_UNLOCK
|
||||
#define SPIFFS_UNLOCK(fs)
|
||||
#endif
|
||||
|
||||
// Enable if only one spiffs instance with constant configuration will exist
|
||||
// on the target. This will reduce calculations, flash and memory accesses.
|
||||
// Parts of configuration must be defined below instead of at time of mount.
|
||||
#ifndef SPIFFS_SINGLETON
|
||||
#define SPIFFS_SINGLETON 0
|
||||
#endif
|
||||
|
||||
#if SPIFFS_SINGLETON
|
||||
// Instead of giving parameters in config struct, singleton build must
|
||||
// give parameters in defines below.
|
||||
#ifndef SPIFFS_CFG_PHYS_SZ
|
||||
#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*1024*2)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_PHYS_ERASE_SZ
|
||||
#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (65536)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_PHYS_ADDR
|
||||
#define SPIFFS_CFG_PHYS_ADDR(ignore) (0)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_LOG_PAGE_SZ
|
||||
#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_LOG_BLOCK_SZ
|
||||
#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Enable this if your target needs aligned data for index tables
|
||||
#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
|
||||
#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 0
|
||||
#endif
|
||||
|
||||
// Enable this if you want the HAL callbacks to be called with the spiffs struct
|
||||
#ifndef SPIFFS_HAL_CALLBACK_EXTRA
|
||||
#define SPIFFS_HAL_CALLBACK_EXTRA 0
|
||||
#endif
|
||||
|
||||
// Enable this if you want to add an integer offset to all file handles
|
||||
// (spiffs_file). This is useful if running multiple instances of spiffs on
|
||||
// same target, in order to recognise to what spiffs instance a file handle
|
||||
// belongs.
|
||||
// NB: This adds config field fh_ix_offset in the configuration struct when
|
||||
// mounting, which must be defined.
|
||||
#ifndef SPIFFS_FILEHDL_OFFSET
|
||||
#define SPIFFS_FILEHDL_OFFSET 0
|
||||
#endif
|
||||
|
||||
// Enable this to compile a read only version of spiffs.
|
||||
// This will reduce binary size of spiffs. All code comprising modification
|
||||
// of the file system will not be compiled. Some config will be ignored.
|
||||
// HAL functions for erasing and writing to spi-flash may be null. Cache
|
||||
// can be disabled for even further binary size reduction (and ram savings).
|
||||
// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL.
|
||||
// If the file system cannot be mounted due to aborted erase operation and
|
||||
// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be
|
||||
// returned.
|
||||
// Might be useful for e.g. bootloaders and such.
|
||||
#ifndef SPIFFS_READ_ONLY
|
||||
#define SPIFFS_READ_ONLY 0
|
||||
#endif
|
||||
|
||||
// Enable this to add a temporal file cache using the fd buffer.
|
||||
// The effects of the cache is that SPIFFS_open will find the file faster in
|
||||
// certain cases. It will make it a lot easier for spiffs to find files
|
||||
// opened frequently, reducing number of readings from the spi flash for
|
||||
// finding those files.
|
||||
// This will grow each fd by 6 bytes. If your files are opened in patterns
|
||||
// with a degree of temporal locality, the system is optimized.
|
||||
// Examples can be letting spiffs serve web content, where one file is the css.
|
||||
// The css is accessed for each html file that is opened, meaning it is
|
||||
// accessed almost every second time a file is opened. Another example could be
|
||||
// a log file that is often opened, written, and closed.
|
||||
// The size of the cache is number of given file descriptors, as it piggybacks
|
||||
// on the fd update mechanism. The cache lives in the closed file descriptors.
|
||||
// When closed, the fd know the whereabouts of the file. Instead of forgetting
|
||||
// this, the temporal cache will keep handling updates to that file even if the
|
||||
// fd is closed. If the file is opened again, the location of the file is found
|
||||
// directly. If all available descriptors become opened, all cache memory is
|
||||
// lost.
|
||||
#ifndef SPIFFS_TEMPORAL_FD_CACHE
|
||||
#define SPIFFS_TEMPORAL_FD_CACHE 1
|
||||
#endif
|
||||
|
||||
// Temporal file cache hit score. Each time a file is opened, all cached files
|
||||
// will lose one point. If the opened file is found in cache, that entry will
|
||||
// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this
|
||||
// value for the specific access patterns of the application. However, it must
|
||||
// be between 1 (no gain for hitting a cached entry often) and 255.
|
||||
#ifndef SPIFFS_TEMPORAL_CACHE_HIT_SCORE
|
||||
#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 4
|
||||
#endif
|
||||
|
||||
// Enable to be able to map object indices to memory.
|
||||
// This allows for faster and more deterministic reading if cases of reading
|
||||
// large files and when changing file offset by seeking around a lot.
|
||||
// When mapping a file's index, the file system will be scanned for index pages
|
||||
// and the info will be put in memory provided by user. When reading, the
|
||||
// memory map can be looked up instead of searching for index pages on the
|
||||
// medium. This way, user can trade memory against performance.
|
||||
// Whole, parts of, or future parts not being written yet can be mapped. The
|
||||
// memory array will be owned by spiffs and updated accordingly during garbage
|
||||
// collecting or when modifying the indices. The latter is invoked by when the
|
||||
// file is modified in some way. The index buffer is tied to the file
|
||||
// descriptor.
|
||||
#ifndef SPIFFS_IX_MAP
|
||||
#define SPIFFS_IX_MAP 1
|
||||
#endif
|
||||
|
||||
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
|
||||
// in the api. This function will visualize all filesystem using given printf
|
||||
// function.
|
||||
#ifndef SPIFFS_TEST_VISUALISATION
|
||||
#define SPIFFS_TEST_VISUALISATION 1
|
||||
#endif
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
#ifndef spiffs_printf
|
||||
#define spiffs_printf(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
// spiffs_printf argument for a free page
|
||||
#ifndef SPIFFS_TEST_VIS_FREE_STR
|
||||
#define SPIFFS_TEST_VIS_FREE_STR "_"
|
||||
#endif
|
||||
// spiffs_printf argument for a deleted page
|
||||
#ifndef SPIFFS_TEST_VIS_DELE_STR
|
||||
#define SPIFFS_TEST_VIS_DELE_STR "/"
|
||||
#endif
|
||||
// spiffs_printf argument for an index page for given object id
|
||||
#ifndef SPIFFS_TEST_VIS_INDX_STR
|
||||
#define SPIFFS_TEST_VIS_INDX_STR(id) "i"
|
||||
#endif
|
||||
// spiffs_printf argument for a data page for given object id
|
||||
#ifndef SPIFFS_TEST_VIS_DATA_STR
|
||||
#define SPIFFS_TEST_VIS_DATA_STR(id) "d"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Types depending on configuration such as the amount of flash bytes
|
||||
// given to spiffs file system in total (spiffs_file_system_size),
|
||||
// the logical block size (log_block_size), and the logical page size
|
||||
// (log_page_size)
|
||||
|
||||
// Block index type. Make sure the size of this type can hold
|
||||
// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size
|
||||
typedef u16_t spiffs_block_ix;
|
||||
// Page index type. Make sure the size of this type can hold
|
||||
// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size
|
||||
typedef u16_t spiffs_page_ix;
|
||||
// Object id type - most significant bit is reserved for index flag. Make sure the
|
||||
// size of this type can hold the highest object id on a full system,
|
||||
// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2
|
||||
typedef u16_t spiffs_obj_id;
|
||||
// Object span index type. Make sure the size of this type can
|
||||
// hold the largest possible span index on the system -
|
||||
// i.e. (spiffs_file_system_size / log_page_size) - 1
|
||||
typedef u16_t spiffs_span_ix;
|
||||
|
||||
#endif /* SPIFFS_CONFIG_H_ */
|
||||
|
|
@ -1,816 +0,0 @@
|
|||
/*
|
||||
* spiffs.h
|
||||
*
|
||||
* Created on: May 26, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#ifndef SPIFFS_H_
|
||||
#define SPIFFS_H_
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "spiffs_config.h"
|
||||
|
||||
#define SPIFFS_OK 0
|
||||
#define SPIFFS_ERR_NOT_MOUNTED -10000
|
||||
#define SPIFFS_ERR_FULL -10001
|
||||
#define SPIFFS_ERR_NOT_FOUND -10002
|
||||
#define SPIFFS_ERR_END_OF_OBJECT -10003
|
||||
#define SPIFFS_ERR_DELETED -10004
|
||||
#define SPIFFS_ERR_NOT_FINALIZED -10005
|
||||
#define SPIFFS_ERR_NOT_INDEX -10006
|
||||
#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007
|
||||
#define SPIFFS_ERR_FILE_CLOSED -10008
|
||||
#define SPIFFS_ERR_FILE_DELETED -10009
|
||||
#define SPIFFS_ERR_BAD_DESCRIPTOR -10010
|
||||
#define SPIFFS_ERR_IS_INDEX -10011
|
||||
#define SPIFFS_ERR_IS_FREE -10012
|
||||
#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013
|
||||
#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014
|
||||
#define SPIFFS_ERR_INDEX_REF_FREE -10015
|
||||
#define SPIFFS_ERR_INDEX_REF_LU -10016
|
||||
#define SPIFFS_ERR_INDEX_REF_INVALID -10017
|
||||
#define SPIFFS_ERR_INDEX_FREE -10018
|
||||
#define SPIFFS_ERR_INDEX_LU -10019
|
||||
#define SPIFFS_ERR_INDEX_INVALID -10020
|
||||
#define SPIFFS_ERR_NOT_WRITABLE -10021
|
||||
#define SPIFFS_ERR_NOT_READABLE -10022
|
||||
#define SPIFFS_ERR_CONFLICTING_NAME -10023
|
||||
#define SPIFFS_ERR_NOT_CONFIGURED -10024
|
||||
|
||||
#define SPIFFS_ERR_NOT_A_FS -10025
|
||||
#define SPIFFS_ERR_MOUNTED -10026
|
||||
#define SPIFFS_ERR_ERASE_FAIL -10027
|
||||
#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028
|
||||
|
||||
#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029
|
||||
|
||||
#define SPIFFS_ERR_FILE_EXISTS -10030
|
||||
|
||||
#define SPIFFS_ERR_NOT_A_FILE -10031
|
||||
#define SPIFFS_ERR_RO_NOT_IMPL -10032
|
||||
#define SPIFFS_ERR_RO_ABORTED_OPERATION -10033
|
||||
#define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034
|
||||
#define SPIFFS_ERR_PROBE_NOT_A_FS -10035
|
||||
#define SPIFFS_ERR_NAME_TOO_LONG -10036
|
||||
|
||||
#define SPIFFS_ERR_IX_MAP_UNMAPPED -10037
|
||||
#define SPIFFS_ERR_IX_MAP_MAPPED -10038
|
||||
#define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039
|
||||
|
||||
#define SPIFFS_ERR_SEEK_BOUNDS -10040
|
||||
|
||||
|
||||
#define SPIFFS_ERR_INTERNAL -10050
|
||||
|
||||
#define SPIFFS_ERR_TEST -10100
|
||||
|
||||
|
||||
// spiffs file descriptor index type. must be signed
|
||||
typedef s16_t spiffs_file;
|
||||
// spiffs file descriptor flags
|
||||
typedef u16_t spiffs_flags;
|
||||
// spiffs file mode
|
||||
typedef u16_t spiffs_mode;
|
||||
// object type
|
||||
typedef u8_t spiffs_obj_type;
|
||||
|
||||
struct spiffs_t;
|
||||
|
||||
#if SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
/* spi read call function type */
|
||||
typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst);
|
||||
/* spi write call function type */
|
||||
typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src);
|
||||
/* spi erase call function type */
|
||||
typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size);
|
||||
|
||||
#else // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
/* spi read call function type */
|
||||
typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst);
|
||||
/* spi write call function type */
|
||||
typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src);
|
||||
/* spi erase call function type */
|
||||
typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size);
|
||||
#endif // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
/* file system check callback report operation */
|
||||
typedef enum {
|
||||
SPIFFS_CHECK_LOOKUP = 0,
|
||||
SPIFFS_CHECK_INDEX,
|
||||
SPIFFS_CHECK_PAGE
|
||||
} spiffs_check_type;
|
||||
|
||||
/* file system check callback report type */
|
||||
typedef enum {
|
||||
SPIFFS_CHECK_PROGRESS = 0,
|
||||
SPIFFS_CHECK_ERROR,
|
||||
SPIFFS_CHECK_FIX_INDEX,
|
||||
SPIFFS_CHECK_FIX_LOOKUP,
|
||||
SPIFFS_CHECK_DELETE_ORPHANED_INDEX,
|
||||
SPIFFS_CHECK_DELETE_PAGE,
|
||||
SPIFFS_CHECK_DELETE_BAD_FILE
|
||||
} spiffs_check_report;
|
||||
|
||||
/* file system check callback function */
|
||||
#if SPIFFS_HAL_CALLBACK_EXTRA
|
||||
typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report,
|
||||
u32_t arg1, u32_t arg2);
|
||||
#else // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report,
|
||||
u32_t arg1, u32_t arg2);
|
||||
#endif // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
/* file system listener callback operation */
|
||||
typedef enum {
|
||||
/* the file has been created */
|
||||
SPIFFS_CB_CREATED = 0,
|
||||
/* the file has been updated or moved to another page */
|
||||
SPIFFS_CB_UPDATED,
|
||||
/* the file has been deleted */
|
||||
SPIFFS_CB_DELETED
|
||||
} spiffs_fileop_type;
|
||||
|
||||
/* file system listener callback function */
|
||||
typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix);
|
||||
|
||||
#ifndef SPIFFS_DBG
|
||||
#define SPIFFS_DBG(...) \
|
||||
printf(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef SPIFFS_GC_DBG
|
||||
#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef SPIFFS_CACHE_DBG
|
||||
#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef SPIFFS_CHECK_DBG
|
||||
#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/* Any write to the filehandle is appended to end of the file */
|
||||
#define SPIFFS_APPEND (1<<0)
|
||||
#define SPIFFS_O_APPEND SPIFFS_APPEND
|
||||
/* If the opened file exists, it will be truncated to zero length before opened */
|
||||
#define SPIFFS_TRUNC (1<<1)
|
||||
#define SPIFFS_O_TRUNC SPIFFS_TRUNC
|
||||
/* If the opened file does not exist, it will be created before opened */
|
||||
#define SPIFFS_CREAT (1<<2)
|
||||
#define SPIFFS_O_CREAT SPIFFS_CREAT
|
||||
/* The opened file may only be read */
|
||||
#define SPIFFS_RDONLY (1<<3)
|
||||
#define SPIFFS_O_RDONLY SPIFFS_RDONLY
|
||||
/* The opened file may only be written */
|
||||
#define SPIFFS_WRONLY (1<<4)
|
||||
#define SPIFFS_O_WRONLY SPIFFS_WRONLY
|
||||
/* The opened file may be both read and written */
|
||||
#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY)
|
||||
#define SPIFFS_O_RDWR SPIFFS_RDWR
|
||||
/* Any writes to the filehandle will never be cached but flushed directly */
|
||||
#define SPIFFS_DIRECT (1<<5)
|
||||
#define SPIFFS_O_DIRECT SPIFFS_DIRECT
|
||||
/* If SPIFFS_O_CREAT and SPIFFS_O_EXCL are set, SPIFFS_open() shall fail if the file exists */
|
||||
#define SPIFFS_EXCL (1<<6)
|
||||
#define SPIFFS_O_EXCL SPIFFS_EXCL
|
||||
|
||||
#define SPIFFS_SEEK_SET (0)
|
||||
#define SPIFFS_SEEK_CUR (1)
|
||||
#define SPIFFS_SEEK_END (2)
|
||||
|
||||
#define SPIFFS_TYPE_FILE (1)
|
||||
#define SPIFFS_TYPE_DIR (2)
|
||||
#define SPIFFS_TYPE_HARD_LINK (3)
|
||||
#define SPIFFS_TYPE_SOFT_LINK (4)
|
||||
|
||||
#ifndef SPIFFS_LOCK
|
||||
#define SPIFFS_LOCK(fs)
|
||||
#endif
|
||||
|
||||
#ifndef SPIFFS_UNLOCK
|
||||
#define SPIFFS_UNLOCK(fs)
|
||||
#endif
|
||||
|
||||
// phys structs
|
||||
|
||||
// spiffs spi configuration struct
|
||||
typedef struct {
|
||||
// physical read function
|
||||
spiffs_read hal_read_f;
|
||||
// physical write function
|
||||
spiffs_write hal_write_f;
|
||||
// physical erase function
|
||||
spiffs_erase hal_erase_f;
|
||||
#if SPIFFS_SINGLETON == 0
|
||||
// physical size of the spi flash
|
||||
u32_t phys_size;
|
||||
// physical offset in spi flash used for spiffs,
|
||||
// must be on block boundary
|
||||
u32_t phys_addr;
|
||||
// physical size when erasing a block
|
||||
u32_t phys_erase_block;
|
||||
|
||||
// logical size of a block, must be on physical
|
||||
// block size boundary and must never be less than
|
||||
// a physical block
|
||||
u32_t log_block_size;
|
||||
// logical size of a page, must be at least
|
||||
// log_block_size / 8
|
||||
u32_t log_page_size;
|
||||
|
||||
#endif
|
||||
#if SPIFFS_FILEHDL_OFFSET
|
||||
// an integer offset added to each file handle
|
||||
u16_t fh_ix_offset;
|
||||
#endif
|
||||
} spiffs_config;
|
||||
|
||||
typedef struct spiffs_t {
|
||||
// file system configuration
|
||||
spiffs_config cfg;
|
||||
// number of logical blocks
|
||||
u32_t block_count;
|
||||
|
||||
// cursor for free blocks, block index
|
||||
spiffs_block_ix free_cursor_block_ix;
|
||||
// cursor for free blocks, entry index
|
||||
int free_cursor_obj_lu_entry;
|
||||
// cursor when searching, block index
|
||||
spiffs_block_ix cursor_block_ix;
|
||||
// cursor when searching, entry index
|
||||
int cursor_obj_lu_entry;
|
||||
|
||||
// primary work buffer, size of a logical page
|
||||
u8_t *lu_work;
|
||||
// secondary work buffer, size of a logical page
|
||||
u8_t *work;
|
||||
// file descriptor memory area
|
||||
u8_t *fd_space;
|
||||
// available file descriptors
|
||||
u32_t fd_count;
|
||||
|
||||
// last error
|
||||
s32_t err_code;
|
||||
|
||||
// current number of free blocks
|
||||
u32_t free_blocks;
|
||||
// current number of busy pages
|
||||
u32_t stats_p_allocated;
|
||||
// current number of deleted pages
|
||||
u32_t stats_p_deleted;
|
||||
// flag indicating that garbage collector is cleaning
|
||||
u8_t cleaning;
|
||||
// max erase count amongst all blocks
|
||||
spiffs_obj_id max_erase_count;
|
||||
|
||||
#if SPIFFS_GC_STATS
|
||||
u32_t stats_gc_runs;
|
||||
#endif
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
// cache memory
|
||||
void *cache;
|
||||
// cache size
|
||||
u32_t cache_size;
|
||||
#if SPIFFS_CACHE_STATS
|
||||
u32_t cache_hits;
|
||||
u32_t cache_misses;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// check callback function
|
||||
spiffs_check_callback check_cb_f;
|
||||
// file callback function
|
||||
spiffs_file_callback file_cb_f;
|
||||
// mounted flag
|
||||
u8_t mounted;
|
||||
// user data
|
||||
void *user_data;
|
||||
// config magic
|
||||
u32_t config_magic;
|
||||
} spiffs;
|
||||
|
||||
/* spiffs file status struct */
|
||||
typedef struct {
|
||||
spiffs_obj_id obj_id;
|
||||
u32_t size;
|
||||
spiffs_obj_type type;
|
||||
spiffs_page_ix pix;
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||
#if SPIFFS_OBJ_META_LEN
|
||||
u8_t meta[SPIFFS_OBJ_META_LEN];
|
||||
#endif
|
||||
} spiffs_stat;
|
||||
|
||||
struct spiffs_dirent {
|
||||
spiffs_obj_id obj_id;
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||
spiffs_obj_type type;
|
||||
u32_t size;
|
||||
spiffs_page_ix pix;
|
||||
#if SPIFFS_OBJ_META_LEN
|
||||
u8_t meta[SPIFFS_OBJ_META_LEN];
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
spiffs *fs;
|
||||
spiffs_block_ix block;
|
||||
int entry;
|
||||
} spiffs_DIR;
|
||||
|
||||
#if SPIFFS_IX_MAP
|
||||
|
||||
typedef struct {
|
||||
// buffer with looked up data pixes
|
||||
spiffs_page_ix *map_buf;
|
||||
// precise file byte offset
|
||||
u32_t offset;
|
||||
// start data span index of lookup buffer
|
||||
spiffs_span_ix start_spix;
|
||||
// end data span index of lookup buffer
|
||||
spiffs_span_ix end_spix;
|
||||
} spiffs_ix_map;
|
||||
|
||||
#endif
|
||||
|
||||
// functions
|
||||
|
||||
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
|
||||
/**
|
||||
* Special function. This takes a spiffs config struct and returns the number
|
||||
* of blocks this file system was formatted with. This function relies on
|
||||
* that following info is set correctly in given config struct:
|
||||
*
|
||||
* phys_addr, log_page_size, and log_block_size.
|
||||
*
|
||||
* Also, hal_read_f must be set in the config struct.
|
||||
*
|
||||
* One must be sure of the correct page size and that the physical address is
|
||||
* correct in the probed file system when calling this function. It is not
|
||||
* checked if the phys_addr actually points to the start of the file system,
|
||||
* so one might get a false positive if entering a phys_addr somewhere in the
|
||||
* middle of the file system at block boundary. In addition, it is not checked
|
||||
* if the page size is actually correct. If it is not, weird file system sizes
|
||||
* will be returned.
|
||||
*
|
||||
* If this function detects a file system it returns the assumed file system
|
||||
* size, which can be used to set the phys_size.
|
||||
*
|
||||
* Otherwise, it returns an error indicating why it is not regarded as a file
|
||||
* system.
|
||||
*
|
||||
* Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK
|
||||
* macros. It returns the error code directly, instead of as read by
|
||||
* SPIFFS_errno.
|
||||
*
|
||||
* @param config essential parts of the physical and logical
|
||||
* configuration of the file system.
|
||||
*/
|
||||
s32_t SPIFFS_probe_fs(spiffs_config *config);
|
||||
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
|
||||
|
||||
/**
|
||||
* Initializes the file system dynamic parameters and mounts the filesystem.
|
||||
* If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS
|
||||
* if the flash does not contain a recognizable file system.
|
||||
* In this case, SPIFFS_format must be called prior to remounting.
|
||||
* @param fs the file system struct
|
||||
* @param config the physical and logical configuration of the file system
|
||||
* @param work a memory work buffer comprising 2*config->log_page_size
|
||||
* bytes used throughout all file system operations
|
||||
* @param fd_space memory for file descriptors
|
||||
* @param fd_space_size memory size of file descriptors
|
||||
* @param cache memory for cache, may be null
|
||||
* @param cache_size memory size of cache
|
||||
* @param check_cb_f callback function for reporting during consistency checks
|
||||
*/
|
||||
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
|
||||
u8_t *fd_space, u32_t fd_space_size,
|
||||
void *cache, u32_t cache_size,
|
||||
spiffs_check_callback check_cb_f);
|
||||
|
||||
/**
|
||||
* Unmounts the file system. All file handles will be flushed of any
|
||||
* cached writes and closed.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
void SPIFFS_unmount(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Creates a new file.
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the new file
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Opens/creates a file.
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the new file
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY,
|
||||
* SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Opens a file by given dir entry.
|
||||
* Optimization purposes, when traversing a file system with SPIFFS_readdir
|
||||
* a normal SPIFFS_open would need to traverse the filesystem again to find
|
||||
* the file, whilst SPIFFS_open_by_dirent already knows where the file resides.
|
||||
* @param fs the file system struct
|
||||
* @param e the dir entry to the file
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
|
||||
* SPIFFS_CREAT will have no effect in this case.
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Opens a file by given page index.
|
||||
* Optimization purposes, opens a file by directly pointing to the page
|
||||
* index in the spi flash.
|
||||
* If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE
|
||||
* is returned.
|
||||
* @param fs the file system struct
|
||||
* @param page_ix the page index
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
|
||||
* SPIFFS_CREAT will have no effect in this case.
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Reads from given filehandle.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param buf where to put read data
|
||||
* @param len how much to read
|
||||
* @returns number of bytes read, or -1 if error
|
||||
*/
|
||||
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
|
||||
|
||||
/**
|
||||
* Writes to given filehandle.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param buf the data to write
|
||||
* @param len how much to write
|
||||
* @returns number of bytes written, or -1 if error
|
||||
*/
|
||||
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
|
||||
|
||||
/**
|
||||
* Moves the read/write file offset. Resulting offset is returned or negative if error.
|
||||
* lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param offs how much/where to move the offset
|
||||
* @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes
|
||||
* if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset
|
||||
* if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative
|
||||
*/
|
||||
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence);
|
||||
|
||||
/**
|
||||
* Removes a file by path
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the file to remove
|
||||
*/
|
||||
s32_t SPIFFS_remove(spiffs *fs, const char *path);
|
||||
|
||||
/**
|
||||
* Removes a file by filehandle
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to remove
|
||||
*/
|
||||
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Gets file status by path
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the file to stat
|
||||
* @param s the stat struct to populate
|
||||
*/
|
||||
s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s);
|
||||
|
||||
/**
|
||||
* Gets file status by filehandle
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to stat
|
||||
* @param s the stat struct to populate
|
||||
*/
|
||||
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s);
|
||||
|
||||
/**
|
||||
* Flushes all pending write operations from cache for given file
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to flush
|
||||
*/
|
||||
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Closes a filehandle. If there are pending write operations, these are finalized before closing.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to close
|
||||
*/
|
||||
s32_t SPIFFS_close(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Renames a file
|
||||
* @param fs the file system struct
|
||||
* @param old path of file to rename
|
||||
* @param newPath new path of file
|
||||
*/
|
||||
s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath);
|
||||
|
||||
#if SPIFFS_OBJ_META_LEN
|
||||
/**
|
||||
* Updates file's metadata
|
||||
* @param fs the file system struct
|
||||
* @param path path to the file
|
||||
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
|
||||
*/
|
||||
s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta);
|
||||
|
||||
/**
|
||||
* Updates file's metadata
|
||||
* @param fs the file system struct
|
||||
* @param fh file handle of the file
|
||||
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
|
||||
*/
|
||||
s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns last error of last file operation.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_errno(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Clears last error.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
void SPIFFS_clearerr(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Opens a directory stream corresponding to the given name.
|
||||
* The stream is positioned at the first entry in the directory.
|
||||
* On hydrogen builds the name argument is ignored as hydrogen builds always correspond
|
||||
* to a flat file structure - no directories.
|
||||
* @param fs the file system struct
|
||||
* @param name the name of the directory
|
||||
* @param d pointer the directory stream to be populated
|
||||
*/
|
||||
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d);
|
||||
|
||||
/**
|
||||
* Closes a directory stream
|
||||
* @param d the directory stream to close
|
||||
*/
|
||||
s32_t SPIFFS_closedir(spiffs_DIR *d);
|
||||
|
||||
/**
|
||||
* Reads a directory into given spifs_dirent struct.
|
||||
* @param d pointer to the directory stream
|
||||
* @param e the dirent struct to be populated
|
||||
* @returns null if error or end of stream, else given dirent is returned
|
||||
*/
|
||||
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e);
|
||||
|
||||
/**
|
||||
* Runs a consistency check on given filesystem.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_check(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Returns number of total bytes available and number of used bytes.
|
||||
* This is an estimation, and depends on if there a many files with little
|
||||
* data or few files with much data.
|
||||
* NB: If used number of bytes exceeds total bytes, a SPIFFS_check should
|
||||
* run. This indicates a power loss in midst of things. In worst case
|
||||
* (repeated powerlosses in mending or gc) you might have to delete some files.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param total total number of bytes in filesystem
|
||||
* @param used used number of bytes in filesystem
|
||||
*/
|
||||
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used);
|
||||
|
||||
/**
|
||||
* Formats the entire file system. All data will be lost.
|
||||
* The filesystem must not be mounted when calling this.
|
||||
*
|
||||
* NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount
|
||||
* MUST be called prior to formatting in order to configure the filesystem.
|
||||
* If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling
|
||||
* SPIFFS_format.
|
||||
* If SPIFFS_mount fails, SPIFFS_format can be called directly without calling
|
||||
* SPIFFS_unmount first.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_format(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Returns nonzero if spiffs is mounted, or zero if unmounted.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
u8_t SPIFFS_mounted(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Tries to find a block where most or all pages are deleted, and erase that
|
||||
* block if found. Does not care for wear levelling. Will not move pages
|
||||
* around.
|
||||
* If parameter max_free_pages are set to 0, only blocks with only deleted
|
||||
* pages will be selected.
|
||||
*
|
||||
* NB: the garbage collector is automatically called when spiffs needs free
|
||||
* pages. The reason for this function is to give possibility to do background
|
||||
* tidying when user knows the system is idle.
|
||||
*
|
||||
* Use with care.
|
||||
*
|
||||
* Setting max_free_pages to anything larger than zero will eventually wear
|
||||
* flash more as a block containing free pages can be erased.
|
||||
*
|
||||
* Will set err_no to SPIFFS_OK if a block was found and erased,
|
||||
* SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found,
|
||||
* or other error.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param max_free_pages maximum number allowed free pages in block
|
||||
*/
|
||||
s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages);
|
||||
|
||||
/**
|
||||
* Will try to make room for given amount of bytes in the filesystem by moving
|
||||
* pages and erasing blocks.
|
||||
* If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If
|
||||
* there already is this amount (or more) of free space, SPIFFS_gc will
|
||||
* silently return. It is recommended to call SPIFFS_info before invoking
|
||||
* this method in order to determine what amount of bytes to give.
|
||||
*
|
||||
* NB: the garbage collector is automatically called when spiffs needs free
|
||||
* pages. The reason for this function is to give possibility to do background
|
||||
* tidying when user knows the system is idle.
|
||||
*
|
||||
* Use with care.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param size amount of bytes that should be freed
|
||||
*/
|
||||
s32_t SPIFFS_gc(spiffs *fs, u32_t size);
|
||||
|
||||
/**
|
||||
* Check if EOF reached.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to check
|
||||
*/
|
||||
s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Get position in file.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to check
|
||||
*/
|
||||
s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Registers a callback function that keeps track on operations on file
|
||||
* headers. Do note, that this callback is called from within internal spiffs
|
||||
* mechanisms. Any operations on the actual file system being callbacked from
|
||||
* in this callback will mess things up for sure - do not do this.
|
||||
* This can be used to track where files are and move around during garbage
|
||||
* collection, which in turn can be used to build location tables in ram.
|
||||
* Used in conjuction with SPIFFS_open_by_page this may improve performance
|
||||
* when opening a lot of files.
|
||||
* Must be invoked after mount.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param cb_func the callback on file operations
|
||||
*/
|
||||
s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func);
|
||||
|
||||
#if SPIFFS_IX_MAP
|
||||
|
||||
/**
|
||||
* Maps the first level index lookup to a given memory map.
|
||||
* This will make reading big files faster, as the memory map will be used for
|
||||
* looking up data pages instead of searching for the indices on the physical
|
||||
* medium. When mapping, all affected indicies are found and the information is
|
||||
* copied to the array.
|
||||
* Whole file or only parts of it may be mapped. The index map will cover file
|
||||
* contents from argument offset until and including arguments (offset+len).
|
||||
* It is valid to map a longer range than the current file size. The map will
|
||||
* then be populated when the file grows.
|
||||
* On garbage collections and file data page movements, the map array will be
|
||||
* automatically updated. Do not tamper with the map array, as this contains
|
||||
* the references to the data pages. Modifying it from outside will corrupt any
|
||||
* future readings using this file descriptor.
|
||||
* The map will no longer be used when the file descriptor closed or the file
|
||||
* is unmapped.
|
||||
* This can be useful to get faster and more deterministic timing when reading
|
||||
* large files, or when seeking and reading a lot within a file.
|
||||
* @param fs the file system struct
|
||||
* @param fh the file handle of the file to map
|
||||
* @param map a spiffs_ix_map struct, describing the index map
|
||||
* @param offset absolute file offset where to start the index map
|
||||
* @param len length of the mapping in actual file bytes
|
||||
* @param map_buf the array buffer for the look up data - number of required
|
||||
* elements in the array can be derived from function
|
||||
* SPIFFS_bytes_to_ix_map_entries given the length
|
||||
*/
|
||||
s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map,
|
||||
u32_t offset, u32_t len, spiffs_page_ix *map_buf);
|
||||
|
||||
/**
|
||||
* Unmaps the index lookup from this filehandle. All future readings will
|
||||
* proceed as normal, requiring reading of the first level indices from
|
||||
* physical media.
|
||||
* The map and map buffer given in function SPIFFS_ix_map will no longer be
|
||||
* referenced by spiffs.
|
||||
* It is not strictly necessary to unmap a file before closing it, as closing
|
||||
* a file will automatically unmap it.
|
||||
* @param fs the file system struct
|
||||
* @param fh the file handle of the file to unmap
|
||||
*/
|
||||
s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Moves the offset for the index map given in function SPIFFS_ix_map. Parts or
|
||||
* all of the map buffer will repopulated.
|
||||
* @param fs the file system struct
|
||||
* @param fh the mapped file handle of the file to remap
|
||||
* @param offset new absolute file offset where to start the index map
|
||||
*/
|
||||
s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs);
|
||||
|
||||
/**
|
||||
* Utility function to get number of spiffs_page_ix entries a map buffer must
|
||||
* contain on order to map given amount of file data in bytes.
|
||||
* See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes.
|
||||
* @param fs the file system struct
|
||||
* @param bytes number of file data bytes to map
|
||||
* @return needed number of elements in a spiffs_page_ix array needed to
|
||||
* map given amount of bytes in a file
|
||||
*/
|
||||
s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes);
|
||||
|
||||
/**
|
||||
* Utility function to amount of file data bytes that can be mapped when
|
||||
* mapping a file with buffer having given number of spiffs_page_ix entries.
|
||||
* See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries.
|
||||
* @param fs the file system struct
|
||||
* @param map_page_ix_entries number of entries in a spiffs_page_ix array
|
||||
* @return amount of file data in bytes that can be mapped given a map
|
||||
* buffer having given amount of spiffs_page_ix entries
|
||||
*/
|
||||
s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries);
|
||||
|
||||
#endif // SPIFFS_IX_MAP
|
||||
|
||||
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
/**
|
||||
* Prints out a visualization of the filesystem.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_vis(spiffs *fs);
|
||||
#endif
|
||||
|
||||
#if SPIFFS_BUFFER_HELP
|
||||
/**
|
||||
* Returns number of bytes needed for the filedescriptor buffer given
|
||||
* amount of file descriptors.
|
||||
*/
|
||||
u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
/**
|
||||
* Returns number of bytes needed for the cache buffer given
|
||||
* amount of cache pages.
|
||||
*/
|
||||
u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SPIFFS_H_ */
|
||||
|
|
@ -1,319 +0,0 @@
|
|||
/*
|
||||
* spiffs_cache.c
|
||||
*
|
||||
* Created on: Jun 23, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
|
||||
// returns cached page for give page index, or null if no such cached page
|
||||
static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) {
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0;
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->cpage_use_map & (1<<i)) &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
|
||||
cp->pix == pix ) {
|
||||
//SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix);
|
||||
cp->last_access = cache->last_access;
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
//SPIFFS_CACHE_DBG("CACHE_GET: no cache for "_SPIPRIpg"\n", pix);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// frees cached page
|
||||
static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix);
|
||||
if (cache->cpage_use_map & (1<<ix)) {
|
||||
if (write_back &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, ix);
|
||||
SPIFFS_CACHE_DBG("CACHE_FREE: write cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix);
|
||||
res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
|
||||
}
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
|
||||
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix);
|
||||
}
|
||||
cache->cpage_use_map &= ~(1 << ix);
|
||||
cp->flags = 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// removes the oldest accessed cached page
|
||||
static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) {
|
||||
// at least one free cpage
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
// all busy, scan thru all to find the cpage which has oldest access
|
||||
int i;
|
||||
int cand_ix = -1;
|
||||
u32_t oldest_val = 0;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->last_access - cp->last_access) > oldest_val &&
|
||||
(cp->flags & flag_mask) == flags) {
|
||||
oldest_val = cache->last_access - cp->last_access;
|
||||
cand_ix = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (cand_ix >= 0) {
|
||||
res = spiffs_cache_page_free(fs, cand_ix, 1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// allocates a new cached page and returns it, or null if all cache pages are busy
|
||||
static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) {
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
if (cache->cpage_use_map == 0xffffffff) {
|
||||
// out of cache memory
|
||||
return 0;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
if ((cache->cpage_use_map & (1<<i)) == 0) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
cache->cpage_use_map |= (1<<i);
|
||||
cp->last_access = cache->last_access;
|
||||
//SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i);
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
// out of cache entries
|
||||
return 0;
|
||||
}
|
||||
|
||||
// drops the cache page for give page index
|
||||
void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) {
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
|
||||
if (cp) {
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
// reads from spi flash or the cache
|
||||
s32_t spiffs_phys_rd(
|
||||
spiffs *fs,
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *dst) {
|
||||
(void)fh;
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr));
|
||||
cache->last_access++;
|
||||
if (cp) {
|
||||
// we've already got one, you see
|
||||
#if SPIFFS_CACHE_STATS
|
||||
fs->cache_hits++;
|
||||
#endif
|
||||
cp->last_access = cache->last_access;
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
|
||||
_SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
|
||||
} else {
|
||||
if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
|
||||
// for second layer lookup functions, we do not cache in order to prevent shredding
|
||||
return SPIFFS_HAL_READ(fs, addr, len, dst);
|
||||
}
|
||||
#if SPIFFS_CACHE_STATS
|
||||
fs->cache_misses++;
|
||||
#endif
|
||||
// this operation will always free one cache page (unless all already free),
|
||||
// the result code stems from the write operation of the possibly freed cache page
|
||||
res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
|
||||
|
||||
cp = spiffs_cache_page_allocate(fs);
|
||||
if (cp) {
|
||||
cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
|
||||
cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
|
||||
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for pix "_SPIPRIpg "\n", cp->ix, cp->pix);
|
||||
|
||||
s32_t res2 = SPIFFS_HAL_READ(fs,
|
||||
addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs),
|
||||
spiffs_get_cache_page(fs, cache, cp->ix));
|
||||
if (res2 != SPIFFS_OK) {
|
||||
// honor read failure before possible write failure (bad idea?)
|
||||
res = res2;
|
||||
}
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
|
||||
_SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
|
||||
} else {
|
||||
// this will never happen, last resort for sake of symmetry
|
||||
s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst);
|
||||
if (res2 != SPIFFS_OK) {
|
||||
// honor read failure before possible write failure (bad idea?)
|
||||
res = res2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// writes to spi flash and/or the cache
|
||||
s32_t spiffs_phys_wr(
|
||||
spiffs *fs,
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *src) {
|
||||
(void)fh;
|
||||
spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
|
||||
|
||||
if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) {
|
||||
// have a cache page
|
||||
// copy in data to cache page
|
||||
|
||||
if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE &&
|
||||
(op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) {
|
||||
// page is being deleted, wipe from cache - unless it is a lookup page
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
return SPIFFS_HAL_WRITE(fs, addr, len, src);
|
||||
}
|
||||
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
|
||||
_SPIFFS_MEMCPY(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len);
|
||||
|
||||
cache->last_access++;
|
||||
cp->last_access = cache->last_access;
|
||||
|
||||
if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) {
|
||||
// page is being updated, no write-cache, just pass thru
|
||||
return SPIFFS_HAL_WRITE(fs, addr, len, src);
|
||||
} else {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
} else {
|
||||
// no cache page, no write cache - just write thru
|
||||
return SPIFFS_HAL_WRITE(fs, addr, len, src);
|
||||
}
|
||||
}
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
// returns the cache page that this fd refers, or null if no cache page
|
||||
spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) {
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) {
|
||||
// all cpages free, no cpage cannot be assigned to obj_id
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->cpage_use_map & (1<<i)) &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) &&
|
||||
cp->obj_id == fd->obj_id) {
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// allocates a new cache page and refers this to given fd - flushes an old cache
|
||||
// page if all cache is busy
|
||||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) {
|
||||
// before this function is called, it is ensured that there is no already existing
|
||||
// cache page with same object id
|
||||
spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_allocate(fs);
|
||||
if (cp == 0) {
|
||||
// could not get cache page
|
||||
return 0;
|
||||
}
|
||||
|
||||
cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR;
|
||||
cp->obj_id = fd->obj_id;
|
||||
fd->cache_page = cp;
|
||||
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for fd "_SPIPRIfd ":"_SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id);
|
||||
return cp;
|
||||
}
|
||||
|
||||
// unrefers all fds that this cache page refers to and releases the cache page
|
||||
void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) {
|
||||
if (cp == 0) return;
|
||||
u32_t i;
|
||||
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
|
||||
for (i = 0; i < fs->fd_count; i++) {
|
||||
spiffs_fd *cur_fd = &fds[i];
|
||||
if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) {
|
||||
cur_fd->cache_page = 0;
|
||||
}
|
||||
}
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
|
||||
cp->obj_id = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// initializes the cache
|
||||
void spiffs_cache_init(spiffs *fs) {
|
||||
if (fs->cache == 0) return;
|
||||
u32_t sz = fs->cache_size;
|
||||
u32_t cache_mask = 0;
|
||||
int i;
|
||||
int cache_entries =
|
||||
(sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs));
|
||||
if (cache_entries <= 0) return;
|
||||
|
||||
for (i = 0; i < cache_entries; i++) {
|
||||
cache_mask <<= 1;
|
||||
cache_mask |= 1;
|
||||
}
|
||||
|
||||
spiffs_cache cache;
|
||||
memset(&cache, 0, sizeof(spiffs_cache));
|
||||
cache.cpage_count = cache_entries;
|
||||
cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache));
|
||||
|
||||
cache.cpage_use_map = 0xffffffff;
|
||||
cache.cpage_use_mask = cache_mask;
|
||||
_SPIFFS_MEMCPY(fs->cache, &cache, sizeof(spiffs_cache));
|
||||
|
||||
spiffs_cache *c = spiffs_get_cache(fs);
|
||||
|
||||
memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs));
|
||||
|
||||
c->cpage_use_map &= ~(c->cpage_use_mask);
|
||||
for (i = 0; i < cache.cpage_count; i++) {
|
||||
spiffs_get_cache_page_hdr(fs, c, i)->ix = i;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SPIFFS_CACHE
|
||||
|
|
@ -1,995 +0,0 @@
|
|||
/*
|
||||
* spiffs_check.c
|
||||
*
|
||||
* Contains functionality for checking file system consistency
|
||||
* and mending problems.
|
||||
* Three levels of consistency checks are implemented:
|
||||
*
|
||||
* Look up consistency
|
||||
* Checks if indices in lookup pages are coherent with page headers
|
||||
* Object index consistency
|
||||
* Checks if there are any orphaned object indices (missing object index headers).
|
||||
* If an object index is found but not its header, the object index is deleted.
|
||||
* This is critical for the following page consistency check.
|
||||
* Page consistency
|
||||
* Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed
|
||||
*
|
||||
*
|
||||
* Created on: Jul 7, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
#if !SPIFFS_READ_ONLY
|
||||
|
||||
#if SPIFFS_HAL_CALLBACK_EXTRA
|
||||
#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \
|
||||
do { \
|
||||
if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \
|
||||
do { \
|
||||
if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
//---------------------------------------
|
||||
// Look up consistency
|
||||
|
||||
// searches in the object indices and returns the referenced page index given
|
||||
// the object id and the data span index
|
||||
// destroys fs->lu_work
|
||||
static s32_t spiffs_object_get_data_page_index_reference(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix data_spix,
|
||||
spiffs_page_ix *pix,
|
||||
spiffs_page_ix *objix_pix) {
|
||||
s32_t res;
|
||||
|
||||
// calculate object index span index for given data page span index
|
||||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
|
||||
|
||||
// find obj index for obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// load obj index entry
|
||||
u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix);
|
||||
if (objix_spix == 0) {
|
||||
// get referenced page from object index header
|
||||
addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix);
|
||||
} else {
|
||||
// get referenced page from object index
|
||||
addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix);
|
||||
}
|
||||
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// copies page contents to a new page
|
||||
static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) {
|
||||
s32_t res;
|
||||
res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_phys_cpy(fs, 0,
|
||||
SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_DATA_PAGE_SIZE(fs));
|
||||
SPIFFS_CHECK_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// rewrites the object index for given object id and replaces the
|
||||
// data page index to a new page index
|
||||
static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) {
|
||||
s32_t res;
|
||||
spiffs_block_ix bix;
|
||||
int entry;
|
||||
spiffs_page_ix free_pix;
|
||||
obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
|
||||
|
||||
// find free entry
|
||||
res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
|
||||
|
||||
// calculate object index span index for given data page span index
|
||||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
|
||||
if (objix_spix == 0) {
|
||||
// calc index in index header
|
||||
entry = data_spix;
|
||||
} else {
|
||||
// calc entry in index
|
||||
entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix);
|
||||
|
||||
}
|
||||
// load index
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work;
|
||||
|
||||
// be ultra safe, double check header against provided data
|
||||
if (objix_p_hdr->obj_id != obj_id) {
|
||||
spiffs_page_delete(fs, free_pix);
|
||||
return SPIFFS_ERR_CHECK_OBJ_ID_MISM;
|
||||
}
|
||||
if (objix_p_hdr->span_ix != objix_spix) {
|
||||
spiffs_page_delete(fs, free_pix);
|
||||
return SPIFFS_ERR_CHECK_SPIX_MISM;
|
||||
}
|
||||
if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX |
|
||||
SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) !=
|
||||
(SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) {
|
||||
spiffs_page_delete(fs, free_pix);
|
||||
return SPIFFS_ERR_CHECK_FLAGS_BAD;
|
||||
}
|
||||
|
||||
// rewrite in mem
|
||||
if (objix_spix == 0) {
|
||||
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix;
|
||||
} else {
|
||||
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix;
|
||||
}
|
||||
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix),
|
||||
sizeof(spiffs_obj_id),
|
||||
(u8_t *)&obj_id);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_page_delete(fs, objix_pix);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// deletes an object just by marking object index header as deleted
|
||||
static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) {
|
||||
spiffs_page_ix objix_hdr_pix;
|
||||
s32_t res;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE;
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags),
|
||||
sizeof(u8_t),
|
||||
(u8_t *)&flags);
|
||||
return res;
|
||||
}
|
||||
|
||||
// validates the given look up entry
|
||||
static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr,
|
||||
spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) {
|
||||
(void)cur_block;
|
||||
(void)cur_entry;
|
||||
u8_t delete_page = 0;
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_ix objix_pix;
|
||||
spiffs_page_ix ref_pix;
|
||||
// check validity, take actions
|
||||
if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) ||
|
||||
((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) {
|
||||
// look up entry deleted / free but used in page header
|
||||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" deleted/free in lu but not on page\n", cur_pix);
|
||||
*reload_lu = 1;
|
||||
delete_page = 1;
|
||||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
|
||||
// header says data page
|
||||
// data page can be removed if not referenced by some object index
|
||||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (ref_pix == cur_pix) {
|
||||
// data page referenced by object index but deleted in lu
|
||||
// copy page to new place and re-write the object index to new place
|
||||
spiffs_page_ix new_pix;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: "_SPIPRIpg" rewritten to "_SPIPRIpg", affected objix_pix "_SPIPRIpg"\n", cur_pix, new_pix, objix_pix);
|
||||
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
|
||||
res = spiffs_page_delete(fs, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
|
||||
} else {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// header says index page
|
||||
// index page can be removed if other index with same obj_id and spanix is found
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no such index page found, check for a data page amongst page headers
|
||||
// lu cannot be trusted
|
||||
res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0);
|
||||
if (res == SPIFFS_OK) { // ignore other errors
|
||||
// got a data page also, assume lu corruption only, rewrite to new page
|
||||
spiffs_page_ix new_pix;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
}
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) {
|
||||
// look up entry used
|
||||
if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" differ in obj_id lu:"_SPIPRIid" ph:"_SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id);
|
||||
delete_page = 1;
|
||||
if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 ||
|
||||
(p_hdr->flags & SPIFFS_PH_FLAG_FINAL) ||
|
||||
(p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) {
|
||||
// page deleted or not finalized, just remove it
|
||||
} else {
|
||||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
|
||||
// if data page, check for reference to this page
|
||||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// if found, rewrite page with object id, update index, and delete current
|
||||
if (ref_pix == cur_pix) {
|
||||
spiffs_page_ix new_pix;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
|
||||
res = spiffs_page_delete(fs, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
|
||||
*reload_lu = 1;
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// else if index, check for other pages with both obj_id's and spanix
|
||||
spiffs_page_ix objix_pix_lu, objix_pix_ph;
|
||||
// see if other object index page exists for lookup obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_lu = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// see if other object index exists for page header obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_ph = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// if both obj_id's found, just delete current
|
||||
if (objix_pix_ph == 0 || objix_pix_lu == 0) {
|
||||
// otherwise try finding first corresponding data pages
|
||||
spiffs_page_ix data_pix_lu, data_pix_ph;
|
||||
// see if other data page exists for look up obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_lu = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// see if other data page exists for page header obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_ph = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
spiffs_page_header new_ph;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL);
|
||||
new_ph.span_ix = p_hdr->span_ix;
|
||||
spiffs_page_ix new_pix;
|
||||
if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) ||
|
||||
(objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) {
|
||||
// got a data page for page header obj id
|
||||
// rewrite as obj_id_ph
|
||||
new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid" to pix "_SPIPRIpg"\n", cur_pix, new_ph.obj_id, new_pix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
} else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) ||
|
||||
(objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) {
|
||||
// got a data page for look up obj id
|
||||
// rewrite as obj_id_lu
|
||||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid"\n", cur_pix, new_ph.obj_id);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
} else {
|
||||
// cannot safely do anything
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) ||
|
||||
((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) {
|
||||
SPIFFS_CHECK_DBG("LU: "_SPIPRIpg" lu/page index marking differ\n", cur_pix);
|
||||
spiffs_page_ix data_pix, objix_pix_d;
|
||||
// see if other data page exists for given obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
data_pix = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// see if other object index exists for given obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_d = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
delete_page = 1;
|
||||
// if other data page exists and object index exists, just delete page
|
||||
if (data_pix && objix_pix_d) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n");
|
||||
} else
|
||||
// if only data page exists, make this page index
|
||||
if (data_pix && objix_pix_d == 0) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n");
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix);
|
||||
spiffs_page_header new_ph;
|
||||
spiffs_page_ix new_pix;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX);
|
||||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
new_ph.span_ix = p_hdr->span_ix;
|
||||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header));
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else
|
||||
// if only index exists, make data page
|
||||
if (data_pix == 0 && objix_pix_d) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n");
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix);
|
||||
spiffs_page_header new_ph;
|
||||
spiffs_page_ix new_pix;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
|
||||
new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
new_ph.span_ix = p_hdr->span_ix;
|
||||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header));
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// if nothing exists, we cannot safely make a decision - delete
|
||||
}
|
||||
}
|
||||
else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) {
|
||||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy in lu but deleted on page\n", cur_pix);
|
||||
delete_page = 1;
|
||||
} else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) {
|
||||
SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy but not final\n", cur_pix);
|
||||
// page can be removed if not referenced by object index
|
||||
*reload_lu = 1;
|
||||
res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
delete_page = 1;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (ref_pix != cur_pix) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n");
|
||||
delete_page = 1;
|
||||
} else {
|
||||
// page referenced by object index but not final
|
||||
// just finalize
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n");
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL;
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags),
|
||||
sizeof(u8_t), (u8_t*)&flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_page) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry,
|
||||
const void *user_const_p, void *user_var_p) {
|
||||
(void)user_const_p;
|
||||
(void)user_var_p;
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
|
||||
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS,
|
||||
(cur_block * 256)/fs->block_count, 0);
|
||||
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
int reload_lu = 0;
|
||||
|
||||
res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (res == SPIFFS_OK) {
|
||||
return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// Scans all object look up. For each entry, corresponding page header is checked for validity.
|
||||
// If an object index header page is found, this is also checked
|
||||
s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) {
|
||||
(void)check_all_objects;
|
||||
s32_t res = SPIFFS_OK;
|
||||
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0);
|
||||
|
||||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0);
|
||||
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
|
||||
if (res != SPIFFS_OK) {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0);
|
||||
}
|
||||
|
||||
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
// Page consistency
|
||||
|
||||
// Scans all pages (except lu pages), reserves 4 bits in working memory for each page
|
||||
// bit 0: 0 == FREE|DELETED, 1 == USED
|
||||
// bit 1: 0 == UNREFERENCED, 1 == REFERENCED
|
||||
// bit 2: 0 == NOT_INDEX, 1 == INDEX
|
||||
// bit 3: unused
|
||||
// A consistent file system will have only pages being
|
||||
// * x000 free, unreferenced, not index
|
||||
// * x011 used, referenced only once, not index
|
||||
// * x101 used, unreferenced, index
|
||||
// The working memory might not fit all pages so several scans might be needed
|
||||
static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
|
||||
const u32_t bits = 4;
|
||||
const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits;
|
||||
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_ix pix_offset = 0;
|
||||
|
||||
// for each range of pages fitting into work memory
|
||||
while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) {
|
||||
// set this flag to abort all checks and rescan the page range
|
||||
u8_t restart = 0;
|
||||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
|
||||
spiffs_block_ix cur_block = 0;
|
||||
// build consistency bitmap for id range traversing all blocks
|
||||
while (!restart && cur_block < fs->block_count) {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS,
|
||||
(pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) +
|
||||
((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count),
|
||||
0);
|
||||
// traverse each page except for lookup pages
|
||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block;
|
||||
while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) {
|
||||
//if ((cur_pix & 0xff) == 0)
|
||||
// SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n",
|
||||
// cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count);
|
||||
|
||||
// read header
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan);
|
||||
const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits);
|
||||
const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits;
|
||||
|
||||
if (within_range &&
|
||||
(p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) {
|
||||
// used
|
||||
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0));
|
||||
}
|
||||
if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) &&
|
||||
(p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) &&
|
||||
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) {
|
||||
// found non-deleted index
|
||||
if (within_range) {
|
||||
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2));
|
||||
}
|
||||
|
||||
// load non-deleted index
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// traverse index for referenced pages
|
||||
spiffs_page_ix *object_page_index;
|
||||
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work;
|
||||
|
||||
int entries;
|
||||
int i;
|
||||
spiffs_span_ix data_spix_offset;
|
||||
if (p_hdr.span_ix == 0) {
|
||||
// object header page index
|
||||
entries = SPIFFS_OBJ_HDR_IX_LEN(fs);
|
||||
data_spix_offset = 0;
|
||||
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header));
|
||||
} else {
|
||||
// object page index
|
||||
entries = SPIFFS_OBJ_IX_LEN(fs);
|
||||
data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1);
|
||||
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix));
|
||||
}
|
||||
|
||||
// for all entries in index
|
||||
for (i = 0; !restart && i < entries; i++) {
|
||||
spiffs_page_ix rpix = object_page_index[i];
|
||||
u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan;
|
||||
|
||||
if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs))
|
||||
|| (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) {
|
||||
|
||||
// bad reference
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg"x bad pix / LU referenced from page "_SPIPRIpg"\n",
|
||||
rpix, cur_pix);
|
||||
// check for data page elsewhere
|
||||
spiffs_page_ix data_pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
|
||||
data_spix_offset + i, 0, &data_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
data_pix = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (data_pix == 0) {
|
||||
// if not, allocate free page
|
||||
spiffs_page_header new_ph;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
|
||||
new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
new_ph.span_ix = data_spix_offset + i;
|
||||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ "_SPIPRIpg"\n", data_pix);
|
||||
}
|
||||
// remap index
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix "_SPIPRIpg"\n", cur_pix);
|
||||
res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG,
|
||||
data_spix_offset + i, data_pix, cur_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend - delete object\n", res);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0);
|
||||
// delete file
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
} else {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
|
||||
} else if (rpix_within_range) {
|
||||
|
||||
// valid reference
|
||||
// read referenced page header
|
||||
spiffs_page_header rp_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// cross reference page header check
|
||||
if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) ||
|
||||
rp_hdr.span_ix != data_spix_offset + i ||
|
||||
(rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) !=
|
||||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) {
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" has inconsistent page header ix id/span:"_SPIPRIid"/"_SPIPRIsp", ref id/span:"_SPIPRIid"/"_SPIPRIsp" flags:"_SPIPRIfl"\n",
|
||||
rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i,
|
||||
rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags);
|
||||
// try finding correct page
|
||||
spiffs_page_ix data_pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
|
||||
data_spix_offset + i, rpix, &data_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
data_pix = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (data_pix == 0) {
|
||||
// not found, this index is badly borked
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id "_SPIPRIid"\n", p_hdr.obj_id);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
break;
|
||||
} else {
|
||||
// found it, so rewrite index
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix "_SPIPRIpg", rewrite ix pix "_SPIPRIpg" id "_SPIPRIid"\n",
|
||||
data_pix, cur_pix, p_hdr.obj_id);
|
||||
res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
} else {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// mark rpix as referenced
|
||||
const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits);
|
||||
const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits;
|
||||
if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) {
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" multiple referenced from page "_SPIPRIpg"\n",
|
||||
rpix, cur_pix);
|
||||
// Here, we should have fixed all broken references - getting this means there
|
||||
// must be multiple files with same object id. Only solution is to delete
|
||||
// the object which is referring to this page
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: removing object "_SPIPRIid" and page "_SPIPRIpg"\n",
|
||||
p_hdr.obj_id, cur_pix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// extra precaution, delete this page also
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
}
|
||||
fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1));
|
||||
}
|
||||
}
|
||||
} // for all index entries
|
||||
} // found index
|
||||
|
||||
// next page
|
||||
cur_pix++;
|
||||
}
|
||||
// next block
|
||||
cur_block++;
|
||||
}
|
||||
// check consistency bitmap
|
||||
if (!restart) {
|
||||
spiffs_page_ix objix_pix;
|
||||
spiffs_page_ix rpix;
|
||||
|
||||
u32_t byte_ix;
|
||||
u8_t bit_ix;
|
||||
for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) {
|
||||
for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) {
|
||||
u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7;
|
||||
spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix;
|
||||
|
||||
// 000 ok - free, unreferenced, not index
|
||||
|
||||
if (bitmask == 0x1) {
|
||||
|
||||
// 001
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, UNREFERENCED, not index\n", cur_pix);
|
||||
|
||||
u8_t rewrite_ix_to_this = 0;
|
||||
u8_t delete_page = 0;
|
||||
// check corresponding object index entry
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix,
|
||||
&rpix, &objix_pix);
|
||||
if (res == SPIFFS_OK) {
|
||||
if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) {
|
||||
// pointing to a bad page altogether, rewrite index to this
|
||||
rewrite_ix_to_this = 1;
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref is bad: "_SPIPRIpg", rewrite to this "_SPIPRIpg"\n", rpix, cur_pix);
|
||||
} else {
|
||||
// pointing to something else, check what
|
||||
spiffs_page_header rp_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) &&
|
||||
((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) ==
|
||||
(SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) {
|
||||
// pointing to something else valid, just delete this page then
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: "_SPIPRIpg", delete this "_SPIPRIpg"\n", rpix, cur_pix);
|
||||
delete_page = 1;
|
||||
} else {
|
||||
// pointing to something weird, update index to point to this page instead
|
||||
if (rpix != cur_pix) {
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref is weird: "_SPIPRIpg" %s%s%s%s, rewrite this "_SPIPRIpg"\n", rpix,
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ",
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ",
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "",
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "",
|
||||
cur_pix);
|
||||
rewrite_ix_to_this = 1;
|
||||
} else {
|
||||
// should not happen, destined for fubar
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete "_SPIPRIpg"\n", cur_pix);
|
||||
delete_page = 1;
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
|
||||
if (rewrite_ix_to_this) {
|
||||
// if pointing to invalid page, redirect index to this page
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id "_SPIPRIid" data spix "_SPIPRIsp" to point to this pix: "_SPIPRIpg"\n",
|
||||
p_hdr.obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
} else {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
continue;
|
||||
} else if (delete_page) {
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
if (bitmask == 0x2) {
|
||||
|
||||
// 010
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, not index\n", cur_pix);
|
||||
|
||||
// no op, this should be taken care of when checking valid references
|
||||
}
|
||||
|
||||
// 011 ok - busy, referenced, not index
|
||||
|
||||
if (bitmask == 0x4) {
|
||||
|
||||
// 100
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, unreferenced, INDEX\n", cur_pix);
|
||||
|
||||
// this should never happen, major fubar
|
||||
}
|
||||
|
||||
// 101 ok - busy, unreferenced, index
|
||||
|
||||
if (bitmask == 0x6) {
|
||||
|
||||
// 110
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, INDEX\n", cur_pix);
|
||||
|
||||
// no op, this should be taken care of when checking valid references
|
||||
}
|
||||
if (bitmask == 0x7) {
|
||||
|
||||
// 111
|
||||
SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, REFERENCED, INDEX\n", cur_pix);
|
||||
|
||||
// no op, this should be taken care of when checking valid references
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SPIFFS_CHECK_DBG("PA: processed "_SPIPRIpg", restart "_SPIPRIi"\n", pix_offset, restart);
|
||||
// next page range
|
||||
if (!restart) {
|
||||
pix_offset += pages_per_scan;
|
||||
}
|
||||
} // while page range not reached end
|
||||
return res;
|
||||
}
|
||||
|
||||
// Checks consistency amongst all pages and fixes irregularities
|
||||
s32_t spiffs_page_consistency_check(spiffs *fs) {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0);
|
||||
s32_t res = spiffs_page_consistency_check_i(fs);
|
||||
if (res != SPIFFS_OK) {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0);
|
||||
}
|
||||
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
// Object index consistency
|
||||
|
||||
// searches for given object id in temporary object id index,
|
||||
// returns the index or -1
|
||||
static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) {
|
||||
u32_t i;
|
||||
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
|
||||
obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) {
|
||||
if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block,
|
||||
int cur_entry, const void *user_const_p, void *user_var_p) {
|
||||
(void)user_const_p;
|
||||
s32_t res_c = SPIFFS_VIS_COUNTINUE;
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t *log_ix = (u32_t*)user_var_p;
|
||||
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
|
||||
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS,
|
||||
(cur_block * 256)/fs->block_count, 0);
|
||||
|
||||
if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
|
||||
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (p_hdr.span_ix == 0 &&
|
||||
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
|
||||
(SPIFFS_PH_FLAG_DELET)) {
|
||||
SPIFFS_CHECK_DBG("IX: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" header not fully deleted - deleting\n",
|
||||
cur_pix, obj_id, p_hdr.span_ix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
return res_c;
|
||||
}
|
||||
|
||||
if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
|
||||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
|
||||
return res_c;
|
||||
}
|
||||
|
||||
if (p_hdr.span_ix == 0) {
|
||||
// objix header page, register objid as reachable
|
||||
int r = spiffs_object_index_search(fs, obj_id);
|
||||
if (r == -1) {
|
||||
// not registered, do it
|
||||
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
(*log_ix)++;
|
||||
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) {
|
||||
*log_ix = 0;
|
||||
}
|
||||
}
|
||||
} else { // span index
|
||||
// objix page, see if header can be found
|
||||
int r = spiffs_object_index_search(fs, obj_id);
|
||||
u8_t delete = 0;
|
||||
if (r == -1) {
|
||||
// not in temporary index, try finding it
|
||||
spiffs_page_ix objix_hdr_pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix);
|
||||
res_c = SPIFFS_VIS_COUNTINUE_RELOAD;
|
||||
if (res == SPIFFS_OK) {
|
||||
// found, register as reachable
|
||||
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
} else if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// not found, register as unreachable
|
||||
delete = 1;
|
||||
obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
(*log_ix)++;
|
||||
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) {
|
||||
*log_ix = 0;
|
||||
}
|
||||
} else {
|
||||
// in temporary index, check reachable flag
|
||||
if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
// registered as unreachable
|
||||
delete = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (delete) {
|
||||
SPIFFS_CHECK_DBG("IX: FIXUP: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" is orphan index - deleting\n",
|
||||
cur_pix, obj_id, p_hdr.span_ix);
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
} // span index
|
||||
} // valid object index id
|
||||
|
||||
return res_c;
|
||||
}
|
||||
|
||||
// Removes orphaned and partially deleted index pages.
|
||||
// Scans for index pages. When an index page is found, corresponding index header is searched for.
|
||||
// If no such page exists, the index page cannot be reached as no index header exists and must be
|
||||
// deleted.
|
||||
s32_t spiffs_object_index_consistency_check(spiffs *fs) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
// impl note:
|
||||
// fs->work is used for a temporary object index memory, listing found object ids and
|
||||
// indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit.
|
||||
// In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate
|
||||
// a reachable/unreachable object id.
|
||||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
u32_t obj_id_log_ix = 0;
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0);
|
||||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix,
|
||||
0, 0);
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
if (res != SPIFFS_OK) {
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0);
|
||||
}
|
||||
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // !SPIFFS_READ_ONLY
|
||||
|
|
@ -1,606 +0,0 @@
|
|||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
#if !SPIFFS_READ_ONLY
|
||||
|
||||
// Erases a logical block and updates the erase counter.
|
||||
// If cache is enabled, all pages that might be cached in this block
|
||||
// is dropped.
|
||||
static s32_t spiffs_gc_erase_block(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix) {
|
||||
s32_t res;
|
||||
|
||||
SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix);
|
||||
res = spiffs_erase_block(fs, bix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
{
|
||||
u32_t i;
|
||||
for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) {
|
||||
spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
// Searches for blocks where all entries are deleted - if one is found,
|
||||
// the block is erased. Compared to the non-quick gc, the quick one ensures
|
||||
// that no updates are needed on existing objects on pages that are erased.
|
||||
s32_t spiffs_gc_quick(
|
||||
spiffs *fs, u16_t max_free_pages) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
u32_t cur_block_addr = 0;
|
||||
int cur_entry = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
|
||||
SPIFFS_GC_DBG("gc_quick: running\n");
|
||||
#if SPIFFS_GC_STATS
|
||||
fs->stats_gc_runs++;
|
||||
#endif
|
||||
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
|
||||
// find fully deleted blocks
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && blocks--) {
|
||||
u16_t deleted_pages_in_block = 0;
|
||||
u16_t free_pages_in_block = 0;
|
||||
|
||||
int obj_lookup_page = 0;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page &&
|
||||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
deleted_pages_in_block++;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// kill scan, go for next block
|
||||
free_pages_in_block++;
|
||||
if (free_pages_in_block > max_free_pages) {
|
||||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// kill scan, go for next block
|
||||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
if (res == 1) res = SPIFFS_OK;
|
||||
|
||||
if (res == SPIFFS_OK &&
|
||||
deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) &&
|
||||
free_pages_in_block <= max_free_pages) {
|
||||
// found a fully deleted block
|
||||
fs->stats_p_deleted -= deleted_pages_in_block;
|
||||
res = spiffs_gc_erase_block(fs, cur_block);
|
||||
return res;
|
||||
}
|
||||
|
||||
cur_entry = 0;
|
||||
cur_block++;
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
if (res == SPIFFS_OK) {
|
||||
res = SPIFFS_ERR_NO_DELETED_BLOCKS;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Checks if garbage collecting is necessary. If so a candidate block is found,
|
||||
// cleansed and erased
|
||||
s32_t spiffs_gc_check(
|
||||
spiffs *fs,
|
||||
u32_t len) {
|
||||
s32_t res;
|
||||
s32_t free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
int tries = 0;
|
||||
|
||||
if (fs->free_blocks > 3 &&
|
||||
(s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
|
||||
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
|
||||
// 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);
|
||||
// return SPIFFS_ERR_FULL;
|
||||
// }
|
||||
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
|
||||
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);
|
||||
return SPIFFS_ERR_FULL;
|
||||
}
|
||||
|
||||
do {
|
||||
SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n",
|
||||
tries,
|
||||
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
|
||||
len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
|
||||
spiffs_block_ix *cands;
|
||||
int count;
|
||||
spiffs_block_ix cand;
|
||||
s32_t prev_free_pages = free_pages;
|
||||
// if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
|
||||
res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (count == 0) {
|
||||
SPIFFS_GC_DBG("gc_check: no candidates, return\n");
|
||||
return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL;
|
||||
}
|
||||
#if SPIFFS_GC_STATS
|
||||
fs->stats_gc_runs++;
|
||||
#endif
|
||||
cand = cands[0];
|
||||
fs->cleaning = 1;
|
||||
//SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand);
|
||||
res = spiffs_gc_clean(fs, cand);
|
||||
fs->cleaning = 0;
|
||||
if (res < 0) {
|
||||
SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
|
||||
} else {
|
||||
SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_gc_erase_page_stats(fs, cand);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_gc_erase_block(fs, cand);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
|
||||
if (prev_free_pages <= 0 && prev_free_pages == free_pages) {
|
||||
// abort early to reduce wear, at least tried once
|
||||
SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
} while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
|
||||
(s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
|
||||
free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
|
||||
res = SPIFFS_ERR_FULL;
|
||||
}
|
||||
|
||||
SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n",
|
||||
fs->stats_p_allocated + fs->stats_p_deleted,
|
||||
fs->free_blocks, free_pages, tries, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Updates page statistics for a block that is about to be erased
|
||||
s32_t spiffs_gc_erase_page_stats(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
int obj_lookup_page = 0;
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
u32_t dele = 0;
|
||||
u32_t allo = 0;
|
||||
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
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);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
dele++;
|
||||
} else {
|
||||
allo++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele);
|
||||
fs->stats_p_allocated -= allo;
|
||||
fs->stats_p_deleted -= dele;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Finds block candidates to erase
|
||||
s32_t spiffs_gc_find_candidate(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix **block_candidates,
|
||||
int *candidate_count,
|
||||
char fs_crammed) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
u32_t cur_block_addr = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
|
||||
// using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
|
||||
int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t)));
|
||||
*candidate_count = 0;
|
||||
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
|
||||
// divide up work area into block indices and scores
|
||||
spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
|
||||
s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
|
||||
|
||||
// align cand_scores on s32_t boundary
|
||||
cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1));
|
||||
|
||||
*block_candidates = cand_blocks;
|
||||
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && blocks--) {
|
||||
u16_t deleted_pages_in_block = 0;
|
||||
u16_t used_pages_in_block = 0;
|
||||
|
||||
int obj_lookup_page = 0;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page &&
|
||||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// when a free entry is encountered, scan logic ensures that all following entries are free also
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
deleted_pages_in_block++;
|
||||
} else {
|
||||
used_pages_in_block++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
if (res == 1) res = SPIFFS_OK;
|
||||
|
||||
// calculate score and insert into candidate table
|
||||
// stoneage sort, but probably not so many blocks
|
||||
if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) {
|
||||
// read erase count
|
||||
spiffs_obj_id erase_count;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
|
||||
SPIFFS_ERASE_COUNT_PADDR(fs, cur_block),
|
||||
sizeof(spiffs_obj_id), (u8_t *)&erase_count);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
spiffs_obj_id erase_age;
|
||||
if (fs->max_erase_count > erase_count) {
|
||||
erase_age = fs->max_erase_count - erase_count;
|
||||
} else {
|
||||
erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count);
|
||||
}
|
||||
|
||||
s32_t score =
|
||||
deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET +
|
||||
used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
|
||||
erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
|
||||
int cand_ix = 0;
|
||||
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);
|
||||
while (cand_ix < max_candidates) {
|
||||
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
} else if (cand_scores[cand_ix] < score) {
|
||||
int reorder_cand_ix = max_candidates - 2;
|
||||
while (reorder_cand_ix >= cand_ix) {
|
||||
cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
|
||||
cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
|
||||
reorder_cand_ix--;
|
||||
}
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
}
|
||||
cand_ix++;
|
||||
}
|
||||
(*candidate_count)++;
|
||||
}
|
||||
|
||||
cur_entry = 0;
|
||||
cur_block++;
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
FIND_OBJ_DATA,
|
||||
MOVE_OBJ_DATA,
|
||||
MOVE_OBJ_IX,
|
||||
FINISHED
|
||||
} spiffs_gc_clean_state;
|
||||
|
||||
typedef struct {
|
||||
spiffs_gc_clean_state state;
|
||||
spiffs_obj_id cur_obj_id;
|
||||
spiffs_span_ix cur_objix_spix;
|
||||
spiffs_page_ix cur_objix_pix;
|
||||
spiffs_page_ix cur_data_pix;
|
||||
int stored_scan_entry_index;
|
||||
u8_t obj_id_found;
|
||||
} spiffs_gc;
|
||||
|
||||
// Empties given block by moving all data into free pages of another block
|
||||
// Strategy:
|
||||
// loop:
|
||||
// scan object lookup for object data pages
|
||||
// for first found id, check spix and load corresponding object index page to memory
|
||||
// push object scan lookup entry index
|
||||
// rescan object lookup, find data pages with same id and referenced by same object index
|
||||
// move data page, update object index in memory
|
||||
// when reached end of lookup, store updated object index
|
||||
// pop object scan lookup entry index
|
||||
// repeat loop until end of object lookup
|
||||
// scan object lookup again for remaining object index pages, move to new page in other block
|
||||
//
|
||||
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
// this is the global localizer being pushed and popped
|
||||
int cur_entry = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
spiffs_gc gc; // our stack frame/state
|
||||
spiffs_page_ix cur_pix = 0;
|
||||
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
|
||||
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
|
||||
|
||||
SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix);
|
||||
|
||||
memset(&gc, 0, sizeof(spiffs_gc));
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
|
||||
if (fs->free_cursor_block_ix == bix) {
|
||||
// move free cursor to next block, cannot use free pages from the block we want to clean
|
||||
fs->free_cursor_block_ix = (bix+1)%fs->block_count;
|
||||
fs->free_cursor_obj_lu_entry = 0;
|
||||
SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix);
|
||||
}
|
||||
|
||||
while (res == SPIFFS_OK && gc.state != FINISHED) {
|
||||
SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry);
|
||||
gc.obj_id_found = 0; // reset (to no found data page)
|
||||
|
||||
// scan through lookup pages
|
||||
int obj_lookup_page = cur_entry / entries_per_page;
|
||||
u8_t scan = 1;
|
||||
// check each object lookup page
|
||||
while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
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);
|
||||
// check each object lookup entry
|
||||
while (scan && res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
|
||||
|
||||
// act upon object id depending on gc state
|
||||
switch (gc.state) {
|
||||
case FIND_OBJ_DATA:
|
||||
// find a data page
|
||||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
||||
((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
|
||||
// found a data page, stop scanning and handle in switch case below
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id);
|
||||
gc.obj_id_found = 1;
|
||||
gc.cur_obj_id = obj_id;
|
||||
gc.cur_data_pix = cur_pix;
|
||||
scan = 0;
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA:
|
||||
// evacuate found data pages for corresponding object index we have in memory,
|
||||
// update memory representation
|
||||
if (obj_id == gc.cur_obj_id) {
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
|
||||
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
|
||||
} else {
|
||||
spiffs_page_ix new_data_pix;
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
|
||||
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);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// move wipes obj_lu, reload it
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
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);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// page is deleted but not deleted in lookup, scrap it -
|
||||
// might seem unnecessary as we will erase this block, but
|
||||
// we might get aborted
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
new_data_pix = SPIFFS_OBJ_ID_FREE;
|
||||
}
|
||||
// update memory representation of object index page with new data page
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
// update object index header page
|
||||
((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
|
||||
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));
|
||||
} else {
|
||||
// update object index page
|
||||
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_IX:
|
||||
// find and evacuate object index pages
|
||||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
||||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
// found an index object id
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix new_pix;
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
|
||||
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);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr,
|
||||
SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0);
|
||||
// move wipes obj_lu, reload it
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
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);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// page is deleted but not deleted in lookup, scrap it -
|
||||
// might seem unnecessary as we will erase this block, but
|
||||
// we might get aborted
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
if (res == SPIFFS_OK) {
|
||||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0,
|
||||
SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
|
||||
}
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
scan = 0;
|
||||
break;
|
||||
} // switch gc state
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop
|
||||
} // per object lookup page
|
||||
if (res != SPIFFS_OK) break;
|
||||
|
||||
// state finalization and switch
|
||||
switch (gc.state) {
|
||||
case FIND_OBJ_DATA:
|
||||
if (gc.obj_id_found) {
|
||||
// handle found data page -
|
||||
// find out corresponding obj ix page and load it to memory
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix objix_pix;
|
||||
gc.stored_scan_entry_index = cur_entry; // push cursor
|
||||
cur_entry = 0; // restart scan from start
|
||||
gc.state = MOVE_OBJ_DATA;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix);
|
||||
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);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// on borked systems we might get an ERR_NOT_FOUND here -
|
||||
// this is handled by simply deleting the page as it is not referenced
|
||||
// from anywhere
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix);
|
||||
res = spiffs_page_delete(fs, gc.cur_data_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// then we restore states and continue scanning for data pages
|
||||
cur_entry = gc.stored_scan_entry_index; // pop cursor
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
break; // done
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix);
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// cannot allow a gc if the presumed index in fact is no index, a
|
||||
// check must run or lot of data may be lost
|
||||
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
|
||||
gc.cur_objix_pix = objix_pix;
|
||||
} else {
|
||||
// no more data pages found, passed thru all block, start evacuating object indices
|
||||
gc.state = MOVE_OBJ_IX;
|
||||
cur_entry = 0; // restart entry scan index
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA: {
|
||||
// store modified objix (hdr) page residing in memory now that all
|
||||
// data pages belonging to this object index and residing in the block
|
||||
// we want to evacuate
|
||||
spiffs_page_ix new_objix_pix;
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
cur_entry = gc.stored_scan_entry_index; // pop cursor
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
// store object index header page
|
||||
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);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// store object index page
|
||||
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);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
|
||||
SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_IX:
|
||||
// scanned thru all block, no more object indices found - our work here is done
|
||||
gc.state = FINISHED;
|
||||
break;
|
||||
default:
|
||||
cur_entry = 0;
|
||||
break;
|
||||
} // switch gc.state
|
||||
SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state);
|
||||
} // while state != FINISHED
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // !SPIFFS_READ_ONLY
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,842 +0,0 @@
|
|||
/*
|
||||
* spiffs_nucleus.h
|
||||
*
|
||||
* Created on: Jun 15, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
/* SPIFFS layout
|
||||
*
|
||||
* spiffs is designed for following spi flash characteristics:
|
||||
* - only big areas of data (blocks) can be erased
|
||||
* - erasing resets all bits in a block to ones
|
||||
* - writing pulls ones to zeroes
|
||||
* - zeroes cannot be pulled to ones, without erase
|
||||
* - wear leveling
|
||||
*
|
||||
* spiffs is also meant to be run on embedded, memory constraint devices.
|
||||
*
|
||||
* Entire area is divided in blocks. Entire area is also divided in pages.
|
||||
* Each block contains same number of pages. A page cannot be erased, but a
|
||||
* block can be erased.
|
||||
*
|
||||
* Entire area must be block_size * x
|
||||
* page_size must be block_size / (2^y) where y > 2
|
||||
*
|
||||
* ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes
|
||||
*
|
||||
* BLOCK 0 PAGE 0 object lookup 1
|
||||
* PAGE 1 object lookup 2
|
||||
* ...
|
||||
* PAGE n-1 object lookup n
|
||||
* PAGE n object data 1
|
||||
* PAGE n+1 object data 2
|
||||
* ...
|
||||
* PAGE n+m-1 object data m
|
||||
*
|
||||
* BLOCK 1 PAGE n+m object lookup 1
|
||||
* PAGE n+m+1 object lookup 2
|
||||
* ...
|
||||
* PAGE 2n+m-1 object lookup n
|
||||
* PAGE 2n+m object data 1
|
||||
* PAGE 2n+m object data 2
|
||||
* ...
|
||||
* PAGE 2n+2m-1 object data m
|
||||
* ...
|
||||
*
|
||||
* n is number of object lookup pages, which is number of pages needed to index all pages
|
||||
* in a block by object id
|
||||
* : block_size / page_size * sizeof(obj_id) / page_size
|
||||
* m is number data pages, which is number of pages in block minus number of lookup pages
|
||||
* : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size
|
||||
* thus, n+m is total number of pages in a block
|
||||
* : block_size / page_size
|
||||
*
|
||||
* ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256
|
||||
*
|
||||
* Object lookup pages contain object id entries. Each entry represent the corresponding
|
||||
* data page.
|
||||
* Assuming a 16 bit object id, an object id being 0xffff represents a free page.
|
||||
* An object id being 0x0000 represents a deleted page.
|
||||
*
|
||||
* ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff ..
|
||||
* page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff ..
|
||||
* page 2 : data : data for object id 0008
|
||||
* page 3 : data : data for object id 0001
|
||||
* page 4 : data : data for object id 0aaa
|
||||
* ...
|
||||
*
|
||||
*
|
||||
* Object data pages can be either object index pages or object content.
|
||||
* All object data pages contains a data page header, containing object id and span index.
|
||||
* The span index denotes the object page ordering amongst data pages with same object id.
|
||||
* This applies to both object index pages (when index spans more than one page of entries),
|
||||
* and object data pages.
|
||||
* An object index page contains page entries pointing to object content page. The entry index
|
||||
* in a object index page correlates to the span index in the actual object data page.
|
||||
* The first object index page (span index 0) is called object index header page, and also
|
||||
* contains object flags (directory/file), size, object name etc.
|
||||
*
|
||||
* ex:
|
||||
* BLOCK 1
|
||||
* PAGE 256: objectl lookup page 1
|
||||
* [*123] [ 123] [ 123] [ 123]
|
||||
* [ 123] [*123] [ 123] [ 123]
|
||||
* [free] [free] [free] [free] ...
|
||||
* PAGE 257: objectl lookup page 2
|
||||
* [free] [free] [free] [free] ...
|
||||
* PAGE 258: object index page (header)
|
||||
* obj.id:0123 span.ix:0000 flags:INDEX
|
||||
* size:1600 name:ex.txt type:file
|
||||
* [259] [260] [261] [262]
|
||||
* PAGE 259: object data page
|
||||
* obj.id:0123 span.ix:0000 flags:DATA
|
||||
* PAGE 260: object data page
|
||||
* obj.id:0123 span.ix:0001 flags:DATA
|
||||
* PAGE 261: object data page
|
||||
* obj.id:0123 span.ix:0002 flags:DATA
|
||||
* PAGE 262: object data page
|
||||
* obj.id:0123 span.ix:0003 flags:DATA
|
||||
* PAGE 263: object index page
|
||||
* obj.id:0123 span.ix:0001 flags:INDEX
|
||||
* [264] [265] [fre] [fre]
|
||||
* [fre] [fre] [fre] [fre]
|
||||
* PAGE 264: object data page
|
||||
* obj.id:0123 span.ix:0004 flags:DATA
|
||||
* PAGE 265: object data page
|
||||
* obj.id:0123 span.ix:0005 flags:DATA
|
||||
*
|
||||
*/
|
||||
#ifndef SPIFFS_NUCLEUS_H_
|
||||
#define SPIFFS_NUCLEUS_H_
|
||||
|
||||
#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1)
|
||||
#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1)
|
||||
#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2)
|
||||
#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3)
|
||||
#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4)
|
||||
|
||||
// visitor result, continue searching
|
||||
#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20)
|
||||
// visitor result, continue searching after reloading lu buffer
|
||||
#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21)
|
||||
// visitor result, stop searching
|
||||
#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22)
|
||||
|
||||
// updating an object index contents
|
||||
#define SPIFFS_EV_IX_UPD (0)
|
||||
// creating a new object index
|
||||
#define SPIFFS_EV_IX_NEW (1)
|
||||
// deleting an object index
|
||||
#define SPIFFS_EV_IX_DEL (2)
|
||||
// moving an object index without updating contents
|
||||
#define SPIFFS_EV_IX_MOV (3)
|
||||
// updating an object index header data only, not the table itself
|
||||
#define SPIFFS_EV_IX_UPD_HDR (4)
|
||||
|
||||
#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1)))
|
||||
|
||||
#define SPIFFS_UNDEFINED_LEN (u32_t)(-1)
|
||||
|
||||
#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0)
|
||||
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1)
|
||||
|
||||
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
/* For GCC and clang */
|
||||
#define SPIFFS_PACKED __attribute__((packed))
|
||||
#elif defined(__ICCARM__) || defined(__CC_ARM)
|
||||
/* For IAR ARM and Keil MDK-ARM compilers */
|
||||
#define SPIFFS_PACKED
|
||||
|
||||
#else
|
||||
/* Unknown compiler */
|
||||
#define SPIFFS_PACKED
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if SPIFFS_USE_MAGIC
|
||||
#if !SPIFFS_USE_MAGIC_LENGTH
|
||||
#define SPIFFS_MAGIC(fs, bix) \
|
||||
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
|
||||
#else // SPIFFS_USE_MAGIC_LENGTH
|
||||
#define SPIFFS_MAGIC(fs, bix) \
|
||||
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix))))
|
||||
#endif // SPIFFS_USE_MAGIC_LENGTH
|
||||
#endif // SPIFFS_USE_MAGIC
|
||||
|
||||
#define SPIFFS_CONFIG_MAGIC (0x20090315)
|
||||
|
||||
#if SPIFFS_SINGLETON == 0
|
||||
#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \
|
||||
((fs)->cfg.log_page_size)
|
||||
#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \
|
||||
((fs)->cfg.log_block_size)
|
||||
#define SPIFFS_CFG_PHYS_SZ(fs) \
|
||||
((fs)->cfg.phys_size)
|
||||
#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \
|
||||
((fs)->cfg.phys_erase_block)
|
||||
#define SPIFFS_CFG_PHYS_ADDR(fs) \
|
||||
((fs)->cfg.phys_addr)
|
||||
#endif
|
||||
|
||||
// total number of pages
|
||||
#define SPIFFS_MAX_PAGES(fs) \
|
||||
( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// total number of pages per block, including object lookup pages
|
||||
#define SPIFFS_PAGES_PER_BLOCK(fs) \
|
||||
( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// number of object lookup pages per block
|
||||
#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \
|
||||
(MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) )
|
||||
// checks if page index belongs to object lookup
|
||||
#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \
|
||||
(((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
// number of object lookup entries in all object lookup pages
|
||||
#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
// converts a block to physical address
|
||||
#define SPIFFS_BLOCK_TO_PADDR(fs, block) \
|
||||
( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) )
|
||||
// converts a object lookup entry to page index
|
||||
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \
|
||||
((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry))
|
||||
// converts a object lookup entry to physical address of corresponding page
|
||||
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \
|
||||
(SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// converts a page to physical address
|
||||
#define SPIFFS_PAGE_TO_PADDR(fs, page) \
|
||||
( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// converts a physical address to page
|
||||
#define SPIFFS_PADDR_TO_PAGE(fs, addr) \
|
||||
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// gives index in page for a physical address
|
||||
#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \
|
||||
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// returns containing block for given page
|
||||
#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \
|
||||
( (page) / SPIFFS_PAGES_PER_BLOCK(fs) )
|
||||
// returns starting page for block
|
||||
#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \
|
||||
( (block) * SPIFFS_PAGES_PER_BLOCK(fs) )
|
||||
// converts page to entry in object lookup page
|
||||
#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \
|
||||
( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) )
|
||||
// returns data size in a data page
|
||||
#define SPIFFS_DATA_PAGE_SIZE(fs) \
|
||||
( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) )
|
||||
// returns physical address for block's erase count,
|
||||
// always in the physical last entry of the last object lookup page
|
||||
#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \
|
||||
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) )
|
||||
// returns physical address for block's magic,
|
||||
// always in the physical second last entry of the last object lookup page
|
||||
#define SPIFFS_MAGIC_PADDR(fs, bix) \
|
||||
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 )
|
||||
// checks if there is any room for magic in the object luts
|
||||
#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \
|
||||
( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \
|
||||
<= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) )
|
||||
|
||||
// define helpers object
|
||||
|
||||
// entries in an object header page index
|
||||
#define SPIFFS_OBJ_HDR_IX_LEN(fs) \
|
||||
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix))
|
||||
// entries in an object page index
|
||||
#define SPIFFS_OBJ_IX_LEN(fs) \
|
||||
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix))
|
||||
// object index entry for given data span index
|
||||
#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \
|
||||
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs)))
|
||||
// object index span index number for given data span index or entry
|
||||
#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \
|
||||
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs)))
|
||||
// get data span index for object index span index
|
||||
#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \
|
||||
( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) )
|
||||
|
||||
#if SPIFFS_FILEHDL_OFFSET
|
||||
#define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0)
|
||||
#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0)
|
||||
#else
|
||||
#define SPIFFS_FH_OFFS(fs, fh) (fh)
|
||||
#define SPIFFS_FH_UNOFFS(fs, fh) (fh)
|
||||
#endif
|
||||
|
||||
|
||||
#define SPIFFS_OP_T_OBJ_LU (0<<0)
|
||||
#define SPIFFS_OP_T_OBJ_LU2 (1<<0)
|
||||
#define SPIFFS_OP_T_OBJ_IX (2<<0)
|
||||
#define SPIFFS_OP_T_OBJ_DA (3<<0)
|
||||
#define SPIFFS_OP_C_DELE (0<<2)
|
||||
#define SPIFFS_OP_C_UPDT (1<<2)
|
||||
#define SPIFFS_OP_C_MOVS (2<<2)
|
||||
#define SPIFFS_OP_C_MOVD (3<<2)
|
||||
#define SPIFFS_OP_C_FLSH (4<<2)
|
||||
#define SPIFFS_OP_C_READ (5<<2)
|
||||
#define SPIFFS_OP_C_WRTHRU (6<<2)
|
||||
|
||||
#define SPIFFS_OP_TYPE_MASK (3<<0)
|
||||
#define SPIFFS_OP_COM_MASK (7<<2)
|
||||
|
||||
|
||||
// if 0, this page is written to, else clean
|
||||
#define SPIFFS_PH_FLAG_USED (1<<0)
|
||||
// if 0, writing is finalized, else under modification
|
||||
#define SPIFFS_PH_FLAG_FINAL (1<<1)
|
||||
// if 0, this is an index page, else a data page
|
||||
#define SPIFFS_PH_FLAG_INDEX (1<<2)
|
||||
// if 0, page is deleted, else valid
|
||||
#define SPIFFS_PH_FLAG_DELET (1<<7)
|
||||
// if 0, this index header is being deleted
|
||||
#define SPIFFS_PH_FLAG_IXDELE (1<<6)
|
||||
|
||||
|
||||
#define SPIFFS_CHECK_MOUNT(fs) \
|
||||
((fs)->mounted != 0)
|
||||
|
||||
#define SPIFFS_CHECK_CFG(fs) \
|
||||
((fs)->config_magic == SPIFFS_CONFIG_MAGIC)
|
||||
|
||||
#define SPIFFS_CHECK_RES(res) \
|
||||
do { \
|
||||
if ((res) < SPIFFS_OK) return (res); \
|
||||
} while (0);
|
||||
|
||||
#define SPIFFS_API_CHECK_MOUNT(fs) \
|
||||
if (!SPIFFS_CHECK_MOUNT((fs))) { \
|
||||
(fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
|
||||
return SPIFFS_ERR_NOT_MOUNTED; \
|
||||
}
|
||||
|
||||
#define SPIFFS_API_CHECK_CFG(fs) \
|
||||
if (!SPIFFS_CHECK_CFG((fs))) { \
|
||||
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
|
||||
return SPIFFS_ERR_NOT_CONFIGURED; \
|
||||
}
|
||||
|
||||
#define SPIFFS_API_CHECK_RES(fs, res) \
|
||||
if ((res) < SPIFFS_OK) { \
|
||||
(fs)->err_code = (res); \
|
||||
return (res); \
|
||||
}
|
||||
|
||||
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
|
||||
if ((res) < SPIFFS_OK) { \
|
||||
(fs)->err_code = (res); \
|
||||
SPIFFS_UNLOCK(fs); \
|
||||
return (res); \
|
||||
}
|
||||
|
||||
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \
|
||||
if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \
|
||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH;
|
||||
//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED;
|
||||
|
||||
#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \
|
||||
if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \
|
||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
|
||||
|
||||
|
||||
// check id, only visit matching objec ids
|
||||
#define SPIFFS_VIS_CHECK_ID (1<<0)
|
||||
// report argument object id to visitor - else object lookup id is reported
|
||||
#define SPIFFS_VIS_CHECK_PH (1<<1)
|
||||
// stop searching at end of all look up pages
|
||||
#define SPIFFS_VIS_NO_WRAP (1<<2)
|
||||
|
||||
#if SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \
|
||||
(_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src))
|
||||
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \
|
||||
(_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst))
|
||||
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \
|
||||
(_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len))
|
||||
|
||||
#else // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \
|
||||
(_fs)->cfg.hal_write_f((_paddr), (_len), (_src))
|
||||
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \
|
||||
(_fs)->cfg.hal_read_f((_paddr), (_len), (_dst))
|
||||
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \
|
||||
(_fs)->cfg.hal_erase_f((_paddr), (_len))
|
||||
|
||||
#endif // SPIFFS_HAL_CALLBACK_EXTRA
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
|
||||
#define SPIFFS_CACHE_FLAG_DIRTY (1<<0)
|
||||
#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1)
|
||||
#define SPIFFS_CACHE_FLAG_OBJLU (1<<2)
|
||||
#define SPIFFS_CACHE_FLAG_OBJIX (1<<3)
|
||||
#define SPIFFS_CACHE_FLAG_DATA (1<<4)
|
||||
#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7)
|
||||
|
||||
#define SPIFFS_CACHE_PAGE_SIZE(fs) \
|
||||
(sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs))
|
||||
|
||||
#define spiffs_get_cache(fs) \
|
||||
((spiffs_cache *)((fs)->cache))
|
||||
|
||||
#define spiffs_get_cache_page_hdr(fs, c, ix) \
|
||||
((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])))
|
||||
|
||||
#define spiffs_get_cache_page(fs, c, ix) \
|
||||
((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page))
|
||||
|
||||
// cache page struct
|
||||
typedef struct {
|
||||
// cache flags
|
||||
u8_t flags;
|
||||
// cache page index
|
||||
u8_t ix;
|
||||
// last access of this cache page
|
||||
u32_t last_access;
|
||||
union {
|
||||
// type read cache
|
||||
struct {
|
||||
// read cache page index
|
||||
spiffs_page_ix pix;
|
||||
};
|
||||
#if SPIFFS_CACHE_WR
|
||||
// type write cache
|
||||
struct {
|
||||
// write cache
|
||||
spiffs_obj_id obj_id;
|
||||
// offset in cache page
|
||||
u32_t offset;
|
||||
// size of cache page
|
||||
u16_t size;
|
||||
};
|
||||
#endif
|
||||
};
|
||||
} spiffs_cache_page;
|
||||
|
||||
// cache struct
|
||||
typedef struct {
|
||||
u8_t cpage_count;
|
||||
u32_t last_access;
|
||||
u32_t cpage_use_map;
|
||||
u32_t cpage_use_mask;
|
||||
u8_t *cpages;
|
||||
} spiffs_cache;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// spiffs nucleus file descriptor
|
||||
typedef struct {
|
||||
// the filesystem of this descriptor
|
||||
spiffs *fs;
|
||||
// number of file descriptor - if 0, the file descriptor is closed
|
||||
spiffs_file file_nbr;
|
||||
// object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted
|
||||
spiffs_obj_id obj_id;
|
||||
// size of the file
|
||||
u32_t size;
|
||||
// cached object index header page index
|
||||
spiffs_page_ix objix_hdr_pix;
|
||||
// cached offset object index page index
|
||||
spiffs_page_ix cursor_objix_pix;
|
||||
// cached offset object index span index
|
||||
spiffs_span_ix cursor_objix_spix;
|
||||
// current absolute offset
|
||||
u32_t offset;
|
||||
// current file descriptor offset (cached)
|
||||
u32_t fdoffset;
|
||||
// fd flags
|
||||
spiffs_flags flags;
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_cache_page *cache_page;
|
||||
#endif
|
||||
#if SPIFFS_TEMPORAL_FD_CACHE
|
||||
// djb2 hash of filename
|
||||
u32_t name_hash;
|
||||
// hit score (score == 0 indicates never used fd)
|
||||
u16_t score;
|
||||
#endif
|
||||
#if SPIFFS_IX_MAP
|
||||
// spiffs index map, if 0 it means unmapped
|
||||
spiffs_ix_map *ix_map;
|
||||
#endif
|
||||
} spiffs_fd;
|
||||
|
||||
|
||||
// object structs
|
||||
|
||||
// page header, part of each page except object lookup pages
|
||||
// NB: this is always aligned when the data page is an object index,
|
||||
// as in this case struct spiffs_page_object_ix is used
|
||||
typedef struct SPIFFS_PACKED {
|
||||
// object id
|
||||
spiffs_obj_id obj_id;
|
||||
// object span index
|
||||
spiffs_span_ix span_ix;
|
||||
// flags
|
||||
u8_t flags;
|
||||
} spiffs_page_header;
|
||||
|
||||
// object index header page header
|
||||
typedef struct SPIFFS_PACKED
|
||||
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
|
||||
__attribute(( aligned(sizeof(spiffs_page_ix)) ))
|
||||
#endif
|
||||
{
|
||||
// common page header
|
||||
spiffs_page_header p_hdr;
|
||||
// alignment
|
||||
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
|
||||
// size of object
|
||||
u32_t size;
|
||||
// type of object
|
||||
spiffs_obj_type type;
|
||||
// name of object
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||
#if SPIFFS_OBJ_META_LEN
|
||||
// metadata. not interpreted by SPIFFS in any way.
|
||||
u8_t meta[SPIFFS_OBJ_META_LEN];
|
||||
#endif
|
||||
} spiffs_page_object_ix_header;
|
||||
|
||||
// object index page header
|
||||
typedef struct SPIFFS_PACKED {
|
||||
spiffs_page_header p_hdr;
|
||||
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
|
||||
} spiffs_page_object_ix;
|
||||
|
||||
// callback func for object lookup visitor
|
||||
typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
|
||||
const void *user_const_p, void *user_var_p);
|
||||
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
#define _spiffs_rd(fs, op, fh, addr, len, dst) \
|
||||
spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst))
|
||||
#define _spiffs_wr(fs, op, fh, addr, len, src) \
|
||||
spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src))
|
||||
#else
|
||||
#define _spiffs_rd(fs, op, fh, addr, len, dst) \
|
||||
spiffs_phys_rd((fs), (addr), (len), (dst))
|
||||
#define _spiffs_wr(fs, op, fh, addr, len, src) \
|
||||
spiffs_phys_wr((fs), (addr), (len), (src))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_phys_rd(
|
||||
spiffs *fs,
|
||||
#if SPIFFS_CACHE
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
#endif
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *dst);
|
||||
|
||||
s32_t spiffs_phys_wr(
|
||||
spiffs *fs,
|
||||
#if SPIFFS_CACHE
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
#endif
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *src);
|
||||
|
||||
s32_t spiffs_phys_cpy(
|
||||
spiffs *fs,
|
||||
spiffs_file fh,
|
||||
u32_t dst,
|
||||
u32_t src,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_phys_count_free_blocks(
|
||||
spiffs *fs);
|
||||
|
||||
s32_t spiffs_obj_lu_find_entry_visitor(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix starting_block,
|
||||
int starting_lu_entry,
|
||||
u8_t flags,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_visitor_f v,
|
||||
const void *user_const_p,
|
||||
void *user_var_p,
|
||||
spiffs_block_ix *block_ix,
|
||||
int *lu_entry);
|
||||
|
||||
s32_t spiffs_erase_block(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix);
|
||||
|
||||
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH
|
||||
s32_t spiffs_probe(
|
||||
spiffs_config *cfg);
|
||||
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_obj_lu_scan(
|
||||
spiffs *fs);
|
||||
|
||||
s32_t spiffs_obj_lu_find_free_obj_id(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id *obj_id,
|
||||
const u8_t *conflicting_name);
|
||||
|
||||
s32_t spiffs_obj_lu_find_free(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix starting_block,
|
||||
int starting_lu_entry,
|
||||
spiffs_block_ix *block_ix,
|
||||
int *lu_entry);
|
||||
|
||||
s32_t spiffs_obj_lu_find_id(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix starting_block,
|
||||
int starting_lu_entry,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_block_ix *block_ix,
|
||||
int *lu_entry);
|
||||
|
||||
s32_t spiffs_obj_lu_find_id_and_span(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix spix,
|
||||
spiffs_page_ix exclusion_pix,
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix spix,
|
||||
spiffs_page_ix exclusion_pix,
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_page_allocate_data(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_page_header *ph,
|
||||
u8_t *data,
|
||||
u32_t len,
|
||||
u32_t page_offs,
|
||||
u8_t finalize,
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
s32_t spiffs_page_move(
|
||||
spiffs *fs,
|
||||
spiffs_file fh,
|
||||
u8_t *page_data,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_page_header *page_hdr,
|
||||
spiffs_page_ix src_pix,
|
||||
spiffs_page_ix *dst_pix);
|
||||
|
||||
s32_t spiffs_page_delete(
|
||||
spiffs *fs,
|
||||
spiffs_page_ix pix);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_object_create(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
const u8_t name[],
|
||||
const u8_t meta[],
|
||||
spiffs_obj_type type,
|
||||
spiffs_page_ix *objix_hdr_pix);
|
||||
|
||||
s32_t spiffs_object_update_index_hdr(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_page_ix objix_hdr_pix,
|
||||
u8_t *new_objix_hdr_data,
|
||||
const u8_t name[],
|
||||
const u8_t meta[],
|
||||
u32_t size,
|
||||
spiffs_page_ix *new_pix);
|
||||
|
||||
#if SPIFFS_IX_MAP
|
||||
|
||||
s32_t spiffs_populate_ix_map(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd,
|
||||
u32_t vec_entry_start,
|
||||
u32_t vec_entry_end);
|
||||
|
||||
#endif
|
||||
|
||||
void spiffs_cb_object_event(
|
||||
spiffs *fs,
|
||||
spiffs_page_object_ix *objix,
|
||||
int ev,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix spix,
|
||||
spiffs_page_ix new_pix,
|
||||
u32_t new_size);
|
||||
|
||||
s32_t spiffs_object_open_by_id(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_fd *f,
|
||||
spiffs_flags flags,
|
||||
spiffs_mode mode);
|
||||
|
||||
s32_t spiffs_object_open_by_page(
|
||||
spiffs *fs,
|
||||
spiffs_page_ix pix,
|
||||
spiffs_fd *f,
|
||||
spiffs_flags flags,
|
||||
spiffs_mode mode);
|
||||
|
||||
s32_t spiffs_object_append(
|
||||
spiffs_fd *fd,
|
||||
u32_t offset,
|
||||
u8_t *data,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_object_modify(
|
||||
spiffs_fd *fd,
|
||||
u32_t offset,
|
||||
u8_t *data,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_object_read(
|
||||
spiffs_fd *fd,
|
||||
u32_t offset,
|
||||
u32_t len,
|
||||
u8_t *dst);
|
||||
|
||||
s32_t spiffs_object_truncate(
|
||||
spiffs_fd *fd,
|
||||
u32_t new_len,
|
||||
u8_t remove_object);
|
||||
|
||||
s32_t spiffs_object_find_object_index_header_by_name(
|
||||
spiffs *fs,
|
||||
const u8_t name[SPIFFS_OBJ_NAME_LEN],
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_gc_check(
|
||||
spiffs *fs,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_gc_erase_page_stats(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix);
|
||||
|
||||
s32_t spiffs_gc_find_candidate(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix **block_candidate,
|
||||
int *candidate_count,
|
||||
char fs_crammed);
|
||||
|
||||
s32_t spiffs_gc_clean(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix);
|
||||
|
||||
s32_t spiffs_gc_quick(
|
||||
spiffs *fs, u16_t max_free_pages);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_fd_find_new(
|
||||
spiffs *fs,
|
||||
spiffs_fd **fd,
|
||||
const char *name);
|
||||
|
||||
s32_t spiffs_fd_return(
|
||||
spiffs *fs,
|
||||
spiffs_file f);
|
||||
|
||||
s32_t spiffs_fd_get(
|
||||
spiffs *fs,
|
||||
spiffs_file f,
|
||||
spiffs_fd **fd);
|
||||
|
||||
#if SPIFFS_TEMPORAL_FD_CACHE
|
||||
void spiffs_fd_temporal_cache_rehash(
|
||||
spiffs *fs,
|
||||
const char *old_path,
|
||||
const char *new_path);
|
||||
#endif
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
void spiffs_cache_init(
|
||||
spiffs *fs);
|
||||
|
||||
void spiffs_cache_drop_page(
|
||||
spiffs *fs,
|
||||
spiffs_page_ix pix);
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd);
|
||||
|
||||
void spiffs_cache_fd_release(
|
||||
spiffs *fs,
|
||||
spiffs_cache_page *cp);
|
||||
|
||||
spiffs_cache_page *spiffs_cache_page_get_by_fd(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
s32_t spiffs_lookup_consistency_check(
|
||||
spiffs *fs,
|
||||
u8_t check_all_objects);
|
||||
|
||||
s32_t spiffs_page_consistency_check(
|
||||
spiffs *fs);
|
||||
|
||||
s32_t spiffs_object_index_consistency_check(
|
||||
spiffs *fs);
|
||||
|
||||
// memcpy macro,
|
||||
// checked in test builds, otherwise plain memcpy (unless already defined)
|
||||
#ifdef _SPIFFS_TEST
|
||||
#define _SPIFFS_MEMCPY(__d, __s, __l) do { \
|
||||
intptr_t __a1 = (intptr_t)((u8_t*)(__s)); \
|
||||
intptr_t __a2 = (intptr_t)((u8_t*)(__s)+(__l)); \
|
||||
intptr_t __b1 = (intptr_t)((u8_t*)(__d)); \
|
||||
intptr_t __b2 = (intptr_t)((u8_t*)(__d)+(__l)); \
|
||||
if (__a1 <= __b2 && __b1 <= __a2) { \
|
||||
printf("FATAL OVERLAP: memcpy from %lx..%lx to %lx..%lx\n", __a1, __a2, __b1, __b2); \
|
||||
ERREXIT(); \
|
||||
} \
|
||||
memcpy((__d),(__s),(__l)); \
|
||||
} while (0)
|
||||
#else
|
||||
#ifndef _SPIFFS_MEMCPY
|
||||
#define _SPIFFS_MEMCPY(__d, __s, __l) do{memcpy((__d),(__s),(__l));}while(0)
|
||||
#endif
|
||||
#endif //_SPIFFS_TEST
|
||||
|
||||
#endif /* SPIFFS_NUCLEUS_H_ */
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#ifndef NO_TEST
|
||||
#include "testrunner.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **args) {
|
||||
#ifndef NO_TEST
|
||||
run_tests(argc, args);
|
||||
#endif
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* params_test.h
|
||||
*
|
||||
* Created on: May 26, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#ifndef PARAMS_TEST_H_
|
||||
#define PARAMS_TEST_H_
|
||||
|
||||
//////////////// TEST PARAMS ////////////////
|
||||
|
||||
// default test total emulated spi flash size
|
||||
#define PHYS_FLASH_SIZE (16*1024*1024)
|
||||
// default test spiffs file system size
|
||||
#define SPIFFS_FLASH_SIZE (2*1024*1024)
|
||||
// default test spiffs file system offset in emulated spi flash
|
||||
#define SPIFFS_PHYS_ADDR (4*1024*1024)
|
||||
// default test sector size
|
||||
#define SECTOR_SIZE 65536
|
||||
// default test logical block size
|
||||
#define LOG_BLOCK (SECTOR_SIZE*2)
|
||||
// default test logical page size
|
||||
#define LOG_PAGE (SECTOR_SIZE/256)
|
||||
// default test number of filedescs
|
||||
#define DEFAULT_NUM_FD 16
|
||||
// default test number of cache pages
|
||||
#define DEFAULT_NUM_CACHE_PAGES 8
|
||||
|
||||
// When testing, test bench create reference files for comparison on
|
||||
// the actual hard drive. By default, put these on ram drive for speed.
|
||||
#define TEST_PATH "/dev/shm/spiffs/test-data/"
|
||||
|
||||
#define ASSERT(c, m) real_assert((c),(m), __FILE__, __LINE__);
|
||||
void real_assert(int c, const char *n, const char *file, int l);
|
||||
|
||||
/////////// SPIFFS BUILD CONFIG ////////////
|
||||
|
||||
// test using filesystem magic
|
||||
#ifndef SPIFFS_USE_MAGIC
|
||||
#define SPIFFS_USE_MAGIC 1
|
||||
#endif
|
||||
// test using filesystem magic length
|
||||
#ifndef SPIFFS_USE_MAGIC_LENGTH
|
||||
#define SPIFFS_USE_MAGIC_LENGTH 1
|
||||
#endif
|
||||
// test using extra param in callback
|
||||
#ifndef SPIFFS_HAL_CALLBACK_EXTRA
|
||||
#define SPIFFS_HAL_CALLBACK_EXTRA 1
|
||||
#endif
|
||||
// test using filehandle offset
|
||||
#ifndef SPIFFS_FILEHDL_OFFSET
|
||||
#define SPIFFS_FILEHDL_OFFSET 1
|
||||
// use this offset
|
||||
#define TEST_SPIFFS_FILEHDL_OFFSET 0x1000
|
||||
#endif
|
||||
|
||||
#ifdef NO_TEST
|
||||
#define SPIFFS_LOCK(fs)
|
||||
#define SPIFFS_UNLOCK(fs)
|
||||
#else
|
||||
struct spiffs_t;
|
||||
extern void test_lock(struct spiffs_t *fs);
|
||||
extern void test_unlock(struct spiffs_t *fs);
|
||||
#define SPIFFS_LOCK(fs) test_lock(fs)
|
||||
#define SPIFFS_UNLOCK(fs) test_unlock(fs)
|
||||
#endif
|
||||
|
||||
// dbg output
|
||||
#define SPIFFS_DBG(_f, ...) //printf("\x1b[32m" _f "\x1b[0m", ## __VA_ARGS__)
|
||||
#define SPIFFS_API_DBG(_f, ...) //printf("\n\x1b[1m\x1b[7m" _f "\x1b[0m", ## __VA_ARGS__)
|
||||
#define SPIFFS_GC_DBG(_f, ...) //printf("\x1b[36m" _f "\x1b[0m", ## __VA_ARGS__)
|
||||
#define SPIFFS_CACHE_DBG(_f, ...) //printf("\x1b[33m" _f "\x1b[0m", ## __VA_ARGS__)
|
||||
#define SPIFFS_CHECK_DBG(_f, ...) //printf("\x1b[31m" _f "\x1b[0m", ## __VA_ARGS__)
|
||||
|
||||
// needed types
|
||||
typedef signed int s32_t;
|
||||
typedef unsigned int u32_t;
|
||||
typedef signed short s16_t;
|
||||
typedef unsigned short u16_t;
|
||||
typedef signed char s8_t;
|
||||
typedef unsigned char u8_t;
|
||||
|
||||
#endif /* PARAMS_TEST_H_ */
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,427 +0,0 @@
|
|||
/*
|
||||
* test_dev.c
|
||||
*
|
||||
* Created on: Jul 14, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
|
||||
#include "testrunner.h"
|
||||
#include "test_spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
#include "spiffs.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
SUITE(check_tests)
|
||||
static void setup() {
|
||||
_setup();
|
||||
}
|
||||
static void teardown() {
|
||||
_teardown();
|
||||
}
|
||||
|
||||
TEST(evil_write) {
|
||||
fs_set_validate_flashing(0);
|
||||
printf("writing corruption to block 1 data range (leaving lu intact)\n");
|
||||
u32_t data_range = SPIFFS_CFG_LOG_BLOCK_SZ(FS) -
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(FS) * (SPIFFS_OBJ_LOOKUP_PAGES(FS));
|
||||
u8_t *corruption = malloc(data_range);
|
||||
memrand(corruption, data_range);
|
||||
u32_t addr = 0 * SPIFFS_CFG_LOG_PAGE_SZ(FS) * SPIFFS_OBJ_LOOKUP_PAGES(FS);
|
||||
area_write(addr, corruption, data_range);
|
||||
free(corruption);
|
||||
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
|
||||
printf("CHECK1-----------------\n");
|
||||
SPIFFS_check(FS);
|
||||
printf("CHECK2-----------------\n");
|
||||
SPIFFS_check(FS);
|
||||
printf("CHECK3-----------------\n");
|
||||
SPIFFS_check(FS);
|
||||
|
||||
res = test_create_and_write_file("file2", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
return TEST_RES_OK;
|
||||
} TEST_END
|
||||
|
||||
|
||||
TEST(lu_check1) {
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
// modify lu entry data page index 1
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
// reset lu entry to being erased, but keep page data
|
||||
spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED;
|
||||
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
|
||||
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
|
||||
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry*sizeof(spiffs_obj_id);
|
||||
|
||||
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
#endif
|
||||
SPIFFS_check(FS);
|
||||
|
||||
return TEST_RES_OK;
|
||||
} TEST_END
|
||||
|
||||
|
||||
TEST(page_cons1) {
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
// modify object index, find object index header
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
// set object index entry 2 to a bad page
|
||||
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 0 * sizeof(spiffs_page_ix);
|
||||
spiffs_page_ix bad_pix_ref = 0x55;
|
||||
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||
area_write(addr + sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||
|
||||
// delete all cache
|
||||
#if SPIFFS_CACHE
|
||||
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
#endif
|
||||
|
||||
SPIFFS_check(FS);
|
||||
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
return TEST_RES_OK;
|
||||
} TEST_END
|
||||
|
||||
|
||||
TEST(page_cons2) {
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
// modify object index, find object index header
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
// find data page span index 0
|
||||
spiffs_page_ix dpix;
|
||||
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &dpix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
// set object index entry 1+2 to a data page 0
|
||||
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix);
|
||||
spiffs_page_ix bad_pix_ref = dpix;
|
||||
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||
area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||
|
||||
// delete all cache
|
||||
#if SPIFFS_CACHE
|
||||
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
#endif
|
||||
|
||||
SPIFFS_check(FS);
|
||||
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
return TEST_RES_OK;
|
||||
} TEST_END
|
||||
|
||||
|
||||
|
||||
TEST(page_cons3) {
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
// modify object index, find object index header
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
// set object index entry 1+2 lookup page
|
||||
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix);
|
||||
spiffs_page_ix bad_pix_ref = SPIFFS_PAGES_PER_BLOCK(FS) * (*FS.block_count - 2);
|
||||
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||
area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||
|
||||
// delete all cache
|
||||
#if SPIFFS_CACHE
|
||||
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
#endif
|
||||
|
||||
SPIFFS_check(FS);
|
||||
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
return TEST_RES_OK;
|
||||
} TEST_END
|
||||
|
||||
|
||||
TEST(page_cons_final) {
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
// modify page header, make unfinalized
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
// set page span ix 1 as unfinalized
|
||||
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags);
|
||||
u8_t flags;
|
||||
area_read(addr, (u8_t*)&flags, 1);
|
||||
flags |= SPIFFS_PH_FLAG_FINAL;
|
||||
area_write(addr, (u8_t*)&flags, 1);
|
||||
|
||||
// delete all cache
|
||||
#if SPIFFS_CACHE
|
||||
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
#endif
|
||||
|
||||
SPIFFS_check(FS);
|
||||
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
return TEST_RES_OK;
|
||||
} TEST_END
|
||||
|
||||
|
||||
TEST(index_cons1) {
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
// modify lu entry data page index header
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
printf(" deleting lu entry pix %04x\n", pix);
|
||||
// reset lu entry to being erased, but keep page data
|
||||
spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED;
|
||||
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
|
||||
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
|
||||
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
|
||||
|
||||
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
#endif
|
||||
SPIFFS_check(FS);
|
||||
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
return TEST_RES_OK;
|
||||
} TEST_END
|
||||
|
||||
|
||||
TEST(index_cons2) {
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
// modify lu entry data page index header
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
printf(" writing lu entry for index page, ix %04x, as data page\n", pix);
|
||||
spiffs_obj_id obj_id = 0x1234;
|
||||
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
|
||||
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
|
||||
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
|
||||
|
||||
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
#endif
|
||||
SPIFFS_check(FS);
|
||||
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
return TEST_RES_OK;
|
||||
} TEST_END
|
||||
|
||||
|
||||
TEST(index_cons3) {
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
// modify lu entry data page index header
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
printf(" setting lu entry pix %04x to another index page\n", pix);
|
||||
// reset lu entry to being erased, but keep page data
|
||||
spiffs_obj_id obj_id = 1234 | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
|
||||
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
|
||||
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
|
||||
|
||||
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
#endif
|
||||
SPIFFS_check(FS);
|
||||
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
return TEST_RES_OK;
|
||||
} TEST_END
|
||||
|
||||
TEST(index_cons4) {
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
// modify lu entry data page index header, flags
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
printf(" cue objix hdr deletion in page %04x\n", pix);
|
||||
// set flags as deleting ix header
|
||||
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags);
|
||||
u8_t flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE);
|
||||
|
||||
area_write(addr, (u8_t*)&flags, 1);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
#endif
|
||||
SPIFFS_check(FS);
|
||||
|
||||
return TEST_RES_OK;
|
||||
} TEST_END
|
||||
|
||||
SUITE_TESTS(check_tests)
|
||||
ADD_TEST(evil_write)
|
||||
ADD_TEST(lu_check1)
|
||||
ADD_TEST(page_cons1)
|
||||
ADD_TEST(page_cons2)
|
||||
ADD_TEST(page_cons3)
|
||||
ADD_TEST(page_cons_final)
|
||||
ADD_TEST(index_cons1)
|
||||
ADD_TEST(index_cons2)
|
||||
ADD_TEST(index_cons3)
|
||||
ADD_TEST(index_cons4)
|
||||
SUITE_END(check_tests)
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
* test_dev.c
|
||||
*
|
||||
* Created on: Jul 14, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
|
||||
#include "testrunner.h"
|
||||
#include "test_spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
#include "spiffs.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
SUITE(dev_tests)
|
||||
static void setup() {
|
||||
_setup();
|
||||
}
|
||||
static void teardown() {
|
||||
_teardown();
|
||||
}
|
||||
|
||||
TEST(interrupted_write) {
|
||||
char *name = "interrupt";
|
||||
char *name2 = "interrupt2";
|
||||
int res;
|
||||
spiffs_file fd;
|
||||
|
||||
const u32_t sz = SPIFFS_CFG_LOG_PAGE_SZ(FS)*8;
|
||||
u8_t *buf = malloc(sz);
|
||||
memrand(buf, sz);
|
||||
|
||||
printf(" create reference file\n");
|
||||
fd = SPIFFS_open(FS, name, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
clear_flash_ops_log();
|
||||
res = SPIFFS_write(FS, fd, buf, sz);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
u32_t written = get_flash_ops_log_write_bytes();
|
||||
printf(" written bytes: %i\n", written);
|
||||
|
||||
|
||||
printf(" create error file\n");
|
||||
fd = SPIFFS_open(FS, name2, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
clear_flash_ops_log();
|
||||
invoke_error_after_write_bytes(written/2, 0);
|
||||
res = SPIFFS_write(FS, fd, buf, sz);
|
||||
SPIFFS_close(FS, fd);
|
||||
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_TEST);
|
||||
|
||||
clear_flash_ops_log();
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
// delete all cache
|
||||
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
#endif
|
||||
|
||||
|
||||
printf(" read error file\n");
|
||||
fd = SPIFFS_open(FS, name2, SPIFFS_RDONLY, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
printf(" file size: %i\n", s.size);
|
||||
|
||||
if (s.size > 0) {
|
||||
u8_t *buf2 = malloc(s.size);
|
||||
res = SPIFFS_read(FS, fd, buf2, s.size);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
u32_t ix = 0;
|
||||
for (ix = 0; ix < s.size; ix += 16) {
|
||||
int i;
|
||||
printf(" ");
|
||||
for (i = 0; i < 16; i++) {
|
||||
printf("%02x", buf[ix+i]);
|
||||
}
|
||||
printf(" ");
|
||||
for (i = 0; i < 16; i++) {
|
||||
printf("%02x", buf2[ix+i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
free(buf2);
|
||||
}
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
|
||||
printf(" FS check\n");
|
||||
SPIFFS_check(FS);
|
||||
|
||||
printf(" read error file again\n");
|
||||
fd = SPIFFS_open(FS, name2, SPIFFS_APPEND | SPIFFS_RDWR, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
printf(" file size: %i\n", s.size);
|
||||
printf(" write file\n");
|
||||
res = SPIFFS_write(FS, fd, buf, sz);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
free(buf);
|
||||
|
||||
return TEST_RES_OK;
|
||||
|
||||
} TEST_END
|
||||
|
||||
SUITE_TESTS(dev_tests)
|
||||
ADD_TEST(interrupted_write)
|
||||
SUITE_END(dev_tests)
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,109 +0,0 @@
|
|||
/*
|
||||
* test_spiffs.h
|
||||
*
|
||||
* Created on: Jun 19, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#ifndef TEST_SPIFFS_H_
|
||||
#define TEST_SPIFFS_H_
|
||||
|
||||
#include "spiffs.h"
|
||||
|
||||
#define FS &__fs
|
||||
|
||||
extern spiffs __fs;
|
||||
|
||||
|
||||
#define CHECK(r) if (!(r)) return -1;
|
||||
#define CHECK_RES(r) if (r < 0) return -1;
|
||||
#define FS_PURE_DATA_PAGES(fs) \
|
||||
(SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_PAGE_SZ(fs)- (fs)->block_count * SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
#define FS_PURE_DATA_SIZE(fs) \
|
||||
FS_PURE_DATA_PAGES(fs) * SPIFFS_DATA_PAGE_SIZE(fs)
|
||||
|
||||
typedef enum {
|
||||
EMPTY,
|
||||
SMALL,
|
||||
MEDIUM,
|
||||
LARGE,
|
||||
} tfile_size;
|
||||
|
||||
typedef enum {
|
||||
UNTAMPERED,
|
||||
APPENDED,
|
||||
MODIFIED,
|
||||
REWRITTEN,
|
||||
} tfile_type;
|
||||
|
||||
typedef enum {
|
||||
SHORT = 3,
|
||||
NORMAL = 15,
|
||||
LONG = 100,
|
||||
} tfile_life;
|
||||
|
||||
typedef struct {
|
||||
tfile_size tsize;
|
||||
tfile_type ttype;
|
||||
tfile_life tlife;
|
||||
} tfile_conf;
|
||||
|
||||
typedef struct {
|
||||
int state;
|
||||
spiffs_file fd;
|
||||
tfile_conf cfg;
|
||||
char name[32];
|
||||
} tfile;
|
||||
|
||||
void fs_reset();
|
||||
void fs_reset_specific(u32_t addr_offset, u32_t phys_addr, u32_t phys_size,
|
||||
u32_t phys_sector_size,
|
||||
u32_t log_block_size, u32_t log_page_size);
|
||||
s32_t fs_mount_specific(u32_t phys_addr, u32_t phys_size,
|
||||
u32_t phys_sector_size,
|
||||
u32_t log_block_size, u32_t log_page_size);
|
||||
void fs_mount_dump(char *fname,
|
||||
u32_t addr_offset, u32_t phys_addr, u32_t phys_size,
|
||||
u32_t phys_sector_size,
|
||||
u32_t log_block_size, u32_t log_page_size);
|
||||
|
||||
void fs_store_dump(char *fname);
|
||||
void fs_load_dump(char *fname);
|
||||
|
||||
void fs_set_addr_offset(u32_t offset);
|
||||
int read_and_verify(char *name);
|
||||
int read_and_verify_fd(spiffs_file fd, char *name);
|
||||
void dump_page(spiffs *fs, spiffs_page_ix p);
|
||||
void hexdump(u32_t addr, u32_t len);
|
||||
char *make_test_fname(const char *name);
|
||||
void clear_test_path();
|
||||
void area_write(u32_t addr, u8_t *buf, u32_t size);
|
||||
void area_set(u32_t addr, u8_t d, u32_t size);
|
||||
void area_read(u32_t addr, u8_t *buf, u32_t size);
|
||||
void dump_erase_counts(spiffs *fs);
|
||||
void dump_flash_access_stats();
|
||||
void set_flash_ops_log(int enable);
|
||||
void clear_flash_ops_log();
|
||||
u32_t get_flash_ops_log_read_bytes();
|
||||
u32_t get_flash_ops_log_write_bytes();
|
||||
void invoke_error_after_read_bytes(u32_t b, char once_only);
|
||||
void invoke_error_after_write_bytes(u32_t b, char once_only);
|
||||
void fs_set_validate_flashing(int i);
|
||||
int get_error_count();
|
||||
int count_taken_fds(spiffs *fs);
|
||||
|
||||
void memrand(u8_t *b, int len);
|
||||
int test_create_file(char *name);
|
||||
int test_create_and_write_file(char *name, int size, int chunk_size);
|
||||
u32_t get_spiffs_file_crc_by_fd(spiffs_file fd);
|
||||
u32_t get_spiffs_file_crc(char *name);
|
||||
void _setup();
|
||||
void _setup_test_only();
|
||||
void _teardown();
|
||||
u32_t tfile_get_size(tfile_size s);
|
||||
int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg);
|
||||
|
||||
void test_lock(spiffs *fs);
|
||||
void test_unlock(spiffs *fs);
|
||||
|
||||
#endif /* TEST_SPIFFS_H_ */
|
||||
|
|
@ -1,238 +0,0 @@
|
|||
/*
|
||||
* testrunner.c
|
||||
*
|
||||
* Created on: Jun 18, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "testrunner.h"
|
||||
|
||||
static struct {
|
||||
test *tests;
|
||||
test *_last_test;
|
||||
int test_count;
|
||||
void (*on_stop)(test *t);
|
||||
test_res *failed;
|
||||
test_res *failed_last;
|
||||
test_res *stopped;
|
||||
test_res *stopped_last;
|
||||
FILE *spec;
|
||||
char incl_filter[256];
|
||||
char excl_filter[256];
|
||||
} test_main;
|
||||
|
||||
void test_init(void (*on_stop)(test *t)) {
|
||||
test_main.on_stop = on_stop;
|
||||
}
|
||||
|
||||
static int abort_on_error = 0;
|
||||
static int error_count = 0;
|
||||
|
||||
static char check_spec(char *name) {
|
||||
if (test_main.spec) {
|
||||
fseek(test_main.spec, 0, SEEK_SET);
|
||||
char *line = NULL;
|
||||
size_t sz;
|
||||
ssize_t read;
|
||||
while ((read = getline(&line, &sz, test_main.spec)) != -1) {
|
||||
if (strncmp(line, name, strlen(line)-1) == 0) {
|
||||
free(line);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static char check_incl_filter(char *name) {
|
||||
if (strlen(test_main.incl_filter)== 0) return 1;
|
||||
return strstr(name, test_main.incl_filter) == 0 ? 0 : 2;
|
||||
}
|
||||
|
||||
static char check_excl_filter(char *name) {
|
||||
if (strlen(test_main.excl_filter)== 0) return 1;
|
||||
return strstr(name, test_main.excl_filter) == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
void _add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t), int non_default) {
|
||||
if (f == 0) return;
|
||||
if (!check_spec(name)) return;
|
||||
if (check_incl_filter(name) <= non_default) return;
|
||||
if (!check_excl_filter(name)) return;
|
||||
DBGT("adding test %s\n", name);
|
||||
test *t = malloc(sizeof(test));
|
||||
memset(t, 0, sizeof(test));
|
||||
t->f = f;
|
||||
strcpy(t->name, name);
|
||||
t->setup = setup;
|
||||
t->teardown = teardown;
|
||||
if (test_main.tests == 0) {
|
||||
test_main.tests = t;
|
||||
} else {
|
||||
test_main._last_test->_next = t;
|
||||
}
|
||||
test_main._last_test = t;
|
||||
test_main.test_count++;
|
||||
}
|
||||
|
||||
static void add_res(test *t, test_res **head, test_res **last) {
|
||||
test_res *tr = malloc(sizeof(test_res));
|
||||
memset(tr,0,sizeof(test_res));
|
||||
strcpy(tr->name, t->name);
|
||||
if (*head == 0) {
|
||||
*head = tr;
|
||||
} else {
|
||||
(*last)->_next = tr;
|
||||
}
|
||||
*last = tr;
|
||||
}
|
||||
|
||||
static void dump_res(test_res **head) {
|
||||
test_res *tr = (*head);
|
||||
while (tr) {
|
||||
test_res *next_tr = tr->_next;
|
||||
printf(" %s\n", tr->name);
|
||||
free(tr);
|
||||
tr = next_tr;
|
||||
}
|
||||
}
|
||||
|
||||
int get_error_count(void) {
|
||||
return error_count;
|
||||
}
|
||||
|
||||
void inc_error_count(void) {
|
||||
error_count++;
|
||||
}
|
||||
|
||||
int set_abort_on_error(int val) {
|
||||
int old_val = abort_on_error;
|
||||
abort_on_error = val;
|
||||
|
||||
return old_val;
|
||||
}
|
||||
|
||||
int get_abort_on_error(void) {
|
||||
return abort_on_error;
|
||||
}
|
||||
|
||||
int run_tests(int argc, char **args) {
|
||||
memset(&test_main, 0, sizeof(test_main));
|
||||
int arg;
|
||||
int incl_filter = 0;
|
||||
int excl_filter = 0;
|
||||
for (arg = 1; arg < argc; arg++) {
|
||||
if (strlen(args[arg]) == 0) continue;
|
||||
if (0 == strcmp("-f", args[arg])) {
|
||||
incl_filter = 1;
|
||||
continue;
|
||||
}
|
||||
if (0 == strcmp("-e", args[arg])) {
|
||||
excl_filter = 1;
|
||||
continue;
|
||||
}
|
||||
if (incl_filter) {
|
||||
strcpy(test_main.incl_filter, args[arg]);
|
||||
incl_filter = 0;
|
||||
} else if (excl_filter) {
|
||||
strcpy(test_main.excl_filter, args[arg]);
|
||||
excl_filter = 0;
|
||||
} else {
|
||||
printf("running tests from %s\n", args[arg]);
|
||||
FILE *fd = fopen(args[1], "r");
|
||||
if (fd == NULL) {
|
||||
printf("%s not found\n", args[arg]);
|
||||
return -2;
|
||||
}
|
||||
test_main.spec = fd;
|
||||
}
|
||||
}
|
||||
|
||||
DBGT("adding suites...\n");
|
||||
add_suites();
|
||||
DBGT("%i tests added\n", test_main.test_count);
|
||||
if (test_main.spec) {
|
||||
fclose(test_main.spec);
|
||||
}
|
||||
|
||||
if (test_main.test_count == 0) {
|
||||
printf("No tests to run\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_success = open("_tests_ok", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
int fd_bad = open("_tests_fail", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
|
||||
DBGT("running tests...\n");
|
||||
int ok = 0;
|
||||
int failed = 0;
|
||||
int stopped = 0;
|
||||
test *cur_t = test_main.tests;
|
||||
int i = 1;
|
||||
while (cur_t) {
|
||||
cur_t->setup(cur_t);
|
||||
test *next_test = cur_t->_next;
|
||||
DBGT("TEST %i/%i : running test %s\n", i, test_main.test_count, cur_t->name);
|
||||
i++;
|
||||
int start_error_count = get_error_count();
|
||||
int res = cur_t->f(cur_t);
|
||||
if (res == TEST_RES_OK && get_error_count() != start_error_count) {
|
||||
res = TEST_RES_FAIL;
|
||||
}
|
||||
cur_t->test_result = res;
|
||||
int fd = res == TEST_RES_OK ? fd_success : fd_bad;
|
||||
write(fd, cur_t->name, strlen(cur_t->name));
|
||||
write(fd, "\n", 1);
|
||||
switch (res) {
|
||||
case TEST_RES_OK:
|
||||
ok++;
|
||||
printf(" .. ok\n");
|
||||
break;
|
||||
case TEST_RES_FAIL:
|
||||
failed++;
|
||||
printf(" .. FAILED\n");
|
||||
if (test_main.on_stop) test_main.on_stop(cur_t);
|
||||
add_res(cur_t, &test_main.failed, &test_main.failed_last);
|
||||
break;
|
||||
case TEST_RES_ASSERT:
|
||||
stopped++;
|
||||
printf(" .. ABORTED\n");
|
||||
if (test_main.on_stop) test_main.on_stop(cur_t);
|
||||
add_res(cur_t, &test_main.stopped, &test_main.stopped_last);
|
||||
break;
|
||||
}
|
||||
cur_t->teardown(cur_t);
|
||||
free(cur_t);
|
||||
cur_t = next_test;
|
||||
}
|
||||
close(fd_success);
|
||||
close(fd_bad);
|
||||
DBGT("ran %i tests\n", test_main.test_count);
|
||||
printf("Test report, %i tests\n", test_main.test_count);
|
||||
printf("%i succeeded\n", ok);
|
||||
printf("%i failed\n", failed);
|
||||
dump_res(&test_main.failed);
|
||||
printf("%i stopped\n", stopped);
|
||||
dump_res(&test_main.stopped);
|
||||
if (ok < test_main.test_count) {
|
||||
printf("\nFAILED\n");
|
||||
return -1;
|
||||
} else {
|
||||
printf("\nALL TESTS OK\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
* testrunner.h
|
||||
*
|
||||
* Created on: Jun 19, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
file mysuite.c:
|
||||
|
||||
SUITE(mysuite)
|
||||
|
||||
static void setup(test *t) {}
|
||||
|
||||
static void teardown(test *t) {}
|
||||
|
||||
TEST(mytest) {
|
||||
printf("mytest runs now..\n");
|
||||
return 0;
|
||||
} TEST_END
|
||||
|
||||
SUITE_TESTS(mysuite)
|
||||
ADD_TEST(mytest)
|
||||
SUITE_END(mysuite)
|
||||
|
||||
|
||||
|
||||
file mysuite2.c:
|
||||
|
||||
SUITE(mysuite2)
|
||||
|
||||
static void setup(test *t) {}
|
||||
|
||||
static void teardown(test *t) {}
|
||||
|
||||
TEST(mytest2a) {
|
||||
printf("mytest2a runs now..\n");
|
||||
return 0;
|
||||
} TEST_END
|
||||
|
||||
TEST(mytest2b) {
|
||||
printf("mytest2b runs now..\n");
|
||||
return 0;
|
||||
} TEST_END
|
||||
|
||||
SUITE_TESTS(mysuite2)
|
||||
ADD_TEST(mytest2a)
|
||||
ADD_TEST(mytest2b)
|
||||
SUITE_END(mysuite2)
|
||||
|
||||
|
||||
some other file.c:
|
||||
|
||||
void add_suites() {
|
||||
ADD_SUITE(mysuite);
|
||||
ADD_SUITE(mysuite2);
|
||||
}
|
||||
*/
|
||||
|
||||
#ifndef TESTRUNNER_H_
|
||||
#define TESTRUNNER_H_
|
||||
|
||||
#define TEST_RES_OK 0
|
||||
#define TEST_RES_FAIL -1
|
||||
#define TEST_RES_ASSERT -2
|
||||
|
||||
#define ERREXIT() if (get_abort_on_error()) abort(); else inc_error_count()
|
||||
|
||||
struct test_s;
|
||||
|
||||
typedef int (*test_f)(struct test_s *t);
|
||||
|
||||
typedef struct test_s {
|
||||
test_f f;
|
||||
char name[256];
|
||||
void *data;
|
||||
void (*setup)(struct test_s *t);
|
||||
void (*teardown)(struct test_s *t);
|
||||
struct test_s *_next;
|
||||
unsigned char test_result;
|
||||
} test;
|
||||
|
||||
typedef struct test_res_s {
|
||||
char name[256];
|
||||
struct test_res_s *_next;
|
||||
} test_res;
|
||||
|
||||
#define TEST_CHECK(x) if (!(x)) { \
|
||||
printf(" TEST FAIL %s:%d\n", __FILE__, __LINE__); \
|
||||
goto __fail_stop; \
|
||||
}
|
||||
#define TEST_CHECK_EQ(x, y) if ((x) != (y)) { \
|
||||
printf(" TEST FAIL %s:%d, %d != %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
|
||||
goto __fail_stop; \
|
||||
}
|
||||
#define TEST_CHECK_NEQ(x, y) if ((x) == (y)) { \
|
||||
printf(" TEST FAIL %s:%d, %d == %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
|
||||
goto __fail_stop; \
|
||||
}
|
||||
#define TEST_CHECK_GT(x, y) if ((x) <= (y)) { \
|
||||
printf(" TEST FAIL %s:%d, %d <= %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
|
||||
goto __fail_stop; \
|
||||
}
|
||||
#define TEST_CHECK_LT(x, y) if ((x) >= (y)) { \
|
||||
printf(" TEST FAIL %s:%d, %d >= %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
|
||||
goto __fail_stop; \
|
||||
}
|
||||
#define TEST_CHECK_GE(x, y) if ((x) < (y)) { \
|
||||
printf(" TEST FAIL %s:%d, %d < %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
|
||||
goto __fail_stop; \
|
||||
}
|
||||
#define TEST_CHECK_LE(x, y) if ((x) > (y)) { \
|
||||
printf(" TEST FAIL %s:%d, %d > %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
|
||||
goto __fail_stop; \
|
||||
}
|
||||
#define TEST_ASSERT(x) if (!(x)) { \
|
||||
printf(" TEST ASSERT %s:%d\n", __FILE__, __LINE__); \
|
||||
goto __fail_assert; \
|
||||
}
|
||||
|
||||
#define DBGT(...) printf(__VA_ARGS__)
|
||||
|
||||
#define str(s) #s
|
||||
|
||||
#define SUITE(sui)
|
||||
|
||||
#define SUITE_TESTS(sui) \
|
||||
void _add_suite_tests_##sui(void) {
|
||||
|
||||
#define SUITE_END(sui) \
|
||||
}
|
||||
|
||||
#define ADD_TEST(tf) \
|
||||
_add_test(__test_##tf, str(tf), setup, teardown, 0);
|
||||
|
||||
#define ADD_TEST_NON_DEFAULT(tf) \
|
||||
_add_test(__test_##tf, str(tf), setup, teardown, 1);
|
||||
|
||||
#define ADD_SUITE(sui) \
|
||||
extern void _add_suite_tests_##sui(void); \
|
||||
_add_suite_tests_##sui();
|
||||
|
||||
#define TEST(tf) \
|
||||
static int __test_##tf(struct test_s *t) { do
|
||||
|
||||
#define TEST_END \
|
||||
while(0); \
|
||||
__fail_stop: return TEST_RES_FAIL; \
|
||||
__fail_assert: return TEST_RES_ASSERT; \
|
||||
}
|
||||
|
||||
int set_abort_on_error(int val);
|
||||
int get_abort_on_error(void);
|
||||
int get_error_count(void);
|
||||
void inc_error_count(void);
|
||||
|
||||
void add_suites(void);
|
||||
void test_init(void (*on_stop)(test *t));
|
||||
// returns 0 if all tests ok, -1 if any test failed, -2 on badness
|
||||
int run_tests(int argc, char **args);
|
||||
void _add_suite(const char *suite_name);
|
||||
void _add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t), int non_default);
|
||||
|
||||
#endif /* TESTRUNNER_H_ */
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* testsuites.c
|
||||
*
|
||||
* Created on: Jun 19, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#include "testrunner.h"
|
||||
|
||||
void add_suites(void) {
|
||||
//ADD_SUITE(dev_tests);
|
||||
ADD_SUITE(check_tests);
|
||||
ADD_SUITE(hydrogen_tests);
|
||||
ADD_SUITE(bug_tests);
|
||||
}
|
||||
|
|
@ -1,692 +0,0 @@
|
|||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: Arg.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_ARGUMENT_H
|
||||
#define TCLAP_ARGUMENT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#else
|
||||
#define HAVE_SSTREAM
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cstdio>
|
||||
|
||||
#if defined(HAVE_SSTREAM)
|
||||
#include <sstream>
|
||||
typedef std::istringstream istringstream;
|
||||
#elif defined(HAVE_STRSTREAM)
|
||||
#include <strstream>
|
||||
typedef std::istrstream istringstream;
|
||||
#else
|
||||
#error "Need a stringstream (sstream or strstream) to compile!"
|
||||
#endif
|
||||
|
||||
#include <tclap/ArgException.h>
|
||||
#include <tclap/Visitor.h>
|
||||
#include <tclap/CmdLineInterface.h>
|
||||
#include <tclap/ArgTraits.h>
|
||||
#include <tclap/StandardTraits.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A virtual base class that defines the essential data for all arguments.
|
||||
* This class, or one of its existing children, must be subclassed to do
|
||||
* anything.
|
||||
*/
|
||||
class Arg
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Prevent accidental copying.
|
||||
*/
|
||||
Arg(const Arg& rhs);
|
||||
|
||||
/**
|
||||
* Prevent accidental copying.
|
||||
*/
|
||||
Arg& operator=(const Arg& rhs);
|
||||
|
||||
/**
|
||||
* Indicates whether the rest of the arguments should be ignored.
|
||||
*/
|
||||
static bool& ignoreRestRef() { static bool ign = false; return ign; }
|
||||
|
||||
/**
|
||||
* The delimiter that separates an argument flag/name from the
|
||||
* value.
|
||||
*/
|
||||
static char& delimiterRef() { static char delim = ' '; return delim; }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The single char flag used to identify the argument.
|
||||
* This value (preceded by a dash {-}), can be used to identify
|
||||
* an argument on the command line. The _flag can be blank,
|
||||
* in fact this is how unlabeled args work. Unlabeled args must
|
||||
* override appropriate functions to get correct handling. Note
|
||||
* that the _flag does NOT include the dash as part of the flag.
|
||||
*/
|
||||
std::string _flag;
|
||||
|
||||
/**
|
||||
* A single work namd indentifying the argument.
|
||||
* This value (preceded by two dashed {--}) can also be used
|
||||
* to identify an argument on the command line. Note that the
|
||||
* _name does NOT include the two dashes as part of the _name. The
|
||||
* _name cannot be blank.
|
||||
*/
|
||||
std::string _name;
|
||||
|
||||
/**
|
||||
* Description of the argument.
|
||||
*/
|
||||
std::string _description;
|
||||
|
||||
/**
|
||||
* Indicating whether the argument is required.
|
||||
*/
|
||||
bool _required;
|
||||
|
||||
/**
|
||||
* Label to be used in usage description. Normally set to
|
||||
* "required", but can be changed when necessary.
|
||||
*/
|
||||
std::string _requireLabel;
|
||||
|
||||
/**
|
||||
* Indicates whether a value is required for the argument.
|
||||
* Note that the value may be required but the argument/value
|
||||
* combination may not be, as specified by _required.
|
||||
*/
|
||||
bool _valueRequired;
|
||||
|
||||
/**
|
||||
* Indicates whether the argument has been set.
|
||||
* Indicates that a value on the command line has matched the
|
||||
* name/flag of this argument and the values have been set accordingly.
|
||||
*/
|
||||
bool _alreadySet;
|
||||
|
||||
/**
|
||||
* A pointer to a vistitor object.
|
||||
* The visitor allows special handling to occur as soon as the
|
||||
* argument is matched. This defaults to NULL and should not
|
||||
* be used unless absolutely necessary.
|
||||
*/
|
||||
Visitor* _visitor;
|
||||
|
||||
/**
|
||||
* Whether this argument can be ignored, if desired.
|
||||
*/
|
||||
bool _ignoreable;
|
||||
|
||||
/**
|
||||
* Indicates that the arg was set as part of an XOR and not on the
|
||||
* command line.
|
||||
*/
|
||||
bool _xorSet;
|
||||
|
||||
bool _acceptsMultipleValues;
|
||||
|
||||
/**
|
||||
* Performs the special handling described by the Vistitor.
|
||||
*/
|
||||
void _checkWithVisitor() const;
|
||||
|
||||
/**
|
||||
* Primary constructor. YOU (yes you) should NEVER construct an Arg
|
||||
* directly, this is a base class that is extended by various children
|
||||
* that are meant to be used. Use SwitchArg, ValueArg, MultiArg,
|
||||
* UnlabeledValueArg, or UnlabeledMultiArg instead.
|
||||
*
|
||||
* \param flag - The flag identifying the argument.
|
||||
* \param name - The name identifying the argument.
|
||||
* \param desc - The description of the argument, used in the usage.
|
||||
* \param req - Whether the argument is required.
|
||||
* \param valreq - Whether the a value is required for the argument.
|
||||
* \param v - The visitor checked by the argument. Defaults to NULL.
|
||||
*/
|
||||
Arg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
bool valreq,
|
||||
Visitor* v = NULL );
|
||||
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Arg();
|
||||
|
||||
/**
|
||||
* Adds this to the specified list of Args.
|
||||
* \param argList - The list to add this to.
|
||||
*/
|
||||
virtual void addToList( std::list<Arg*>& argList ) const;
|
||||
|
||||
/**
|
||||
* Begin ignoring arguments since the "--" argument was specified.
|
||||
*/
|
||||
static void beginIgnoring() { ignoreRestRef() = true; }
|
||||
|
||||
/**
|
||||
* Whether to ignore the rest.
|
||||
*/
|
||||
static bool ignoreRest() { return ignoreRestRef(); }
|
||||
|
||||
/**
|
||||
* The delimiter that separates an argument flag/name from the
|
||||
* value.
|
||||
*/
|
||||
static char delimiter() { return delimiterRef(); }
|
||||
|
||||
/**
|
||||
* The char used as a place holder when SwitchArgs are combined.
|
||||
* Currently set to the bell char (ASCII 7).
|
||||
*/
|
||||
static char blankChar() { return (char)7; }
|
||||
|
||||
/**
|
||||
* The char that indicates the beginning of a flag. Defaults to '-', but
|
||||
* clients can define TCLAP_FLAGSTARTCHAR to override.
|
||||
*/
|
||||
#ifndef TCLAP_FLAGSTARTCHAR
|
||||
#define TCLAP_FLAGSTARTCHAR '-'
|
||||
#endif
|
||||
static char flagStartChar() { return TCLAP_FLAGSTARTCHAR; }
|
||||
|
||||
/**
|
||||
* The sting that indicates the beginning of a flag. Defaults to "-", but
|
||||
* clients can define TCLAP_FLAGSTARTSTRING to override. Should be the same
|
||||
* as TCLAP_FLAGSTARTCHAR.
|
||||
*/
|
||||
#ifndef TCLAP_FLAGSTARTSTRING
|
||||
#define TCLAP_FLAGSTARTSTRING "-"
|
||||
#endif
|
||||
static const std::string flagStartString() { return TCLAP_FLAGSTARTSTRING; }
|
||||
|
||||
/**
|
||||
* The sting that indicates the beginning of a name. Defaults to "--", but
|
||||
* clients can define TCLAP_NAMESTARTSTRING to override.
|
||||
*/
|
||||
#ifndef TCLAP_NAMESTARTSTRING
|
||||
#define TCLAP_NAMESTARTSTRING "--"
|
||||
#endif
|
||||
static const std::string nameStartString() { return TCLAP_NAMESTARTSTRING; }
|
||||
|
||||
/**
|
||||
* The name used to identify the ignore rest argument.
|
||||
*/
|
||||
static const std::string ignoreNameString() { return "ignore_rest"; }
|
||||
|
||||
/**
|
||||
* Sets the delimiter for all arguments.
|
||||
* \param c - The character that delimits flags/names from values.
|
||||
*/
|
||||
static void setDelimiter( char c ) { delimiterRef() = c; }
|
||||
|
||||
/**
|
||||
* Pure virtual method meant to handle the parsing and value assignment
|
||||
* of the string on the command line.
|
||||
* \param i - Pointer the the current argument in the list.
|
||||
* \param args - Mutable list of strings. What is
|
||||
* passed in from main.
|
||||
*/
|
||||
virtual bool processArg(int *i, std::vector<std::string>& args) = 0;
|
||||
|
||||
/**
|
||||
* Operator ==.
|
||||
* Equality operator. Must be virtual to handle unlabeled args.
|
||||
* \param a - The Arg to be compared to this.
|
||||
*/
|
||||
virtual bool operator==(const Arg& a) const;
|
||||
|
||||
/**
|
||||
* Returns the argument flag.
|
||||
*/
|
||||
const std::string& getFlag() const;
|
||||
|
||||
/**
|
||||
* Returns the argument name.
|
||||
*/
|
||||
const std::string& getName() const;
|
||||
|
||||
/**
|
||||
* Returns the argument description.
|
||||
*/
|
||||
std::string getDescription() const;
|
||||
|
||||
/**
|
||||
* Indicates whether the argument is required.
|
||||
*/
|
||||
virtual bool isRequired() const;
|
||||
|
||||
/**
|
||||
* Sets _required to true. This is used by the XorHandler.
|
||||
* You really have no reason to ever use it.
|
||||
*/
|
||||
void forceRequired();
|
||||
|
||||
/**
|
||||
* Sets the _alreadySet value to true. This is used by the XorHandler.
|
||||
* You really have no reason to ever use it.
|
||||
*/
|
||||
void xorSet();
|
||||
|
||||
/**
|
||||
* Indicates whether a value must be specified for argument.
|
||||
*/
|
||||
bool isValueRequired() const;
|
||||
|
||||
/**
|
||||
* Indicates whether the argument has already been set. Only true
|
||||
* if the arg has been matched on the command line.
|
||||
*/
|
||||
bool isSet() const;
|
||||
|
||||
/**
|
||||
* Indicates whether the argument can be ignored, if desired.
|
||||
*/
|
||||
bool isIgnoreable() const;
|
||||
|
||||
/**
|
||||
* A method that tests whether a string matches this argument.
|
||||
* This is generally called by the processArg() method. This
|
||||
* method could be re-implemented by a child to change how
|
||||
* arguments are specified on the command line.
|
||||
* \param s - The string to be compared to the flag/name to determine
|
||||
* whether the arg matches.
|
||||
*/
|
||||
virtual bool argMatches( const std::string& s ) const;
|
||||
|
||||
/**
|
||||
* Returns a simple string representation of the argument.
|
||||
* Primarily for debugging.
|
||||
*/
|
||||
virtual std::string toString() const;
|
||||
|
||||
/**
|
||||
* Returns a short ID for the usage.
|
||||
* \param valueId - The value used in the id.
|
||||
*/
|
||||
virtual std::string shortID( const std::string& valueId = "val" ) const;
|
||||
|
||||
/**
|
||||
* Returns a long ID for the usage.
|
||||
* \param valueId - The value used in the id.
|
||||
*/
|
||||
virtual std::string longID( const std::string& valueId = "val" ) const;
|
||||
|
||||
/**
|
||||
* Trims a value off of the flag.
|
||||
* \param flag - The string from which the flag and value will be
|
||||
* trimmed. Contains the flag once the value has been trimmed.
|
||||
* \param value - Where the value trimmed from the string will
|
||||
* be stored.
|
||||
*/
|
||||
virtual void trimFlag( std::string& flag, std::string& value ) const;
|
||||
|
||||
/**
|
||||
* Checks whether a given string has blank chars, indicating that
|
||||
* it is a combined SwitchArg. If so, return true, otherwise return
|
||||
* false.
|
||||
* \param s - string to be checked.
|
||||
*/
|
||||
bool _hasBlanks( const std::string& s ) const;
|
||||
|
||||
/**
|
||||
* Sets the requireLabel. Used by XorHandler. You shouldn't ever
|
||||
* use this.
|
||||
* \param s - Set the requireLabel to this value.
|
||||
*/
|
||||
void setRequireLabel( const std::string& s );
|
||||
|
||||
/**
|
||||
* Used for MultiArgs and XorHandler to determine whether args
|
||||
* can still be set.
|
||||
*/
|
||||
virtual bool allowMore();
|
||||
|
||||
/**
|
||||
* Use by output classes to determine whether an Arg accepts
|
||||
* multiple values.
|
||||
*/
|
||||
virtual bool acceptsMultipleValues();
|
||||
|
||||
/**
|
||||
* Clears the Arg object and allows it to be reused by new
|
||||
* command lines.
|
||||
*/
|
||||
virtual void reset();
|
||||
};
|
||||
|
||||
/**
|
||||
* Typedef of an Arg list iterator.
|
||||
*/
|
||||
typedef std::list<Arg*>::iterator ArgListIterator;
|
||||
|
||||
/**
|
||||
* Typedef of an Arg vector iterator.
|
||||
*/
|
||||
typedef std::vector<Arg*>::iterator ArgVectorIterator;
|
||||
|
||||
/**
|
||||
* Typedef of a Visitor list iterator.
|
||||
*/
|
||||
typedef std::list<Visitor*>::iterator VisitorListIterator;
|
||||
|
||||
/*
|
||||
* Extract a value of type T from it's string representation contained
|
||||
* in strVal. The ValueLike parameter used to select the correct
|
||||
* specialization of ExtractValue depending on the value traits of T.
|
||||
* ValueLike traits use operator>> to assign the value from strVal.
|
||||
*/
|
||||
template<typename T> void
|
||||
ExtractValue(T &destVal, const std::string& strVal, ValueLike vl)
|
||||
{
|
||||
static_cast<void>(vl); // Avoid warning about unused vl
|
||||
std::istringstream is(strVal);
|
||||
|
||||
int valuesRead = 0;
|
||||
while ( is.good() ) {
|
||||
if ( is.peek() != EOF )
|
||||
#ifdef TCLAP_SETBASE_ZERO
|
||||
is >> std::setbase(0) >> destVal;
|
||||
#else
|
||||
is >> destVal;
|
||||
#endif
|
||||
else
|
||||
break;
|
||||
|
||||
valuesRead++;
|
||||
}
|
||||
|
||||
if ( is.fail() )
|
||||
throw( ArgParseException("Couldn't read argument value "
|
||||
"from string '" + strVal + "'"));
|
||||
|
||||
|
||||
if ( valuesRead > 1 )
|
||||
throw( ArgParseException("More than one valid value parsed from "
|
||||
"string '" + strVal + "'"));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a value of type T from it's string representation contained
|
||||
* in strVal. The ValueLike parameter used to select the correct
|
||||
* specialization of ExtractValue depending on the value traits of T.
|
||||
* StringLike uses assignment (operator=) to assign from strVal.
|
||||
*/
|
||||
template<typename T> void
|
||||
ExtractValue(T &destVal, const std::string& strVal, StringLike sl)
|
||||
{
|
||||
static_cast<void>(sl); // Avoid warning about unused sl
|
||||
SetString(destVal, strVal);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//BEGIN Arg.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline Arg::Arg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
bool valreq,
|
||||
Visitor* v) :
|
||||
_flag(flag),
|
||||
_name(name),
|
||||
_description(desc),
|
||||
_required(req),
|
||||
_requireLabel("required"),
|
||||
_valueRequired(valreq),
|
||||
_alreadySet(false),
|
||||
_visitor( v ),
|
||||
_ignoreable(true),
|
||||
_xorSet(false),
|
||||
_acceptsMultipleValues(false)
|
||||
{
|
||||
if ( _flag.length() > 1 )
|
||||
throw(SpecificationException(
|
||||
"Argument flag can only be one character long", toString() ) );
|
||||
|
||||
if ( _name != ignoreNameString() &&
|
||||
( _flag == Arg::flagStartString() ||
|
||||
_flag == Arg::nameStartString() ||
|
||||
_flag == " " ) )
|
||||
throw(SpecificationException("Argument flag cannot be either '" +
|
||||
Arg::flagStartString() + "' or '" +
|
||||
Arg::nameStartString() + "' or a space.",
|
||||
toString() ) );
|
||||
|
||||
if ( ( _name.substr( 0, Arg::flagStartString().length() ) == Arg::flagStartString() ) ||
|
||||
( _name.substr( 0, Arg::nameStartString().length() ) == Arg::nameStartString() ) ||
|
||||
( _name.find( " ", 0 ) != std::string::npos ) )
|
||||
throw(SpecificationException("Argument name begin with either '" +
|
||||
Arg::flagStartString() + "' or '" +
|
||||
Arg::nameStartString() + "' or space.",
|
||||
toString() ) );
|
||||
|
||||
}
|
||||
|
||||
inline Arg::~Arg() { }
|
||||
|
||||
inline std::string Arg::shortID( const std::string& valueId ) const
|
||||
{
|
||||
std::string id = "";
|
||||
|
||||
if ( _flag != "" )
|
||||
id = Arg::flagStartString() + _flag;
|
||||
else
|
||||
id = Arg::nameStartString() + _name;
|
||||
|
||||
if ( _valueRequired )
|
||||
id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">";
|
||||
|
||||
if ( !_required )
|
||||
id = "[" + id + "]";
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
inline std::string Arg::longID( const std::string& valueId ) const
|
||||
{
|
||||
std::string id = "";
|
||||
|
||||
if ( _flag != "" )
|
||||
{
|
||||
id += Arg::flagStartString() + _flag;
|
||||
|
||||
if ( _valueRequired )
|
||||
id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">";
|
||||
|
||||
id += ", ";
|
||||
}
|
||||
|
||||
id += Arg::nameStartString() + _name;
|
||||
|
||||
if ( _valueRequired )
|
||||
id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">";
|
||||
|
||||
return id;
|
||||
|
||||
}
|
||||
|
||||
inline bool Arg::operator==(const Arg& a) const
|
||||
{
|
||||
if ( ( _flag != "" && _flag == a._flag ) || _name == a._name)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string Arg::getDescription() const
|
||||
{
|
||||
std::string desc = "";
|
||||
if ( _required )
|
||||
desc = "(" + _requireLabel + ") ";
|
||||
|
||||
// if ( _valueRequired )
|
||||
// desc += "(value required) ";
|
||||
|
||||
desc += _description;
|
||||
return desc;
|
||||
}
|
||||
|
||||
inline const std::string& Arg::getFlag() const { return _flag; }
|
||||
|
||||
inline const std::string& Arg::getName() const { return _name; }
|
||||
|
||||
inline bool Arg::isRequired() const { return _required; }
|
||||
|
||||
inline bool Arg::isValueRequired() const { return _valueRequired; }
|
||||
|
||||
inline bool Arg::isSet() const
|
||||
{
|
||||
if ( _alreadySet && !_xorSet )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Arg::isIgnoreable() const { return _ignoreable; }
|
||||
|
||||
inline void Arg::setRequireLabel( const std::string& s)
|
||||
{
|
||||
_requireLabel = s;
|
||||
}
|
||||
|
||||
inline bool Arg::argMatches( const std::string& argFlag ) const
|
||||
{
|
||||
if ( ( argFlag == Arg::flagStartString() + _flag && _flag != "" ) ||
|
||||
argFlag == Arg::nameStartString() + _name )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string Arg::toString() const
|
||||
{
|
||||
std::string s = "";
|
||||
|
||||
if ( _flag != "" )
|
||||
s += Arg::flagStartString() + _flag + " ";
|
||||
|
||||
s += "(" + Arg::nameStartString() + _name + ")";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
inline void Arg::_checkWithVisitor() const
|
||||
{
|
||||
if ( _visitor != NULL )
|
||||
_visitor->visit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of trimFlag.
|
||||
*/
|
||||
inline void Arg::trimFlag(std::string& flag, std::string& value) const
|
||||
{
|
||||
int stop = 0;
|
||||
for ( int i = 0; static_cast<unsigned int>(i) < flag.length(); i++ )
|
||||
if ( flag[i] == Arg::delimiter() )
|
||||
{
|
||||
stop = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( stop > 1 )
|
||||
{
|
||||
value = flag.substr(stop+1);
|
||||
flag = flag.substr(0,stop);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of _hasBlanks.
|
||||
*/
|
||||
inline bool Arg::_hasBlanks( const std::string& s ) const
|
||||
{
|
||||
for ( int i = 1; static_cast<unsigned int>(i) < s.length(); i++ )
|
||||
if ( s[i] == Arg::blankChar() )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void Arg::forceRequired()
|
||||
{
|
||||
_required = true;
|
||||
}
|
||||
|
||||
inline void Arg::xorSet()
|
||||
{
|
||||
_alreadySet = true;
|
||||
_xorSet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden by Args that need to added to the end of the list.
|
||||
*/
|
||||
inline void Arg::addToList( std::list<Arg*>& argList ) const
|
||||
{
|
||||
argList.push_front( const_cast<Arg*>(this) );
|
||||
}
|
||||
|
||||
inline bool Arg::allowMore()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Arg::acceptsMultipleValues()
|
||||
{
|
||||
return _acceptsMultipleValues;
|
||||
}
|
||||
|
||||
inline void Arg::reset()
|
||||
{
|
||||
_xorSet = false;
|
||||
_alreadySet = false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//END Arg.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
} //namespace TCLAP
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,200 +0,0 @@
|
|||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: ArgException.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_ARG_EXCEPTION_H
|
||||
#define TCLAP_ARG_EXCEPTION_H
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A simple class that defines and argument exception. Should be caught
|
||||
* whenever a CmdLine is created and parsed.
|
||||
*/
|
||||
class ArgException : public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param text - The text of the exception.
|
||||
* \param id - The text identifying the argument source.
|
||||
* \param td - Text describing the type of ArgException it is.
|
||||
* of the exception.
|
||||
*/
|
||||
ArgException( const std::string& text = "undefined exception",
|
||||
const std::string& id = "undefined",
|
||||
const std::string& td = "Generic ArgException")
|
||||
: std::exception(),
|
||||
_errorText(text),
|
||||
_argId( id ),
|
||||
_typeDescription(td)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~ArgException() throw() { }
|
||||
|
||||
/**
|
||||
* Returns the error text.
|
||||
*/
|
||||
std::string error() const { return ( _errorText ); }
|
||||
|
||||
/**
|
||||
* Returns the argument id.
|
||||
*/
|
||||
std::string argId() const
|
||||
{
|
||||
if ( _argId == "undefined" )
|
||||
return " ";
|
||||
else
|
||||
return ( "Argument: " + _argId );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arg id and error text.
|
||||
*/
|
||||
const char* what() const throw()
|
||||
{
|
||||
static std::string ex;
|
||||
ex = _argId + " -- " + _errorText;
|
||||
return ex.c_str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the exception. Used to explain and distinguish
|
||||
* between different child exceptions.
|
||||
*/
|
||||
std::string typeDescription() const
|
||||
{
|
||||
return _typeDescription;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* The text of the exception message.
|
||||
*/
|
||||
std::string _errorText;
|
||||
|
||||
/**
|
||||
* The argument related to this exception.
|
||||
*/
|
||||
std::string _argId;
|
||||
|
||||
/**
|
||||
* Describes the type of the exception. Used to distinguish
|
||||
* between different child exceptions.
|
||||
*/
|
||||
std::string _typeDescription;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Thrown from within the child Arg classes when it fails to properly
|
||||
* parse the argument it has been passed.
|
||||
*/
|
||||
class ArgParseException : public ArgException
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param text - The text of the exception.
|
||||
* \param id - The text identifying the argument source
|
||||
* of the exception.
|
||||
*/
|
||||
ArgParseException( const std::string& text = "undefined exception",
|
||||
const std::string& id = "undefined" )
|
||||
: ArgException( text,
|
||||
id,
|
||||
std::string( "Exception found while parsing " ) +
|
||||
std::string( "the value the Arg has been passed." ))
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* Thrown from CmdLine when the arguments on the command line are not
|
||||
* properly specified, e.g. too many arguments, required argument missing, etc.
|
||||
*/
|
||||
class CmdLineParseException : public ArgException
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param text - The text of the exception.
|
||||
* \param id - The text identifying the argument source
|
||||
* of the exception.
|
||||
*/
|
||||
CmdLineParseException( const std::string& text = "undefined exception",
|
||||
const std::string& id = "undefined" )
|
||||
: ArgException( text,
|
||||
id,
|
||||
std::string( "Exception found when the values ") +
|
||||
std::string( "on the command line do not meet ") +
|
||||
std::string( "the requirements of the defined ") +
|
||||
std::string( "Args." ))
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* Thrown from Arg and CmdLine when an Arg is improperly specified, e.g.
|
||||
* same flag as another Arg, same name, etc.
|
||||
*/
|
||||
class SpecificationException : public ArgException
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param text - The text of the exception.
|
||||
* \param id - The text identifying the argument source
|
||||
* of the exception.
|
||||
*/
|
||||
SpecificationException( const std::string& text = "undefined exception",
|
||||
const std::string& id = "undefined" )
|
||||
: ArgException( text,
|
||||
id,
|
||||
std::string("Exception found when an Arg object ")+
|
||||
std::string("is improperly defined by the ") +
|
||||
std::string("developer." ))
|
||||
{ }
|
||||
|
||||
};
|
||||
|
||||
class ExitException {
|
||||
public:
|
||||
ExitException(int estat) : _estat(estat) {}
|
||||
|
||||
int getExitStatus() const { return _estat; }
|
||||
|
||||
private:
|
||||
int _estat;
|
||||
};
|
||||
|
||||
} // namespace TCLAP
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: ArgTraits.h
|
||||
*
|
||||
* Copyright (c) 2007, Daniel Aarno, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
// This is an internal tclap file, you should probably not have to
|
||||
// include this directly
|
||||
|
||||
#ifndef TCLAP_ARGTRAITS_H
|
||||
#define TCLAP_ARGTRAITS_H
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
// We use two empty structs to get compile type specialization
|
||||
// function to work
|
||||
|
||||
/**
|
||||
* A value like argument value type is a value that can be set using
|
||||
* operator>>. This is the default value type.
|
||||
*/
|
||||
struct ValueLike {
|
||||
typedef ValueLike ValueCategory;
|
||||
virtual ~ValueLike() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A string like argument value type is a value that can be set using
|
||||
* operator=(string). Usefull if the value type contains spaces which
|
||||
* will be broken up into individual tokens by operator>>.
|
||||
*/
|
||||
struct StringLike {
|
||||
virtual ~StringLike() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A class can inherit from this object to make it have string like
|
||||
* traits. This is a compile time thing and does not add any overhead
|
||||
* to the inherenting class.
|
||||
*/
|
||||
struct StringLikeTrait {
|
||||
typedef StringLike ValueCategory;
|
||||
virtual ~StringLikeTrait() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A class can inherit from this object to make it have value like
|
||||
* traits. This is a compile time thing and does not add any overhead
|
||||
* to the inherenting class.
|
||||
*/
|
||||
struct ValueLikeTrait {
|
||||
typedef ValueLike ValueCategory;
|
||||
virtual ~ValueLikeTrait() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Arg traits are used to get compile type specialization when parsing
|
||||
* argument values. Using an ArgTraits you can specify the way that
|
||||
* values gets assigned to any particular type during parsing. The two
|
||||
* supported types are StringLike and ValueLike.
|
||||
*/
|
||||
template<typename T>
|
||||
struct ArgTraits {
|
||||
typedef typename T::ValueCategory ValueCategory;
|
||||
virtual ~ArgTraits() {}
|
||||
//typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
|
||||
|
||||
Copyright (c) 2003 Michael E. Smoot
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
|
|
@ -1,633 +0,0 @@
|
|||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: CmdLine.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_CMDLINE_H
|
||||
#define TCLAP_CMDLINE_H
|
||||
|
||||
#include <tclap/SwitchArg.h>
|
||||
#include <tclap/MultiSwitchArg.h>
|
||||
#include <tclap/UnlabeledValueArg.h>
|
||||
#include <tclap/UnlabeledMultiArg.h>
|
||||
|
||||
#include <tclap/XorHandler.h>
|
||||
#include <tclap/HelpVisitor.h>
|
||||
#include <tclap/VersionVisitor.h>
|
||||
#include <tclap/IgnoreRestVisitor.h>
|
||||
|
||||
#include <tclap/CmdLineOutput.h>
|
||||
#include <tclap/StdOutput.h>
|
||||
|
||||
#include <tclap/Constraint.h>
|
||||
#include <tclap/ValuesConstraint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <stdlib.h> // Needed for exit(), which isn't defined in some envs.
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
template<typename T> void DelPtr(T ptr)
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
template<typename C> void ClearContainer(C &c)
|
||||
{
|
||||
typedef typename C::value_type value_type;
|
||||
std::for_each(c.begin(), c.end(), DelPtr<value_type>);
|
||||
c.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The base class that manages the command line definition and passes
|
||||
* along the parsing to the appropriate Arg classes.
|
||||
*/
|
||||
class CmdLine : public CmdLineInterface
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The list of arguments that will be tested against the
|
||||
* command line.
|
||||
*/
|
||||
std::list<Arg*> _argList;
|
||||
|
||||
/**
|
||||
* The name of the program. Set to argv[0].
|
||||
*/
|
||||
std::string _progName;
|
||||
|
||||
/**
|
||||
* A message used to describe the program. Used in the usage output.
|
||||
*/
|
||||
std::string _message;
|
||||
|
||||
/**
|
||||
* The version to be displayed with the --version switch.
|
||||
*/
|
||||
std::string _version;
|
||||
|
||||
/**
|
||||
* The number of arguments that are required to be present on
|
||||
* the command line. This is set dynamically, based on the
|
||||
* Args added to the CmdLine object.
|
||||
*/
|
||||
int _numRequired;
|
||||
|
||||
/**
|
||||
* The character that is used to separate the argument flag/name
|
||||
* from the value. Defaults to ' ' (space).
|
||||
*/
|
||||
char _delimiter;
|
||||
|
||||
/**
|
||||
* The handler that manages xoring lists of args.
|
||||
*/
|
||||
XorHandler _xorHandler;
|
||||
|
||||
/**
|
||||
* A list of Args to be explicitly deleted when the destructor
|
||||
* is called. At the moment, this only includes the three default
|
||||
* Args.
|
||||
*/
|
||||
std::list<Arg*> _argDeleteOnExitList;
|
||||
|
||||
/**
|
||||
* A list of Visitors to be explicitly deleted when the destructor
|
||||
* is called. At the moment, these are the Vistors created for the
|
||||
* default Args.
|
||||
*/
|
||||
std::list<Visitor*> _visitorDeleteOnExitList;
|
||||
|
||||
/**
|
||||
* Object that handles all output for the CmdLine.
|
||||
*/
|
||||
CmdLineOutput* _output;
|
||||
|
||||
/**
|
||||
* Should CmdLine handle parsing exceptions internally?
|
||||
*/
|
||||
bool _handleExceptions;
|
||||
|
||||
/**
|
||||
* Throws an exception listing the missing args.
|
||||
*/
|
||||
void missingArgsException();
|
||||
|
||||
/**
|
||||
* Checks whether a name/flag string matches entirely matches
|
||||
* the Arg::blankChar. Used when multiple switches are combined
|
||||
* into a single argument.
|
||||
* \param s - The message to be used in the usage.
|
||||
*/
|
||||
bool _emptyCombined(const std::string& s);
|
||||
|
||||
/**
|
||||
* Perform a delete ptr; operation on ptr when this object is deleted.
|
||||
*/
|
||||
void deleteOnExit(Arg* ptr);
|
||||
|
||||
/**
|
||||
* Perform a delete ptr; operation on ptr when this object is deleted.
|
||||
*/
|
||||
void deleteOnExit(Visitor* ptr);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Prevent accidental copying.
|
||||
*/
|
||||
CmdLine(const CmdLine& rhs);
|
||||
CmdLine& operator=(const CmdLine& rhs);
|
||||
|
||||
/**
|
||||
* Encapsulates the code common to the constructors
|
||||
* (which is all of it).
|
||||
*/
|
||||
void _constructor();
|
||||
|
||||
|
||||
/**
|
||||
* Is set to true when a user sets the output object. We use this so
|
||||
* that we don't delete objects that are created outside of this lib.
|
||||
*/
|
||||
bool _userSetOutput;
|
||||
|
||||
/**
|
||||
* Whether or not to automatically create help and version switches.
|
||||
*/
|
||||
bool _helpAndVersion;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Command line constructor. Defines how the arguments will be
|
||||
* parsed.
|
||||
* \param message - The message to be used in the usage
|
||||
* output.
|
||||
* \param delimiter - The character that is used to separate
|
||||
* the argument flag/name from the value. Defaults to ' ' (space).
|
||||
* \param version - The version number to be used in the
|
||||
* --version switch.
|
||||
* \param helpAndVersion - Whether or not to create the Help and
|
||||
* Version switches. Defaults to true.
|
||||
*/
|
||||
CmdLine(const std::string& message,
|
||||
const char delimiter = ' ',
|
||||
const std::string& version = "none",
|
||||
bool helpAndVersion = true);
|
||||
|
||||
/**
|
||||
* Deletes any resources allocated by a CmdLine object.
|
||||
*/
|
||||
virtual ~CmdLine();
|
||||
|
||||
/**
|
||||
* Adds an argument to the list of arguments to be parsed.
|
||||
* \param a - Argument to be added.
|
||||
*/
|
||||
void add( Arg& a );
|
||||
|
||||
/**
|
||||
* An alternative add. Functionally identical.
|
||||
* \param a - Argument to be added.
|
||||
*/
|
||||
void add( Arg* a );
|
||||
|
||||
/**
|
||||
* Add two Args that will be xor'd. If this method is used, add does
|
||||
* not need to be called.
|
||||
* \param a - Argument to be added and xor'd.
|
||||
* \param b - Argument to be added and xor'd.
|
||||
*/
|
||||
void xorAdd( Arg& a, Arg& b );
|
||||
|
||||
/**
|
||||
* Add a list of Args that will be xor'd. If this method is used,
|
||||
* add does not need to be called.
|
||||
* \param xors - List of Args to be added and xor'd.
|
||||
*/
|
||||
void xorAdd( std::vector<Arg*>& xors );
|
||||
|
||||
/**
|
||||
* Parses the command line.
|
||||
* \param argc - Number of arguments.
|
||||
* \param argv - Array of arguments.
|
||||
*/
|
||||
void parse(int argc, const char * const * argv);
|
||||
|
||||
/**
|
||||
* Parses the command line.
|
||||
* \param args - A vector of strings representing the args.
|
||||
* args[0] is still the program name.
|
||||
*/
|
||||
void parse(std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
CmdLineOutput* getOutput();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void setOutput(CmdLineOutput* co);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::string& getVersion();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::string& getProgramName();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::list<Arg*>& getArgList();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
XorHandler& getXorHandler();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
char getDelimiter();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::string& getMessage();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool hasHelpAndVersion();
|
||||
|
||||
/**
|
||||
* Disables or enables CmdLine's internal parsing exception handling.
|
||||
*
|
||||
* @param state Should CmdLine handle parsing exceptions internally?
|
||||
*/
|
||||
void setExceptionHandling(const bool state);
|
||||
|
||||
/**
|
||||
* Returns the current state of the internal exception handling.
|
||||
*
|
||||
* @retval true Parsing exceptions are handled internally.
|
||||
* @retval false Parsing exceptions are propagated to the caller.
|
||||
*/
|
||||
bool getExceptionHandling() const;
|
||||
|
||||
/**
|
||||
* Allows the CmdLine object to be reused.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//Begin CmdLine.cpp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline CmdLine::CmdLine(const std::string& m,
|
||||
char delim,
|
||||
const std::string& v,
|
||||
bool help )
|
||||
:
|
||||
_argList(std::list<Arg*>()),
|
||||
_progName("not_set_yet"),
|
||||
_message(m),
|
||||
_version(v),
|
||||
_numRequired(0),
|
||||
_delimiter(delim),
|
||||
_xorHandler(XorHandler()),
|
||||
_argDeleteOnExitList(std::list<Arg*>()),
|
||||
_visitorDeleteOnExitList(std::list<Visitor*>()),
|
||||
_output(0),
|
||||
_handleExceptions(true),
|
||||
_userSetOutput(false),
|
||||
_helpAndVersion(help)
|
||||
{
|
||||
_constructor();
|
||||
}
|
||||
|
||||
inline CmdLine::~CmdLine()
|
||||
{
|
||||
ClearContainer(_argDeleteOnExitList);
|
||||
ClearContainer(_visitorDeleteOnExitList);
|
||||
|
||||
if ( !_userSetOutput ) {
|
||||
delete _output;
|
||||
_output = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CmdLine::_constructor()
|
||||
{
|
||||
_output = new StdOutput;
|
||||
|
||||
Arg::setDelimiter( _delimiter );
|
||||
|
||||
Visitor* v;
|
||||
|
||||
if ( _helpAndVersion )
|
||||
{
|
||||
v = new HelpVisitor( this, &_output );
|
||||
SwitchArg* help = new SwitchArg("h","help",
|
||||
"Displays usage information and exits.",
|
||||
false, v);
|
||||
add( help );
|
||||
deleteOnExit(help);
|
||||
deleteOnExit(v);
|
||||
|
||||
v = new VersionVisitor( this, &_output );
|
||||
SwitchArg* vers = new SwitchArg("","version",
|
||||
"Displays version information and exits.",
|
||||
false, v);
|
||||
add( vers );
|
||||
deleteOnExit(vers);
|
||||
deleteOnExit(v);
|
||||
}
|
||||
|
||||
v = new IgnoreRestVisitor();
|
||||
SwitchArg* ignore = new SwitchArg(Arg::flagStartString(),
|
||||
Arg::ignoreNameString(),
|
||||
"Ignores the rest of the labeled arguments following this flag.",
|
||||
false, v);
|
||||
add( ignore );
|
||||
deleteOnExit(ignore);
|
||||
deleteOnExit(v);
|
||||
}
|
||||
|
||||
inline void CmdLine::xorAdd( std::vector<Arg*>& ors )
|
||||
{
|
||||
_xorHandler.add( ors );
|
||||
|
||||
for (ArgVectorIterator it = ors.begin(); it != ors.end(); it++)
|
||||
{
|
||||
(*it)->forceRequired();
|
||||
(*it)->setRequireLabel( "OR required" );
|
||||
add( *it );
|
||||
}
|
||||
}
|
||||
|
||||
inline void CmdLine::xorAdd( Arg& a, Arg& b )
|
||||
{
|
||||
std::vector<Arg*> ors;
|
||||
ors.push_back( &a );
|
||||
ors.push_back( &b );
|
||||
xorAdd( ors );
|
||||
}
|
||||
|
||||
inline void CmdLine::add( Arg& a )
|
||||
{
|
||||
add( &a );
|
||||
}
|
||||
|
||||
inline void CmdLine::add( Arg* a )
|
||||
{
|
||||
for( ArgListIterator it = _argList.begin(); it != _argList.end(); it++ )
|
||||
if ( *a == *(*it) )
|
||||
throw( SpecificationException(
|
||||
"Argument with same flag/name already exists!",
|
||||
a->longID() ) );
|
||||
|
||||
a->addToList( _argList );
|
||||
|
||||
if ( a->isRequired() )
|
||||
_numRequired++;
|
||||
}
|
||||
|
||||
|
||||
inline void CmdLine::parse(int argc, const char * const * argv)
|
||||
{
|
||||
// this step is necessary so that we have easy access to
|
||||
// mutable strings.
|
||||
std::vector<std::string> args;
|
||||
for (int i = 0; i < argc; i++)
|
||||
args.push_back(argv[i]);
|
||||
|
||||
parse(args);
|
||||
}
|
||||
|
||||
inline void CmdLine::parse(std::vector<std::string>& args)
|
||||
{
|
||||
bool shouldExit = false;
|
||||
int estat = 0;
|
||||
|
||||
try {
|
||||
_progName = args.front();
|
||||
args.erase(args.begin());
|
||||
|
||||
int requiredCount = 0;
|
||||
|
||||
for (int i = 0; static_cast<unsigned int>(i) < args.size(); i++)
|
||||
{
|
||||
bool matched = false;
|
||||
for (ArgListIterator it = _argList.begin();
|
||||
it != _argList.end(); it++) {
|
||||
if ( (*it)->processArg( &i, args ) )
|
||||
{
|
||||
requiredCount += _xorHandler.check( *it );
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// checks to see if the argument is an empty combined
|
||||
// switch and if so, then we've actually matched it
|
||||
if ( !matched && _emptyCombined( args[i] ) )
|
||||
matched = true;
|
||||
|
||||
if ( !matched && !Arg::ignoreRest() )
|
||||
throw(CmdLineParseException("Couldn't find match "
|
||||
"for argument",
|
||||
args[i]));
|
||||
}
|
||||
|
||||
if ( requiredCount < _numRequired )
|
||||
missingArgsException();
|
||||
|
||||
if ( requiredCount > _numRequired )
|
||||
throw(CmdLineParseException("Too many arguments!"));
|
||||
|
||||
} catch ( ArgException& e ) {
|
||||
// If we're not handling the exceptions, rethrow.
|
||||
if ( !_handleExceptions) {
|
||||
throw;
|
||||
}
|
||||
|
||||
try {
|
||||
_output->failure(*this,e);
|
||||
} catch ( ExitException &ee ) {
|
||||
estat = ee.getExitStatus();
|
||||
shouldExit = true;
|
||||
}
|
||||
} catch (ExitException &ee) {
|
||||
// If we're not handling the exceptions, rethrow.
|
||||
if ( !_handleExceptions) {
|
||||
throw;
|
||||
}
|
||||
|
||||
estat = ee.getExitStatus();
|
||||
shouldExit = true;
|
||||
}
|
||||
|
||||
if (shouldExit)
|
||||
exit(estat);
|
||||
}
|
||||
|
||||
inline bool CmdLine::_emptyCombined(const std::string& s)
|
||||
{
|
||||
if ( s.length() > 0 && s[0] != Arg::flagStartChar() )
|
||||
return false;
|
||||
|
||||
for ( int i = 1; static_cast<unsigned int>(i) < s.length(); i++ )
|
||||
if ( s[i] != Arg::blankChar() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void CmdLine::missingArgsException()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
std::string missingArgList;
|
||||
for (ArgListIterator it = _argList.begin(); it != _argList.end(); it++)
|
||||
{
|
||||
if ( (*it)->isRequired() && !(*it)->isSet() )
|
||||
{
|
||||
missingArgList += (*it)->getName();
|
||||
missingArgList += ", ";
|
||||
count++;
|
||||
}
|
||||
}
|
||||
missingArgList = missingArgList.substr(0,missingArgList.length()-2);
|
||||
|
||||
std::string msg;
|
||||
if ( count > 1 )
|
||||
msg = "Required arguments missing: ";
|
||||
else
|
||||
msg = "Required argument missing: ";
|
||||
|
||||
msg += missingArgList;
|
||||
|
||||
throw(CmdLineParseException(msg));
|
||||
}
|
||||
|
||||
inline void CmdLine::deleteOnExit(Arg* ptr)
|
||||
{
|
||||
_argDeleteOnExitList.push_back(ptr);
|
||||
}
|
||||
|
||||
inline void CmdLine::deleteOnExit(Visitor* ptr)
|
||||
{
|
||||
_visitorDeleteOnExitList.push_back(ptr);
|
||||
}
|
||||
|
||||
inline CmdLineOutput* CmdLine::getOutput()
|
||||
{
|
||||
return _output;
|
||||
}
|
||||
|
||||
inline void CmdLine::setOutput(CmdLineOutput* co)
|
||||
{
|
||||
if ( !_userSetOutput )
|
||||
delete _output;
|
||||
_userSetOutput = true;
|
||||
_output = co;
|
||||
}
|
||||
|
||||
inline std::string& CmdLine::getVersion()
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
inline std::string& CmdLine::getProgramName()
|
||||
{
|
||||
return _progName;
|
||||
}
|
||||
|
||||
inline std::list<Arg*>& CmdLine::getArgList()
|
||||
{
|
||||
return _argList;
|
||||
}
|
||||
|
||||
inline XorHandler& CmdLine::getXorHandler()
|
||||
{
|
||||
return _xorHandler;
|
||||
}
|
||||
|
||||
inline char CmdLine::getDelimiter()
|
||||
{
|
||||
return _delimiter;
|
||||
}
|
||||
|
||||
inline std::string& CmdLine::getMessage()
|
||||
{
|
||||
return _message;
|
||||
}
|
||||
|
||||
inline bool CmdLine::hasHelpAndVersion()
|
||||
{
|
||||
return _helpAndVersion;
|
||||
}
|
||||
|
||||
inline void CmdLine::setExceptionHandling(const bool state)
|
||||
{
|
||||
_handleExceptions = state;
|
||||
}
|
||||
|
||||
inline bool CmdLine::getExceptionHandling() const
|
||||
{
|
||||
return _handleExceptions;
|
||||
}
|
||||
|
||||
inline void CmdLine::reset()
|
||||
{
|
||||
for( ArgListIterator it = _argList.begin(); it != _argList.end(); it++ )
|
||||
(*it)->reset();
|
||||
|
||||
_progName.clear();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//End CmdLine.cpp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: CmdLineInterface.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_COMMANDLINE_INTERFACE_H
|
||||
#define TCLAP_COMMANDLINE_INTERFACE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
class Arg;
|
||||
class CmdLineOutput;
|
||||
class XorHandler;
|
||||
|
||||
/**
|
||||
* The base class that manages the command line definition and passes
|
||||
* along the parsing to the appropriate Arg classes.
|
||||
*/
|
||||
class CmdLineInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~CmdLineInterface() {}
|
||||
|
||||
/**
|
||||
* Adds an argument to the list of arguments to be parsed.
|
||||
* \param a - Argument to be added.
|
||||
*/
|
||||
virtual void add( Arg& a )=0;
|
||||
|
||||
/**
|
||||
* An alternative add. Functionally identical.
|
||||
* \param a - Argument to be added.
|
||||
*/
|
||||
virtual void add( Arg* a )=0;
|
||||
|
||||
/**
|
||||
* Add two Args that will be xor'd.
|
||||
* If this method is used, add does
|
||||
* not need to be called.
|
||||
* \param a - Argument to be added and xor'd.
|
||||
* \param b - Argument to be added and xor'd.
|
||||
*/
|
||||
virtual void xorAdd( Arg& a, Arg& b )=0;
|
||||
|
||||
/**
|
||||
* Add a list of Args that will be xor'd. If this method is used,
|
||||
* add does not need to be called.
|
||||
* \param xors - List of Args to be added and xor'd.
|
||||
*/
|
||||
virtual void xorAdd( std::vector<Arg*>& xors )=0;
|
||||
|
||||
/**
|
||||
* Parses the command line.
|
||||
* \param argc - Number of arguments.
|
||||
* \param argv - Array of arguments.
|
||||
*/
|
||||
virtual void parse(int argc, const char * const * argv)=0;
|
||||
|
||||
/**
|
||||
* Parses the command line.
|
||||
* \param args - A vector of strings representing the args.
|
||||
* args[0] is still the program name.
|
||||
*/
|
||||
void parse(std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Returns the CmdLineOutput object.
|
||||
*/
|
||||
virtual CmdLineOutput* getOutput()=0;
|
||||
|
||||
/**
|
||||
* \param co - CmdLineOutput object that we want to use instead.
|
||||
*/
|
||||
virtual void setOutput(CmdLineOutput* co)=0;
|
||||
|
||||
/**
|
||||
* Returns the version string.
|
||||
*/
|
||||
virtual std::string& getVersion()=0;
|
||||
|
||||
/**
|
||||
* Returns the program name string.
|
||||
*/
|
||||
virtual std::string& getProgramName()=0;
|
||||
|
||||
/**
|
||||
* Returns the argList.
|
||||
*/
|
||||
virtual std::list<Arg*>& getArgList()=0;
|
||||
|
||||
/**
|
||||
* Returns the XorHandler.
|
||||
*/
|
||||
virtual XorHandler& getXorHandler()=0;
|
||||
|
||||
/**
|
||||
* Returns the delimiter string.
|
||||
*/
|
||||
virtual char getDelimiter()=0;
|
||||
|
||||
/**
|
||||
* Returns the message string.
|
||||
*/
|
||||
virtual std::string& getMessage()=0;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the help and version switches were created
|
||||
* automatically.
|
||||
*/
|
||||
virtual bool hasHelpAndVersion()=0;
|
||||
|
||||
/**
|
||||
* Resets the instance as if it had just been constructed so that the
|
||||
* instance can be reused.
|
||||
*/
|
||||
virtual void reset()=0;
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: CmdLineOutput.h
|
||||
*
|
||||
* Copyright (c) 2004, Michael E. Smoot
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_CMDLINEOUTPUT_H
|
||||
#define TCLAP_CMDLINEOUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
class CmdLineInterface;
|
||||
class ArgException;
|
||||
|
||||
/**
|
||||
* The interface that any output object must implement.
|
||||
*/
|
||||
class CmdLineOutput
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Virtual destructor.
|
||||
*/
|
||||
virtual ~CmdLineOutput() {}
|
||||
|
||||
/**
|
||||
* Generates some sort of output for the USAGE.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void usage(CmdLineInterface& c)=0;
|
||||
|
||||
/**
|
||||
* Generates some sort of output for the version.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void version(CmdLineInterface& c)=0;
|
||||
|
||||
/**
|
||||
* Generates some sort of output for a failure.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param e - The ArgException that caused the failure.
|
||||
*/
|
||||
virtual void failure( CmdLineInterface& c,
|
||||
ArgException& e )=0;
|
||||
|
||||
};
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: Constraint.h
|
||||
*
|
||||
* Copyright (c) 2005, Michael E. Smoot
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_CONSTRAINT_H
|
||||
#define TCLAP_CONSTRAINT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* The interface that defines the interaction between the Arg and Constraint.
|
||||
*/
|
||||
template<class T>
|
||||
class Constraint
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns a description of the Constraint.
|
||||
*/
|
||||
virtual std::string description() const =0;
|
||||
|
||||
/**
|
||||
* Returns the short ID for the Constraint.
|
||||
*/
|
||||
virtual std::string shortID() const =0;
|
||||
|
||||
/**
|
||||
* The method used to verify that the value parsed from the command
|
||||
* line meets the constraint.
|
||||
* \param value - The value that will be checked.
|
||||
*/
|
||||
virtual bool check(const T& value) const =0;
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* Silences warnings about Constraint being a base class with virtual
|
||||
* functions but without a virtual destructor.
|
||||
*/
|
||||
virtual ~Constraint() { ; }
|
||||
};
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
||||
|
|
@ -1,299 +0,0 @@
|
|||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: DocBookOutput.h
|
||||
*
|
||||
* Copyright (c) 2004, Michael E. Smoot
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_DOCBOOKOUTPUT_H
|
||||
#define TCLAP_DOCBOOKOUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <tclap/CmdLineInterface.h>
|
||||
#include <tclap/CmdLineOutput.h>
|
||||
#include <tclap/XorHandler.h>
|
||||
#include <tclap/Arg.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A class that generates DocBook output for usage() method for the
|
||||
* given CmdLine and its Args.
|
||||
*/
|
||||
class DocBookOutput : public CmdLineOutput
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Prints the usage to stdout. Can be overridden to
|
||||
* produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void usage(CmdLineInterface& c);
|
||||
|
||||
/**
|
||||
* Prints the version to stdout. Can be overridden
|
||||
* to produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void version(CmdLineInterface& c);
|
||||
|
||||
/**
|
||||
* Prints (to stderr) an error message, short usage
|
||||
* Can be overridden to produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param e - The ArgException that caused the failure.
|
||||
*/
|
||||
virtual void failure(CmdLineInterface& c,
|
||||
ArgException& e );
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Substitutes the char r for string x in string s.
|
||||
* \param s - The string to operate on.
|
||||
* \param r - The char to replace.
|
||||
* \param x - What to replace r with.
|
||||
*/
|
||||
void substituteSpecialChars( std::string& s, char r, std::string& x );
|
||||
void removeChar( std::string& s, char r);
|
||||
void basename( std::string& s );
|
||||
|
||||
void printShortArg(Arg* it);
|
||||
void printLongArg(Arg* it);
|
||||
|
||||
char theDelimiter;
|
||||
};
|
||||
|
||||
|
||||
inline void DocBookOutput::version(CmdLineInterface& _cmd)
|
||||
{
|
||||
std::cout << _cmd.getVersion() << std::endl;
|
||||
}
|
||||
|
||||
inline void DocBookOutput::usage(CmdLineInterface& _cmd )
|
||||
{
|
||||
std::list<Arg*> argList = _cmd.getArgList();
|
||||
std::string progName = _cmd.getProgramName();
|
||||
std::string xversion = _cmd.getVersion();
|
||||
theDelimiter = _cmd.getDelimiter();
|
||||
XorHandler xorHandler = _cmd.getXorHandler();
|
||||
std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList();
|
||||
basename(progName);
|
||||
|
||||
std::cout << "<?xml version='1.0'?>" << std::endl;
|
||||
std::cout << "<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.2//EN\"" << std::endl;
|
||||
std::cout << "\t\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\">" << std::endl << std::endl;
|
||||
|
||||
std::cout << "<refentry>" << std::endl;
|
||||
|
||||
std::cout << "<refmeta>" << std::endl;
|
||||
std::cout << "<refentrytitle>" << progName << "</refentrytitle>" << std::endl;
|
||||
std::cout << "<manvolnum>1</manvolnum>" << std::endl;
|
||||
std::cout << "</refmeta>" << std::endl;
|
||||
|
||||
std::cout << "<refnamediv>" << std::endl;
|
||||
std::cout << "<refname>" << progName << "</refname>" << std::endl;
|
||||
std::cout << "<refpurpose>" << _cmd.getMessage() << "</refpurpose>" << std::endl;
|
||||
std::cout << "</refnamediv>" << std::endl;
|
||||
|
||||
std::cout << "<refsynopsisdiv>" << std::endl;
|
||||
std::cout << "<cmdsynopsis>" << std::endl;
|
||||
|
||||
std::cout << "<command>" << progName << "</command>" << std::endl;
|
||||
|
||||
// xor
|
||||
for ( int i = 0; (unsigned int)i < xorList.size(); i++ )
|
||||
{
|
||||
std::cout << "<group choice='req'>" << std::endl;
|
||||
for ( ArgVectorIterator it = xorList[i].begin();
|
||||
it != xorList[i].end(); it++ )
|
||||
printShortArg((*it));
|
||||
|
||||
std::cout << "</group>" << std::endl;
|
||||
}
|
||||
|
||||
// rest of args
|
||||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
|
||||
if ( !xorHandler.contains( (*it) ) )
|
||||
printShortArg((*it));
|
||||
|
||||
std::cout << "</cmdsynopsis>" << std::endl;
|
||||
std::cout << "</refsynopsisdiv>" << std::endl;
|
||||
|
||||
std::cout << "<refsect1>" << std::endl;
|
||||
std::cout << "<title>Description</title>" << std::endl;
|
||||
std::cout << "<para>" << std::endl;
|
||||
std::cout << _cmd.getMessage() << std::endl;
|
||||
std::cout << "</para>" << std::endl;
|
||||
std::cout << "</refsect1>" << std::endl;
|
||||
|
||||
std::cout << "<refsect1>" << std::endl;
|
||||
std::cout << "<title>Options</title>" << std::endl;
|
||||
|
||||
std::cout << "<variablelist>" << std::endl;
|
||||
|
||||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
|
||||
printLongArg((*it));
|
||||
|
||||
std::cout << "</variablelist>" << std::endl;
|
||||
std::cout << "</refsect1>" << std::endl;
|
||||
|
||||
std::cout << "<refsect1>" << std::endl;
|
||||
std::cout << "<title>Version</title>" << std::endl;
|
||||
std::cout << "<para>" << std::endl;
|
||||
std::cout << xversion << std::endl;
|
||||
std::cout << "</para>" << std::endl;
|
||||
std::cout << "</refsect1>" << std::endl;
|
||||
|
||||
std::cout << "</refentry>" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
inline void DocBookOutput::failure( CmdLineInterface& _cmd,
|
||||
ArgException& e )
|
||||
{
|
||||
static_cast<void>(_cmd); // unused
|
||||
std::cout << e.what() << std::endl;
|
||||
throw ExitException(1);
|
||||
}
|
||||
|
||||
inline void DocBookOutput::substituteSpecialChars( std::string& s,
|
||||
char r,
|
||||
std::string& x )
|
||||
{
|
||||
size_t p;
|
||||
while ( (p = s.find_first_of(r)) != std::string::npos )
|
||||
{
|
||||
s.erase(p,1);
|
||||
s.insert(p,x);
|
||||
}
|
||||
}
|
||||
|
||||
inline void DocBookOutput::removeChar( std::string& s, char r)
|
||||
{
|
||||
size_t p;
|
||||
while ( (p = s.find_first_of(r)) != std::string::npos )
|
||||
{
|
||||
s.erase(p,1);
|
||||
}
|
||||
}
|
||||
|
||||
inline void DocBookOutput::basename( std::string& s )
|
||||
{
|
||||
size_t p = s.find_last_of('/');
|
||||
if ( p != std::string::npos )
|
||||
{
|
||||
s.erase(0, p + 1);
|
||||
}
|
||||
}
|
||||
|
||||
inline void DocBookOutput::printShortArg(Arg* a)
|
||||
{
|
||||
std::string lt = "<";
|
||||
std::string gt = ">";
|
||||
|
||||
std::string id = a->shortID();
|
||||
substituteSpecialChars(id,'<',lt);
|
||||
substituteSpecialChars(id,'>',gt);
|
||||
removeChar(id,'[');
|
||||
removeChar(id,']');
|
||||
|
||||
std::string choice = "opt";
|
||||
if ( a->isRequired() )
|
||||
choice = "plain";
|
||||
|
||||
std::cout << "<arg choice='" << choice << '\'';
|
||||
if ( a->acceptsMultipleValues() )
|
||||
std::cout << " rep='repeat'";
|
||||
|
||||
|
||||
std::cout << '>';
|
||||
if ( !a->getFlag().empty() )
|
||||
std::cout << a->flagStartChar() << a->getFlag();
|
||||
else
|
||||
std::cout << a->nameStartString() << a->getName();
|
||||
if ( a->isValueRequired() )
|
||||
{
|
||||
std::string arg = a->shortID();
|
||||
removeChar(arg,'[');
|
||||
removeChar(arg,']');
|
||||
removeChar(arg,'<');
|
||||
removeChar(arg,'>');
|
||||
arg.erase(0, arg.find_last_of(theDelimiter) + 1);
|
||||
std::cout << theDelimiter;
|
||||
std::cout << "<replaceable>" << arg << "</replaceable>";
|
||||
}
|
||||
std::cout << "</arg>" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
inline void DocBookOutput::printLongArg(Arg* a)
|
||||
{
|
||||
std::string lt = "<";
|
||||
std::string gt = ">";
|
||||
|
||||
std::string desc = a->getDescription();
|
||||
substituteSpecialChars(desc,'<',lt);
|
||||
substituteSpecialChars(desc,'>',gt);
|
||||
|
||||
std::cout << "<varlistentry>" << std::endl;
|
||||
|
||||
if ( !a->getFlag().empty() )
|
||||
{
|
||||
std::cout << "<term>" << std::endl;
|
||||
std::cout << "<option>";
|
||||
std::cout << a->flagStartChar() << a->getFlag();
|
||||
std::cout << "</option>" << std::endl;
|
||||
std::cout << "</term>" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "<term>" << std::endl;
|
||||
std::cout << "<option>";
|
||||
std::cout << a->nameStartString() << a->getName();
|
||||
if ( a->isValueRequired() )
|
||||
{
|
||||
std::string arg = a->shortID();
|
||||
removeChar(arg,'[');
|
||||
removeChar(arg,']');
|
||||
removeChar(arg,'<');
|
||||
removeChar(arg,'>');
|
||||
arg.erase(0, arg.find_last_of(theDelimiter) + 1);
|
||||
std::cout << theDelimiter;
|
||||
std::cout << "<replaceable>" << arg << "</replaceable>";
|
||||
}
|
||||
std::cout << "</option>" << std::endl;
|
||||
std::cout << "</term>" << std::endl;
|
||||
|
||||
std::cout << "<listitem>" << std::endl;
|
||||
std::cout << "<para>" << std::endl;
|
||||
std::cout << desc << std::endl;
|
||||
std::cout << "</para>" << std::endl;
|
||||
std::cout << "</listitem>" << std::endl;
|
||||
|
||||
std::cout << "</varlistentry>" << std::endl;
|
||||
}
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: HelpVisitor.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_HELP_VISITOR_H
|
||||
#define TCLAP_HELP_VISITOR_H
|
||||
|
||||
#include <tclap/CmdLineInterface.h>
|
||||
#include <tclap/CmdLineOutput.h>
|
||||
#include <tclap/Visitor.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A Visitor object that calls the usage method of the given CmdLineOutput
|
||||
* object for the specified CmdLine object.
|
||||
*/
|
||||
class HelpVisitor: public Visitor
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Prevent accidental copying.
|
||||
*/
|
||||
HelpVisitor(const HelpVisitor& rhs);
|
||||
HelpVisitor& operator=(const HelpVisitor& rhs);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The CmdLine the output will be generated for.
|
||||
*/
|
||||
CmdLineInterface* _cmd;
|
||||
|
||||
/**
|
||||
* The output object.
|
||||
*/
|
||||
CmdLineOutput** _out;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param cmd - The CmdLine the output will be generated for.
|
||||
* \param out - The type of output.
|
||||
*/
|
||||
HelpVisitor(CmdLineInterface* cmd, CmdLineOutput** out)
|
||||
: Visitor(), _cmd( cmd ), _out( out ) { }
|
||||
|
||||
/**
|
||||
* Calls the usage method of the CmdLineOutput for the
|
||||
* specified CmdLine.
|
||||
*/
|
||||
void visit() { (*_out)->usage(*_cmd); throw ExitException(0); }
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: IgnoreRestVisitor.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_IGNORE_REST_VISITOR_H
|
||||
#define TCLAP_IGNORE_REST_VISITOR_H
|
||||
|
||||
#include <tclap/Visitor.h>
|
||||
#include <tclap/Arg.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A Vistor that tells the CmdLine to begin ignoring arguments after
|
||||
* this one is parsed.
|
||||
*/
|
||||
class IgnoreRestVisitor: public Visitor
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
IgnoreRestVisitor() : Visitor() {}
|
||||
|
||||
/**
|
||||
* Sets Arg::_ignoreRest.
|
||||
*/
|
||||
void visit() { Arg::beginIgnoring(); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,433 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* file: MultiArg.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_MULTIPLE_ARGUMENT_H
|
||||
#define TCLAP_MULTIPLE_ARGUMENT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <tclap/Arg.h>
|
||||
#include <tclap/Constraint.h>
|
||||
|
||||
namespace TCLAP {
|
||||
/**
|
||||
* An argument that allows multiple values of type T to be specified. Very
|
||||
* similar to a ValueArg, except a vector of values will be returned
|
||||
* instead of just one.
|
||||
*/
|
||||
template<class T>
|
||||
class MultiArg : public Arg
|
||||
{
|
||||
public:
|
||||
typedef std::vector<T> container_type;
|
||||
typedef typename container_type::iterator iterator;
|
||||
typedef typename container_type::const_iterator const_iterator;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The list of values parsed from the CmdLine.
|
||||
*/
|
||||
std::vector<T> _values;
|
||||
|
||||
/**
|
||||
* The description of type T to be used in the usage.
|
||||
*/
|
||||
std::string _typeDesc;
|
||||
|
||||
/**
|
||||
* A list of constraint on this Arg.
|
||||
*/
|
||||
Constraint<T>* _constraint;
|
||||
|
||||
/**
|
||||
* Extracts the value from the string.
|
||||
* Attempts to parse string as type T, if this fails an exception
|
||||
* is thrown.
|
||||
* \param val - The string to be read.
|
||||
*/
|
||||
void _extractValue( const std::string& val );
|
||||
|
||||
/**
|
||||
* Used by XorHandler to decide whether to keep parsing for this arg.
|
||||
*/
|
||||
bool _allowMore;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param typeDesc - A short, human readable description of the
|
||||
* type that this object expects. This is used in the generation
|
||||
* of the USAGE statement. The goal is to be helpful to the end user
|
||||
* of the program.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
Visitor* v = NULL);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param typeDesc - A short, human readable description of the
|
||||
* type that this object expects. This is used in the generation
|
||||
* of the USAGE statement. The goal is to be helpful to the end user
|
||||
* of the program.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param constraint - A pointer to a Constraint object used
|
||||
* to constrain this Arg.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param constraint - A pointer to a Constraint object used
|
||||
* to constrain this Arg.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Handles the processing of the argument.
|
||||
* This re-implements the Arg version of this method to set the
|
||||
* _value of the argument appropriately. It knows the difference
|
||||
* between labeled and unlabeled.
|
||||
* \param i - Pointer the the current argument in the list.
|
||||
* \param args - Mutable list of strings. Passed from main().
|
||||
*/
|
||||
virtual bool processArg(int* i, std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Returns a vector of type T containing the values parsed from
|
||||
* the command line.
|
||||
*/
|
||||
const std::vector<T>& getValue();
|
||||
|
||||
/**
|
||||
* Returns an iterator over the values parsed from the command
|
||||
* line.
|
||||
*/
|
||||
const_iterator begin() const { return _values.begin(); }
|
||||
|
||||
/**
|
||||
* Returns the end of the values parsed from the command
|
||||
* line.
|
||||
*/
|
||||
const_iterator end() const { return _values.end(); }
|
||||
|
||||
/**
|
||||
* Returns the a short id string. Used in the usage.
|
||||
* \param val - value to be used.
|
||||
*/
|
||||
virtual std::string shortID(const std::string& val="val") const;
|
||||
|
||||
/**
|
||||
* Returns the a long id string. Used in the usage.
|
||||
* \param val - value to be used.
|
||||
*/
|
||||
virtual std::string longID(const std::string& val="val") const;
|
||||
|
||||
/**
|
||||
* Once we've matched the first value, then the arg is no longer
|
||||
* required.
|
||||
*/
|
||||
virtual bool isRequired() const;
|
||||
|
||||
virtual bool allowMore();
|
||||
|
||||
virtual void reset();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Prevent accidental copying
|
||||
*/
|
||||
MultiArg<T>(const MultiArg<T>& rhs);
|
||||
MultiArg<T>& operator=(const MultiArg<T>& rhs);
|
||||
|
||||
};
|
||||
|
||||
template<class T>
|
||||
MultiArg<T>::MultiArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
Visitor* v) :
|
||||
Arg( flag, name, desc, req, true, v ),
|
||||
_values(std::vector<T>()),
|
||||
_typeDesc( typeDesc ),
|
||||
_constraint( NULL ),
|
||||
_allowMore(false)
|
||||
{
|
||||
_acceptsMultipleValues = true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
MultiArg<T>::MultiArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v)
|
||||
: Arg( flag, name, desc, req, true, v ),
|
||||
_values(std::vector<T>()),
|
||||
_typeDesc( typeDesc ),
|
||||
_constraint( NULL ),
|
||||
_allowMore(false)
|
||||
{
|
||||
parser.add( this );
|
||||
_acceptsMultipleValues = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
MultiArg<T>::MultiArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
Visitor* v)
|
||||
: Arg( flag, name, desc, req, true, v ),
|
||||
_values(std::vector<T>()),
|
||||
_typeDesc( constraint->shortID() ),
|
||||
_constraint( constraint ),
|
||||
_allowMore(false)
|
||||
{
|
||||
_acceptsMultipleValues = true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
MultiArg<T>::MultiArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v)
|
||||
: Arg( flag, name, desc, req, true, v ),
|
||||
_values(std::vector<T>()),
|
||||
_typeDesc( constraint->shortID() ),
|
||||
_constraint( constraint ),
|
||||
_allowMore(false)
|
||||
{
|
||||
parser.add( this );
|
||||
_acceptsMultipleValues = true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
const std::vector<T>& MultiArg<T>::getValue() { return _values; }
|
||||
|
||||
template<class T>
|
||||
bool MultiArg<T>::processArg(int *i, std::vector<std::string>& args)
|
||||
{
|
||||
if ( _ignoreable && Arg::ignoreRest() )
|
||||
return false;
|
||||
|
||||
if ( _hasBlanks( args[*i] ) )
|
||||
return false;
|
||||
|
||||
std::string flag = args[*i];
|
||||
std::string value = "";
|
||||
|
||||
trimFlag( flag, value );
|
||||
|
||||
if ( argMatches( flag ) )
|
||||
{
|
||||
if ( Arg::delimiter() != ' ' && value == "" )
|
||||
throw( ArgParseException(
|
||||
"Couldn't find delimiter for this argument!",
|
||||
toString() ) );
|
||||
|
||||
// always take the first one, regardless of start string
|
||||
if ( value == "" )
|
||||
{
|
||||
(*i)++;
|
||||
if ( static_cast<unsigned int>(*i) < args.size() )
|
||||
_extractValue( args[*i] );
|
||||
else
|
||||
throw( ArgParseException("Missing a value for this argument!",
|
||||
toString() ) );
|
||||
}
|
||||
else
|
||||
_extractValue( value );
|
||||
|
||||
/*
|
||||
// continuing taking the args until we hit one with a start string
|
||||
while ( (unsigned int)(*i)+1 < args.size() &&
|
||||
args[(*i)+1].find_first_of( Arg::flagStartString() ) != 0 &&
|
||||
args[(*i)+1].find_first_of( Arg::nameStartString() ) != 0 )
|
||||
_extractValue( args[++(*i)] );
|
||||
*/
|
||||
|
||||
_alreadySet = true;
|
||||
_checkWithVisitor();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
std::string MultiArg<T>::shortID(const std::string& val) const
|
||||
{
|
||||
static_cast<void>(val); // Ignore input, don't warn
|
||||
return Arg::shortID(_typeDesc) + " ... ";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
std::string MultiArg<T>::longID(const std::string& val) const
|
||||
{
|
||||
static_cast<void>(val); // Ignore input, don't warn
|
||||
return Arg::longID(_typeDesc) + " (accepted multiple times)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Once we've matched the first value, then the arg is no longer
|
||||
* required.
|
||||
*/
|
||||
template<class T>
|
||||
bool MultiArg<T>::isRequired() const
|
||||
{
|
||||
if ( _required )
|
||||
{
|
||||
if ( _values.size() > 1 )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void MultiArg<T>::_extractValue( const std::string& val )
|
||||
{
|
||||
try {
|
||||
T tmp;
|
||||
ExtractValue(tmp, val, typename ArgTraits<T>::ValueCategory());
|
||||
_values.push_back(tmp);
|
||||
} catch( ArgParseException &e) {
|
||||
throw ArgParseException(e.error(), toString());
|
||||
}
|
||||
|
||||
if ( _constraint != NULL )
|
||||
if ( ! _constraint->check( _values.back() ) )
|
||||
throw( CmdLineParseException( "Value '" + val +
|
||||
"' does not meet constraint: " +
|
||||
_constraint->description(),
|
||||
toString() ) );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool MultiArg<T>::allowMore()
|
||||
{
|
||||
bool am = _allowMore;
|
||||
_allowMore = true;
|
||||
return am;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void MultiArg<T>::reset()
|
||||
{
|
||||
Arg::reset();
|
||||
_values.clear();
|
||||
}
|
||||
|
||||
} // namespace TCLAP
|
||||
|
||||
#endif
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: MultiSwitchArg.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* Copyright (c) 2005, Michael E. Smoot, Daniel Aarno, Erik Zeek.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_MULTI_SWITCH_ARG_H
|
||||
#define TCLAP_MULTI_SWITCH_ARG_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <tclap/SwitchArg.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A multiple switch argument. If the switch is set on the command line, then
|
||||
* the getValue method will return the number of times the switch appears.
|
||||
*/
|
||||
class MultiSwitchArg : public SwitchArg
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The value of the switch.
|
||||
*/
|
||||
int _value;
|
||||
|
||||
/**
|
||||
* Used to support the reset() method so that ValueArg can be
|
||||
* reset to their constructed value.
|
||||
*/
|
||||
int _default;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* MultiSwitchArg constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param init - Optional. The initial/default value of this Arg.
|
||||
* Defaults to 0.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiSwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
int init = 0,
|
||||
Visitor* v = NULL);
|
||||
|
||||
|
||||
/**
|
||||
* MultiSwitchArg constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param init - Optional. The initial/default value of this Arg.
|
||||
* Defaults to 0.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiSwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
CmdLineInterface& parser,
|
||||
int init = 0,
|
||||
Visitor* v = NULL);
|
||||
|
||||
|
||||
/**
|
||||
* Handles the processing of the argument.
|
||||
* This re-implements the SwitchArg version of this method to set the
|
||||
* _value of the argument appropriately.
|
||||
* \param i - Pointer the the current argument in the list.
|
||||
* \param args - Mutable list of strings. Passed
|
||||
* in from main().
|
||||
*/
|
||||
virtual bool processArg(int* i, std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Returns int, the number of times the switch has been set.
|
||||
*/
|
||||
int getValue();
|
||||
|
||||
/**
|
||||
* Returns the shortID for this Arg.
|
||||
*/
|
||||
std::string shortID(const std::string& val) const;
|
||||
|
||||
/**
|
||||
* Returns the longID for this Arg.
|
||||
*/
|
||||
std::string longID(const std::string& val) const;
|
||||
|
||||
void reset();
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//BEGIN MultiSwitchArg.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
inline MultiSwitchArg::MultiSwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
int init,
|
||||
Visitor* v )
|
||||
: SwitchArg(flag, name, desc, false, v),
|
||||
_value( init ),
|
||||
_default( init )
|
||||
{ }
|
||||
|
||||
inline MultiSwitchArg::MultiSwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
CmdLineInterface& parser,
|
||||
int init,
|
||||
Visitor* v )
|
||||
: SwitchArg(flag, name, desc, false, v),
|
||||
_value( init ),
|
||||
_default( init )
|
||||
{
|
||||
parser.add( this );
|
||||
}
|
||||
|
||||
inline int MultiSwitchArg::getValue() { return _value; }
|
||||
|
||||
inline bool MultiSwitchArg::processArg(int *i, std::vector<std::string>& args)
|
||||
{
|
||||
if ( _ignoreable && Arg::ignoreRest() )
|
||||
return false;
|
||||
|
||||
if ( argMatches( args[*i] ))
|
||||
{
|
||||
// so the isSet() method will work
|
||||
_alreadySet = true;
|
||||
|
||||
// Matched argument: increment value.
|
||||
++_value;
|
||||
|
||||
_checkWithVisitor();
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( combinedSwitchesMatch( args[*i] ) )
|
||||
{
|
||||
// so the isSet() method will work
|
||||
_alreadySet = true;
|
||||
|
||||
// Matched argument: increment value.
|
||||
++_value;
|
||||
|
||||
// Check for more in argument and increment value.
|
||||
while ( combinedSwitchesMatch( args[*i] ) )
|
||||
++_value;
|
||||
|
||||
_checkWithVisitor();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string
|
||||
MultiSwitchArg::shortID(const std::string& val) const
|
||||
{
|
||||
return Arg::shortID(val) + " ... ";
|
||||
}
|
||||
|
||||
inline std::string
|
||||
MultiSwitchArg::longID(const std::string& val) const
|
||||
{
|
||||
return Arg::longID(val) + " (accepted multiple times)";
|
||||
}
|
||||
|
||||
inline void
|
||||
MultiSwitchArg::reset()
|
||||
{
|
||||
MultiSwitchArg::_value = MultiSwitchArg::_default;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//END MultiSwitchArg.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
} //namespace TCLAP
|
||||
|
||||
#endif
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: OptionalUnlabeledTracker.h
|
||||
*
|
||||
* Copyright (c) 2005, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_OPTIONAL_UNLABELED_TRACKER_H
|
||||
#define TCLAP_OPTIONAL_UNLABELED_TRACKER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
class OptionalUnlabeledTracker
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
static void check( bool req, const std::string& argName );
|
||||
|
||||
static void gotOptional() { alreadyOptionalRef() = true; }
|
||||
|
||||
static bool& alreadyOptional() { return alreadyOptionalRef(); }
|
||||
|
||||
private:
|
||||
|
||||
static bool& alreadyOptionalRef() { static bool ct = false; return ct; }
|
||||
};
|
||||
|
||||
|
||||
inline void OptionalUnlabeledTracker::check( bool req, const std::string& argName )
|
||||
{
|
||||
if ( OptionalUnlabeledTracker::alreadyOptional() )
|
||||
throw( SpecificationException(
|
||||
"You can't specify ANY Unlabeled Arg following an optional Unlabeled Arg",
|
||||
argName ) );
|
||||
|
||||
if ( !req )
|
||||
OptionalUnlabeledTracker::gotOptional();
|
||||
}
|
||||
|
||||
|
||||
} // namespace TCLAP
|
||||
|
||||
#endif
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: StandardTraits.h
|
||||
*
|
||||
* Copyright (c) 2007, Daniel Aarno, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
// This is an internal tclap file, you should probably not have to
|
||||
// include this directly
|
||||
|
||||
#ifndef TCLAP_STANDARD_TRAITS_H
|
||||
#define TCLAP_STANDARD_TRAITS_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // To check for long long
|
||||
#endif
|
||||
|
||||
// If Microsoft has already typedef'd wchar_t as an unsigned
|
||||
// short, then compiles will break because it's as if we're
|
||||
// creating ArgTraits twice for unsigned short. Thus...
|
||||
#ifdef _MSC_VER
|
||||
#ifndef _NATIVE_WCHAR_T_DEFINED
|
||||
#define TCLAP_DONT_DECLARE_WCHAR_T_ARGTRAITS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
// ======================================================================
|
||||
// Integer types
|
||||
// ======================================================================
|
||||
|
||||
/**
|
||||
* longs have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<long> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* ints have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<int> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* shorts have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<short> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* chars have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<char> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
#ifdef HAVE_LONG_LONG
|
||||
/**
|
||||
* long longs have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<long long> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
#endif
|
||||
|
||||
// ======================================================================
|
||||
// Unsigned integer types
|
||||
// ======================================================================
|
||||
|
||||
/**
|
||||
* unsigned longs have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<unsigned long> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* unsigned ints have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<unsigned int> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* unsigned shorts have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<unsigned short> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* unsigned chars have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<unsigned char> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
// Microsoft implements size_t awkwardly.
|
||||
#if defined(_MSC_VER) && defined(_M_X64)
|
||||
/**
|
||||
* size_ts have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<size_t> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_LONG_LONG
|
||||
/**
|
||||
* unsigned long longs have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<unsigned long long> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
#endif
|
||||
|
||||
// ======================================================================
|
||||
// Float types
|
||||
// ======================================================================
|
||||
|
||||
/**
|
||||
* floats have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<float> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* doubles have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<double> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
// Other types
|
||||
// ======================================================================
|
||||
|
||||
/**
|
||||
* bools have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<bool> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* wchar_ts have value-like semantics.
|
||||
*/
|
||||
#ifndef TCLAP_DONT_DECLARE_WCHAR_T_ARGTRAITS
|
||||
template<>
|
||||
struct ArgTraits<wchar_t> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Strings have string like argument traits.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<std::string> {
|
||||
typedef StringLike ValueCategory;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void SetString(T &dst, const std::string &src)
|
||||
{
|
||||
dst = src;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,298 +0,0 @@
|
|||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: StdOutput.h
|
||||
*
|
||||
* Copyright (c) 2004, Michael E. Smoot
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_STDCMDLINEOUTPUT_H
|
||||
#define TCLAP_STDCMDLINEOUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <tclap/CmdLineInterface.h>
|
||||
#include <tclap/CmdLineOutput.h>
|
||||
#include <tclap/XorHandler.h>
|
||||
#include <tclap/Arg.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A class that isolates any output from the CmdLine object so that it
|
||||
* may be easily modified.
|
||||
*/
|
||||
class StdOutput : public CmdLineOutput
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Prints the usage to stdout. Can be overridden to
|
||||
* produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void usage(CmdLineInterface& c);
|
||||
|
||||
/**
|
||||
* Prints the version to stdout. Can be overridden
|
||||
* to produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void version(CmdLineInterface& c);
|
||||
|
||||
/**
|
||||
* Prints (to stderr) an error message, short usage
|
||||
* Can be overridden to produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param e - The ArgException that caused the failure.
|
||||
*/
|
||||
virtual void failure(CmdLineInterface& c,
|
||||
ArgException& e );
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Writes a brief usage message with short args.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param os - The stream to write the message to.
|
||||
*/
|
||||
void _shortUsage( CmdLineInterface& c, std::ostream& os ) const;
|
||||
|
||||
/**
|
||||
* Writes a longer usage message with long and short args,
|
||||
* provides descriptions and prints message.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param os - The stream to write the message to.
|
||||
*/
|
||||
void _longUsage( CmdLineInterface& c, std::ostream& os ) const;
|
||||
|
||||
/**
|
||||
* This function inserts line breaks and indents long strings
|
||||
* according the params input. It will only break lines at spaces,
|
||||
* commas and pipes.
|
||||
* \param os - The stream to be printed to.
|
||||
* \param s - The string to be printed.
|
||||
* \param maxWidth - The maxWidth allowed for the output line.
|
||||
* \param indentSpaces - The number of spaces to indent the first line.
|
||||
* \param secondLineOffset - The number of spaces to indent the second
|
||||
* and all subsequent lines in addition to indentSpaces.
|
||||
*/
|
||||
void spacePrint( std::ostream& os,
|
||||
const std::string& s,
|
||||
int maxWidth,
|
||||
int indentSpaces,
|
||||
int secondLineOffset ) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
inline void StdOutput::version(CmdLineInterface& _cmd)
|
||||
{
|
||||
std::string progName = _cmd.getProgramName();
|
||||
std::string xversion = _cmd.getVersion();
|
||||
|
||||
std::cout << std::endl << progName << " version: "
|
||||
<< xversion << std::endl << std::endl;
|
||||
}
|
||||
|
||||
inline void StdOutput::usage(CmdLineInterface& _cmd )
|
||||
{
|
||||
std::cout << std::endl << "USAGE: " << std::endl << std::endl;
|
||||
|
||||
_shortUsage( _cmd, std::cout );
|
||||
|
||||
std::cout << std::endl << std::endl << "Where: " << std::endl << std::endl;
|
||||
|
||||
_longUsage( _cmd, std::cout );
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
}
|
||||
|
||||
inline void StdOutput::failure( CmdLineInterface& _cmd,
|
||||
ArgException& e )
|
||||
{
|
||||
std::string progName = _cmd.getProgramName();
|
||||
|
||||
std::cerr << "PARSE ERROR: " << e.argId() << std::endl
|
||||
<< " " << e.error() << std::endl << std::endl;
|
||||
|
||||
if ( _cmd.hasHelpAndVersion() )
|
||||
{
|
||||
std::cerr << "Brief USAGE: " << std::endl;
|
||||
|
||||
_shortUsage( _cmd, std::cerr );
|
||||
|
||||
std::cerr << std::endl << "For complete USAGE and HELP type: "
|
||||
<< std::endl << " " << progName << " --help"
|
||||
<< std::endl << std::endl;
|
||||
}
|
||||
else
|
||||
usage(_cmd);
|
||||
|
||||
throw ExitException(1);
|
||||
}
|
||||
|
||||
inline void
|
||||
StdOutput::_shortUsage( CmdLineInterface& _cmd,
|
||||
std::ostream& os ) const
|
||||
{
|
||||
std::list<Arg*> argList = _cmd.getArgList();
|
||||
std::string progName = _cmd.getProgramName();
|
||||
XorHandler xorHandler = _cmd.getXorHandler();
|
||||
std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList();
|
||||
|
||||
std::string s = progName + " ";
|
||||
|
||||
// first the xor
|
||||
for ( int i = 0; static_cast<unsigned int>(i) < xorList.size(); i++ )
|
||||
{
|
||||
s += " {";
|
||||
for ( ArgVectorIterator it = xorList[i].begin();
|
||||
it != xorList[i].end(); it++ )
|
||||
s += (*it)->shortID() + "|";
|
||||
|
||||
s[s.length()-1] = '}';
|
||||
}
|
||||
|
||||
// then the rest
|
||||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
|
||||
if ( !xorHandler.contains( (*it) ) )
|
||||
s += " " + (*it)->shortID();
|
||||
|
||||
// if the program name is too long, then adjust the second line offset
|
||||
int secondLineOffset = static_cast<int>(progName.length()) + 2;
|
||||
if ( secondLineOffset > 75/2 )
|
||||
secondLineOffset = static_cast<int>(75/2);
|
||||
|
||||
spacePrint( os, s, 75, 3, secondLineOffset );
|
||||
}
|
||||
|
||||
inline void
|
||||
StdOutput::_longUsage( CmdLineInterface& _cmd,
|
||||
std::ostream& os ) const
|
||||
{
|
||||
std::list<Arg*> argList = _cmd.getArgList();
|
||||
std::string message = _cmd.getMessage();
|
||||
XorHandler xorHandler = _cmd.getXorHandler();
|
||||
std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList();
|
||||
|
||||
// first the xor
|
||||
for ( int i = 0; static_cast<unsigned int>(i) < xorList.size(); i++ )
|
||||
{
|
||||
for ( ArgVectorIterator it = xorList[i].begin();
|
||||
it != xorList[i].end();
|
||||
it++ )
|
||||
{
|
||||
spacePrint( os, (*it)->longID(), 75, 3, 3 );
|
||||
spacePrint( os, (*it)->getDescription(), 75, 5, 0 );
|
||||
|
||||
if ( it+1 != xorList[i].end() )
|
||||
spacePrint(os, "-- OR --", 75, 9, 0);
|
||||
}
|
||||
os << std::endl << std::endl;
|
||||
}
|
||||
|
||||
// then the rest
|
||||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
|
||||
if ( !xorHandler.contains( (*it) ) )
|
||||
{
|
||||
spacePrint( os, (*it)->longID(), 75, 3, 3 );
|
||||
spacePrint( os, (*it)->getDescription(), 75, 5, 0 );
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
os << std::endl;
|
||||
|
||||
spacePrint( os, message, 75, 3, 0 );
|
||||
}
|
||||
|
||||
inline void StdOutput::spacePrint( std::ostream& os,
|
||||
const std::string& s,
|
||||
int maxWidth,
|
||||
int indentSpaces,
|
||||
int secondLineOffset ) const
|
||||
{
|
||||
int len = static_cast<int>(s.length());
|
||||
|
||||
if ( (len + indentSpaces > maxWidth) && maxWidth > 0 )
|
||||
{
|
||||
int allowedLen = maxWidth - indentSpaces;
|
||||
int start = 0;
|
||||
while ( start < len )
|
||||
{
|
||||
// find the substring length
|
||||
// int stringLen = std::min<int>( len - start, allowedLen );
|
||||
// doing it this way to support a VisualC++ 2005 bug
|
||||
using namespace std;
|
||||
int stringLen = min<int>( len - start, allowedLen );
|
||||
|
||||
// trim the length so it doesn't end in middle of a word
|
||||
if ( stringLen == allowedLen )
|
||||
while ( stringLen >= 0 &&
|
||||
s[stringLen+start] != ' ' &&
|
||||
s[stringLen+start] != ',' &&
|
||||
s[stringLen+start] != '|' )
|
||||
stringLen--;
|
||||
|
||||
// ok, the word is longer than the line, so just split
|
||||
// wherever the line ends
|
||||
if ( stringLen <= 0 )
|
||||
stringLen = allowedLen;
|
||||
|
||||
// check for newlines
|
||||
for ( int i = 0; i < stringLen; i++ )
|
||||
if ( s[start+i] == '\n' )
|
||||
stringLen = i+1;
|
||||
|
||||
// print the indent
|
||||
for ( int i = 0; i < indentSpaces; i++ )
|
||||
os << " ";
|
||||
|
||||
if ( start == 0 )
|
||||
{
|
||||
// handle second line offsets
|
||||
indentSpaces += secondLineOffset;
|
||||
|
||||
// adjust allowed len
|
||||
allowedLen -= secondLineOffset;
|
||||
}
|
||||
|
||||
os << s.substr(start,stringLen) << std::endl;
|
||||
|
||||
// so we don't start a line with a space
|
||||
while ( s[stringLen+start] == ' ' && start < len )
|
||||
start++;
|
||||
|
||||
start += stringLen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i = 0; i < indentSpaces; i++ )
|
||||
os << " ";
|
||||
os << s << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue