Compare commits

..

428 commits

Author SHA1 Message Date
Ha Thach
4336fbe17a
Merge pull request #72 from adafruit/fix-tls-socket
Fix tls socket race condition
2025-07-28 21:48:33 +07:00
hathach
fa599780ee
bump up version, comment out socket reset if not connected in getClientStateTcp().
remove
2025-07-28 14:54:26 +07:00
hathach
91fc6766da
use socket mutex to prevent race condition.
revert gpio0Updater task priority to 1
2025-07-26 18:35:05 +07:00
hathach
28ce73b8a6
change gpio0Updater task priority to 2 (higher than loop task) to prevent race condition when checking socket connected() and available() 2025-07-25 23:38:22 +07:00
hathach
54ee662201
update to esp-idf v5.5 and arduino-esp32 v3.3.0 2025-07-25 23:21:43 +07:00
Ha Thach
36c78d4d4a
Merge pull request #70 from adafruit/idf-5.x
upgrade to Idf v5.3.2, added esp32c6
2025-06-26 22:12:14 +07:00
hathach
81c521d286
use base mac for getMACaddr() if interface is not up 2025-06-10 11:00:40 +07:00
hathach
40b28eae7e
move sdkconfig and board.h to boards folder
update combine.py to take build folder and output filename
run combine.py as postbuild
2025-06-03 11:02:28 +07:00
hathach
662b31ebc2
build need -DBOARD= instead of IDF_TARGET to allow flexible pinout for variant 2025-06-03 02:27:21 +07:00
hathach
74f8e1f93a
fix GPIO0 (esp32) GPIO9 (s6) as wifi available pin due to typo. Make our hook as IRAM and also correct the context where handleWiFiReceive() is called 2025-03-19 12:38:36 +07:00
hathach
07096cd812
add target for combined binary e.g NINA-ADAFRUIT-esp32, upload combined as artifacts and release asseets 2025-03-18 20:08:28 +07:00
hathach
82324914fe
update sketch.ino.cpp as closed as upstream
fix c6 compile issue in release build type
2025-03-18 16:12:42 +07:00
hathach
7f17ca5f40
- parameterize SPIs & UART pin setting
- move SPIS definition to sketch.ino.cpp
- max log level is info, debug will delay the initial boot causing reading CS incorrectly for BT and wifi mode (only available for 750ms)
2025-03-18 15:32:40 +07:00
hathach
f7c4cde6b9
BT works tested with esp32 2025-03-17 22:16:05 +07:00
hathach
5a6b5b3451
separate esp32 and c6 sdkconfig
testing BLE controller
2025-03-17 16:07:39 +07:00
Dan Halbert
1ad988ac30 more precise return value check on beginPacket() 2025-03-16 20:54:10 -04:00
Dan Halbert
a85404884e check WiFi.hostByName() return value properly 2025-03-16 19:27:54 -04:00
Dan Halbert
4d89dd36c2 fix combine.py to handle different bootloader offstes 2025-03-16 11:39:01 -04:00
Dan Halbert
74339a4809 use default root cert bundle provided by ESP-IDF 2025-03-14 14:53:19 -04:00
hathach
309a3ae589
try upload artifact with release/debug build 2025-03-12 12:08:17 +07:00
hathach
fa09a76bf7
wifitest work wwith esp32c6 2025-03-12 11:21:55 +07:00
hathach
85d1d6d8ea
fix esp32c6 compile issue 2025-03-11 22:43:39 +07:00
hathach
633451610d
update ci build 2025-03-11 21:29:10 +07:00
hathach
e1fcb6cbfc
use log_i() (default), log_d() is enabled when CMAKE_BUILD_TYPE=DEBUG is used. Also initial debug is also enable with debug build 2025-03-11 21:13:22 +07:00
hathach
0f3d2efb4f
remove managed_components 2025-03-11 20:59:53 +07:00
Dan Halbert
0cd6ae693f wip: preparing for c6; use sdkconfig.defaults, don't check in sdkconfig 2025-03-06 21:43:55 -05:00
Dan Halbert
1d83f417ed wip: HTTP working 2025-03-06 16:54:36 -05:00
Dan Halbert
1e8cfaad0d wip: compiles; crashes early; debug printing now working 2025-03-04 20:35:43 -05:00
Dan Halbert
a08e8cbb36 wip: restructure to use arduino-esp32 as a component 2025-03-03 23:32:26 -05:00
Dan Halbert
d6cc9859ce wip: use esp32-arduino libraries a lot more 2025-03-02 22:52:37 -05:00
Dan Halbert
14a5007afc wip: cmake conversion 2025-02-22 20:50:27 -05:00
Dan Halbert
dfc0c29971
Merge pull request #62 from adafruit/sync-upstream
Sync with upstream
2025-02-09 15:46:36 -05:00
Dan Halbert
27407c65db update certificates submodule; roots.pem got smaller 2025-02-09 15:23:48 -05:00
Dan Halbert
c432f797d6 allow longer version numbers 2025-02-09 15:23:08 -05:00
Dan Halbert
8638e99752 further reduce sockets to 6 to prevent intermittent failures 2025-02-08 18:57:35 -05:00
Dan Halbert
9b75975e8e revert '-adafruit' suffix on version number; it breaks fetching version 2025-02-06 19:07:54 -05:00
Dan Halbert
086a0a6c19 update to actions/upload-artifact@v4 2025-02-06 16:29:31 -05:00
Dan Halbert
215ece3283 more flexible version numbers; use adafruit suffix; bump to 2.0.0-adafruit 2025-02-06 16:12:16 -05:00
Dan Halbert
43816f100d manual merge of Arduino-specific trust anchors
From https://github.com/arduino/nina-fw/pull/100/files
2025-02-06 13:16:27 -05:00
Dan Halbert
d7fe1b5d8c Reduce sockets to 7 to avoid heap allocation failure; annotate Adafruit changes; add more debugging support
@hathach was using 8, but I got failures with very simple SSL socket use with 8.
Reducing to 7 fixed the problem. This is also dependent on the size of the roots.pem file.
Reducing the size of roots.pem also helps.
2025-02-06 13:05:36 -05:00
Limor "Ladyada" Fried
0ea40d89db
Merge pull request #65 from FoamyGuy/update_actions_version
update actions version
2024-11-07 09:49:20 -05:00
foamyguy
9bb4034f53 update actions version 2024-11-07 07:12:08 -06:00
Dan Halbert
bc73e26780 rerun 2024-05-02 12:19:17 -04:00
hathach
ca322bcb40 correct build ci 2023-12-21 23:21:44 +07:00
hathach
320ad469bb update ci to upload both release and debug bin firmware 2023-12-21 23:14:48 +07:00
hathach
88dac12ddb reduce CONFIG_LWIP_MAX_SOCKETS from 10 to 8 to save sram for ssl connection 2023-12-21 22:12:11 +07:00
hathach
338408fc78 Merge remote-tracking branch 'arduino/master' into sync-upstream
# Conflicts:
#	CHANGELOG
#	README.md
#	arduino/cores/esp32/wiring_digital.h
#	arduino/libraries/WiFi/src/WiFi.cpp
#	arduino/libraries/WiFi/src/WiFi.h
#	arduino/libraries/WiFi/src/WiFiClient.cpp
#	combine.py
#	data/roots.pem
#	main/CommandHandler.cpp
#	main/sketch.ino.cpp
#	sdkconfig
2023-12-20 00:44:36 +07:00
Ha Thach
b1145b466d
Merge pull request #57 from dhalbert/certificates-submodule
get roots.pem from adafruit/certificates repo
2023-12-19 17:52:02 +07:00
Dan Halbert
fc3c7f770b remove leftover debugging print() in combine.py 2023-12-14 12:14:39 -05:00
Dan Halbert
3e6ff08544 fetch submodules 2023-12-14 11:57:23 -05:00
Dan Halbert
c895779e4a get roots.pem from adafruit/certificates repo 2023-12-14 11:33:25 -05:00
Ha Thach
42f4766ab3
Merge pull request #61 from adafruit/add-action
add gh action
2023-12-13 10:34:58 +07:00
hathach
a399c40f53 reduce version duplication
- combine.py extract version from CommandHandler.cpp
- make load-nina use wildcard to load bin file
2023-12-12 19:04:15 +07:00
hathach
052e25993a more gh action update 2023-12-12 18:16:33 +07:00
hathach
f808187589 gh action upload bin when making release 2023-12-12 18:14:40 +07:00
hathach
fee5612088 try to add gh action 2023-12-12 18:06:42 +07:00
Ibrahim Abdelkader
feca16003a
Merge pull request #87 from JAndrassy/dns_ip
Add a new command to return the DNS IP.
2023-09-18 13:42:38 +03:00
Juraj Andrassy
7a5695dc68 DNS server IP getter handler 2023-09-18 12:39:42 +02:00
Brent Rubell
dffbd447bd
Merge pull request #54 from brentru/fix-windows-linux-upload
Fix Windows and Linux Esptool.py upload error caused by 1.7.5
2023-07-31 14:16:59 -04:00
brentru
d0d1c19b9a bump version to 1.7.6 2023-07-28 15:37:16 -04:00
brentru
3e529cf82f Fix windows and linux upload 2023-07-27 15:25:47 -04:00
Brent Rubell
21205e4005
Merge pull request #51 from brentru/add-io-root-cert-july-23
Add root CA cert for Adafruit IO, July 2023
2023-07-21 13:44:20 -04:00
brentru
dfa76d4d6f update 2023-07-21 13:44:04 -04:00
brentru
45cade91b4 update semver 2023-07-21 12:57:51 -04:00
brentru
e38b13bea7 add root CA for io 2023 july 2023-07-21 12:56:45 -04:00
Martino Facchin
c84aa34067
Merge pull request #79 from JAndrassy/server_accept
support server.accept() for WiFiNINA library
2022-06-09 13:00:35 +02:00
Martino Facchin
77d4881763
Merge branch 'master' into server_accept 2022-06-09 13:00:19 +02:00
Martino Facchin
af252c24be
Update README with info on combine.py 2022-06-09 12:47:20 +02:00
Martino Facchin
016e71eaae
Update CHANGELOG 2022-06-09 12:35:00 +02:00
Martino Facchin
bdb8b39a0d
Merge pull request #81 from iabdalkader/ninafw_bug_fixes
Ninafw bug fixes
2022-05-17 16:34:21 +02:00
iabdalkader
e8c31635e5 CommandHandler: Add BSD-like sockets API. 2022-05-17 12:27:07 +02:00
iabdalkader
cca05d5cf0 CommandHandler: Fix ADC attenuation value. 2022-05-17 12:27:07 +02:00
iabdalkader
495425a9b0 WiFiServer: Return and check error from begin(). 2022-04-25 16:35:17 +02:00
iabdalkader
90e1715e3d Update README.
* Nano RP2040 also requires the `no_reset` flag.
2022-04-25 13:21:07 +02:00
Limor "Ladyada" Fried
d73fe315cc
Merge pull request #41 from FoamyGuy/update_cert
updating cert
2022-02-25 21:43:01 -05:00
foamyguy
8b20b3e2f4 updating cert 2022-02-25 19:32:38 -06:00
Juraj Andrassy
da5aca87cf support server.accept() for WiFiNINA library 2022-01-07 14:55:33 +01:00
Alexander Entinger
63823f9eb3 Release v1.4.8. 2021-07-29 12:45:56 +02:00
Alexander Entinger
77ac756366
Merge pull request #73 from giulcioffi/fix_repeated_status_issue
Fix issue due to repeated status()/connected() call
2021-07-29 12:44:16 +02:00
giulcioffi
e303e391d1 Call peek() when there are no available data to make sure socket gets closed when connection ends 2021-07-29 10:26:14 +02:00
giulcioffi
e4040df857 Revert "Revert #50."
This reverts commit 6f82133a90.
2021-07-29 09:37:14 +02:00
Alexander Entinger
fc6bd42754 Release v1.4.7 2021-06-18 09:17:31 +02:00
Alexander Entinger
09eb519cf4
Merge pull request #71 from arduino/new-cert-list
Add new certificate list and instructions on how to build it.
2021-06-09 14:44:28 +02:00
Alexander Entinger
a33dc6d489 Eliminate commented-out bash code. 2021-06-08 15:25:19 +02:00
Alexander Entinger
a7f3bc2951 Extract constant openssl x509 parameters into variable to avoid duplication by copy. 2021-06-08 15:24:16 +02:00
Alexander Entinger
0d7c4628e7 Cleaning up certificate creation script. 2021-06-08 15:07:07 +02:00
Alexander Entinger
8cc5a433f5 Document how to use and the purpose of the SSL check script. 2021-06-08 15:03:03 +02:00
Alexander Entinger
224ef65d83 Release v1.4.6. 2021-06-08 08:46:33 +02:00
Alexander Entinger
756309ec4d Moving root creation script, adding USTERTRUST CA, generating new roots.pem. 2021-06-08 08:45:31 +02:00
Brent Rubell
0e267bc885
Merge pull request #37 from Gadgetoid/patch-changelog-v1.7.2
Insert v1.7.2 notes into CHANGELOG
2021-06-04 16:05:58 -04:00
Phil Howard
535ddb0d7b Insert v1.7.2 notes into CHANGELOG 2021-06-04 20:33:33 +01:00
Brent Rubell
104c48cb48
Merge pull request #36 from Gadgetoid/patch-set-hostname
Fix setHostname API
2021-06-04 15:17:24 -04:00
pennam
613015ae3f Initial sslcheck script to test roots.pem certificate file 2021-06-04 10:45:17 +02:00
Alexander Entinger
70ec99069c Add new certificate list and instructions on how to build it. 2021-06-04 09:58:28 +02:00
Phil Howard
e1385807c8 Bump version to 1.7.4 2021-06-03 15:04:53 +01:00
Phil Howard
6fc263c282 Fix setHostname API
Slightly modified version of the upstream fix to the set hostname API,
found here: f63b70aa3d

Sets the custom hostname in the system STA_START event, rather than directly. Falls back to "defaultHostname"

Allows a custom client-mode hostname to be set.

Co-authored-by: Riccardo Rizzo <r.rizzo@arduino.cc>
2021-06-02 18:14:18 +01:00
Alexander Entinger
9b4c4355cb
Merge pull request #67 from arduino/rp2040_final
Adding support for Arduino RP2040 Nano Connect
2021-05-05 14:33:11 +02:00
Alexander Entinger
de7f0db6a5
Merge pull request #68 from MattyBoy4444/patch-1
Added ISRG Root X1 for LetsEncrypt
2021-05-04 10:20:08 +02:00
MattyBoy
f857f944cd
Added ISRG Root X1 for LetsEncrypt
Added missing root cert
2021-05-04 03:13:37 -04:00
Martino Facchin
8713b0938d Release 1.4.5 2021-05-01 14:33:06 +02:00
Martino Facchin
c6000b22dc Merge remote-tracking branch 'arduino/master' into rp2040 2021-05-01 14:32:37 +02:00
Martino Facchin
3832dae91b rp2040_connect: change pinmux 2021-05-01 14:32:29 +02:00
Alexander Entinger
51acbd91c0 Release v1.4.4 2021-04-27 11:43:34 +02:00
Alexander Entinger
ac95d6a459
Merge pull request #66 from arduino/fix-reconnection-errors
Fix firmware reconnection errors
2021-04-27 11:39:48 +02:00
Alexander Entinger
d4075376ac Bugfix: Opening a TCP connection when having a running BEARSSL connection leads to a conflict.
BearSSLClient is injected with tcpClients[0], but if a new TCP connection is requested than tcpClients[0] is assigned to the new connection which is of course a conflict with BearSSL which needs this very same WiFiClient instance.
2021-04-26 15:45:54 +02:00
Alexander Entinger
6f82133a90 Revert #50. 2021-04-26 15:43:29 +02:00
Alexander Entinger
eeefeb598f Replacing 'recv' with 'lwip_recv_r' ensures that the desired recv function is called. 2021-04-26 15:42:06 +02:00
Alexander Entinger
054edb8798 Release v1.4.4. 2021-04-13 09:12:33 +02:00
Alexander Entinger
bed5b6268a Fix: There is no need to manually power-up the ADC, as the ADC is powered-up within 'adc1_get_raw' 2021-04-13 08:36:04 +02:00
Alexander Entinger
ae9c8535b5 Add API for configuring a GPIO as INPUT_PULLUP. 2021-04-13 07:17:09 +02:00
Alexander Entinger
a323261c55 Implement analogRead() API which allows to read the value provided at any of the analog pins of the ESP32. 2021-04-12 15:23:41 +02:00
Alexander Entinger
0f9f831086 Implement digitalRead API which allows to read the status of a digital input pin from the host processor. 2021-04-12 14:46:02 +02:00
Brent Rubell
ec9e20f508
Merge pull request #33 from ZodiusInfuser/master
Added INPUT_PULLUP support and let analog write use full PWM range
2021-03-31 11:13:52 -04:00
ZodiusInfuser
0c19f316c3
Merge branch 'master' into master 2021-03-27 12:45:39 +00:00
Brent Rubell
2c3edb8d82
Merge pull request #31 from ajs256/patch-1
Merge in commit 73375c3 from upstream
2021-03-26 16:10:19 -04:00
ZodiusInfuser
6782e16f47 Advanced version by one patch 2021-03-26 15:39:12 +00:00
ZodiusInfuser
d0074840ab Added INPUT_PULLUP support and let analog write use full PWM range 2021-03-22 16:52:06 +00:00
ajs256
4a7e461534
Update version number, part 3 2021-03-17 15:46:19 -07:00
ajs256
197c50ffe5
Update version number, part 2 2021-03-17 15:45:53 -07:00
ajs256
516f3feb9e
Update version numbers, part 1 2021-03-17 15:45:27 -07:00
ajs256
d2726992d8
Merge in commit 73375c3 from upstream
Replace `peek()` with `available()` during connection status check to avoid losing messages.

Closes adafruit#22.
2021-03-04 17:32:51 -08:00
Martino Facchin
6bb97b6a89 rp2040: give LEDs safe defaults 2021-03-01 13:06:55 +01:00
Martino Facchin
92b38f6b39 Fix makefile 2021-03-01 09:54:16 +01:00
Martino Facchin
c3e699de00 Fix gpios for Nano rp2040 connect 2021-03-01 09:48:28 +01:00
Alexander Entinger
443042362d
Merge pull request #62 from giulcioffi/Issue61_lwip_disconn
Do not immediately close connection after 1 failed write
2021-01-29 08:32:09 +01:00
Alexander Entinger
a44bbbb89d Release v1.4.3 2021-01-29 08:28:09 +01:00
giulcioffi
b5d9a387b5 Return 0 instead of -1 if write fails 2021-01-28 12:40:50 +01:00
giulcioffi
27ecd662c4 Do not close socket if send fails 2021-01-28 12:40:50 +01:00
Alexander Entinger
00a6c79255
Merge pull request #57 from facchinm/bearssl-nina-hack
Port BearSSL + crypto integration to NINA
2021-01-28 12:02:56 +01:00
Martino Facchin
55c21a4b88 Release 1.4.2 2021-01-27 17:33:33 +01:00
Martino Facchin
cd843d8ebd Increase delays for reliable ECCx08 operations 2021-01-27 17:33:13 +01:00
Martino Facchin
3e7590d3e9 Merge remote-tracking branch 'arduino/master' into HEAD 2021-01-27 14:28:35 +01:00
Alexander Entinger
70b08d85f8 Adding section to README explaining how to update the UNO WIFI REV2 NINA module via SerialNINAPassthrough 2020-11-17 09:50:24 +01:00
Dan Halbert
f2a0e601b2
Merge pull request #29 from adafruit/tannewt-patch-1
Add some newlines to improve debug output
2020-11-04 19:17:03 -05:00
Scott Shawcroft
3960966446
Add some newlines to improve debug output 2020-11-04 16:12:20 -08:00
Martino Facchin
200f09d1d5
Merge pull request #55 from giulcioffi/issue116BLE
Increase max number of BLE connection up to 7
2020-10-26 08:49:07 +01:00
Limor "Ladyada" Fried
d1a03dd26d
Merge pull request #27 from dhalbert/fix-hci
Fix BLE HCI mode
2020-10-24 20:52:41 -04:00
Dan Halbert
0365542bc1 Fix BLE HCI mode 2020-10-24 18:31:46 -04:00
giulcioffi
cc7006833f Increase max number of BLE connection up to 7 2020-10-22 17:59:18 +02:00
Martino Facchin
be90d36495 Backwards compatibility: use 0x04 for BearSSLClient 2020-10-20 16:34:53 +02:00
Martino Facchin
f2a0cfddb3 Offload BearSSL to Nina module 2020-10-20 16:28:55 +02:00
Martino Facchin
8f986c1892 Add proper bearsslClient with socket_type == 3 2020-10-19 12:31:05 +02:00
Alexander Entinger
7b7bd471e2 Use Arduino IoT Cloud TAs and add a bit of curious logging 2020-09-11 07:09:55 +02:00
Alexander Entinger
93925ac61c Use ArduinoIoTCloud trust anchors instead of the generic default ones (which anyway don't include the ArduinoIoTCloud TAs) 2020-09-11 07:08:06 +02:00
Alexander Entinger
1b7b7f78fc Adding CryptoUtil and EXXX08Cert classes to git (forgotten until now - although used) 2020-09-11 06:44:47 +02:00
Alexander Entinger
e2577c267b ECCX08 fully configurable on startup now (due to fixing I2C Wire bug) 2020-09-11 06:42:47 +02:00
Alexander Entinger
e6c4dd60e8 Use ArduinoCore-API:Ringbuffer and increase size to 256 bytes. In ECCX08CertClass::generatePublicKey whey are performing a 64 byte read from the ECCX08. Although the previously used Ringbuffer was 64 bytes large it contained in fact a bug that reduced it's effective size to 63 bytes which resulted in an infinite loop. 2020-09-11 06:40:13 +02:00
Alexander Entinger
f5ae0c855e Use ArduinoCore-API:Ringbuffer implementation (the one present in this core contains bugs) 2020-09-11 06:38:03 +02:00
Alexander Entinger
05da3b1ef5 Adding partial certificate reconstruction 2020-09-07 06:56:23 +02:00
Alexander Entinger
f7b76d6721 Fixing up WiFiClient in the presence of a IPAddress class 2020-09-02 16:01:44 +02:00
Alexander Entinger
7cdef7c3e7 Ensure that ArduinoBearSSL/bearssl and ArduinoBearSSL/utility directories are compiled too. 2020-09-02 15:45:05 +02:00
Alexander Entinger
991d4cda7b Ajusting WiFiClient to be derived from Client in order to spoonfeed it to BearSSLClient 2020-09-02 15:44:28 +02:00
Alexander Entinger
6bac6fb182 Cleaning up IPAddress (would become an abstract base class otherwise and could not be instantiated) due to pruning due to fitting in this minimal core 2020-09-02 15:43:50 +02:00
Alexander Entinger
b2ce6df1c8 Compile ArduinoBearSSL 2020-09-02 13:58:29 +02:00
Alexander Entinger
34b7097936 Add global C++ define ARDUINO which is required by ArduinoBearSSL to include int br_sslio_read_available(br_sslio_context *cc); and int br_sslio_peek(br_sslio_context *cc, void *dst, size_t len);" 2020-09-02 13:57:39 +02:00
Alexander Entinger
cf26475c64 Adding Client/IPAddress and extending Arduino.h and WMath to provide all the functions necessary for compiling ArduinoBearSSL 2020-09-02 13:56:44 +02:00
Alexander Entinger
70fdc0d575 Moving ArduinoBearSSL code to arduino/libraries/ArduinoBearSSL 2020-09-02 13:43:21 +02:00
Alexander Entinger
f82ccd851c Integrate ArduinoBearSSL with nina fw. 2020-09-02 13:40:21 +02:00
Alexander Entinger
2768bc43ac Use a maximum of 400 kHz when connecting ECCX08 with ESP32 nina module 2020-09-02 12:48:39 +02:00
Alexander Entinger
8632328d95 Correcting I2C pin assignment 2020-09-02 12:48:04 +02:00
Alexander Entinger
768fd8ed84 Initialize ECCX08 when nina is used for WIFI 2020-09-02 10:11:38 +02:00
Alexander Entinger
03dbeacd4a Adding min/max/max (min is needed by ArduinoECCX08 2020-09-02 09:03:23 +02:00
Alexander Entinger
8fed17bd4f Adding a delayMicroseconds as required by the core 2020-09-02 09:03:00 +02:00
Alexander Entinger
9b0289b6c4 Fixing now differently named union member 2020-09-02 09:02:35 +02:00
Alexander Entinger
ad851ea424 Add Wire to the libraries which need to be compiled 2020-09-02 08:49:32 +02:00
Alexander Entinger
0b5b3649ca Move Wire.h/cpp into src subfolder (where it belongs) 2020-09-02 08:49:07 +02:00
Alexander Entinger
28d6ced436 Adding missing core files for being able to compile Wire 2020-09-02 08:48:27 +02:00
Alexander Entinger
1f97e6eef9 Moving Wire library in correct place 2020-09-02 07:34:16 +02:00
Alexander Entinger
f005e15c56 Integrate bcmi-labs/ESP32/libraries/Wire while preserving history. 2020-09-02 07:31:03 +02:00
Alexander Entinger
0542d5507f Moving extracted Wire library back to the subfolder it belongs 2020-09-02 07:30:01 +02:00
Alexander Entinger
7020ad26fa Add ArduinoECCX08 to firmware build 2020-09-02 07:22:56 +02:00
Alexander Entinger
d6e0e3ea7b Moving the imported ArduinoECCX08 library into desired subfolder arduino/libraries/ArduinoEXXX08 2020-09-02 07:16:53 +02:00
Alexander Entinger
a67d4f0957 Integrating ArduinoECCX08 while preserving history. 2020-09-02 07:14:28 +02:00
Brent Rubell
4b244e7559
Merge pull request #25 from adafruit/revert-24-github-actions-test
Revert "add workflow for build"
2020-08-23 11:14:15 -04:00
Brent Rubell
731718b6d8
Revert "add workflow for build" 2020-08-23 11:13:56 -04:00
Brent Rubell
20f66f10d5
Merge pull request #24 from brentru/github-actions-test
add workflow for build
2020-08-23 11:12:33 -04:00
brentru
1961926613 add workflow for build 2020-08-23 11:11:42 -04:00
Limor "Ladyada" Fried
2c3a8e2230
Merge pull request #23 from adafruit/BT_HCI
BT HCI support in airlift
2020-08-21 14:07:30 -04:00
Martino Facchin
07a98a6da8
Merge pull request #54 from luigigubello/cert_renew
Renewing CA certificates
2020-08-17 15:53:31 +02:00
Luigi Gubello
18064ffd67 Renewing CA certificates 2020-08-17 15:02:32 +02:00
Alexander Entinger
6b0137b7aa Release v1.4.1 2020-08-17 11:05:15 +02:00
Alexander Entinger
d4a2118db2
Merge pull request #53 from arduino/nina-download-ota
Add 'downloadOTA' command to download OTA file and verify length/CRC
2020-08-06 08:11:08 +02:00
Alexander Entinger
ec7c1c82ce Allow the passage of a server certificate when downloading from URL 2020-08-06 08:04:38 +02:00
Alexander Entinger
6e0c3d2939 Add 'downloadOTA' command to download OTA file and verify length/CRC 2020-08-04 14:57:15 +02:00
Alexander Entinger
44db420fed Allow default singleton on different Wire (#26)
Co-authored-by: Martino Facchin <m.facchin@arduino.cc>
2020-07-24 10:37:32 +02:00
Alexander Entinger
83b0d101d2 Release v1.4.0 2020-07-13 07:05:01 +02:00
Alexander Entinger
701b3c0a8f
There's no need to prepend the filename with '/fs/' for file operations since that's not done for any of the other file operations (#51) 2020-07-09 12:19:47 +02:00
giulcioffi
2fe9f0cfff
Remove packet parsing from availDataTcp() to avoid packets corruption (#49) 2020-07-09 11:50:34 +02:00
giulcioffi
759397ba57
Replace peek() with available() during connection status check to avoid loosing messages (#50) 2020-07-09 11:48:24 +02:00
Alexander Entinger
1a5604f3bc
Merge pull request #32 from arduino/unowifirev2_ota
Implement filesystem operation on spare flash and UNO WiFI OTA
2020-06-24 11:59:52 +02:00
Alexander Entinger
65d6e1aacf Adding the errno code of the rename operation to the response message 2020-06-24 09:47:51 +02:00
lady ada
56d7b740ed bump version, change HCI uart to main uart. kill main thread when done setting up bt 2020-06-19 18:36:22 -04:00
lady ada
43731b8da5 debuggy but working with RX/TX on 22/23, gpio 0 must be pulled down by arduino for flow control 2020-06-19 18:14:44 -04:00
Brent Rubell
240fef2ca8
Merge pull request #21 from virgilvox/master
Added AWS Certs
2020-06-11 15:05:09 -04:00
Moheeb Zara
e19252de3f bump to verson 1.6.1 2020-06-11 11:49:32 -07:00
Moheeb Zara
76b9fc6339
Added AWS Certs
Added AmazonRootCA1 and AmazonRootCA3 for AWS IoT
2020-06-10 17:32:04 -07:00
Alexander Entinger
abb02a3cdc Adding 'rename' operation necessary for using the WiFiNiNa for OTA 2020-06-10 11:14:14 +02:00
Alexander Entinger
885eae2292 Adding an additional BearSSL constructor as well as a method setClient to allow late initialisation. (#29)
This allows for the ArduinoIoTCloud firmware to intantiate ArduinoBearSSL on the stack instead of the heap.
2020-05-28 14:39:01 +02:00
Giampaolo Mancini
5de9bb0a6c Safer implementation of no-SNI connection (#24) 2020-05-28 07:44:19 +02:00
Fabrice Fontaine
c666e0cc9b Use two buffers instead of split mode (#18)
Use two dedicated buffers for input and output instead of split mode.

Indeed some MQTT server (especially with TLS) needs a full 8k buffer as
they send their Certificate. On the other hand, on output, a smaller
buffer is needed.

Clients will be able to finely tune those values by defining
BEAR_SSL_CLIENT_{I,O}BUF_SIZE before including ArduinoBearSSL.h, the
default default values have been chosen to keep current behavior as
requested during review.

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2020-05-18 13:18:31 +02:00
Giampaolo Mancini
0957a82aca Fix local TA + implicit noSNI constructor 2020-03-30 18:55:02 +02:00
Giampaolo Mancini
00521cd59b Add support for not sending any SNI 2020-03-30 10:24:12 +02:00
Sandeep Mistry
3ba538f27e Version 1.3.0 2019-12-23 11:35:46 -05:00
Sandeep Mistry
a2cf3b9797 WPA2 Enterprise support (PEAP/MSCHAPv2 + EAP-TLS) 2019-12-23 09:37:56 -05:00
Sandeep Mistry
f500453578 Update to IDF v3.3.1 2019-12-23 09:37:56 -05:00
Brent Rubell
0a386927f3
Merge pull request #18 from brentru/update-to-esp-idf-3-3-1
Update to use ESP-IDF v3.3.1
2019-12-19 10:03:17 -05:00
brentru
de4371626b update README to reflect working CoC urls 2019-12-18 15:53:05 -05:00
brentru
2cff6632ee update travis for deployment on v3.1.1 2019-12-18 13:47:44 -05:00
brentru
2361e23881 update to 3.3.1, firmware v1.6.0 2019-12-18 13:45:04 -05:00
Arturo Guadalupi
175c192f2b
Update README.md
Use IDF V3.3
2019-11-19 09:46:04 +01:00
Brent Rubell
b569157e09
Merge pull request #15 from brentru/add-travis-build-script
Add Travis CI Script for nina-fw
2019-10-31 13:57:50 -04:00
brentru
22c0f001ee enable file_glob for * pattern 2019-10-31 11:59:37 -04:00
brentru
2390b88243 deploy on tag releases 2019-10-31 11:48:28 -04:00
brentru
a4ac932c3f drop tag 2019-10-31 11:31:58 -04:00
brentru
1c72c5ae71 reduce releases 2019-10-31 11:31:13 -04:00
brentru
6ae580d917 add travis badge 2019-10-31 11:26:04 -04:00
brentru
6809f38aa8 deploy based on nina_102*.bin 2019-10-31 11:18:34 -04:00
brentru
1c9a07eff4 update deployment script 2019-10-31 11:13:56 -04:00
brentru
999710e92d add gperf for cmake 2019-10-31 11:08:14 -04:00
brentru
9f02cb294b fix untar location 2019-10-31 11:04:45 -04:00
brentru
497e8332c4 py 3.5 2019-10-31 10:50:54 -04:00
brentru
613da21bb3 bump esp toolchain version 2019-10-31 10:50:21 -04:00
brentru
16708753b4 add deployment, drop gperf, python3 2019-10-31 10:47:37 -04:00
brentru
fa6a438981 add test build script... 2019-10-31 10:43:02 -04:00
Brent Rubell
d68d708739
Merge pull request #14 from anecdata/Kraken_II
New command handlers for Digital Read and Analog Read. New command for Analog Read.
2019-10-23 12:51:22 -04:00
anecdata
8a15b9e195 Metadata updates
Pulled over a code-of-conduct.md from a random Arduino library (the 
CircuitPython one mentions a lot of CircuitPython specifics), and added 
**Contributing** section to README. Curious that there isn't a central 
place where a code of conduct can be stored, with a reference rather 
than a copy in each repo.

Updated CHANGELOG for 1.3.1, 1.4.0, and 1.5.0.
2019-10-21 10:57:43 -05:00
anecdata
fa5f7a5486 v1.5.0. 2019-10-17 10:51:01 -05:00
anecdata
687498c981 Bumped version to 1.4.1 2019-10-17 10:17:38 -05:00
anecdata
6bb17bb179 Add basic ADC calibration. 2019-10-17 00:12:35 -05:00
anecdata
b9c66fa1b1 Convert pin to ADC1 channel. 2019-10-14 00:32:48 -05:00
anecdata
5687201ec9 Add setAnalogRead type & command handler. Add core analogRead function. 2019-10-13 21:32:06 -05:00
anecdata
b90bbd6349 Add setDigitalRead command handler type and command handler. 2019-10-13 14:13:15 -05:00
Brent Rubell
e813fbe664
Merge pull request #13 from brentru/add-ESP-33
Update nina-fw to ESP-IDF 3.3 (LTS)
2019-10-09 12:36:34 -04:00
brentru
cd594a1617 update sdkconfig, documentation for ESP-IDFv3.3. Makefile load-nina reflects latest firmware version 2019-10-09 11:39:24 -04:00
Limor "Ladyada" Fried
08bdc03880
Merge pull request #11 from brentru/certificate-work
Extending WifiSSLClient
2019-10-08 23:06:47 -04:00
brentru
97b8ce167d update combine.py to 1.4.0 2019-10-08 22:58:47 -04:00
brentru
133f48dd74 fix release cflags to be less verbose 2019-10-08 16:46:18 -04:00
brentru
2934d32d1f revert to standard sdkconfig 2019-10-08 16:42:31 -04:00
brentru
d900dae127 private _handshake_timeout, set to reflect arduino-esp32 WiFiClientSecure by default 2019-10-08 16:41:11 -04:00
brentru
c0e30f4192 strip out uf2s, generalize makefile 2019-10-08 16:24:42 -04:00
brentru
8be4f3dbed set the cert/key given a known socket within startClientTCP 2019-10-08 13:32:24 -04:00
brentru
33bfa15fc8 use key provided from code instead 2019-10-08 13:06:05 -04:00
brentru
5256ea5247 Bumping TLS RX buffer back up, 16384 bytes instead of 8182 bytes 2019-10-08 12:51:06 -04:00
brentru
7217c83ed1 set user-provided buffer 2019-10-08 12:44:19 -04:00
brentru
3bd209446a add private key from WifiSSLClient into CommandHandler instead 2019-10-08 12:33:43 -04:00
brentru
cce35ecb74 set certificate within CommandHandler, setCertKey call for certificate 2019-10-08 12:18:14 -04:00
brentru
0d6137a8a9 drop char buffer sizes down to more realistic sizes 2019-10-08 11:23:54 -04:00
brentru
ac197818d3 set certificate sizes, works 2019-10-08 11:14:11 -04:00
brentru
54939b773e add extra prints, \n at tend of printfs 2019-10-07 17:00:28 -04:00
brentru
3be514d04d const char -> char 2019-10-07 16:54:59 -04:00
brentru
369381ecf0 making const chars globals, working connection to AWS IOT broker 2019-10-07 16:48:11 -04:00
brentru
40b46f1329 add printing for final deallocation() 2019-10-03 16:01:46 -04:00
brentru
c697543815 remove free() call, causes errors, remove Bluetooth in menuconfig 2019-10-03 14:51:08 -04:00
brentru
58bfe02bcb remove AWS_CERT_CRT, add check for heap into handler 2019-10-03 14:25:47 -04:00
brentru
872f1cd1f3 inc. TLS Maximum Message Length to 8KB 2019-10-03 14:20:33 -04:00
brentru
eeb579fb9c add heap size check 2019-10-03 13:09:03 -04:00
brentru
fb9f9cc3bb reduced tls maximum message content length in menuconfig to 4KB 2019-10-03 13:07:14 -04:00
brentru
5c75800a83 print cert, return on error after free'ing 2019-10-03 12:49:20 -04:00
brentru
d2fa1ba9cc add MBEDTLS_SSL_ALLOC_FAILED error 2019-10-03 12:24:31 -04:00
brentru
ab949fe922 malloc and fail certificate 2019-10-03 12:13:43 -04:00
brentru
a7efa04365 start breaking out the handler 2019-10-02 17:40:41 -04:00
brentru
5dbda1a8be upload port as variable 2019-10-01 17:15:30 -04:00
brentru
3b9427b282 create connect(host,port) function 2019-10-01 16:25:22 -04:00
brentru
ae8a059e89 add verification for cert, freeup heap 2019-10-01 16:10:48 -04:00
brentru
beb3a98b05 removed ssl/tls handshake, replaced with one which honors ssl handshake timeout 2019-10-01 15:59:13 -04:00
brentru
e2761767e7 add loading/ for client key and certificate 2019-10-01 15:05:05 -04:00
brentru
4b30ab4284 print return code and more verbose errors, move things around to correspond with their logical steps 2019-10-01 14:44:49 -04:00
brentru
0b558d2ea3 move pers to h, add return code int 2019-10-01 14:22:13 -04:00
brentru
d7fb4c556e entropy init. incl. device-specific identifiers instead of NULL 2019-10-01 14:14:58 -04:00
brentru
f3cbd721cb add a handshake timeout, stop() should free the new clientCr and clientKey 2019-10-01 13:56:47 -04:00
brentru
ffbca9c1e3 add setCertificate, setPrivateKey, mbed headers 2019-10-01 13:42:09 -04:00
brentru
e26d792713 remove certificate malloc for now 2019-10-01 12:13:05 -04:00
brentru
ae2b0660ca upload-nina and upload-circuitpython 2019-10-01 12:04:18 -04:00
brentru
38a1cd7ee8 swap out esphelper for makefile commands to throw m4 into passthrough, upload via esptool, and start the serial 2019-10-01 11:34:04 -04:00
brentru
bd00a04d30 helper script for uploading uf2, flashing firmware, openin uart 2019-09-30 17:14:35 -04:00
brentru
65b4f7dd56 try debug level logging 2019-09-30 16:51:56 -04:00
brentru
95066a7d29 add UF2 for SerialPassThru with the AirLift FeatherWing 2019-09-30 16:50:23 -04:00
brentru
449e8ddd29 add setClientCert, setCertKey, add debug prints, define commands 2019-09-30 16:43:43 -04:00
Sandeep Mistry
5451041458 Add errorCode() API 2019-08-20 10:34:14 -04:00
Sandeep Mistry
dfcb32b1f3 Allow built-in trust anchors to be overrided via constructor 2019-08-19 09:26:00 -04:00
Martino Facchin
072354249d Add OTA routine for UNO WiFi rev2 2019-08-09 10:13:36 +02:00
Martino Facchin
a567034480 Add downloadFile helper 2019-08-08 17:38:23 +02:00
Martino Facchin
ad93421f36 Initial implementation of FS handling 2019-08-08 17:35:34 +02:00
Alan Chen
c14d921460 #define BEAR_SSL_CLIENT_IOBUF_SIZE 2019-07-18 10:33:50 -04:00
Riccardo Rizzo
f63b70aa3d fix setHostname API
fixed set host name API now allow to set a hsotname with max lenght 32 characters, otherwise set the default one
2019-07-16 10:55:43 -04:00
Riccardo Rizzo
00b93d86c0 Fix channel's assignement 2019-07-15 12:18:27 -04:00
Martino Facchin
17d506e81f Increase delay in sign()
Calling sign() with 60ms of delay is not ok on ECC608, while it works just fine on 508. 70ms looks fine for both.
2019-06-17 12:55:43 +02:00
Martino Facchin
bb31ec8102 Update fw version to 1.2.4 2019-06-14 15:46:44 +02:00
Martino Facchin
4bf4a4f5dc Update to esp-idf v3.3 2019-06-14 15:45:39 +02:00
Limor "Ladyada" Fried
47875b775a
Merge pull request #5 from docmollo/docs_idf_cleanup
Various documentation cleanups
2019-05-19 19:19:07 -04:00
Martino Facchin
f28554d94d
Merge pull request #20 from sandeepmistry/issue-18-fix
Add small delay to WiFiUDP.parsePacket() allow other tasks to run
2019-05-02 12:13:28 +02:00
Sandeep Mistry
a693f84cb9 Add small delay to WiFiUDP.parsePacket() allow other tasks to run 2019-04-26 13:40:05 -04:00
Dustin Mollo
b665dc1fdd minor cleanup 2019-04-24 15:48:25 -07:00
Dustin Mollo
e2df4fdb4e Updated README.md to incdicate use of Espressif's IDF v3.2; various other clean-ups 2019-04-24 15:44:09 -07:00
Sandeep Mistry
70ab489d2a Disable CONFIG_LWIP_DHCP_DOES_ARP_CHECK 2019-04-11 12:27:59 -04:00
Sandeep Mistry
3c55153ed6 Version 1.2.3 2019-04-09 09:33:58 -04:00
Sandeep Mistry
f8756c882b WiFiSSLClient: enable SNI vis mbedtls_ssl_set_hostname when using hostname 2019-04-09 09:19:09 -04:00
Sandeep Mistry
81f91c1e26 Add reason code command 2019-04-09 09:17:59 -04:00
Limor "Ladyada" Fried
67980cf636
Merge pull request #4 from docmollo/docs_wpa2_enterprise
Firmware version tweaks
2019-04-06 20:08:36 -04:00
Dustin Mollo
93b4959250 Update firmware version number and mod'd combine.py to have version number in generated firmware file 2019-04-06 10:26:47 -07:00
Sandeep Mistry
b08837e4f5 Add SHA1 and SHA256 API's and examples 2019-04-05 10:50:15 -04:00
Limor "Ladyada" Fried
e427aad6be
Merge pull request #3 from docmollo/docs_wpa2_enterprise
Add initial WPA2 Enterprise support
2019-04-04 19:50:10 -04:00
Dustin Mollo
bb06eb887a ignore firmware files in top level 2019-04-04 13:19:11 -07:00
Dustin Mollo
2d853b81a4 WPA2 Enterprise additions 2019-04-04 13:10:14 -07:00
Sandeep Mistry
0e5b41e329 Define SHA1 function as SHA1_ to make it a "private" symbol when building on Arduino 2019-04-04 14:06:13 -04:00
guillep2k
9861c11f85 Fixes to various integer encoding 2019-03-28 16:32:47 -04:00
Sandeep Mistry
c8fd53f096 Version 1.2.2 2019-03-26 10:01:47 -04:00
Sandeep Mistry
f01ce17d3b Update to IDF v3.1.3 2019-03-26 09:55:14 -04:00
Sandeep Mistry
4ec78c64ce Don't call tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA)
Sometimes the DNS servers would be set to 0.0.0.0
2019-03-26 09:36:53 -04:00
Sandeep Mistry
06e362c454 Track if static IP is assigned in separate var, clear event group bits before stop 2019-03-26 09:36:53 -04:00
Sandeep Mistry
b13dcbafec Call freeaddrinfo after getaddrinfo 2019-03-26 09:36:53 -04:00
Sandeep Mistry
63c42dd176 Replace gethostbyname calls with getaddrinfo 2019-03-26 09:36:53 -04:00
Sandeep Mistry
c31790876b Close all non-listening/bound sockets on wifi disconnect instead of just TCP sockets 2019-03-21 15:05:15 -04:00
Sandeep Mistry
79ff5cf761 Close all client TCP sockets when the WiFi is disconnected, this avoids hangs in WiFiClient::connect 2019-03-21 15:05:15 -04:00
Sandeep Mistry
b3257ddd55 Add support for megaavr arch 2019-03-19 14:54:54 -04:00
Sandeep Mistry
71cef5e3a0 Add JWS support utilty and setup sketch 2019-03-19 09:34:29 -04:00
Limor "Ladyada" Fried
35966a57f9
Merge pull request #2 from jerryneedell/jerryn_nobin
remove .bin file
2019-02-28 14:42:29 -05:00
Jerry Needell
a5fcd5b435 remove .bin file 2019-02-28 14:31:37 -05:00
Limor "Ladyada" Fried
38dffb0ee9
Merge pull request #1 from jerryneedell/jerryn_version
update version to 1.2.2
2019-02-28 14:11:39 -05:00
Jerry Needell
db81c87f59 remove sdkconfig.old 2019-02-28 14:04:17 -05:00
Jerry Needell
2b31ee733a update version # to 1.2.2 2019-02-28 13:59:45 -05:00
Jesse Kerkhoven
4cbaae83b0 Put device to sleep when calling end() 2019-02-25 10:42:08 -05:00
ladyada
6179c0981d fix missing set hostname for TLS, update to latest IDF, add debug (can remove later) 2019-02-16 22:54:41 -05:00
Sandeep Mistry
cd73b9d222 Add default serial number with value of 1 2019-02-11 09:53:06 -05:00
Sandeep Mistry
98350c7157 Print the SHA1 of the cert 2019-02-11 09:53:06 -05:00
Sandeep Mistry
453a5f9340 Compile warning 2019-02-11 09:53:06 -05:00
Sandeep Mistry
fd8ef8d3e6 Add SHA1 from https://github.com/clibs/sha1
Commit d5b29e11ec4871b78b370e8e00bd0cd56d798dda
2019-02-11 09:53:06 -05:00
Sandeep Mistry
abdd3fe467 Add ECCX08SelfSignedCert to generate + store self signed certs and restore 2019-02-11 09:53:06 -05:00
Sandeep Mistry
703d84f707 Split out PEM utils, return append length for some ASN.1 utils 2019-02-11 09:53:06 -05:00
Sandeep Mistry
61054d17e0 More ASN.1 utils 2019-02-11 09:53:06 -05:00
Sandeep Mistry
fa772f3905 Add API to get serial number as bytes 2019-02-11 09:53:06 -05:00
Sandeep Mistry
69c2e4a696 Split out ASN.1 to new files 2019-02-11 09:53:06 -05:00
ladyada
9e76479a7e change pin to not conflict with boot, add some debugs 2019-02-10 14:32:56 -05:00
Sandeep Mistry
ee167aba00 Use phy_init partition for phy init data and use recommended u-blox data 2019-01-31 16:37:49 -05:00
Riccardo Rizzo
c4f8990430
Merge pull request #9 from sandeepmistry/faster-connect
Use WIFI_FAST_SCAN for connection instead of WIFI_ALL_CHANNEL_SCAN
2019-01-29 16:25:36 +01:00
Sandeep Mistry
3e8d790b17 Try to re-connect on ASSOC_FAIL errors 2019-01-23 13:23:34 -05:00
Sandeep Mistry
1bb0a1fcf8 Use WIFI_FAST_SCAN for connection instead of WIFI_ALL_CHANNEL_SCAN 2019-01-23 12:15:32 -05:00
Sandeep Mistry
093c3ee3c0 Put in idle mode on begin, bump wake/sleep/idle delays to match datasheet 2019-01-21 17:20:24 -05:00
Sandeep Mistry
c6a06c9ff1 Adjust command execution delays for stability 2019-01-15 09:07:27 -05:00
Sandeep Mistry
1158be469e Bump wakeup time delay to improve stability 2019-01-11 17:12:00 -05:00
Sandeep Mistry
37e262d437 Add support for PEM certs with ECC slot 2019-01-04 17:24:45 -05:00
Sandeep Mistry
70cb98f1e0 Add AmazonRoot CA 1 2019-01-04 16:37:41 -05:00
Sandeep Mistry
f4efa29c9c Add CSR generation capabilities and default TLS config 2019-01-04 16:33:52 -05:00
Sandeep Mistry
61649995d3 Bump receive response retry count to 100 (was 20) for increased stability 2019-01-04 16:33:52 -05:00
Sandeep Mistry
1fce504d28 Add SHA256 support 2019-01-04 16:33:42 -05:00
Sandeep Mistry
a745b06920 Update to bearssl 0.6.0 2019-01-02 17:25:47 -05:00
Martino Facchin
a55c66afb4 Update to 1.2.1 2018-11-16 11:19:03 -05:00
Martino Facchin
35d4a3c84b Free BT memory if using only WiFi
Partially fixes https://github.com/arduino/nina-fw/issues/4.

Problem: default certificates are a lot (~70KB) and BT uses a lot of RAM even if just configured in menuconfig.
Avoid spurious certificates parsing and SSL errors by freeing bt memory if unused.
2018-11-16 16:53:55 +01:00
Sandeep Mistry
c9f1c51063 Version 1.2.0 2018-11-13 10:23:51 -05:00
Sandeep Mistry
8d43a385b9 Add changelog 2018-11-13 10:21:25 -05:00
Martino Facchin
bd556e24e4
Merge pull request #3 from sandeepmistry/bluetooth
Add support for BLE HCI UART mode
2018-11-13 16:13:11 +01:00
Sandeep Mistry
bd477c1d6b Fix DNS servers not sticking when static IP address is used 2018-11-13 09:07:53 -05:00
Sandeep Mistry
f83faa4fd2 Suspend task 2018-11-13 08:56:11 -05:00
Sandeep Mistry
25b73d5196 Erase NVS flash on init failure 2018-11-13 08:56:11 -05:00
Sandeep Mistry
7481928992 Build options for Uno WiFi Rev2 2018-11-13 08:56:11 -05:00
Sandeep Mistry
d718b1f539 Enable HCI UART mode on boot if SS is LOW 2018-11-13 08:56:11 -05:00
Sandeep Mistry
21b60cf657 Add DST Root CA X3 and ISGR Root X1 as trust anchors, for Let's Encrypt support 2018-10-16 09:11:17 -04:00
agdl
e84cf1854b Mask version for version number 2018-10-13 12:35:37 +02:00
Sandeep Mistry
2aed17fc1e Version 1.0.0 -> 1.1.0 2018-10-04 11:51:11 -04:00
Sandeep Mistry
7e35e717f3 Update read me for IDF v3.1 and new sketch to use 2018-10-04 11:33:32 -04:00
Martino Facchin
53580a082b Merge Sandeep's root certificates curated list with Google's 2018-10-04 11:33:32 -04:00
Martino Facchin
eab54f15e1 Update sdkconfig for esp-idf v3.1 2018-10-04 11:33:32 -04:00
Martino Facchin
06b9b9e641 Move certs partition before actual firmware 2018-10-04 11:33:32 -04:00
Martino Facchin
a1fbf4b2ce Move certificates to separate, mmapped partition 2018-10-04 11:33:32 -04:00
Sandeep Mistry
a75b35c6f8
Fixes for static IP's in STA + AP modes, fail on bad AP params (#3) 2018-10-03 11:03:26 -04:00
Sandeep Mistry
fa268db77f Remove bundled ECCX08 driver, and use ArduinoECCX08 library 2018-07-19 11:41:57 -04:00
Sandeep Mistry
be79c97d9f Add random(max) and random(min, max) API's 2018-07-19 11:35:50 -04:00
Sandeep Mistry
5f3beee6d8 Port files from ArduinoBearSSL 2018-07-19 11:33:10 -04:00
Sandeep Mistry
073fd6da96 Initial library structure and metadata 2018-07-19 11:32:28 -04:00
Sandeep Mistry
09b3794e25 Add Root CA's and comment on how Trust Anchors file contents was made 2018-07-19 09:55:07 -04:00
Sandeep Mistry
ba178ce226 Update comments 2018-07-19 09:55:07 -04:00
Sandeep Mistry
6ab105020c Add license 2018-07-18 14:29:00 -04:00
Sandeep Mistry
70d40bd8ee Fixes for Uno WiFi rev2 and ECC608 2018-07-13 11:33:49 -04:00
Riccardo Rizzo
cbdd42debe infinite while loop fix write() and clientWrite() (#2)
* Reimplemented return logic in BearSSLClient::write() and BearSSLClient::clientWrite() in  order to avoid infinite loop in https://github.com/Rocketct/ArduinoBearSSL/blob/master/src/bearssl/ssl_io.c#L279 when networking error happend in low level client(GSMClient)
* Reimplemented logic of BearSSLClient::write(const uint8_t *buf, size_t size) function
2018-06-20 11:31:16 +02:00
Sandeep Mistry
6090cd8d16 Minor tweaks for non-SAMD boards 2018-03-08 09:23:02 -05:00
Sandeep Mistry
aea0616fa7 Rename ECC508 to ECCX08 2018-02-14 15:55:24 -05:00
Sandeep Mistry
dfa149df19 Improve slot read and write performance and reliability 2018-02-13 17:42:56 -05:00
Sandeep Mistry
6906aab802 Remove ECC508 CSR and cert support and examples 2018-02-13 15:48:51 -05:00
Sandeep Mistry
5e32ca078d Add setEccSlot API to set private key slot and cert bytes
instead or in constructor
2018-02-13 15:46:25 -05:00
Sandeep Mistry
3e04f381e4 ECC508: correct serial number length 2018-02-13 15:46:25 -05:00
Sandeep Mistry
5ea1f9d76f ECC508: make serial number uppercase 2018-02-08 13:10:08 -05:00
Sandeep Mistry
17114aaa3f ECC508: add support for reading and writing slots 2018-02-08 13:09:06 -05:00
Sandeep Mistry
620fcf0a45 Use traditional Wire lib API's 2018-01-25 11:01:19 -05:00
Sandeep Mistry
8fbd956ff0 Correct incorrect case 2018-01-17 10:10:35 -05:00
Sandeep Mistry
4130cffc0a Update to new Wire API proposal 2018-01-17 10:03:18 -05:00
Sandeep Mistry
31c0586275 Operate ECC508 at 1 MHz after waking up
Wake up frequency needs to remain at 100 kHz
2017-12-21 11:57:11 -05:00
Sandeep Mistry
b044a0fae7 Check if ECC508 is locked before using it 2017-12-19 13:45:38 -05:00
Sandeep Mistry
0b194f457b Add ECC508 TLS config and integrate in CSR sketch 2017-12-19 11:22:42 -05:00
Sandeep Mistry
0ee51f2def Add ECC508 for reading/writing config, lock status, locking and serial number 2017-12-19 11:21:26 -05:00
Sandeep Mistry
39dddfdc37 Add support for passing ECC508 key slot and cert. byte array + length in constructor 2017-12-14 15:19:14 -05:00
Sandeep Mistry
e13ae73f4a Add ECC508 key slot CSR generation support 2017-12-13 14:47:37 -05:00
Sandeep Mistry
5ca368e466 Add ECC508 private and public key generation support 2017-12-12 15:08:00 -05:00
Sandeep Mistry
0d7c48739b Add ECC508 EC sign support 2017-11-27 14:07:21 -05:00
Sandeep Mistry
94b751b3a5 Use ECC508 for ECDSA verify if present 2017-11-23 16:42:59 -05:00
Sandeep Mistry
a5b2d0ea10 Verify response CRC and dynamic CRC calculations for commands 2017-11-22 16:11:34 -05:00
Sandeep Mistry
d5dbfea2dc Use ECC508 for entropy inject, if available 2017-11-22 14:20:09 -05:00
Sandeep Mistry
00c8c761c9 Correct include guard style 2017-11-22 12:51:50 -05:00
Sandeep Mistry
3b3c5b8329 Inject (pseudo) entropy via br_ssl_engine_inject_entropy
before br_ssl_client_reset
2017-11-15 16:57:50 -05:00
Sandeep Mistry
22494beefd Revert "Inject fake (stack based entropy) in br_ssl_engine_init_rand on Arduino"
This reverts commit 0d353757d75a2c83129d220bf3dc731e08a2669a.
2017-11-15 16:46:51 -05:00
Sandeep Mistry
3811b04182 Use br_x509_minimal_set_time to inject current 2017-11-15 16:45:45 -05:00
Sandeep Mistry
7da22663e0 Revert "Enable BR_USE_UNIX_TIME on Arduino"
This reverts commit 30b1db8865ac4cc6c5468f43ba7725378afab4e0.
2017-11-15 16:27:56 -05:00
Sandeep Mistry
f2510bd575 Enable BR_LOMUL for Arduino 2017-10-13 15:55:48 -04:00
Sandeep Mistry
e3cf838246 Lower buffer size and enable full duplex mode 2017-10-13 15:40:18 -04:00
Sandeep Mistry
387b0f4ad7 HACK: make br_sslio_read_available and br_sslio_peek non-blocking 2017-10-13 15:34:16 -04:00
Sandeep Mistry
ce5a2735fb Use custom br_sslio_read_available and br_sslio_peek in peek() and available() 2017-10-13 10:44:12 -04:00
Sandeep Mistry
31db912528 Add custom br_sslio_read_available and br_sslio_peek APIs 2017-10-13 10:42:17 -04:00
Sandeep Mistry
87f1ac8789 Revert "HACK: make br_sslio_read(ctx, dst, len) return buffered length if dst is NULL"
This reverts commit 2bd615f380611fc26dd5ecdb916a91f90f9763ae.
2017-10-13 10:34:55 -04:00
Sandeep Mistry
28f4764c25 Add ArduinoBearSSL.onGetTime(callback) API
Used to return _gettimeofday value
2017-10-13 10:32:15 -04:00
Sandeep Mistry
f9df9dcccb Update trust anchors to be same as Arduino WINC1500 19.5.2 firmware image 2017-10-13 10:00:29 -04:00
Sandeep Mistry
355101b145 Make available() return values greater than 1
By using br_sslio_read(ctx, NULL, len) hack
2017-10-12 17:57:03 -04:00
Sandeep Mistry
c8e656dd42 HACK: make br_sslio_read(ctx, dst, len) return buffered length if dst is NULL
This let’s us use it in Client::available()
2017-10-12 17:55:31 -04:00
Sandeep Mistry
aaebc17f5e Enable BR_ARMEL_CORTEXM_GCC on Arduino 2017-10-12 17:41:24 -04:00
Sandeep Mistry
c214077d66 Implement BearSSLClient::connect(ip, port) 2017-10-12 17:38:28 -04:00
Sandeep Mistry
3a101ff39f Fill in initial BearSSLClient methods
Limitations:
- available only returns 1 or 0
- connect(ip, port) not implemented
- trust anchors hard coded in file
- requires _gettimeofday implementation in sketch
2017-10-12 14:39:18 -04:00
Sandeep Mistry
11f71124ae Enable BR_USE_UNIX_TIME on Arduino 2017-10-12 14:35:26 -04:00
Sandeep Mistry
2394029037 Inject fake (stack based entropy) in br_ssl_engine_init_rand on Arduino 2017-10-12 14:35:15 -04:00
Sandeep Mistry
6221177919 Add BearSSLClient stubs 2017-10-12 10:55:27 -04:00
Sandeep Mistry
6bae28c91b Import BearSSL 0.5 src and include files 2017-10-12 10:51:17 -04:00
Sandeep Mistry
2b14fa45c4 Initial Arduino lib structure 2017-10-12 10:44:10 -04:00
Sandeep Mistry
f48ae8f065 Remove Wire slave sender support for now
Since there is no start condition interrupt
2016-10-21 13:09:28 -04:00
Sandeep Mistry
93c6f4e635 Wire stability improvements 2016-10-21 11:28:49 -04:00
Sandeep Mistry
d290019bdc Disable I2C interrupts on Wire.end() 2016-10-20 17:14:40 -04:00
Sandeep Mistry
c43365abbd Make Wire slave sender example functional 2016-10-20 17:12:51 -04:00
Sandeep Mistry
3f4a6e1ae9 Port Wire save receiver functionality 2016-10-20 16:09:22 -04:00
Sandeep Mistry
a541a40036 Correct issues muxing I2C pins 2016-10-19 15:57:24 -04:00
Sandeep Mistry
8e51386a7e Initial port of Wire library from SAMD core, master mode only 2016-10-19 14:56:59 -04:00
Martino Facchin
47836f74e3 remove libraries 2016-09-27 17:17:22 +02:00
Me No Dev
68ff762ebd Initial Upload 2016-01-07 16:31:07 +02:00
174 changed files with 5773 additions and 43237 deletions

67
.github/workflows/build.yml vendored Normal file
View 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
View file

@ -1 +1,8 @@
build/ build/
*.bin
# Regenerate sdkconfig from the sdkconfig.defaults* files.
sdkconfig.esp32
sdkconfig.esp32c6
sdkconfig.*.old
sdkconfig.old
managed_components/

6
.gitmodules vendored Normal file
View 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
View 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
View 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
View 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
)

View file

@ -1,15 +1,57 @@
PROJECT_NAME := nina-fw 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 EXTRA_COMPONENT_DIRS := $(PWD)/arduino
CPPFLAGS += -DARDUINO
ifeq ($(RELEASE),1) ifeq ($(RELEASE),1)
CFLAGS += -DNDEBUG -DCONFIG_FREERTOS_ASSERT_DISABLE -Os -DLOG_LOCAL_LEVEL=0 CFLAGS += -DNDEBUG -DCONFIG_FREERTOS_ASSERT_DISABLE -Os -DLOG_LOCAL_LEVEL=0
CPPFLAGS += -DNDEBUG -Os CPPFLAGS += -DNDEBUG -Os
endif 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 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 python combine.py
.PHONY: firmware .PHONY: firmware
.PHONY: load-passthrough
.PHONY: load-nina
.PHONY: load-circuitpython
.PHONY: serial

View file

@ -1,20 +1,69 @@
# Arduino NINA-W102 firmware # Adafruit fork of the Arduino NINA-W102 firmware
[![Build Status](https://travis-ci.com/adafruit/nina-fw.svg?branch=master)](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) 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 ## 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. 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. 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. `git submodule update --init` to fetch the `certificates` submodule.
1. Load the `WiFiNINAFirmwareUpdater` example sketch on to the board 1. Run `make firmware` to build the firmware (in the directory of this read me)
1. Use `esptool` to flash the compiled firmware 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 ## 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 This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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);
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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);
}

View file

@ -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 */

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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
View file

@ -0,0 +1,3 @@
idf_component_register(
INCLUDE_DIRS . ${BOARD}
)

18
boards/esp32/board.h Normal file
View 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
View file

@ -0,0 +1,2 @@
CONFIG_BTDM_CTRL_HCI_MODE_UART_H4=y
CONFIG_BTDM_CTRL_HCI_UART_BAUDRATE=115200

View 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

View 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

Binary file not shown.

1
certificates Submodule

@ -0,0 +1 @@
Subproject commit 96174bf8bafd24c6b215fa065460a582a7624037

127
code-of-conduct.md Normal file
View 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 dont 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 communitys 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.

View file

@ -1,39 +1,97 @@
#!/usr/bin/env python #!/usr/bin/env python
booloaderData = open("build/bootloader/bootloader.bin", "rb").read() import json
partitionData = open("build/partitions.bin", "rb").read() import sys
appData = open("build/nina-fw.bin", "rb").read() import argparse
certsData = open("data/roots.pem", "rb").read() import os
storageData = open("build/spiffs_image.img", "rb").read()
# calculate the output binary size, app offset def extract_firmware_version():
outputSize = 0x1B0000 + len(storageData) with open('main/CommandHandler.cpp', 'r') as file:
if (outputSize % 1024): 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")
def get_idf_target(build_dir):
with open(f"{build_dir}/config.env") as file:
config = json.load(file)
return config["IDF_TARGET"]
def main():
parser = argparse.ArgumentParser()
parser.add_argument('outfile', help='output file')
parser.add_argument('-b', '--build_dir', default='build', help='build directory')
args = parser.parse_args()
outfile = args.outfile
build_dir = os.path.normpath(args.build_dir)
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()
# 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
# calculate the output binary size, app offset
outputSize = 0x30000 + len(appData)
if outputSize % 1024:
outputSize += 1024 - (outputSize % 1024) outputSize += 1024 - (outputSize % 1024)
# allocate and init to 0xff # allocate and init to 0xff
outputData = bytearray(b'\xff') * outputSize outputData = bytearray(b"\xff") * outputSize
# copy data: bootloader, partitions, app # copy data: bootloader, partitions, app
for i in range(0, len(booloaderData)): BOOTLOADER_OFFSET = {
outputData[0x1000 + i] = booloaderData[i] "esp32" : 0x1000,
"esp32c6" : 0x0000,
}
for i in range(0, len(partitionData)): 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] outputData[0x8000 + i] = partitionData[i]
for i in range(0, len(appData)): #for i in range(0, len(phyData)):
outputData[0x10000 + i] = appData[i] # outputData[0xf000 + i] = phyData[i]
for i in range(0, len(certsData)): for i in range(0, len(certsData)):
outputData[0x190000 + i] = certsData[i] outputData[0x10000 + i] = certsData[i]
# zero terminate the pem file # zero terminate the pem file
outputData[0x190000 + len(certsData)] = 0 outputData[0x10000 + len(certsData)] = 0
for i in range(0, len(storageData)): for i in range(0, len(appData)):
outputData[0x1B0000 + i] = storageData[i] outputData[0x30000 + i] = appData[i]
# write out version = extract_firmware_version()
with open("NINA_W102.bin","w+b") as f: outputFilename = f"{outfile}-{version}.bin"
# write out
with open(outputFilename, "w+b") as f:
f.seek(0) f.seek(0)
f.write(outputData) f.write(outputData)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,4 @@
idf_component_register(SRCS src/SPIS.cpp
INCLUDE_DIRS src
REQUIRES arduino-esp32
)

View file

@ -22,8 +22,7 @@
#include <driver/gpio.h> #include <driver/gpio.h>
#include <driver/spi_slave.h> #include <driver/spi_slave.h>
#include "wiring_digital.h" #include "esp32-hal-gpio.h"
#include "WInterrupts.h"
#include "SPIS.h" #include "SPIS.h"
@ -113,4 +112,5 @@ void SPISClass::handleSetupComplete()
xSemaphoreGiveFromISR(_readySemaphore, NULL); 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);

@ -0,0 +1 @@
Subproject commit dbaf6a3226317a7c5e452e7b8a15e54c86bc2b6a

View file

@ -1,10 +0,0 @@
*.o
mkspiffs
mkspiffs.exe
out.*
*.tar.gz
# eclipse wokspace files and dirs
.project
.cproject
.settings

View file

@ -1,3 +0,0 @@
[submodule "spiffs"]
path = spiffs
url = https://github.com/pellepl/spiffs.git

View file

@ -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.

View file

@ -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

View file

@ -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
------|-------
[![Linux build status](http://img.shields.io/travis/igrr/mkspiffs.svg)](https://travis-ci.org/igrr/mkspiffs) | [![Windows build status](http://img.shields.io/appveyor/ci/igrr/mkspiffs.svg)](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

View file

@ -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

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -1,212 +0,0 @@
# SPIFFS (SPI Flash File System)
**V0.3.7**
[![Build Status](https://travis-ci.org/pellepl/spiffs.svg?branch=master)](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

View file

@ -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

View file

@ -1,18 +0,0 @@
b55
O4W4R4C4D4
b45
d5rh
O4W4R4f4C4
baaU
d5rh
OaWaRafaCa
cd5rh
OaWaRafaCa
O4S4W4R4C4
d5rh
O4W4S4R4C4
d5rh
O4W4R4S4C4
d5rh
O4W4R4C4
d5rh

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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_ */

View file

@ -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);
}

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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_ */

View file

@ -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;
}
}

View file

@ -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_ */

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 = "&lt;";
std::string gt = "&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 = "&lt;";
std::string gt = "&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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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