mirror of
https://github.com/esp8266/Arduino.git
synced 2025-08-05 13:16:13 +03:00
Merge branch 'master' into master
This commit is contained in:
4
.git-blame-ignore-revs
Normal file
4
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
9bdcd4f36a2e5285267b69b17e8fc26482dc1c72
|
||||||
|
eea9999dc5eaf464a432f77d5b65269f9baf198d
|
||||||
|
98125f88605cd7e46e9be4e1b3ad0600dd5d2b51
|
||||||
|
19b7a29720a6f2c95d06e2ea4baa335dcf32e68f
|
57
.github/workflows/pull-request.yml
vendored
57
.github/workflows/pull-request.yml
vendored
@@ -15,6 +15,9 @@ jobs:
|
|||||||
build-linux:
|
build-linux:
|
||||||
name: Build ${{ matrix.chunk }}
|
name: Build ${{ matrix.chunk }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
chunk: [0, 1, 2, 3, 4, 5, 6, 7]
|
chunk: [0, 1, 2, 3, 4, 5, 6, 7]
|
||||||
@@ -47,6 +50,9 @@ jobs:
|
|||||||
build-debug-ipv6:
|
build-debug-ipv6:
|
||||||
name: Debug IPv6 ${{ matrix.chunk }}
|
name: Debug IPv6 ${{ matrix.chunk }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
chunk: [0, 1, 2, 3, 4, 5, 6, 7]
|
chunk: [0, 1, 2, 3, 4, 5, 6, 7]
|
||||||
@@ -110,6 +116,9 @@ jobs:
|
|||||||
build-mac:
|
build-mac:
|
||||||
name: Mac
|
name: Mac
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
@@ -139,6 +148,9 @@ jobs:
|
|||||||
build-pio:
|
build-pio:
|
||||||
name: Build Platform.IO
|
name: Build Platform.IO
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
@@ -163,6 +175,9 @@ jobs:
|
|||||||
host-tests:
|
host-tests:
|
||||||
name: Host tests
|
name: Host tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
@@ -184,6 +199,9 @@ jobs:
|
|||||||
documentation:
|
documentation:
|
||||||
name: Documentation
|
name: Documentation
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
@@ -208,6 +226,9 @@ jobs:
|
|||||||
style-check:
|
style-check:
|
||||||
name: Style and formatting
|
name: Style and formatting
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
@@ -217,11 +238,21 @@ jobs:
|
|||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Style check
|
- name: Style check
|
||||||
env:
|
env:
|
||||||
|
LLVM_SNAPSHOT_KEY: "6084F3CF814B57C1CF12EFD515CF4D18AF4F7421"
|
||||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||||
TRAVIS_TAG: ${{ github.ref }}
|
TRAVIS_TAG: ${{ github.ref }}
|
||||||
run: |
|
run: |
|
||||||
|
export GNUPGHOME=$(mktemp -d)
|
||||||
|
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$LLVM_SNAPSHOT_KEY"
|
||||||
|
gpg --batch --armor --export "$LLVM_SNAPSHOT_KEY" | \
|
||||||
|
sudo tee /etc/apt/trusted.gpg.d/llvm-snapshot.gpg.asc
|
||||||
|
gpgconf --kill all
|
||||||
|
rm -r $GNUPGHOME
|
||||||
|
echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/llvm.list
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install astyle
|
sudo apt install clang-format-13
|
||||||
|
pip3 install pyyaml
|
||||||
bash ./tests/ci/style_check.sh
|
bash ./tests/ci/style_check.sh
|
||||||
|
|
||||||
|
|
||||||
@@ -229,6 +260,9 @@ jobs:
|
|||||||
mock-check:
|
mock-check:
|
||||||
name: Mock trivial test
|
name: Mock trivial test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
@@ -248,6 +282,9 @@ jobs:
|
|||||||
boards-check:
|
boards-check:
|
||||||
name: Boards.txt check
|
name: Boards.txt check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
@@ -269,3 +306,21 @@ jobs:
|
|||||||
bash ./tests/ci/build_boards.sh
|
bash ./tests/ci/build_boards.sh
|
||||||
bash ./tests/ci/eboot_test.sh
|
bash ./tests/ci/eboot_test.sh
|
||||||
bash ./tests/ci/pkgrefs_test.sh
|
bash ./tests/ci/pkgrefs_test.sh
|
||||||
|
|
||||||
|
|
||||||
|
# Validate orthography
|
||||||
|
code-spell:
|
||||||
|
name: Check spelling
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Run codespell
|
||||||
|
uses: codespell-project/actions-codespell@master
|
||||||
|
with:
|
||||||
|
skip: ./libraries/ESP8266SdFat,./libraries/LittleFS/lib,./tools/pyserial,./tools/sdk,./tools/esptool,./libraries/SoftwareSerial,./libraries/Ethernet,./github/workflows,./libraries/ESP8266HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino,./libraries/esp8266/examples/RTCUserMemory/RTCUserMemory.ino,./libraries/esp8266/examples/StreamString/StreamString.ino,./libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino,./libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino,./libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/ESP8266WebServer/examples/HttpHashCredAuth/HttpHashCredAuth.ino,./cores/esp8266/spiffs,./tests/device/test_libc/libm_string.c, ./libraries/Netdump/examples/Netdump/Netdump.ino,./libraries/ESP8266WiFi/examples/BearSSL_Server,./cores/esp8266/LwipIntfDev.h
|
||||||
|
ignore_words_list: ESP8266,esp8266,esp,dout,DOUT,ser,ans
|
||||||
|
16
.github/workflows/release-to-publish.yml
vendored
16
.github/workflows/release-to-publish.yml
vendored
@@ -32,16 +32,14 @@ jobs:
|
|||||||
package:
|
package:
|
||||||
name: Update master JSON file
|
name: Update master JSON file
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: false
|
||||||
- uses: actions/setup-python@v2
|
fetch-depth: 0
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
- name: Set GIT tag name
|
|
||||||
run: |
|
|
||||||
echo "::set-env name=TRAVIS_TAG::$(git describe --exact-match --tags)"
|
|
||||||
- name: Deploy updated JSON
|
- name: Deploy updated JSON
|
||||||
env:
|
env:
|
||||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||||
@@ -49,8 +47,4 @@ jobs:
|
|||||||
CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
|
CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
|
||||||
GHCI_DEPLOY_KEY: ${{ secrets.GHCI_DEPLOY_KEY }}
|
GHCI_DEPLOY_KEY: ${{ secrets.GHCI_DEPLOY_KEY }}
|
||||||
run: |
|
run: |
|
||||||
bash ./tests/ci/build_package.sh
|
|
||||||
# Only the regenerated JSON file will be used, but it's simpler
|
|
||||||
# than looking for it in a GH release.
|
|
||||||
bash ./package/deploy_package_index.sh
|
bash ./package/deploy_package_index.sh
|
||||||
|
|
||||||
|
7
.github/workflows/tag-to-draft-release.yml
vendored
7
.github/workflows/tag-to-draft-release.yml
vendored
@@ -14,17 +14,22 @@ jobs:
|
|||||||
package:
|
package:
|
||||||
name: Package
|
name: Package
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
fetch-depth: 0
|
||||||
- uses: actions/setup-python@v2
|
- uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Set GIT tag name
|
- name: Set GIT tag name
|
||||||
run: |
|
run: |
|
||||||
# Sets an environment variable used in the next steps
|
# Sets an environment variable used in the next steps
|
||||||
echo "::set-env name=TRAVIS_TAG::$(git describe --exact-match --tags)"
|
TRAVIS_TAG="$(git describe --exact-match --tags)"
|
||||||
|
echo "TRAVIS_TAG=${TRAVIS_TAG}" >> $GITHUB_ENV
|
||||||
- name: Build package JSON
|
- name: Build package JSON
|
||||||
env:
|
env:
|
||||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,15 +3,9 @@ tools/dist/
|
|||||||
tools/xtensa-lx106-elf/
|
tools/xtensa-lx106-elf/
|
||||||
tools/mkspiffs/
|
tools/mkspiffs/
|
||||||
tools/mklittlefs/
|
tools/mklittlefs/
|
||||||
tools/python/
|
|
||||||
tools/python3/
|
tools/python3/
|
||||||
package/versions/
|
package/versions/
|
||||||
exclude.txt
|
exclude.txt
|
||||||
tools/sdk/lib/liblwip_src.a
|
|
||||||
tools/sdk/lwip/src/build
|
|
||||||
tools/sdk/lwip/src/liblwip_src.a
|
|
||||||
tools/sdk/ld/backup
|
|
||||||
tools/sdk/ld/eagle.app.v6.common.ld
|
|
||||||
|
|
||||||
tests/hosts/lcov/
|
tests/hosts/lcov/
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ Arduino core for ESP8266 WiFi chip
|
|||||||
|
|
||||||
# Quick links
|
# Quick links
|
||||||
|
|
||||||
- [Latest release documentation](https://arduino-esp8266.readthedocs.io/en/2.7.4_a/)
|
- [Latest release documentation](https://arduino-esp8266.readthedocs.io/en/3.0.2/)
|
||||||
- [Current "git version" documentation](https://arduino-esp8266.readthedocs.io/en/latest/)
|
- [Current "git version" documentation](https://arduino-esp8266.readthedocs.io/en/latest/)
|
||||||
- [Install git version](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version) ([sources](doc/installing.rst#using-git-version))
|
- [Install git version](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version) ([sources](doc/installing.rst#using-git-version))
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ ESP8266 Arduino core comes with libraries to communicate over WiFi using TCP and
|
|||||||
|
|
||||||
Starting with 1.6.4, Arduino allows installation of third-party platform packages using Boards Manager. We have packages available for Windows, Mac OS, and Linux (32 and 64 bit).
|
Starting with 1.6.4, Arduino allows installation of third-party platform packages using Boards Manager. We have packages available for Windows, Mac OS, and Linux (32 and 64 bit).
|
||||||
|
|
||||||
- Install the current upstream Arduino IDE at the 1.8.9 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/main/software).
|
- Install the current upstream Arduino IDE at the 1.8.9 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/software).
|
||||||
- Start Arduino and open the Preferences window.
|
- Start Arduino and open the Preferences window.
|
||||||
- Enter ```https://arduino.esp8266.com/stable/package_esp8266com_index.json``` into the *File>Preferences>Additional Boards Manager URLs* field of the Arduino IDE. You can add multiple URLs, separating them with commas.
|
- Enter ```https://arduino.esp8266.com/stable/package_esp8266com_index.json``` into the *File>Preferences>Additional Boards Manager URLs* field of the Arduino IDE. You can add multiple URLs, separating them with commas.
|
||||||
- Open Boards Manager from Tools > Board menu and install *esp8266* platform (and don't forget to select your ESP8266 board from Tools > Board menu after installation).
|
- Open Boards Manager from Tools > Board menu and install *esp8266* platform (and don't forget to select your ESP8266 board from Tools > Board menu after installation).
|
||||||
@@ -36,14 +36,14 @@ Starting with 1.6.4, Arduino allows installation of third-party platform package
|
|||||||
#### Latest release [](https://github.com/esp8266/Arduino/releases/latest/)
|
#### Latest release [](https://github.com/esp8266/Arduino/releases/latest/)
|
||||||
Boards manager link: `https://arduino.esp8266.com/stable/package_esp8266com_index.json`
|
Boards manager link: `https://arduino.esp8266.com/stable/package_esp8266com_index.json`
|
||||||
|
|
||||||
Documentation: [https://arduino-esp8266.readthedocs.io/en/2.7.4_a/](https://arduino-esp8266.readthedocs.io/en/2.7.4_a/)
|
Documentation: [https://arduino-esp8266.readthedocs.io/en/3.0.2/](https://arduino-esp8266.readthedocs.io/en/3.0.2/)
|
||||||
|
|
||||||
### Using git version
|
### Using git version
|
||||||
[](https://travis-ci.org/esp8266/Arduino)
|
[](https://travis-ci.org/esp8266/Arduino)
|
||||||
|
|
||||||
Also known as latest git or master branch.
|
Also known as latest git or master branch.
|
||||||
|
|
||||||
- Install the current upstream Arduino IDE at the 1.8 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/main/software).
|
- Install the current upstream Arduino IDE at the 1.8 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/software).
|
||||||
- Follow the [instructions in the documentation](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version).
|
- Follow the [instructions in the documentation](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version).
|
||||||
|
|
||||||
### Using PlatformIO
|
### Using PlatformIO
|
||||||
|
1084
boards.txt
1084
boards.txt
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -186,6 +186,9 @@ void attachInterrupt(uint8_t pin, void (*)(void), int mode);
|
|||||||
void detachInterrupt(uint8_t pin);
|
void detachInterrupt(uint8_t pin);
|
||||||
void attachInterruptArg(uint8_t pin, void (*)(void*), void* arg, int mode);
|
void attachInterruptArg(uint8_t pin, void (*)(void*), void* arg, int mode);
|
||||||
|
|
||||||
|
#if FLASH_MAP_SUPPORT
|
||||||
|
#include "flash_hal.h"
|
||||||
|
#endif
|
||||||
void preinit(void);
|
void preinit(void);
|
||||||
void setup(void);
|
void setup(void);
|
||||||
void loop(void);
|
void loop(void);
|
||||||
@@ -279,6 +282,8 @@ inline void configTzTime(const char* tz, const char* server1,
|
|||||||
configTime(tz, server1, server2, server3);
|
configTime(tz, server1, server2, server3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getLocalTime(struct tm * info, uint32_t ms = 5000);
|
||||||
|
|
||||||
// Everything we expect to be implicitly loaded for the sketch
|
// Everything we expect to be implicitly loaded for the sketch
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
|
|
||||||
|
@@ -1,9 +1,8 @@
|
|||||||
#ifndef __CALLBACKLIST_H__
|
#ifndef __CALLBACKLIST_H__
|
||||||
#define __CALLBACKLIST_H__
|
#define __CALLBACKLIST_H__
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
CallBackList, An implemention for handling callback execution
|
CallBackList, An implementation for handling callback execution
|
||||||
|
|
||||||
Copyright (c) 2019 Herman Reintke. All rights reserved.
|
Copyright (c) 2019 Herman Reintke. All rights reserved.
|
||||||
This file is part of the esp8266 core for Arduino environment.
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
@@ -23,19 +23,18 @@
|
|||||||
#include "coredecls.h"
|
#include "coredecls.h"
|
||||||
#include "Esp.h"
|
#include "Esp.h"
|
||||||
|
|
||||||
void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag)
|
void EspClass::getHeapStats(uint32_t* hfree, uint32_t* hmax, uint8_t* hfrag)
|
||||||
{
|
{
|
||||||
// L2 / Euclidian norm of free block sizes.
|
// L2 / Euclidean norm of free block sizes.
|
||||||
// Having getFreeHeap()=sum(hole-size), fragmentation is given by
|
// Having getFreeHeap()=sum(hole-size), fragmentation is given by
|
||||||
// 100 * (1 - sqrt(sum(hole-size²)) / sum(hole-size))
|
// 100 * (1 - sqrt(sum(hole-size²)) / sum(hole-size))
|
||||||
|
|
||||||
umm_info(NULL, false);
|
umm_info(NULL, false);
|
||||||
|
|
||||||
uint32_t free_size = umm_free_heap_size_core(umm_get_current_heap());
|
uint32_t free_size = umm_free_heap_size_core(umm_get_current_heap());
|
||||||
if (hfree)
|
if (hfree)
|
||||||
*hfree = free_size;
|
*hfree = free_size;
|
||||||
if (hmax)
|
if (hmax)
|
||||||
*hmax = (uint16_t)umm_max_block_size_core(umm_get_current_heap());
|
*hmax = umm_max_block_size_core(umm_get_current_heap());
|
||||||
if (hfrag) {
|
if (hfrag) {
|
||||||
if (free_size) {
|
if (free_size) {
|
||||||
*hfrag = umm_fragmentation_metric_core(umm_get_current_heap());
|
*hfrag = umm_fragmentation_metric_core(umm_get_current_heap());
|
||||||
@@ -45,6 +44,18 @@ void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag)
|
||||||
|
{
|
||||||
|
uint32_t hmax32;
|
||||||
|
getHeapStats(hfree, &hmax32, hfrag);
|
||||||
|
if (hmax) {
|
||||||
|
// With the MMU_EXTERNAL_HEAP option, hmax could overflow for heaps larger
|
||||||
|
// then 64KB. return UINT16_MAX (saturation) for those cases.
|
||||||
|
// Added deprecated attribute and message.
|
||||||
|
*hmax = (hmax32 > UINT16_MAX) ? UINT16_MAX : hmax32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t EspClass::getHeapFragmentation()
|
uint8_t EspClass::getHeapFragmentation()
|
||||||
{
|
{
|
||||||
return (uint8_t)umm_fragmentation_metric();
|
return (uint8_t)umm_fragmentation_metric();
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
#include "MD5Builder.h"
|
#include "MD5Builder.h"
|
||||||
#include "umm_malloc/umm_malloc.h"
|
#include "umm_malloc/umm_malloc.h"
|
||||||
#include "cont.h"
|
#include "cont.h"
|
||||||
|
#include "flash_hal.h"
|
||||||
#include "coredecls.h"
|
#include "coredecls.h"
|
||||||
#include "umm_malloc/umm_malloc.h"
|
#include "umm_malloc/umm_malloc.h"
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
@@ -115,20 +115,18 @@ void EspClass::wdtFeed(void)
|
|||||||
system_soft_wdt_feed();
|
system_soft_wdt_feed();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void esp_yield();
|
|
||||||
|
|
||||||
void EspClass::deepSleep(uint64_t time_us, WakeMode mode)
|
void EspClass::deepSleep(uint64_t time_us, WakeMode mode)
|
||||||
{
|
{
|
||||||
system_deep_sleep_set_option(static_cast<int>(mode));
|
system_deep_sleep_set_option(static_cast<int>(mode));
|
||||||
system_deep_sleep(time_us);
|
system_deep_sleep(time_us);
|
||||||
esp_yield();
|
esp_suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EspClass::deepSleepInstant(uint64_t time_us, WakeMode mode)
|
void EspClass::deepSleepInstant(uint64_t time_us, WakeMode mode)
|
||||||
{
|
{
|
||||||
system_deep_sleep_set_option(static_cast<int>(mode));
|
system_deep_sleep_set_option(static_cast<int>(mode));
|
||||||
system_deep_sleep_instant(time_us);
|
system_deep_sleep_instant(time_us);
|
||||||
esp_yield();
|
esp_suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
//this calculation was taken verbatim from the SDK api reference for SDK 2.1.0.
|
//this calculation was taken verbatim from the SDK api reference for SDK 2.1.0.
|
||||||
@@ -200,7 +198,7 @@ void EspClass::reset(void)
|
|||||||
void EspClass::restart(void)
|
void EspClass::restart(void)
|
||||||
{
|
{
|
||||||
system_restart();
|
system_restart();
|
||||||
esp_yield();
|
esp_suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void EspClass::rebootIntoUartDownloadMode()
|
[[noreturn]] void EspClass::rebootIntoUartDownloadMode()
|
||||||
@@ -224,7 +222,7 @@ uint32_t EspClass::getFreeHeap(void)
|
|||||||
return system_get_free_heap_size();
|
return system_get_free_heap_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t EspClass::getMaxFreeBlockSize(void)
|
uint32_t EspClass::getMaxFreeBlockSize(void)
|
||||||
{
|
{
|
||||||
return umm_max_block_size();
|
return umm_max_block_size();
|
||||||
}
|
}
|
||||||
@@ -293,6 +291,9 @@ uint32_t EspClass::getFlashChipRealSize(void)
|
|||||||
|
|
||||||
uint32_t EspClass::getFlashChipSize(void)
|
uint32_t EspClass::getFlashChipSize(void)
|
||||||
{
|
{
|
||||||
|
#if FLASH_MAP_SUPPORT
|
||||||
|
return getFlashChipRealSize();
|
||||||
|
#else
|
||||||
uint32_t data;
|
uint32_t data;
|
||||||
uint8_t * bytes = (uint8_t *) &data;
|
uint8_t * bytes = (uint8_t *) &data;
|
||||||
// read first 4 byte (magic byte + flash config)
|
// read first 4 byte (magic byte + flash config)
|
||||||
@@ -300,6 +301,7 @@ uint32_t EspClass::getFlashChipSize(void)
|
|||||||
return magicFlashChipSize((bytes[3] & 0xf0) >> 4);
|
return magicFlashChipSize((bytes[3] & 0xf0) >> 4);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EspClass::getFlashChipSpeed(void)
|
uint32_t EspClass::getFlashChipSpeed(void)
|
||||||
@@ -325,6 +327,7 @@ FlashMode_t EspClass::getFlashChipMode(void)
|
|||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !FLASH_MAP_SUPPORT
|
||||||
uint32_t EspClass::magicFlashChipSize(uint8_t byte) {
|
uint32_t EspClass::magicFlashChipSize(uint8_t byte) {
|
||||||
switch(byte & 0x0F) {
|
switch(byte & 0x0F) {
|
||||||
case 0x0: // 4 Mbit (512KB)
|
case 0x0: // 4 Mbit (512KB)
|
||||||
@@ -345,6 +348,7 @@ uint32_t EspClass::magicFlashChipSize(uint8_t byte) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) {
|
uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) {
|
||||||
switch(byte & 0x0F) {
|
switch(byte & 0x0F) {
|
||||||
@@ -614,14 +618,12 @@ uint32_t EspClass::getSketchSize() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" uint32_t _FS_start;
|
|
||||||
|
|
||||||
uint32_t EspClass::getFreeSketchSpace() {
|
uint32_t EspClass::getFreeSketchSpace() {
|
||||||
|
|
||||||
uint32_t usedSize = getSketchSize();
|
uint32_t usedSize = getSketchSize();
|
||||||
// round one sector up
|
// round one sector up
|
||||||
uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
|
uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
|
||||||
uint32_t freeSpaceEnd = (uint32_t)&_FS_start - 0x40200000;
|
uint32_t freeSpaceEnd = (uint32_t)FS_start - 0x40200000;
|
||||||
|
|
||||||
#ifdef DEBUG_SERIAL
|
#ifdef DEBUG_SERIAL
|
||||||
DEBUG_SERIAL.printf("usedSize=%u freeSpaceStart=%u freeSpaceEnd=%u\r\n", usedSize, freeSpaceStart, freeSpaceEnd);
|
DEBUG_SERIAL.printf("usedSize=%u freeSpaceStart=%u freeSpaceEnd=%u\r\n", usedSize, freeSpaceStart, freeSpaceEnd);
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
#include "spi_vendors.h"
|
#include "spi_vendors.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AVR macros for WDT managment
|
* AVR macros for WDT management
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WDTO_0MS = 0, //!< WDTO_0MS
|
WDTO_0MS = 0, //!< WDTO_0MS
|
||||||
@@ -114,9 +114,10 @@ class EspClass {
|
|||||||
static uint32_t getChipId();
|
static uint32_t getChipId();
|
||||||
|
|
||||||
static uint32_t getFreeHeap();
|
static uint32_t getFreeHeap();
|
||||||
static uint16_t getMaxFreeBlockSize();
|
static uint32_t getMaxFreeBlockSize();
|
||||||
static uint8_t getHeapFragmentation(); // in %
|
static uint8_t getHeapFragmentation(); // in %
|
||||||
static void getHeapStats(uint32_t* free = nullptr, uint16_t* max = nullptr, uint8_t* frag = nullptr);
|
static void getHeapStats(uint32_t* free = nullptr, uint16_t* max = nullptr, uint8_t* frag = nullptr) __attribute__((deprecated("Use 'uint32_t*' on max, 2nd argument")));
|
||||||
|
static void getHeapStats(uint32_t* free = nullptr, uint32_t* max = nullptr, uint8_t* frag = nullptr);
|
||||||
|
|
||||||
static uint32_t getFreeContStack();
|
static uint32_t getFreeContStack();
|
||||||
static void resetFreeContStack();
|
static void resetFreeContStack();
|
||||||
@@ -264,7 +265,7 @@ class EspClass {
|
|||||||
static bool flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount);
|
static bool flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount);
|
||||||
/**
|
/**
|
||||||
* @brief Write up to @a size bytes from @a data to flash at @a address
|
* @brief Write up to @a size bytes from @a data to flash at @a address
|
||||||
* This function takes case of unaligned memory acces by copying @a data to a temporary buffer,
|
* This function takes case of unaligned memory access by copying @a data to a temporary buffer,
|
||||||
* it also takes care of page boundary crossing see @a flashWritePageBreak as to why it's done.
|
* it also takes care of page boundary crossing see @a flashWritePageBreak as to why it's done.
|
||||||
* Less than @a size bytes may be written, due to 4 byte alignment requirement of spi_flash_write
|
* Less than @a size bytes may be written, due to 4 byte alignment requirement of spi_flash_write
|
||||||
* @param address address on flash where write should start
|
* @param address address on flash where write should start
|
||||||
|
@@ -117,6 +117,18 @@ public:
|
|||||||
time_t getCreationTime();
|
time_t getCreationTime();
|
||||||
void setTimeCallback(time_t (*cb)(void));
|
void setTimeCallback(time_t (*cb)(void));
|
||||||
|
|
||||||
|
// Stream::send configuration
|
||||||
|
|
||||||
|
bool inputCanTimeout () override {
|
||||||
|
// unavailable data can't become later available
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool outputCanTimeout () override {
|
||||||
|
// free space for write can't increase later
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FileImplPtr _p;
|
FileImplPtr _p;
|
||||||
time_t (*_timeCallback)(void) = nullptr;
|
time_t (*_timeCallback)(void) = nullptr;
|
||||||
|
@@ -15,7 +15,7 @@ void close_all_fs(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// default weak definitions
|
// default weak definitions
|
||||||
// they are overriden in their respective real implementation
|
// they are overridden in their respective real implementation
|
||||||
// hint: https://github.com/esp8266/Arduino/pull/6699#issuecomment-549085382
|
// hint: https://github.com/esp8266/Arduino/pull/6699#issuecomment-549085382
|
||||||
|
|
||||||
void littlefs_request_end(void) __attribute__((weak));
|
void littlefs_request_end(void) __attribute__((weak));
|
||||||
|
59
cores/esp8266/FlashMap.h
Normal file
59
cores/esp8266/FlashMap.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
// - do not edit - autogenerated by boards.txt.py
|
||||||
|
|
||||||
|
#ifndef __FLASH_MAP_H
|
||||||
|
#define __FLASH_MAP_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t eeprom_start;
|
||||||
|
uint32_t fs_start;
|
||||||
|
uint32_t fs_end;
|
||||||
|
uint32_t fs_block_size;
|
||||||
|
uint32_t fs_page_size;
|
||||||
|
uint32_t flash_size_kb;
|
||||||
|
} flash_map_s;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Following definitions map the above structure, one per line.
|
||||||
|
FLASH_MAP_* is a user choice in sketch:
|
||||||
|
`FLASH_MAP_SETUP_CONFIG(FLASH_MAP_OTA_FS)`
|
||||||
|
Configuration is made at boot with detected flash chip size (last argument 512..16384)
|
||||||
|
Other values are defined from `tools/boards.txt.py`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FLASH_MAP_OTA_FS \
|
||||||
|
{ \
|
||||||
|
{ .eeprom_start = 0x402fb000, .fs_start = 0x402eb000, .fs_end = 0x402fb000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 1024 }, \
|
||||||
|
{ .eeprom_start = 0x403fb000, .fs_start = 0x403c0000, .fs_end = 0x403fb000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 2048 }, \
|
||||||
|
{ .eeprom_start = 0x405fb000, .fs_start = 0x40400000, .fs_end = 0x405fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 4096 }, \
|
||||||
|
{ .eeprom_start = 0x409fb000, .fs_start = 0x40400000, .fs_end = 0x409fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 8192 }, \
|
||||||
|
{ .eeprom_start = 0x411fb000, .fs_start = 0x40400000, .fs_end = 0x411fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 16384 }, \
|
||||||
|
{ .eeprom_start = 0x4027b000, .fs_start = 0x40273000, .fs_end = 0x4027b000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 512 }, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FLASH_MAP_MAX_FS \
|
||||||
|
{ \
|
||||||
|
{ .eeprom_start = 0x402fb000, .fs_start = 0x4027b000, .fs_end = 0x402fb000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 1024 }, \
|
||||||
|
{ .eeprom_start = 0x403fb000, .fs_start = 0x40300000, .fs_end = 0x403fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 2048 }, \
|
||||||
|
{ .eeprom_start = 0x405fb000, .fs_start = 0x40300000, .fs_end = 0x405fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 4096 }, \
|
||||||
|
{ .eeprom_start = 0x409fb000, .fs_start = 0x40300000, .fs_end = 0x409fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 8192 }, \
|
||||||
|
{ .eeprom_start = 0x411fb000, .fs_start = 0x40300000, .fs_end = 0x411fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 16384 }, \
|
||||||
|
{ .eeprom_start = 0x4027b000, .fs_start = 0x4025b000, .fs_end = 0x4027b000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 512 }, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FLASH_MAP_NO_FS \
|
||||||
|
{ \
|
||||||
|
{ .eeprom_start = 0x402fb000, .fs_start = 0x402fb000, .fs_end = 0x402fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 1024 }, \
|
||||||
|
{ .eeprom_start = 0x403fb000, .fs_start = 0x403fb000, .fs_end = 0x403fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 2048 }, \
|
||||||
|
{ .eeprom_start = 0x405fb000, .fs_start = 0x405fb000, .fs_end = 0x405fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 4096 }, \
|
||||||
|
{ .eeprom_start = 0x409fb000, .fs_start = 0x409fb000, .fs_end = 0x409fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 8192 }, \
|
||||||
|
{ .eeprom_start = 0x411fb000, .fs_start = 0x411fb000, .fs_end = 0x411fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 16384 }, \
|
||||||
|
{ .eeprom_start = 0x4027b000, .fs_start = 0x4027b000, .fs_end = 0x4027b000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 512 }, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __FLASH_MAP_H
|
||||||
|
|
@@ -35,9 +35,6 @@
|
|||||||
|
|
||||||
// SerialEvent functions are weak, so when the user doesn't define them,
|
// SerialEvent functions are weak, so when the user doesn't define them,
|
||||||
// the linker just sets their address to 0 (which is checked below).
|
// the linker just sets their address to 0 (which is checked below).
|
||||||
// The Serialx_available is just a wrapper around Serialx.available(),
|
|
||||||
// but we can refer to it weakly so we don't pull in the entire
|
|
||||||
// HardwareSerial instance if the user doesn't also refer to it.
|
|
||||||
void serialEvent() __attribute__((weak));
|
void serialEvent() __attribute__((weak));
|
||||||
|
|
||||||
HardwareSerial::HardwareSerial(int uart_nr)
|
HardwareSerial::HardwareSerial(int uart_nr)
|
||||||
@@ -146,8 +143,7 @@ unsigned long HardwareSerial::detectBaudrate(time_t timeoutMillis)
|
|||||||
if ((detectedBaudrate = testBaudrate())) {
|
if ((detectedBaudrate = testBaudrate())) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
yield();
|
esp_delay(100);
|
||||||
delay(100);
|
|
||||||
}
|
}
|
||||||
return detectedBaudrate;
|
return detectedBaudrate;
|
||||||
}
|
}
|
||||||
|
@@ -132,7 +132,7 @@ public:
|
|||||||
|
|
||||||
int peek(void) override
|
int peek(void) override
|
||||||
{
|
{
|
||||||
// return -1 when data is unvailable (arduino api)
|
// return -1 when data is unavailable (arduino api)
|
||||||
return uart_peek_char(_uart);
|
return uart_peek_char(_uart);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ public:
|
|||||||
|
|
||||||
int read(void) override
|
int read(void) override
|
||||||
{
|
{
|
||||||
// return -1 when data is unvailable (arduino api)
|
// return -1 when data is unavailable (arduino api)
|
||||||
return uart_read_char(_uart);
|
return uart_read_char(_uart);
|
||||||
}
|
}
|
||||||
// ::read(buffer, size): same as readBytes without timeout
|
// ::read(buffer, size): same as readBytes without timeout
|
||||||
@@ -183,7 +183,7 @@ public:
|
|||||||
{
|
{
|
||||||
return static_cast<int>(uart_tx_free(_uart));
|
return static_cast<int>(uart_tx_free(_uart));
|
||||||
}
|
}
|
||||||
void flush(void) override;
|
void flush(void) override; // wait for all outgoing characters to be sent, output buffer is empty after this call
|
||||||
size_t write(uint8_t c) override
|
size_t write(uint8_t c) override
|
||||||
{
|
{
|
||||||
return uart_write_char(_uart, c);
|
return uart_write_char(_uart, c);
|
||||||
@@ -233,8 +233,12 @@ protected:
|
|||||||
size_t _rx_size;
|
size_t _rx_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||||
extern HardwareSerial Serial;
|
extern HardwareSerial Serial;
|
||||||
|
#endif
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL1)
|
||||||
extern HardwareSerial Serial1;
|
extern HardwareSerial Serial1;
|
||||||
|
#endif
|
||||||
|
|
||||||
extern void serialEventRun(void) __attribute__((weak));
|
extern void serialEventRun(void) __attribute__((weak));
|
||||||
|
|
||||||
|
@@ -69,7 +69,7 @@ class IPAddress: public Printable {
|
|||||||
IPAddress(const IPAddress& from);
|
IPAddress(const IPAddress& from);
|
||||||
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
||||||
IPAddress(uint32_t address) { ctor32(address); }
|
IPAddress(uint32_t address) { ctor32(address); }
|
||||||
IPAddress(u32_t address) { ctor32(address); }
|
IPAddress(unsigned long address) { ctor32(address); }
|
||||||
IPAddress(int address) { ctor32(address); }
|
IPAddress(int address) { ctor32(address); }
|
||||||
IPAddress(const uint8_t *address);
|
IPAddress(const uint8_t *address);
|
||||||
|
|
||||||
@@ -80,16 +80,14 @@ class IPAddress: public Printable {
|
|||||||
// to a four-byte uint8_t array is expected
|
// to a four-byte uint8_t array is expected
|
||||||
operator uint32_t() const { return isV4()? v4(): (uint32_t)0; }
|
operator uint32_t() const { return isV4()? v4(): (uint32_t)0; }
|
||||||
operator uint32_t() { return isV4()? v4(): (uint32_t)0; }
|
operator uint32_t() { return isV4()? v4(): (uint32_t)0; }
|
||||||
operator u32_t() const { return isV4()? v4(): (u32_t)0; }
|
|
||||||
operator u32_t() { return isV4()? v4(): (u32_t)0; }
|
|
||||||
|
|
||||||
bool isSet () const;
|
bool isSet () const;
|
||||||
operator bool () const { return isSet(); } // <-
|
operator bool () const { return isSet(); } // <-
|
||||||
operator bool () { return isSet(); } // <- both are needed
|
operator bool () { return isSet(); } // <- both are needed
|
||||||
|
|
||||||
// generic IPv4 wrapper to uint32-view like arduino loves to see it
|
// generic IPv4 wrapper to uint32-view like arduino loves to see it
|
||||||
const u32_t& v4() const { return ip_2_ip4(&_ip)->addr; } // for raw_address(const)
|
const uint32_t& v4() const { return ip_2_ip4(&_ip)->addr; } // for raw_address(const)
|
||||||
u32_t& v4() { return ip_2_ip4(&_ip)->addr; }
|
uint32_t& v4() { return ip_2_ip4(&_ip)->addr; }
|
||||||
|
|
||||||
bool operator==(const IPAddress& addr) const {
|
bool operator==(const IPAddress& addr) const {
|
||||||
return ip_addr_cmp(&_ip, &addr._ip);
|
return ip_addr_cmp(&_ip, &addr._ip);
|
||||||
@@ -100,14 +98,14 @@ class IPAddress: public Printable {
|
|||||||
bool operator==(uint32_t addr) const {
|
bool operator==(uint32_t addr) const {
|
||||||
return isV4() && v4() == addr;
|
return isV4() && v4() == addr;
|
||||||
}
|
}
|
||||||
bool operator==(u32_t addr) const {
|
bool operator==(unsigned long addr) const {
|
||||||
return isV4() && v4() == addr;
|
return isV4() && v4() == (uint32_t)addr;
|
||||||
}
|
}
|
||||||
bool operator!=(uint32_t addr) const {
|
bool operator!=(uint32_t addr) const {
|
||||||
return !(isV4() && v4() == addr);
|
return !(isV4() && v4() == addr);
|
||||||
}
|
}
|
||||||
bool operator!=(u32_t addr) const {
|
bool operator!=(unsigned long addr) const {
|
||||||
return !(isV4() && v4() == addr);
|
return isV4() && v4() != (uint32_t)addr;
|
||||||
}
|
}
|
||||||
bool operator==(const uint8_t* addr) const;
|
bool operator==(const uint8_t* addr) const;
|
||||||
|
|
||||||
|
@@ -1,64 +1,97 @@
|
|||||||
/*
|
/*
|
||||||
lwIPDhcpServer-NonOS.cpp - DHCP server wrapper
|
NonOS DHCP server helpers
|
||||||
|
|
||||||
Copyright (c) 2020 esp8266 arduino. All rights reserved.
|
Copyright (c) 2020-2022 esp8266 arduino. All rights reserved.
|
||||||
This file is part of the esp8266 core for Arduino environment.
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
|
||||||
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
|
||||||
License as published by the Free Software Foundation; either
|
License as published by the Free Software Foundation; either
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
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,
|
This library is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
Lesser General Public License for more details.
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
You should have received a copy of the GNU Lesser General Public
|
||||||
License along with this library; if not, write to the Free Software
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// STARTS/STOPS DHCP SERVER ON WIFI AP INTERFACE
|
#include "LwipDhcpServer-NonOS.h"
|
||||||
// these functions must exists as-is with "C" interface,
|
|
||||||
// nonos-sdk calls them at boot time and later
|
|
||||||
|
|
||||||
#include <lwip/init.h> // LWIP_VERSION
|
|
||||||
|
|
||||||
|
#include <lwip/init.h>
|
||||||
#include <lwip/netif.h>
|
#include <lwip/netif.h>
|
||||||
#include "LwipDhcpServer.h"
|
|
||||||
|
|
||||||
extern netif netif_git[2];
|
// Global static DHCP instance for softAP interface
|
||||||
|
// (since the netif object never goes away, even when AP is disabled)
|
||||||
// global DHCP instance for softAP interface
|
// Initial version fully emulates nonos-sdk api in DhcpServer class,
|
||||||
DhcpServer dhcpSoftAP(&netif_git[SOFTAP_IF]);
|
// before trying to further change it and possibly break legacy behaviour
|
||||||
|
DhcpServer& getNonOSDhcpServer()
|
||||||
|
{
|
||||||
|
extern netif netif_git[2];
|
||||||
|
static DhcpServer server(&netif_git[SOFTAP_IF]);
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
// `ip_info` is useless, since we get the information from the netif directly
|
||||||
void dhcps_start(struct ip_info *info, netif* apnetif)
|
// `netif` would be netif_git[SOFTAP_IF], which we get from the lwip2 glue
|
||||||
|
void dhcps_start(ip_info*, netif*)
|
||||||
{
|
{
|
||||||
// apnetif is esp interface, replaced by lwip2's
|
auto& server = getNonOSDhcpServer();
|
||||||
// netif_git[SOFTAP_IF] interface in constructor
|
if (!server.isRunning())
|
||||||
(void)apnetif;
|
{
|
||||||
|
server.begin();
|
||||||
#if 0
|
}
|
||||||
// can't use C++ now, global ctors are not initialized yet
|
|
||||||
dhcpSoftAP.begin(info);
|
|
||||||
#else
|
|
||||||
(void)info;
|
|
||||||
// initial version: emulate nonos-sdk in DhcpServer class before
|
|
||||||
// trying to change legacy behavor
|
|
||||||
// `fw_has_started_softap_dhcps` will be read in DhcpServer::DhcpServer
|
|
||||||
// which is called when c++ ctors are initialized, specifically
|
|
||||||
// dhcpSoftAP intialized with AP interface number above.
|
|
||||||
fw_has_started_softap_dhcps = 1;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dhcps_stop()
|
void dhcps_stop()
|
||||||
{
|
{
|
||||||
dhcpSoftAP.end();
|
auto& server = getNonOSDhcpServer();
|
||||||
|
if (server.isRunning())
|
||||||
|
{
|
||||||
|
server.end();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // extern "C"
|
// providing the rest of the nonos-sdk API, which was originally removed in 3.0.0
|
||||||
|
|
||||||
|
bool wifi_softap_set_dhcps_lease(dhcps_lease* please)
|
||||||
|
{
|
||||||
|
auto& server = getNonOSDhcpServer();
|
||||||
|
return server.set_dhcps_lease(please);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wifi_softap_get_dhcps_lease(dhcps_lease* please)
|
||||||
|
{
|
||||||
|
auto& server = getNonOSDhcpServer();
|
||||||
|
return server.get_dhcps_lease(please);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 wifi_softap_get_dhcps_lease_time()
|
||||||
|
{
|
||||||
|
auto& server = getNonOSDhcpServer();
|
||||||
|
return server.getLeaseTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wifi_softap_set_dhcps_lease_time(uint32 minutes)
|
||||||
|
{
|
||||||
|
auto& server = getNonOSDhcpServer();
|
||||||
|
server.setLeaseTime(minutes);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wifi_softap_reset_dhcps_lease_time()
|
||||||
|
{
|
||||||
|
auto& server = getNonOSDhcpServer();
|
||||||
|
server.resetLeaseTime();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wifi_softap_add_dhcps_lease(uint8* macaddr)
|
||||||
|
{
|
||||||
|
auto& server = getNonOSDhcpServer();
|
||||||
|
return server.add_dhcps_lease(macaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
24
cores/esp8266/LwipDhcpServer-NonOS.h
Normal file
24
cores/esp8266/LwipDhcpServer-NonOS.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
NonOS DHCP server helpers
|
||||||
|
|
||||||
|
Copyright (c) 2020-2022 esp8266 arduino. All rights reserved.
|
||||||
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "LwipDhcpServer.h"
|
||||||
|
|
||||||
|
// Global static DHCP instance for softAP interface
|
||||||
|
DhcpServer& getNonOSDhcpServer();
|
File diff suppressed because it is too large
Load Diff
@@ -28,23 +28,147 @@
|
|||||||
// nearly as-is. This is an initial version to guaranty legacy behavior
|
// nearly as-is. This is an initial version to guaranty legacy behavior
|
||||||
// with same default values.
|
// with same default values.
|
||||||
|
|
||||||
#ifndef __DHCPS_H__
|
#pragma once
|
||||||
#define __DHCPS_H__
|
|
||||||
|
|
||||||
#include <lwip/init.h> // LWIP_VERSION
|
#include <lwip/init.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
class DhcpServer
|
class DhcpServer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static constexpr int DefaultLeaseTime = 720; // minutes
|
||||||
|
static constexpr uint32 MagicCookie = 0x63538263; // https://tools.ietf.org/html/rfc1497
|
||||||
|
//
|
||||||
|
struct OptionsBuffer
|
||||||
|
{
|
||||||
|
OptionsBuffer(uint8_t* begin, uint8_t* end) : _it(begin), _begin(begin), _end(end) { }
|
||||||
|
|
||||||
|
OptionsBuffer& add(uint8_t code, const uint8_t* data, size_t size);
|
||||||
|
|
||||||
|
OptionsBuffer& add(uint8_t code, const char* data, size_t size)
|
||||||
|
{
|
||||||
|
return add(code, reinterpret_cast<const uint8_t*>(data), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Size>
|
||||||
|
OptionsBuffer& add(uint8_t code, const char (&data)[Size])
|
||||||
|
{
|
||||||
|
return add(code, &data[0], Size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Size>
|
||||||
|
OptionsBuffer& add(uint8_t code, const uint8_t (&data)[Size])
|
||||||
|
{
|
||||||
|
return add(code, &data[0], Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionsBuffer& add(uint8_t code, std::initializer_list<uint8_t> data)
|
||||||
|
{
|
||||||
|
return add(code, data.begin(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionsBuffer& add(uint8_t code, const ip4_addr_t* addr)
|
||||||
|
{
|
||||||
|
return add(code,
|
||||||
|
{ ip4_addr1(addr), ip4_addr2(addr), ip4_addr3(addr), ip4_addr4(addr) });
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionsBuffer& add(uint8_t code, uint8_t value)
|
||||||
|
{
|
||||||
|
return add(code, { value });
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionsBuffer& add(uint8_t code, uint16_t value)
|
||||||
|
{
|
||||||
|
return add(code, { static_cast<uint8_t>((value >> 8) & 0xff),
|
||||||
|
static_cast<uint8_t>(value & 0xff) });
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionsBuffer& add(uint8_t code, uint32_t value)
|
||||||
|
{
|
||||||
|
return add(code, { static_cast<uint8_t>((value >> 24) & 0xff),
|
||||||
|
static_cast<uint8_t>((value >> 16) & 0xff),
|
||||||
|
static_cast<uint8_t>((value >> 8) & 0xff),
|
||||||
|
static_cast<uint8_t>((value & 0xff)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionsBuffer& add(uint8_t code)
|
||||||
|
{
|
||||||
|
if (_it != _end)
|
||||||
|
{
|
||||||
|
*_it++ = code;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t* _it;
|
||||||
|
uint8_t* _begin;
|
||||||
|
uint8_t* _end;
|
||||||
|
};
|
||||||
|
|
||||||
|
using OptionsBufferHandler = void (*)(const DhcpServer&, OptionsBuffer&);
|
||||||
|
|
||||||
DhcpServer(netif* netif);
|
DhcpServer(netif* netif);
|
||||||
~DhcpServer();
|
~DhcpServer();
|
||||||
|
|
||||||
void setDns(int num, const ipv4_addr_t* dns);
|
netif* getNetif() const
|
||||||
|
{
|
||||||
|
return _netif;
|
||||||
|
}
|
||||||
|
|
||||||
bool begin(ip_info* info);
|
void setRouter(bool value)
|
||||||
|
{
|
||||||
|
offer_router = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getRouter() const
|
||||||
|
{
|
||||||
|
return offer_router;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDns(ip4_addr_t addr)
|
||||||
|
{
|
||||||
|
dns_address = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip4_addr_t getDns() const
|
||||||
|
{
|
||||||
|
return dns_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetLeaseTime()
|
||||||
|
{
|
||||||
|
lease_time = DefaultLeaseTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLeaseTime(uint32_t minutes)
|
||||||
|
{
|
||||||
|
lease_time = minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getLeaseTime() const
|
||||||
|
{
|
||||||
|
return lease_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will use provided callback for ACK and OFFER replies
|
||||||
|
// `options.add(...)` to append to the options list
|
||||||
|
// (does not check for duplicates!)
|
||||||
|
void onSendOptions(OptionsBufferHandler handler)
|
||||||
|
{
|
||||||
|
custom_offer_options = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool begin();
|
||||||
void end();
|
void end();
|
||||||
bool isRunning();
|
bool isRunning() const;
|
||||||
|
|
||||||
// this is the C interface encapsulated in a class
|
// this is the C interface encapsulated in a class
|
||||||
// (originally dhcpserver.c in lwIP-v1.4 in NonOS-SDK)
|
// (originally dhcpserver.c in lwIP-v1.4 in NonOS-SDK)
|
||||||
@@ -54,71 +178,59 @@ public:
|
|||||||
// legacy public C structure and API to eventually turn into C++
|
// legacy public C structure and API to eventually turn into C++
|
||||||
|
|
||||||
void init_dhcps_lease(uint32 ip);
|
void init_dhcps_lease(uint32 ip);
|
||||||
bool set_dhcps_lease(struct dhcps_lease *please);
|
bool set_dhcps_lease(struct dhcps_lease* please);
|
||||||
bool get_dhcps_lease(struct dhcps_lease *please);
|
bool get_dhcps_lease(struct dhcps_lease* please);
|
||||||
bool set_dhcps_offer_option(uint8 level, void* optarg);
|
bool add_dhcps_lease(uint8* macaddr);
|
||||||
bool set_dhcps_lease_time(uint32 minute);
|
|
||||||
bool reset_dhcps_lease_time(void);
|
|
||||||
uint32 get_dhcps_lease_time(void);
|
|
||||||
bool add_dhcps_lease(uint8 *macaddr);
|
|
||||||
|
|
||||||
void dhcps_set_dns(int num, const ipv4_addr_t* dns);
|
void offers();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void add_offer_options(OptionsBuffer&);
|
||||||
|
|
||||||
// legacy C structure and API to eventually turn into C++
|
// legacy C structure and API to eventually turn into C++
|
||||||
|
|
||||||
typedef struct _list_node
|
typedef struct _list_node
|
||||||
{
|
{
|
||||||
void *pnode;
|
void* pnode;
|
||||||
struct _list_node *pnext;
|
struct _list_node* pnext;
|
||||||
} list_node;
|
} list_node;
|
||||||
|
|
||||||
void node_insert_to_list(list_node **phead, list_node* pinsert);
|
void node_insert_to_list(list_node** phead, list_node* pinsert);
|
||||||
void node_remove_from_list(list_node **phead, list_node* pdelete);
|
void node_remove_from_list(list_node** phead, list_node* pdelete);
|
||||||
uint8_t* add_msg_type(uint8_t *optptr, uint8_t type);
|
|
||||||
uint8_t* add_offer_options(uint8_t *optptr);
|
OptionsBuffer create_msg(struct dhcps_msg* m);
|
||||||
uint8_t* add_end(uint8_t *optptr);
|
|
||||||
void create_msg(struct dhcps_msg *m);
|
void send_offer(struct dhcps_msg* m);
|
||||||
void send_offer(struct dhcps_msg *m);
|
void send_nak(struct dhcps_msg* m);
|
||||||
void send_nak(struct dhcps_msg *m);
|
void send_ack(struct dhcps_msg* m);
|
||||||
void send_ack(struct dhcps_msg *m);
|
uint8_t parse_options(uint8_t* optptr, sint16_t len);
|
||||||
uint8_t parse_options(uint8_t *optptr, sint16_t len);
|
sint16_t parse_msg(struct dhcps_msg* m, u16_t len);
|
||||||
sint16_t parse_msg(struct dhcps_msg *m, u16_t len);
|
static void S_handle_dhcp(void* arg, struct udp_pcb* pcb, struct pbuf* p, const ip_addr_t* addr,
|
||||||
static void S_handle_dhcp(void *arg,
|
|
||||||
struct udp_pcb *pcb,
|
|
||||||
struct pbuf *p,
|
|
||||||
const ip_addr_t *addr,
|
|
||||||
uint16_t port);
|
uint16_t port);
|
||||||
void handle_dhcp(
|
void handle_dhcp(struct udp_pcb* pcb, struct pbuf* p, const ip_addr_t* addr, uint16_t port);
|
||||||
struct udp_pcb *pcb,
|
void kill_oldest_dhcps_pool(void);
|
||||||
struct pbuf *p,
|
void dhcps_coarse_tmr(void); // CURRENTLY NOT CALLED
|
||||||
const ip_addr_t *addr,
|
void dhcps_client_leave(u8* bssid, struct ipv4_addr* ip, bool force);
|
||||||
uint16_t port);
|
uint32 dhcps_client_update(u8* bssid, struct ipv4_addr* ip);
|
||||||
void kill_oldest_dhcps_pool(void);
|
|
||||||
void dhcps_coarse_tmr(void); // CURRENTLY NOT CALLED
|
|
||||||
void dhcps_client_leave(u8 *bssid, struct ipv4_addr *ip, bool force);
|
|
||||||
uint32 dhcps_client_update(u8 *bssid, struct ipv4_addr *ip);
|
|
||||||
|
|
||||||
netif* _netif;
|
netif* _netif = nullptr;
|
||||||
|
|
||||||
struct udp_pcb *pcb_dhcps;
|
struct udp_pcb* pcb_dhcps = nullptr;
|
||||||
ip_addr_t broadcast_dhcps;
|
ip_addr_t broadcast_dhcps {};
|
||||||
struct ipv4_addr server_address;
|
ip4_addr_t server_address {};
|
||||||
struct ipv4_addr client_address;
|
ip4_addr_t client_address {};
|
||||||
struct ipv4_addr dns_address;
|
|
||||||
uint32 dhcps_lease_time;
|
|
||||||
|
|
||||||
struct dhcps_lease dhcps_lease;
|
uint32_t lease_time = DefaultLeaseTime;
|
||||||
list_node *plist;
|
|
||||||
uint8 offer;
|
bool offer_router = true;
|
||||||
bool renew;
|
ip4_addr_t dns_address {};
|
||||||
|
|
||||||
|
dhcps_lease lease {};
|
||||||
|
|
||||||
|
list_node* plist = nullptr;
|
||||||
|
bool renew = false;
|
||||||
|
|
||||||
|
OptionsBufferHandler custom_offer_options = nullptr;
|
||||||
|
|
||||||
static const uint32 magic_cookie;
|
static const uint32 magic_cookie;
|
||||||
};
|
};
|
||||||
|
|
||||||
// SoftAP DHCP server always exists and is started on boot
|
|
||||||
extern DhcpServer dhcpSoftAP;
|
|
||||||
extern "C" int fw_has_started_softap_dhcps;
|
|
||||||
|
|
||||||
#endif // __DHCPS_H__
|
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
|
|
||||||
extern "C" {
|
extern "C"
|
||||||
|
{
|
||||||
#include "lwip/err.h"
|
#include "lwip/err.h"
|
||||||
#include "lwip/ip_addr.h"
|
#include "lwip/ip_addr.h"
|
||||||
#include "lwip/dns.h"
|
#include "lwip/dns.h"
|
||||||
#include "lwip/dhcp.h"
|
#include "lwip/dhcp.h"
|
||||||
#include "lwip/init.h" // LWIP_VERSION_
|
#include "lwip/init.h" // LWIP_VERSION_
|
||||||
#if LWIP_IPV6
|
#if LWIP_IPV6
|
||||||
#include "lwip/netif.h" // struct netif
|
#include "lwip/netif.h" // struct netif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <user_interface.h>
|
#include <user_interface.h>
|
||||||
@@ -24,19 +25,23 @@ extern "C" {
|
|||||||
//
|
//
|
||||||
// result stored into gateway/netmask/dns1
|
// result stored into gateway/netmask/dns1
|
||||||
|
|
||||||
bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1, const IPAddress& arg2, const IPAddress& arg3,
|
bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1,
|
||||||
IPAddress& gateway, IPAddress& netmask, IPAddress& dns1)
|
const IPAddress& arg2, const IPAddress& arg3, IPAddress& gateway,
|
||||||
|
IPAddress& netmask, IPAddress& dns1)
|
||||||
{
|
{
|
||||||
//To allow compatibility, check first octet of 3rd arg. If 255, interpret as ESP order, otherwise Arduino order.
|
// To allow compatibility, check first octet of 3rd arg. If 255, interpret as ESP order,
|
||||||
|
// otherwise Arduino order.
|
||||||
gateway = arg1;
|
gateway = arg1;
|
||||||
netmask = arg2;
|
netmask = arg2;
|
||||||
dns1 = arg3;
|
dns1 = arg3;
|
||||||
|
|
||||||
if (netmask[0] != 255)
|
if (netmask[0] != 255)
|
||||||
{
|
{
|
||||||
//octet is not 255 => interpret as Arduino order
|
// octet is not 255 => interpret as Arduino order
|
||||||
gateway = arg2;
|
gateway = arg2;
|
||||||
netmask = arg3[0] == 0 ? IPAddress(255, 255, 255, 0) : arg3; //arg order is arduino and 4th arg not given => assign it arduino default
|
netmask = arg3[0] == 0 ? IPAddress(255, 255, 255, 0)
|
||||||
|
: arg3; // arg order is arduino and 4th arg not given => assign it
|
||||||
|
// arduino default
|
||||||
dns1 = arg1;
|
dns1 = arg1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +51,7 @@ bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//ip and gateway must be in the same netmask
|
// ip and gateway must be in the same netmask
|
||||||
if (gateway.isSet() && (local_ip.v4() & netmask.v4()) != (gateway.v4() & netmask.v4()))
|
if (gateway.isSet() && (local_ip.v4() & netmask.v4()) != (gateway.v4() & netmask.v4()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -143,7 +148,6 @@ bool LwipIntf::hostname(const char* aHostname)
|
|||||||
// harmless for AP, also compatible with ethernet adapters (to come)
|
// harmless for AP, also compatible with ethernet adapters (to come)
|
||||||
for (netif* intf = netif_list; intf; intf = intf->next)
|
for (netif* intf = netif_list; intf; intf = intf->next)
|
||||||
{
|
{
|
||||||
|
|
||||||
// unconditionally update all known interfaces
|
// unconditionally update all known interfaces
|
||||||
intf->hostname = wifi_station_get_hostname();
|
intf->hostname = wifi_station_get_hostname();
|
||||||
|
|
||||||
@@ -162,4 +166,3 @@ bool LwipIntf::hostname(const char* aHostname)
|
|||||||
|
|
||||||
return ret && compliant;
|
return ret && compliant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,8 +10,7 @@
|
|||||||
class LwipIntf
|
class LwipIntf
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using CBType = std::function<void(netif*)>;
|
||||||
using CBType = std::function <void(netif*)>;
|
|
||||||
|
|
||||||
static bool stateUpCB(LwipIntf::CBType&& cb);
|
static bool stateUpCB(LwipIntf::CBType&& cb);
|
||||||
|
|
||||||
@@ -24,12 +23,12 @@ public:
|
|||||||
// arg3 | dns1 netmask
|
// arg3 | dns1 netmask
|
||||||
//
|
//
|
||||||
// result stored into gateway/netmask/dns1
|
// result stored into gateway/netmask/dns1
|
||||||
static
|
static bool ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1,
|
||||||
bool ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1, const IPAddress& arg2, const IPAddress& arg3,
|
const IPAddress& arg2, const IPAddress& arg3, IPAddress& gateway,
|
||||||
IPAddress& gateway, IPAddress& netmask, IPAddress& dns1);
|
IPAddress& netmask, IPAddress& dns1);
|
||||||
|
|
||||||
String hostname();
|
String hostname();
|
||||||
bool hostname(const String& aHostname)
|
bool hostname(const String& aHostname)
|
||||||
{
|
{
|
||||||
return hostname(aHostname.c_str());
|
return hostname(aHostname.c_str());
|
||||||
}
|
}
|
||||||
@@ -42,8 +41,7 @@ public:
|
|||||||
const char* getHostname();
|
const char* getHostname();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
static bool stateChangeSysCB(LwipIntf::CBType&& cb);
|
static bool stateChangeSysCB(LwipIntf::CBType&& cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _LWIPINTF_H
|
#endif // _LWIPINTF_H
|
||||||
|
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
#define NETIF_STATUS_CB_SIZE 3
|
#define NETIF_STATUS_CB_SIZE 3
|
||||||
|
|
||||||
static int netifStatusChangeListLength = 0;
|
static int netifStatusChangeListLength = 0;
|
||||||
LwipIntf::CBType netifStatusChangeList [NETIF_STATUS_CB_SIZE];
|
LwipIntf::CBType netifStatusChangeList[NETIF_STATUS_CB_SIZE];
|
||||||
|
|
||||||
extern "C" void netif_status_changed(struct netif* netif)
|
extern "C" void netif_status_changed(struct netif* netif)
|
||||||
{
|
{
|
||||||
@@ -33,12 +33,14 @@ bool LwipIntf::stateChangeSysCB(LwipIntf::CBType&& cb)
|
|||||||
|
|
||||||
bool LwipIntf::stateUpCB(LwipIntf::CBType&& cb)
|
bool LwipIntf::stateUpCB(LwipIntf::CBType&& cb)
|
||||||
{
|
{
|
||||||
return stateChangeSysCB([cb](netif * nif)
|
return stateChangeSysCB(
|
||||||
{
|
[cb](netif* nif)
|
||||||
if (netif_is_up(nif))
|
|
||||||
schedule_function([cb, nif]()
|
|
||||||
{
|
{
|
||||||
cb(nif);
|
if (netif_is_up(nif))
|
||||||
|
schedule_function(
|
||||||
|
[cb, nif]()
|
||||||
|
{
|
||||||
|
cb(nif);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@@ -11,9 +11,10 @@
|
|||||||
#include <lwip/netif.h>
|
#include <lwip/netif.h>
|
||||||
#include <lwip/etharp.h>
|
#include <lwip/etharp.h>
|
||||||
#include <lwip/dhcp.h>
|
#include <lwip/dhcp.h>
|
||||||
|
#include <lwip/dns.h>
|
||||||
#include <lwip/apps/sntp.h>
|
#include <lwip/apps/sntp.h>
|
||||||
|
|
||||||
#include <user_interface.h> // wifi_get_macaddr()
|
#include <user_interface.h> // wifi_get_macaddr()
|
||||||
|
|
||||||
#include "SPI.h"
|
#include "SPI.h"
|
||||||
#include "Schedule.h"
|
#include "Schedule.h"
|
||||||
@@ -24,63 +25,74 @@
|
|||||||
#define DEFAULT_MTU 1500
|
#define DEFAULT_MTU 1500
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
class LwipIntfDev: public LwipIntf, public RawDev
|
class LwipIntfDev: public LwipIntf, public RawDev
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
LwipIntfDev(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1) :
|
||||||
LwipIntfDev(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1):
|
RawDev(cs, spi, intr), _mtu(DEFAULT_MTU), _intrPin(intr), _started(false), _default(false)
|
||||||
RawDev(cs, spi, intr),
|
|
||||||
_mtu(DEFAULT_MTU),
|
|
||||||
_intrPin(intr),
|
|
||||||
_started(false),
|
|
||||||
_default(false)
|
|
||||||
{
|
{
|
||||||
memset(&_netif, 0, sizeof(_netif));
|
memset(&_netif, 0, sizeof(_netif));
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean config(const IPAddress& local_ip, const IPAddress& arg1, const IPAddress& arg2, const IPAddress& arg3, const IPAddress& dns2);
|
boolean config(const IPAddress& local_ip, const IPAddress& arg1, const IPAddress& arg2,
|
||||||
|
const IPAddress& arg3 = IPADDR_NONE, const IPAddress& dns2 = IPADDR_NONE);
|
||||||
|
|
||||||
// default mac-address is inferred from esp8266's STA interface
|
// default mac-address is inferred from esp8266's STA interface
|
||||||
boolean begin(const uint8_t *macAddress = nullptr, const uint16_t mtu = DEFAULT_MTU);
|
boolean begin(const uint8_t* macAddress = nullptr, const uint16_t mtu = DEFAULT_MTU);
|
||||||
|
|
||||||
const netif* getNetIf() const
|
const netif* getNetIf() const
|
||||||
{
|
{
|
||||||
return &_netif;
|
return &_netif;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPAddress localIP() const
|
IPAddress localIP() const
|
||||||
{
|
{
|
||||||
return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.ip_addr)));
|
return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.ip_addr)));
|
||||||
}
|
}
|
||||||
IPAddress subnetMask() const
|
IPAddress subnetMask() const
|
||||||
{
|
{
|
||||||
return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.netmask)));
|
return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.netmask)));
|
||||||
}
|
}
|
||||||
IPAddress gatewayIP() const
|
IPAddress gatewayIP() const
|
||||||
{
|
{
|
||||||
return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.gw)));
|
return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.gw)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDefault();
|
// 1. Currently when no default is set, esp8266-Arduino uses the first
|
||||||
|
// DHCP client interface receiving a valid address and gateway to
|
||||||
|
// become the new lwIP default interface.
|
||||||
|
// 2. Otherwise - when using static addresses - lwIP for every packets by
|
||||||
|
// defaults selects automatically the best suited output interface
|
||||||
|
// matching the destination address. If several interfaces match,
|
||||||
|
// the first one is picked. On esp8266/Arduno: WiFi interfaces are
|
||||||
|
// checked first.
|
||||||
|
// 3. Or, use `::setDefault(true)` to force using this interface's gateway
|
||||||
|
// as default router.
|
||||||
|
void setDefault(bool deflt = true);
|
||||||
|
|
||||||
|
// true if interface has a valid IPv4 address
|
||||||
bool connected()
|
bool connected()
|
||||||
{
|
{
|
||||||
return !!ip4_addr_get_u32(ip_2_ip4(&_netif.ip_addr));
|
return !!ip4_addr_get_u32(ip_2_ip4(&_netif.ip_addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool routable()
|
||||||
|
{
|
||||||
|
return !ip_addr_isany(&_netif.gw);
|
||||||
|
}
|
||||||
|
|
||||||
// ESP8266WiFi API compatibility
|
// ESP8266WiFi API compatibility
|
||||||
|
|
||||||
wl_status_t status();
|
wl_status_t status();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
err_t netif_init();
|
err_t netif_init();
|
||||||
|
void check_route();
|
||||||
void netif_status_callback();
|
void netif_status_callback();
|
||||||
|
|
||||||
static err_t netif_init_s(netif* netif);
|
static err_t netif_init_s(netif* netif);
|
||||||
static err_t linkoutput_s(netif *netif, struct pbuf *p);
|
static err_t linkoutput_s(netif* netif, struct pbuf* p);
|
||||||
static void netif_status_callback_s(netif* netif);
|
static void netif_status_callback_s(netif* netif);
|
||||||
|
|
||||||
// called on a regular basis or on interrupt
|
// called on a regular basis or on interrupt
|
||||||
@@ -88,18 +100,19 @@ protected:
|
|||||||
|
|
||||||
// members
|
// members
|
||||||
|
|
||||||
netif _netif;
|
netif _netif;
|
||||||
|
|
||||||
uint16_t _mtu;
|
|
||||||
int8_t _intrPin;
|
|
||||||
uint8_t _macAddress[6];
|
|
||||||
bool _started;
|
|
||||||
bool _default;
|
|
||||||
|
|
||||||
|
uint16_t _mtu;
|
||||||
|
int8_t _intrPin;
|
||||||
|
uint8_t _macAddress[6];
|
||||||
|
bool _started;
|
||||||
|
bool _default;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
boolean LwipIntfDev<RawDev>::config(const IPAddress& localIP, const IPAddress& gateway, const IPAddress& netmask, const IPAddress& dns1, const IPAddress& dns2)
|
boolean LwipIntfDev<RawDev>::config(const IPAddress& localIP, const IPAddress& gateway,
|
||||||
|
const IPAddress& netmask, const IPAddress& dns1,
|
||||||
|
const IPAddress& dns2)
|
||||||
{
|
{
|
||||||
if (_started)
|
if (_started)
|
||||||
{
|
{
|
||||||
@@ -116,10 +129,21 @@ boolean LwipIntfDev<RawDev>::config(const IPAddress& localIP, const IPAddress& g
|
|||||||
ip4_addr_set_u32(ip_2_ip4(&_netif.gw), realGateway.v4());
|
ip4_addr_set_u32(ip_2_ip4(&_netif.gw), realGateway.v4());
|
||||||
ip4_addr_set_u32(ip_2_ip4(&_netif.netmask), realNetmask.v4());
|
ip4_addr_set_u32(ip_2_ip4(&_netif.netmask), realNetmask.v4());
|
||||||
|
|
||||||
|
if (realDns1.isSet())
|
||||||
|
{
|
||||||
|
// Set DNS1-Server
|
||||||
|
dns_setserver(0, realDns1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dns2.isSet())
|
||||||
|
{
|
||||||
|
// Set DNS2-Server
|
||||||
|
dns_setserver(1, dns2);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu)
|
boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu)
|
||||||
{
|
{
|
||||||
if (mtu)
|
if (mtu)
|
||||||
@@ -149,9 +173,9 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
|
|||||||
memset(_macAddress, 0, 6);
|
memset(_macAddress, 0, 6);
|
||||||
_macAddress[0] = 0xEE;
|
_macAddress[0] = 0xEE;
|
||||||
#endif
|
#endif
|
||||||
_macAddress[3] += _netif.num; // alter base mac address
|
_macAddress[3] += _netif.num; // alter base mac address
|
||||||
_macAddress[0] &= 0xfe; // set as locally administered, unicast, per
|
_macAddress[0] &= 0xfe; // set as locally administered, unicast, per
|
||||||
_macAddress[0] |= 0x02; // https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local
|
_macAddress[0] |= 0x02; // https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!RawDev::begin(_macAddress))
|
if (!RawDev::begin(_macAddress))
|
||||||
@@ -170,15 +194,16 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
|
|||||||
ip_addr_copy(netmask, _netif.netmask);
|
ip_addr_copy(netmask, _netif.netmask);
|
||||||
ip_addr_copy(gw, _netif.gw);
|
ip_addr_copy(gw, _netif.gw);
|
||||||
|
|
||||||
if (!netif_add(&_netif, ip_2_ip4(&ip_addr), ip_2_ip4(&netmask), ip_2_ip4(&gw), this, netif_init_s, ethernet_input))
|
if (!netif_add(&_netif, ip_2_ip4(&ip_addr), ip_2_ip4(&netmask), ip_2_ip4(&gw), this,
|
||||||
|
netif_init_s, ethernet_input))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_netif.flags |= NETIF_FLAG_UP;
|
|
||||||
|
|
||||||
if (localIP().v4() == 0)
|
if (localIP().v4() == 0)
|
||||||
{
|
{
|
||||||
|
// IP not set, starting DHCP
|
||||||
|
_netif.flags |= NETIF_FLAG_UP;
|
||||||
switch (dhcp_start(&_netif))
|
switch (dhcp_start(&_netif))
|
||||||
{
|
{
|
||||||
case ERR_OK:
|
case ERR_OK:
|
||||||
@@ -192,6 +217,12 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// IP is set, static config
|
||||||
|
netif_set_link_up(&_netif);
|
||||||
|
netif_set_up(&_netif);
|
||||||
|
}
|
||||||
|
|
||||||
_started = true;
|
_started = true;
|
||||||
|
|
||||||
@@ -199,20 +230,24 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
|
|||||||
{
|
{
|
||||||
if (RawDev::interruptIsPossible())
|
if (RawDev::interruptIsPossible())
|
||||||
{
|
{
|
||||||
//attachInterrupt(_intrPin, [&]() { this->handlePackets(); }, FALLING);
|
// attachInterrupt(_intrPin, [&]() { this->handlePackets(); }, FALLING);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
::printf((PGM_P)F("lwIP_Intf: Interrupt not implemented yet, enabling transparent polling\r\n"));
|
::printf((PGM_P)F(
|
||||||
|
"lwIP_Intf: Interrupt not implemented yet, enabling transparent polling\r\n"));
|
||||||
_intrPin = -1;
|
_intrPin = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_intrPin < 0 && !schedule_recurrent_function_us([&]()
|
if (_intrPin < 0
|
||||||
{
|
&& !schedule_recurrent_function_us(
|
||||||
this->handlePackets();
|
[&]()
|
||||||
return true;
|
{
|
||||||
}, 100))
|
this->handlePackets();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
100))
|
||||||
{
|
{
|
||||||
netif_remove(&_netif);
|
netif_remove(&_netif);
|
||||||
return false;
|
return false;
|
||||||
@@ -221,14 +256,14 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
wl_status_t LwipIntfDev<RawDev>::status()
|
wl_status_t LwipIntfDev<RawDev>::status()
|
||||||
{
|
{
|
||||||
return _started ? (connected() ? WL_CONNECTED : WL_DISCONNECTED) : WL_NO_SHIELD;
|
return _started ? (connected() ? WL_CONNECTED : WL_DISCONNECTED) : WL_NO_SHIELD;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
err_t LwipIntfDev<RawDev>::linkoutput_s(netif *netif, struct pbuf *pbuf)
|
err_t LwipIntfDev<RawDev>::linkoutput_s(netif* netif, struct pbuf* pbuf)
|
||||||
{
|
{
|
||||||
LwipIntfDev* ths = (LwipIntfDev*)netif->state;
|
LwipIntfDev* ths = (LwipIntfDev*)netif->state;
|
||||||
|
|
||||||
@@ -242,37 +277,34 @@ err_t LwipIntfDev<RawDev>::linkoutput_s(netif *netif, struct pbuf *pbuf)
|
|||||||
#if PHY_HAS_CAPTURE
|
#if PHY_HAS_CAPTURE
|
||||||
if (phy_capture)
|
if (phy_capture)
|
||||||
{
|
{
|
||||||
phy_capture(ths->_netif.num, (const char*)pbuf->payload, pbuf->len, /*out*/1, /*success*/len == pbuf->len);
|
phy_capture(ths->_netif.num, (const char*)pbuf->payload, pbuf->len, /*out*/ 1,
|
||||||
|
/*success*/ len == pbuf->len);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return len == pbuf->len ? ERR_OK : ERR_MEM;
|
return len == pbuf->len ? ERR_OK : ERR_MEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
err_t LwipIntfDev<RawDev>::netif_init_s(struct netif* netif)
|
err_t LwipIntfDev<RawDev>::netif_init_s(struct netif* netif)
|
||||||
{
|
{
|
||||||
return ((LwipIntfDev*)netif->state)->netif_init();
|
return ((LwipIntfDev*)netif->state)->netif_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
void LwipIntfDev<RawDev>::netif_status_callback_s(struct netif* netif)
|
void LwipIntfDev<RawDev>::netif_status_callback_s(struct netif* netif)
|
||||||
{
|
{
|
||||||
((LwipIntfDev*)netif->state)->netif_status_callback();
|
((LwipIntfDev*)netif->state)->netif_status_callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
err_t LwipIntfDev<RawDev>::netif_init()
|
err_t LwipIntfDev<RawDev>::netif_init()
|
||||||
{
|
{
|
||||||
_netif.name[0] = 'e';
|
_netif.name[0] = 'e';
|
||||||
_netif.name[1] = '0' + _netif.num;
|
_netif.name[1] = '0' + _netif.num;
|
||||||
_netif.mtu = _mtu;
|
_netif.mtu = _mtu;
|
||||||
_netif.chksum_flags = NETIF_CHECKSUM_ENABLE_ALL;
|
_netif.chksum_flags = NETIF_CHECKSUM_ENABLE_ALL;
|
||||||
_netif.flags =
|
_netif.flags = NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP | NETIF_FLAG_BROADCAST | NETIF_FLAG_LINK_UP;
|
||||||
NETIF_FLAG_ETHARP
|
|
||||||
| NETIF_FLAG_IGMP
|
|
||||||
| NETIF_FLAG_BROADCAST
|
|
||||||
| NETIF_FLAG_LINK_UP;
|
|
||||||
|
|
||||||
// lwIP's doc: This function typically first resolves the hardware
|
// lwIP's doc: This function typically first resolves the hardware
|
||||||
// address, then sends the packet. For ethernet physical layer, this is
|
// address, then sends the packet. For ethernet physical layer, this is
|
||||||
@@ -288,17 +320,28 @@ err_t LwipIntfDev<RawDev>::netif_init()
|
|||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
void LwipIntfDev<RawDev>::netif_status_callback()
|
void LwipIntfDev<RawDev>::netif_status_callback()
|
||||||
|
{
|
||||||
|
check_route();
|
||||||
|
if (connected())
|
||||||
|
{
|
||||||
|
sntp_stop();
|
||||||
|
sntp_init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class RawDev>
|
||||||
|
void LwipIntfDev<RawDev>::check_route()
|
||||||
{
|
{
|
||||||
if (connected())
|
if (connected())
|
||||||
{
|
{
|
||||||
if (_default)
|
if (_default || (netif_default == nullptr && routable()))
|
||||||
{
|
{
|
||||||
|
// on user request,
|
||||||
|
// or if there is no current default interface, but our gateway is valid
|
||||||
netif_set_default(&_netif);
|
netif_set_default(&_netif);
|
||||||
}
|
}
|
||||||
sntp_stop();
|
|
||||||
sntp_init();
|
|
||||||
}
|
}
|
||||||
else if (netif_default == &_netif)
|
else if (netif_default == &_netif)
|
||||||
{
|
{
|
||||||
@@ -306,14 +349,14 @@ void LwipIntfDev<RawDev>::netif_status_callback()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
err_t LwipIntfDev<RawDev>::handlePackets()
|
err_t LwipIntfDev<RawDev>::handlePackets()
|
||||||
{
|
{
|
||||||
int pkt = 0;
|
int pkt = 0;
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (++pkt == 10)
|
if (++pkt == 10)
|
||||||
// prevent starvation
|
// prevent starvation
|
||||||
{
|
{
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
@@ -359,7 +402,8 @@ err_t LwipIntfDev<RawDev>::handlePackets()
|
|||||||
#if PHY_HAS_CAPTURE
|
#if PHY_HAS_CAPTURE
|
||||||
if (phy_capture)
|
if (phy_capture)
|
||||||
{
|
{
|
||||||
phy_capture(_netif.num, (const char*)pbuf->payload, tot_len, /*out*/0, /*success*/err == ERR_OK);
|
phy_capture(_netif.num, (const char*)pbuf->payload, tot_len, /*out*/ 0,
|
||||||
|
/*success*/ err == ERR_OK);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -369,18 +413,14 @@ err_t LwipIntfDev<RawDev>::handlePackets()
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
// (else) allocated pbuf is now lwIP's responsibility
|
// (else) allocated pbuf is now lwIP's responsibility
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class RawDev>
|
template<class RawDev>
|
||||||
void LwipIntfDev<RawDev>::setDefault()
|
void LwipIntfDev<RawDev>::setDefault(bool deflt)
|
||||||
{
|
{
|
||||||
_default = true;
|
_default = deflt;
|
||||||
if (connected())
|
check_route();
|
||||||
{
|
|
||||||
netif_set_default(&_netif);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // _LWIPINTFDEV_H
|
#endif // _LWIPINTFDEV_H
|
||||||
|
@@ -27,6 +27,7 @@
|
|||||||
#include <limits> // std::numeric_limits
|
#include <limits> // std::numeric_limits
|
||||||
#include <type_traits> // std::is_unsigned
|
#include <type_traits> // std::is_unsigned
|
||||||
#include <core_esp8266_features.h>
|
#include <core_esp8266_features.h>
|
||||||
|
#include <coredecls.h>
|
||||||
|
|
||||||
namespace esp8266
|
namespace esp8266
|
||||||
{
|
{
|
||||||
@@ -45,7 +46,7 @@ struct DoNothing
|
|||||||
|
|
||||||
struct YieldOrSkip
|
struct YieldOrSkip
|
||||||
{
|
{
|
||||||
static void execute() {delay(0);}
|
static void execute() {esp_yield();}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <unsigned long delayMs>
|
template <unsigned long delayMs>
|
||||||
|
@@ -76,7 +76,7 @@ class Print {
|
|||||||
inline size_t write(int8_t c) { return write((uint8_t) c); }
|
inline size_t write(int8_t c) { return write((uint8_t) c); }
|
||||||
|
|
||||||
// default to zero, meaning "a single write may block"
|
// default to zero, meaning "a single write may block"
|
||||||
// should be overriden by subclasses with buffering
|
// should be overridden by subclasses with buffering
|
||||||
virtual int availableForWrite() { return 0; }
|
virtual int availableForWrite() { return 0; }
|
||||||
|
|
||||||
size_t printf(const char * format, ...) __attribute__ ((format (printf, 2, 3)));
|
size_t printf(const char * format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
@@ -110,7 +110,10 @@ class Print {
|
|||||||
size_t println(const Printable&);
|
size_t println(const Printable&);
|
||||||
size_t println(void);
|
size_t println(void);
|
||||||
|
|
||||||
virtual void flush() { /* Empty implementation for backward compatibility */ }
|
// flush():
|
||||||
|
// Empty implementation by default in Print::
|
||||||
|
// should wait for all outgoing characters to be sent, output buffer is empty after this call
|
||||||
|
virtual void flush() { }
|
||||||
|
|
||||||
// by default write timeout is possible (outgoing data from network,serial..)
|
// by default write timeout is possible (outgoing data from network,serial..)
|
||||||
// (children can override to false (like String))
|
// (children can override to false (like String))
|
||||||
|
@@ -135,8 +135,6 @@ bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,
|
|||||||
|
|
||||||
void run_scheduled_functions()
|
void run_scheduled_functions()
|
||||||
{
|
{
|
||||||
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
|
|
||||||
|
|
||||||
// prevent scheduling of new functions during this run
|
// prevent scheduling of new functions during this run
|
||||||
auto stop = sLast;
|
auto stop = sLast;
|
||||||
bool done = false;
|
bool done = false;
|
||||||
@@ -161,13 +159,10 @@ void run_scheduled_functions()
|
|||||||
recycle_fn_unsafe(to_recycle);
|
recycle_fn_unsafe(to_recycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (yieldNow)
|
// scheduled functions might last too long for watchdog etc.
|
||||||
{
|
// yield() is allowed in scheduled functions, therefore
|
||||||
// because scheduled functions might last too long for watchdog etc,
|
// recursion into run_scheduled_recurrent_functions() is permitted
|
||||||
// this is yield() in cont stack:
|
optimistic_yield(100000);
|
||||||
esp_schedule();
|
|
||||||
cont_yield(g_pcont);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,9 +236,10 @@ void run_scheduled_recurrent_functions()
|
|||||||
if (yieldNow)
|
if (yieldNow)
|
||||||
{
|
{
|
||||||
// because scheduled functions might last too long for watchdog etc,
|
// because scheduled functions might last too long for watchdog etc,
|
||||||
// this is yield() in cont stack:
|
// this is yield() in cont stack, but need to call cont_suspend directly
|
||||||
|
// to prevent recursion into run_scheduled_recurrent_functions()
|
||||||
esp_schedule();
|
esp_schedule();
|
||||||
cont_yield(g_pcont);
|
cont_suspend(g_pcont);
|
||||||
}
|
}
|
||||||
} while (current && !done);
|
} while (current && !done);
|
||||||
|
|
||||||
|
@@ -148,7 +148,7 @@ long Stream::parseInt(char skipChar) {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
if(c == skipChar)
|
if(c == skipChar)
|
||||||
; // ignore this charactor
|
; // ignore this character
|
||||||
else if(c == '-')
|
else if(c == '-')
|
||||||
isNegative = true;
|
isNegative = true;
|
||||||
else if(c >= '0' && c <= '9') // is c a digit?
|
else if(c >= '0' && c <= '9') // is c a digit?
|
||||||
@@ -274,7 +274,7 @@ int Stream::read (uint8_t* buffer, size_t maxLen)
|
|||||||
int c = read();
|
int c = read();
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
buffer[nbread++] = read();
|
buffer[nbread++] = c;
|
||||||
}
|
}
|
||||||
return nbread;
|
return nbread;
|
||||||
}
|
}
|
||||||
|
@@ -61,6 +61,7 @@ class Stream: public Print {
|
|||||||
virtual int peek() = 0;
|
virtual int peek() = 0;
|
||||||
|
|
||||||
Stream() {}
|
Stream() {}
|
||||||
|
virtual ~Stream() {}
|
||||||
|
|
||||||
// parsing methods
|
// parsing methods
|
||||||
|
|
||||||
@@ -162,27 +163,27 @@ class Stream: public Print {
|
|||||||
// ::send*() methods:
|
// ::send*() methods:
|
||||||
// - always stop before timeout when "no-more-input-possible-data"
|
// - always stop before timeout when "no-more-input-possible-data"
|
||||||
// or "no-more-output-possible-data" condition is met
|
// or "no-more-output-possible-data" condition is met
|
||||||
// - always return number of transfered bytes
|
// - always return number of transferred bytes
|
||||||
// When result is 0 or less than requested maxLen, Print::getLastSend()
|
// When result is 0 or less than requested maxLen, Print::getLastSend()
|
||||||
// contains an error reason.
|
// contains an error reason.
|
||||||
|
|
||||||
// transfers already buffered / immediately available data (no timeout)
|
// transfers already buffered / immediately available data (no timeout)
|
||||||
// returns number of transfered bytes
|
// returns number of transferred bytes
|
||||||
size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); }
|
size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); }
|
||||||
size_t sendAvailable (Print& to) { return sendAvailable(&to); }
|
size_t sendAvailable (Print& to) { return sendAvailable(&to); }
|
||||||
|
|
||||||
// transfers data until timeout
|
// transfers data until timeout
|
||||||
// returns number of transfered bytes
|
// returns number of transferred bytes
|
||||||
size_t sendAll (Print* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); }
|
size_t sendAll (Print* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); }
|
||||||
size_t sendAll (Print& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); }
|
size_t sendAll (Print& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); }
|
||||||
|
|
||||||
// transfers data until a char is encountered (the char is swallowed but not transfered) with timeout
|
// transfers data until a char is encountered (the char is swallowed but not transferred) with timeout
|
||||||
// returns number of transfered bytes
|
// returns number of transferred bytes
|
||||||
size_t sendUntil (Print* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); }
|
size_t sendUntil (Print* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); }
|
||||||
size_t sendUntil (Print& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); }
|
size_t sendUntil (Print& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); }
|
||||||
|
|
||||||
// transfers data until requested size or timeout
|
// transfers data until requested size or timeout
|
||||||
// returns number of transfered bytes
|
// returns number of transferred bytes
|
||||||
size_t sendSize (Print* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); }
|
size_t sendSize (Print* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); }
|
||||||
size_t sendSize (Print& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); }
|
size_t sendSize (Print& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); }
|
||||||
|
|
||||||
|
@@ -160,6 +160,7 @@ protected:
|
|||||||
size_t _peekPointer = 0;
|
size_t _peekPointer = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
StreamConstPtr(const String&& string) = delete; // prevents passing String temporary, use ctor(buffer,size) if you know what you are doing
|
||||||
StreamConstPtr(const String& string): _buffer(string.c_str()), _size(string.length()), _byteAddressable(true) { }
|
StreamConstPtr(const String& string): _buffer(string.c_str()), _size(string.length()), _byteAddressable(true) { }
|
||||||
StreamConstPtr(const char* buffer, size_t size): _buffer(buffer), _size(size), _byteAddressable(__byteAddressable(buffer)) { }
|
StreamConstPtr(const char* buffer, size_t size): _buffer(buffer), _size(size), _byteAddressable(__byteAddressable(buffer)) { }
|
||||||
StreamConstPtr(const uint8_t* buffer, size_t size): _buffer((const char*)buffer), _size(size), _byteAddressable(__byteAddressable(buffer)) { }
|
StreamConstPtr(const uint8_t* buffer, size_t size): _buffer((const char*)buffer), _size(size), _byteAddressable(__byteAddressable(buffer)) { }
|
||||||
|
@@ -19,20 +19,17 @@
|
|||||||
parsing functions based on TextFinder library by Michael Margolis
|
parsing functions based on TextFinder library by Michael Margolis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <StreamDev.h>
|
#include <StreamDev.h>
|
||||||
|
|
||||||
size_t Stream::sendGeneric(Print* to,
|
size_t Stream::sendGeneric(Print* to, const ssize_t len, const int readUntilChar,
|
||||||
const ssize_t len,
|
|
||||||
const int readUntilChar,
|
|
||||||
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||||
{
|
{
|
||||||
setReport(Report::Success);
|
setReport(Report::Success);
|
||||||
|
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
{
|
{
|
||||||
return 0; // conveniently avoids timeout for no requested data
|
return 0; // conveniently avoids timeout for no requested data
|
||||||
}
|
}
|
||||||
|
|
||||||
// There are two timeouts:
|
// There are two timeouts:
|
||||||
@@ -57,14 +54,17 @@ size_t Stream::sendGeneric(Print* to,
|
|||||||
return SendGenericRegular(to, len, timeoutMs);
|
return SendGenericRegular(to, len, timeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar,
|
||||||
|
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||||
{
|
{
|
||||||
// "neverExpires (default, impossible)" is translated to default timeout
|
// "neverExpires (default, impossible)" is translated to default timeout
|
||||||
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() : timeoutMs);
|
esp8266::polledTimeout::oneShotFastMs timedOut(
|
||||||
|
timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
|
||||||
|
: timeoutMs);
|
||||||
// len==-1 => maxLen=0 <=> until starvation
|
// len==-1 => maxLen=0 <=> until starvation
|
||||||
const size_t maxLen = std::max((ssize_t)0, len);
|
const size_t maxLen = std::max((ssize_t)0, len);
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
|
|
||||||
while (!maxLen || written < maxLen)
|
while (!maxLen || written < maxLen)
|
||||||
{
|
{
|
||||||
@@ -90,13 +90,13 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea
|
|||||||
if (w)
|
if (w)
|
||||||
{
|
{
|
||||||
const char* directbuf = peekBuffer();
|
const char* directbuf = peekBuffer();
|
||||||
bool foundChar = false;
|
bool foundChar = false;
|
||||||
if (readUntilChar >= 0)
|
if (readUntilChar >= 0)
|
||||||
{
|
{
|
||||||
const char* last = (const char*)memchr(directbuf, readUntilChar, w);
|
const char* last = (const char*)memchr(directbuf, readUntilChar, w);
|
||||||
if (last)
|
if (last)
|
||||||
{
|
{
|
||||||
w = std::min((size_t)(last - directbuf), w);
|
w = std::min((size_t)(last - directbuf), w);
|
||||||
foundChar = true;
|
foundChar = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea
|
|||||||
{
|
{
|
||||||
peekConsume(w);
|
peekConsume(w);
|
||||||
written += w;
|
written += w;
|
||||||
timedOut.reset(); // something has been written
|
timedOut.reset(); // something has been written
|
||||||
}
|
}
|
||||||
if (foundChar)
|
if (foundChar)
|
||||||
{
|
{
|
||||||
@@ -145,16 +145,20 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea
|
|||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int readUntilChar, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
size_t
|
||||||
|
Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int readUntilChar,
|
||||||
|
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||||
{
|
{
|
||||||
// regular Stream API
|
// regular Stream API
|
||||||
// no other choice than reading byte by byte
|
// no other choice than reading byte by byte
|
||||||
|
|
||||||
// "neverExpires (default, impossible)" is translated to default timeout
|
// "neverExpires (default, impossible)" is translated to default timeout
|
||||||
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() : timeoutMs);
|
esp8266::polledTimeout::oneShotFastMs timedOut(
|
||||||
|
timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
|
||||||
|
: timeoutMs);
|
||||||
// len==-1 => maxLen=0 <=> until starvation
|
// len==-1 => maxLen=0 <=> until starvation
|
||||||
const size_t maxLen = std::max((ssize_t)0, len);
|
const size_t maxLen = std::max((ssize_t)0, len);
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
|
|
||||||
while (!maxLen || written < maxLen)
|
while (!maxLen || written < maxLen)
|
||||||
{
|
{
|
||||||
@@ -186,7 +190,7 @@ size_t Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int r
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
written += 1;
|
written += 1;
|
||||||
timedOut.reset(); // something has been written
|
timedOut.reset(); // something has been written
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timedOut)
|
if (timedOut)
|
||||||
@@ -221,16 +225,19 @@ size_t Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int r
|
|||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
size_t Stream::SendGenericRegular(Print* to, const ssize_t len,
|
||||||
|
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||||
{
|
{
|
||||||
// regular Stream API
|
// regular Stream API
|
||||||
// use an intermediary buffer
|
// use an intermediary buffer
|
||||||
|
|
||||||
// "neverExpires (default, impossible)" is translated to default timeout
|
// "neverExpires (default, impossible)" is translated to default timeout
|
||||||
esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() : timeoutMs);
|
esp8266::polledTimeout::oneShotFastMs timedOut(
|
||||||
|
timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout()
|
||||||
|
: timeoutMs);
|
||||||
// len==-1 => maxLen=0 <=> until starvation
|
// len==-1 => maxLen=0 <=> until starvation
|
||||||
const size_t maxLen = std::max((ssize_t)0, len);
|
const size_t maxLen = std::max((ssize_t)0, len);
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
|
|
||||||
while (!maxLen || written < maxLen)
|
while (!maxLen || written < maxLen)
|
||||||
{
|
{
|
||||||
@@ -243,7 +250,7 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::p
|
|||||||
|
|
||||||
size_t w = to->availableForWrite();
|
size_t w = to->availableForWrite();
|
||||||
if (w == 0 && !to->outputCanTimeout())
|
if (w == 0 && !to->outputCanTimeout())
|
||||||
// no more data can be written, ever
|
// no more data can be written, ever
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -256,7 +263,7 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::p
|
|||||||
w = std::min(w, (decltype(w))temporaryStackBufferSize);
|
w = std::min(w, (decltype(w))temporaryStackBufferSize);
|
||||||
if (w)
|
if (w)
|
||||||
{
|
{
|
||||||
char temp[w];
|
char temp[w];
|
||||||
ssize_t r = read(temp, w);
|
ssize_t r = read(temp, w);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
{
|
{
|
||||||
@@ -270,7 +277,7 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::p
|
|||||||
setReport(Report::WriteError);
|
setReport(Report::WriteError);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
timedOut.reset(); // something has been written
|
timedOut.reset(); // something has been written
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timedOut)
|
if (timedOut)
|
||||||
@@ -305,19 +312,19 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::p
|
|||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream& operator << (Stream& out, String& string)
|
Stream& operator<<(Stream& out, String& string)
|
||||||
{
|
{
|
||||||
StreamConstPtr(string).sendAll(out);
|
StreamConstPtr(string).sendAll(out);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream& operator << (Stream& out, StreamString& stream)
|
Stream& operator<<(Stream& out, StreamString& stream)
|
||||||
{
|
{
|
||||||
stream.sendAll(out);
|
stream.sendAll(out);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream& operator << (Stream& out, Stream& stream)
|
Stream& operator<<(Stream& out, Stream& stream)
|
||||||
{
|
{
|
||||||
if (stream.streamRemaining() < 0)
|
if (stream.streamRemaining() < 0)
|
||||||
{
|
{
|
||||||
@@ -339,13 +346,13 @@ Stream& operator << (Stream& out, Stream& stream)
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream& operator << (Stream& out, const char* text)
|
Stream& operator<<(Stream& out, const char* text)
|
||||||
{
|
{
|
||||||
StreamConstPtr(text).sendAll(out);
|
StreamConstPtr(text, strlen_P(text)).sendAll(out);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream& operator << (Stream& out, const __FlashStringHelper* text)
|
Stream& operator<<(Stream& out, const __FlashStringHelper* text)
|
||||||
{
|
{
|
||||||
StreamConstPtr(text).sendAll(out);
|
StreamConstPtr(text).sendAll(out);
|
||||||
return out;
|
return out;
|
||||||
|
@@ -24,6 +24,8 @@
|
|||||||
#define __STREAMSTRING_H
|
#define __STREAMSTRING_H
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "Stream.h"
|
||||||
#include "WString.h"
|
#include "WString.h"
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////
|
||||||
@@ -33,16 +35,9 @@
|
|||||||
class S2Stream: public Stream
|
class S2Stream: public Stream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
S2Stream(String& string, int peekPointer = -1) : string(&string), peekPointer(peekPointer) { }
|
||||||
|
|
||||||
S2Stream(String& string, int peekPointer = -1):
|
S2Stream(String* string, int peekPointer = -1) : string(string), peekPointer(peekPointer) { }
|
||||||
string(&string), peekPointer(peekPointer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
S2Stream(String* string, int peekPointer = -1):
|
|
||||||
string(string), peekPointer(peekPointer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int available() override
|
virtual int available() override
|
||||||
{
|
{
|
||||||
@@ -205,18 +200,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
String* string;
|
String* string;
|
||||||
int peekPointer; // -1:String is consumed / >=0:resettable pointer
|
int peekPointer; // -1:String is consumed / >=0:resettable pointer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// StreamString is a S2Stream holding the String
|
// StreamString is a S2Stream holding the String
|
||||||
|
|
||||||
class StreamString: public String, public S2Stream
|
class StreamString: public String, public S2Stream
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void resetpp()
|
void resetpp()
|
||||||
{
|
{
|
||||||
if (peekPointer > 0)
|
if (peekPointer > 0)
|
||||||
@@ -226,55 +218,68 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
StreamString(StreamString&& bro) : String(bro), S2Stream(this) { }
|
||||||
|
StreamString(const StreamString& bro) : String(bro), S2Stream(this) { }
|
||||||
|
|
||||||
StreamString(StreamString&& bro): String(bro), S2Stream(this) { }
|
// duplicate String constructors and operator=:
|
||||||
StreamString(const StreamString& bro): String(bro), S2Stream(this) { }
|
|
||||||
|
|
||||||
// duplicate String contructors and operator=:
|
StreamString(const char* text = nullptr) : String(text), S2Stream(this) { }
|
||||||
|
StreamString(const String& string) : String(string), S2Stream(this) { }
|
||||||
|
StreamString(const __FlashStringHelper* str) : String(str), S2Stream(this) { }
|
||||||
|
StreamString(String&& string) : String(string), S2Stream(this) { }
|
||||||
|
|
||||||
StreamString(const char* text = nullptr): String(text), S2Stream(this) { }
|
explicit StreamString(char c) : String(c), S2Stream(this) { }
|
||||||
StreamString(const String& string): String(string), S2Stream(this) { }
|
explicit StreamString(unsigned char c, unsigned char base = 10) :
|
||||||
StreamString(const __FlashStringHelper *str): String(str), S2Stream(this) { }
|
String(c, base), S2Stream(this)
|
||||||
StreamString(String&& string): String(string), S2Stream(this) { }
|
{
|
||||||
|
}
|
||||||
|
explicit StreamString(int i, unsigned char base = 10) : String(i, base), S2Stream(this) { }
|
||||||
|
explicit StreamString(unsigned int i, unsigned char base = 10) : String(i, base), S2Stream(this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit StreamString(long l, unsigned char base = 10) : String(l, base), S2Stream(this) { }
|
||||||
|
explicit StreamString(unsigned long l, unsigned char base = 10) :
|
||||||
|
String(l, base), S2Stream(this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit StreamString(float f, unsigned char decimalPlaces = 2) :
|
||||||
|
String(f, decimalPlaces), S2Stream(this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit StreamString(double d, unsigned char decimalPlaces = 2) :
|
||||||
|
String(d, decimalPlaces), S2Stream(this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
explicit StreamString(char c): String(c), S2Stream(this) { }
|
StreamString& operator=(const StreamString& rhs)
|
||||||
explicit StreamString(unsigned char c, unsigned char base = 10): String(c, base), S2Stream(this) { }
|
|
||||||
explicit StreamString(int i, unsigned char base = 10): String(i, base), S2Stream(this) { }
|
|
||||||
explicit StreamString(unsigned int i, unsigned char base = 10): String(i, base), S2Stream(this) { }
|
|
||||||
explicit StreamString(long l, unsigned char base = 10): String(l, base), S2Stream(this) { }
|
|
||||||
explicit StreamString(unsigned long l, unsigned char base = 10): String(l, base), S2Stream(this) { }
|
|
||||||
explicit StreamString(float f, unsigned char decimalPlaces = 2): String(f, decimalPlaces), S2Stream(this) { }
|
|
||||||
explicit StreamString(double d, unsigned char decimalPlaces = 2): String(d, decimalPlaces), S2Stream(this) { }
|
|
||||||
|
|
||||||
StreamString& operator= (const StreamString& rhs)
|
|
||||||
{
|
{
|
||||||
String::operator=(rhs);
|
String::operator=(rhs);
|
||||||
resetpp();
|
resetpp();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamString& operator= (const String& rhs)
|
StreamString& operator=(const String& rhs)
|
||||||
{
|
{
|
||||||
String::operator=(rhs);
|
String::operator=(rhs);
|
||||||
resetpp();
|
resetpp();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamString& operator= (const char* cstr)
|
StreamString& operator=(const char* cstr)
|
||||||
{
|
{
|
||||||
String::operator=(cstr);
|
String::operator=(cstr);
|
||||||
resetpp();
|
resetpp();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamString& operator= (const __FlashStringHelper* str)
|
StreamString& operator=(const __FlashStringHelper* str)
|
||||||
{
|
{
|
||||||
String::operator=(str);
|
String::operator=(str);
|
||||||
resetpp();
|
resetpp();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamString& operator= (String&& rval)
|
StreamString& operator=(String&& rval)
|
||||||
{
|
{
|
||||||
String::operator=(rval);
|
String::operator=(rval);
|
||||||
resetpp();
|
resetpp();
|
||||||
@@ -282,4 +287,4 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __STREAMSTRING_H
|
#endif // __STREAMSTRING_H
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
// autogenerated from https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv
|
// autogenerated from https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv
|
||||||
// by script <esp8266 arduino core>/tools/TZupdate.sh
|
// by script <esp8266 arduino core>/tools/TZupdate.sh
|
||||||
// Thu Nov 12 04:07:03 UTC 2020
|
// Mon Jul 26 20:04:37 UTC 2021
|
||||||
//
|
//
|
||||||
// This database is autogenerated from IANA timezone database
|
// This database is autogenerated from IANA timezone database
|
||||||
// https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv
|
// https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv
|
||||||
@@ -172,6 +172,7 @@
|
|||||||
#define TZ_America_North_Dakota_Beulah PSTR("CST6CDT,M3.2.0,M11.1.0")
|
#define TZ_America_North_Dakota_Beulah PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
#define TZ_America_North_Dakota_Center PSTR("CST6CDT,M3.2.0,M11.1.0")
|
#define TZ_America_North_Dakota_Center PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
#define TZ_America_North_Dakota_New_Salem PSTR("CST6CDT,M3.2.0,M11.1.0")
|
#define TZ_America_North_Dakota_New_Salem PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Nuuk PSTR("<-03>3<-02>,M3.5.0/-2,M10.5.0/-1")
|
||||||
#define TZ_America_Ojinaga PSTR("MST7MDT,M3.2.0,M11.1.0")
|
#define TZ_America_Ojinaga PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||||
#define TZ_America_Panama PSTR("EST5")
|
#define TZ_America_Panama PSTR("EST5")
|
||||||
#define TZ_America_Pangnirtung PSTR("EST5EDT,M3.2.0,M11.1.0")
|
#define TZ_America_Pangnirtung PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
@@ -383,7 +384,7 @@
|
|||||||
#define TZ_Europe_Vatican PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
#define TZ_Europe_Vatican PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
#define TZ_Europe_Vienna PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
#define TZ_Europe_Vienna PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
#define TZ_Europe_Vilnius PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
#define TZ_Europe_Vilnius PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
#define TZ_Europe_Volgograd PSTR("<+04>-4")
|
#define TZ_Europe_Volgograd PSTR("<+03>-3")
|
||||||
#define TZ_Europe_Warsaw PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
#define TZ_Europe_Warsaw PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
#define TZ_Europe_Zagreb PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
#define TZ_Europe_Zagreb PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
#define TZ_Europe_Zaporozhye PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
#define TZ_Europe_Zaporozhye PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
@@ -24,8 +24,7 @@ extern "C" {
|
|||||||
#include "user_interface.h"
|
#include "user_interface.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" uint32_t _FS_start;
|
#include <flash_hal.h> // not "flash_hal.h": can use hijacked MOCK version
|
||||||
extern "C" uint32_t _FS_end;
|
|
||||||
|
|
||||||
UpdaterClass::UpdaterClass()
|
UpdaterClass::UpdaterClass()
|
||||||
{
|
{
|
||||||
@@ -118,7 +117,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
|||||||
|
|
||||||
if (command == U_FLASH) {
|
if (command == U_FLASH) {
|
||||||
//address of the end of the space available for sketch and update
|
//address of the end of the space available for sketch and update
|
||||||
uintptr_t updateEndAddress = (uintptr_t)&_FS_start - 0x40200000;
|
uintptr_t updateEndAddress = FS_start - 0x40200000;
|
||||||
|
|
||||||
updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0;
|
updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0;
|
||||||
|
|
||||||
@@ -135,14 +134,14 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (command == U_FS) {
|
else if (command == U_FS) {
|
||||||
if((uintptr_t)&_FS_start + roundedSize > (uintptr_t)&_FS_end) {
|
if(FS_start + roundedSize > FS_end) {
|
||||||
_setError(UPDATE_ERROR_SPACE);
|
_setError(UPDATE_ERROR_SPACE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ATOMIC_FS_UPDATE
|
#ifdef ATOMIC_FS_UPDATE
|
||||||
//address of the end of the space available for update
|
//address of the end of the space available for update
|
||||||
uintptr_t updateEndAddress = (uintptr_t)&_FS_start - 0x40200000;
|
uintptr_t updateEndAddress = FS_start - 0x40200000;
|
||||||
|
|
||||||
updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0;
|
updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0;
|
||||||
|
|
||||||
@@ -151,7 +150,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
updateStartAddress = (uintptr_t)&_FS_start - 0x40200000;
|
updateStartAddress = FS_start - 0x40200000;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -224,19 +223,29 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
|||||||
_size = progress();
|
_size = progress();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t sigLen = 0;
|
|
||||||
if (_verify) {
|
if (_verify) {
|
||||||
ESP.flashRead(_startAddress + _size - sizeof(uint32_t), &sigLen, sizeof(uint32_t));
|
const uint32_t expectedSigLen = _verify->length();
|
||||||
|
// If expectedSigLen is non-zero, we expect the last four bytes of the buffer to
|
||||||
|
// contain a matching length field, preceded by the bytes of the signature itself.
|
||||||
|
// But if expectedSigLen is zero, we expect neither a signature nor a length field;
|
||||||
|
uint32_t sigLen = 0;
|
||||||
|
|
||||||
|
if (expectedSigLen > 0) {
|
||||||
|
ESP.flashRead(_startAddress + _size - sizeof(uint32_t), &sigLen, sizeof(uint32_t));
|
||||||
|
}
|
||||||
#ifdef DEBUG_UPDATER
|
#ifdef DEBUG_UPDATER
|
||||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen: %d\n"), sigLen);
|
DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen: %d\n"), sigLen);
|
||||||
#endif
|
#endif
|
||||||
if (sigLen != _verify->length()) {
|
if (sigLen != expectedSigLen) {
|
||||||
_setError(UPDATE_ERROR_SIGN);
|
_setError(UPDATE_ERROR_SIGN);
|
||||||
_reset();
|
_reset();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int binSize = _size - sigLen - sizeof(uint32_t) /* The siglen word */;
|
int binSize = _size;
|
||||||
|
if (expectedSigLen > 0) {
|
||||||
|
_size -= (sigLen + sizeof(uint32_t) /* The siglen word */);
|
||||||
|
}
|
||||||
_hash->begin();
|
_hash->begin();
|
||||||
#ifdef DEBUG_UPDATER
|
#ifdef DEBUG_UPDATER
|
||||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted binsize: %d\n"), binSize);
|
DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted binsize: %d\n"), binSize);
|
||||||
@@ -255,20 +264,24 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
|||||||
for (int i=0; i<_hash->len(); i++) DEBUG_UPDATER.printf(" %02x", ret[i]);
|
for (int i=0; i<_hash->len(); i++) DEBUG_UPDATER.printf(" %02x", ret[i]);
|
||||||
DEBUG_UPDATER.printf("\n");
|
DEBUG_UPDATER.printf("\n");
|
||||||
#endif
|
#endif
|
||||||
uint8_t *sig = (uint8_t*)malloc(sigLen);
|
|
||||||
if (!sig) {
|
uint8_t *sig = nullptr; // Safe to free if we don't actually malloc
|
||||||
_setError(UPDATE_ERROR_SIGN);
|
if (expectedSigLen > 0) {
|
||||||
_reset();
|
sig = (uint8_t*)malloc(sigLen);
|
||||||
return false;
|
if (!sig) {
|
||||||
}
|
_setError(UPDATE_ERROR_SIGN);
|
||||||
ESP.flashRead(_startAddress + binSize, sig, sigLen);
|
_reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP.flashRead(_startAddress + binSize, sig, sigLen);
|
||||||
#ifdef DEBUG_UPDATER
|
#ifdef DEBUG_UPDATER
|
||||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] Received Signature:"));
|
DEBUG_UPDATER.printf_P(PSTR("[Updater] Received Signature:"));
|
||||||
for (size_t i=0; i<sigLen; i++) {
|
for (size_t i=0; i<sigLen; i++) {
|
||||||
DEBUG_UPDATER.printf(" %02x", sig[i]);
|
DEBUG_UPDATER.printf(" %02x", sig[i]);
|
||||||
}
|
}
|
||||||
DEBUG_UPDATER.printf("\n");
|
DEBUG_UPDATER.printf("\n");
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
if (!_verify->verify(_hash, (void *)sig, sigLen)) {
|
if (!_verify->verify(_hash, (void *)sig, sigLen)) {
|
||||||
free(sig);
|
free(sig);
|
||||||
_setError(UPDATE_ERROR_SIGN);
|
_setError(UPDATE_ERROR_SIGN);
|
||||||
@@ -314,7 +327,7 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
|||||||
eboot_command ebcmd;
|
eboot_command ebcmd;
|
||||||
ebcmd.action = ACTION_COPY_RAW;
|
ebcmd.action = ACTION_COPY_RAW;
|
||||||
ebcmd.args[0] = _startAddress;
|
ebcmd.args[0] = _startAddress;
|
||||||
ebcmd.args[1] = (uintptr_t)&_FS_start - 0x40200000;
|
ebcmd.args[1] = FS_start - 0x40200000;
|
||||||
ebcmd.args[2] = _size;
|
ebcmd.args[2] = _size;
|
||||||
eboot_command_write(&ebcmd);
|
eboot_command_write(&ebcmd);
|
||||||
#endif
|
#endif
|
||||||
@@ -410,7 +423,7 @@ size_t UpdaterClass::write(uint8_t *data, size_t len) {
|
|||||||
left -= toBuff;
|
left -= toBuff;
|
||||||
if(!_async) yield();
|
if(!_async) yield();
|
||||||
}
|
}
|
||||||
//lets see whats left
|
//lets see what's left
|
||||||
memcpy(_buffer + _bufferLen, data + (len - left), left);
|
memcpy(_buffer + _bufferLen, data + (len - left), left);
|
||||||
_bufferLen += left;
|
_bufferLen += left;
|
||||||
if(_bufferLen == remaining()){
|
if(_bufferLen == remaining()){
|
||||||
@@ -460,6 +473,9 @@ bool UpdaterClass::_verifyEnd() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// it makes no sense to check flash size in auto flash mode
|
||||||
|
// (sketch size would have to be set in bin header, instead of flash size)
|
||||||
|
#if !FLASH_MAP_SUPPORT
|
||||||
uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4);
|
uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4);
|
||||||
|
|
||||||
// check if new bin fits to SPI flash
|
// check if new bin fits to SPI flash
|
||||||
@@ -468,6 +484,7 @@ bool UpdaterClass::_verifyEnd() {
|
|||||||
_setError(UPDATE_ERROR_NEW_FLASH_CONFIG);
|
_setError(UPDATE_ERROR_NEW_FLASH_CONFIG);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if(_command == U_FS) {
|
} else if(_command == U_FS) {
|
||||||
|
@@ -108,12 +108,12 @@ class UpdaterClass {
|
|||||||
bool setMD5(const char * expected_md5);
|
bool setMD5(const char * expected_md5);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
returns the MD5 String of the sucessfully ended firmware
|
returns the MD5 String of the successfully ended firmware
|
||||||
*/
|
*/
|
||||||
String md5String(void){ return _md5.toString(); }
|
String md5String(void){ return _md5.toString(); }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
populated the result with the md5 bytes of the sucessfully ended firmware
|
populated the result with the md5 bytes of the successfully ended firmware
|
||||||
*/
|
*/
|
||||||
void md5(uint8_t * result){ return _md5.getBytes(result); }
|
void md5(uint8_t * result){ return _md5.getBytes(result); }
|
||||||
|
|
||||||
|
@@ -23,6 +23,8 @@
|
|||||||
$Id$
|
$Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
}
|
}
|
||||||
@@ -77,10 +79,10 @@ long map(long x, long in_min, long in_max, long out_min, long out_max) {
|
|||||||
return (delta * dividend + (divisor / 2)) / divisor + out_min;
|
return (delta * dividend + (divisor / 2)) / divisor + out_min;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int makeWord(unsigned int w) {
|
uint16_t makeWord(uint16_t w) {
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int makeWord(unsigned char h, unsigned char l) {
|
uint16_t makeWord(byte h, byte l) {
|
||||||
return (h << 8) | l;
|
return (h << 8) | l;
|
||||||
}
|
}
|
||||||
|
@@ -25,12 +25,96 @@
|
|||||||
#include "WString.h"
|
#include "WString.h"
|
||||||
#include "stdlib_noniso.h"
|
#include "stdlib_noniso.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#define OOM_STRING_BORDER_DISPLAY 10
|
#define OOM_STRING_BORDER_DISPLAY 10
|
||||||
#define OOM_STRING_THRESHOLD_REALLOC_WARN 128
|
#define OOM_STRING_THRESHOLD_REALLOC_WARN 128
|
||||||
|
|
||||||
#define __STRHELPER(x) #x
|
#define __STRHELPER(x) #x
|
||||||
#define STR(x) __STRHELPER(x) // stringifier
|
#define STR(x) __STRHELPER(x) // stringifier
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* Conversion helpers */
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
static String toString(unsigned char value, unsigned char base) {
|
||||||
|
String out;
|
||||||
|
|
||||||
|
char buf[1 + std::numeric_limits<unsigned char>::digits];
|
||||||
|
out = utoa(value, buf, base);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toString(int value, unsigned char base) {
|
||||||
|
String out;
|
||||||
|
|
||||||
|
char buf[2 + std::numeric_limits<int>::digits];
|
||||||
|
out = itoa(value, buf, base);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toString(unsigned int value, unsigned char base) {
|
||||||
|
String out;
|
||||||
|
|
||||||
|
char buf[1 + std::numeric_limits<unsigned int>::digits];
|
||||||
|
out = utoa(value, buf, base);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toString(long value, unsigned char base) {
|
||||||
|
String out;
|
||||||
|
|
||||||
|
char buf[2 + std::numeric_limits<long>::digits];
|
||||||
|
out = ltoa(value, buf, base);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toString(unsigned long value, unsigned char base) {
|
||||||
|
String out;
|
||||||
|
|
||||||
|
char buf[1 + std::numeric_limits<unsigned long>::digits];
|
||||||
|
out = ultoa(value, buf, base);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: {u,}lltoa don't guarantee that the buffer is usable directly, one should always use the returned pointer
|
||||||
|
|
||||||
|
static String toString(long long value, unsigned char base) {
|
||||||
|
String out;
|
||||||
|
|
||||||
|
char buf[2 + std::numeric_limits<long long>::digits];
|
||||||
|
out = lltoa(value, buf, sizeof(buf), base);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toString(unsigned long long value, unsigned char base) {
|
||||||
|
String out;
|
||||||
|
|
||||||
|
char buf[1 + std::numeric_limits<unsigned long long>::digits];
|
||||||
|
out = ulltoa(value, buf, sizeof(buf), base);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toString(double value, unsigned char decimalPlaces) {
|
||||||
|
String out;
|
||||||
|
|
||||||
|
char buf[33];
|
||||||
|
out = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toString(float value, unsigned char decimalPlaces) {
|
||||||
|
return toString(static_cast<double>(value), decimalPlaces);
|
||||||
|
}
|
||||||
|
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
/* Constructors */
|
/* Constructors */
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
@@ -56,86 +140,41 @@ String::String(String &&rval) noexcept {
|
|||||||
move(rval);
|
move(rval);
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(unsigned char value, unsigned char base) {
|
String::String(unsigned char value, unsigned char base) :
|
||||||
init();
|
String(toString(value, base))
|
||||||
char buf[1 + 8 * sizeof(unsigned char)];
|
{}
|
||||||
utoa(value, buf, base);
|
|
||||||
*this = buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(int value, unsigned char base) {
|
String::String(int value, unsigned char base) :
|
||||||
init();
|
String(toString(value, base))
|
||||||
char buf[2 + 8 * sizeof(int)];
|
{}
|
||||||
if (base == 10) {
|
|
||||||
sprintf(buf, "%d", value);
|
|
||||||
} else {
|
|
||||||
itoa(value, buf, base);
|
|
||||||
}
|
|
||||||
*this = buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(unsigned int value, unsigned char base) {
|
String::String(unsigned int value, unsigned char base) :
|
||||||
init();
|
String(toString(value, base))
|
||||||
char buf[1 + 8 * sizeof(unsigned int)];
|
{}
|
||||||
utoa(value, buf, base);
|
|
||||||
*this = buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(long value, unsigned char base) {
|
String::String(long value, unsigned char base) :
|
||||||
init();
|
String(toString(value, base))
|
||||||
char buf[2 + 8 * sizeof(long)];
|
{}
|
||||||
if (base == 10) {
|
|
||||||
sprintf(buf, "%ld", value);
|
|
||||||
} else {
|
|
||||||
ltoa(value, buf, base);
|
|
||||||
}
|
|
||||||
*this = buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(unsigned long value, unsigned char base) {
|
String::String(unsigned long value, unsigned char base) :
|
||||||
init();
|
String(toString(value, base))
|
||||||
char buf[1 + 8 * sizeof(unsigned long)];
|
{}
|
||||||
ultoa(value, buf, base);
|
|
||||||
*this = buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(long long value) {
|
String::String(long long value, unsigned char base) :
|
||||||
init();
|
String(toString(value, base))
|
||||||
char buf[2 + 8 * sizeof(long long)];
|
{}
|
||||||
sprintf(buf, "%lld", value);
|
|
||||||
*this = buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(unsigned long long value) {
|
String::String(unsigned long long value, unsigned char base) :
|
||||||
init();
|
String(toString(value, base))
|
||||||
char buf[1 + 8 * sizeof(unsigned long long)];
|
{}
|
||||||
sprintf(buf, "%llu", value);
|
|
||||||
*this = buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(long long value, unsigned char base) {
|
String::String(float value, unsigned char decimalPlaces) :
|
||||||
init();
|
String(toString(value, decimalPlaces))
|
||||||
char buf[2 + 8 * sizeof(long long)];
|
{}
|
||||||
*this = lltoa(value, buf, sizeof(buf), base);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(unsigned long long value, unsigned char base) {
|
String::String(double value, unsigned char decimalPlaces) :
|
||||||
init();
|
String(toString(value, decimalPlaces))
|
||||||
char buf[1 + 8 * sizeof(unsigned long long)];
|
{}
|
||||||
*this = ulltoa(value, buf, sizeof(buf), base);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(float value, unsigned char decimalPlaces) {
|
|
||||||
init();
|
|
||||||
char buf[33];
|
|
||||||
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(double value, unsigned char decimalPlaces) {
|
|
||||||
init();
|
|
||||||
char buf[33];
|
|
||||||
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
/* Memory Management */
|
/* Memory Management */
|
||||||
@@ -147,18 +186,18 @@ void String::invalidate(void) {
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::reserve(unsigned int size) {
|
bool String::reserve(unsigned int size) {
|
||||||
if (buffer() && capacity() >= size)
|
if (buffer() && capacity() >= size)
|
||||||
return 1;
|
return true;
|
||||||
if (changeBuffer(size)) {
|
if (changeBuffer(size)) {
|
||||||
if (len() == 0)
|
if (len() == 0)
|
||||||
wbuffer()[0] = 0;
|
wbuffer()[0] = 0;
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::changeBuffer(unsigned int maxStrLen) {
|
bool String::changeBuffer(unsigned int maxStrLen) {
|
||||||
// Can we use SSO here to avoid allocation?
|
// Can we use SSO here to avoid allocation?
|
||||||
if (maxStrLen < sizeof(sso.buff) - 1) {
|
if (maxStrLen < sizeof(sso.buff) - 1) {
|
||||||
if (isSSO() || !buffer()) {
|
if (isSSO() || !buffer()) {
|
||||||
@@ -175,21 +214,21 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
|
|||||||
memcpy(wbuffer(), temp, maxStrLen);
|
memcpy(wbuffer(), temp, maxStrLen);
|
||||||
free((void *)temp);
|
free((void *)temp);
|
||||||
}
|
}
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
// Fallthrough to normal allocator
|
// Fallthrough to normal allocator
|
||||||
size_t newSize = (maxStrLen + 16) & (~0xf);
|
size_t newSize = (maxStrLen + 16) & (~0xf);
|
||||||
#ifdef DEBUG_ESP_OOM
|
#ifdef DEBUG_ESP_OOM
|
||||||
if (!isSSO() && capacity() >= OOM_STRING_THRESHOLD_REALLOC_WARN && maxStrLen > capacity()) {
|
if (!isSSO() && capacity() >= OOM_STRING_THRESHOLD_REALLOC_WARN && maxStrLen > capacity()) {
|
||||||
// warn when badly re-allocating
|
// warn when badly re-allocating
|
||||||
DEBUGV("[offending String op %d->%d ('%." STR(OOM_STRING_BORDER_DISPLAY) "s ... %." STR(OOM_STRING_BORDER_DISPLAY) "s')]\n",
|
DEBUGV("[String] Reallocating large String(%d -> %d bytes) '%." STR(OOM_STRING_BORDER_DISPLAY) "s ... %." STR(OOM_STRING_BORDER_DISPLAY) "s'\n",
|
||||||
len(), maxStrLen, c_str(),
|
len(), maxStrLen, c_str(),
|
||||||
len() > OOM_STRING_BORDER_DISPLAY? c_str() + std::max((int)len() - OOM_STRING_BORDER_DISPLAY, OOM_STRING_BORDER_DISPLAY): "");
|
len() > OOM_STRING_BORDER_DISPLAY? c_str() + std::max((int)len() - OOM_STRING_BORDER_DISPLAY, OOM_STRING_BORDER_DISPLAY): "");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Make sure we can fit newsize in the buffer
|
// Make sure we can fit newsize in the buffer
|
||||||
if (newSize > CAPACITY_MAX) {
|
if (newSize > CAPACITY_MAX) {
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
uint16_t oldLen = len();
|
uint16_t oldLen = len();
|
||||||
char *newbuffer = (char *)realloc(isSSO() ? nullptr : wbuffer(), newSize);
|
char *newbuffer = (char *)realloc(isSSO() ? nullptr : wbuffer(), newSize);
|
||||||
@@ -206,9 +245,9 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
|
|||||||
setCapacity(newSize - 1);
|
setCapacity(newSize - 1);
|
||||||
setLen(oldLen); // Needed in case of SSO where len() never existed
|
setLen(oldLen); // Needed in case of SSO where len() never existed
|
||||||
setBuffer(newbuffer);
|
setBuffer(newbuffer);
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
@@ -273,115 +312,108 @@ String &String::operator =(const __FlashStringHelper *pstr) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String &String::operator =(char c) {
|
||||||
|
char buffer[2] { c, '\0' };
|
||||||
|
*this = buffer;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
/* concat */
|
/* concat */
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
|
|
||||||
unsigned char String::concat(const String &s) {
|
bool String::concat(const String &s) {
|
||||||
// Special case if we're concatting ourself (s += s;) since we may end up
|
// Special case if we're concatting ourself (s += s;) since we may end up
|
||||||
// realloc'ing the buffer and moving s.buffer in the method called
|
// realloc'ing the buffer and moving s.buffer in the method called
|
||||||
if (&s == this) {
|
if (&s == this) {
|
||||||
unsigned int newlen = 2 * len();
|
unsigned int newlen = 2 * len();
|
||||||
if (!s.buffer())
|
if (!s.buffer())
|
||||||
return 0;
|
return false;
|
||||||
if (s.len() == 0)
|
if (s.len() == 0)
|
||||||
return 1;
|
return true;
|
||||||
if (!reserve(newlen))
|
if (!reserve(newlen))
|
||||||
return 0;
|
return false;
|
||||||
memmove_P(wbuffer() + len(), buffer(), len());
|
memmove_P(wbuffer() + len(), buffer(), len());
|
||||||
setLen(newlen);
|
setLen(newlen);
|
||||||
wbuffer()[newlen] = 0;
|
wbuffer()[newlen] = 0;
|
||||||
return 1;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return concat(s.buffer(), s.len());
|
return concat(s.buffer(), s.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(const char *cstr, unsigned int length) {
|
bool String::concat(const char *cstr, unsigned int length) {
|
||||||
unsigned int newlen = len() + length;
|
unsigned int newlen = len() + length;
|
||||||
if (!cstr)
|
if (!cstr)
|
||||||
return 0;
|
return false;
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
return 1;
|
return true;
|
||||||
if (!reserve(newlen))
|
if (!reserve(newlen))
|
||||||
return 0;
|
return false;
|
||||||
memmove_P(wbuffer() + len(), cstr, length + 1);
|
memmove_P(wbuffer() + len(), cstr, length);
|
||||||
setLen(newlen);
|
setLen(newlen);
|
||||||
wbuffer()[newlen] = 0;
|
wbuffer()[newlen] = 0;
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(const char *cstr) {
|
bool String::concat(const char *cstr) {
|
||||||
if (!cstr)
|
if (!cstr)
|
||||||
return 0;
|
return false;
|
||||||
return concat(cstr, strlen(cstr));
|
return concat(cstr, strlen(cstr));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(char c) {
|
bool String::concat(char c) {
|
||||||
return concat(&c, 1);
|
return concat(&c, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(unsigned char num) {
|
bool String::concat(unsigned char num) {
|
||||||
char buf[1 + 3 * sizeof(unsigned char)];
|
return concat(String(num));
|
||||||
return concat(buf, sprintf(buf, "%d", num));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(int num) {
|
bool String::concat(int num) {
|
||||||
char buf[2 + 3 * sizeof(int)];
|
return concat(String(num));
|
||||||
return concat(buf, sprintf(buf, "%d", num));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(unsigned int num) {
|
bool String::concat(unsigned int num) {
|
||||||
char buf[1 + 3 * sizeof(unsigned int)];
|
return concat(String(num));
|
||||||
utoa(num, buf, 10);
|
|
||||||
return concat(buf, strlen(buf));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(long num) {
|
bool String::concat(long num) {
|
||||||
char buf[2 + 3 * sizeof(long)];
|
return concat(String(num));
|
||||||
return concat(buf, sprintf(buf, "%ld", num));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(unsigned long num) {
|
bool String::concat(unsigned long num) {
|
||||||
char buf[1 + 3 * sizeof(unsigned long)];
|
return concat(String(num));
|
||||||
ultoa(num, buf, 10);
|
|
||||||
return concat(buf, strlen(buf));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(long long num) {
|
bool String::concat(long long num) {
|
||||||
char buf[2 + 3 * sizeof(long long)];
|
return concat(String(num));
|
||||||
return concat(buf, sprintf(buf, "%lld", num));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(unsigned long long num) {
|
bool String::concat(unsigned long long num) {
|
||||||
char buf[1 + 3 * sizeof(unsigned long long)];
|
return concat(String(num));
|
||||||
return concat(buf, sprintf(buf, "%llu", num));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(float num) {
|
bool String::concat(float num) {
|
||||||
char buf[20];
|
return concat(String(num));
|
||||||
char *string = dtostrf(num, 4, 2, buf);
|
|
||||||
return concat(string, strlen(string));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(double num) {
|
bool String::concat(double num) {
|
||||||
char buf[20];
|
return concat(String(num));
|
||||||
char *string = dtostrf(num, 4, 2, buf);
|
|
||||||
return concat(string, strlen(string));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::concat(const __FlashStringHelper *str) {
|
bool String::concat(const __FlashStringHelper *str) {
|
||||||
if (!str)
|
if (!str)
|
||||||
return 0;
|
return false;
|
||||||
int length = strlen_P((PGM_P)str);
|
int length = strlen_P((PGM_P)str);
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
return 1;
|
return true;
|
||||||
unsigned int newlen = len() + length;
|
unsigned int newlen = len() + length;
|
||||||
if (!reserve(newlen))
|
if (!reserve(newlen))
|
||||||
return 0;
|
return false;
|
||||||
memcpy_P(wbuffer() + len(), (PGM_P)str, length + 1);
|
memcpy_P(wbuffer() + len(), (PGM_P)str, length + 1);
|
||||||
setLen(newlen);
|
setLen(newlen);
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
@@ -488,11 +520,11 @@ int String::compareTo(const String &s) const {
|
|||||||
return strcmp(buffer(), s.buffer());
|
return strcmp(buffer(), s.buffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::equals(const String &s2) const {
|
bool String::equals(const String &s2) const {
|
||||||
return (len() == s2.len() && compareTo(s2) == 0);
|
return (len() == s2.len() && compareTo(s2) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::equals(const char *cstr) const {
|
bool String::equals(const char *cstr) const {
|
||||||
if (len() == 0)
|
if (len() == 0)
|
||||||
return (cstr == NULL || *cstr == 0);
|
return (cstr == NULL || *cstr == 0);
|
||||||
if (cstr == NULL)
|
if (cstr == NULL)
|
||||||
@@ -500,36 +532,44 @@ unsigned char String::equals(const char *cstr) const {
|
|||||||
return strcmp(buffer(), cstr) == 0;
|
return strcmp(buffer(), cstr) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::operator<(const String &rhs) const {
|
bool String::equals(const __FlashStringHelper *s) const {
|
||||||
|
return equals(String(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::operator<(const String &rhs) const {
|
||||||
return compareTo(rhs) < 0;
|
return compareTo(rhs) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::operator>(const String &rhs) const {
|
bool String::operator>(const String &rhs) const {
|
||||||
return compareTo(rhs) > 0;
|
return compareTo(rhs) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::operator<=(const String &rhs) const {
|
bool String::operator<=(const String &rhs) const {
|
||||||
return compareTo(rhs) <= 0;
|
return compareTo(rhs) <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::operator>=(const String &rhs) const {
|
bool String::operator>=(const String &rhs) const {
|
||||||
return compareTo(rhs) >= 0;
|
return compareTo(rhs) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::equalsIgnoreCase(const String &s2) const {
|
bool String::equalsIgnoreCase(const String &s2) const {
|
||||||
if (this == &s2)
|
if (this == &s2)
|
||||||
return 1;
|
return true;
|
||||||
if (len() != s2.len())
|
if (len() != s2.len())
|
||||||
return 0;
|
return false;
|
||||||
if (len() == 0)
|
if (len() == 0)
|
||||||
return 1;
|
return true;
|
||||||
const char *p1 = buffer();
|
const char *p1 = buffer();
|
||||||
const char *p2 = s2.buffer();
|
const char *p2 = s2.buffer();
|
||||||
while (*p1) {
|
while (*p1) {
|
||||||
if (tolower(*p1++) != tolower(*p2++))
|
if (tolower(*p1++) != tolower(*p2++))
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
return 1;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::equalsIgnoreCase(const __FlashStringHelper *s) const {
|
||||||
|
return equalsIgnoreCase(String(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::equalsConstantTime(const String &s2) const {
|
unsigned char String::equalsConstantTime(const String &s2) const {
|
||||||
@@ -559,24 +599,43 @@ unsigned char String::equalsConstantTime(const String &s2) const {
|
|||||||
return (equalcond & diffcond); //bitwise AND
|
return (equalcond & diffcond); //bitwise AND
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::startsWith(const String &s2) const {
|
bool String::startsWith(const String &s2) const {
|
||||||
if (len() < s2.len())
|
if (len() < s2.len())
|
||||||
return 0;
|
return false;
|
||||||
return startsWith(s2, 0);
|
return startsWith(s2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::startsWith(const String &s2, unsigned int offset) const {
|
bool String::startsWith(const char *prefix) const {
|
||||||
|
return this->startsWith(String(prefix));
|
||||||
|
}
|
||||||
|
bool String::startsWith(const __FlashStringHelper *prefix) const {
|
||||||
|
return this->startsWith(String(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::startsWith(const String &s2, unsigned int offset) const {
|
||||||
if (offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer())
|
if (offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer())
|
||||||
return 0;
|
return false;
|
||||||
return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0;
|
return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char String::endsWith(const String &s2) const {
|
bool String::startsWith(const __FlashStringHelper *prefix, unsigned int offset) const {
|
||||||
|
return startsWith(String(prefix), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::endsWith(const String &s2) const {
|
||||||
if (len() < s2.len() || !buffer() || !s2.buffer())
|
if (len() < s2.len() || !buffer() || !s2.buffer())
|
||||||
return 0;
|
return false;
|
||||||
return strcmp(&buffer()[len() - s2.len()], s2.buffer()) == 0;
|
return strcmp(&buffer()[len() - s2.len()], s2.buffer()) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool String::endsWith(const char *suffix) const {
|
||||||
|
return this->endsWith(String(suffix));
|
||||||
|
}
|
||||||
|
bool String::endsWith(const __FlashStringHelper *suffix) const {
|
||||||
|
return this->endsWith(String(suffix));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
/* Character Access */
|
/* Character Access */
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
@@ -597,7 +656,7 @@ char &String::operator[](unsigned int index) {
|
|||||||
|
|
||||||
char String::operator[](unsigned int index) const {
|
char String::operator[](unsigned int index) const {
|
||||||
if (index >= len() || !buffer())
|
if (index >= len() || !buffer())
|
||||||
return 0;
|
return '\0';
|
||||||
return buffer()[index];
|
return buffer()[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,14 +707,9 @@ int String::lastIndexOf(char ch) const {
|
|||||||
int String::lastIndexOf(char ch, unsigned int fromIndex) const {
|
int String::lastIndexOf(char ch, unsigned int fromIndex) const {
|
||||||
if (fromIndex >= len())
|
if (fromIndex >= len())
|
||||||
return -1;
|
return -1;
|
||||||
char *writeTo = wbuffer();
|
int index = fromIndex + 1;
|
||||||
char tempchar = writeTo[fromIndex + 1]; // save the replaced character
|
while (index-- > 0 && buffer()[index] != ch);
|
||||||
writeTo[fromIndex + 1] = '\0';
|
return index;
|
||||||
char *temp = strrchr(writeTo, ch);
|
|
||||||
writeTo[fromIndex + 1] = tempchar; // restore character
|
|
||||||
if (temp == NULL)
|
|
||||||
return -1;
|
|
||||||
return temp - writeTo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int String::lastIndexOf(const String &s2) const {
|
int String::lastIndexOf(const String &s2) const {
|
||||||
@@ -678,6 +732,15 @@ int String::lastIndexOf(const String &s2, unsigned int fromIndex) const {
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int String::lastIndexOf(const __FlashStringHelper *str) const {
|
||||||
|
return lastIndexOf(String(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::lastIndexOf(const __FlashStringHelper *str, unsigned int fromIndex) const {
|
||||||
|
return lastIndexOf(String(str), fromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
String String::substring(unsigned int left, unsigned int right) const {
|
String String::substring(unsigned int left, unsigned int right) const {
|
||||||
if (left > right) {
|
if (left > right) {
|
||||||
unsigned int temp = right;
|
unsigned int temp = right;
|
||||||
@@ -689,11 +752,7 @@ String String::substring(unsigned int left, unsigned int right) const {
|
|||||||
return out;
|
return out;
|
||||||
if (right > len())
|
if (right > len())
|
||||||
right = len();
|
right = len();
|
||||||
char *writeTo = wbuffer();
|
out.concat(buffer() + left, right - left);
|
||||||
char tempchar = writeTo[right]; // save the replaced character
|
|
||||||
writeTo[right] = '\0';
|
|
||||||
out = writeTo + left; // pointer arithmetic
|
|
||||||
writeTo[right] = tempchar; // restore character
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,6 +815,24 @@ void String::replace(const String &find, const String &replace) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void String::replace(const char *find, const String &replace) {
|
||||||
|
this->replace(String(find), replace);
|
||||||
|
}
|
||||||
|
void String::replace(const __FlashStringHelper *find, const String &replace) {
|
||||||
|
this->replace(String(find), replace);
|
||||||
|
}
|
||||||
|
void String::replace(const char *find, const char *replace) {
|
||||||
|
this->replace(String(find), String(replace));
|
||||||
|
}
|
||||||
|
void String::replace(const __FlashStringHelper *find, const char *replace) {
|
||||||
|
this->replace(String(find), String(replace));
|
||||||
|
}
|
||||||
|
void String::replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) {
|
||||||
|
this->replace(String(find), String(replace));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void String::remove(unsigned int index, unsigned int count) {
|
void String::remove(unsigned int index, unsigned int count) {
|
||||||
if (index >= len()) {
|
if (index >= len()) {
|
||||||
return;
|
return;
|
||||||
|
@@ -45,13 +45,6 @@ class StringSumHelper;
|
|||||||
|
|
||||||
// The string class
|
// The string class
|
||||||
class String {
|
class String {
|
||||||
// use a function pointer to allow for "if (s)" without the
|
|
||||||
// complications of an operator bool(). for more information, see:
|
|
||||||
// http://www.artima.com/cppsource/safebool.html
|
|
||||||
typedef void (String::*StringIfHelperType)() const;
|
|
||||||
void StringIfHelper() const {
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// constructors
|
// constructors
|
||||||
// creates a copy of the initial value.
|
// creates a copy of the initial value.
|
||||||
@@ -65,23 +58,59 @@ class String {
|
|||||||
String(const String &str);
|
String(const String &str);
|
||||||
String(const __FlashStringHelper *str);
|
String(const __FlashStringHelper *str);
|
||||||
String(String &&rval) noexcept;
|
String(String &&rval) noexcept;
|
||||||
|
|
||||||
explicit String(char c) {
|
explicit String(char c) {
|
||||||
sso.buff[0] = c;
|
sso.buff[0] = c;
|
||||||
sso.buff[1] = 0;
|
sso.buff[1] = 0;
|
||||||
sso.len = 1;
|
sso.len = 1;
|
||||||
sso.isHeap = 0;
|
sso.isHeap = 0;
|
||||||
}
|
}
|
||||||
explicit String(unsigned char, unsigned char base = 10);
|
|
||||||
explicit String(int, unsigned char base = 10);
|
String(unsigned char, unsigned char base);
|
||||||
explicit String(unsigned int, unsigned char base = 10);
|
explicit String(unsigned char value) :
|
||||||
explicit String(long, unsigned char base = 10);
|
String(value, 10)
|
||||||
explicit String(unsigned long, unsigned char base = 10);
|
{}
|
||||||
explicit String(long long /* base 10 */);
|
|
||||||
explicit String(long long, unsigned char base);
|
String(int, unsigned char base);
|
||||||
explicit String(unsigned long long /* base 10 */);
|
explicit String(int value) :
|
||||||
explicit String(unsigned long long, unsigned char base);
|
String(value, 10)
|
||||||
explicit String(float, unsigned char decimalPlaces = 2);
|
{}
|
||||||
explicit String(double, unsigned char decimalPlaces = 2);
|
|
||||||
|
String(unsigned int, unsigned char base);
|
||||||
|
explicit String(unsigned int value) :
|
||||||
|
String(value, 10)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String(long, unsigned char base);
|
||||||
|
explicit String(long value) :
|
||||||
|
String(value, 10)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String(unsigned long, unsigned char base);
|
||||||
|
explicit String(unsigned long value) :
|
||||||
|
String(value, 10)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String(long long, unsigned char base);
|
||||||
|
explicit String(long long value) :
|
||||||
|
String(value, 10)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String(unsigned long long, unsigned char base);
|
||||||
|
explicit String(unsigned long long value) :
|
||||||
|
String(value, 10)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String(float, unsigned char decimalPlaces);
|
||||||
|
explicit String(float value) :
|
||||||
|
String(value, 2)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String(double, unsigned char decimalPlaces);
|
||||||
|
explicit String(double value) :
|
||||||
|
String(value, 2)
|
||||||
|
{}
|
||||||
|
|
||||||
~String() {
|
~String() {
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
@@ -90,7 +119,7 @@ class String {
|
|||||||
// return true on success, false on failure (in which case, the string
|
// return true on success, false on failure (in which case, the string
|
||||||
// is left unchanged). reserve(0), if successful, will validate an
|
// is left unchanged). reserve(0), if successful, will validate an
|
||||||
// invalid string (i.e., "if (s)" will be true afterwards)
|
// invalid string (i.e., "if (s)" will be true afterwards)
|
||||||
unsigned char reserve(unsigned int size);
|
bool reserve(unsigned int size);
|
||||||
unsigned int length(void) const {
|
unsigned int length(void) const {
|
||||||
return buffer() ? len() : 0;
|
return buffer() ? len() : 0;
|
||||||
}
|
}
|
||||||
@@ -101,38 +130,78 @@ class String {
|
|||||||
return length() == 0;
|
return length() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a copy of the assigned value. if the value is null or
|
// assign string types as well as built-in numeric types
|
||||||
// invalid, or if the memory allocation fails, the string will be
|
|
||||||
// marked as invalid ("if (s)" will be false).
|
|
||||||
String &operator =(const String &rhs);
|
String &operator =(const String &rhs);
|
||||||
|
String &operator =(String &&rval) noexcept;
|
||||||
String &operator =(const char *cstr);
|
String &operator =(const char *cstr);
|
||||||
String &operator =(const __FlashStringHelper *str);
|
String &operator =(const __FlashStringHelper *str);
|
||||||
String &operator =(String &&rval) noexcept;
|
String &operator =(char c);
|
||||||
String &operator =(char c) {
|
|
||||||
char buffer[2] { c, '\0' };
|
String &operator =(unsigned char value) {
|
||||||
*this = buffer;
|
*this = String(value);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// concatenate (works w/ built-in types)
|
String &operator =(int value) {
|
||||||
|
*this = String(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &operator =(unsigned int value) {
|
||||||
|
*this = String(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &operator =(long value) {
|
||||||
|
*this = String(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &operator =(unsigned long value) {
|
||||||
|
*this = String(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &operator =(long long value) {
|
||||||
|
*this = String(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &operator =(unsigned long long value) {
|
||||||
|
*this = String(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &operator =(float value) {
|
||||||
|
*this = String(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &operator =(double value) {
|
||||||
|
*this = String(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// concatenate (works w/ built-in types, same as assignment)
|
||||||
|
|
||||||
// returns true on success, false on failure (in which case, the string
|
// returns true on success, false on failure (in which case, the string
|
||||||
// is left unchanged). if the argument is null or invalid, the
|
// is left unchanged). if the argument is null or invalid, the
|
||||||
// concatenation is considered unsuccessful.
|
// concatenation is considered unsuccessful.
|
||||||
unsigned char concat(const String &str);
|
bool concat(const String &str);
|
||||||
unsigned char concat(const char *cstr);
|
bool concat(const char *cstr);
|
||||||
unsigned char concat(char c);
|
bool concat(const char *cstr, unsigned int length);
|
||||||
unsigned char concat(unsigned char c);
|
bool concat(const __FlashStringHelper *str);
|
||||||
unsigned char concat(int num);
|
bool concat(char c);
|
||||||
unsigned char concat(unsigned int num);
|
|
||||||
unsigned char concat(long num);
|
bool concat(unsigned char c);
|
||||||
unsigned char concat(unsigned long num);
|
bool concat(int num);
|
||||||
unsigned char concat(long long num);
|
bool concat(unsigned int num);
|
||||||
unsigned char concat(unsigned long long num);
|
bool concat(long num);
|
||||||
unsigned char concat(float num);
|
bool concat(unsigned long num);
|
||||||
unsigned char concat(double num);
|
bool concat(long long num);
|
||||||
unsigned char concat(const __FlashStringHelper *str);
|
bool concat(unsigned long long num);
|
||||||
unsigned char concat(const char *cstr, unsigned int length);
|
bool concat(float num);
|
||||||
|
bool concat(double num);
|
||||||
|
|
||||||
// if there's not enough memory for the concatenated value, the string
|
// if there's not enough memory for the concatenated value, the string
|
||||||
// will be left unchanged (but this isn't signalled in any way)
|
// will be left unchanged (but this isn't signalled in any way)
|
||||||
@@ -142,46 +211,43 @@ class String {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// comparison (only works w/ Strings and "strings")
|
// checks whether the internal buffer pointer is set.
|
||||||
operator StringIfHelperType() const {
|
// (should not be the case for us, since we always reset the pointer to the SSO buffer instead of setting it to nullptr)
|
||||||
return buffer() ? &String::StringIfHelper : 0;
|
explicit operator bool() const {
|
||||||
|
return buffer() != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int compareTo(const String &s) const;
|
int compareTo(const String &s) const;
|
||||||
unsigned char equals(const String &s) const;
|
bool equals(const String &s) const;
|
||||||
unsigned char equals(const char *cstr) const;
|
bool equals(const char *cstr) const;
|
||||||
unsigned char operator ==(const String &rhs) const {
|
bool equals(const __FlashStringHelper *s) const;
|
||||||
|
bool operator ==(const String &rhs) const {
|
||||||
return equals(rhs);
|
return equals(rhs);
|
||||||
}
|
}
|
||||||
unsigned char operator ==(const char *cstr) const {
|
bool operator ==(const char *cstr) const {
|
||||||
return equals(cstr);
|
return equals(cstr);
|
||||||
}
|
}
|
||||||
unsigned char operator !=(const String &rhs) const {
|
bool operator !=(const String &rhs) const {
|
||||||
return !equals(rhs);
|
return !equals(rhs);
|
||||||
}
|
}
|
||||||
unsigned char operator !=(const char *cstr) const {
|
bool operator !=(const char *cstr) const {
|
||||||
return !equals(cstr);
|
return !equals(cstr);
|
||||||
}
|
}
|
||||||
unsigned char operator <(const String &rhs) const;
|
bool operator <(const String &rhs) const;
|
||||||
unsigned char operator >(const String &rhs) const;
|
bool operator >(const String &rhs) const;
|
||||||
unsigned char operator <=(const String &rhs) const;
|
bool operator <=(const String &rhs) const;
|
||||||
unsigned char operator >=(const String &rhs) const;
|
bool operator >=(const String &rhs) const;
|
||||||
unsigned char equalsIgnoreCase(const String &s) const;
|
bool equalsIgnoreCase(const String &s) const;
|
||||||
|
bool equalsIgnoreCase(const __FlashStringHelper *s) const;
|
||||||
unsigned char equalsConstantTime(const String &s) const;
|
unsigned char equalsConstantTime(const String &s) const;
|
||||||
unsigned char startsWith(const String &prefix) const;
|
bool startsWith(const String &prefix) const;
|
||||||
unsigned char startsWith(const char *prefix) const {
|
bool startsWith(const char *prefix) const;
|
||||||
return this->startsWith(String(prefix));
|
bool startsWith(const __FlashStringHelper *prefix) const;
|
||||||
}
|
bool startsWith(const String &prefix, unsigned int offset) const;
|
||||||
unsigned char startsWith(const __FlashStringHelper *prefix) const {
|
bool startsWith(const __FlashStringHelper *prefix, unsigned int offset) const;
|
||||||
return this->startsWith(String(prefix));
|
bool endsWith(const String &suffix) const;
|
||||||
}
|
bool endsWith(const char *suffix) const;
|
||||||
unsigned char startsWith(const String &prefix, unsigned int offset) const;
|
bool endsWith(const __FlashStringHelper *suffix) const;
|
||||||
unsigned char endsWith(const String &suffix) const;
|
|
||||||
unsigned char endsWith(const char *suffix) const {
|
|
||||||
return this->endsWith(String(suffix));
|
|
||||||
}
|
|
||||||
unsigned char endsWith(const __FlashStringHelper *suffix) const {
|
|
||||||
return this->endsWith(String(suffix));
|
|
||||||
}
|
|
||||||
|
|
||||||
// character access
|
// character access
|
||||||
char charAt(unsigned int index) const {
|
char charAt(unsigned int index) const {
|
||||||
@@ -211,6 +277,8 @@ class String {
|
|||||||
int lastIndexOf(char ch, unsigned int fromIndex) const;
|
int lastIndexOf(char ch, unsigned int fromIndex) const;
|
||||||
int lastIndexOf(const String &str) const;
|
int lastIndexOf(const String &str) const;
|
||||||
int lastIndexOf(const String &str, unsigned int fromIndex) const;
|
int lastIndexOf(const String &str, unsigned int fromIndex) const;
|
||||||
|
int lastIndexOf(const __FlashStringHelper *str) const;
|
||||||
|
int lastIndexOf(const __FlashStringHelper *str, unsigned int fromIndex) const;
|
||||||
String substring(unsigned int beginIndex) const {
|
String substring(unsigned int beginIndex) const {
|
||||||
return substring(beginIndex, len());
|
return substring(beginIndex, len());
|
||||||
}
|
}
|
||||||
@@ -219,21 +287,12 @@ class String {
|
|||||||
// modification
|
// modification
|
||||||
void replace(char find, char replace);
|
void replace(char find, char replace);
|
||||||
void replace(const String &find, const String &replace);
|
void replace(const String &find, const String &replace);
|
||||||
void replace(const char *find, const String &replace) {
|
void replace(const char *find, const String &replace);
|
||||||
this->replace(String(find), replace);
|
void replace(const __FlashStringHelper *find, const String &replace);
|
||||||
}
|
void replace(const char *find, const char *replace);
|
||||||
void replace(const __FlashStringHelper *find, const String &replace) {
|
void replace(const __FlashStringHelper *find, const char *replace);
|
||||||
this->replace(String(find), replace);
|
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace);
|
||||||
}
|
|
||||||
void replace(const char *find, const char *replace) {
|
|
||||||
this->replace(String(find), String(replace));
|
|
||||||
}
|
|
||||||
void replace(const __FlashStringHelper *find, const char *replace) {
|
|
||||||
this->replace(String(find), String(replace));
|
|
||||||
}
|
|
||||||
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) {
|
|
||||||
this->replace(String(find), String(replace));
|
|
||||||
}
|
|
||||||
// Pass the biggest integer if the count is not specified.
|
// Pass the biggest integer if the count is not specified.
|
||||||
// The remove method below will take care of truncating it at the end of the string.
|
// The remove method below will take care of truncating it at the end of the string.
|
||||||
void remove(unsigned int index, unsigned int count = (unsigned int)-1);
|
void remove(unsigned int index, unsigned int count = (unsigned int)-1);
|
||||||
@@ -280,8 +339,8 @@ class String {
|
|||||||
void setCapacity(int cap) { if (!isSSO()) ptr.cap = cap; }
|
void setCapacity(int cap) { if (!isSSO()) ptr.cap = cap; }
|
||||||
void setBuffer(char *buff) { if (!isSSO()) ptr.buff = buff; }
|
void setBuffer(char *buff) { if (!isSSO()) ptr.buff = buff; }
|
||||||
// Buffer accessor functions
|
// Buffer accessor functions
|
||||||
const char *buffer() const { return wbuffer(); }
|
const char *buffer() const { return isSSO() ? sso.buff : ptr.buff; }
|
||||||
char *wbuffer() const { return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff; } // Writable version of buffer
|
char *wbuffer() { return const_cast<char *>(buffer()); } // Writable version of buffer
|
||||||
|
|
||||||
// concatenation is done via non-member functions
|
// concatenation is done via non-member functions
|
||||||
// make sure we still have access to internal methods, since we optimize based on capacity of both sides and want to manipulate internal buffers directly
|
// make sure we still have access to internal methods, since we optimize based on capacity of both sides and want to manipulate internal buffers directly
|
||||||
@@ -292,6 +351,8 @@ class String {
|
|||||||
friend String operator +(const __FlashStringHelper *lhs, String &&rhs);
|
friend String operator +(const __FlashStringHelper *lhs, String &&rhs);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// TODO: replace init() with a union constructor, so it's called implicitly
|
||||||
|
|
||||||
void init(void) __attribute__((always_inline)) {
|
void init(void) __attribute__((always_inline)) {
|
||||||
sso.buff[0] = 0;
|
sso.buff[0] = 0;
|
||||||
sso.len = 0;
|
sso.len = 0;
|
||||||
@@ -309,8 +370,10 @@ class String {
|
|||||||
// Unfortunately, GCC seems not to re-evaluate the cost of inlining after the store-merging optimizer stage,
|
// Unfortunately, GCC seems not to re-evaluate the cost of inlining after the store-merging optimizer stage,
|
||||||
// `always_inline` attribute is necessary in order to keep inlining.
|
// `always_inline` attribute is necessary in order to keep inlining.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resets the string storage to the initial state
|
||||||
void invalidate(void);
|
void invalidate(void);
|
||||||
unsigned char changeBuffer(unsigned int maxStrLen);
|
bool changeBuffer(unsigned int maxStrLen);
|
||||||
|
|
||||||
// copy or insert at a specific position
|
// copy or insert at a specific position
|
||||||
String ©(const char *cstr, unsigned int length);
|
String ©(const char *cstr, unsigned int length);
|
||||||
|
@@ -21,9 +21,9 @@
|
|||||||
.section .irom0.text
|
.section .irom0.text
|
||||||
.align 4
|
.align 4
|
||||||
.literal_position
|
.literal_position
|
||||||
.global cont_yield
|
.global cont_suspend
|
||||||
.type cont_yield, @function
|
.type cont_suspend, @function
|
||||||
cont_yield:
|
cont_suspend:
|
||||||
/* a1: sp */
|
/* a1: sp */
|
||||||
/* a2: void* cont_ctx */
|
/* a2: void* cont_ctx */
|
||||||
/* adjust stack and save registers */
|
/* adjust stack and save registers */
|
||||||
@@ -35,10 +35,10 @@ cont_yield:
|
|||||||
s32i a0, a1, 16
|
s32i a0, a1, 16
|
||||||
s32i a2, a1, 20
|
s32i a2, a1, 20
|
||||||
|
|
||||||
/* &cont_continue -> cont_ctx.pc_yield */
|
/* &cont_continue -> cont_ctx.pc_suspend */
|
||||||
movi a3, cont_continue
|
movi a3, cont_continue
|
||||||
s32i a3, a2, 8
|
s32i a3, a2, 8
|
||||||
/* sp -> cont_ctx.sp_yield */
|
/* sp -> cont_ctx.sp_suspend */
|
||||||
s32i a1, a2, 12
|
s32i a1, a2, 12
|
||||||
|
|
||||||
/* a0 <- cont_ctx.pc_ret */
|
/* a0 <- cont_ctx.pc_ret */
|
||||||
@@ -56,7 +56,7 @@ cont_continue:
|
|||||||
l32i a2, a1, 20
|
l32i a2, a1, 20
|
||||||
addi a1, a1, 24
|
addi a1, a1, 24
|
||||||
ret
|
ret
|
||||||
.size cont_yield, . - cont_yield
|
.size cont_suspend, . - cont_suspend
|
||||||
|
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ cont_run:
|
|||||||
/* sp -> cont_ctx.sp_ret */
|
/* sp -> cont_ctx.sp_ret */
|
||||||
s32i a1, a2, 4
|
s32i a1, a2, 4
|
||||||
|
|
||||||
/* if cont_ctx.pc_yield != 0, goto cont_resume */
|
/* if cont_ctx.pc_suspend != 0, goto cont_resume */
|
||||||
l32i a4, a2, 8
|
l32i a4, a2, 8
|
||||||
bnez a4, cont_resume
|
bnez a4, cont_resume
|
||||||
/* else */
|
/* else */
|
||||||
@@ -119,12 +119,12 @@ cont_run:
|
|||||||
jx a2
|
jx a2
|
||||||
|
|
||||||
cont_resume:
|
cont_resume:
|
||||||
/* a1 <- cont_ctx.sp_yield */
|
/* a1 <- cont_ctx.sp_suspend */
|
||||||
l32i a1, a2, 12
|
l32i a1, a2, 12
|
||||||
/* reset yield flag, 0 -> cont_ctx.pc_yield */
|
/* reset yield flag, 0 -> cont_ctx.pc_suspend */
|
||||||
movi a3, 0
|
movi a3, 0
|
||||||
s32i a3, a2, 8
|
s32i a3, a2, 8
|
||||||
/* jump to saved cont_ctx.pc_yield */
|
/* jump to saved cont_ctx.pc_suspend */
|
||||||
movi a0, cont_ret
|
movi a0, cont_ret
|
||||||
jx a4
|
jx a4
|
||||||
|
|
||||||
|
@@ -35,8 +35,8 @@ typedef struct cont_ {
|
|||||||
void (*pc_ret)(void);
|
void (*pc_ret)(void);
|
||||||
unsigned* sp_ret;
|
unsigned* sp_ret;
|
||||||
|
|
||||||
void (*pc_yield)(void);
|
void (*pc_suspend)(void);
|
||||||
unsigned* sp_yield;
|
unsigned* sp_suspend;
|
||||||
|
|
||||||
unsigned* stack_end;
|
unsigned* stack_end;
|
||||||
unsigned unused1;
|
unsigned unused1;
|
||||||
@@ -55,12 +55,12 @@ extern cont_t* g_pcont;
|
|||||||
void cont_init(cont_t*);
|
void cont_init(cont_t*);
|
||||||
|
|
||||||
// Run function pfn in a separate stack, or continue execution
|
// Run function pfn in a separate stack, or continue execution
|
||||||
// at the point where cont_yield was called
|
// at the point where cont_suspend was called
|
||||||
void cont_run(cont_t*, void (*pfn)(void));
|
void cont_run(cont_t*, void (*pfn)(void));
|
||||||
|
|
||||||
// Return to the point where cont_run was called, saving the
|
// Return to the point where cont_run was called, saving the
|
||||||
// execution state (registers and stack)
|
// execution state (registers and stack)
|
||||||
void cont_yield(cont_t*);
|
void cont_suspend(cont_t*);
|
||||||
|
|
||||||
// Check guard bytes around the stack. Return 0 in case everything is ok,
|
// Check guard bytes around the stack. Return 0 in case everything is ok,
|
||||||
// return 1 if guard bytes were overwritten.
|
// return 1 if guard bytes were overwritten.
|
||||||
@@ -70,9 +70,9 @@ int cont_check(cont_t* cont);
|
|||||||
// and thus weren't used by the user code. i.e. that stack space is free. (high water mark)
|
// and thus weren't used by the user code. i.e. that stack space is free. (high water mark)
|
||||||
int cont_get_free_stack(cont_t* cont);
|
int cont_get_free_stack(cont_t* cont);
|
||||||
|
|
||||||
// Check if yield() may be called. Returns true if we are running inside
|
// Check if cont_suspend() may be called. Returns true if we are running inside
|
||||||
// continuation stack
|
// continuation stack
|
||||||
bool cont_can_yield(cont_t* cont);
|
bool cont_can_suspend(cont_t* cont);
|
||||||
|
|
||||||
// Repaint the stack from the current SP to the end, to allow individual
|
// Repaint the stack from the current SP to the end, to allow individual
|
||||||
// routines' stack usages to be calculated by re-painting, checking current
|
// routines' stack usages to be calculated by re-painting, checking current
|
||||||
|
@@ -62,9 +62,9 @@ int cont_get_free_stack(cont_t* cont) {
|
|||||||
return freeWords * 4;
|
return freeWords * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IRAM_ATTR cont_can_yield(cont_t* cont) {
|
bool IRAM_ATTR cont_can_suspend(cont_t* cont) {
|
||||||
return !ETS_INTR_WITHINISR() &&
|
return !ETS_INTR_WITHINISR() &&
|
||||||
cont->pc_ret != 0 && cont->pc_yield == 0;
|
cont->pc_ret != 0 && cont->pc_suspend == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need for this to be in IRAM, not expected to be IRQ called
|
// No need for this to be in IRAM, not expected to be IRQ called
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
#include <c_types.h>
|
#include <c_types.h>
|
||||||
#include "cont.h"
|
#include "cont.h"
|
||||||
#include "coredecls.h"
|
#include "coredecls.h"
|
||||||
|
#include <umm_malloc/umm_malloc.h>
|
||||||
|
|
||||||
void disable_extra4k_at_link_time (void)
|
void disable_extra4k_at_link_time (void)
|
||||||
{
|
{
|
||||||
@@ -38,6 +39,19 @@ extern "C" void app_entry_redefinable(void)
|
|||||||
{
|
{
|
||||||
g_pcont = &g_cont;
|
g_pcont = &g_cont;
|
||||||
|
|
||||||
|
#ifdef UMM_INIT_USE_IRAM
|
||||||
|
/*
|
||||||
|
* Legacy option: the umm_init() call path must reside in IRAM.
|
||||||
|
*/
|
||||||
|
umm_init();
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* Instruction cache is enabled/disabled around running umm_init().
|
||||||
|
* Allows the use of IROM (flash) to store umm_init().
|
||||||
|
*/
|
||||||
|
mmu_wrap_irom_fn(umm_init);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Call the entry point of the SDK code. */
|
/* Call the entry point of the SDK code. */
|
||||||
call_user_start();
|
call_user_start();
|
||||||
}
|
}
|
||||||
|
@@ -483,7 +483,7 @@ void i2s_set_dividers(uint8_t div1, uint8_t div2) {
|
|||||||
i2sc_temp |= (I2STXR); // Hold transmitter in reset
|
i2sc_temp |= (I2STXR); // Hold transmitter in reset
|
||||||
I2SC = i2sc_temp;
|
I2SC = i2sc_temp;
|
||||||
|
|
||||||
// trans master(active low), recv master(active_low), !bits mod(==16 bits/chanel), clear clock dividers
|
// trans master(active low), recv master(active_low), !bits mod(==16 bits/channel), clear clock dividers
|
||||||
i2sc_temp &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
|
i2sc_temp &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
|
||||||
|
|
||||||
// I2SRF = Send/recv right channel first (? may be swapped form I2S spec of WS=0 => left)
|
// I2SRF = Send/recv right channel first (? may be swapped form I2S spec of WS=0 => left)
|
||||||
|
@@ -18,8 +18,8 @@
|
|||||||
License along with this library; if not, write to the Free Software
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
#ifndef I2S_h
|
#ifndef CORE_ESP8266_I2S_H
|
||||||
#define I2S_h
|
#define CORE_ESP8266_I2S_H
|
||||||
|
|
||||||
#define I2S_HAS_BEGIN_RXTX_DRIVE_CLOCKS 1
|
#define I2S_HAS_BEGIN_RXTX_DRIVE_CLOCKS 1
|
||||||
|
|
||||||
|
@@ -62,14 +62,14 @@ cont_t* g_pcont __attribute__((section(".noinit")));
|
|||||||
static os_event_t s_loop_queue[LOOP_QUEUE_SIZE];
|
static os_event_t s_loop_queue[LOOP_QUEUE_SIZE];
|
||||||
|
|
||||||
/* Used to implement optimistic_yield */
|
/* Used to implement optimistic_yield */
|
||||||
static uint32_t s_cycles_at_yield_start;
|
static uint32_t s_cycles_at_resume;
|
||||||
|
|
||||||
/* For ets_intr_lock_nest / ets_intr_unlock_nest
|
/* For ets_intr_lock_nest / ets_intr_unlock_nest
|
||||||
* Max nesting seen by SDK so far is 2.
|
* Max nesting seen by SDK so far is 2.
|
||||||
*/
|
*/
|
||||||
#define ETS_INTR_LOCK_NEST_MAX 7
|
#define ETS_INTR_LOCK_NEST_MAX 7
|
||||||
static uint16_t ets_intr_lock_stack[ETS_INTR_LOCK_NEST_MAX];
|
static uint16_t ets_intr_lock_stack[ETS_INTR_LOCK_NEST_MAX];
|
||||||
static byte ets_intr_lock_stack_ptr=0;
|
static uint8_t ets_intr_lock_stack_ptr=0;
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -80,6 +80,10 @@ const char* core_release =
|
|||||||
#else
|
#else
|
||||||
NULL;
|
NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static os_timer_t delay_timer;
|
||||||
|
#define ONCE 0
|
||||||
|
#define REPEAT 1
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
void initVariant() __attribute__((weak));
|
void initVariant() __attribute__((weak));
|
||||||
@@ -106,32 +110,71 @@ extern "C" void __preloop_update_frequency() {
|
|||||||
extern "C" void preloop_update_frequency() __attribute__((weak, alias("__preloop_update_frequency")));
|
extern "C" void preloop_update_frequency() __attribute__((weak, alias("__preloop_update_frequency")));
|
||||||
|
|
||||||
extern "C" bool can_yield() {
|
extern "C" bool can_yield() {
|
||||||
return cont_can_yield(g_pcont);
|
return cont_can_suspend(g_pcont);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void esp_yield_within_cont() __attribute__((always_inline));
|
static inline void esp_suspend_within_cont() __attribute__((always_inline));
|
||||||
static void esp_yield_within_cont() {
|
static void esp_suspend_within_cont() {
|
||||||
cont_yield(g_pcont);
|
cont_suspend(g_pcont);
|
||||||
s_cycles_at_yield_start = ESP.getCycleCount();
|
s_cycles_at_resume = ESP.getCycleCount();
|
||||||
run_scheduled_recurrent_functions();
|
run_scheduled_recurrent_functions();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void __esp_yield() {
|
extern "C" void __esp_suspend() {
|
||||||
if (can_yield()) {
|
if (cont_can_suspend(g_pcont)) {
|
||||||
esp_yield_within_cont();
|
esp_suspend_within_cont();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void esp_yield() __attribute__ ((weak, alias("__esp_yield")));
|
extern "C" void esp_suspend() __attribute__ ((weak, alias("__esp_suspend")));
|
||||||
|
|
||||||
extern "C" IRAM_ATTR void esp_schedule() {
|
extern "C" IRAM_ATTR void esp_schedule() {
|
||||||
ets_post(LOOP_TASK_PRIORITY, 0, 0);
|
ets_post(LOOP_TASK_PRIORITY, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void __yield() {
|
// Replacement for delay(0). In CONT, same as yield(). Whereas yield() panics
|
||||||
if (can_yield()) {
|
// in SYS, esp_yield() is safe to call and only schedules CONT. Use yield()
|
||||||
|
// whereever only called from CONT, use esp_yield() if code is called from SYS
|
||||||
|
// or both CONT and SYS.
|
||||||
|
extern "C" void esp_yield() {
|
||||||
|
esp_schedule();
|
||||||
|
esp_suspend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void delay_end(void* arg) {
|
||||||
|
(void)arg;
|
||||||
|
esp_schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void __esp_delay(unsigned long ms) {
|
||||||
|
if (ms) {
|
||||||
|
os_timer_setfn(&delay_timer, (os_timer_func_t*)&delay_end, 0);
|
||||||
|
os_timer_arm(&delay_timer, ms, ONCE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
esp_schedule();
|
esp_schedule();
|
||||||
esp_yield_within_cont();
|
}
|
||||||
|
esp_suspend();
|
||||||
|
if (ms) {
|
||||||
|
os_timer_disarm(&delay_timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void esp_delay(unsigned long ms) __attribute__((weak, alias("__esp_delay")));
|
||||||
|
|
||||||
|
bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uint32_t intvl_ms) {
|
||||||
|
uint32_t expired = millis() - start_ms;
|
||||||
|
if (expired >= timeout_ms) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
esp_delay(std::min((timeout_ms - expired), intvl_ms));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void __yield() {
|
||||||
|
if (cont_can_suspend(g_pcont)) {
|
||||||
|
esp_schedule();
|
||||||
|
esp_suspend_within_cont();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
panic();
|
panic();
|
||||||
@@ -140,6 +183,9 @@ extern "C" void __yield() {
|
|||||||
|
|
||||||
extern "C" void yield(void) __attribute__ ((weak, alias("__yield")));
|
extern "C" void yield(void) __attribute__ ((weak, alias("__yield")));
|
||||||
|
|
||||||
|
// In CONT, actually performs yield() only once the given time interval
|
||||||
|
// has elapsed since the last time yield() occured. Whereas yield() panics
|
||||||
|
// in SYS, optimistic_yield() additionally is safe to call and does nothing.
|
||||||
extern "C" void optimistic_yield(uint32_t interval_us) {
|
extern "C" void optimistic_yield(uint32_t interval_us) {
|
||||||
const uint32_t intvl_cycles = interval_us *
|
const uint32_t intvl_cycles = interval_us *
|
||||||
#if defined(F_CPU)
|
#if defined(F_CPU)
|
||||||
@@ -147,7 +193,7 @@ extern "C" void optimistic_yield(uint32_t interval_us) {
|
|||||||
#else
|
#else
|
||||||
ESP.getCpuFreqMHz();
|
ESP.getCpuFreqMHz();
|
||||||
#endif
|
#endif
|
||||||
if ((ESP.getCycleCount() - s_cycles_at_yield_start) > intvl_cycles &&
|
if ((ESP.getCycleCount() - s_cycles_at_resume) > intvl_cycles &&
|
||||||
can_yield())
|
can_yield())
|
||||||
{
|
{
|
||||||
yield();
|
yield();
|
||||||
@@ -207,7 +253,7 @@ static void loop_wrapper() {
|
|||||||
|
|
||||||
static void loop_task(os_event_t *events) {
|
static void loop_task(os_event_t *events) {
|
||||||
(void) events;
|
(void) events;
|
||||||
s_cycles_at_yield_start = ESP.getCycleCount();
|
s_cycles_at_resume = ESP.getCycleCount();
|
||||||
ESP.resetHeap();
|
ESP.resetHeap();
|
||||||
cont_run(g_pcont, &loop_wrapper);
|
cont_run(g_pcont, &loop_wrapper);
|
||||||
ESP.setDramHeap();
|
ESP.setDramHeap();
|
||||||
@@ -215,8 +261,8 @@ static void loop_task(os_event_t *events) {
|
|||||||
panic();
|
panic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
struct object { long placeholder[ 10 ]; };
|
struct object { long placeholder[ 10 ]; };
|
||||||
void __register_frame_info (const void *begin, struct object *ob);
|
void __register_frame_info (const void *begin, struct object *ob);
|
||||||
extern char __eh_frame[];
|
extern char __eh_frame[];
|
||||||
@@ -253,7 +299,6 @@ static void __unhandled_exception_cpp()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_done() {
|
void init_done() {
|
||||||
@@ -290,7 +335,7 @@ void init_done() {
|
|||||||
know if other features are using this, or if this memory is going to be
|
know if other features are using this, or if this memory is going to be
|
||||||
used in future SDK releases.
|
used in future SDK releases.
|
||||||
|
|
||||||
WPS beeing flawed by its poor security, or not beeing used by lots of
|
WPS being flawed by its poor security, or not being used by lots of
|
||||||
users, it has been decided that we are still going to use that memory for
|
users, it has been decided that we are still going to use that memory for
|
||||||
user's stack and disable the use of WPS.
|
user's stack and disable the use of WPS.
|
||||||
|
|
||||||
@@ -318,6 +363,14 @@ extern "C" void app_entry_redefinable(void)
|
|||||||
cont_t s_cont __attribute__((aligned(16)));
|
cont_t s_cont __attribute__((aligned(16)));
|
||||||
g_pcont = &s_cont;
|
g_pcont = &s_cont;
|
||||||
|
|
||||||
|
/* Doing umm_init just once before starting the SDK, allowed us to remove
|
||||||
|
test and init calls at each malloc API entry point, saving IRAM. */
|
||||||
|
#ifdef UMM_INIT_USE_IRAM
|
||||||
|
umm_init();
|
||||||
|
#else
|
||||||
|
// umm_init() is in IROM
|
||||||
|
mmu_wrap_irom_fn(umm_init);
|
||||||
|
#endif
|
||||||
/* Call the entry point of the SDK code. */
|
/* Call the entry point of the SDK code. */
|
||||||
call_user_start();
|
call_user_start();
|
||||||
}
|
}
|
||||||
@@ -325,7 +378,6 @@ static void app_entry_custom (void) __attribute__((weakref("app_entry_redefinabl
|
|||||||
|
|
||||||
extern "C" void app_entry (void)
|
extern "C" void app_entry (void)
|
||||||
{
|
{
|
||||||
umm_init();
|
|
||||||
return app_entry_custom();
|
return app_entry_custom();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,6 +398,12 @@ extern "C" void __disableWiFiAtBootTime (void)
|
|||||||
wifi_fpm_do_sleep(0xFFFFFFF);
|
wifi_fpm_do_sleep(0xFFFFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if FLASH_MAP_SUPPORT
|
||||||
|
#include "flash_hal.h"
|
||||||
|
extern "C" void flashinit (void);
|
||||||
|
uint32_t __flashindex;
|
||||||
|
#endif
|
||||||
|
|
||||||
extern "C" void user_init(void) {
|
extern "C" void user_init(void) {
|
||||||
struct rst_info *rtc_info_ptr = system_get_rst_info();
|
struct rst_info *rtc_info_ptr = system_get_rst_info();
|
||||||
memcpy((void *) &resetInfo, (void *) rtc_info_ptr, sizeof(resetInfo));
|
memcpy((void *) &resetInfo, (void *) rtc_info_ptr, sizeof(resetInfo));
|
||||||
@@ -367,13 +425,16 @@ extern "C" void user_init(void) {
|
|||||||
#if defined(UMM_HEAP_EXTERNAL)
|
#if defined(UMM_HEAP_EXTERNAL)
|
||||||
install_vm_exception_handler();
|
install_vm_exception_handler();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP)
|
#if defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP)
|
||||||
install_non32xfer_exception_handler();
|
install_non32xfer_exception_handler();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(MMU_IRAM_HEAP)
|
#if defined(MMU_IRAM_HEAP)
|
||||||
umm_init_iram();
|
umm_init_iram();
|
||||||
|
#endif
|
||||||
|
#if FLASH_MAP_SUPPORT
|
||||||
|
flashinit();
|
||||||
#endif
|
#endif
|
||||||
preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable.
|
preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable.
|
||||||
__disableWiFiAtBootTime(); // default weak function disables WiFi
|
__disableWiFiAtBootTime(); // default weak function disables WiFi
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
core_esp8266_noniso.c - nonstandard (but usefull) conversion functions
|
core_esp8266_noniso.c - nonstandard (but useful) conversion functions
|
||||||
|
|
||||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||||
This file is part of the esp8266 core for Arduino environment.
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
@@ -289,7 +289,7 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] =
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// These functions will be overriden from C++ code.
|
// These functions will be overridden from C++ code.
|
||||||
// Unfortunately, we can't use extern "C" because Arduino preprocessor
|
// Unfortunately, we can't use extern "C" because Arduino preprocessor
|
||||||
// doesn't generate forward declarations for extern "C" functions correctly,
|
// doesn't generate forward declarations for extern "C" functions correctly,
|
||||||
// so we use mangled names here.
|
// so we use mangled names here.
|
||||||
|
@@ -245,12 +245,12 @@ static void print_stack(uint32_t start, uint32_t end) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_write_char_d(char c) {
|
static void IRAM_ATTR uart_write_char_d(char c) {
|
||||||
uart0_write_char_d(c);
|
uart0_write_char_d(c);
|
||||||
uart1_write_char_d(c);
|
uart1_write_char_d(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart0_write_char_d(char c) {
|
static void IRAM_ATTR uart0_write_char_d(char c) {
|
||||||
while (((USS(0) >> USTXC) & 0xff)) { }
|
while (((USS(0) >> USTXC) & 0xff)) { }
|
||||||
|
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
@@ -259,7 +259,7 @@ static void uart0_write_char_d(char c) {
|
|||||||
USF(0) = c;
|
USF(0) = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart1_write_char_d(char c) {
|
static void IRAM_ATTR uart1_write_char_d(char c) {
|
||||||
while (((USS(1) >> USTXC) & 0xff) >= 0x7e) { }
|
while (((USS(1) >> USTXC) & 0xff) >= 0x7e) { }
|
||||||
|
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
|
@@ -24,9 +24,8 @@
|
|||||||
#include "wiring_private.h"
|
#include "wiring_private.h"
|
||||||
#include "PolledTimeout.h"
|
#include "PolledTimeout.h"
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
extern "C" {
|
|
||||||
#include "twi_util.h"
|
#include "twi_util.h"
|
||||||
#include "ets_sys.h"
|
#include "ets_sys.h"
|
||||||
};
|
};
|
||||||
@@ -57,78 +56,113 @@ static inline __attribute__((always_inline)) bool SCL_READ(const int twi_scl)
|
|||||||
return (GPI & (1 << twi_scl)) != 0;
|
return (GPI & (1 << twi_scl)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement as a class to reduce code size by allowing access to many global variables with a
|
||||||
// Implement as a class to reduce code size by allowing access to many global variables with a single base pointer
|
// single base pointer
|
||||||
class Twi
|
class Twi
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
unsigned int preferred_si2c_clock = 100000;
|
unsigned int preferred_si2c_clock = 100000;
|
||||||
uint32_t twi_dcount = 18;
|
uint32_t twi_dcount = 18;
|
||||||
unsigned char twi_sda = 0;
|
unsigned char twi_sda = 0;
|
||||||
unsigned char twi_scl = 0;
|
unsigned char twi_scl = 0;
|
||||||
unsigned char twi_addr = 0;
|
unsigned char twi_addr = 0;
|
||||||
uint32_t twi_clockStretchLimit = 0;
|
uint32_t twi_clockStretchLimit = 0;
|
||||||
|
|
||||||
// These are int-wide, even though they could all fit in a byte, to reduce code size and avoid any potential
|
// These are int-wide, even though they could all fit in a byte, to reduce code size and avoid
|
||||||
// issues about RmW on packed bytes. The int-wide variations of asm instructions are smaller than the equivalent
|
// any potential issues about RmW on packed bytes. The int-wide variations of asm instructions
|
||||||
// byte-wide ones, and since these emums are used everywhere, the difference adds up fast. There is only a single
|
// are smaller than the equivalent byte-wide ones, and since these emums are used everywhere,
|
||||||
// instance of the class, though, so the extra 12 bytes of RAM used here saves a lot more IRAM.
|
// the difference adds up fast. There is only a single instance of the class, though, so the
|
||||||
volatile enum { TWIPM_UNKNOWN = 0, TWIPM_IDLE, TWIPM_ADDRESSED, TWIPM_WAIT} twip_mode = TWIPM_IDLE;
|
// extra 12 bytes of RAM used here saves a lot more IRAM.
|
||||||
volatile enum { TWIP_UNKNOWN = 0, TWIP_IDLE, TWIP_START, TWIP_SEND_ACK, TWIP_WAIT_ACK, TWIP_WAIT_STOP, TWIP_SLA_W, TWIP_SLA_R, TWIP_REP_START, TWIP_READ, TWIP_STOP, TWIP_REC_ACK, TWIP_READ_ACK, TWIP_RWAIT_ACK, TWIP_WRITE, TWIP_BUS_ERR } twip_state = TWIP_IDLE;
|
volatile enum {
|
||||||
|
TWIPM_UNKNOWN = 0,
|
||||||
|
TWIPM_IDLE,
|
||||||
|
TWIPM_ADDRESSED,
|
||||||
|
TWIPM_WAIT
|
||||||
|
} twip_mode
|
||||||
|
= TWIPM_IDLE;
|
||||||
|
volatile enum {
|
||||||
|
TWIP_UNKNOWN = 0,
|
||||||
|
TWIP_IDLE,
|
||||||
|
TWIP_START,
|
||||||
|
TWIP_SEND_ACK,
|
||||||
|
TWIP_WAIT_ACK,
|
||||||
|
TWIP_WAIT_STOP,
|
||||||
|
TWIP_SLA_W,
|
||||||
|
TWIP_SLA_R,
|
||||||
|
TWIP_REP_START,
|
||||||
|
TWIP_READ,
|
||||||
|
TWIP_STOP,
|
||||||
|
TWIP_REC_ACK,
|
||||||
|
TWIP_READ_ACK,
|
||||||
|
TWIP_RWAIT_ACK,
|
||||||
|
TWIP_WRITE,
|
||||||
|
TWIP_BUS_ERR
|
||||||
|
} twip_state
|
||||||
|
= TWIP_IDLE;
|
||||||
volatile int twip_status = TW_NO_INFO;
|
volatile int twip_status = TW_NO_INFO;
|
||||||
volatile int bitCount = 0;
|
volatile int bitCount = 0;
|
||||||
|
|
||||||
volatile uint8_t twi_data = 0x00;
|
volatile uint8_t twi_data = 0x00;
|
||||||
volatile int twi_ack = 0;
|
volatile int twi_ack = 0;
|
||||||
volatile int twi_ack_rec = 0;
|
volatile int twi_ack_rec = 0;
|
||||||
volatile int twi_timeout_ms = 10;
|
volatile int twi_timeout_ms = 10;
|
||||||
|
|
||||||
volatile enum { TWI_READY = 0, TWI_MRX, TWI_MTX, TWI_SRX, TWI_STX } twi_state = TWI_READY;
|
volatile enum { TWI_READY = 0, TWI_MRX, TWI_MTX, TWI_SRX, TWI_STX } twi_state = TWI_READY;
|
||||||
volatile uint8_t twi_error = 0xFF;
|
volatile uint8_t twi_error = 0xFF;
|
||||||
|
|
||||||
uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
|
uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
|
||||||
volatile int twi_txBufferIndex = 0;
|
volatile int twi_txBufferIndex = 0;
|
||||||
volatile int twi_txBufferLength = 0;
|
volatile int twi_txBufferLength = 0;
|
||||||
|
|
||||||
uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
|
uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
|
||||||
volatile int twi_rxBufferIndex = 0;
|
volatile int twi_rxBufferIndex = 0;
|
||||||
|
|
||||||
void (*twi_onSlaveTransmit)(void);
|
void (*twi_onSlaveTransmit)(void);
|
||||||
void (*twi_onSlaveReceive)(uint8_t*, size_t);
|
void (*twi_onSlaveReceive)(uint8_t*, size_t);
|
||||||
|
|
||||||
// ETS queue/timer interfaces
|
// ETS queue/timer interfaces
|
||||||
enum { EVENTTASK_QUEUE_SIZE = 1, EVENTTASK_QUEUE_PRIO = 2 };
|
enum
|
||||||
enum { TWI_SIG_RANGE = 0x00000100, TWI_SIG_RX = 0x00000101, TWI_SIG_TX = 0x00000102 };
|
{
|
||||||
|
EVENTTASK_QUEUE_SIZE = 1,
|
||||||
|
EVENTTASK_QUEUE_PRIO = 2
|
||||||
|
};
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TWI_SIG_RANGE = 0x00000100,
|
||||||
|
TWI_SIG_RX = 0x00000101,
|
||||||
|
TWI_SIG_TX = 0x00000102
|
||||||
|
};
|
||||||
ETSEvent eventTaskQueue[EVENTTASK_QUEUE_SIZE];
|
ETSEvent eventTaskQueue[EVENTTASK_QUEUE_SIZE];
|
||||||
ETSTimer timer;
|
ETSTimer timer;
|
||||||
|
|
||||||
// Event/IRQ callbacks, so they can't use "this" and need to be static
|
// Event/IRQ callbacks, so they can't use "this" and need to be static
|
||||||
static void IRAM_ATTR onSclChange(void);
|
static void IRAM_ATTR onSclChange(void);
|
||||||
static void IRAM_ATTR onSdaChange(void);
|
static void IRAM_ATTR onSdaChange(void);
|
||||||
static void eventTask(ETSEvent *e);
|
static void eventTask(ETSEvent* e);
|
||||||
static void IRAM_ATTR onTimer(void *unused);
|
static void IRAM_ATTR onTimer(void* unused);
|
||||||
|
|
||||||
// Allow not linking in the slave code if there is no call to setAddress
|
// Allow not linking in the slave code if there is no call to setAddress
|
||||||
bool _slaveEnabled = false;
|
bool _slaveEnabled = false;
|
||||||
|
|
||||||
// Internal use functions
|
// Internal use functions
|
||||||
void IRAM_ATTR busywait(unsigned int v);
|
void IRAM_ATTR busywait(unsigned int v);
|
||||||
bool write_start(void);
|
bool write_start(void);
|
||||||
bool write_stop(void);
|
bool write_stop(void);
|
||||||
bool write_bit(bool bit);
|
bool write_bit(bool bit);
|
||||||
bool read_bit(void);
|
bool read_bit(void);
|
||||||
bool write_byte(unsigned char byte);
|
bool write_byte(unsigned char byte);
|
||||||
unsigned char read_byte(bool nack);
|
unsigned char read_byte(bool nack);
|
||||||
void IRAM_ATTR onTwipEvent(uint8_t status);
|
void IRAM_ATTR onTwipEvent(uint8_t status);
|
||||||
|
|
||||||
// Handle the case where a slave needs to stretch the clock with a time-limited busy wait
|
// Handle the case where a slave needs to stretch the clock with a time-limited busy wait
|
||||||
inline void WAIT_CLOCK_STRETCH()
|
inline void WAIT_CLOCK_STRETCH()
|
||||||
{
|
{
|
||||||
esp8266::polledTimeout::oneShotFastUs timeout(twi_clockStretchLimit);
|
esp8266::polledTimeout::oneShotFastUs timeout(twi_clockStretchLimit);
|
||||||
esp8266::polledTimeout::periodicFastUs yieldTimeout(5000);
|
esp8266::polledTimeout::periodicFastUs yieldTimeout(5000);
|
||||||
while (!timeout && !SCL_READ(twi_scl)) // outer loop is stretch duration up to stretch limit
|
while (!timeout
|
||||||
|
&& !SCL_READ(twi_scl)) // outer loop is stretch duration up to stretch limit
|
||||||
{
|
{
|
||||||
if (yieldTimeout) // inner loop yields every 5ms
|
if (yieldTimeout) // inner loop yields every 5ms
|
||||||
{
|
{
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
@@ -139,19 +173,21 @@ private:
|
|||||||
void twi_scl_valley(void);
|
void twi_scl_valley(void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setClock(unsigned int freq);
|
void setClock(unsigned int freq);
|
||||||
void setClockStretchLimit(uint32_t limit);
|
void setClockStretchLimit(uint32_t limit);
|
||||||
void init(unsigned char sda, unsigned char scl);
|
void init(unsigned char sda, unsigned char scl);
|
||||||
void setAddress(uint8_t address);
|
void setAddress(uint8_t address);
|
||||||
unsigned char writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop);
|
unsigned char writeTo(unsigned char address, unsigned char* buf, unsigned int len,
|
||||||
unsigned char readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop);
|
unsigned char sendStop);
|
||||||
uint8_t status();
|
unsigned char readFrom(unsigned char address, unsigned char* buf, unsigned int len,
|
||||||
uint8_t transmit(const uint8_t* data, uint8_t length);
|
unsigned char sendStop);
|
||||||
void attachSlaveRxEvent(void (*function)(uint8_t*, size_t));
|
uint8_t status();
|
||||||
void attachSlaveTxEvent(void (*function)(void));
|
uint8_t transmit(const uint8_t* data, uint8_t length);
|
||||||
|
void attachSlaveRxEvent(void (*function)(uint8_t*, size_t));
|
||||||
|
void attachSlaveTxEvent(void (*function)(void));
|
||||||
void IRAM_ATTR reply(uint8_t ack);
|
void IRAM_ATTR reply(uint8_t ack);
|
||||||
void IRAM_ATTR releaseBus(void);
|
void IRAM_ATTR releaseBus(void);
|
||||||
void enableSlave();
|
void enableSlave();
|
||||||
};
|
};
|
||||||
|
|
||||||
static Twi twi;
|
static Twi twi;
|
||||||
@@ -176,7 +212,8 @@ void Twi::setClock(unsigned int freq)
|
|||||||
freq = 400000;
|
freq = 400000;
|
||||||
}
|
}
|
||||||
twi_dcount = (500000000 / freq); // half-cycle period in ns
|
twi_dcount = (500000000 / freq); // half-cycle period in ns
|
||||||
twi_dcount = (1000 * (twi_dcount - 1120)) / 62500; // (half cycle - overhead) / busywait loop time
|
twi_dcount
|
||||||
|
= (1000 * (twi_dcount - 1120)) / 62500; // (half cycle - overhead) / busywait loop time
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
@@ -185,7 +222,8 @@ void Twi::setClock(unsigned int freq)
|
|||||||
freq = 800000;
|
freq = 800000;
|
||||||
}
|
}
|
||||||
twi_dcount = (500000000 / freq); // half-cycle period in ns
|
twi_dcount = (500000000 / freq); // half-cycle period in ns
|
||||||
twi_dcount = (1000 * (twi_dcount - 560)) / 31250; // (half cycle - overhead) / busywait loop time
|
twi_dcount
|
||||||
|
= (1000 * (twi_dcount - 560)) / 31250; // (half cycle - overhead) / busywait loop time
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -195,8 +233,6 @@ void Twi::setClockStretchLimit(uint32_t limit)
|
|||||||
twi_clockStretchLimit = limit;
|
twi_clockStretchLimit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Twi::init(unsigned char sda, unsigned char scl)
|
void Twi::init(unsigned char sda, unsigned char scl)
|
||||||
{
|
{
|
||||||
// set timer function
|
// set timer function
|
||||||
@@ -210,7 +246,7 @@ void Twi::init(unsigned char sda, unsigned char scl)
|
|||||||
pinMode(twi_sda, INPUT_PULLUP);
|
pinMode(twi_sda, INPUT_PULLUP);
|
||||||
pinMode(twi_scl, INPUT_PULLUP);
|
pinMode(twi_scl, INPUT_PULLUP);
|
||||||
twi_setClock(preferred_si2c_clock);
|
twi_setClock(preferred_si2c_clock);
|
||||||
twi_setClockStretchLimit(150000L); // default value is 150 mS
|
twi_setClockStretchLimit(150000L); // default value is 150 mS
|
||||||
}
|
}
|
||||||
|
|
||||||
void Twi::setAddress(uint8_t address)
|
void Twi::setAddress(uint8_t address)
|
||||||
@@ -234,7 +270,8 @@ void IRAM_ATTR Twi::busywait(unsigned int v)
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
for (i = 0; i < v; i++) // loop time is 5 machine cycles: 31.25ns @ 160MHz, 62.5ns @ 80MHz
|
for (i = 0; i < v; i++) // loop time is 5 machine cycles: 31.25ns @ 160MHz, 62.5ns @ 80MHz
|
||||||
{
|
{
|
||||||
__asm__ __volatile__("nop"); // minimum element to keep GCC from optimizing this function out.
|
__asm__ __volatile__(
|
||||||
|
"nop"); // minimum element to keep GCC from optimizing this function out.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,8 +284,13 @@ bool Twi::write_start(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
busywait(twi_dcount);
|
busywait(twi_dcount);
|
||||||
|
// A high-to-low transition on the SDA line while the SCL is high defines a START condition.
|
||||||
SDA_LOW(twi_sda);
|
SDA_LOW(twi_sda);
|
||||||
busywait(twi_dcount);
|
busywait(twi_dcount);
|
||||||
|
// An additional delay between the SCL line high-to-low transition and setting up the SDA line
|
||||||
|
// to prevent a STOP condition execute.
|
||||||
|
SCL_LOW(twi_scl);
|
||||||
|
busywait(twi_dcount);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +302,7 @@ bool Twi::write_stop(void)
|
|||||||
SCL_HIGH(twi_scl);
|
SCL_HIGH(twi_scl);
|
||||||
WAIT_CLOCK_STRETCH();
|
WAIT_CLOCK_STRETCH();
|
||||||
busywait(twi_dcount);
|
busywait(twi_dcount);
|
||||||
|
// A low-to-high transition on the SDA line while the SCL is high defines a STOP condition.
|
||||||
SDA_HIGH(twi_sda);
|
SDA_HIGH(twi_sda);
|
||||||
busywait(twi_dcount);
|
busywait(twi_dcount);
|
||||||
return true;
|
return true;
|
||||||
@@ -303,7 +346,7 @@ bool Twi::write_byte(unsigned char byte)
|
|||||||
write_bit(byte & 0x80);
|
write_bit(byte & 0x80);
|
||||||
byte <<= 1;
|
byte <<= 1;
|
||||||
}
|
}
|
||||||
return !read_bit();//NACK/ACK
|
return !read_bit(); // NACK/ACK
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char Twi::read_byte(bool nack)
|
unsigned char Twi::read_byte(bool nack)
|
||||||
@@ -318,12 +361,13 @@ unsigned char Twi::read_byte(bool nack)
|
|||||||
return byte;
|
return byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop)
|
unsigned char Twi::writeTo(unsigned char address, unsigned char* buf, unsigned int len,
|
||||||
|
unsigned char sendStop)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
if (!write_start())
|
if (!write_start())
|
||||||
{
|
{
|
||||||
return 4; //line busy
|
return 4; // line busy
|
||||||
}
|
}
|
||||||
if (!write_byte(((address << 1) | 0) & 0xFF))
|
if (!write_byte(((address << 1) | 0) & 0xFF))
|
||||||
{
|
{
|
||||||
@@ -331,7 +375,7 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned
|
|||||||
{
|
{
|
||||||
write_stop();
|
write_stop();
|
||||||
}
|
}
|
||||||
return 2; //received NACK on transmit of address
|
return 2; // received NACK on transmit of address
|
||||||
}
|
}
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
@@ -341,7 +385,7 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned
|
|||||||
{
|
{
|
||||||
write_stop();
|
write_stop();
|
||||||
}
|
}
|
||||||
return 3;//received NACK on transmit of data
|
return 3; // received NACK on transmit of data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sendStop)
|
if (sendStop)
|
||||||
@@ -363,12 +407,13 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop)
|
unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned int len,
|
||||||
|
unsigned char sendStop)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
if (!write_start())
|
if (!write_start())
|
||||||
{
|
{
|
||||||
return 4; //line busy
|
return 4; // line busy
|
||||||
}
|
}
|
||||||
if (!write_byte(((address << 1) | 1) & 0xFF))
|
if (!write_byte(((address << 1) | 1) & 0xFF))
|
||||||
{
|
{
|
||||||
@@ -376,7 +421,7 @@ unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned
|
|||||||
{
|
{
|
||||||
write_stop();
|
write_stop();
|
||||||
}
|
}
|
||||||
return 2;//received NACK on transmit of address
|
return 2; // received NACK on transmit of address
|
||||||
}
|
}
|
||||||
for (i = 0; i < (len - 1); i++)
|
for (i = 0; i < (len - 1); i++)
|
||||||
{
|
{
|
||||||
@@ -415,21 +460,25 @@ uint8_t Twi::status()
|
|||||||
WAIT_CLOCK_STRETCH(); // wait for a slow slave to finish
|
WAIT_CLOCK_STRETCH(); // wait for a slow slave to finish
|
||||||
if (!SCL_READ(twi_scl))
|
if (!SCL_READ(twi_scl))
|
||||||
{
|
{
|
||||||
return I2C_SCL_HELD_LOW; // SCL held low by another device, no procedure available to recover
|
return I2C_SCL_HELD_LOW; // SCL held low by another device, no procedure available to
|
||||||
|
// recover
|
||||||
}
|
}
|
||||||
|
|
||||||
int clockCount = 20;
|
int clockCount = 20;
|
||||||
while (!SDA_READ(twi_sda) && clockCount-- > 0) // if SDA low, read the bits slaves have to sent to a max
|
while (!SDA_READ(twi_sda)
|
||||||
|
&& clockCount-- > 0) // if SDA low, read the bits slaves have to sent to a max
|
||||||
{
|
{
|
||||||
read_bit();
|
read_bit();
|
||||||
if (!SCL_READ(twi_scl))
|
if (!SCL_READ(twi_scl))
|
||||||
{
|
{
|
||||||
return I2C_SCL_HELD_LOW_AFTER_READ; // I2C bus error. SCL held low beyond slave clock stretch time
|
return I2C_SCL_HELD_LOW_AFTER_READ; // I2C bus error. SCL held low beyond slave clock
|
||||||
|
// stretch time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!SDA_READ(twi_sda))
|
if (!SDA_READ(twi_sda))
|
||||||
{
|
{
|
||||||
return I2C_SDA_HELD_LOW; // I2C bus error. SDA line held low by slave/another_master after n bits.
|
return I2C_SDA_HELD_LOW; // I2C bus error. SDA line held low by slave/another_master after
|
||||||
|
// n bits.
|
||||||
}
|
}
|
||||||
|
|
||||||
return I2C_OK;
|
return I2C_OK;
|
||||||
@@ -471,58 +520,57 @@ void Twi::attachSlaveTxEvent(void (*function)(void))
|
|||||||
twi_onSlaveTransmit = function;
|
twi_onSlaveTransmit = function;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function breakup into
|
// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function
|
||||||
// parts and the IRAM_ATTR isn't propagated correctly to all parts, which of course causes crashes.
|
// breakup into parts and the IRAM_ATTR isn't propagated correctly to all parts, which of course
|
||||||
|
// causes crashes.
|
||||||
// TODO: test with gcc 9.x and if it still fails, disable optimization with -fdisable-ipa-fnsplit
|
// TODO: test with gcc 9.x and if it still fails, disable optimization with -fdisable-ipa-fnsplit
|
||||||
void IRAM_ATTR Twi::reply(uint8_t ack)
|
void IRAM_ATTR Twi::reply(uint8_t ack)
|
||||||
{
|
{
|
||||||
// transmit master read ready signal, with or without ack
|
// transmit master read ready signal, with or without ack
|
||||||
if (ack)
|
if (ack)
|
||||||
{
|
{
|
||||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
|
// TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
|
||||||
SCL_HIGH(twi.twi_scl); // _BV(TWINT)
|
SCL_HIGH(twi.twi_scl); // _BV(TWINT)
|
||||||
twi_ack = 1; // _BV(TWEA)
|
twi_ack = 1; // _BV(TWEA)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
|
// TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
|
||||||
SCL_HIGH(twi.twi_scl); // _BV(TWINT)
|
SCL_HIGH(twi.twi_scl); // _BV(TWINT)
|
||||||
twi_ack = 0; // ~_BV(TWEA)
|
twi_ack = 0; // ~_BV(TWEA)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void IRAM_ATTR Twi::releaseBus(void)
|
void IRAM_ATTR Twi::releaseBus(void)
|
||||||
{
|
{
|
||||||
// release bus
|
// release bus
|
||||||
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
|
// TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
|
||||||
SCL_HIGH(twi.twi_scl); // _BV(TWINT)
|
SCL_HIGH(twi.twi_scl); // _BV(TWINT)
|
||||||
twi_ack = 1; // _BV(TWEA)
|
twi_ack = 1; // _BV(TWEA)
|
||||||
SDA_HIGH(twi.twi_sda);
|
SDA_HIGH(twi.twi_sda);
|
||||||
|
|
||||||
// update twi state
|
// update twi state
|
||||||
twi_state = TWI_READY;
|
twi_state = TWI_READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void IRAM_ATTR Twi::onTwipEvent(uint8_t status)
|
void IRAM_ATTR Twi::onTwipEvent(uint8_t status)
|
||||||
{
|
{
|
||||||
twip_status = status;
|
twip_status = status;
|
||||||
switch (status)
|
switch (status)
|
||||||
{
|
{
|
||||||
// Slave Receiver
|
// Slave Receiver
|
||||||
case TW_SR_SLA_ACK: // addressed, returned ack
|
case TW_SR_SLA_ACK: // addressed, returned ack
|
||||||
case TW_SR_GCALL_ACK: // addressed generally, returned ack
|
case TW_SR_GCALL_ACK: // addressed generally, returned ack
|
||||||
case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack
|
case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack
|
||||||
case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack
|
case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack
|
||||||
// enter slave receiver mode
|
// enter slave receiver mode
|
||||||
twi_state = TWI_SRX;
|
twi_state = TWI_SRX;
|
||||||
// indicate that rx buffer can be overwritten and ack
|
// indicate that rx buffer can be overwritten and ack
|
||||||
twi_rxBufferIndex = 0;
|
twi_rxBufferIndex = 0;
|
||||||
reply(1);
|
reply(1);
|
||||||
break;
|
break;
|
||||||
case TW_SR_DATA_ACK: // data received, returned ack
|
case TW_SR_DATA_ACK: // data received, returned ack
|
||||||
case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
|
case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
|
||||||
// if there is still room in the rx buffer
|
// if there is still room in the rx buffer
|
||||||
if (twi_rxBufferIndex < TWI_BUFFER_LENGTH)
|
if (twi_rxBufferIndex < TWI_BUFFER_LENGTH)
|
||||||
{
|
{
|
||||||
@@ -536,29 +584,29 @@ void IRAM_ATTR Twi::onTwipEvent(uint8_t status)
|
|||||||
reply(0);
|
reply(0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TW_SR_STOP: // stop or repeated start condition received
|
case TW_SR_STOP: // stop or repeated start condition received
|
||||||
// put a null char after data if there's room
|
// put a null char after data if there's room
|
||||||
if (twi_rxBufferIndex < TWI_BUFFER_LENGTH)
|
if (twi_rxBufferIndex < TWI_BUFFER_LENGTH)
|
||||||
{
|
{
|
||||||
twi_rxBuffer[twi_rxBufferIndex] = '\0';
|
twi_rxBuffer[twi_rxBufferIndex] = '\0';
|
||||||
}
|
}
|
||||||
// callback to user-defined callback over event task to allow for non-RAM-residing code
|
// callback to user-defined callback over event task to allow for non-RAM-residing code
|
||||||
//twi_rxBufferLock = true; // This may be necessary
|
// twi_rxBufferLock = true; // This may be necessary
|
||||||
ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_RX, twi_rxBufferIndex);
|
ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_RX, twi_rxBufferIndex);
|
||||||
|
|
||||||
// since we submit rx buffer to "wire" library, we can reset it
|
// since we submit rx buffer to "wire" library, we can reset it
|
||||||
twi_rxBufferIndex = 0;
|
twi_rxBufferIndex = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TW_SR_DATA_NACK: // data received, returned nack
|
case TW_SR_DATA_NACK: // data received, returned nack
|
||||||
case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack
|
case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack
|
||||||
// nack back at master
|
// nack back at master
|
||||||
reply(0);
|
reply(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Slave Transmitter
|
// Slave Transmitter
|
||||||
case TW_ST_SLA_ACK: // addressed, returned ack
|
case TW_ST_SLA_ACK: // addressed, returned ack
|
||||||
case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
|
case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
|
||||||
// enter slave transmitter mode
|
// enter slave transmitter mode
|
||||||
twi_state = TWI_STX;
|
twi_state = TWI_STX;
|
||||||
// ready the tx buffer index for iteration
|
// ready the tx buffer index for iteration
|
||||||
@@ -571,7 +619,7 @@ void IRAM_ATTR Twi::onTwipEvent(uint8_t status)
|
|||||||
ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_TX, 0);
|
ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_TX, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TW_ST_DATA_ACK: // byte sent, ack returned
|
case TW_ST_DATA_ACK: // byte sent, ack returned
|
||||||
// copy data to output register
|
// copy data to output register
|
||||||
twi_data = twi_txBuffer[twi_txBufferIndex++];
|
twi_data = twi_txBuffer[twi_txBufferIndex++];
|
||||||
|
|
||||||
@@ -597,33 +645,32 @@ void IRAM_ATTR Twi::onTwipEvent(uint8_t status)
|
|||||||
reply(0);
|
reply(0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TW_ST_DATA_NACK: // received nack, we are done
|
case TW_ST_DATA_NACK: // received nack, we are done
|
||||||
case TW_ST_LAST_DATA: // received ack, but we are done already!
|
case TW_ST_LAST_DATA: // received ack, but we are done already!
|
||||||
// leave slave receiver state
|
// leave slave receiver state
|
||||||
releaseBus();
|
releaseBus();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// All
|
// All
|
||||||
case TW_NO_INFO: // no state information
|
case TW_NO_INFO: // no state information
|
||||||
break;
|
break;
|
||||||
case TW_BUS_ERROR: // bus error, illegal stop/start
|
case TW_BUS_ERROR: // bus error, illegal stop/start
|
||||||
twi_error = TW_BUS_ERROR;
|
twi_error = TW_BUS_ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR Twi::onTimer(void *unused)
|
void IRAM_ATTR Twi::onTimer(void* unused)
|
||||||
{
|
{
|
||||||
(void)unused;
|
(void)unused;
|
||||||
twi.releaseBus();
|
twi.releaseBus();
|
||||||
twi.onTwipEvent(TW_BUS_ERROR);
|
twi.onTwipEvent(TW_BUS_ERROR);
|
||||||
twi.twip_mode = TWIPM_WAIT;
|
twi.twip_mode = TWIPM_WAIT;
|
||||||
twi.twip_state = TWIP_BUS_ERR;
|
twi.twip_state = TWIP_BUS_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Twi::eventTask(ETSEvent *e)
|
void Twi::eventTask(ETSEvent* e)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (e == NULL)
|
if (e == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -638,7 +685,7 @@ void Twi::eventTask(ETSEvent *e)
|
|||||||
if (twi.twi_txBufferLength == 0)
|
if (twi.twi_txBufferLength == 0)
|
||||||
{
|
{
|
||||||
twi.twi_txBufferLength = 1;
|
twi.twi_txBufferLength = 1;
|
||||||
twi.twi_txBuffer[0] = 0x00;
|
twi.twi_txBuffer[0] = 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initiate transmission
|
// Initiate transmission
|
||||||
@@ -658,7 +705,7 @@ void Twi::eventTask(ETSEvent *e)
|
|||||||
// compared to the logical-or of all states with the same branch. This removes the need
|
// compared to the logical-or of all states with the same branch. This removes the need
|
||||||
// for a large series of straight-line compares. The biggest win is when multiple states
|
// for a large series of straight-line compares. The biggest win is when multiple states
|
||||||
// all have the same branch (onSdaChange), but for others there is some benefit, still.
|
// all have the same branch (onSdaChange), but for others there is some benefit, still.
|
||||||
#define S2M(x) (1<<(x))
|
#define S2M(x) (1 << (x))
|
||||||
// Shorthand for if the state is any of the or'd bitmask x
|
// Shorthand for if the state is any of the or'd bitmask x
|
||||||
#define IFSTATE(x) if (twip_state_mask & (x))
|
#define IFSTATE(x) if (twip_state_mask & (x))
|
||||||
|
|
||||||
@@ -672,7 +719,7 @@ void IRAM_ATTR Twi::onSclChange(void)
|
|||||||
sda = SDA_READ(twi.twi_sda);
|
sda = SDA_READ(twi.twi_sda);
|
||||||
scl = SCL_READ(twi.twi_scl);
|
scl = SCL_READ(twi.twi_scl);
|
||||||
|
|
||||||
twi.twip_status = 0xF8; // reset TWI status
|
twi.twip_status = 0xF8; // reset TWI status
|
||||||
|
|
||||||
int twip_state_mask = S2M(twi.twip_state);
|
int twip_state_mask = S2M(twi.twip_state);
|
||||||
IFSTATE(S2M(TWIP_START) | S2M(TWIP_REP_START) | S2M(TWIP_SLA_W) | S2M(TWIP_READ))
|
IFSTATE(S2M(TWIP_START) | S2M(TWIP_REP_START) | S2M(TWIP_SLA_W) | S2M(TWIP_READ))
|
||||||
@@ -747,13 +794,13 @@ void IRAM_ATTR Twi::onSclChange(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SCL_LOW(twi.twi_scl); // clock stretching
|
SCL_LOW(twi.twi_scl); // clock stretching
|
||||||
SDA_HIGH(twi.twi_sda);
|
SDA_HIGH(twi.twi_sda);
|
||||||
twi.twip_mode = TWIPM_ADDRESSED;
|
twi.twip_mode = TWIPM_ADDRESSED;
|
||||||
if (!(twi.twi_data & 0x01))
|
if (!(twi.twi_data & 0x01))
|
||||||
{
|
{
|
||||||
twi.onTwipEvent(TW_SR_SLA_ACK);
|
twi.onTwipEvent(TW_SR_SLA_ACK);
|
||||||
twi.bitCount = 8;
|
twi.bitCount = 8;
|
||||||
twi.twip_state = TWIP_SLA_W;
|
twi.twip_state = TWIP_SLA_W;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -765,18 +812,18 @@ void IRAM_ATTR Twi::onSclChange(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SCL_LOW(twi.twi_scl); // clock stretching
|
SCL_LOW(twi.twi_scl); // clock stretching
|
||||||
SDA_HIGH(twi.twi_sda);
|
SDA_HIGH(twi.twi_sda);
|
||||||
if (!twi.twi_ack)
|
if (!twi.twi_ack)
|
||||||
{
|
{
|
||||||
twi.onTwipEvent(TW_SR_DATA_NACK);
|
twi.onTwipEvent(TW_SR_DATA_NACK);
|
||||||
twi.twip_mode = TWIPM_WAIT;
|
twi.twip_mode = TWIPM_WAIT;
|
||||||
twi.twip_state = TWIP_WAIT_STOP;
|
twi.twip_state = TWIP_WAIT_STOP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
twi.onTwipEvent(TW_SR_DATA_ACK);
|
twi.onTwipEvent(TW_SR_DATA_ACK);
|
||||||
twi.bitCount = 8;
|
twi.bitCount = 8;
|
||||||
twi.twip_state = TWIP_READ;
|
twi.twip_state = TWIP_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -832,7 +879,7 @@ void IRAM_ATTR Twi::onSclChange(void)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
twi.twi_ack_rec = !sda;
|
twi.twi_ack_rec = !sda;
|
||||||
twi.twip_state = TWIP_RWAIT_ACK;
|
twi.twip_state = TWIP_RWAIT_ACK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else IFSTATE(S2M(TWIP_RWAIT_ACK))
|
else IFSTATE(S2M(TWIP_RWAIT_ACK))
|
||||||
@@ -843,7 +890,7 @@ void IRAM_ATTR Twi::onSclChange(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SCL_LOW(twi.twi_scl); // clock stretching
|
SCL_LOW(twi.twi_scl); // clock stretching
|
||||||
if (twi.twi_ack && twi.twi_ack_rec)
|
if (twi.twi_ack && twi.twi_ack_rec)
|
||||||
{
|
{
|
||||||
twi.onTwipEvent(TW_ST_DATA_ACK);
|
twi.onTwipEvent(TW_ST_DATA_ACK);
|
||||||
@@ -853,7 +900,7 @@ void IRAM_ATTR Twi::onSclChange(void)
|
|||||||
{
|
{
|
||||||
// we have no more data to send and/or the master doesn't want anymore
|
// we have no more data to send and/or the master doesn't want anymore
|
||||||
twi.onTwipEvent(twi.twi_ack_rec ? TW_ST_LAST_DATA : TW_ST_DATA_NACK);
|
twi.onTwipEvent(twi.twi_ack_rec ? TW_ST_LAST_DATA : TW_ST_DATA_NACK);
|
||||||
twi.twip_mode = TWIPM_WAIT;
|
twi.twip_mode = TWIPM_WAIT;
|
||||||
twi.twip_state = TWIP_WAIT_STOP;
|
twi.twip_state = TWIP_WAIT_STOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -870,7 +917,7 @@ void IRAM_ATTR Twi::onSdaChange(void)
|
|||||||
scl = SCL_READ(twi.twi_scl);
|
scl = SCL_READ(twi.twi_scl);
|
||||||
|
|
||||||
int twip_state_mask = S2M(twi.twip_state);
|
int twip_state_mask = S2M(twi.twip_state);
|
||||||
if (scl) /* !DATA */
|
if (scl) /* !DATA */
|
||||||
{
|
{
|
||||||
IFSTATE(S2M(TWIP_IDLE))
|
IFSTATE(S2M(TWIP_IDLE))
|
||||||
{
|
{
|
||||||
@@ -881,17 +928,19 @@ void IRAM_ATTR Twi::onSdaChange(void)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// START
|
// START
|
||||||
twi.bitCount = 8;
|
twi.bitCount = 8;
|
||||||
twi.twip_state = TWIP_START;
|
twi.twip_state = TWIP_START;
|
||||||
ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms
|
ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else IFSTATE(S2M(TWIP_START) | S2M(TWIP_REP_START) | S2M(TWIP_SEND_ACK) | S2M(TWIP_WAIT_ACK) | S2M(TWIP_SLA_R) | S2M(TWIP_REC_ACK) | S2M(TWIP_READ_ACK) | S2M(TWIP_RWAIT_ACK) | S2M(TWIP_WRITE))
|
else IFSTATE(S2M(TWIP_START) | S2M(TWIP_REP_START) | S2M(TWIP_SEND_ACK) | S2M(TWIP_WAIT_ACK)
|
||||||
|
| S2M(TWIP_SLA_R) | S2M(TWIP_REC_ACK) | S2M(TWIP_READ_ACK)
|
||||||
|
| S2M(TWIP_RWAIT_ACK) | S2M(TWIP_WRITE))
|
||||||
{
|
{
|
||||||
// START or STOP
|
// START or STOP
|
||||||
SDA_HIGH(twi.twi_sda); // Should not be necessary
|
SDA_HIGH(twi.twi_sda); // Should not be necessary
|
||||||
twi.onTwipEvent(TW_BUS_ERROR);
|
twi.onTwipEvent(TW_BUS_ERROR);
|
||||||
twi.twip_mode = TWIPM_WAIT;
|
twi.twip_mode = TWIPM_WAIT;
|
||||||
twi.twip_state = TWIP_BUS_ERR;
|
twi.twip_state = TWIP_BUS_ERR;
|
||||||
}
|
}
|
||||||
else IFSTATE(S2M(TWIP_WAIT_STOP) | S2M(TWIP_BUS_ERR))
|
else IFSTATE(S2M(TWIP_WAIT_STOP) | S2M(TWIP_BUS_ERR))
|
||||||
@@ -899,10 +948,10 @@ void IRAM_ATTR Twi::onSdaChange(void)
|
|||||||
if (sda)
|
if (sda)
|
||||||
{
|
{
|
||||||
// STOP
|
// STOP
|
||||||
SCL_LOW(twi.twi_scl); // generates a low SCL pulse after STOP
|
SCL_LOW(twi.twi_scl); // generates a low SCL pulse after STOP
|
||||||
ets_timer_disarm(&twi.timer);
|
ets_timer_disarm(&twi.timer);
|
||||||
twi.twip_state = TWIP_IDLE;
|
twi.twip_state = TWIP_IDLE;
|
||||||
twi.twip_mode = TWIPM_IDLE;
|
twi.twip_mode = TWIPM_IDLE;
|
||||||
SCL_HIGH(twi.twi_scl);
|
SCL_HIGH(twi.twi_scl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -914,9 +963,9 @@ void IRAM_ATTR Twi::onSdaChange(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
twi.bitCount = 8;
|
twi.bitCount = 8;
|
||||||
twi.twip_state = TWIP_REP_START;
|
twi.twip_state = TWIP_REP_START;
|
||||||
ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms
|
ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -927,28 +976,28 @@ void IRAM_ATTR Twi::onSdaChange(void)
|
|||||||
{
|
{
|
||||||
// inside byte transfer - error
|
// inside byte transfer - error
|
||||||
twi.onTwipEvent(TW_BUS_ERROR);
|
twi.onTwipEvent(TW_BUS_ERROR);
|
||||||
twi.twip_mode = TWIPM_WAIT;
|
twi.twip_mode = TWIPM_WAIT;
|
||||||
twi.twip_state = TWIP_BUS_ERR;
|
twi.twip_state = TWIP_BUS_ERR;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// during first bit in byte transfer - ok
|
// during first bit in byte transfer - ok
|
||||||
SCL_LOW(twi.twi_scl); // clock stretching
|
SCL_LOW(twi.twi_scl); // clock stretching
|
||||||
twi.onTwipEvent(TW_SR_STOP);
|
twi.onTwipEvent(TW_SR_STOP);
|
||||||
if (sda)
|
if (sda)
|
||||||
{
|
{
|
||||||
// STOP
|
// STOP
|
||||||
ets_timer_disarm(&twi.timer);
|
ets_timer_disarm(&twi.timer);
|
||||||
twi.twip_state = TWIP_IDLE;
|
twi.twip_state = TWIP_IDLE;
|
||||||
twi.twip_mode = TWIPM_IDLE;
|
twi.twip_mode = TWIPM_IDLE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// START
|
// START
|
||||||
twi.bitCount = 8;
|
twi.bitCount = 8;
|
||||||
ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms
|
ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms
|
||||||
twi.twip_state = TWIP_REP_START;
|
twi.twip_state = TWIP_REP_START;
|
||||||
twi.twip_mode = TWIPM_IDLE;
|
twi.twip_mode = TWIPM_IDLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -956,8 +1005,8 @@ void IRAM_ATTR Twi::onSdaChange(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// C wrappers for the object, since API is exposed only as C
|
// C wrappers for the object, since API is exposed only as C
|
||||||
extern "C" {
|
extern "C"
|
||||||
|
{
|
||||||
void twi_init(unsigned char sda, unsigned char scl)
|
void twi_init(unsigned char sda, unsigned char scl)
|
||||||
{
|
{
|
||||||
return twi.init(sda, scl);
|
return twi.init(sda, scl);
|
||||||
@@ -978,12 +1027,14 @@ extern "C" {
|
|||||||
twi.setClockStretchLimit(limit);
|
twi.setClockStretchLimit(limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop)
|
uint8_t twi_writeTo(unsigned char address, unsigned char* buf, unsigned int len,
|
||||||
|
unsigned char sendStop)
|
||||||
{
|
{
|
||||||
return twi.writeTo(address, buf, len, sendStop);
|
return twi.writeTo(address, buf, len, sendStop);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t twi_readFrom(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop)
|
uint8_t twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len,
|
||||||
|
unsigned char sendStop)
|
||||||
{
|
{
|
||||||
return twi.readFrom(address, buf, len, sendStop);
|
return twi.readFrom(address, buf, len, sendStop);
|
||||||
}
|
}
|
||||||
@@ -993,7 +1044,7 @@ extern "C" {
|
|||||||
return twi.status();
|
return twi.status();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t twi_transmit(const uint8_t * buf, uint8_t len)
|
uint8_t twi_transmit(const uint8_t* buf, uint8_t len)
|
||||||
{
|
{
|
||||||
return twi.transmit(buf, len);
|
return twi.transmit(buf, len);
|
||||||
}
|
}
|
||||||
@@ -1022,5 +1073,4 @@ extern "C" {
|
|||||||
{
|
{
|
||||||
twi.enableSlave();
|
twi.enableSlave();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -56,7 +56,7 @@ _SPICommand(volatile uint32_t spiIfNum,
|
|||||||
if (spiIfNum>1)
|
if (spiIfNum>1)
|
||||||
return SPI_RESULT_ERR;
|
return SPI_RESULT_ERR;
|
||||||
|
|
||||||
// force SPI register access via base+offest.
|
// force SPI register access via base+offset.
|
||||||
// Prevents loading individual address constants from flash.
|
// Prevents loading individual address constants from flash.
|
||||||
uint32_t *spibase = (uint32_t*)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD));
|
uint32_t *spibase = (uint32_t*)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD));
|
||||||
#define SPIREG(reg) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD)))))
|
#define SPIREG(reg) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD)))))
|
||||||
@@ -141,7 +141,7 @@ _SPICommand(volatile uint32_t spiIfNum,
|
|||||||
* data has been sent.
|
* data has been sent.
|
||||||
*
|
*
|
||||||
* Note: This code has only been tested with SPI bus 0, but should work
|
* Note: This code has only been tested with SPI bus 0, but should work
|
||||||
* equally well with other busses. The ESP8266 has bus 0 and 1,
|
* equally well with other buses. The ESP8266 has bus 0 and 1,
|
||||||
* newer chips may have more one day.
|
* newer chips may have more one day.
|
||||||
*/
|
*/
|
||||||
SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits) {
|
SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits) {
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
core_esp8266_version.h - parse "git describe" at compile time
|
core_esp8266_version.h - parse "git describe" at compile time
|
||||||
Copyright (c) 2018 david gauchard. All rights reserved.
|
Copyright (c) 2018 david gauchard. All rights reserved.
|
||||||
@@ -159,7 +158,7 @@ int coreVersionSubRevision ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* unique revision indentifier (never decreases)
|
* unique revision identifier (never decreases)
|
||||||
*/
|
*/
|
||||||
constexpr
|
constexpr
|
||||||
int coreVersionNumeric ()
|
int coreVersionNumeric ()
|
||||||
|
@@ -56,7 +56,7 @@
|
|||||||
* Higher density PSRAM (ESP-PSRAM64H/etc.) works as well, but may be too
|
* Higher density PSRAM (ESP-PSRAM64H/etc.) works as well, but may be too
|
||||||
large to effectively use with UMM. Only 256K is available vial malloc,
|
large to effectively use with UMM. Only 256K is available vial malloc,
|
||||||
but addresses above 256K do work and can be used for fixed buffers.
|
but addresses above 256K do work and can be used for fixed buffers.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef MMU_EXTERNAL_HEAP
|
#ifdef MMU_EXTERNAL_HEAP
|
||||||
@@ -71,6 +71,8 @@
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
#define VM_OFFSET_MASK 0x007fffffu
|
||||||
|
|
||||||
#define SHORT_MASK 0x000008u
|
#define SHORT_MASK 0x000008u
|
||||||
#define LOAD_MASK 0x00f00fu
|
#define LOAD_MASK 0x00f00fu
|
||||||
#define L8UI_MATCH 0x000002u
|
#define L8UI_MATCH 0x000002u
|
||||||
@@ -324,21 +326,21 @@ static IRAM_ATTR void loadstore_exception_handler(struct __exception_frame *ef,
|
|||||||
uint32_t val = ef->a_reg[regno];
|
uint32_t val = ef->a_reg[regno];
|
||||||
uint32_t what = insn & STORE_MASK;
|
uint32_t what = insn & STORE_MASK;
|
||||||
if (what == S8I_MATCH) {
|
if (what == S8I_MATCH) {
|
||||||
spi_ramwrite(spi1, excvaddr & 0x1ffff, 8-1, val);
|
spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 8-1, val);
|
||||||
} else if (what == S16I_MATCH) {
|
} else if (what == S16I_MATCH) {
|
||||||
spi_ramwrite(spi1, excvaddr & 0x1ffff, 16-1, val);
|
spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 16-1, val);
|
||||||
} else {
|
} else {
|
||||||
spi_ramwrite(spi1, excvaddr & 0x1ffff, 32-1, val);
|
spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 32-1, val);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (insn & L32_MASK) {
|
if (insn & L32_MASK) {
|
||||||
ef->a_reg[regno] = spi_ramread(spi1, excvaddr & 0x1ffff, 32-1);
|
ef->a_reg[regno] = spi_ramread(spi1, excvaddr & VM_OFFSET_MASK, 32-1);
|
||||||
} else if (insn & L16_MASK) {
|
} else if (insn & L16_MASK) {
|
||||||
ef->a_reg[regno] = spi_ramread(spi1, excvaddr & 0x1ffff, 16-1);
|
ef->a_reg[regno] = spi_ramread(spi1, excvaddr & VM_OFFSET_MASK, 16-1);
|
||||||
if ((insn & SIGNED_MASK ) && (ef->a_reg[regno] & 0x8000))
|
if ((insn & SIGNED_MASK ) && (ef->a_reg[regno] & 0x8000))
|
||||||
ef->a_reg[regno] |= 0xffff0000;
|
ef->a_reg[regno] |= 0xffff0000;
|
||||||
} else {
|
} else {
|
||||||
ef->a_reg[regno] = spi_ramread(spi1, excvaddr & 0x1ffff, 8-1);
|
ef->a_reg[regno] = spi_ramread(spi1, excvaddr & VM_OFFSET_MASK, 8-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -389,6 +391,11 @@ void install_vm_exception_handler()
|
|||||||
__vm_cache_line[cache_ways - 1].next = NULL;
|
__vm_cache_line[cache_ways - 1].next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Our umm_malloc configuration can only support a maximum of 256K RAM. A
|
||||||
|
// change would affect the block size of all heaps, and a larger block size
|
||||||
|
// would result in wasted space in the smaller heaps.
|
||||||
|
static_assert(MMU_EXTERNAL_HEAP <= 256, "Heap size must not exceed 256K");
|
||||||
|
|
||||||
// Hook into memory manager
|
// Hook into memory manager
|
||||||
umm_init_vm( (void *)0x10000000, MMU_EXTERNAL_HEAP * 1024);
|
umm_init_vm( (void *)0x10000000, MMU_EXTERNAL_HEAP * 1024);
|
||||||
}
|
}
|
||||||
|
@@ -55,7 +55,7 @@ extern "C" void enablePhaseLockedWaveform (void)
|
|||||||
|
|
||||||
// No-op calls to override the PWM implementation
|
// No-op calls to override the PWM implementation
|
||||||
extern "C" void _setPWMFreq_weak(uint32_t freq) { (void) freq; }
|
extern "C" void _setPWMFreq_weak(uint32_t freq) { (void) freq; }
|
||||||
extern "C" bool _stopPWM_weak(int pin) { (void) pin; return false; }
|
extern "C" IRAM_ATTR bool _stopPWM_weak(int pin) { (void) pin; return false; }
|
||||||
extern "C" bool _setPWM_weak(int pin, uint32_t val, uint32_t range) { (void) pin; (void) val; (void) range; return false; }
|
extern "C" bool _setPWM_weak(int pin, uint32_t val, uint32_t range) { (void) pin; (void) val; (void) range; return false; }
|
||||||
|
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ int startWaveformClockCycles_weak(uint8_t pin, uint32_t highCcys, uint32_t lowCc
|
|||||||
}
|
}
|
||||||
std::atomic_thread_fence(std::memory_order_acq_rel);
|
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||||
while (waveform.toSetBits) {
|
while (waveform.toSetBits) {
|
||||||
delay(0); // Wait for waveform to update
|
esp_yield(); // Wait for waveform to update
|
||||||
std::atomic_thread_fence(std::memory_order_acquire);
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@@ -40,6 +40,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <coredecls.h>
|
||||||
#include "ets_sys.h"
|
#include "ets_sys.h"
|
||||||
#include "core_esp8266_waveform.h"
|
#include "core_esp8266_waveform.h"
|
||||||
#include "user_interface.h"
|
#include "user_interface.h"
|
||||||
@@ -162,7 +163,7 @@ static IRAM_ATTR void _notifyPWM(PWMState *p, bool idle) {
|
|||||||
forceTimerInterrupt();
|
forceTimerInterrupt();
|
||||||
while (pwmState.pwmUpdate) {
|
while (pwmState.pwmUpdate) {
|
||||||
if (idle) {
|
if (idle) {
|
||||||
delay(0);
|
esp_yield();
|
||||||
}
|
}
|
||||||
MEMBARRIER();
|
MEMBARRIER();
|
||||||
}
|
}
|
||||||
@@ -260,7 +261,7 @@ IRAM_ATTR bool _stopPWM_weak(uint8_t pin) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
static bool _stopPWM_bound(uint8_t pin) __attribute__((weakref("_stopPWM_weak")));
|
static bool _stopPWM_bound(uint8_t pin) __attribute__((weakref("_stopPWM_weak")));
|
||||||
bool _stopPWM(uint8_t pin) {
|
IRAM_ATTR bool _stopPWM(uint8_t pin) {
|
||||||
return _stopPWM_bound(pin);
|
return _stopPWM_bound(pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,8 +373,8 @@ int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t
|
|||||||
if (wvfState.waveformEnabled & mask) {
|
if (wvfState.waveformEnabled & mask) {
|
||||||
// Make sure no waveform changes are waiting to be applied
|
// Make sure no waveform changes are waiting to be applied
|
||||||
while (wvfState.waveformToChange) {
|
while (wvfState.waveformToChange) {
|
||||||
delay(0); // Wait for waveform to update
|
esp_yield(); // Wait for waveform to update
|
||||||
// No mem barrier here, the call to a global function implies global state updated
|
MEMBARRIER();
|
||||||
}
|
}
|
||||||
wvfState.waveformNewHigh = timeHighCycles;
|
wvfState.waveformNewHigh = timeHighCycles;
|
||||||
wvfState.waveformNewLow = timeLowCycles;
|
wvfState.waveformNewLow = timeLowCycles;
|
||||||
@@ -392,8 +393,8 @@ int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t
|
|||||||
initTimer();
|
initTimer();
|
||||||
forceTimerInterrupt();
|
forceTimerInterrupt();
|
||||||
while (wvfState.waveformToEnable) {
|
while (wvfState.waveformToEnable) {
|
||||||
delay(0); // Wait for waveform to update
|
esp_yield(); // Wait for waveform to update
|
||||||
// No mem barrier here, the call to a global function implies global state updated
|
MEMBARRIER();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,37 +23,18 @@
|
|||||||
#include "ets_sys.h"
|
#include "ets_sys.h"
|
||||||
#include "osapi.h"
|
#include "osapi.h"
|
||||||
#include "user_interface.h"
|
#include "user_interface.h"
|
||||||
#include "cont.h"
|
#include "coredecls.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
extern void ets_delay_us(uint32_t us);
|
|
||||||
extern void esp_schedule();
|
|
||||||
extern void esp_yield();
|
|
||||||
|
|
||||||
static os_timer_t delay_timer;
|
|
||||||
static os_timer_t micros_overflow_timer;
|
static os_timer_t micros_overflow_timer;
|
||||||
static uint32_t micros_at_last_overflow_tick = 0;
|
static uint32_t micros_at_last_overflow_tick = 0;
|
||||||
static uint32_t micros_overflow_count = 0;
|
static uint32_t micros_overflow_count = 0;
|
||||||
#define ONCE 0
|
#define ONCE 0
|
||||||
#define REPEAT 1
|
#define REPEAT 1
|
||||||
|
|
||||||
void delay_end(void* arg) {
|
|
||||||
(void) arg;
|
|
||||||
esp_schedule();
|
|
||||||
}
|
|
||||||
|
|
||||||
void __delay(unsigned long ms) {
|
void __delay(unsigned long ms) {
|
||||||
if(ms) {
|
esp_delay(ms);
|
||||||
os_timer_setfn(&delay_timer, (os_timer_func_t*) &delay_end, 0);
|
|
||||||
os_timer_arm(&delay_timer, ms, ONCE);
|
|
||||||
} else {
|
|
||||||
esp_schedule();
|
|
||||||
}
|
|
||||||
esp_yield();
|
|
||||||
if(ms) {
|
|
||||||
os_timer_disarm(&delay_timer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void delay(unsigned long ms) __attribute__ ((weak, alias("__delay")));
|
void delay(unsigned long ms) __attribute__ ((weak, alias("__delay")));
|
||||||
@@ -69,8 +50,8 @@ void micros_overflow_tick(void* arg) {
|
|||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// millis() 'magic multiplier' approximation
|
// millis() 'magic multiplier' approximation
|
||||||
//
|
//
|
||||||
// This function corrects the cumlative (296us / usec overflow) drift
|
// This function corrects the cumulative (296us / usec overflow) drift
|
||||||
// seen in the orignal 'millis()' function.
|
// seen in the original 'millis()' function.
|
||||||
//
|
//
|
||||||
// Input:
|
// Input:
|
||||||
// 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF
|
// 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF
|
||||||
|
@@ -6,6 +6,18 @@
|
|||||||
#define ARDUINO_ESP8266_GIT_DESC unspecified
|
#define ARDUINO_ESP8266_GIT_DESC unspecified
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARDUINO_ESP8266_MAJOR
|
||||||
|
#define ARDUINO_ESP8266_MAJOR 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARDUINO_ESP8266_MINOR
|
||||||
|
#define ARDUINO_ESP8266_MINOR 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARDUINO_ESP8266_REVISION
|
||||||
|
#define ARDUINO_ESP8266_REVISION 0
|
||||||
|
#endif
|
||||||
|
|
||||||
// ARDUINO_ESP8266_RELEASE is defined for released versions as a string containing the version name, i.e. "2_3_0_RC1"
|
// ARDUINO_ESP8266_RELEASE is defined for released versions as a string containing the version name, i.e. "2_3_0_RC1"
|
||||||
// ARDUINO_ESP8266_RELEASE is used in the core internally. Please use ESP.getCoreVersion() function instead.
|
// ARDUINO_ESP8266_RELEASE is used in the core internally. Please use ESP.getCoreVersion() function instead.
|
||||||
|
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
#ifndef __COREDECLS_H
|
#ifndef __COREDECLS_H
|
||||||
#define __COREDECLS_H
|
#define __COREDECLS_H
|
||||||
|
|
||||||
|
#include "core_esp8266_features.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -13,10 +15,13 @@ extern "C" {
|
|||||||
#include <cont.h> // g_pcont declaration
|
#include <cont.h> // g_pcont declaration
|
||||||
|
|
||||||
bool can_yield();
|
bool can_yield();
|
||||||
void esp_yield();
|
void esp_suspend();
|
||||||
|
void esp_delay(unsigned long ms);
|
||||||
void esp_schedule();
|
void esp_schedule();
|
||||||
|
void esp_yield();
|
||||||
void tune_timeshift64 (uint64_t now_us);
|
void tune_timeshift64 (uint64_t now_us);
|
||||||
void disable_extra4k_at_link_time (void) __attribute__((noinline));
|
void disable_extra4k_at_link_time (void) __attribute__((noinline));
|
||||||
|
void enable_wifi_enterprise_patch(void) __attribute__((noinline));
|
||||||
bool sntp_set_timezone_in_seconds(int32_t timezone);
|
bool sntp_set_timezone_in_seconds(int32_t timezone);
|
||||||
void __disableWiFiAtBootTime (void) __attribute__((noinline));
|
void __disableWiFiAtBootTime (void) __attribute__((noinline));
|
||||||
void __real_system_restart_local() __attribute__((noreturn));
|
void __real_system_restart_local() __attribute__((noreturn));
|
||||||
@@ -32,9 +37,45 @@ uint32_t crc32 (const void* data, size_t length, uint32_t crc = 0xffffffff);
|
|||||||
using BoolCB = std::function<void(bool)>;
|
using BoolCB = std::function<void(bool)>;
|
||||||
using TrivialCB = std::function<void()>;
|
using TrivialCB = std::function<void()>;
|
||||||
|
|
||||||
|
void settimeofday_cb (BoolCB&& cb);
|
||||||
void settimeofday_cb (const BoolCB& cb);
|
void settimeofday_cb (const BoolCB& cb);
|
||||||
void settimeofday_cb (const TrivialCB& cb);
|
void settimeofday_cb (const TrivialCB& cb);
|
||||||
|
|
||||||
|
// This overload of esp_suspend() performs the blocked callback whenever it is resumed,
|
||||||
|
// and if that returns true, it immediately suspends again.
|
||||||
|
template <typename T>
|
||||||
|
inline void esp_suspend(T&& blocked) {
|
||||||
|
do {
|
||||||
|
esp_suspend();
|
||||||
|
} while (blocked());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to delay until timeout_ms has expired since start_ms.
|
||||||
|
// Returns true if timeout_ms has completely expired on entry.
|
||||||
|
// Otherwise returns false after delaying for the relative
|
||||||
|
// remainder of timeout_ms, or an absolute intvl_ms, whichever is shorter.
|
||||||
|
// The delay may be asynchronously cancelled, before that timeout is reached.
|
||||||
|
bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uint32_t intvl_ms);
|
||||||
|
|
||||||
|
// This overload of esp_delay() delays for a duration of at most timeout_ms milliseconds.
|
||||||
|
// Whenever it is resumed, as well as every intvl_ms millisconds, it performs
|
||||||
|
// the blocked callback, and if that returns true, it keeps delaying for the remainder
|
||||||
|
// of the original timeout_ms period.
|
||||||
|
template <typename T>
|
||||||
|
inline void esp_delay(const uint32_t timeout_ms, T&& blocked, const uint32_t intvl_ms) {
|
||||||
|
const auto start_ms = millis();
|
||||||
|
while (!esp_try_delay(start_ms, timeout_ms, intvl_ms) && blocked()) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This overload of esp_delay() delays for a duration of at most timeout_ms milliseconds.
|
||||||
|
// Whenever it is resumed, it performs the blocked callback, and if that returns true,
|
||||||
|
// it keeps delaying for the remainder of the original timeout_ms period.
|
||||||
|
template <typename T>
|
||||||
|
inline void esp_delay(const uint32_t timeout_ms, T&& blocked) {
|
||||||
|
esp_delay(timeout_ms, std::forward<T>(blocked), timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
#endif // __COREDECLS_H
|
#endif // __COREDECLS_H
|
||||||
|
@@ -30,7 +30,7 @@ void __iamslow(const char* what)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
IRAM_ATTR
|
IRAM_ATTR
|
||||||
void hexdump(const void *mem, uint32_t len, uint8_t cols)
|
void hexdump(const void* mem, uint32_t len, uint8_t cols)
|
||||||
{
|
{
|
||||||
const char* src = (const char*)mem;
|
const char* src = (const char*)mem;
|
||||||
os_printf("\n[HEXDUMP] Address: %p len: 0x%X (%d)", src, len, len);
|
os_printf("\n[HEXDUMP] Address: %p len: 0x%X (%d)", src, len, len);
|
||||||
@@ -54,7 +54,7 @@ void hexdump(const void *mem, uint32_t len, uint8_t cols)
|
|||||||
}
|
}
|
||||||
src += linesize;
|
src += linesize;
|
||||||
len -= linesize;
|
len -= linesize;
|
||||||
yield();
|
optimistic_yield(10000);
|
||||||
}
|
}
|
||||||
os_printf("\n");
|
os_printf("\n");
|
||||||
}
|
}
|
||||||
|
@@ -5,44 +5,54 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_CORE
|
#ifdef DEBUG_ESP_CORE
|
||||||
#define DEBUGV(fmt, ...) ::printf((PGM_P)PSTR(fmt), ## __VA_ARGS__)
|
#define DEBUGV(fmt, ...) ::printf((PGM_P)PSTR(fmt), ##__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DEBUGV
|
#ifndef DEBUGV
|
||||||
#define DEBUGV(...) do { (void)0; } while (0)
|
#define DEBUGV(...) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
(void)0; \
|
||||||
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" void hexdump(const void *mem, uint32_t len, uint8_t cols = 16);
|
extern "C" void hexdump(const void* mem, uint32_t len, uint8_t cols = 16);
|
||||||
#else
|
#else
|
||||||
void hexdump(const void *mem, uint32_t len, uint8_t cols);
|
void hexdump(const void* mem, uint32_t len, uint8_t cols);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C"
|
||||||
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void __unhandled_exception(const char *str) __attribute__((noreturn));
|
void __unhandled_exception(const char* str) __attribute__((noreturn));
|
||||||
void __panic_func(const char* file, int line, const char* func) __attribute__((noreturn));
|
void __panic_func(const char* file, int line, const char* func) __attribute__((noreturn));
|
||||||
#define panic() __panic_func(PSTR(__FILE__), __LINE__, __func__)
|
#define panic() __panic_func(PSTR(__FILE__), __LINE__, __func__)
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_CORE
|
#ifdef DEBUG_ESP_CORE
|
||||||
extern void __iamslow(const char* what);
|
extern void __iamslow(const char* what);
|
||||||
#define IAMSLOW() \
|
#define IAMSLOW() \
|
||||||
do { \
|
do \
|
||||||
static bool once = false; \
|
{ \
|
||||||
if (!once) { \
|
static bool once = false; \
|
||||||
once = true; \
|
if (!once) \
|
||||||
__iamslow((PGM_P)FPSTR(__FUNCTION__)); \
|
{ \
|
||||||
} \
|
once = true; \
|
||||||
|
__iamslow((PGM_P)FPSTR(__FUNCTION__)); \
|
||||||
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
#else
|
#else
|
||||||
#define IAMSLOW() do { (void)0; } while (0)
|
#define IAMSLOW() \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
(void)0; \
|
||||||
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif // ARD_DEBUG_H
|
||||||
#endif//ARD_DEBUG_H
|
|
||||||
|
@@ -163,7 +163,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16];
|
|||||||
#define TCIS 8 //Interrupt Status
|
#define TCIS 8 //Interrupt Status
|
||||||
#define TCTE 7 //Timer Enable
|
#define TCTE 7 //Timer Enable
|
||||||
#define TCAR 6 //AutoReload (restart timer when condition is reached)
|
#define TCAR 6 //AutoReload (restart timer when condition is reached)
|
||||||
#define TCPD 2 //Prescale Devider (2bit) 0:1(12.5ns/tick), 1:16(0.2us/tick), 2/3:256(3.2us/tick)
|
#define TCPD 2 //Prescale Divider (2bit) 0:1(12.5ns/tick), 1:16(0.2us/tick), 2/3:256(3.2us/tick)
|
||||||
#define TCIT 0 //Interrupt Type 0:edge, 1:level
|
#define TCIT 0 //Interrupt Type 0:edge, 1:level
|
||||||
|
|
||||||
//RTC Registers
|
//RTC Registers
|
||||||
@@ -255,7 +255,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16];
|
|||||||
//UART STATUS Registers Bits
|
//UART STATUS Registers Bits
|
||||||
#define USTX 31 //TX PIN Level (Doesn't seem to work, always reads as 0 for both uarts. HW bug? Possible workaround: Enable loopback UxC0 |= 1<<UCLBE and read USRXD, see https://github.com/esp8266/Arduino/issues/7256 for discussion.)
|
#define USTX 31 //TX PIN Level (Doesn't seem to work, always reads as 0 for both uarts. HW bug? Possible workaround: Enable loopback UxC0 |= 1<<UCLBE and read USRXD, see https://github.com/esp8266/Arduino/issues/7256 for discussion.)
|
||||||
#define USRTS 30 //RTS PIN Level
|
#define USRTS 30 //RTS PIN Level
|
||||||
#define USDTR 39 //DTR PIN Level
|
#define USDTR 29 //DTR PIN Level
|
||||||
#define USTXC 16 //TX FIFO COUNT (8bit)
|
#define USTXC 16 //TX FIFO COUNT (8bit)
|
||||||
#define USRXD 15 //RX PIN Level
|
#define USRXD 15 //RX PIN Level
|
||||||
#define USCTS 14 //CTS PIN Level
|
#define USCTS 14 //CTS PIN Level
|
||||||
@@ -271,7 +271,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16];
|
|||||||
#define UCRXI 19 //Invert RX
|
#define UCRXI 19 //Invert RX
|
||||||
#define UCTXRST 18 //Reset TX FIFO
|
#define UCTXRST 18 //Reset TX FIFO
|
||||||
#define UCRXRST 17 //Reset RX FIFO
|
#define UCRXRST 17 //Reset RX FIFO
|
||||||
#define UCTXHFE 15 //TX Harware Flow Enable
|
#define UCTXHFE 15 //TX Hardware Flow Enable
|
||||||
#define UCLBE 14 //LoopBack Enable
|
#define UCLBE 14 //LoopBack Enable
|
||||||
#define UCBRK 8 //Send Break on the TX line
|
#define UCBRK 8 //Send Break on the TX line
|
||||||
#define UCSWDTR 7 //Set this bit to assert DTR
|
#define UCSWDTR 7 //Set this bit to assert DTR
|
||||||
@@ -283,11 +283,11 @@ extern volatile uint32_t* const esp8266_gpioToFn[16];
|
|||||||
|
|
||||||
//UART CONF1 Registers Bits
|
//UART CONF1 Registers Bits
|
||||||
#define UCTOE 31 //RX TimeOut Enable
|
#define UCTOE 31 //RX TimeOut Enable
|
||||||
#define UCTOT 24 //RX TimeOut Treshold (7bit)
|
#define UCTOT 24 //RX TimeOut Threshold (7bit)
|
||||||
#define UCRXHFE 23 //RX Harware Flow Enable
|
#define UCRXHFE 23 //RX Hardware Flow Enable
|
||||||
#define UCRXHFT 16 //RX Harware Flow Treshold (7bit)
|
#define UCRXHFT 16 //RX Hardware Flow Threshold (7bit)
|
||||||
#define UCFET 8 //TX FIFO Empty Treshold (7bit)
|
#define UCFET 8 //TX FIFO Empty Threshold (7bit)
|
||||||
#define UCFFT 0 //RX FIFO Full Treshold (7bit)
|
#define UCFFT 0 //RX FIFO Full Threshold (7bit)
|
||||||
|
|
||||||
//WDT Feed (the dog) Register
|
//WDT Feed (the dog) Register
|
||||||
#define WDTFEED ESP8266_REG(0x914)
|
#define WDTFEED ESP8266_REG(0x914)
|
||||||
@@ -372,7 +372,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16];
|
|||||||
#define SPI1E3 ESP8266_REG(0x1FC)
|
#define SPI1E3 ESP8266_REG(0x1FC)
|
||||||
#define SPI1W(p) ESP8266_REG(0x140 + ((p & 0xF) * 4))
|
#define SPI1W(p) ESP8266_REG(0x140 + ((p & 0xF) * 4))
|
||||||
|
|
||||||
//SPI0, SPI1 & I2S Interupt Register
|
//SPI0, SPI1 & I2S Interrupt Register
|
||||||
#define SPIIR ESP8266_DREG(0x20)
|
#define SPIIR ESP8266_DREG(0x20)
|
||||||
#define SPII0 4 //SPI0 Interrupt
|
#define SPII0 4 //SPI0 Interrupt
|
||||||
#define SPII1 7 //SPI1 Interrupt
|
#define SPII1 7 //SPI1 Interrupt
|
||||||
|
@@ -52,12 +52,22 @@ calls to ets_install_putc1().
|
|||||||
*/
|
*/
|
||||||
extern void uart_buff_switch(uint8_t);
|
extern void uart_buff_switch(uint8_t);
|
||||||
|
|
||||||
|
/*
|
||||||
|
ROM function, ets_install_uart_printf, is used to installs the internal ROM
|
||||||
|
putc1 driver used to print on UART0 or UART1. The installed driver is use by ets_printf.
|
||||||
|
Side note, ets_install_uart_printf just happens to return the address of the
|
||||||
|
internal putc1 driver installed.
|
||||||
|
*/
|
||||||
|
extern void ets_install_uart_printf(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ROM function, ets_uart_printf(), prints on the UART selected by
|
ROM function, ets_uart_printf(), prints on the UART selected by
|
||||||
uart_buff_switch(). Supported format options are the same as vprintf(). Also
|
uart_buff_switch(). Supported format options are the same as vprintf(). Also
|
||||||
has cooked newline behavior. No flash format/string support; however, ISR safe.
|
has cooked newline behavior. No flash format/string support; however, ISR safe.
|
||||||
Also, uses a static function in ROM to print characters which is only
|
It also uses a static function in ROM to print characters. The UART selection
|
||||||
controlled by uart_buff_switch().
|
is handled by a prior call to uart_buff_switch(). An advantage over ets_printf,
|
||||||
|
this call is not affected by calls made to ets_install_putc1 or
|
||||||
|
ets_install_putc2.
|
||||||
*/
|
*/
|
||||||
extern int ets_uart_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
|
extern int ets_uart_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
|
||||||
|
|
||||||
@@ -185,7 +195,7 @@ typedef void (*fn_c_exception_handler_t)(struct __exception_frame *ef, int cause
|
|||||||
_xtos_c_handler_table[]. It is present when an exception handler has not been
|
_xtos_c_handler_table[]. It is present when an exception handler has not been
|
||||||
registered. It simply consist of a single instruction, `ret`.
|
registered. It simply consist of a single instruction, `ret`.
|
||||||
It is also internally used by `_xtos_set_exception_handler(cause, NULL)` to
|
It is also internally used by `_xtos_set_exception_handler(cause, NULL)` to
|
||||||
reset a "C" exception handler back to the unhandled state. The coresponding
|
reset a "C" exception handler back to the unhandled state. The corresponding
|
||||||
`_xtos_exc_handler_table` entry will be set to `_xtos_unhandled_exception`.
|
`_xtos_exc_handler_table` entry will be set to `_xtos_unhandled_exception`.
|
||||||
Note, if nesting handlers is desired this must be implemented in the new "C"
|
Note, if nesting handlers is desired this must be implemented in the new "C"
|
||||||
exception handler(s) being registered.
|
exception handler(s) being registered.
|
||||||
@@ -236,7 +246,6 @@ extern void Cache_Read_Disable();
|
|||||||
extern int32_t system_func1(uint32_t);
|
extern int32_t system_func1(uint32_t);
|
||||||
extern void clockgate_watchdog(uint32_t);
|
extern void clockgate_watchdog(uint32_t);
|
||||||
extern void pm_open_rf();
|
extern void pm_open_rf();
|
||||||
extern void ets_install_uart_printf(uint32_t uart_no);
|
|
||||||
extern void UartDwnLdProc(uint8_t* ram_addr, uint32_t size, void (**user_start_ptr)());
|
extern void UartDwnLdProc(uint8_t* ram_addr, uint32_t size, void (**user_start_ptr)());
|
||||||
extern int boot_from_flash();
|
extern int boot_from_flash();
|
||||||
extern void ets_run() __attribute__((noreturn));
|
extern void ets_run() __attribute__((noreturn));
|
||||||
|
@@ -47,7 +47,7 @@
|
|||||||
* https://github.com/qca/open-ath9k-htc-firmware/blob/master/sboot/magpie_1_1/sboot/athos/src/xtos/exc-sethandler.c
|
* https://github.com/qca/open-ath9k-htc-firmware/blob/master/sboot/magpie_1_1/sboot/athos/src/xtos/exc-sethandler.c
|
||||||
*
|
*
|
||||||
* It has been revised to use Arduino ESP8266 core includes, types, and
|
* It has been revised to use Arduino ESP8266 core includes, types, and
|
||||||
* formating.
|
* formatting.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* exc-sethandler.c - register an exception handler in XTOS */
|
/* exc-sethandler.c - register an exception handler in XTOS */
|
||||||
|
@@ -68,3 +68,13 @@ int32_t flash_hal_erase(uint32_t addr, uint32_t size) {
|
|||||||
}
|
}
|
||||||
return FLASH_HAL_OK;
|
return FLASH_HAL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if FLASH_MAP_SUPPORT
|
||||||
|
|
||||||
|
// default weak configuration:
|
||||||
|
FLASH_MAP_SETUP_CONFIG_ATTR(__attribute__((weak)), FLASH_MAP_OTA_FS)
|
||||||
|
|
||||||
|
// can be overridden by user with:
|
||||||
|
//FLASH_MAP_SETUP_CONFIG(FLASH_MAP_some_configuration)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@@ -24,18 +24,58 @@
|
|||||||
License along with this library; if not, write to the Free Software
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
#ifdef ARDUINO
|
|
||||||
extern "C" uint32_t _FS_start;
|
|
||||||
extern "C" uint32_t _FS_end;
|
|
||||||
extern "C" uint32_t _FS_page;
|
|
||||||
extern "C" uint32_t _FS_block;
|
|
||||||
|
|
||||||
#define FS_PHYS_ADDR ((uint32_t) (&_FS_start) - 0x40200000)
|
#ifdef __cplusplus
|
||||||
#define FS_PHYS_SIZE ((uint32_t) (&_FS_end) - (uint32_t) (&_FS_start))
|
extern "C" {
|
||||||
#define FS_PHYS_PAGE ((uint32_t) &_FS_page)
|
|
||||||
#define FS_PHYS_BLOCK ((uint32_t) &_FS_block)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if FLASH_MAP_SUPPORT
|
||||||
|
#include <FlashMap.h>
|
||||||
|
|
||||||
|
extern uint32_t spi_flash_get_id (void); // <user_interface.h>
|
||||||
|
extern void flashinit(void);
|
||||||
|
extern uint32_t __flashindex;
|
||||||
|
extern const flash_map_s __flashdesc[];
|
||||||
|
|
||||||
|
#define FLASH_MAP_SETUP_CONFIG(conf) FLASH_MAP_SETUP_CONFIG_ATTR(,conf)
|
||||||
|
#define FLASH_MAP_SETUP_CONFIG_ATTR(attr, conf...) \
|
||||||
|
const flash_map_s __flashdesc[] PROGMEM = conf; \
|
||||||
|
void flashinit (void) attr; \
|
||||||
|
void flashinit (void) \
|
||||||
|
{ \
|
||||||
|
uint32_t flash_chip_size_kb = 1 << (((spi_flash_get_id() >> 16) & 0xff) - 10); \
|
||||||
|
for (__flashindex = 0; __flashindex < sizeof(__flashdesc) / sizeof(__flashdesc[0]); __flashindex++) \
|
||||||
|
if (__flashdesc[__flashindex].flash_size_kb == flash_chip_size_kb) \
|
||||||
|
return; \
|
||||||
|
panic(); /* configuration not found */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EEPROM_start (__flashdesc[__flashindex].eeprom_start)
|
||||||
|
#define FS_start (__flashdesc[__flashindex].fs_start)
|
||||||
|
#define FS_end (__flashdesc[__flashindex].fs_end)
|
||||||
|
#define FS_block (__flashdesc[__flashindex].fs_block_size)
|
||||||
|
#define FS_page (__flashdesc[__flashindex].fs_page_size)
|
||||||
|
|
||||||
|
#else // !FLASH_MAP_SUPPORT
|
||||||
|
|
||||||
|
extern uint32_t _FS_start;
|
||||||
|
extern uint32_t _FS_end;
|
||||||
|
extern uint32_t _FS_page;
|
||||||
|
extern uint32_t _FS_block;
|
||||||
|
extern uint32_t _EEPROM_start;
|
||||||
|
#define EEPROM_start ((uint32_t)&_EEPROM_start)
|
||||||
|
#define FS_start ((uint32_t)&_FS_start)
|
||||||
|
#define FS_end ((uint32_t)&_FS_end)
|
||||||
|
#define FS_page ((uint32_t)&_FS_page)
|
||||||
|
#define FS_block ((uint32_t)&_FS_block)
|
||||||
|
|
||||||
|
#endif // FLASH_MAP_SUPPORT
|
||||||
|
|
||||||
|
#define FS_PHYS_ADDR ((uint32_t)FS_start - 0x40200000)
|
||||||
|
#define FS_PHYS_SIZE ((uint32_t)(FS_end - FS_start))
|
||||||
|
#define FS_PHYS_PAGE ((uint32_t)FS_page)
|
||||||
|
#define FS_PHYS_BLOCK ((uint32_t)FS_block)
|
||||||
|
|
||||||
// Return values of the following functions
|
// Return values of the following functions
|
||||||
#define FLASH_HAL_OK (0)
|
#define FLASH_HAL_OK (0)
|
||||||
#define FLASH_HAL_READ_ERROR (-1)
|
#define FLASH_HAL_READ_ERROR (-1)
|
||||||
@@ -46,4 +86,8 @@ extern int32_t flash_hal_write(uint32_t addr, uint32_t size, const uint8_t *src)
|
|||||||
extern int32_t flash_hal_erase(uint32_t addr, uint32_t size);
|
extern int32_t flash_hal_erase(uint32_t addr, uint32_t size);
|
||||||
extern int32_t flash_hal_read(uint32_t addr, uint32_t size, uint8_t *dst);
|
extern int32_t flash_hal_read(uint32_t addr, uint32_t size, uint8_t *dst);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // !defined(flash_hal_h)
|
#endif // !defined(flash_hal_h)
|
||||||
|
@@ -27,7 +27,7 @@ extern "C" {
|
|||||||
* @brief Initialize GDB stub, if present
|
* @brief Initialize GDB stub, if present
|
||||||
*
|
*
|
||||||
* By default, this function is a no-op. When GDBStub library is linked,
|
* By default, this function is a no-op. When GDBStub library is linked,
|
||||||
* this function is overriden and does necessary initialization of that library.
|
* this function is overridden and does necessary initialization of that library.
|
||||||
* Called early at startup.
|
* Called early at startup.
|
||||||
*/
|
*/
|
||||||
void gdb_init(void);
|
void gdb_init(void);
|
||||||
@@ -36,7 +36,7 @@ void gdb_init(void);
|
|||||||
* @brief Break into GDB, if present
|
* @brief Break into GDB, if present
|
||||||
*
|
*
|
||||||
* By default, this function is a no-op. When GDBStub library is linked,
|
* By default, this function is a no-op. When GDBStub library is linked,
|
||||||
* this function is overriden and triggers entry into the debugger, which
|
* this function is overridden and triggers entry into the debugger, which
|
||||||
* looks like a breakpoint hit.
|
* looks like a breakpoint hit.
|
||||||
*/
|
*/
|
||||||
void gdb_do_break(void);
|
void gdb_do_break(void);
|
||||||
@@ -45,7 +45,7 @@ void gdb_do_break(void);
|
|||||||
* @brief Check if GDB stub is present.
|
* @brief Check if GDB stub is present.
|
||||||
*
|
*
|
||||||
* By default, this function returns false. When GDBStub library is linked,
|
* By default, this function returns false. When GDBStub library is linked,
|
||||||
* this function is overriden and returns true. Can be used to check whether
|
* this function is overridden and returns true. Can be used to check whether
|
||||||
* GDB is used.
|
* GDB is used.
|
||||||
*
|
*
|
||||||
* @return true if GDB stub is present
|
* @return true if GDB stub is present
|
||||||
@@ -58,7 +58,7 @@ bool gdb_present(void);
|
|||||||
* @brief Check if GDB is installing a putc1 callback.
|
* @brief Check if GDB is installing a putc1 callback.
|
||||||
*
|
*
|
||||||
* By default, this function returns false. When GDBStub library is linked,
|
* By default, this function returns false. When GDBStub library is linked,
|
||||||
* this function is overriden and returns true.
|
* this function is overridden and returns true.
|
||||||
*
|
*
|
||||||
* @return true if GDB is installing a putc1 callback
|
* @return true if GDB is installing a putc1 callback
|
||||||
*/
|
*/
|
||||||
@@ -69,7 +69,7 @@ bool gdbstub_has_putc1_control(void);
|
|||||||
* @param func function GDB will proxy putc1 data to
|
* @param func function GDB will proxy putc1 data to
|
||||||
*
|
*
|
||||||
* By default, this function is a no-op. When GDBStub library is linked,
|
* By default, this function is a no-op. When GDBStub library is linked,
|
||||||
* this function is overriden and sets GDB stub's secondary putc1 callback to
|
* this function is overridden and sets GDB stub's secondary putc1 callback to
|
||||||
* func. When GDB stub is linked, but a GDB session is not current attached,
|
* func. When GDB stub is linked, but a GDB session is not current attached,
|
||||||
* then GDB stub will pass putc1 chars directly to this function.
|
* then GDB stub will pass putc1 chars directly to this function.
|
||||||
*/
|
*/
|
||||||
@@ -79,7 +79,7 @@ void gdbstub_set_putc1_callback(void (*func)(char));
|
|||||||
* @brief Check if GDB is installing a uart0 isr callback.
|
* @brief Check if GDB is installing a uart0 isr callback.
|
||||||
*
|
*
|
||||||
* By default, this function returns false. When GDBStub library is linked,
|
* By default, this function returns false. When GDBStub library is linked,
|
||||||
* this function is overriden and returns true.
|
* this function is overridden and returns true.
|
||||||
*
|
*
|
||||||
* @return true if GDB is installing a uart0 isr callback
|
* @return true if GDB is installing a uart0 isr callback
|
||||||
*/
|
*/
|
||||||
@@ -90,7 +90,7 @@ bool gdbstub_has_uart_isr_control(void);
|
|||||||
* @param func function GDB will proxy uart0 isr data to
|
* @param func function GDB will proxy uart0 isr data to
|
||||||
*
|
*
|
||||||
* By default, this function is a no-op. When GDBStub library is linked,
|
* By default, this function is a no-op. When GDBStub library is linked,
|
||||||
* this function is overriden and sets GDB stub's secondary uart0 isr callback
|
* this function is overridden and sets GDB stub's secondary uart0 isr callback
|
||||||
* to func. When GDB stub is linked, but a GDB session is not current attached,
|
* to func. When GDB stub is linked, but a GDB session is not current attached,
|
||||||
* then GDB stub will pass uart0 isr data back to this function.
|
* then GDB stub will pass uart0 isr data back to this function.
|
||||||
*/
|
*/
|
||||||
@@ -101,7 +101,7 @@ void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg);
|
|||||||
* @param c character to write
|
* @param c character to write
|
||||||
*
|
*
|
||||||
* By default, this function is a no-op. When GDBStub library is linked,
|
* By default, this function is a no-op. When GDBStub library is linked,
|
||||||
* this function is overriden and writes a char to either the GDB session on
|
* this function is overridden and writes a char to either the GDB session on
|
||||||
* uart0 or directly to uart0 if not GDB session is attached.
|
* uart0 or directly to uart0 if not GDB session is attached.
|
||||||
*/
|
*/
|
||||||
void gdbstub_write_char(char c);
|
void gdbstub_write_char(char c);
|
||||||
@@ -112,7 +112,7 @@ void gdbstub_write_char(char c);
|
|||||||
* @param size length of buffer
|
* @param size length of buffer
|
||||||
*
|
*
|
||||||
* By default, this function is a no-op. When GDBStub library is linked,
|
* By default, this function is a no-op. When GDBStub library is linked,
|
||||||
* this function is overriden and writes a buffer to either the GDB session on
|
* this function is overridden and writes a buffer to either the GDB session on
|
||||||
* uart0 or directly to uart0 if not GDB session is attached.
|
* uart0 or directly to uart0 if not GDB session is attached.
|
||||||
*/
|
*/
|
||||||
void gdbstub_write(const char* buf, size_t size);
|
void gdbstub_write(const char* buf, size_t size);
|
||||||
|
@@ -5,6 +5,13 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "umm_malloc/umm_malloc.h"
|
#include "umm_malloc/umm_malloc.h"
|
||||||
|
extern "C" size_t umm_umul_sat(const size_t a, const size_t b);
|
||||||
|
|
||||||
|
// z2EapFree: See wpa2_eap_patch.cpp for details
|
||||||
|
extern "C" void z2EapFree(void *ptr, const char* file, int line) __attribute__((weak, alias("vPortFree"), nothrow));
|
||||||
|
// I don't understand all the compiler noise around this alias.
|
||||||
|
// Adding "__attribute__ ((nothrow))" seems to resolve the issue.
|
||||||
|
// This may be relevant: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81824
|
||||||
|
|
||||||
// Need FORCE_ALWAYS_INLINE to put HeapSelect class constructor/deconstructor in IRAM
|
// Need FORCE_ALWAYS_INLINE to put HeapSelect class constructor/deconstructor in IRAM
|
||||||
#define FORCE_ALWAYS_INLINE_HEAP_SELECT
|
#define FORCE_ALWAYS_INLINE_HEAP_SELECT
|
||||||
@@ -42,7 +49,7 @@ extern "C" {
|
|||||||
#define UMM_REALLOC_FL(p,s,f,l) realloc(p,s)
|
#define UMM_REALLOC_FL(p,s,f,l) realloc(p,s)
|
||||||
#define UMM_FREE_FL(p,f,l) free(p)
|
#define UMM_FREE_FL(p,f,l) free(p)
|
||||||
|
|
||||||
// STATIC_ALWAYS_INLINE only applys to the non-debug build path,
|
// STATIC_ALWAYS_INLINE only applies to the non-debug build path,
|
||||||
// it must not be enabled on the debug build path.
|
// it must not be enabled on the debug build path.
|
||||||
#define STATIC_ALWAYS_INLINE static ALWAYS_INLINE
|
#define STATIC_ALWAYS_INLINE static ALWAYS_INLINE
|
||||||
#endif
|
#endif
|
||||||
@@ -153,7 +160,7 @@ void* _calloc_r(struct _reent* unused, size_t count, size_t size)
|
|||||||
{
|
{
|
||||||
(void) unused;
|
(void) unused;
|
||||||
void *ret = calloc(count, size);
|
void *ret = calloc(count, size);
|
||||||
PTR_CHECK__LOG_LAST_FAIL(ret, count * size);
|
PTR_CHECK__LOG_LAST_FAIL(ret, umm_umul_sat(count, size));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +179,7 @@ void IRAM_ATTR print_loc(size_t size, const char* file, int line)
|
|||||||
DEBUG_HEAP_PRINTF(":oom(%d)@", (int)size);
|
DEBUG_HEAP_PRINTF(":oom(%d)@", (int)size);
|
||||||
|
|
||||||
bool inISR = ETS_INTR_WITHINISR();
|
bool inISR = ETS_INTR_WITHINISR();
|
||||||
if (inISR && (uint32_t)file >= 0x40200000) {
|
if (NULL == file || (inISR && (uint32_t)file >= 0x40200000)) {
|
||||||
DEBUG_HEAP_PRINTF("File: %p", file);
|
DEBUG_HEAP_PRINTF("File: %p", file);
|
||||||
} else if (!inISR && (uint32_t)file >= 0x40200000) {
|
} else if (!inISR && (uint32_t)file >= 0x40200000) {
|
||||||
char buf[strlen_P(file) + 1];
|
char buf[strlen_P(file) + 1];
|
||||||
@@ -226,10 +233,10 @@ void IRAM_ATTR print_oom_size(size_t size)
|
|||||||
b. Before, when the *alloc function creates a new, not modified, allocation.
|
b. Before, when the *alloc function creates a new, not modified, allocation.
|
||||||
|
|
||||||
In a free() or realloc() call, the focus is on their allocation. It is
|
In a free() or realloc() call, the focus is on their allocation. It is
|
||||||
checked 1st and reported on 1ST if an error exists. Full Posion Check is
|
checked 1st and reported on 1ST if an error exists. Full Poison Check is
|
||||||
done after.
|
done after.
|
||||||
|
|
||||||
For malloc(), calloc(), and zalloc() Full Posion Check is done 1st since
|
For malloc(), calloc(), and zalloc() Full Poison Check is done 1st since
|
||||||
these functions do not modify an existing allocation.
|
these functions do not modify an existing allocation.
|
||||||
*/
|
*/
|
||||||
void* IRAM_ATTR malloc(size_t size)
|
void* IRAM_ATTR malloc(size_t size)
|
||||||
@@ -247,8 +254,11 @@ void* IRAM_ATTR calloc(size_t count, size_t size)
|
|||||||
INTEGRITY_CHECK__ABORT();
|
INTEGRITY_CHECK__ABORT();
|
||||||
POISON_CHECK__ABORT();
|
POISON_CHECK__ABORT();
|
||||||
void* ret = UMM_CALLOC(count, size);
|
void* ret = UMM_CALLOC(count, size);
|
||||||
PTR_CHECK__LOG_LAST_FAIL(ret, count * size);
|
#if defined(DEBUG_ESP_OOM)
|
||||||
OOM_CHECK__PRINT_OOM(ret, size);
|
size_t total_size = umm_umul_sat(count, size);// For logging purposes
|
||||||
|
#endif
|
||||||
|
PTR_CHECK__LOG_LAST_FAIL(ret, total_size);
|
||||||
|
OOM_CHECK__PRINT_OOM(ret, total_size);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,8 +297,11 @@ void* IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char* file, i
|
|||||||
INTEGRITY_CHECK__PANIC_FL(file, line);
|
INTEGRITY_CHECK__PANIC_FL(file, line);
|
||||||
POISON_CHECK__PANIC_FL(file, line);
|
POISON_CHECK__PANIC_FL(file, line);
|
||||||
void* ret = UMM_CALLOC(count, size);
|
void* ret = UMM_CALLOC(count, size);
|
||||||
PTR_CHECK__LOG_LAST_FAIL_FL(ret, count * size, file, line);
|
#if defined(DEBUG_ESP_OOM)
|
||||||
OOM_CHECK__PRINT_LOC(ret, size, file, line);
|
size_t total_size = umm_umul_sat(count, size);
|
||||||
|
#endif
|
||||||
|
PTR_CHECK__LOG_LAST_FAIL_FL(ret, total_size, file, line);
|
||||||
|
OOM_CHECK__PRINT_LOC(ret, total_size, file, line);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -275,6 +275,7 @@
|
|||||||
#include <esp8266_peri.h>
|
#include <esp8266_peri.h>
|
||||||
#include <uart.h>
|
#include <uart.h>
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
|
#include "mmu_iram.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <user_interface.h>
|
#include <user_interface.h>
|
||||||
@@ -376,7 +377,7 @@ extern hwdt_info_t hwdt_info;
|
|||||||
#undef hwdt_info_
|
#undef hwdt_info_
|
||||||
#undef hwdt_info
|
#undef hwdt_info
|
||||||
#undef HWDT_VERIFY_HWDT_INFO
|
#undef HWDT_VERIFY_HWDT_INFO
|
||||||
static_assert(sizeof(hwdt_info_t) == sizeof(LOCAL_HWDT_INFO_T), "Local and include verison of hwdt_info_t do not match.");
|
static_assert(sizeof(hwdt_info_t) == sizeof(LOCAL_HWDT_INFO_T), "Local and include version of hwdt_info_t do not match.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -411,7 +412,7 @@ static_assert(sizeof(hwdt_info_t) == sizeof(LOCAL_HWDT_INFO_T), "Local and inclu
|
|||||||
#define CONT_STACK_A16_SZ (MK_ALIGN16_SZ(sizeof(cont_t)))
|
#define CONT_STACK_A16_SZ (MK_ALIGN16_SZ(sizeof(cont_t)))
|
||||||
/*
|
/*
|
||||||
* For WPS support, cont stack comes out of the user's heap address space.
|
* For WPS support, cont stack comes out of the user's heap address space.
|
||||||
* The the NONOS-SDK stack address is initialized before tbe reserved ROM stack
|
* The NONOS-SDK stack address is initialized before the reserved ROM stack
|
||||||
* space. In this configuration there is no extra 4K in the heap.
|
* space. In this configuration there is no extra 4K in the heap.
|
||||||
* Memory map: 0x3FFE8000, ..., (CONT_STACK), ..., (SYS), (ROM_STACK), 0x4000000
|
* Memory map: 0x3FFE8000, ..., (CONT_STACK), ..., (SYS), (ROM_STACK), 0x4000000
|
||||||
*
|
*
|
||||||
@@ -764,8 +765,8 @@ void adjust_uart_speed(uint32_t uart_divisor) {
|
|||||||
* stablilize, and let the remote receiver come to an idle state before
|
* stablilize, and let the remote receiver come to an idle state before
|
||||||
* continuing.
|
* continuing.
|
||||||
*
|
*
|
||||||
* Load a Rubout character for the final charcter shifting out to stop
|
* Load a Rubout character for the final character shifting out to stop
|
||||||
* the last charcter from getting crunched during the speed change.
|
* the last character from getting crunched during the speed change.
|
||||||
*
|
*
|
||||||
* The thinking is if the speed changes while shifting out, as long as the
|
* The thinking is if the speed changes while shifting out, as long as the
|
||||||
* start bit gets out before the change. The change will not be noticed
|
* start bit gets out before the change. The change will not be noticed
|
||||||
@@ -952,13 +953,13 @@ STATIC void IRAM_MAYBE handle_hwdt(void) {
|
|||||||
/* Print separate ctx: cont stack */
|
/* Print separate ctx: cont stack */
|
||||||
|
|
||||||
/* Check if cont stack is yielding to SYS */
|
/* Check if cont stack is yielding to SYS */
|
||||||
if (0 == hwdt_info.cont_integrity && 0 != g_pcont->pc_yield) {
|
if (0 == hwdt_info.cont_integrity && 0 != g_pcont->pc_suspend) {
|
||||||
ctx_cont_ptr = (const uint32_t *)((uintptr_t)g_pcont->sp_yield - 8u);
|
ctx_cont_ptr = (const uint32_t *)((uintptr_t)g_pcont->sp_suspend - 8u);
|
||||||
}
|
}
|
||||||
print_stack((uintptr_t)ctx_cont_ptr, (uintptr_t)g_pcont->stack_end, PRINT_STACK::CONT);
|
print_stack((uintptr_t)ctx_cont_ptr, (uintptr_t)g_pcont->stack_end, PRINT_STACK::CONT);
|
||||||
} else {
|
} else {
|
||||||
if (0 == hwdt_info.cont_integrity && 0 != g_pcont->pc_yield) {
|
if (0 == hwdt_info.cont_integrity && 0 != g_pcont->pc_suspend) {
|
||||||
ETS_PRINTF("\nCont stack is yielding. Active stack starts at 0x%08X.\n", (uint32_t)g_pcont->sp_yield - 8u);
|
ETS_PRINTF("\nCont stack is yielding. Active stack starts at 0x%08X.\n", (uint32_t)g_pcont->sp_suspend - 8u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1007,35 +1008,6 @@ STATIC void IRAM_MAYBE handle_hwdt(void) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Using Cache_Read_Enable/Cache_Read_Disable to reduce IRAM usage. Moved
|
|
||||||
* strings and most functions to flash. At this phase of the startup, "C++" has
|
|
||||||
* not initialized. So, we needed a local "C" function to handle printing from
|
|
||||||
* flash. For this, I grabbed a copy of umm_info_safe_printf_P.
|
|
||||||
*
|
|
||||||
* This reduced IRAM usage by ~1K and DRAM ~200 bytes.
|
|
||||||
*
|
|
||||||
* Inspiration for using Cache_Read_Enable came from reviewing rboot, zboot, and
|
|
||||||
* https://richard.burtons.org/2015/06/12/esp8266-cache_read_enable/.
|
|
||||||
* Additional insight can be gleemed from reviewing the ESP8266_RTOS_SDK.
|
|
||||||
* (eg. ../components/bootloader_support/src/bootloader_utility.c)
|
|
||||||
*/
|
|
||||||
#define ICACHE_SIZE_32 1
|
|
||||||
#define ICACHE_SIZE_16 0
|
|
||||||
|
|
||||||
extern "C" void Cache_Read_Disable(void);
|
|
||||||
extern "C" void Cache_Read_Enable(uint8_t map, uint8_t p, uint8_t v);
|
|
||||||
|
|
||||||
#ifndef USE_IRAM
|
|
||||||
static void IRAM_ATTR __attribute__((noinline)) handle_hwdt_icache() __attribute__((used));
|
|
||||||
void handle_hwdt_icache() {
|
|
||||||
Cache_Read_Enable(0, 0, ICACHE_SIZE_16);
|
|
||||||
handle_hwdt();
|
|
||||||
Cache_Read_Disable();
|
|
||||||
}
|
|
||||||
#endif // USE_IRAM
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(DEBUG_ESP_HWDT_DEV_DEBUG) && !defined(USE_IRAM)
|
#if defined(DEBUG_ESP_HWDT_DEV_DEBUG) && !defined(USE_IRAM)
|
||||||
static void printSanityCheck() {
|
static void printSanityCheck() {
|
||||||
ETS_PRINTF("\n\nsys_stack_first: %p\n", sys_stack_first);
|
ETS_PRINTF("\n\nsys_stack_first: %p\n", sys_stack_first);
|
||||||
@@ -1048,6 +1020,40 @@ static void printSanityCheck() {
|
|||||||
}
|
}
|
||||||
#endif //DEBUG_ESP_HWDT_DEV_DEBUG
|
#endif //DEBUG_ESP_HWDT_DEV_DEBUG
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Using Cache_Read_Enable/Cache_Read_Disable to reduce IRAM usage. Moved
|
||||||
|
* strings and most functions to flash. At this phase of the startup, "C++" has
|
||||||
|
* not initialized. So, we needed a local "C" function to handle printing from
|
||||||
|
* flash. For this, I grabbed a copy of umm_info_safe_printf_P.
|
||||||
|
*
|
||||||
|
* This reduced IRAM usage by ~1K and DRAM ~200 bytes.
|
||||||
|
*
|
||||||
|
* Inspiration for using Cache_Read_Enable came from reviewing rboot, zboot, and
|
||||||
|
* https://richard.burtons.org/2015/06/12/esp8266-cache_read_enable/.
|
||||||
|
* Additional insight can be gleemed from reviewing the ESP8266_RTOS_SDK.
|
||||||
|
* (eg. ../components/bootloader_support/src/bootloader_utility.c)
|
||||||
|
*
|
||||||
|
* The logic to use Cache_Read_Enable and Cache_Read_Disable has been
|
||||||
|
* generalized into a wrapper function, mmu_wrap_irom_fn, and moved to
|
||||||
|
* mmu_iram.cpp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
hwdt_pre_sdk_init() is the result of a hook for development diagnotics which
|
||||||
|
evolved and was generlized to run any optional diagnostic code supplied at
|
||||||
|
link time.
|
||||||
|
|
||||||
|
Summary of the hwdt_pre_sdk_init() runtime environment:
|
||||||
|
* The code can run from flash and use PROGMEM strings.
|
||||||
|
* All functions must be extern "C" type
|
||||||
|
* C/C++ runtime has not started. Structures have not been initialized and
|
||||||
|
should have the values prior to reboot. With the exception of hwdt_info,
|
||||||
|
which was updated before this call.
|
||||||
|
* You can reference hwdt_info.reset_reason to control the action of the diagnostic.
|
||||||
|
* The stack is on the SYS stack. You have about 3K available before you
|
||||||
|
overwrite ROM Data area.
|
||||||
|
* Printing will work best with ets_uart_printf and umm_info_safe_printf_P.
|
||||||
|
*/
|
||||||
void hwdt_pre_sdk_init(void) __attribute__((weak));
|
void hwdt_pre_sdk_init(void) __attribute__((weak));
|
||||||
void hwdt_pre_sdk_init(void) {
|
void hwdt_pre_sdk_init(void) {
|
||||||
#if defined(DEBUG_ESP_HWDT_DEV_DEBUG) && !defined(USE_IRAM)
|
#if defined(DEBUG_ESP_HWDT_DEV_DEBUG) && !defined(USE_IRAM)
|
||||||
@@ -1055,9 +1061,8 @@ void hwdt_pre_sdk_init(void) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void IRAM_ATTR __attribute__((noinline)) hwdt_pre_sdk_init_icache(void) __attribute__((used));
|
static void __attribute__((noinline)) hwdt_pre_sdk_init_icache(void) __attribute__((used));
|
||||||
void hwdt_pre_sdk_init_icache(void) {
|
void hwdt_pre_sdk_init_icache(void) {
|
||||||
Cache_Read_Enable(0, 0, ICACHE_SIZE_16);
|
|
||||||
#ifdef DEBUG_ESP_HWDT_UART_SPEED
|
#ifdef DEBUG_ESP_HWDT_UART_SPEED
|
||||||
const uint32_t uart_divisor = set_uart_speed(0, DEBUG_ESP_HWDT_UART_SPEED);
|
const uint32_t uart_divisor = set_uart_speed(0, DEBUG_ESP_HWDT_UART_SPEED);
|
||||||
#endif
|
#endif
|
||||||
@@ -1069,17 +1074,14 @@ void hwdt_pre_sdk_init_icache(void) {
|
|||||||
adjust_uart_speed(uart_divisor);
|
adjust_uart_speed(uart_divisor);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Cache_Read_Disable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
/*
|
/*
|
||||||
An asm function alternative to the function with inline asm at the #else. I
|
For app_entry_redefinable, use Basic ASM instead of "C" with Extended ASM. The
|
||||||
find the inline asm requires constant inspection to verify that the compiler
|
(inline) Extended ASM approach required constant inspection to verify that the
|
||||||
optimizer does not clobber needed registers, after small changes in code or
|
compiler's optimizer did not clobber needed registers or do something weird
|
||||||
compiler updates. Hints to the compiler don't always work for me. Last I
|
after minor changes in code or compiler updates. Also, I think Basic ASM is
|
||||||
checked, the inline version below was working.
|
the safer route when changing the stack pointer multiple times.
|
||||||
*/
|
*/
|
||||||
cont_t *hwdt_app_entry__cont_stack __attribute__((used)) = CONT_STACK;
|
cont_t *hwdt_app_entry__cont_stack __attribute__((used)) = CONT_STACK;
|
||||||
|
|
||||||
@@ -1089,8 +1091,10 @@ asm (
|
|||||||
".literal .g_pcont, g_pcont\n\t"
|
".literal .g_pcont, g_pcont\n\t"
|
||||||
".literal .pcont_stack, hwdt_app_entry__cont_stack\n\t"
|
".literal .pcont_stack, hwdt_app_entry__cont_stack\n\t"
|
||||||
".literal .sys_stack_first, sys_stack_first\n\t"
|
".literal .sys_stack_first, sys_stack_first\n\t"
|
||||||
|
".literal .umm_init, umm_init\n\t"
|
||||||
".literal .call_user_start, call_user_start\n\t"
|
".literal .call_user_start, call_user_start\n\t"
|
||||||
".literal .get_noextra4k_g_pcont, get_noextra4k_g_pcont\n\t"
|
".literal .get_noextra4k_g_pcont, get_noextra4k_g_pcont\n\t"
|
||||||
|
".literal .mmu_wrap_irom_fn, mmu_wrap_irom_fn\n\t"
|
||||||
".align 4\n\t"
|
".align 4\n\t"
|
||||||
".global app_entry_redefinable\n\t"
|
".global app_entry_redefinable\n\t"
|
||||||
".type app_entry_redefinable, @function\n\t"
|
".type app_entry_redefinable, @function\n\t"
|
||||||
@@ -1114,7 +1118,9 @@ asm (
|
|||||||
#ifdef USE_IRAM
|
#ifdef USE_IRAM
|
||||||
"call0 handle_hwdt\n\t"
|
"call0 handle_hwdt\n\t"
|
||||||
#else
|
#else
|
||||||
"call0 handle_hwdt_icache\n\t"
|
"l32r a0, .mmu_wrap_irom_fn\n\t"
|
||||||
|
"movi a2, handle_hwdt\n\t"
|
||||||
|
"callx0 a0\n\t"
|
||||||
#endif
|
#endif
|
||||||
/*
|
/*
|
||||||
* Use new calculated SYS stack from top.
|
* Use new calculated SYS stack from top.
|
||||||
@@ -1135,90 +1141,46 @@ asm (
|
|||||||
"l32r a13, .pcont_stack\n\t"
|
"l32r a13, .pcont_stack\n\t"
|
||||||
"l32r a0, .get_noextra4k_g_pcont\n\t"
|
"l32r a0, .get_noextra4k_g_pcont\n\t"
|
||||||
"l32r a14, .g_pcont\n\t"
|
"l32r a14, .g_pcont\n\t"
|
||||||
|
// We now switch to the SYS stack the SDK will use
|
||||||
"l32i.n a1, a2, 0\n\t" // delayed load for pipeline
|
"l32i.n a1, a2, 0\n\t" // delayed load for pipeline
|
||||||
"l32i.n a13, a13, 0\n\t"
|
"l32i.n a13, a13, 0\n\t"
|
||||||
"callx0 a0\n\t"
|
"callx0 a0\n\t"
|
||||||
"moveqz a2, a13, a2\n\t"
|
"moveqz a2, a13, a2\n\t"
|
||||||
"s32i.n a2, a14, 0\n\t"
|
"s32i.n a2, a14, 0\n\t"
|
||||||
|
|
||||||
"call0 hwdt_pre_sdk_init_icache\n\t"
|
/*
|
||||||
|
* Allow for running additional diagnotics supplied at link time.
|
||||||
|
*/
|
||||||
|
"l32r a0, .mmu_wrap_irom_fn\n\t"
|
||||||
|
"movi a2, hwdt_pre_sdk_init_icache\n\t"
|
||||||
|
"callx0 a0\n\t"
|
||||||
|
|
||||||
|
// In case somebody cares, leave things as we found them
|
||||||
|
// - Restore ROM BSS zeros.
|
||||||
"movi a2, 0x3FFFE000\n\t" // ROM BSS Area
|
"movi a2, 0x3FFFE000\n\t" // ROM BSS Area
|
||||||
"movi a3, 0x0b30\n\t" // ROM BSS Size
|
"movi a3, 0x0b30\n\t" // ROM BSS Size
|
||||||
"call0 ets_bzero\n\t"
|
"call0 ets_bzero\n\t"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Up until this call, the heap at crash time has been available for
|
||||||
|
* analysis. This is needed for dumping the bearssl stack. Also, future
|
||||||
|
* improvements could possibly use hwdt_pre_sdk_init() to run other early
|
||||||
|
* diagnostic tools.
|
||||||
|
*/
|
||||||
|
#ifdef UMM_INIT_USE_IRAM
|
||||||
|
"l32r a0, .umm_init\n\t"
|
||||||
|
#else
|
||||||
|
"l32r a0, .mmu_wrap_irom_fn\n\t"
|
||||||
|
"l32r a2, .umm_init\n\t"
|
||||||
|
#endif
|
||||||
|
"callx0 a0\n\t"
|
||||||
|
|
||||||
"l32r a3, .call_user_start\n\t"
|
"l32r a3, .call_user_start\n\t"
|
||||||
"movi a0, 0x4000044c\n\t"
|
"movi a0, 0x4000044c\n\t"
|
||||||
"jx a3\n\t"
|
"jx a3\n\t"
|
||||||
".size app_entry_redefinable, .-app_entry_redefinable\n\t"
|
".size app_entry_redefinable, .-app_entry_redefinable\n\t"
|
||||||
);
|
);
|
||||||
|
|
||||||
#else
|
|
||||||
void IRAM_ATTR app_entry_start(void) {
|
|
||||||
|
|
||||||
#ifdef USE_IRAM
|
|
||||||
handle_hwdt();
|
|
||||||
#else
|
|
||||||
handle_hwdt_icache();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Continuation context is in BSS.
|
|
||||||
*/
|
|
||||||
g_pcont = get_noextra4k_g_pcont();
|
|
||||||
|
|
||||||
if (!g_pcont) {
|
|
||||||
/*
|
|
||||||
* The continuation context is on the stack just after the reserved
|
|
||||||
* space for the ROM/eboot stack and before the SYS stack begins. All
|
|
||||||
* computations were done at top, save pointer to it now.
|
|
||||||
*/
|
|
||||||
g_pcont = CONT_STACK;
|
|
||||||
}
|
|
||||||
#if defined(DEBUG_ESP_HWDT_DEV_DEBUG) && !defined(USE_IRAM)
|
|
||||||
print_sanity_check_icache();
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
* Use new calculated SYS stack from top.
|
|
||||||
* Call the entry point of the SDK code.
|
|
||||||
*/
|
|
||||||
asm volatile ("mov.n a1, %0\n\t"
|
|
||||||
"movi a0, 0x4000044c\n\t" /* Should never return; however, set return to Boot ROM Breakpoint */
|
|
||||||
"jx %1\n\t" ::
|
|
||||||
"r" (sys_stack_first), "r" (call_user_start):
|
|
||||||
"a0", "memory");
|
|
||||||
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IRAM_ATTR app_entry_redefinable(void) {
|
|
||||||
/*
|
|
||||||
* There are 4 sections of code that share the stack starting near
|
|
||||||
* 0x40000000.
|
|
||||||
* 1) The Boot ROM (uses around 640 bytes)
|
|
||||||
* 2) The Bootloader, eboot.elf (last seen using 720 bytes.)
|
|
||||||
* 3) `app_entry_redefinable()` just before it starts the SDK.
|
|
||||||
* 4) The NONOS SDK, optionally the Core when the extra 4K option is
|
|
||||||
* selected.
|
|
||||||
*
|
|
||||||
* Use the ROM BSS zeroed out memory as the home for our temporary stack.
|
|
||||||
* This way no additional information will be lost. That will remove this
|
|
||||||
* tool from the list of possible concerns for stack overwrite.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
asm volatile ("movi a1, 0x3fffeb30\n\t"
|
|
||||||
"j app_entry_start" ::: "memory");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Keep this function with just asm seems to help avoid a stack frame being
|
|
||||||
* created for this function and things getting really confused.
|
|
||||||
*/
|
|
||||||
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(DEBUG_ESP_HWDT_INFO) || defined(ROM_STACK_DUMP)
|
#if defined(DEBUG_ESP_HWDT_INFO) || defined(ROM_STACK_DUMP)
|
||||||
void debug_hwdt_init(void) {
|
void debug_hwdt_init(void) {
|
||||||
/*
|
/*
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
// This include file is a hack to ensure backward compatibility with
|
|
||||||
// pre 3.0.0 versions of the core. There was a *lowercase* "i2s.h"
|
|
||||||
// header which was in this directory, now renamed to "core_esp82i66s.h"
|
|
||||||
// But, the I2S class has a header, "I2S.h" in uppercase. On Linux
|
|
||||||
// the two names are different, but on Windows it's case-insensitive
|
|
||||||
// so the names conflict.
|
|
||||||
//
|
|
||||||
// Avoid the issue by preserving the old i2s.h file and have it redirect
|
|
||||||
// to I2S.h which will give the ESP8266-specific functions as well as
|
|
||||||
// the generic I2S class.
|
|
||||||
|
|
||||||
#include "../../libraries/I2S/src/I2S.h"
|
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
// these auto classes wrap up xt_rsil so your code can be simplier, but can only be
|
// these auto classes wrap up xt_rsil so your code can be simpler, but can only be
|
||||||
// used in an ino or cpp files.
|
// used in an ino or cpp files.
|
||||||
|
|
||||||
// InterruptLock is used when you want to completely disable interrupts
|
// InterruptLock is used when you want to completely disable interrupts
|
||||||
|
@@ -19,13 +19,16 @@
|
|||||||
#include "mmu_iram.h"
|
#include "mmu_iram.h"
|
||||||
#include <user_interface.h>
|
#include <user_interface.h>
|
||||||
|
|
||||||
|
#define ICACHE_SIZE_32 1
|
||||||
|
#define ICACHE_SIZE_16 0
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
#if (MMU_ICACHE_SIZE == 0x4000)
|
#if (MMU_ICACHE_SIZE == 0x4000)
|
||||||
#define SOC_CACHE_SIZE 0 // 16KB
|
#define SOC_CACHE_SIZE ICACHE_SIZE_16
|
||||||
#pragma message("ICACHE size 16K")
|
#pragma message("ICACHE size 16K")
|
||||||
#else
|
#else
|
||||||
#define SOC_CACHE_SIZE 1 // 32KB
|
#define SOC_CACHE_SIZE ICACHE_SIZE_32
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (MMU_ICACHE_SIZE == 0x4000)
|
#if (MMU_ICACHE_SIZE == 0x4000)
|
||||||
@@ -80,7 +83,7 @@ extern "C" {
|
|||||||
*
|
*
|
||||||
* "Cache_Read_Enable" is underdocumented. Main sources of information were from
|
* "Cache_Read_Enable" is underdocumented. Main sources of information were from
|
||||||
* rboot, zboot, https://richard.burtons.org/2015/06/12/esp8266-cache_read_enable/,
|
* rboot, zboot, https://richard.burtons.org/2015/06/12/esp8266-cache_read_enable/,
|
||||||
* and other places. And some additional expermentation.
|
* and other places. And some additional experimentation.
|
||||||
*
|
*
|
||||||
* Searching through the NONOS SDK shows nothing on this API; however, some
|
* Searching through the NONOS SDK shows nothing on this API; however, some
|
||||||
* clues on what the NONOS SDK might be doing with ICACHE related calls can be
|
* clues on what the NONOS SDK might be doing with ICACHE related calls can be
|
||||||
@@ -185,8 +188,21 @@ extern "C" void pinMode( uint8_t pin, uint8_t mode ) {
|
|||||||
|
|
||||||
__pinMode( pin, mode );
|
__pinMode( pin, mode );
|
||||||
}
|
}
|
||||||
|
#else // #ifdef DEV_DEBUG_PRINT
|
||||||
|
extern void Cache_Read_Disable(void);
|
||||||
#endif // #ifdef DEV_DEBUG_PRINT
|
#endif // #ifdef DEV_DEBUG_PRINT
|
||||||
|
|
||||||
|
#else // #if (MMU_ICACHE_SIZE == 0x4000)
|
||||||
|
extern void Cache_Read_Enable(uint8_t map, uint8_t p, uint8_t v);
|
||||||
#endif // #if (MMU_ICACHE_SIZE == 0x4000)
|
#endif // #if (MMU_ICACHE_SIZE == 0x4000)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This wrapper is for running code from IROM (flash) before the SDK starts.
|
||||||
|
*/
|
||||||
|
void IRAM_ATTR mmu_wrap_irom_fn(void (*fn)(void)) {
|
||||||
|
Cache_Read_Enable(0, 0, ICACHE_SIZE_16);
|
||||||
|
fn();
|
||||||
|
Cache_Read_Disable();
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -26,13 +26,24 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//C This turns on range checking. Is this the value you want to trigger it?
|
// This turns on range checking.
|
||||||
#ifdef DEBUG_ESP_CORE
|
#ifdef DEBUG_ESP_CORE
|
||||||
#define DEBUG_ESP_MMU
|
#define DEBUG_ESP_MMU
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CORE_MOCK)
|
#if defined(CORE_MOCK)
|
||||||
#define ets_uart_printf(...) do {} while(false)
|
#define ets_uart_printf(...) do {} while(false)
|
||||||
|
#define XCHAL_INSTRAM0_VADDR 0x40000000
|
||||||
|
#define XCHAL_INSTRAM1_VADDR 0x40100000
|
||||||
|
#define XCHAL_INSTROM0_VADDR 0x40200000
|
||||||
|
#else
|
||||||
|
#include <sys/config.h> // For config/core-isa.h
|
||||||
|
/*
|
||||||
|
Cautiously use XCHAL_..._VADDR values where possible.
|
||||||
|
While XCHAL_..._VADDR values in core-isa.h may define the Xtensa processor
|
||||||
|
CONFIG options, they are not always an indication of DRAM, IRAM, or ROM
|
||||||
|
size or position in the address space.
|
||||||
|
*/
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -69,34 +80,54 @@ DBG_MMU_FLUSH(0)
|
|||||||
#define DBG_MMU_PRINTF(...) do {} while(false)
|
#define DBG_MMU_PRINTF(...) do {} while(false)
|
||||||
#endif // defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU)
|
#endif // defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This wrapper is for running code from IROM (flash) before the SDK starts.
|
||||||
|
*
|
||||||
|
* Wraps a `void fn(void)` call with calls to enable and disable iCACHE.
|
||||||
|
* Allows a function that resides in IROM to run before the SDK starts.
|
||||||
|
*
|
||||||
|
* Do not use once the SDK has started.
|
||||||
|
*
|
||||||
|
* Because the SDK initialization code has not run, nearly all the SDK functions
|
||||||
|
* are not safe to call.
|
||||||
|
*
|
||||||
|
* Note printing at this early stage is complicated. To gain more insight,
|
||||||
|
* review DEV_DEBUG_PRINT build path in mmu_iram.cpp. To handle strings stored
|
||||||
|
* in IROM, review printing method and comments in hwdt_app_entry.cpp.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void IRAM_ATTR mmu_wrap_irom_fn(void (*fn)(void));
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
bool mmu_is_iram(const void *addr) {
|
bool mmu_is_iram(const void *addr) {
|
||||||
#define IRAM_START 0x40100000UL
|
const uintptr_t iram_start = (uintptr_t)XCHAL_INSTRAM1_VADDR;
|
||||||
#ifndef MMU_IRAM_SIZE
|
#ifndef MMU_IRAM_SIZE
|
||||||
#if defined(__GNUC__) && !defined(CORE_MOCK)
|
#if defined(__GNUC__) && !defined(CORE_MOCK)
|
||||||
#warning "MMU_IRAM_SIZE was undefined, setting to 0x8000UL!"
|
#warning "MMU_IRAM_SIZE was undefined, setting to 0x8000UL!"
|
||||||
#endif
|
#endif
|
||||||
#define MMU_IRAM_SIZE 0x8000UL
|
#define MMU_IRAM_SIZE 0x8000ul
|
||||||
#endif
|
#endif
|
||||||
#define IRAM_END (IRAM_START + MMU_IRAM_SIZE)
|
const uintptr_t iram_end = iram_start + MMU_IRAM_SIZE;
|
||||||
|
|
||||||
return (IRAM_START <= (uintptr_t)addr && IRAM_END > (uintptr_t)addr);
|
return (iram_start <= (uintptr_t)addr && iram_end > (uintptr_t)addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
bool mmu_is_dram(const void *addr) {
|
bool mmu_is_dram(const void *addr) {
|
||||||
#define DRAM_START 0x3FF80000UL
|
const uintptr_t dram_start = 0x3FFE8000ul;
|
||||||
#define DRAM_END 0x40000000UL
|
// The start of the Boot ROM sits at the end of DRAM. 0x40000000ul;
|
||||||
|
const uintptr_t dram_end = (uintptr_t)XCHAL_INSTRAM0_VADDR;
|
||||||
|
|
||||||
return (DRAM_START <= (uintptr_t)addr && DRAM_END > (uintptr_t)addr);
|
return (dram_start <= (uintptr_t)addr && dram_end > (uintptr_t)addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
bool mmu_is_icache(const void *addr) {
|
bool mmu_is_icache(const void *addr) {
|
||||||
#define ICACHE_START 0x40200000UL
|
extern void _irom0_text_end(void);
|
||||||
#define ICACHE_END (ICACHE_START + 0x100000UL)
|
const uintptr_t icache_start = (uintptr_t)XCHAL_INSTROM0_VADDR;
|
||||||
|
const uintptr_t icache_end = (uintptr_t)_irom0_text_end;
|
||||||
|
|
||||||
return (ICACHE_START <= (uintptr_t)addr && ICACHE_END > (uintptr_t)addr);
|
return (icache_start <= (uintptr_t)addr && icache_end > (uintptr_t)addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_MMU
|
#ifdef DEBUG_ESP_MMU
|
||||||
@@ -122,13 +153,31 @@ bool mmu_is_icache(const void *addr) {
|
|||||||
/*
|
/*
|
||||||
* Some inlines to allow faster random access to non32bit access of iRAM or
|
* Some inlines to allow faster random access to non32bit access of iRAM or
|
||||||
* iCACHE data elements. These remove the extra time and stack space that would
|
* iCACHE data elements. These remove the extra time and stack space that would
|
||||||
* have occured by relying on exception processing.
|
* have occurred by relying on exception processing.
|
||||||
*/
|
*/
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
uint8_t mmu_get_uint8(const void *p8) {
|
uint8_t mmu_get_uint8(const void *p8) {
|
||||||
ASSERT_RANGE_TEST_READ(p8);
|
ASSERT_RANGE_TEST_READ(p8);
|
||||||
uint32_t val = (*(uint32_t *)((uintptr_t)p8 & ~0x3));
|
// https://gist.github.com/shafik/848ae25ee209f698763cffee272a58f8#how-do-we-type-pun-correctly
|
||||||
uint32_t pos = ((uintptr_t)p8 & 0x3) * 8;
|
// Comply with strict-aliasing rules. Using memcpy is a Standards suggested
|
||||||
|
// method for type punning. The compiler optimizer will replace the memcpy
|
||||||
|
// with an `l32i` instruction. Using __builtin_memcpy to ensure we get the
|
||||||
|
// effects of the compiler optimization and not some #define version of
|
||||||
|
// memcpy.
|
||||||
|
void *v32 = (void *)((uintptr_t)p8 & ~(uintptr_t)3u);
|
||||||
|
uint32_t val;
|
||||||
|
__builtin_memcpy(&val, v32, sizeof(uint32_t));
|
||||||
|
// Use an empty ASM to reference the 32-bit value. This will block the
|
||||||
|
// compiler from immediately optimizing to an 8-bit or 16-bit load instruction
|
||||||
|
// against IRAM memory. (This approach was inspired by
|
||||||
|
// https://github.com/esp8266/Arduino/pull/7780#discussion_r548303374)
|
||||||
|
// This issue was seen when using a constant address with the GCC 10.3
|
||||||
|
// compiler.
|
||||||
|
// As a general practice, I think referencing by way of Extended ASM R/W
|
||||||
|
// output register will stop the the compiler from reloading the value later
|
||||||
|
// as 8-bit load from IRAM.
|
||||||
|
asm volatile ("" :"+r"(val)); // inject 32-bit dependency
|
||||||
|
uint32_t pos = ((uintptr_t)p8 & 3u) * 8u;
|
||||||
val >>= pos;
|
val >>= pos;
|
||||||
return (uint8_t)val;
|
return (uint8_t)val;
|
||||||
}
|
}
|
||||||
@@ -136,8 +185,11 @@ uint8_t mmu_get_uint8(const void *p8) {
|
|||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
uint16_t mmu_get_uint16(const uint16_t *p16) {
|
uint16_t mmu_get_uint16(const uint16_t *p16) {
|
||||||
ASSERT_RANGE_TEST_READ(p16);
|
ASSERT_RANGE_TEST_READ(p16);
|
||||||
uint32_t val = (*(uint32_t *)((uintptr_t)p16 & ~0x3));
|
void *v32 = (void *)((uintptr_t)p16 & ~(uintptr_t)0x3u);
|
||||||
uint32_t pos = ((uintptr_t)p16 & 0x3) * 8;
|
uint32_t val;
|
||||||
|
__builtin_memcpy(&val, v32, sizeof(uint32_t));
|
||||||
|
asm volatile ("" :"+r"(val));
|
||||||
|
uint32_t pos = ((uintptr_t)p16 & 3u) * 8u;
|
||||||
val >>= pos;
|
val >>= pos;
|
||||||
return (uint16_t)val;
|
return (uint16_t)val;
|
||||||
}
|
}
|
||||||
@@ -145,8 +197,11 @@ uint16_t mmu_get_uint16(const uint16_t *p16) {
|
|||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
int16_t mmu_get_int16(const int16_t *p16) {
|
int16_t mmu_get_int16(const int16_t *p16) {
|
||||||
ASSERT_RANGE_TEST_READ(p16);
|
ASSERT_RANGE_TEST_READ(p16);
|
||||||
uint32_t val = (*(uint32_t *)((uintptr_t)p16 & ~0x3));
|
void *v32 = (void *)((uintptr_t)p16 & ~(uintptr_t)3u);
|
||||||
uint32_t pos = ((uintptr_t)p16 & 0x3) * 8;
|
uint32_t val;
|
||||||
|
__builtin_memcpy(&val, v32, sizeof(uint32_t));
|
||||||
|
asm volatile ("" :"+r"(val));
|
||||||
|
uint32_t pos = ((uintptr_t)p16 & 3u) * 8u;
|
||||||
val >>= pos;
|
val >>= pos;
|
||||||
return (int16_t)val;
|
return (int16_t)val;
|
||||||
}
|
}
|
||||||
@@ -154,30 +209,43 @@ int16_t mmu_get_int16(const int16_t *p16) {
|
|||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
uint8_t mmu_set_uint8(void *p8, const uint8_t val) {
|
uint8_t mmu_set_uint8(void *p8, const uint8_t val) {
|
||||||
ASSERT_RANGE_TEST_WRITE(p8);
|
ASSERT_RANGE_TEST_WRITE(p8);
|
||||||
uint32_t pos = ((uintptr_t)p8 & 0x3) * 8;
|
uint32_t pos = ((uintptr_t)p8 & 3u) * 8u;
|
||||||
uint32_t sval = val << pos;
|
uint32_t sval = val << pos;
|
||||||
uint32_t valmask = 0x0FF << pos;
|
uint32_t valmask = 0x0FFu << pos;
|
||||||
|
|
||||||
|
void *v32 = (void *)((uintptr_t)p8 & ~(uintptr_t)3u);
|
||||||
|
uint32_t ival;
|
||||||
|
__builtin_memcpy(&ival, v32, sizeof(uint32_t));
|
||||||
|
asm volatile ("" :"+r"(ival));
|
||||||
|
|
||||||
uint32_t *p32 = (uint32_t *)((uintptr_t)p8 & ~0x3);
|
|
||||||
uint32_t ival = *p32;
|
|
||||||
ival &= (~valmask);
|
ival &= (~valmask);
|
||||||
ival |= sval;
|
ival |= sval;
|
||||||
*p32 = ival;
|
/*
|
||||||
|
This 32-bit dependency injection does not appear to be needed with the
|
||||||
|
current GCC 10.3; however, that could change in the future versions. Or, I
|
||||||
|
may not have the right test for it to fail.
|
||||||
|
*/
|
||||||
|
asm volatile ("" :"+r"(ival));
|
||||||
|
__builtin_memcpy(v32, &ival, sizeof(uint32_t));
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
uint16_t mmu_set_uint16(uint16_t *p16, const uint16_t val) {
|
uint16_t mmu_set_uint16(uint16_t *p16, const uint16_t val) {
|
||||||
ASSERT_RANGE_TEST_WRITE(p16);
|
ASSERT_RANGE_TEST_WRITE(p16);
|
||||||
uint32_t pos = ((uintptr_t)p16 & 0x3) * 8;
|
uint32_t pos = ((uintptr_t)p16 & 3u) * 8u;
|
||||||
uint32_t sval = val << pos;
|
uint32_t sval = val << pos;
|
||||||
uint32_t valmask = 0x0FFFF << pos;
|
uint32_t valmask = 0x0FFFFu << pos;
|
||||||
|
|
||||||
|
void *v32 = (void *)((uintptr_t)p16 & ~(uintptr_t)3u);
|
||||||
|
uint32_t ival;
|
||||||
|
__builtin_memcpy(&ival, v32, sizeof(uint32_t));
|
||||||
|
asm volatile ("" :"+r"(ival));
|
||||||
|
|
||||||
uint32_t *p32 = (uint32_t *)((uintptr_t)p16 & ~0x3);
|
|
||||||
uint32_t ival = *p32;
|
|
||||||
ival &= (~valmask);
|
ival &= (~valmask);
|
||||||
ival |= sval;
|
ival |= sval;
|
||||||
*p32 = ival;
|
asm volatile ("" :"+r"(ival));
|
||||||
|
__builtin_memcpy(v32, &ival, sizeof(uint32_t));
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,32 +253,36 @@ static inline __attribute__((always_inline))
|
|||||||
int16_t mmu_set_int16(int16_t *p16, const int16_t val) {
|
int16_t mmu_set_int16(int16_t *p16, const int16_t val) {
|
||||||
ASSERT_RANGE_TEST_WRITE(p16);
|
ASSERT_RANGE_TEST_WRITE(p16);
|
||||||
uint32_t sval = (uint16_t)val;
|
uint32_t sval = (uint16_t)val;
|
||||||
uint32_t pos = ((uintptr_t)p16 & 0x3) * 8;
|
uint32_t pos = ((uintptr_t)p16 & 3u) * 8u;
|
||||||
sval <<= pos;
|
sval <<= pos;
|
||||||
uint32_t valmask = 0x0FFFF << pos;
|
uint32_t valmask = 0x0FFFFu << pos;
|
||||||
|
|
||||||
|
void *v32 = (void *)((uintptr_t)p16 & ~(uintptr_t)3u);
|
||||||
|
uint32_t ival;
|
||||||
|
__builtin_memcpy(&ival, v32, sizeof(uint32_t));
|
||||||
|
asm volatile ("" :"+r"(ival));
|
||||||
|
|
||||||
uint32_t *p32 = (uint32_t *)((uintptr_t)p16 & ~0x3);
|
|
||||||
uint32_t ival = *p32;
|
|
||||||
ival &= (~valmask);
|
ival &= (~valmask);
|
||||||
ival |= sval;
|
ival |= sval;
|
||||||
*p32 = ival;
|
asm volatile ("" :"+r"(ival));
|
||||||
|
__builtin_memcpy(v32, &ival, sizeof(uint32_t));
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (MMU_IRAM_SIZE > 32*1024) && !defined(MMU_SEC_HEAP)
|
#if (MMU_IRAM_SIZE > 32*1024) && !defined(MMU_SEC_HEAP)
|
||||||
extern void _text_end(void);
|
|
||||||
#define MMU_SEC_HEAP mmu_sec_heap()
|
#define MMU_SEC_HEAP mmu_sec_heap()
|
||||||
#define MMU_SEC_HEAP_SIZE mmu_sec_heap_size()
|
#define MMU_SEC_HEAP_SIZE mmu_sec_heap_size()
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
void *mmu_sec_heap(void) {
|
void *mmu_sec_heap(void) {
|
||||||
uint32_t sec_heap = (uint32_t)_text_end + 32;
|
extern void _text_end(void);
|
||||||
return (void *)(sec_heap &= ~7);
|
uintptr_t sec_heap = (uintptr_t)_text_end + (uintptr_t)32u;
|
||||||
|
return (void *)(sec_heap &= ~(uintptr_t)7u);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline))
|
||||||
size_t mmu_sec_heap_size(void) {
|
size_t mmu_sec_heap_size(void) {
|
||||||
return (size_t)0xC000UL - ((size_t)mmu_sec_heap() - 0x40100000UL);
|
return (size_t)0xC000ul - ((uintptr_t)mmu_sec_heap() - (uintptr_t)XCHAL_INSTRAM1_VADDR);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
// This file's contents have been moved to newlib. This file simply
|
// This file's contents have been moved to newlib. This file simply
|
||||||
// includes the newlib pgmspace file as well as some ets headers
|
// includes the newlib pgmspace file as well as some ets headers
|
||||||
// to preserve backwards compatibility
|
// to preserve backwards compatibility
|
||||||
|
// current source: https://github.com/earlephilhower/newlib-xtensa/blob/xtensa-4_0_0-lock-arduino/newlib/libc/sys/xtensa/sys/pgmspace.h
|
||||||
|
|
||||||
#include <sys/pgmspace.h>
|
#include <sys/pgmspace.h>
|
||||||
|
|
||||||
|
@@ -19,10 +19,10 @@
|
|||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
This implementation is based on the original implementation of the ROM.
|
This implementation is based on the original implementation of the ROM.
|
||||||
It was shortend to reduce the memory usage. The complete version and the
|
It was shortened to reduce the memory usage. The complete version and the
|
||||||
development history can be found in:
|
development history can be found in:
|
||||||
https://github.com/twischer/Arduino/tree/reboot_uart_download_full
|
https://github.com/twischer/Arduino/tree/reboot_uart_download_full
|
||||||
This might be usefull in case of issues.
|
This might be useful in case of issues.
|
||||||
*/
|
*/
|
||||||
#include "reboot_uart_dwnld.h"
|
#include "reboot_uart_dwnld.h"
|
||||||
#include <stdnoreturn.h>
|
#include <stdnoreturn.h>
|
||||||
@@ -106,7 +106,7 @@ static inline void __wsr_vecbase(uint32_t vector_base) {
|
|||||||
const uint32_t uart_no = 0;
|
const uint32_t uart_no = 0;
|
||||||
uartAttach();
|
uartAttach();
|
||||||
Uart_Init(uart_no);
|
Uart_Init(uart_no);
|
||||||
ets_install_uart_printf(uart_no);
|
ets_install_uart_printf();
|
||||||
|
|
||||||
/* reverse engineered from boot_from_something() */
|
/* reverse engineered from boot_from_something() */
|
||||||
const uint16_t divlatch = uart_baudrate_detect(uart_no, 0);
|
const uint16_t divlatch = uart_baudrate_detect(uart_no, 0);
|
||||||
@@ -148,4 +148,3 @@ static inline void __wsr_vecbase(uint32_t vector_base) {
|
|||||||
|
|
||||||
esp8266UartDownloadMode();
|
esp8266UartDownloadMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -113,7 +113,7 @@ Choosing the page size for the system involves many factors:
|
|||||||
- How fast must spiffs be
|
- How fast must spiffs be
|
||||||
- Other things impossible to find out
|
- Other things impossible to find out
|
||||||
|
|
||||||
So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't
|
So, choosing 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
|
fret - there is no optimal page size. This varies from how the target will use
|
||||||
spiffs. Use the golden rule:
|
spiffs. Use the golden rule:
|
||||||
|
|
||||||
|
@@ -695,7 +695,7 @@ s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
|
|||||||
* in this callback will mess things up for sure - do not do this.
|
* 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
|
* 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.
|
* 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
|
* Used in conjunction with SPIFFS_open_by_page this may improve performance
|
||||||
* when opening a lot of files.
|
* when opening a lot of files.
|
||||||
* Must be invoked after mount.
|
* Must be invoked after mount.
|
||||||
*
|
*
|
||||||
@@ -710,7 +710,7 @@ s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func);
|
|||||||
* Maps the first level index lookup to a given memory 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
|
* 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
|
* 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
|
* medium. When mapping, all affected indices are found and the information is
|
||||||
* copied to the array.
|
* copied to the array.
|
||||||
* Whole file or only parts of it may be mapped. The index map will cover file
|
* 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).
|
* contents from argument offset until and including arguments (offset+len).
|
||||||
|
@@ -353,7 +353,7 @@ extern "C" {
|
|||||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
|
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
|
||||||
|
|
||||||
|
|
||||||
// check id, only visit matching objec ids
|
// check id, only visit matching object ids
|
||||||
#define SPIFFS_VIS_CHECK_ID (1<<0)
|
#define SPIFFS_VIS_CHECK_ID (1<<0)
|
||||||
// report argument object id to visitor - else object lookup id is reported
|
// report argument object id to visitor - else object lookup id is reported
|
||||||
#define SPIFFS_VIS_CHECK_PH (1<<1)
|
#define SPIFFS_VIS_CHECK_PH (1<<1)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
stdlib_noniso.h - nonstandard (but usefull) conversion functions
|
stdlib_noniso.h - nonstandard (but useful) conversion functions
|
||||||
|
|
||||||
Copyright (c) 2021 David Gauchard. All rights reserved.
|
Copyright (c) 2021 David Gauchard. All rights reserved.
|
||||||
This file is part of the esp8266 core for Arduino environment.
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
stdlib_noniso.h - nonstandard (but usefull) conversion functions
|
stdlib_noniso.h - nonstandard (but useful) conversion functions
|
||||||
|
|
||||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||||
This file is part of the esp8266 core for Arduino environment.
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
@@ -20,6 +20,28 @@
|
|||||||
* synchronisation of the two through timeshift64
|
* synchronisation of the two through timeshift64
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-time.c
|
||||||
|
|
||||||
|
bool getLocalTime(struct tm * info, uint32_t ms)
|
||||||
|
{
|
||||||
|
uint32_t start = millis();
|
||||||
|
time_t now;
|
||||||
|
while((millis()-start) <= ms) {
|
||||||
|
time(&now);
|
||||||
|
localtime_r(&now, info);
|
||||||
|
if(info->tm_year > (2016 - 1900)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(CORE_MOCK)
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <../include/time.h> // See issue #6714
|
#include <../include/time.h> // See issue #6714
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
@@ -33,7 +55,6 @@ extern "C" {
|
|||||||
#include <coredecls.h>
|
#include <coredecls.h>
|
||||||
#include <Schedule.h>
|
#include <Schedule.h>
|
||||||
|
|
||||||
#include <Arduino.h> // configTime()
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
@@ -214,6 +235,11 @@ void settimeofday_cb (const TrivialCB& cb)
|
|||||||
_settimeofday_cb = [cb](bool sntp) { (void)sntp; cb(); };
|
_settimeofday_cb = [cb](bool sntp) { (void)sntp; cb(); };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void settimeofday_cb (BoolCB&& cb)
|
||||||
|
{
|
||||||
|
_settimeofday_cb = std::move(cb);
|
||||||
|
}
|
||||||
|
|
||||||
void settimeofday_cb (const BoolCB& cb)
|
void settimeofday_cb (const BoolCB& cb)
|
||||||
{
|
{
|
||||||
_settimeofday_cb = cb;
|
_settimeofday_cb = cb;
|
||||||
@@ -252,4 +278,6 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}; // extern "C"
|
||||||
|
|
||||||
|
#endif // !defined(CORE_MOCK)
|
||||||
|
@@ -41,6 +41,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
#include "coredecls.h"
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
#include "gdb_hooks.h"
|
#include "gdb_hooks.h"
|
||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
@@ -493,13 +494,13 @@ uart_stop_isr(uart_t* uart)
|
|||||||
-tools/sdk/uart_register.h
|
-tools/sdk/uart_register.h
|
||||||
-cores/esp8266/esp8266_peri.h
|
-cores/esp8266/esp8266_peri.h
|
||||||
*/
|
*/
|
||||||
inline size_t
|
inline __attribute__((always_inline)) size_t
|
||||||
uart_tx_fifo_available(const int uart_nr)
|
uart_tx_fifo_available(const int uart_nr)
|
||||||
{
|
{
|
||||||
return (USS(uart_nr) >> USTXC) & 0xff;
|
return (USS(uart_nr) >> USTXC) & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline __attribute__((always_inline)) bool
|
||||||
uart_tx_fifo_full(const int uart_nr)
|
uart_tx_fifo_full(const int uart_nr)
|
||||||
{
|
{
|
||||||
return uart_tx_fifo_available(uart_nr) >= 0x7f;
|
return uart_tx_fifo_available(uart_nr) >= 0x7f;
|
||||||
@@ -566,7 +567,7 @@ uart_wait_tx_empty(uart_t* uart)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
while(uart_tx_fifo_available(uart->uart_nr) > 0)
|
while(uart_tx_fifo_available(uart->uart_nr) > 0)
|
||||||
delay(0);
|
esp_yield();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -943,23 +944,23 @@ uart_ignore_char(char c)
|
|||||||
(void) c;
|
(void) c;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline __attribute__((always_inline)) void
|
||||||
uart_write_char_delay(const int uart_nr, char c)
|
uart_write_char_delay(const int uart_nr, char c)
|
||||||
{
|
{
|
||||||
while(uart_tx_fifo_full(uart_nr))
|
while(uart_tx_fifo_full(uart_nr))
|
||||||
delay(0);
|
esp_yield();
|
||||||
|
|
||||||
USF(uart_nr) = c;
|
USF(uart_nr) = c;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void IRAM_ATTR
|
||||||
uart0_write_char(char c)
|
uart0_write_char(char c)
|
||||||
{
|
{
|
||||||
uart_write_char_delay(0, c);
|
uart_write_char_delay(0, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void IRAM_ATTR
|
||||||
uart1_write_char(char c)
|
uart1_write_char(char c)
|
||||||
{
|
{
|
||||||
uart_write_char_delay(1, c);
|
uart_write_char_delay(1, c);
|
||||||
|
@@ -248,4 +248,32 @@ Enhancement ideas:
|
|||||||
save on the execution time spent with interrupts disabled.
|
save on the execution time spent with interrupts disabled.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dec 29, 2021
|
||||||
|
Upstream umm_malloc at git hash id 4dac43c3be7a7470dd669323021ba238081da18e
|
||||||
|
processed all project files with the style program uncrustify.
|
||||||
|
|
||||||
|
This PR updates our ported version of umm_malloc processed with "uncrustify".
|
||||||
|
This should make subsequent merges of upstream into this port easier.
|
||||||
|
|
||||||
|
This also makes the style more consistant through umm_malloc.
|
||||||
|
|
||||||
|
Some edits to source files was needed to get uncrustify to work.
|
||||||
|
1) macros with "if"s need to be of the form "if ( blah ) { } " curley braces
|
||||||
|
are needed for it to parse correctly
|
||||||
|
2) These "#ifdef __cplusplus" also had to be commented out while running to
|
||||||
|
avoid parser confusion.
|
||||||
|
```
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
and
|
||||||
|
```
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
*/
|
||||||
#endif
|
#endif
|
||||||
|
@@ -126,7 +126,7 @@ later.
|
|||||||
The result is that a block of memory on the free list uses just 8 bytes
|
The result is that a block of memory on the free list uses just 8 bytes
|
||||||
instead of 16.
|
instead of 16.
|
||||||
|
|
||||||
In fact, we go even one step futher when we realize that the free block
|
In fact, we go even one step further when we realize that the free block
|
||||||
index values are available to store data when the block is allocated.
|
index values are available to store data when the block is allocated.
|
||||||
|
|
||||||
The overhead of an allocated block is therefore just 4 bytes.
|
The overhead of an allocated block is therefore just 4 bytes.
|
||||||
@@ -205,7 +205,7 @@ described.
|
|||||||
allocated for use by the upper block.
|
allocated for use by the upper block.
|
||||||
|
|
||||||
While we're talking about "upper" and "lower" blocks, we should make
|
While we're talking about "upper" and "lower" blocks, we should make
|
||||||
a comment about adresses. In the diagrams, a block higher up in the
|
a comment about addresses. In the diagrams, a block higher up in the
|
||||||
picture is at a lower address. And the blocks grow downwards their
|
picture is at a lower address. And the blocks grow downwards their
|
||||||
block index increases as does their physical address.
|
block index increases as does their physical address.
|
||||||
|
|
||||||
@@ -347,7 +347,7 @@ nf |*?? | ?? | ?? | cf | nf | ?? | ?? | ?? | pf |
|
|||||||
```
|
```
|
||||||
|
|
||||||
This one is prety easy too, except we don't need to mess with the
|
This one is prety easy too, except we don't need to mess with the
|
||||||
free list indexes at all becasue we'll allocate the new block at the
|
free list indexes at all because we'll allocate the new block at the
|
||||||
end of the current free block. We do, however have to adjust the
|
end of the current free block. We do, however have to adjust the
|
||||||
indexes in cf, c, and n.
|
indexes in cf, c, and n.
|
||||||
|
|
||||||
@@ -539,5 +539,5 @@ BEFORE AFTER
|
|||||||
+----+----+----+----+ +----+----+----+----+
|
+----+----+----+----+ +----+----+----+----+
|
||||||
```
|
```
|
||||||
|
|
||||||
Then we call free() with the adress of the data portion of the new
|
Then we call free() with the address of the data portion of the new
|
||||||
block (s) which adds it to the free list.
|
block (s) which adds it to the free list.
|
||||||
|
@@ -1 +1,2 @@
|
|||||||
Downloaded from: https://github.com/rhempel/c-helper-macros/tree/develop
|
Downloaded from: https://github.com/rhempel/c-helper-macros/tree/develop
|
||||||
|
Applied uncrustify to be consistent with the rest of the umm_malloc files.
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
* to set the trace level #define DBGLOG_LEVEL x
|
* to set the trace level #define DBGLOG_LEVEL x
|
||||||
*
|
*
|
||||||
* To update which of the DBGLOG macros are compiled in, you must redefine the
|
* To update which of the DBGLOG macros are compiled in, you must redefine the
|
||||||
* DBGLOG_LEVEL macro and the inlcude the dbglog.h file again, like this:
|
* DBGLOG_LEVEL macro and the include the dbglog.h file again, like this:
|
||||||
*
|
*
|
||||||
* #undef DBGLOG_LEVEL
|
* #undef DBGLOG_LEVEL
|
||||||
* #define DBGLOG_LEVEL 6
|
* #define DBGLOG_LEVEL 6
|
||||||
@@ -50,11 +50,11 @@
|
|||||||
#undef DBGLOG_FORCE
|
#undef DBGLOG_FORCE
|
||||||
|
|
||||||
#ifndef DBGLOG_LEVEL
|
#ifndef DBGLOG_LEVEL
|
||||||
# define DBGLOG_LEVEL 0
|
#define DBGLOG_LEVEL 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DBGLOG_FUNCTION
|
#ifndef DBGLOG_FUNCTION
|
||||||
# define DBGLOG_FUNCTION printf
|
#define DBGLOG_FUNCTION printf
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define DBGLOG_32_BIT_PTR(x) ((uint32_t)(((uintptr_t)(x)) & 0xffffffff))
|
#define DBGLOG_32_BIT_PTR(x) ((uint32_t)(((uintptr_t)(x)) & 0xffffffff))
|
||||||
@@ -62,39 +62,39 @@
|
|||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
#if DBGLOG_LEVEL >= 6
|
#if DBGLOG_LEVEL >= 6
|
||||||
# define DBGLOG_TRACE(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
#define DBGLOG_TRACE(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
# define DBGLOG_TRACE(format, ...)
|
#define DBGLOG_TRACE(format, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DBGLOG_LEVEL >= 5
|
#if DBGLOG_LEVEL >= 5
|
||||||
# define DBGLOG_DEBUG(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
#define DBGLOG_DEBUG(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
# define DBGLOG_DEBUG(format, ...)
|
#define DBGLOG_DEBUG(format, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DBGLOG_LEVEL >= 4
|
#if DBGLOG_LEVEL >= 4
|
||||||
# define DBGLOG_CRITICAL(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
#define DBGLOG_CRITICAL(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
# define DBGLOG_CRITICAL(format, ...)
|
#define DBGLOG_CRITICAL(format, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DBGLOG_LEVEL >= 3
|
#if DBGLOG_LEVEL >= 3
|
||||||
# define DBGLOG_ERROR(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
#define DBGLOG_ERROR(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
# define DBGLOG_ERROR(format, ...)
|
#define DBGLOG_ERROR(format, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DBGLOG_LEVEL >= 2
|
#if DBGLOG_LEVEL >= 2
|
||||||
# define DBGLOG_WARNING(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
#define DBGLOG_WARNING(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
# define DBGLOG_WARNING(format, ...)
|
#define DBGLOG_WARNING(format, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DBGLOG_LEVEL >= 1
|
#if DBGLOG_LEVEL >= 1
|
||||||
# define DBGLOG_INFO(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
#define DBGLOG_INFO(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
# define DBGLOG_INFO(format, ...)
|
#define DBGLOG_INFO(format, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define DBGLOG_FORCE(force, format, ...) {if(force) {DBGLOG_FUNCTION(format, ## __VA_ARGS__);}}
|
#define DBGLOG_FORCE(force, format, ...) {if (force) {DBGLOG_FUNCTION(format,##__VA_ARGS__);}}
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
#define ALWAYS_INLINE inline __attribute__ ((always_inline))
|
#define ALWAYS_INLINE inline __attribute__ ((always_inline))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Use FORCE_ALWAYS_INLINE to ensure HeapSelect... construtor/deconstructor
|
// Use FORCE_ALWAYS_INLINE to ensure HeapSelect... constructor/deconstructor
|
||||||
// are placed in IRAM
|
// are placed in IRAM
|
||||||
#ifdef FORCE_ALWAYS_INLINE_HEAP_SELECT
|
#ifdef FORCE_ALWAYS_INLINE_HEAP_SELECT
|
||||||
#define MAYBE_ALWAYS_INLINE ALWAYS_INLINE
|
#define MAYBE_ALWAYS_INLINE ALWAYS_INLINE
|
||||||
@@ -32,70 +32,77 @@
|
|||||||
class HeapSelect {
|
class HeapSelect {
|
||||||
public:
|
public:
|
||||||
#if (UMM_NUM_HEAPS == 1)
|
#if (UMM_NUM_HEAPS == 1)
|
||||||
MAYBE_ALWAYS_INLINE
|
MAYBE_ALWAYS_INLINE
|
||||||
HeapSelect(size_t id) { (void)id; }
|
HeapSelect(size_t id) {
|
||||||
MAYBE_ALWAYS_INLINE
|
(void)id;
|
||||||
~HeapSelect() {}
|
}
|
||||||
|
MAYBE_ALWAYS_INLINE
|
||||||
|
~HeapSelect() {
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
MAYBE_ALWAYS_INLINE
|
MAYBE_ALWAYS_INLINE
|
||||||
HeapSelect(size_t id) : _heap_id(umm_get_current_heap_id()) {
|
HeapSelect(size_t id) : _heap_id(umm_get_current_heap_id()) {
|
||||||
umm_set_heap_by_id(id);
|
umm_set_heap_by_id(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
MAYBE_ALWAYS_INLINE
|
MAYBE_ALWAYS_INLINE
|
||||||
~HeapSelect() {
|
~HeapSelect() {
|
||||||
umm_set_heap_by_id(_heap_id);
|
umm_set_heap_by_id(_heap_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
size_t _heap_id;
|
size_t _heap_id;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
class HeapSelectIram {
|
class HeapSelectIram {
|
||||||
public:
|
public:
|
||||||
#ifdef UMM_HEAP_IRAM
|
#ifdef UMM_HEAP_IRAM
|
||||||
MAYBE_ALWAYS_INLINE
|
MAYBE_ALWAYS_INLINE
|
||||||
HeapSelectIram() : _heap_id(umm_get_current_heap_id()) {
|
HeapSelectIram() : _heap_id(umm_get_current_heap_id()) {
|
||||||
umm_set_heap_by_id(UMM_HEAP_IRAM);
|
umm_set_heap_by_id(UMM_HEAP_IRAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
MAYBE_ALWAYS_INLINE
|
MAYBE_ALWAYS_INLINE
|
||||||
~HeapSelectIram() {
|
~HeapSelectIram() {
|
||||||
umm_set_heap_by_id(_heap_id);
|
umm_set_heap_by_id(_heap_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
size_t _heap_id;
|
size_t _heap_id;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
MAYBE_ALWAYS_INLINE
|
MAYBE_ALWAYS_INLINE
|
||||||
HeapSelectIram() {}
|
HeapSelectIram() {
|
||||||
MAYBE_ALWAYS_INLINE
|
}
|
||||||
~HeapSelectIram() {}
|
MAYBE_ALWAYS_INLINE
|
||||||
|
~HeapSelectIram() {
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
class HeapSelectDram {
|
class HeapSelectDram {
|
||||||
public:
|
public:
|
||||||
#if (UMM_NUM_HEAPS == 1)
|
#if (UMM_NUM_HEAPS == 1)
|
||||||
MAYBE_ALWAYS_INLINE
|
MAYBE_ALWAYS_INLINE
|
||||||
HeapSelectDram() {}
|
HeapSelectDram() {
|
||||||
MAYBE_ALWAYS_INLINE
|
}
|
||||||
~HeapSelectDram() {}
|
MAYBE_ALWAYS_INLINE
|
||||||
|
~HeapSelectDram() {
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
MAYBE_ALWAYS_INLINE
|
MAYBE_ALWAYS_INLINE
|
||||||
HeapSelectDram() : _heap_id(umm_get_current_heap_id()) {
|
HeapSelectDram() : _heap_id(umm_get_current_heap_id()) {
|
||||||
umm_set_heap_by_id(UMM_HEAP_DRAM);
|
umm_set_heap_by_id(UMM_HEAP_DRAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
MAYBE_ALWAYS_INLINE
|
MAYBE_ALWAYS_INLINE
|
||||||
~HeapSelectDram() {
|
~HeapSelectDram() {
|
||||||
umm_set_heap_by_id(_heap_id);
|
umm_set_heap_by_id(_heap_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
size_t _heap_id;
|
size_t _heap_id;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -25,235 +25,236 @@
|
|||||||
|
|
||||||
// UMM_HEAP_INFO ummHeapInfo;
|
// UMM_HEAP_INFO ummHeapInfo;
|
||||||
|
|
||||||
void *umm_info( void *ptr, bool force ) {
|
void *umm_info(void *ptr, bool force) {
|
||||||
UMM_CRITICAL_DECL(id_info);
|
UMM_CRITICAL_DECL(id_info);
|
||||||
|
|
||||||
UMM_INIT_HEAP;
|
UMM_CHECK_INITIALIZED();
|
||||||
|
|
||||||
uint16_t blockNo = 0;
|
uint16_t blockNo = 0;
|
||||||
|
|
||||||
/* Protect the critical section... */
|
/* Protect the critical section... */
|
||||||
UMM_CRITICAL_ENTRY(id_info);
|
UMM_CRITICAL_ENTRY(id_info);
|
||||||
|
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clear out all of the entries in the ummHeapInfo structure before doing
|
* Clear out all of the entries in the ummHeapInfo structure before doing
|
||||||
* any calculations..
|
* any calculations..
|
||||||
*/
|
*/
|
||||||
memset( &_context->info, 0, sizeof( _context->info ) );
|
memset(&_context->info, 0, sizeof(_context->info));
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "\n" );
|
DBGLOG_FORCE(force, "\n");
|
||||||
DBGLOG_FORCE( force, "+----------+-------+--------+--------+-------+--------+--------+\n" );
|
DBGLOG_FORCE(force, "+----------+-------+--------+--------+-------+--------+--------+\n");
|
||||||
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n",
|
DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n",
|
||||||
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
|
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
|
||||||
blockNo,
|
blockNo,
|
||||||
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
||||||
UMM_PBLOCK(blockNo),
|
UMM_PBLOCK(blockNo),
|
||||||
(UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo,
|
(UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK) - blockNo,
|
||||||
UMM_NFREE(blockNo),
|
UMM_NFREE(blockNo),
|
||||||
UMM_PFREE(blockNo) );
|
UMM_PFREE(blockNo));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now loop through the block lists, and keep track of the number and size
|
* Now loop through the block lists, and keep track of the number and size
|
||||||
* of used and free blocks. The terminating condition is an nb pointer with
|
* of used and free blocks. The terminating condition is an nb pointer with
|
||||||
* a value of zero...
|
* a value of zero...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK;
|
|
||||||
|
|
||||||
while( UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK ) {
|
|
||||||
size_t curBlocks = (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo;
|
|
||||||
|
|
||||||
++_context->info.totalEntries;
|
|
||||||
_context->info.totalBlocks += curBlocks;
|
|
||||||
|
|
||||||
/* Is this a free block? */
|
|
||||||
|
|
||||||
if( UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK ) {
|
|
||||||
++_context->info.freeEntries;
|
|
||||||
_context->info.freeBlocks += curBlocks;
|
|
||||||
_context->info.freeBlocksSquared += (curBlocks * curBlocks);
|
|
||||||
|
|
||||||
if (_context->info.maxFreeContiguousBlocks < curBlocks) {
|
|
||||||
_context->info.maxFreeContiguousBlocks = curBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|NF %5d|PF %5d|\n",
|
|
||||||
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
|
|
||||||
blockNo,
|
|
||||||
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
|
||||||
UMM_PBLOCK(blockNo),
|
|
||||||
(uint16_t)curBlocks,
|
|
||||||
UMM_NFREE(blockNo),
|
|
||||||
UMM_PFREE(blockNo) );
|
|
||||||
|
|
||||||
/* Does this block address match the ptr we may be trying to free? */
|
|
||||||
|
|
||||||
if( ptr == &UMM_BLOCK(blockNo) ) {
|
|
||||||
|
|
||||||
/* Release the critical section... */
|
|
||||||
UMM_CRITICAL_EXIT(id_info);
|
|
||||||
|
|
||||||
return( ptr );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
++_context->info.usedEntries;
|
|
||||||
_context->info.usedBlocks += curBlocks;
|
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|\n",
|
|
||||||
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
|
|
||||||
blockNo,
|
|
||||||
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
|
||||||
UMM_PBLOCK(blockNo),
|
|
||||||
(uint16_t)curBlocks );
|
|
||||||
}
|
|
||||||
|
|
||||||
blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK;
|
blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
while (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK) {
|
||||||
* The very last block is used as a placeholder to indicate that
|
size_t curBlocks = (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK) - blockNo;
|
||||||
* there are no more blocks in the heap, so it cannot be used
|
|
||||||
* for anything - at the same time, the size of this block must
|
|
||||||
* ALWAYS be exactly 1 !
|
|
||||||
*/
|
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n",
|
++_context->info.totalEntries;
|
||||||
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
|
_context->info.totalBlocks += curBlocks;
|
||||||
blockNo,
|
|
||||||
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
|
||||||
UMM_PBLOCK(blockNo),
|
|
||||||
UMM_NUMBLOCKS-blockNo,
|
|
||||||
UMM_NFREE(blockNo),
|
|
||||||
UMM_PFREE(blockNo) );
|
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "+----------+-------+--------+--------+-------+--------+--------+\n" );
|
/* Is this a free block? */
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "Total Entries %5d Used Entries %5d Free Entries %5d\n",
|
if (UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK) {
|
||||||
_context->info.totalEntries,
|
++_context->info.freeEntries;
|
||||||
_context->info.usedEntries,
|
_context->info.freeBlocks += curBlocks;
|
||||||
_context->info.freeEntries );
|
_context->info.freeBlocksSquared += (curBlocks * curBlocks);
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n",
|
if (_context->info.maxFreeContiguousBlocks < curBlocks) {
|
||||||
_context->info.totalBlocks,
|
_context->info.maxFreeContiguousBlocks = curBlocks;
|
||||||
_context->info.usedBlocks,
|
}
|
||||||
_context->info.freeBlocks );
|
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" );
|
DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|NF %5d|PF %5d|\n",
|
||||||
|
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
|
||||||
|
blockNo,
|
||||||
|
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
||||||
|
UMM_PBLOCK(blockNo),
|
||||||
|
(uint16_t)curBlocks,
|
||||||
|
UMM_NFREE(blockNo),
|
||||||
|
UMM_PFREE(blockNo));
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "Usage Metric: %5d\n", umm_usage_metric_core(_context));
|
/* Does this block address match the ptr we may be trying to free? */
|
||||||
DBGLOG_FORCE( force, "Fragmentation Metric: %5d\n", umm_fragmentation_metric_core(_context));
|
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" );
|
if (ptr == &UMM_BLOCK(blockNo)) {
|
||||||
|
|
||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
/* Release the critical section... */
|
||||||
#if !defined(UMM_INLINE_METRICS)
|
UMM_CRITICAL_EXIT(id_info);
|
||||||
if (_context->info.freeBlocks == _context->stats.free_blocks) {
|
|
||||||
DBGLOG_FORCE( force, "heap info Free blocks and heap statistics Free blocks match.\n");
|
|
||||||
} else {
|
|
||||||
DBGLOG_FORCE( force, "\nheap info Free blocks %5d != heap statistics Free Blocks %5d\n\n",
|
|
||||||
_context->info.freeBlocks,
|
|
||||||
_context->stats.free_blocks );
|
|
||||||
}
|
|
||||||
DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
umm_print_stats(force);
|
return ptr;
|
||||||
#endif
|
}
|
||||||
|
} else {
|
||||||
|
++_context->info.usedEntries;
|
||||||
|
_context->info.usedBlocks += curBlocks;
|
||||||
|
|
||||||
/* Release the critical section... */
|
DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|\n",
|
||||||
UMM_CRITICAL_EXIT(id_info);
|
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
|
||||||
|
blockNo,
|
||||||
|
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
||||||
|
UMM_PBLOCK(blockNo),
|
||||||
|
(uint16_t)curBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
return( NULL );
|
blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The very last block is used as a placeholder to indicate that
|
||||||
|
* there are no more blocks in the heap, so it cannot be used
|
||||||
|
* for anything - at the same time, the size of this block must
|
||||||
|
* ALWAYS be exactly 1 !
|
||||||
|
*/
|
||||||
|
|
||||||
|
DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n",
|
||||||
|
DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)),
|
||||||
|
blockNo,
|
||||||
|
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
||||||
|
UMM_PBLOCK(blockNo),
|
||||||
|
UMM_NUMBLOCKS - blockNo,
|
||||||
|
UMM_NFREE(blockNo),
|
||||||
|
UMM_PFREE(blockNo));
|
||||||
|
|
||||||
|
DBGLOG_FORCE(force, "+----------+-------+--------+--------+-------+--------+--------+\n");
|
||||||
|
|
||||||
|
DBGLOG_FORCE(force, "Total Entries %5d Used Entries %5d Free Entries %5d\n",
|
||||||
|
_context->info.totalEntries,
|
||||||
|
_context->info.usedEntries,
|
||||||
|
_context->info.freeEntries);
|
||||||
|
|
||||||
|
DBGLOG_FORCE(force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n",
|
||||||
|
_context->info.totalBlocks,
|
||||||
|
_context->info.usedBlocks,
|
||||||
|
_context->info.freeBlocks);
|
||||||
|
|
||||||
|
DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n");
|
||||||
|
|
||||||
|
DBGLOG_FORCE(force, "Usage Metric: %5d\n", umm_usage_metric_core(_context));
|
||||||
|
DBGLOG_FORCE(force, "Fragmentation Metric: %5d\n", umm_fragmentation_metric_core(_context));
|
||||||
|
|
||||||
|
DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n");
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
|
#if !defined(UMM_INLINE_METRICS)
|
||||||
|
if (_context->info.freeBlocks == _context->stats.free_blocks) {
|
||||||
|
DBGLOG_FORCE(force, "heap info Free blocks and heap statistics Free blocks match.\n");
|
||||||
|
} else {
|
||||||
|
DBGLOG_FORCE(force, "\nheap info Free blocks %5d != heap statistics Free Blocks %5d\n\n",
|
||||||
|
_context->info.freeBlocks,
|
||||||
|
_context->stats.free_blocks);
|
||||||
|
}
|
||||||
|
DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
umm_print_stats(force);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Release the critical section... */
|
||||||
|
UMM_CRITICAL_EXIT(id_info);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
size_t umm_free_heap_size_core( umm_heap_context_t *_context ) {
|
size_t umm_free_heap_size_core(umm_heap_context_t *_context) {
|
||||||
return (size_t)_context->info.freeBlocks * sizeof(umm_block);
|
return (size_t)_context->info.freeBlocks * sizeof(umm_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t umm_free_heap_size( void ) {
|
size_t umm_free_heap_size(void) {
|
||||||
#ifndef UMM_INLINE_METRICS
|
#ifndef UMM_INLINE_METRICS
|
||||||
umm_info(NULL, false);
|
umm_info(NULL, false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return umm_free_heap_size_core(umm_get_current_heap());
|
return umm_free_heap_size_core(umm_get_current_heap());
|
||||||
}
|
}
|
||||||
|
|
||||||
//C Breaking change in upstream umm_max_block_size() was changed to
|
// C Breaking change in upstream umm_max_block_size() was changed to
|
||||||
//C umm_max_free_block_size() keeping old function name for (dot) releases.
|
// C umm_max_free_block_size() keeping old function name for (dot) releases.
|
||||||
//C TODO: update at next major release.
|
// C TODO: update at next major release.
|
||||||
//C size_t umm_max_free_block_size( void ) {
|
// C size_t umm_max_free_block_size( void ) {
|
||||||
size_t umm_max_block_size_core( umm_heap_context_t *_context ) {
|
size_t umm_max_block_size_core(umm_heap_context_t *_context) {
|
||||||
return _context->info.maxFreeContiguousBlocks * sizeof(umm_block);
|
return _context->info.maxFreeContiguousBlocks * sizeof(umm_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t umm_max_block_size( void ) {
|
size_t umm_max_block_size(void) {
|
||||||
umm_info(NULL, false);
|
umm_info(NULL, false);
|
||||||
return umm_max_block_size_core(umm_get_current_heap());
|
return umm_max_block_size_core(umm_get_current_heap());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Without build option UMM_INLINE_METRICS, calls to umm_usage_metric() or
|
Without build option UMM_INLINE_METRICS, calls to umm_usage_metric() or
|
||||||
umm_fragmentation_metric() must to be preceeded by a call to umm_info(NULL, false)
|
umm_fragmentation_metric() must to be preceded by a call to umm_info(NULL, false)
|
||||||
for updated results.
|
for updated results.
|
||||||
*/
|
*/
|
||||||
int umm_usage_metric_core( umm_heap_context_t *_context ) {
|
int umm_usage_metric_core(umm_heap_context_t *_context) {
|
||||||
//C Note, umm_metrics also appears in the upstrean w/o definition. I suspect it is suppose to be ummHeapInfo.
|
// C Note, umm_metrics also appears in the upstrean w/o definition. I suspect it is suppose to be ummHeapInfo.
|
||||||
// DBGLOG_DEBUG( "usedBlocks %d totalBlocks %d\n", umm_metrics.usedBlocks, ummHeapInfo.totalBlocks);
|
// DBGLOG_DEBUG( "usedBlocks %d totalBlocks %d\n", umm_metrics.usedBlocks, ummHeapInfo.totalBlocks);
|
||||||
DBGLOG_DEBUG( "usedBlocks %d totalBlocks %d\n", _context->info.usedBlocks, _context->info.totalBlocks);
|
DBGLOG_DEBUG("usedBlocks %d totalBlocks %d\n", _context->info.usedBlocks, _context->info.totalBlocks);
|
||||||
if (_context->info.freeBlocks)
|
if (_context->info.freeBlocks) {
|
||||||
return (int)((_context->info.usedBlocks * 100)/(_context->info.freeBlocks));
|
return (int)((_context->info.usedBlocks * 100) / (_context->info.freeBlocks));
|
||||||
|
}
|
||||||
|
|
||||||
return -1; // no freeBlocks
|
return -1; // no freeBlocks
|
||||||
}
|
}
|
||||||
|
|
||||||
int umm_usage_metric( void ) {
|
int umm_usage_metric(void) {
|
||||||
#ifndef UMM_INLINE_METRICS
|
#ifndef UMM_INLINE_METRICS
|
||||||
umm_info(NULL, false);
|
umm_info(NULL, false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return umm_usage_metric_core(umm_get_current_heap());
|
return umm_usage_metric_core(umm_get_current_heap());
|
||||||
}
|
}
|
||||||
uint32_t sqrt32 (uint32_t n);
|
uint32_t sqrt32(uint32_t n);
|
||||||
|
|
||||||
int umm_fragmentation_metric_core( umm_heap_context_t *_context ) {
|
int umm_fragmentation_metric_core(umm_heap_context_t *_context) {
|
||||||
// DBGLOG_DEBUG( "freeBlocks %d freeBlocksSquared %d\n", umm_metrics.freeBlocks, ummHeapInfo.freeBlocksSquared);
|
// DBGLOG_DEBUG( "freeBlocks %d freeBlocksSquared %d\n", umm_metrics.freeBlocks, ummHeapInfo.freeBlocksSquared);
|
||||||
DBGLOG_DEBUG( "freeBlocks %d freeBlocksSquared %d\n", _context->info.freeBlocks, _context->info.freeBlocksSquared);
|
DBGLOG_DEBUG("freeBlocks %d freeBlocksSquared %d\n", _context->info.freeBlocks, _context->info.freeBlocksSquared);
|
||||||
if (0 == _context->info.freeBlocks) {
|
if (0 == _context->info.freeBlocks) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
//upstream version: return (100 - (((uint32_t)(sqrtf(ummHeapInfo.freeBlocksSquared)) * 100)/(ummHeapInfo.freeBlocks)));
|
// upstream version: return (100 - (((uint32_t)(sqrtf(ummHeapInfo.freeBlocksSquared)) * 100)/(ummHeapInfo.freeBlocks)));
|
||||||
return (100 - (((uint32_t)(sqrt32(_context->info.freeBlocksSquared)) * 100)/(_context->info.freeBlocks)));
|
return 100 - (((uint32_t)(sqrt32(_context->info.freeBlocksSquared)) * 100) / (_context->info.freeBlocks));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int umm_fragmentation_metric( void ) {
|
int umm_fragmentation_metric(void) {
|
||||||
#ifndef UMM_INLINE_METRICS
|
#ifndef UMM_INLINE_METRICS
|
||||||
umm_info(NULL, false);
|
umm_info(NULL, false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return umm_fragmentation_metric_core(umm_get_current_heap());
|
return umm_fragmentation_metric_core(umm_get_current_heap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef UMM_INLINE_METRICS
|
#ifdef UMM_INLINE_METRICS
|
||||||
static void umm_fragmentation_metric_init( umm_heap_context_t *_context ) {
|
static void umm_fragmentation_metric_init(umm_heap_context_t *_context) {
|
||||||
_context->info.freeBlocks = UMM_NUMBLOCKS - 2;
|
_context->info.freeBlocks = UMM_NUMBLOCKS - 2;
|
||||||
_context->info.freeBlocksSquared = _context->info.freeBlocks * _context->info.freeBlocks;
|
_context->info.freeBlocksSquared = _context->info.freeBlocks * _context->info.freeBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void umm_fragmentation_metric_add( umm_heap_context_t *_context, uint16_t c ) {
|
static void umm_fragmentation_metric_add(umm_heap_context_t *_context, uint16_t c) {
|
||||||
uint16_t blocks = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) - c;
|
uint16_t blocks = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) - c;
|
||||||
DBGLOG_DEBUG( "Add block %d size %d to free metric\n", c, blocks);
|
DBGLOG_DEBUG("Add block %d size %d to free metric\n", c, blocks);
|
||||||
_context->info.freeBlocks += blocks;
|
_context->info.freeBlocks += blocks;
|
||||||
_context->info.freeBlocksSquared += (blocks * blocks);
|
_context->info.freeBlocksSquared += (blocks * blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void umm_fragmentation_metric_remove( umm_heap_context_t *_context, uint16_t c ) {
|
static void umm_fragmentation_metric_remove(umm_heap_context_t *_context, uint16_t c) {
|
||||||
uint16_t blocks = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) - c;
|
uint16_t blocks = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) - c;
|
||||||
DBGLOG_DEBUG( "Remove block %d size %d from free metric\n", c, blocks);
|
DBGLOG_DEBUG("Remove block %d size %d from free metric\n", c, blocks);
|
||||||
_context->info.freeBlocks -= blocks;
|
_context->info.freeBlocks -= blocks;
|
||||||
_context->info.freeBlocksSquared -= (blocks * blocks);
|
_context->info.freeBlocksSquared -= (blocks * blocks);
|
||||||
}
|
}
|
||||||
|
@@ -28,109 +28,108 @@
|
|||||||
* chain.
|
* chain.
|
||||||
*/
|
*/
|
||||||
bool umm_integrity_check(void) {
|
bool umm_integrity_check(void) {
|
||||||
UMM_CRITICAL_DECL(id_integrity);
|
UMM_CRITICAL_DECL(id_integrity);
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
uint16_t prev;
|
uint16_t prev;
|
||||||
uint16_t cur;
|
uint16_t cur;
|
||||||
|
|
||||||
UMM_INIT_HEAP;
|
UMM_CHECK_INITIALIZED();
|
||||||
|
|
||||||
/* Iterate through all free blocks */
|
/* Iterate through all free blocks */
|
||||||
prev = 0;
|
prev = 0;
|
||||||
UMM_CRITICAL_ENTRY(id_integrity);
|
UMM_CRITICAL_ENTRY(id_integrity);
|
||||||
|
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
|
|
||||||
while(1) {
|
while (1) {
|
||||||
cur = UMM_NFREE(prev);
|
cur = UMM_NFREE(prev);
|
||||||
|
|
||||||
/* Check that next free block number is valid */
|
/* Check that next free block number is valid */
|
||||||
if (cur >= UMM_NUMBLOCKS) {
|
if (cur >= UMM_NUMBLOCKS) {
|
||||||
DBGLOG_FUNCTION("heap integrity broken: too large next free num: %d "
|
DBGLOG_FUNCTION("heap integrity broken: too large next free num: %d "
|
||||||
"(in block %d, addr 0x%08x)\n", cur, prev,
|
"(in block %d, addr 0x%08x)\n", cur, prev,
|
||||||
DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev)));
|
DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev)));
|
||||||
ok = false;
|
ok = false;
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
if (cur == 0) {
|
if (cur == 0) {
|
||||||
/* No more free blocks */
|
/* No more free blocks */
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if prev free block number matches */
|
||||||
|
if (UMM_PFREE(cur) != prev) {
|
||||||
|
DBGLOG_FUNCTION("heap integrity broken: free links don't match: "
|
||||||
|
"%d -> %d, but %d -> %d\n",
|
||||||
|
prev, cur, cur, UMM_PFREE(cur));
|
||||||
|
ok = false;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
UMM_PBLOCK(cur) |= UMM_FREELIST_MASK;
|
||||||
|
|
||||||
|
prev = cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if prev free block number matches */
|
/* Iterate through all blocks */
|
||||||
if (UMM_PFREE(cur) != prev) {
|
prev = 0;
|
||||||
DBGLOG_FUNCTION("heap integrity broken: free links don't match: "
|
while (1) {
|
||||||
"%d -> %d, but %d -> %d\n",
|
cur = UMM_NBLOCK(prev) & UMM_BLOCKNO_MASK;
|
||||||
prev, cur, cur, UMM_PFREE(cur));
|
|
||||||
ok = false;
|
|
||||||
goto clean;
|
|
||||||
}
|
|
||||||
|
|
||||||
UMM_PBLOCK(cur) |= UMM_FREELIST_MASK;
|
/* Check that next block number is valid */
|
||||||
|
if (cur >= UMM_NUMBLOCKS) {
|
||||||
|
DBGLOG_FUNCTION("heap integrity broken: too large next block num: %d "
|
||||||
|
"(in block %d, addr 0x%08x)\n", cur, prev,
|
||||||
|
DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev)));
|
||||||
|
ok = false;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
if (cur == 0) {
|
||||||
|
/* No more blocks */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
prev = cur;
|
/* make sure the free mark is appropriate, and unmark it */
|
||||||
}
|
if ((UMM_NBLOCK(cur) & UMM_FREELIST_MASK)
|
||||||
|
!= (UMM_PBLOCK(cur) & UMM_FREELIST_MASK)) {
|
||||||
|
DBGLOG_FUNCTION("heap integrity broken: mask wrong at addr 0x%08x: n=0x%x, p=0x%x\n",
|
||||||
|
DBGLOG_32_BIT_PTR(&UMM_NBLOCK(cur)),
|
||||||
|
(UMM_NBLOCK(cur) & UMM_FREELIST_MASK),
|
||||||
|
(UMM_PBLOCK(cur) & UMM_FREELIST_MASK));
|
||||||
|
ok = false;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
/* Iterate through all blocks */
|
/* make sure the block list is sequential */
|
||||||
prev = 0;
|
if (cur <= prev) {
|
||||||
while(1) {
|
DBGLOG_FUNCTION("heap integrity broken: next block %d is before prev this one "
|
||||||
cur = UMM_NBLOCK(prev) & UMM_BLOCKNO_MASK;
|
"(in block %d, addr 0x%08x)\n", cur, prev,
|
||||||
|
DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev)));
|
||||||
/* Check that next block number is valid */
|
ok = false;
|
||||||
if (cur >= UMM_NUMBLOCKS) {
|
goto clean;
|
||||||
DBGLOG_FUNCTION("heap integrity broken: too large next block num: %d "
|
}
|
||||||
"(in block %d, addr 0x%08x)\n", cur, prev,
|
|
||||||
DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev)));
|
|
||||||
ok = false;
|
|
||||||
goto clean;
|
|
||||||
}
|
|
||||||
if (cur == 0) {
|
|
||||||
/* No more blocks */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* make sure the free mark is appropriate, and unmark it */
|
|
||||||
if ((UMM_NBLOCK(cur) & UMM_FREELIST_MASK)
|
|
||||||
!= (UMM_PBLOCK(cur) & UMM_FREELIST_MASK))
|
|
||||||
{
|
|
||||||
DBGLOG_FUNCTION("heap integrity broken: mask wrong at addr 0x%08x: n=0x%x, p=0x%x\n",
|
|
||||||
DBGLOG_32_BIT_PTR(&UMM_NBLOCK(cur)),
|
|
||||||
(UMM_NBLOCK(cur) & UMM_FREELIST_MASK),
|
|
||||||
(UMM_PBLOCK(cur) & UMM_FREELIST_MASK));
|
|
||||||
ok = false;
|
|
||||||
goto clean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* make sure the block list is sequential */
|
|
||||||
if (cur <= prev ) {
|
|
||||||
DBGLOG_FUNCTION("heap integrity broken: next block %d is before prev this one "
|
|
||||||
"(in block %d, addr 0x%08x)\n", cur, prev,
|
|
||||||
DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev)));
|
|
||||||
ok = false;
|
|
||||||
goto clean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* unmark */
|
/* unmark */
|
||||||
UMM_PBLOCK(cur) &= UMM_BLOCKNO_MASK;
|
UMM_PBLOCK(cur) &= UMM_BLOCKNO_MASK;
|
||||||
|
|
||||||
/* Check if prev block number matches */
|
/* Check if prev block number matches */
|
||||||
if (UMM_PBLOCK(cur) != prev) {
|
if (UMM_PBLOCK(cur) != prev) {
|
||||||
DBGLOG_FUNCTION("heap integrity broken: block links don't match: "
|
DBGLOG_FUNCTION("heap integrity broken: block links don't match: "
|
||||||
"%d -> %d, but %d -> %d\n",
|
"%d -> %d, but %d -> %d\n",
|
||||||
prev, cur, cur, UMM_PBLOCK(cur));
|
prev, cur, cur, UMM_PBLOCK(cur));
|
||||||
ok = false;
|
ok = false;
|
||||||
goto clean;
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
prev = cur;
|
|
||||||
}
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
UMM_CRITICAL_EXIT(id_integrity);
|
UMM_CRITICAL_EXIT(id_integrity);
|
||||||
if (!ok){
|
if (!ok) {
|
||||||
UMM_HEAP_CORRUPTION_CB();
|
UMM_HEAP_CORRUPTION_CB();
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -6,28 +6,27 @@
|
|||||||
|
|
||||||
#if defined(UMM_CRITICAL_METRICS)
|
#if defined(UMM_CRITICAL_METRICS)
|
||||||
/*
|
/*
|
||||||
* umm_malloc performance measurments for critical sections
|
* umm_malloc performance measurements for critical sections
|
||||||
*/
|
*/
|
||||||
UMM_TIME_STATS time_stats = {
|
UMM_TIME_STATS time_stats = {
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
#ifdef UMM_INFO
|
#ifdef UMM_INFO
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
#endif
|
#endif
|
||||||
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
#endif
|
#endif
|
||||||
#ifdef UMM_INTEGRITY_CHECK
|
#ifdef UMM_INTEGRITY_CHECK
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
#endif
|
#endif
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U} };
|
{0xFFFFFFFF, 0U, 0U, 0U}
|
||||||
|
};
|
||||||
|
|
||||||
bool ICACHE_FLASH_ATTR get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size)
|
bool ICACHE_FLASH_ATTR get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size) {
|
||||||
{
|
|
||||||
UMM_CRITICAL_DECL(id_no_tag);
|
UMM_CRITICAL_DECL(id_no_tag);
|
||||||
if (p && sizeof(time_stats) == size)
|
if (p && sizeof(time_stats) == size) {
|
||||||
{
|
|
||||||
UMM_CRITICAL_ENTRY(id_no_tag);
|
UMM_CRITICAL_ENTRY(id_no_tag);
|
||||||
memcpy(p, &time_stats, size);
|
memcpy(p, &time_stats, size);
|
||||||
UMM_CRITICAL_EXIT(id_no_tag);
|
UMM_CRITICAL_EXIT(id_no_tag);
|
||||||
@@ -42,42 +41,45 @@ bool ICACHE_FLASH_ATTR get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size)
|
|||||||
#if defined(UMM_POISON_CHECK_LITE)
|
#if defined(UMM_POISON_CHECK_LITE)
|
||||||
// We skip this when doing the full poison check.
|
// We skip this when doing the full poison check.
|
||||||
|
|
||||||
static bool check_poison_neighbors( umm_heap_context_t *_context, uint16_t cur ) {
|
static bool check_poison_neighbors(umm_heap_context_t *_context, uint16_t cur) {
|
||||||
uint16_t c;
|
uint16_t c;
|
||||||
|
|
||||||
|
if (0 == cur) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = UMM_PBLOCK(cur) & UMM_BLOCKNO_MASK;
|
||||||
|
while (c && (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK)) {
|
||||||
|
/*
|
||||||
|
There can be up to 1 free block neighbor in either direction.
|
||||||
|
This loop should self limit to 2 passes, due to heap design.
|
||||||
|
i.e. Adjacent free space is always consolidated.
|
||||||
|
*/
|
||||||
|
if (!(UMM_NBLOCK(c) & UMM_FREELIST_MASK)) {
|
||||||
|
if (!check_poison_block(&UMM_BLOCK(c))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = UMM_PBLOCK(c) & UMM_BLOCKNO_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK;
|
||||||
|
while ((UMM_NBLOCK(c) & UMM_BLOCKNO_MASK)) {
|
||||||
|
if (!(UMM_NBLOCK(c) & UMM_FREELIST_MASK)) {
|
||||||
|
if (!check_poison_block(&UMM_BLOCK(c))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = UMM_NBLOCK(c) & UMM_BLOCKNO_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
if ( 0 == cur )
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
c = UMM_PBLOCK(cur) & UMM_BLOCKNO_MASK;
|
|
||||||
while( c && (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) ) {
|
|
||||||
/*
|
|
||||||
There can be up to 1 free block neighbor in either direction.
|
|
||||||
This loop should self limit to 2 passes, due to heap design.
|
|
||||||
i.e. Adjacent free space is always consolidated.
|
|
||||||
*/
|
|
||||||
if ( !(UMM_NBLOCK(c) & UMM_FREELIST_MASK) ) {
|
|
||||||
if ( !check_poison_block(&UMM_BLOCK(c)) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
c = UMM_PBLOCK(c) & UMM_BLOCKNO_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
c = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK;
|
|
||||||
while( (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) ) {
|
|
||||||
if ( !(UMM_NBLOCK(c) & UMM_FREELIST_MASK) ) {
|
|
||||||
if ( !check_poison_block(&UMM_BLOCK(c)) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
c = UMM_NBLOCK(c) & UMM_BLOCKNO_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -85,57 +87,60 @@ static bool check_poison_neighbors( umm_heap_context_t *_context, uint16_t cur )
|
|||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
static void *get_unpoisoned_check_neighbors( void *vptr, const char* file, int line ) {
|
static void *get_unpoisoned_check_neighbors(void *vptr, const char *file, int line) {
|
||||||
uintptr_t ptr = (uintptr_t)vptr;
|
uintptr_t ptr = (uintptr_t)vptr;
|
||||||
|
|
||||||
if (ptr != 0) {
|
if (ptr != 0) {
|
||||||
|
|
||||||
ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
|
ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
|
||||||
|
|
||||||
#if defined(UMM_POISON_CHECK_LITE)
|
#if defined(UMM_POISON_CHECK_LITE)
|
||||||
UMM_CRITICAL_DECL(id_poison);
|
UMM_CRITICAL_DECL(id_poison);
|
||||||
uint16_t c;
|
uint16_t c;
|
||||||
bool poison = false;
|
bool poison = false;
|
||||||
umm_heap_context_t *_context = umm_get_ptr_context( vptr );
|
umm_heap_context_t *_context = _umm_get_ptr_context((void *)ptr);
|
||||||
if (NULL == _context) {
|
if (_context) {
|
||||||
panic();
|
|
||||||
return NULL;
|
/* Figure out which block we're in. Note the use of truncated division... */
|
||||||
|
c = (ptr - (uintptr_t)(&(_context->heap[0]))) / sizeof(umm_block);
|
||||||
|
|
||||||
|
UMM_CRITICAL_ENTRY(id_poison);
|
||||||
|
poison =
|
||||||
|
check_poison_block(&UMM_BLOCK(c)) &&
|
||||||
|
check_poison_neighbors(_context, c);
|
||||||
|
UMM_CRITICAL_EXIT(id_poison);
|
||||||
|
} else {
|
||||||
|
DBGLOG_ERROR("\nPointer %p is not a Heap address.\n", vptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!poison) {
|
||||||
|
if (file) {
|
||||||
|
__panic_func(file, line, "");
|
||||||
|
} else {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* No need to check poison here. POISON_CHECK() has already done a
|
||||||
|
* full heap check.
|
||||||
|
*/
|
||||||
|
(void)file;
|
||||||
|
(void)line;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
/* Figure out which block we're in. Note the use of truncated division... */
|
|
||||||
c = (ptr - (uintptr_t)(&(_context->heap[0])))/sizeof(umm_block);
|
|
||||||
|
|
||||||
UMM_CRITICAL_ENTRY(id_poison);
|
return (void *)ptr;
|
||||||
poison = check_poison_block(&UMM_BLOCK(c)) && check_poison_neighbors(_context, c);
|
|
||||||
UMM_CRITICAL_EXIT(id_poison);
|
|
||||||
|
|
||||||
if (!poison) {
|
|
||||||
if (file) {
|
|
||||||
__panic_func(file, line, "");
|
|
||||||
} else {
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/*
|
|
||||||
* No need to check poison here. POISON_CHECK() has already done a
|
|
||||||
* full heap check.
|
|
||||||
*/
|
|
||||||
(void)file;
|
|
||||||
(void)line;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return (void *)ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
void *umm_poison_realloc_fl(void *ptr, size_t size, const char* file, int line) {
|
void *umm_poison_realloc_fl(void *ptr, size_t size, const char *file, int line) {
|
||||||
void *ret;
|
void *ret;
|
||||||
|
|
||||||
ptr = get_unpoisoned_check_neighbors(ptr, file, line);
|
ptr = get_unpoisoned_check_neighbors(ptr, file, line);
|
||||||
|
|
||||||
size += poison_size(size);
|
add_poison_size(&size);
|
||||||
ret = umm_realloc(ptr, size);
|
ret = umm_realloc(ptr, size);
|
||||||
|
|
||||||
ret = get_poisoned(ret, size);
|
ret = get_poisoned(ret, size);
|
||||||
@@ -145,7 +150,7 @@ void *umm_poison_realloc_fl(void *ptr, size_t size, const char* file, int line)
|
|||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
void umm_poison_free_fl(void *ptr, const char* file, int line) {
|
void umm_poison_free_fl(void *ptr, const char *file, int line) {
|
||||||
|
|
||||||
ptr = get_unpoisoned_check_neighbors(ptr, file, line);
|
ptr = get_unpoisoned_check_neighbors(ptr, file, line);
|
||||||
|
|
||||||
@@ -156,18 +161,18 @@ void umm_poison_free_fl(void *ptr, const char* file, int line) {
|
|||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
|
||||||
size_t umm_block_size( void ) {
|
size_t umm_block_size(void) {
|
||||||
return sizeof(umm_block);
|
return sizeof(umm_block);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
// Keep complete call path in IRAM
|
// Keep complete call path in IRAM
|
||||||
size_t umm_free_heap_size_lw( void ) {
|
size_t umm_free_heap_size_lw(void) {
|
||||||
UMM_INIT_HEAP;
|
UMM_CHECK_INITIALIZED();
|
||||||
|
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return (size_t)_context->UMM_FREE_BLOCKS * sizeof(umm_block);
|
return (size_t)_context->UMM_FREE_BLOCKS * sizeof(umm_block);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -187,19 +192,19 @@ size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size")));
|
|||||||
|
|
||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
void umm_print_stats(int force) {
|
void umm_print_stats(int force) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
|
|
||||||
DBGLOG_FORCE( force, "umm heap statistics:\n");
|
DBGLOG_FORCE(force, "umm heap statistics:\n");
|
||||||
DBGLOG_FORCE( force, " Heap ID %5u\n", _context->id);
|
DBGLOG_FORCE(force, " Heap ID %7u\n", _context->id);
|
||||||
DBGLOG_FORCE( force, " Free Space %5u\n", _context->UMM_FREE_BLOCKS * sizeof(umm_block));
|
DBGLOG_FORCE(force, " Free Space %7u\n", _context->UMM_FREE_BLOCKS * sizeof(umm_block));
|
||||||
DBGLOG_FORCE( force, " OOM Count %5u\n", _context->UMM_OOM_COUNT);
|
DBGLOG_FORCE(force, " OOM Count %7u\n", _context->UMM_OOM_COUNT);
|
||||||
#if defined(UMM_STATS_FULL)
|
#if defined(UMM_STATS_FULL)
|
||||||
DBGLOG_FORCE( force, " Low Watermark %5u\n", _context->stats.free_blocks_min * sizeof(umm_block));
|
DBGLOG_FORCE(force, " Low Watermark %7u\n", _context->stats.free_blocks_min * sizeof(umm_block));
|
||||||
DBGLOG_FORCE( force, " Low Watermark ISR %5u\n", _context->stats.free_blocks_isr_min * sizeof(umm_block));
|
DBGLOG_FORCE(force, " Low Watermark ISR %7u\n", _context->stats.free_blocks_isr_min * sizeof(umm_block));
|
||||||
DBGLOG_FORCE( force, " MAX Alloc Request %5u\n", _context->stats.alloc_max_size);
|
DBGLOG_FORCE(force, " MAX Alloc Request %7u\n", _context->stats.alloc_max_size);
|
||||||
#endif
|
#endif
|
||||||
DBGLOG_FORCE( force, " Size of umm_block %5u\n", sizeof(umm_block));
|
DBGLOG_FORCE(force, " Size of umm_block %7u\n", sizeof(umm_block));
|
||||||
DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" );
|
DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -214,9 +219,9 @@ int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
size_t ICACHE_FLASH_ATTR umm_get_oom_count( void ) {
|
size_t ICACHE_FLASH_ATTR umm_get_oom_count(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->UMM_OOM_COUNT;
|
return _context->UMM_OOM_COUNT;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -228,70 +233,98 @@ size_t ICACHE_FLASH_ATTR umm_get_oom_count( void ) {
|
|||||||
//
|
//
|
||||||
// If this is correct use alias.
|
// If this is correct use alias.
|
||||||
//
|
//
|
||||||
size_t ICACHE_FLASH_ATTR umm_free_heap_size_lw_min( void ) {
|
size_t ICACHE_FLASH_ATTR umm_free_heap_size_lw_min(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.free_blocks_min * umm_block_size();
|
return _context->stats.free_blocks_min * umm_block_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset( void ) {
|
size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
_context->stats.free_blocks_min = _context->UMM_FREE_BLOCKS;
|
_context->stats.free_blocks_min = _context->UMM_FREE_BLOCKS;
|
||||||
return _context->stats.free_blocks_min * umm_block_size();
|
return _context->stats.free_blocks_min * umm_block_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0 // TODO - Don't understand this why do both umm_free_heap_size_(lw_)min exist
|
#if 0 // TODO - Don't understand this why do both umm_free_heap_size_(lw_)min exist
|
||||||
size_t umm_free_heap_size_min(void) __attribute__ ((alias("umm_free_heap_size_lw_min")));
|
size_t umm_free_heap_size_min(void) __attribute__ ((alias("umm_free_heap_size_lw_min")));
|
||||||
#else
|
#else
|
||||||
size_t ICACHE_FLASH_ATTR umm_free_heap_size_min( void ) {
|
size_t ICACHE_FLASH_ATTR umm_free_heap_size_min(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.free_blocks_min * umm_block_size();
|
return _context->stats.free_blocks_min * umm_block_size();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_free_heap_size_isr_min( void ) {
|
size_t ICACHE_FLASH_ATTR umm_free_heap_size_isr_min(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.free_blocks_isr_min * umm_block_size();
|
return _context->stats.free_blocks_isr_min * umm_block_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_get_max_alloc_size( void ) {
|
size_t ICACHE_FLASH_ATTR umm_get_max_alloc_size(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.alloc_max_size;
|
return _context->stats.alloc_max_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_get_last_alloc_size( void ) {
|
size_t ICACHE_FLASH_ATTR umm_get_last_alloc_size(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.last_alloc_size;
|
return _context->stats.last_alloc_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_get_malloc_count( void ) {
|
size_t ICACHE_FLASH_ATTR umm_get_malloc_count(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.id_malloc_count;
|
return _context->stats.id_malloc_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_get_malloc_zero_count( void ) {
|
size_t ICACHE_FLASH_ATTR umm_get_malloc_zero_count(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.id_malloc_zero_count;
|
return _context->stats.id_malloc_zero_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_get_realloc_count( void ) {
|
size_t ICACHE_FLASH_ATTR umm_get_realloc_count(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.id_realloc_count;
|
return _context->stats.id_realloc_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_get_realloc_zero_count( void ) {
|
size_t ICACHE_FLASH_ATTR umm_get_realloc_zero_count(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.id_realloc_zero_count;
|
return _context->stats.id_realloc_zero_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_get_free_count( void ) {
|
size_t ICACHE_FLASH_ATTR umm_get_free_count(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.id_free_count;
|
return _context->stats.id_free_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_get_free_null_count( void ) {
|
size_t ICACHE_FLASH_ATTR umm_get_free_null_count(void) {
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
return _context->stats.id_free_null_count;
|
return _context->stats.id_free_null_count;
|
||||||
}
|
}
|
||||||
#endif // UMM_STATS_FULL
|
#endif // UMM_STATS_FULL
|
||||||
|
|
||||||
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
|
/*
|
||||||
|
* Saturated unsigned add
|
||||||
|
* Poison added to allocation size requires overflow protection.
|
||||||
|
*/
|
||||||
|
static size_t umm_uadd_sat(const size_t a, const size_t b) {
|
||||||
|
size_t r = a + b;
|
||||||
|
if (r < a) {
|
||||||
|
return SIZE_MAX;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use platform-specific functions to protect against unsigned overflow/wrap by
|
||||||
|
* implementing saturated unsigned multiply.
|
||||||
|
* The function umm_calloc requires a saturated multiply function.
|
||||||
|
*/
|
||||||
|
size_t umm_umul_sat(const size_t a, const size_t b) {
|
||||||
|
size_t r;
|
||||||
|
if (__builtin_mul_overflow(a, b, &r)) {
|
||||||
|
return SIZE_MAX;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // BUILD_UMM_MALLOC_C
|
#endif // BUILD_UMM_MALLOC_C
|
||||||
|
@@ -15,6 +15,14 @@
|
|||||||
#define memset ets_memset
|
#define memset ets_memset
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Saturated unsigned add and unsigned multiply
|
||||||
|
*/
|
||||||
|
size_t umm_umul_sat(const size_t a, const size_t b); // share with heap.cpp
|
||||||
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
|
static size_t umm_uadd_sat(const size_t a, const size_t b);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This redefines DBGLOG_FORCE defined in dbglog/dbglog.h
|
* This redefines DBGLOG_FORCE defined in dbglog/dbglog.h
|
||||||
* Just for printing from umm_info() which is assumed to always be called from
|
* Just for printing from umm_info() which is assumed to always be called from
|
||||||
@@ -22,7 +30,7 @@
|
|||||||
* string while INTLEVEL is non-zero.
|
* string while INTLEVEL is non-zero.
|
||||||
*/
|
*/
|
||||||
#undef DBGLOG_FORCE
|
#undef DBGLOG_FORCE
|
||||||
#define DBGLOG_FORCE(force, format, ...) {if(force) {UMM_INFO_PRINTF(format, ## __VA_ARGS__);}}
|
#define DBGLOG_FORCE(force, format, ...) {if (force) {UMM_INFO_PRINTF(format,##__VA_ARGS__);}}
|
||||||
// #define DBGLOG_FORCE(force, format, ...) {if(force) {::printf(PSTR(format), ## __VA_ARGS__);}}
|
// #define DBGLOG_FORCE(force, format, ...) {if(force) {::printf(PSTR(format), ## __VA_ARGS__);}}
|
||||||
|
|
||||||
|
|
||||||
@@ -37,7 +45,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#if defined(UMM_POISON_CHECK_LITE)
|
#if defined(UMM_POISON_CHECK_LITE)
|
||||||
static bool check_poison_neighbors( umm_heap_context_t *_context, uint16_t cur );
|
static bool check_poison_neighbors(umm_heap_context_t *_context, uint16_t cur);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -48,22 +56,22 @@ void ICACHE_FLASH_ATTR umm_print_stats(int force);
|
|||||||
|
|
||||||
|
|
||||||
int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||||
#define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR(fmt), ##__VA_ARGS__)
|
#define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR(fmt),##__VA_ARGS__)
|
||||||
|
|
||||||
|
|
||||||
typedef struct umm_block_t umm_block;
|
typedef struct umm_block_t umm_block;
|
||||||
|
|
||||||
struct UMM_HEAP_CONTEXT {
|
struct UMM_HEAP_CONTEXT {
|
||||||
umm_block *heap;
|
umm_block *heap;
|
||||||
void *heap_end;
|
void *heap_end;
|
||||||
#if (!defined(UMM_INLINE_METRICS) && defined(UMM_STATS)) || defined(UMM_STATS_FULL)
|
#if (!defined(UMM_INLINE_METRICS) && defined(UMM_STATS)) || defined(UMM_STATS_FULL)
|
||||||
UMM_STATISTICS stats;
|
UMM_STATISTICS stats;
|
||||||
#endif
|
#endif
|
||||||
#ifdef UMM_INFO
|
#ifdef UMM_INFO
|
||||||
UMM_HEAP_INFO info;
|
UMM_HEAP_INFO info;
|
||||||
#endif
|
#endif
|
||||||
unsigned short int numblocks;
|
unsigned short int numblocks;
|
||||||
unsigned char id;
|
unsigned char id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
//C This include is not in upstream
|
// C This include is not in upstream
|
||||||
#include "umm_malloc_cfg.h" /* user-dependent */
|
#include "umm_malloc_cfg.h" /* user-dependent */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -19,28 +19,28 @@ extern "C" {
|
|||||||
|
|
||||||
|
|
||||||
#ifdef UMM_HEAP_EXTERNAL
|
#ifdef UMM_HEAP_EXTERNAL
|
||||||
extern void umm_init_vm( void *vmaddr, unsigned int vmsize );
|
extern void umm_init_vm(void *vmaddr, unsigned int vmsize);
|
||||||
#endif
|
#endif
|
||||||
#ifdef UMM_HEAP_IRAM
|
#ifdef UMM_HEAP_IRAM
|
||||||
extern void umm_init_iram(void);
|
extern void umm_init_iram(void);
|
||||||
extern void umm_init_iram_ex( void *addr, unsigned int size, bool zero );
|
extern void umm_init_iram_ex(void *addr, unsigned int size, bool zero);
|
||||||
#endif
|
#endif
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
extern void umm_init( void );
|
extern void umm_init(void);
|
||||||
extern void *umm_malloc( size_t size );
|
extern void *umm_malloc(size_t size);
|
||||||
extern void *umm_calloc( size_t num, size_t size );
|
extern void *umm_calloc(size_t num, size_t size);
|
||||||
extern void *umm_realloc( void *ptr, size_t size );
|
extern void *umm_realloc(void *ptr, size_t size);
|
||||||
extern void umm_free( void *ptr );
|
extern void umm_free(void *ptr);
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
extern umm_heap_context_t *umm_push_heap( size_t heap_number );
|
extern umm_heap_context_t *umm_push_heap(size_t heap_number);
|
||||||
extern umm_heap_context_t *umm_pop_heap( void );
|
extern umm_heap_context_t *umm_pop_heap(void);
|
||||||
extern int umm_get_heap_stack_index( void );
|
extern int umm_get_heap_stack_index(void);
|
||||||
extern umm_heap_context_t *umm_set_heap_by_id( size_t which );
|
extern umm_heap_context_t *umm_set_heap_by_id(size_t which);
|
||||||
extern size_t umm_get_current_heap_id( void );
|
extern size_t umm_get_current_heap_id(void);
|
||||||
extern umm_heap_context_t *umm_get_current_heap( void );
|
extern umm_heap_context_t *umm_get_current_heap(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* Configuration for umm_malloc - target Arduino ESP8266 core
|
|
||||||
*
|
|
||||||
* Changes specific to a target platform go here.
|
|
||||||
*
|
|
||||||
* This comment section changed to below in the upstream version, keeping old method for now.
|
|
||||||
*
|
|
||||||
* Configuration for umm_malloc - DO NOT EDIT THIS FILE BY HAND!
|
* Configuration for umm_malloc - DO NOT EDIT THIS FILE BY HAND!
|
||||||
*
|
*
|
||||||
* Refer to the notes below for how to configure the build at compile time
|
* NOTE WELL: Your project MUST have a umm_malloc_cfgport.h - even if
|
||||||
* using -D to define non-default values
|
* it's empty!!!
|
||||||
|
*
|
||||||
|
* Refer to the notes below for details on the umm_malloc configuration
|
||||||
|
* options.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Minimized changes in umm_malloc_cfg.h, transition Arduino ESP8266 specific
|
||||||
|
* changes to umm_malloc_cfgport.h.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _UMM_MALLOC_CFG_H
|
#ifndef _UMM_MALLOC_CFG_H
|
||||||
@@ -18,65 +20,10 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include <pgmspace.h>
|
|
||||||
#include <mmu_iram.h>
|
|
||||||
#include "../debug.h"
|
|
||||||
#include "../esp8266_undocumented.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <core_esp8266_features.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <osapi.h>
|
|
||||||
|
|
||||||
#include "c_types.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Define active Heaps
|
|
||||||
*/
|
|
||||||
#if defined(MMU_IRAM_HEAP)
|
|
||||||
#define UMM_HEAP_IRAM
|
|
||||||
#else
|
|
||||||
#undef UMM_HEAP_IRAM
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(MMU_EXTERNAL_HEAP)
|
|
||||||
#define UMM_HEAP_EXTERNAL
|
|
||||||
#else
|
|
||||||
#undef UMM_HEAP_EXTERNAL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Assign IDs to active Heaps and tally. DRAM is always active.
|
|
||||||
*/
|
|
||||||
#define UMM_HEAP_DRAM 0
|
|
||||||
#define UMM_HEAP_DRAM_DEFINED 1
|
|
||||||
|
|
||||||
#ifdef UMM_HEAP_IRAM
|
|
||||||
#undef UMM_HEAP_IRAM
|
|
||||||
#define UMM_HEAP_IRAM_DEFINED 1
|
|
||||||
#define UMM_HEAP_IRAM UMM_HEAP_DRAM_DEFINED
|
|
||||||
#else
|
|
||||||
#define UMM_HEAP_IRAM_DEFINED 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef UMM_HEAP_EXTERNAL
|
|
||||||
#undef UMM_HEAP_EXTERNAL
|
|
||||||
#define UMM_HEAP_EXTERNAL_DEFINED 1
|
|
||||||
#define UMM_HEAP_EXTERNAL (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED)
|
|
||||||
#else
|
|
||||||
#define UMM_HEAP_EXTERNAL_DEFINED 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define UMM_NUM_HEAPS (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED + UMM_HEAP_EXTERNAL_DEFINED)
|
|
||||||
|
|
||||||
#if (UMM_NUM_HEAPS == 1)
|
|
||||||
#else
|
|
||||||
#define UMM_HEAP_STACK_DEPTH 32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are a number of defines you can set at compile time that affect how
|
* There are a number of defines you can set at compile time that affect how
|
||||||
* the memory allocator will operate.
|
* the memory allocator will operate.
|
||||||
@@ -116,6 +63,16 @@ extern "C" {
|
|||||||
* Setting this at compile time will automatically set UMM_INFO.
|
* Setting this at compile time will automatically set UMM_INFO.
|
||||||
* Note that enabling this define will add a slight runtime penalty.
|
* Note that enabling this define will add a slight runtime penalty.
|
||||||
*
|
*
|
||||||
|
* UMM_CHECK_INITIALIZED
|
||||||
|
*
|
||||||
|
* Set if you want to be able to verify that the heap is intialized
|
||||||
|
* before any operation - the default is no check. You may set the
|
||||||
|
* UMM_CHECK_INITIALIZED macro to the following provided macros, or
|
||||||
|
* write your own handler:
|
||||||
|
*
|
||||||
|
* UMM_INIT_IF_UNINITIALIZED
|
||||||
|
* UMM_HANG_IF_UNINITIALIZED
|
||||||
|
*
|
||||||
* UMM_INTEGRITY_CHECK
|
* UMM_INTEGRITY_CHECK
|
||||||
*
|
*
|
||||||
* Set if you want to be able to verify that the heap is semantically correct
|
* Set if you want to be able to verify that the heap is semantically correct
|
||||||
@@ -143,6 +100,12 @@ extern "C" {
|
|||||||
* ----------------------------------------------------------------------------
|
* ----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef UMM_CFGFILE
|
||||||
|
#include UMM_CFGFILE
|
||||||
|
#else
|
||||||
|
#include "umm_malloc_cfgport.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define UMM_BEST_FIT
|
#define UMM_BEST_FIT
|
||||||
#define UMM_INFO
|
#define UMM_INFO
|
||||||
// #define UMM_INLINE_METRICS
|
// #define UMM_INLINE_METRICS
|
||||||
@@ -168,22 +131,6 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifdef UMM_TEST_BUILD
|
|
||||||
extern char test_umm_heap[];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef UMM_TEST_BUILD
|
|
||||||
/* Start addresses and the size of the heap */
|
|
||||||
#define UMM_MALLOC_CFG_HEAP_ADDR (test_umm_heap)
|
|
||||||
#define UMM_MALLOC_CFG_HEAP_SIZE 0x10000
|
|
||||||
#else
|
|
||||||
/* Start addresses and the size of the heap */
|
|
||||||
extern char _heap_start[];
|
|
||||||
#define UMM_HEAP_END_ADDR 0x3FFFC000UL
|
|
||||||
#define UMM_MALLOC_CFG_HEAP_ADDR ((uint32_t)&_heap_start[0])
|
|
||||||
#define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(UMM_HEAP_END_ADDR - UMM_MALLOC_CFG_HEAP_ADDR))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* A couple of macros to make packing structures less compiler dependent */
|
/* A couple of macros to make packing structures less compiler dependent */
|
||||||
|
|
||||||
#define UMM_H_ATTPACKPRE
|
#define UMM_H_ATTPACKPRE
|
||||||
@@ -191,14 +138,28 @@ extern char _heap_start[];
|
|||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#ifndef UMM_INIT_IF_UNINITIALIZED
|
||||||
|
#define UMM_INIT_IF_UNINITIALIZED() do { if (UMM_HEAP == NULL) { umm_init(); } } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef UMM_HANG_IF_UNINITIALIZED
|
||||||
|
#define UMM_HANG_IF_UNINITIALIZED() do { if (UMM_HEAP == NULL) { while (1) {} } } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef UMM_CHECK_INITIALIZED
|
||||||
|
#define UMM_CHECK_INITIALIZED()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
#ifdef UMM_BEST_FIT
|
#ifdef UMM_BEST_FIT
|
||||||
#ifdef UMM_FIRST_FIT
|
#ifdef UMM_FIRST_FIT
|
||||||
#error Both UMM_BEST_FIT and UMM_FIRST_FIT are defined - pick one!
|
#error Both UMM_BEST_FIT and UMM_FIRST_FIT are defined - pick one!
|
||||||
#endif
|
#endif
|
||||||
#else /* UMM_BEST_FIT is not defined */
|
#else /* UMM_BEST_FIT is not defined */
|
||||||
#ifndef UMM_FIRST_FIT
|
#ifndef UMM_FIRST_FIT
|
||||||
#define UMM_BEST_FIT
|
#define UMM_BEST_FIT
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
@@ -207,9 +168,9 @@ extern char _heap_start[];
|
|||||||
#define UMM_FRAGMENTATION_METRIC_INIT() umm_fragmentation_metric_init(_context)
|
#define UMM_FRAGMENTATION_METRIC_INIT() umm_fragmentation_metric_init(_context)
|
||||||
#define UMM_FRAGMENTATION_METRIC_ADD(c) umm_fragmentation_metric_add(_context, c)
|
#define UMM_FRAGMENTATION_METRIC_ADD(c) umm_fragmentation_metric_add(_context, c)
|
||||||
#define UMM_FRAGMENTATION_METRIC_REMOVE(c) umm_fragmentation_metric_remove(_context, c)
|
#define UMM_FRAGMENTATION_METRIC_REMOVE(c) umm_fragmentation_metric_remove(_context, c)
|
||||||
#ifndef UMM_INFO
|
#ifndef UMM_INFO
|
||||||
#define UMM_INFO
|
#define UMM_INFO
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#define UMM_FRAGMENTATION_METRIC_INIT()
|
#define UMM_FRAGMENTATION_METRIC_INIT()
|
||||||
#define UMM_FRAGMENTATION_METRIC_ADD(c)
|
#define UMM_FRAGMENTATION_METRIC_ADD(c)
|
||||||
@@ -229,7 +190,7 @@ extern char _heap_start[];
|
|||||||
// #define UMM_INFO
|
// #define UMM_INFO
|
||||||
|
|
||||||
#ifdef UMM_INFO
|
#ifdef UMM_INFO
|
||||||
typedef struct UMM_HEAP_INFO_t {
|
typedef struct UMM_HEAP_INFO_t {
|
||||||
unsigned int totalEntries;
|
unsigned int totalEntries;
|
||||||
unsigned int usedEntries;
|
unsigned int usedEntries;
|
||||||
unsigned int freeEntries;
|
unsigned int freeEntries;
|
||||||
@@ -238,33 +199,33 @@ extern char _heap_start[];
|
|||||||
unsigned int usedBlocks;
|
unsigned int usedBlocks;
|
||||||
unsigned int freeBlocks;
|
unsigned int freeBlocks;
|
||||||
unsigned int freeBlocksSquared;
|
unsigned int freeBlocksSquared;
|
||||||
#ifdef UMM_INLINE_METRICS
|
#ifdef UMM_INLINE_METRICS
|
||||||
size_t oom_count;
|
size_t oom_count;
|
||||||
#define UMM_OOM_COUNT info.oom_count
|
#define UMM_OOM_COUNT info.oom_count
|
||||||
#define UMM_FREE_BLOCKS info.freeBlocks
|
#define UMM_FREE_BLOCKS info.freeBlocks
|
||||||
#endif
|
#endif
|
||||||
unsigned int maxFreeContiguousBlocks;
|
unsigned int maxFreeContiguousBlocks;
|
||||||
}
|
}
|
||||||
UMM_HEAP_INFO;
|
UMM_HEAP_INFO;
|
||||||
|
|
||||||
// extern UMM_HEAP_INFO ummHeapInfo;
|
// extern UMM_HEAP_INFO ummHeapInfo;
|
||||||
struct UMM_HEAP_CONTEXT;
|
struct UMM_HEAP_CONTEXT;
|
||||||
typedef struct UMM_HEAP_CONTEXT umm_heap_context_t;
|
typedef struct UMM_HEAP_CONTEXT umm_heap_context_t;
|
||||||
|
|
||||||
extern ICACHE_FLASH_ATTR void *umm_info( void *ptr, bool force );
|
extern ICACHE_FLASH_ATTR void *umm_info(void *ptr, bool force);
|
||||||
#ifdef UMM_INLINE_METRICS
|
#ifdef UMM_INLINE_METRICS
|
||||||
extern size_t umm_free_heap_size( void );
|
extern size_t umm_free_heap_size(void);
|
||||||
#else
|
#else
|
||||||
extern ICACHE_FLASH_ATTR size_t umm_free_heap_size( void );
|
extern ICACHE_FLASH_ATTR size_t umm_free_heap_size(void);
|
||||||
#endif
|
#endif
|
||||||
// umm_max_block_size changed to umm_max_free_block_size in upstream.
|
// umm_max_block_size changed to umm_max_free_block_size in upstream.
|
||||||
extern ICACHE_FLASH_ATTR size_t umm_max_block_size( void );
|
extern ICACHE_FLASH_ATTR size_t umm_max_block_size(void);
|
||||||
extern ICACHE_FLASH_ATTR int umm_usage_metric( void );
|
extern ICACHE_FLASH_ATTR int umm_usage_metric(void);
|
||||||
extern ICACHE_FLASH_ATTR int umm_fragmentation_metric( void );
|
extern ICACHE_FLASH_ATTR int umm_fragmentation_metric(void);
|
||||||
extern ICACHE_FLASH_ATTR size_t umm_free_heap_size_core( umm_heap_context_t *_context );
|
extern ICACHE_FLASH_ATTR size_t umm_free_heap_size_core(umm_heap_context_t *_context);
|
||||||
extern ICACHE_FLASH_ATTR size_t umm_max_block_size_core( umm_heap_context_t *_context );
|
extern ICACHE_FLASH_ATTR size_t umm_max_block_size_core(umm_heap_context_t *_context);
|
||||||
extern ICACHE_FLASH_ATTR int umm_usage_metric_core( umm_heap_context_t *_context );
|
extern ICACHE_FLASH_ATTR int umm_usage_metric_core(umm_heap_context_t *_context);
|
||||||
extern ICACHE_FLASH_ATTR int umm_fragmentation_metric_core( umm_heap_context_t *_context );
|
extern ICACHE_FLASH_ATTR int umm_fragmentation_metric_core(umm_heap_context_t *_context);
|
||||||
#else
|
#else
|
||||||
#define umm_info(p,b)
|
#define umm_info(p,b)
|
||||||
#define umm_free_heap_size() (0)
|
#define umm_free_heap_size() (0)
|
||||||
@@ -312,27 +273,27 @@ typedef struct UMM_HEAP_CONTEXT umm_heap_context_t;
|
|||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
|
|
||||||
typedef struct UMM_STATISTICS_t {
|
typedef struct UMM_STATISTICS_t {
|
||||||
#ifndef UMM_INLINE_METRICS
|
#ifndef UMM_INLINE_METRICS
|
||||||
// If we are doing UMM_INLINE_METRICS, we can move oom_count and free_blocks to
|
// If we are doing UMM_INLINE_METRICS, we can move oom_count and free_blocks to
|
||||||
// umm_info's structure and save a little DRAM and IRAM.
|
// umm_info's structure and save a little DRAM and IRAM.
|
||||||
// Otherwise it is defined here.
|
// Otherwise it is defined here.
|
||||||
size_t free_blocks;
|
size_t free_blocks;
|
||||||
size_t oom_count;
|
size_t oom_count;
|
||||||
#define UMM_OOM_COUNT stats.oom_count
|
#define UMM_OOM_COUNT stats.oom_count
|
||||||
#define UMM_FREE_BLOCKS stats.free_blocks
|
#define UMM_FREE_BLOCKS stats.free_blocks
|
||||||
#endif
|
#endif
|
||||||
#ifdef UMM_STATS_FULL
|
#ifdef UMM_STATS_FULL
|
||||||
size_t free_blocks_min;
|
size_t free_blocks_min;
|
||||||
size_t free_blocks_isr_min;
|
size_t free_blocks_isr_min;
|
||||||
size_t alloc_max_size;
|
size_t alloc_max_size;
|
||||||
size_t last_alloc_size;
|
size_t last_alloc_size;
|
||||||
size_t id_malloc_count;
|
size_t id_malloc_count;
|
||||||
size_t id_malloc_zero_count;
|
size_t id_malloc_zero_count;
|
||||||
size_t id_realloc_count;
|
size_t id_realloc_count;
|
||||||
size_t id_realloc_zero_count;
|
size_t id_realloc_zero_count;
|
||||||
size_t id_free_count;
|
size_t id_free_count;
|
||||||
size_t id_free_null_count;
|
size_t id_free_null_count;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
UMM_STATISTICS;
|
UMM_STATISTICS;
|
||||||
|
|
||||||
@@ -344,8 +305,8 @@ UMM_STATISTICS;
|
|||||||
|
|
||||||
#define STATS__OOM_UPDATE() _context->UMM_OOM_COUNT += 1
|
#define STATS__OOM_UPDATE() _context->UMM_OOM_COUNT += 1
|
||||||
|
|
||||||
extern size_t umm_free_heap_size_lw( void );
|
extern size_t umm_free_heap_size_lw(void);
|
||||||
extern size_t umm_get_oom_count( void );
|
extern size_t umm_get_oom_count(void);
|
||||||
|
|
||||||
#else // not UMM_STATS or UMM_STATS_FULL
|
#else // not UMM_STATS or UMM_STATS_FULL
|
||||||
#define STATS__FREE_BLOCKS_UPDATE(s) (void)(s)
|
#define STATS__FREE_BLOCKS_UPDATE(s) (void)(s)
|
||||||
@@ -353,59 +314,62 @@ extern size_t umm_get_oom_count( void );
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
|
||||||
size_t ICACHE_FLASH_ATTR umm_block_size( void );
|
size_t ICACHE_FLASH_ATTR umm_block_size(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef UMM_STATS_FULL
|
#ifdef UMM_STATS_FULL
|
||||||
#define STATS__FREE_BLOCKS_MIN() \
|
#define STATS__FREE_BLOCKS_MIN() \
|
||||||
do { \
|
do { \
|
||||||
if (_context->UMM_FREE_BLOCKS < _context->stats.free_blocks_min) \
|
if (_context->UMM_FREE_BLOCKS < _context->stats.free_blocks_min) { \
|
||||||
_context->stats.free_blocks_min = _context->UMM_FREE_BLOCKS; \
|
_context->stats.free_blocks_min = _context->UMM_FREE_BLOCKS; \
|
||||||
} while(false)
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
#define STATS__FREE_BLOCKS_ISR_MIN() \
|
#define STATS__FREE_BLOCKS_ISR_MIN() \
|
||||||
do { \
|
do { \
|
||||||
if (_context->UMM_FREE_BLOCKS < _context->stats.free_blocks_isr_min) \
|
if (_context->UMM_FREE_BLOCKS < _context->stats.free_blocks_isr_min) { \
|
||||||
_context->stats.free_blocks_isr_min = _context->UMM_FREE_BLOCKS; \
|
_context->stats.free_blocks_isr_min = _context->UMM_FREE_BLOCKS; \
|
||||||
} while(false)
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
#define STATS__ALLOC_REQUEST(tag, s) \
|
#define STATS__ALLOC_REQUEST(tag, s) \
|
||||||
do { \
|
do { \
|
||||||
_context->stats.tag##_count += 1; \
|
_context->stats.tag##_count += 1; \
|
||||||
_context->stats.last_alloc_size = s; \
|
_context->stats.last_alloc_size = s; \
|
||||||
if (_context->stats.alloc_max_size < s) \
|
if (_context->stats.alloc_max_size < s) { \
|
||||||
_context->stats.alloc_max_size = s; \
|
_context->stats.alloc_max_size = s; \
|
||||||
} while(false)
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
#define STATS__ZERO_ALLOC_REQUEST(tag, s) \
|
#define STATS__ZERO_ALLOC_REQUEST(tag, s) \
|
||||||
do { \
|
do { \
|
||||||
_context->stats.tag##_zero_count += 1; \
|
_context->stats.tag##_zero_count += 1; \
|
||||||
} while(false)
|
} while (false)
|
||||||
|
|
||||||
#define STATS__NULL_FREE_REQUEST(tag) \
|
#define STATS__NULL_FREE_REQUEST(tag) \
|
||||||
do { \
|
do { \
|
||||||
umm_heap_context_t *_context = umm_get_current_heap(); \
|
umm_heap_context_t *_context = umm_get_current_heap(); \
|
||||||
_context->stats.tag##_null_count += 1; \
|
_context->stats.tag##_null_count += 1; \
|
||||||
} while(false)
|
} while (false)
|
||||||
|
|
||||||
#define STATS__FREE_REQUEST(tag) \
|
#define STATS__FREE_REQUEST(tag) \
|
||||||
do { \
|
do { \
|
||||||
_context->stats.tag##_count += 1; \
|
_context->stats.tag##_count += 1; \
|
||||||
} while(false)
|
} while (false)
|
||||||
|
|
||||||
|
|
||||||
size_t umm_free_heap_size_lw_min( void );
|
size_t umm_free_heap_size_lw_min(void);
|
||||||
size_t umm_free_heap_size_min_reset( void );
|
size_t umm_free_heap_size_min_reset(void);
|
||||||
size_t umm_free_heap_size_min( void );
|
size_t umm_free_heap_size_min(void);
|
||||||
size_t umm_free_heap_size_isr_min( void );
|
size_t umm_free_heap_size_isr_min(void);
|
||||||
size_t umm_get_max_alloc_size( void );
|
size_t umm_get_max_alloc_size(void);
|
||||||
size_t umm_get_last_alloc_size( void );
|
size_t umm_get_last_alloc_size(void);
|
||||||
size_t umm_get_malloc_count( void );
|
size_t umm_get_malloc_count(void);
|
||||||
size_t umm_get_malloc_zero_count( void );
|
size_t umm_get_malloc_zero_count(void);
|
||||||
size_t umm_get_realloc_count( void );
|
size_t umm_get_realloc_count(void);
|
||||||
size_t umm_get_realloc_zero_count( void );
|
size_t umm_get_realloc_zero_count(void);
|
||||||
size_t umm_get_free_count( void );
|
size_t umm_get_free_count(void);
|
||||||
size_t umm_get_free_null_count( void );
|
size_t umm_get_free_null_count(void);
|
||||||
|
|
||||||
#else // Not UMM_STATS_FULL
|
#else // Not UMM_STATS_FULL
|
||||||
#define STATS__FREE_BLOCKS_MIN() (void)0
|
#define STATS__FREE_BLOCKS_MIN() (void)0
|
||||||
@@ -448,10 +412,10 @@ size_t umm_get_free_null_count( void );
|
|||||||
// This option adds support for gathering time locked data
|
// This option adds support for gathering time locked data
|
||||||
|
|
||||||
typedef struct UMM_TIME_STAT_t {
|
typedef struct UMM_TIME_STAT_t {
|
||||||
uint32_t min;
|
uint32_t min;
|
||||||
uint32_t max;
|
uint32_t max;
|
||||||
uint32_t start;
|
uint32_t start;
|
||||||
uint32_t intlevel;
|
uint32_t intlevel;
|
||||||
}
|
}
|
||||||
UMM_TIME_STAT;
|
UMM_TIME_STAT;
|
||||||
|
|
||||||
@@ -462,23 +426,25 @@ extern UMM_TIME_STATS time_stats;
|
|||||||
bool get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size);
|
bool get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size);
|
||||||
|
|
||||||
static inline void _critical_entry(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
static inline void _critical_entry(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
||||||
*saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL);
|
*saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL);
|
||||||
if (0U != (*saved_ps & 0x0FU)) {
|
if (0U != (*saved_ps & 0x0FU)) {
|
||||||
p->intlevel += 1U;
|
p->intlevel += 1U;
|
||||||
}
|
}
|
||||||
|
|
||||||
p->start = esp_get_cycle_count();
|
p->start = esp_get_cycle_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
||||||
uint32_t elapse = esp_get_cycle_count() - p->start;
|
uint32_t elapse = esp_get_cycle_count() - p->start;
|
||||||
if (elapse < p->min)
|
if (elapse < p->min) {
|
||||||
p->min = elapse;
|
p->min = elapse;
|
||||||
|
}
|
||||||
|
|
||||||
if (elapse > p->max)
|
if (elapse > p->max) {
|
||||||
p->max = elapse;
|
p->max = elapse;
|
||||||
|
}
|
||||||
|
|
||||||
xt_wsr_ps(*saved_ps);
|
xt_wsr_ps(*saved_ps);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -495,33 +461,33 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef UMM_TEST_BUILD
|
#ifdef UMM_TEST_BUILD
|
||||||
extern int umm_critical_depth;
|
extern int umm_critical_depth;
|
||||||
extern int umm_max_critical_depth;
|
extern int umm_max_critical_depth;
|
||||||
#define UMM_CRITICAL_ENTRY() {\
|
#define UMM_CRITICAL_ENTRY() { \
|
||||||
++umm_critical_depth; \
|
++umm_critical_depth; \
|
||||||
if (umm_critical_depth > umm_max_critical_depth) { \
|
if (umm_critical_depth > umm_max_critical_depth) { \
|
||||||
umm_max_critical_depth = umm_critical_depth; \
|
umm_max_critical_depth = umm_critical_depth; \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
#define UMM_CRITICAL_EXIT() (umm_critical_depth--)
|
#define UMM_CRITICAL_EXIT() (umm_critical_depth--)
|
||||||
#else
|
#else
|
||||||
#if defined(UMM_CRITICAL_METRICS)
|
#if defined(UMM_CRITICAL_METRICS)
|
||||||
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
|
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
|
||||||
#define UMM_CRITICAL_ENTRY(tag)_critical_entry(&time_stats.tag, &_saved_ps_##tag)
|
#define UMM_CRITICAL_ENTRY(tag)_critical_entry(&time_stats.tag, &_saved_ps_##tag)
|
||||||
#define UMM_CRITICAL_EXIT(tag) _critical_exit(&time_stats.tag, &_saved_ps_##tag)
|
#define UMM_CRITICAL_EXIT(tag) _critical_exit(&time_stats.tag, &_saved_ps_##tag)
|
||||||
#define UMM_CRITICAL_WITHINISR(tag) (0 != (_saved_ps_##tag & 0x0F))
|
#define UMM_CRITICAL_WITHINISR(tag) (0 != (_saved_ps_##tag & 0x0F))
|
||||||
|
|
||||||
#else // ! UMM_CRITICAL_METRICS
|
#else // ! UMM_CRITICAL_METRICS
|
||||||
// This method preserves the intlevel on entry and restores the
|
// This method preserves the intlevel on entry and restores the
|
||||||
// original intlevel at exit.
|
// original intlevel at exit.
|
||||||
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
|
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
|
||||||
#define UMM_CRITICAL_ENTRY(tag) _saved_ps_##tag = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL)
|
#define UMM_CRITICAL_ENTRY(tag) _saved_ps_##tag = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL)
|
||||||
#define UMM_CRITICAL_EXIT(tag) xt_wsr_ps(_saved_ps_##tag)
|
#define UMM_CRITICAL_EXIT(tag) xt_wsr_ps(_saved_ps_##tag)
|
||||||
#define UMM_CRITICAL_WITHINISR(tag) (0 != (_saved_ps_##tag & 0x0F))
|
#define UMM_CRITICAL_WITHINISR(tag) (0 != (_saved_ps_##tag & 0x0F))
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* -D UMM_LIGHTWEIGHT_CPU
|
* -D UMM_LIGHTWEIGHT_CPU
|
||||||
*
|
*
|
||||||
* The use of this macro is hardware/application specific.
|
* The use of this macro is hardware/application specific.
|
||||||
@@ -550,8 +516,8 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
|||||||
#define UMM_CRITICAL_SUSPEND(tag) UMM_CRITICAL_EXIT(tag)
|
#define UMM_CRITICAL_SUSPEND(tag) UMM_CRITICAL_EXIT(tag)
|
||||||
#define UMM_CRITICAL_RESUME(tag) UMM_CRITICAL_ENTRY(tag)
|
#define UMM_CRITICAL_RESUME(tag) UMM_CRITICAL_ENTRY(tag)
|
||||||
#else
|
#else
|
||||||
#define UMM_CRITICAL_SUSPEND(tag) do {} while(0)
|
#define UMM_CRITICAL_SUSPEND(tag) do {} while (0)
|
||||||
#define UMM_CRITICAL_RESUME(tag) do {} while(0)
|
#define UMM_CRITICAL_RESUME(tag) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -564,7 +530,7 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
|||||||
* direction of the beginning of the heap when possible.
|
* direction of the beginning of the heap when possible.
|
||||||
*
|
*
|
||||||
* Status: TODO: These are new options introduced to optionally restore the
|
* Status: TODO: These are new options introduced to optionally restore the
|
||||||
* previous defrag propery of realloc. The issue has been raised in the upstream
|
* previous defrag property of realloc. The issue has been raised in the upstream
|
||||||
* repo. No response at this time. Based on response, may propose for upstream.
|
* repo. No response at this time. Based on response, may propose for upstream.
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
@@ -594,12 +560,12 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef UMM_INTEGRITY_CHECK
|
#ifdef UMM_INTEGRITY_CHECK
|
||||||
extern bool umm_integrity_check( void );
|
extern bool umm_integrity_check(void);
|
||||||
# define INTEGRITY_CHECK() umm_integrity_check()
|
#define INTEGRITY_CHECK() umm_integrity_check()
|
||||||
extern void umm_corruption(void);
|
extern void umm_corruption(void);
|
||||||
# define UMM_HEAP_CORRUPTION_CB() DBGLOG_FUNCTION( "Heap Corruption!" )
|
#define UMM_HEAP_CORRUPTION_CB() DBGLOG_FUNCTION("Heap Corruption!")
|
||||||
#else
|
#else
|
||||||
# define INTEGRITY_CHECK() (1)
|
#define INTEGRITY_CHECK() (1)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
@@ -669,33 +635,33 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
|||||||
#define UMM_POISONED_BLOCK_LEN_TYPE uint32_t
|
#define UMM_POISONED_BLOCK_LEN_TYPE uint32_t
|
||||||
|
|
||||||
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
extern void *umm_poison_malloc( size_t size );
|
extern void *umm_poison_malloc(size_t size);
|
||||||
extern void *umm_poison_calloc( size_t num, size_t size );
|
extern void *umm_poison_calloc(size_t num, size_t size);
|
||||||
extern void *umm_poison_realloc( void *ptr, size_t size );
|
extern void *umm_poison_realloc(void *ptr, size_t size);
|
||||||
extern void umm_poison_free( void *ptr );
|
extern void umm_poison_free(void *ptr);
|
||||||
extern bool umm_poison_check( void );
|
extern bool umm_poison_check(void);
|
||||||
// Local Additions to better report location in code of the caller.
|
// Local Additions to better report location in code of the caller.
|
||||||
void *umm_poison_realloc_fl( void *ptr, size_t size, const char* file, int line );
|
void *umm_poison_realloc_fl(void *ptr, size_t size, const char *file, int line);
|
||||||
void umm_poison_free_fl( void *ptr, const char* file, int line );
|
void umm_poison_free_fl(void *ptr, const char *file, int line);
|
||||||
#if defined(UMM_POISON_CHECK_LITE)
|
#if defined(UMM_POISON_CHECK_LITE)
|
||||||
/*
|
/*
|
||||||
* We can safely do individual poison checks at free and realloc and stay
|
* We can safely do individual poison checks at free and realloc and stay
|
||||||
* under 10us or close.
|
* under 10us or close.
|
||||||
*/
|
*/
|
||||||
# define POISON_CHECK() 1
|
#define POISON_CHECK() 1
|
||||||
# define POISON_CHECK_NEIGHBORS(c) \
|
#define POISON_CHECK_NEIGHBORS(c) \
|
||||||
do {\
|
do { \
|
||||||
if(!check_poison_neighbors(_context, c)) \
|
if (!check_poison_neighbors(_context, c)) \
|
||||||
panic();\
|
panic(); \
|
||||||
} while(false)
|
} while (false)
|
||||||
#else
|
|
||||||
/* Not normally enabled. A full heap poison check may exceed 10us. */
|
|
||||||
# define POISON_CHECK() umm_poison_check()
|
|
||||||
# define POISON_CHECK_NEIGHBORS(c) do{}while(false)
|
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
# define POISON_CHECK() 1
|
/* Not normally enabled. A full heap poison check may exceed 10us. */
|
||||||
# define POISON_CHECK_NEIGHBORS(c) do{}while(false)
|
#define POISON_CHECK() umm_poison_check()
|
||||||
|
#define POISON_CHECK_NEIGHBORS(c) do {} while (false)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define POISON_CHECK() 1
|
||||||
|
#define POISON_CHECK_NEIGHBORS(c) do {} while (false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -705,13 +671,13 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
|||||||
* that can actually be allocated.
|
* that can actually be allocated.
|
||||||
*/
|
*/
|
||||||
#define UMM_OVERHEAD_ADJUST ( \
|
#define UMM_OVERHEAD_ADJUST ( \
|
||||||
umm_block_size()/2 + \
|
umm_block_size() / 2 + \
|
||||||
UMM_POISON_SIZE_BEFORE + \
|
UMM_POISON_SIZE_BEFORE + \
|
||||||
UMM_POISON_SIZE_AFTER + \
|
UMM_POISON_SIZE_AFTER + \
|
||||||
sizeof(UMM_POISONED_BLOCK_LEN_TYPE))
|
sizeof(UMM_POISONED_BLOCK_LEN_TYPE))
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define UMM_OVERHEAD_ADJUST (umm_block_size()/2)
|
#define UMM_OVERHEAD_ADJUST (umm_block_size() / 2)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -722,9 +688,9 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
|||||||
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_OOM) || \
|
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_OOM) || \
|
||||||
defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || \
|
defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || \
|
||||||
defined(UMM_INTEGRITY_CHECK)
|
defined(UMM_INTEGRITY_CHECK)
|
||||||
#define DBGLOG_FUNCTION(fmt, ...) ets_uart_printf(fmt, ##__VA_ARGS__)
|
#define DBGLOG_FUNCTION(fmt, ...) ets_uart_printf(fmt,##__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define DBGLOG_FUNCTION(fmt, ...) do { (void)fmt; } while(false)
|
#define DBGLOG_FUNCTION(fmt, ...) do { (void)fmt; } while (false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
@@ -739,19 +705,19 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
|||||||
|
|
||||||
#if defined(UMM_CRITICAL_METRICS)
|
#if defined(UMM_CRITICAL_METRICS)
|
||||||
struct UMM_TIME_STATS_t {
|
struct UMM_TIME_STATS_t {
|
||||||
UMM_TIME_STAT id_malloc;
|
UMM_TIME_STAT id_malloc;
|
||||||
UMM_TIME_STAT id_realloc;
|
UMM_TIME_STAT id_realloc;
|
||||||
UMM_TIME_STAT id_free;
|
UMM_TIME_STAT id_free;
|
||||||
#ifdef UMM_INFO
|
#ifdef UMM_INFO
|
||||||
UMM_TIME_STAT id_info;
|
UMM_TIME_STAT id_info;
|
||||||
#endif
|
#endif
|
||||||
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
UMM_TIME_STAT id_poison;
|
UMM_TIME_STAT id_poison;
|
||||||
#endif
|
#endif
|
||||||
#ifdef UMM_INTEGRITY_CHECK
|
#ifdef UMM_INTEGRITY_CHECK
|
||||||
UMM_TIME_STAT id_integrity;
|
UMM_TIME_STAT id_integrity;
|
||||||
#endif
|
#endif
|
||||||
UMM_TIME_STAT id_no_tag;
|
UMM_TIME_STAT id_no_tag;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
@@ -764,17 +730,17 @@ struct UMM_TIME_STATS_t {
|
|||||||
|
|
||||||
#define umm_zalloc(s) umm_calloc(1,s)
|
#define umm_zalloc(s) umm_calloc(1,s)
|
||||||
|
|
||||||
void* malloc_loc (size_t s, const char* file, int line);
|
void *malloc_loc(size_t s, const char *file, int line);
|
||||||
void* calloc_loc (size_t n, size_t s, const char* file, int line);
|
void *calloc_loc(size_t n, size_t s, const char *file, int line);
|
||||||
void* realloc_loc (void* p, size_t s, const char* file, int line);
|
void *realloc_loc(void *p, size_t s, const char *file, int line);
|
||||||
// *alloc are macro calling *alloc_loc calling+checking umm_*alloc()
|
// *alloc are macro calling *alloc_loc calling+checking umm_*alloc()
|
||||||
// they are defined at the bottom of this file
|
// they are defined at the bottom of this file
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
|
|
||||||
#elif defined(UMM_POISON_CHECK)
|
#elif defined(UMM_POISON_CHECK)
|
||||||
void* realloc_loc (void* p, size_t s, const char* file, int line);
|
void *realloc_loc(void *p, size_t s, const char *file, int line);
|
||||||
void free_loc (void* p, const char* file, int line);
|
void free_loc(void *p, const char *file, int line);
|
||||||
#else // !defined(ESP_DEBUG_OOM)
|
#else // !defined(ESP_DEBUG_OOM)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -796,12 +762,12 @@ extern "C" {
|
|||||||
// Arduino.h recall us to redefine them
|
// Arduino.h recall us to redefine them
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
// Reuse pvPort* calls, since they already support passing location information.
|
// Reuse pvPort* calls, since they already support passing location information.
|
||||||
// Specificly the debug version (heap_...) that does not force DRAM heap.
|
// Specifically the debug version (heap_...) that does not force DRAM heap.
|
||||||
void* IRAM_ATTR heap_pvPortMalloc(size_t size, const char* file, int line);
|
void *IRAM_ATTR heap_pvPortMalloc(size_t size, const char *file, int line);
|
||||||
void* IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char* file, int line);
|
void *IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char *file, int line);
|
||||||
void* IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char* file, int line);
|
void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line);
|
||||||
void* IRAM_ATTR heap_pvPortZalloc(size_t size, const char* file, int line);
|
void *IRAM_ATTR heap_pvPortZalloc(size_t size, const char *file, int line);
|
||||||
void IRAM_ATTR heap_vPortFree(void *ptr, const char* file, int line);
|
void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line);
|
||||||
|
|
||||||
#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortMalloc(s, mem_debug_file, __LINE__); })
|
#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortMalloc(s, mem_debug_file, __LINE__); })
|
||||||
#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortCalloc(n, s, mem_debug_file, __LINE__); })
|
#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortCalloc(n, s, mem_debug_file, __LINE__); })
|
||||||
@@ -813,13 +779,13 @@ void IRAM_ATTR heap_vPortFree(void *ptr, const char* file, int line);
|
|||||||
#define dbg_heap_free(p) free(p)
|
#define dbg_heap_free(p) free(p)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#elif defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
#elif defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) // #elif for #ifdef DEBUG_ESP_OOM
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
void* IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char* file, int line);
|
void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line);
|
||||||
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); })
|
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); })
|
||||||
|
|
||||||
void IRAM_ATTR heap_vPortFree(void *ptr, const char* file, int line);
|
void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line);
|
||||||
//C - to be discussed
|
// C - to be discussed
|
||||||
/*
|
/*
|
||||||
Problem, I would like to report the file and line number with the umm poison
|
Problem, I would like to report the file and line number with the umm poison
|
||||||
event as close as possible to the event. The #define method works for malloc,
|
event as close as possible to the event. The #define method works for malloc,
|
||||||
|
91
cores/esp8266/umm_malloc/umm_malloc_cfgport.h
Normal file
91
cores/esp8266/umm_malloc/umm_malloc_cfgport.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#ifndef _UMM_MALLOC_CFGPORT_H
|
||||||
|
#define _UMM_MALLOC_CFGPORT_H
|
||||||
|
|
||||||
|
#ifndef _UMM_MALLOC_CFG_H
|
||||||
|
#error "This include file must be used with umm_malloc_cfg.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Arduino ESP8266 core umm_malloc port config
|
||||||
|
*/
|
||||||
|
#include <pgmspace.h>
|
||||||
|
#include <mmu_iram.h>
|
||||||
|
#include "../debug.h"
|
||||||
|
#include "../esp8266_undocumented.h"
|
||||||
|
|
||||||
|
#include <core_esp8266_features.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <osapi.h>
|
||||||
|
|
||||||
|
#include "c_types.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -DUMM_INIT_USE_IRAM
|
||||||
|
*
|
||||||
|
* Historically, the umm_init() call path has been in IRAM. The umm_init() call
|
||||||
|
* path is now in ICACHE (flash). Use the build option UMM_INIT_USE_IRAM to
|
||||||
|
* restore the legacy behavor.
|
||||||
|
*
|
||||||
|
* If you have your own app_entry_redefinable() function, see
|
||||||
|
* app_entry_redefinable() in core_esp8266_app_entry_noextra4k.cpp for an
|
||||||
|
* example of how to toggle between ICACHE and IRAM in your build.
|
||||||
|
*
|
||||||
|
* The default is to use ICACHE.
|
||||||
|
*/
|
||||||
|
// #define UMM_INIT_USE_IRAM 1
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start addresses and the size of the heap
|
||||||
|
*/
|
||||||
|
extern char _heap_start[];
|
||||||
|
#define UMM_HEAP_END_ADDR 0x3FFFC000UL
|
||||||
|
#define UMM_MALLOC_CFG_HEAP_ADDR ((uint32_t)&_heap_start[0])
|
||||||
|
#define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(UMM_HEAP_END_ADDR - UMM_MALLOC_CFG_HEAP_ADDR))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define active Heaps
|
||||||
|
*/
|
||||||
|
#if defined(MMU_IRAM_HEAP)
|
||||||
|
#define UMM_HEAP_IRAM
|
||||||
|
#else
|
||||||
|
#undef UMM_HEAP_IRAM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(MMU_EXTERNAL_HEAP)
|
||||||
|
#define UMM_HEAP_EXTERNAL
|
||||||
|
#else
|
||||||
|
#undef UMM_HEAP_EXTERNAL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assign IDs to active Heaps and tally. DRAM is always active.
|
||||||
|
*/
|
||||||
|
#define UMM_HEAP_DRAM 0
|
||||||
|
#define UMM_HEAP_DRAM_DEFINED 1
|
||||||
|
|
||||||
|
#ifdef UMM_HEAP_IRAM
|
||||||
|
#undef UMM_HEAP_IRAM
|
||||||
|
#define UMM_HEAP_IRAM_DEFINED 1
|
||||||
|
#define UMM_HEAP_IRAM UMM_HEAP_DRAM_DEFINED
|
||||||
|
#else
|
||||||
|
#define UMM_HEAP_IRAM_DEFINED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef UMM_HEAP_EXTERNAL
|
||||||
|
#undef UMM_HEAP_EXTERNAL
|
||||||
|
#define UMM_HEAP_EXTERNAL_DEFINED 1
|
||||||
|
#define UMM_HEAP_EXTERNAL (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED)
|
||||||
|
#else
|
||||||
|
#define UMM_HEAP_EXTERNAL_DEFINED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define UMM_NUM_HEAPS (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED + UMM_HEAP_EXTERNAL_DEFINED)
|
||||||
|
|
||||||
|
#if (UMM_NUM_HEAPS == 1)
|
||||||
|
#else
|
||||||
|
#define UMM_HEAP_STACK_DEPTH 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@@ -8,32 +8,36 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define UMM_POISON_BLOCK_SIZE (UMM_POISON_SIZE_BEFORE + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_AFTER)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Yields a size of the poison for the block of size `s`.
|
* Yields the total size of a poison block of size `s`.
|
||||||
* If `s` is 0, returns 0.
|
* If `s` is 0, returns 0.
|
||||||
|
* If result overflows/wraps, return saturation value.
|
||||||
*/
|
*/
|
||||||
static size_t poison_size(size_t s) {
|
static void add_poison_size(size_t* s) {
|
||||||
return(s ? (UMM_POISON_SIZE_BEFORE +
|
if (*s == 0) {
|
||||||
sizeof(UMM_POISONED_BLOCK_LEN_TYPE) +
|
return;
|
||||||
UMM_POISON_SIZE_AFTER)
|
}
|
||||||
: 0);
|
|
||||||
|
*s = umm_uadd_sat(*s, UMM_POISON_BLOCK_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Print memory contents starting from given `ptr`
|
* Print memory contents starting from given `ptr`
|
||||||
*/
|
*/
|
||||||
static void dump_mem ( const void *vptr, size_t len ) {
|
static void dump_mem(const void *vptr, size_t len) {
|
||||||
const uint8_t *ptr = (const uint8_t *)vptr;
|
const uint8_t *ptr = (const uint8_t *)vptr;
|
||||||
while (len--) {
|
while (len--) {
|
||||||
DBGLOG_ERROR(" 0x%.2x", (unsigned int)(*ptr++));
|
DBGLOG_ERROR(" 0x%.2x", (unsigned int)(*ptr++));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Put poison data at given `ptr` and `poison_size`
|
* Put poison data at given `ptr` and `poison_size`
|
||||||
*/
|
*/
|
||||||
static void put_poison( void *ptr, size_t poison_size ) {
|
static void put_poison(void *ptr, size_t poison_size) {
|
||||||
memset(ptr, POISON_BYTE, poison_size);
|
memset(ptr, POISON_BYTE, poison_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -43,56 +47,56 @@ static void put_poison( void *ptr, size_t poison_size ) {
|
|||||||
* If poison is there, returns 1.
|
* If poison is there, returns 1.
|
||||||
* Otherwise, prints the appropriate message, and returns 0.
|
* Otherwise, prints the appropriate message, and returns 0.
|
||||||
*/
|
*/
|
||||||
static bool check_poison( const void *ptr, size_t poison_size,
|
static bool check_poison(const void *ptr, size_t poison_size,
|
||||||
const char *where) {
|
const char *where) {
|
||||||
size_t i;
|
size_t i;
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
|
||||||
for (i = 0; i < poison_size; i++) {
|
for (i = 0; i < poison_size; i++) {
|
||||||
if (((const uint8_t *)ptr)[i] != POISON_BYTE) {
|
if (((const uint8_t *)ptr)[i] != POISON_BYTE) {
|
||||||
ok = false;
|
ok = false;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
DBGLOG_ERROR( "No poison %s block at: 0x%lx, actual data:", where, (unsigned long)ptr);
|
DBGLOG_ERROR("No poison %s block at: 0x%lx, actual data:", where, (unsigned long)ptr);
|
||||||
dump_mem(ptr, poison_size);
|
dump_mem(ptr, poison_size);
|
||||||
DBGLOG_ERROR( "\n" );
|
DBGLOG_ERROR("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if a block is properly poisoned. Must be called only for non-free
|
* Check if a block is properly poisoned. Must be called only for non-free
|
||||||
* blocks.
|
* blocks.
|
||||||
*/
|
*/
|
||||||
static bool check_poison_block( umm_block *pblock ) {
|
static bool check_poison_block(umm_block *pblock) {
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
|
||||||
if (pblock->header.used.next & UMM_FREELIST_MASK) {
|
if (pblock->header.used.next & UMM_FREELIST_MASK) {
|
||||||
DBGLOG_ERROR( "check_poison_block is called for free block 0x%lx\n", (unsigned long)pblock);
|
DBGLOG_ERROR("check_poison_block is called for free block 0x%lx\n", (unsigned long)pblock);
|
||||||
} else {
|
} else {
|
||||||
/* the block is used; let's check poison */
|
/* the block is used; let's check poison */
|
||||||
unsigned char *pc = (unsigned char *)pblock->body.data;
|
unsigned char *pc = (unsigned char *)pblock->body.data;
|
||||||
unsigned char *pc_cur;
|
unsigned char *pc_cur;
|
||||||
|
|
||||||
pc_cur = pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE);
|
pc_cur = pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE);
|
||||||
if (!check_poison(pc_cur, UMM_POISON_SIZE_BEFORE, "before")) {
|
if (!check_poison(pc_cur, UMM_POISON_SIZE_BEFORE, "before")) {
|
||||||
ok = false;
|
ok = false;
|
||||||
goto clean;
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
pc_cur = pc + *((UMM_POISONED_BLOCK_LEN_TYPE *)pc) - UMM_POISON_SIZE_AFTER;
|
||||||
|
if (!check_poison(pc_cur, UMM_POISON_SIZE_AFTER, "after")) {
|
||||||
|
ok = false;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pc_cur = pc + *((UMM_POISONED_BLOCK_LEN_TYPE *)pc) - UMM_POISON_SIZE_AFTER;
|
|
||||||
if (!check_poison(pc_cur, UMM_POISON_SIZE_AFTER, "after")) {
|
|
||||||
ok = false;
|
|
||||||
goto clean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -102,25 +106,25 @@ clean:
|
|||||||
*
|
*
|
||||||
* `size_w_poison` is a size of the whole block, including a poison.
|
* `size_w_poison` is a size of the whole block, including a poison.
|
||||||
*/
|
*/
|
||||||
static void *get_poisoned( void *vptr, size_t size_w_poison ) {
|
static void *get_poisoned(void *vptr, size_t size_w_poison) {
|
||||||
unsigned char *ptr = (unsigned char *)vptr;
|
unsigned char *ptr = (unsigned char *)vptr;
|
||||||
|
|
||||||
if (size_w_poison != 0 && ptr != NULL) {
|
if (size_w_poison != 0 && ptr != NULL) {
|
||||||
|
|
||||||
/* Poison beginning and the end of the allocated chunk */
|
/* Poison beginning and the end of the allocated chunk */
|
||||||
put_poison(ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE),
|
put_poison(ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE),
|
||||||
UMM_POISON_SIZE_BEFORE);
|
UMM_POISON_SIZE_BEFORE);
|
||||||
put_poison(ptr + size_w_poison - UMM_POISON_SIZE_AFTER,
|
put_poison(ptr + size_w_poison - UMM_POISON_SIZE_AFTER,
|
||||||
UMM_POISON_SIZE_AFTER);
|
UMM_POISON_SIZE_AFTER);
|
||||||
|
|
||||||
/* Put exact length of the user's chunk of memory */
|
/* Put exact length of the user's chunk of memory */
|
||||||
*(UMM_POISONED_BLOCK_LEN_TYPE *)ptr = (UMM_POISONED_BLOCK_LEN_TYPE)size_w_poison;
|
*(UMM_POISONED_BLOCK_LEN_TYPE *)ptr = (UMM_POISONED_BLOCK_LEN_TYPE)size_w_poison;
|
||||||
|
|
||||||
/* Return pointer at the first non-poisoned byte */
|
/* Return pointer at the first non-poisoned byte */
|
||||||
ptr += sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE;
|
ptr += sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (void *)ptr;
|
return (void *)ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -129,84 +133,86 @@ static void *get_poisoned( void *vptr, size_t size_w_poison ) {
|
|||||||
*
|
*
|
||||||
* Returns unpoisoned pointer, i.e. actual pointer to the allocated memory.
|
* Returns unpoisoned pointer, i.e. actual pointer to the allocated memory.
|
||||||
*/
|
*/
|
||||||
static void *get_unpoisoned( void *vptr ) {
|
static void *get_unpoisoned(void *vptr) {
|
||||||
uintptr_t ptr = (uintptr_t)vptr;
|
uintptr_t ptr = (uintptr_t)vptr;
|
||||||
|
|
||||||
if (ptr != 0) {
|
if (ptr != 0) {
|
||||||
uint16_t c;
|
uint16_t c;
|
||||||
|
|
||||||
ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
|
ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
|
||||||
|
|
||||||
umm_heap_context_t *_context = umm_get_ptr_context( vptr );
|
umm_heap_context_t *_context = umm_get_ptr_context(vptr);
|
||||||
if (NULL == _context) {
|
NON_NULL_CONTEXT_ASSERT();
|
||||||
panic();
|
|
||||||
return NULL;
|
/* Figure out which block we're in. Note the use of truncated division... */
|
||||||
|
c = (ptr - (uintptr_t)(&(_context->heap[0]))) / sizeof(umm_block);
|
||||||
|
|
||||||
|
check_poison_block(&UMM_BLOCK(c));
|
||||||
}
|
}
|
||||||
/* Figure out which block we're in. Note the use of truncated division... */
|
|
||||||
c = (ptr - (uintptr_t)(&(_context->heap[0])))/sizeof(umm_block);
|
|
||||||
|
|
||||||
check_poison_block(&UMM_BLOCK(c));
|
return (void *)ptr;
|
||||||
}
|
|
||||||
|
|
||||||
return (void *)ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
void *umm_poison_malloc( size_t size ) {
|
void *umm_poison_malloc(size_t size) {
|
||||||
void *ret;
|
void *ret;
|
||||||
|
|
||||||
size += poison_size(size);
|
add_poison_size(&size);
|
||||||
|
|
||||||
ret = umm_malloc( size );
|
ret = umm_malloc(size);
|
||||||
|
|
||||||
ret = get_poisoned(ret, size);
|
ret = get_poisoned(ret, size);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
void *umm_poison_calloc( size_t num, size_t item_size ) {
|
void *umm_poison_calloc(size_t num, size_t item_size) {
|
||||||
void *ret;
|
void *ret;
|
||||||
size_t size = item_size * num;
|
|
||||||
|
|
||||||
size += poison_size(size);
|
// Use saturated multiply.
|
||||||
|
// Rely on umm_malloc to supply the fail response as needed.
|
||||||
|
size_t size = umm_umul_sat(num, item_size);
|
||||||
|
|
||||||
ret = umm_malloc(size);
|
add_poison_size(&size);
|
||||||
|
|
||||||
if (NULL != ret)
|
ret = umm_malloc(size);
|
||||||
memset(ret, 0x00, size);
|
|
||||||
|
|
||||||
ret = get_poisoned(ret, size);
|
if (NULL != ret) {
|
||||||
|
memset(ret, 0x00, size);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
ret = get_poisoned(ret, size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
void *umm_poison_realloc( void *ptr, size_t size ) {
|
void *umm_poison_realloc(void *ptr, size_t size) {
|
||||||
void *ret;
|
void *ret;
|
||||||
|
|
||||||
ptr = get_unpoisoned(ptr);
|
ptr = get_unpoisoned(ptr);
|
||||||
|
|
||||||
size += poison_size(size);
|
add_poison_size(&size);
|
||||||
ret = umm_realloc( ptr, size );
|
ret = umm_realloc(ptr, size);
|
||||||
|
|
||||||
ret = get_poisoned(ret, size);
|
ret = get_poisoned(ret, size);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
void umm_poison_free( void *ptr ) {
|
void umm_poison_free(void *ptr) {
|
||||||
|
|
||||||
ptr = get_unpoisoned(ptr);
|
ptr = get_unpoisoned(ptr);
|
||||||
|
|
||||||
umm_free( ptr );
|
umm_free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -215,32 +221,32 @@ void umm_poison_free( void *ptr ) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
bool umm_poison_check(void) {
|
bool umm_poison_check(void) {
|
||||||
UMM_CRITICAL_DECL(id_poison);
|
UMM_CRITICAL_DECL(id_poison);
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
uint16_t cur;
|
uint16_t cur;
|
||||||
|
|
||||||
UMM_INIT_HEAP;
|
UMM_CHECK_INITIALIZED();
|
||||||
|
|
||||||
UMM_CRITICAL_ENTRY(id_poison);
|
UMM_CRITICAL_ENTRY(id_poison);
|
||||||
umm_heap_context_t *_context = umm_get_current_heap();
|
umm_heap_context_t *_context = umm_get_current_heap();
|
||||||
|
|
||||||
/* Now iterate through the blocks list */
|
/* Now iterate through the blocks list */
|
||||||
cur = UMM_NBLOCK(0) & UMM_BLOCKNO_MASK;
|
cur = UMM_NBLOCK(0) & UMM_BLOCKNO_MASK;
|
||||||
|
|
||||||
while( UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK ) {
|
while (UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK) {
|
||||||
if ( !(UMM_NBLOCK(cur) & UMM_FREELIST_MASK) ) {
|
if (!(UMM_NBLOCK(cur) & UMM_FREELIST_MASK)) {
|
||||||
/* This is a used block (not free), so, check its poison */
|
/* This is a used block (not free), so, check its poison */
|
||||||
ok = check_poison_block(&UMM_BLOCK(cur));
|
ok = check_poison_block(&UMM_BLOCK(cur));
|
||||||
if (!ok){
|
if (!ok) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK;
|
||||||
}
|
}
|
||||||
|
UMM_CRITICAL_EXIT(id_poison);
|
||||||
|
|
||||||
cur = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK;
|
return ok;
|
||||||
}
|
|
||||||
UMM_CRITICAL_EXIT(id_poison);
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
157
cores/esp8266/wpa2_eap_patch.cpp
Normal file
157
cores/esp8266/wpa2_eap_patch.cpp
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* To implement this patch, the SDK module `eap.o` from archive `libwpa2.a` must
|
||||||
|
* be patched to call `z2EapFree` instead of `vPortFree`. This limits extending
|
||||||
|
* the execution time of vPortFree to that module only. Not impacting other
|
||||||
|
* modules.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ets_sys.h>
|
||||||
|
#include <pgmspace.h>
|
||||||
|
#include "coredecls.h"
|
||||||
|
|
||||||
|
#ifdef DEBUG_WPA2_EAP_PATCH
|
||||||
|
#include "esp8266_undocumented.h"
|
||||||
|
#define DEBUG_PRINTF ets_uart_printf
|
||||||
|
#else
|
||||||
|
#define DEBUG_PRINTF(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
// extern "C" void z2EapFree(void *ptr, const char* file, int line) __attribute__((weak, alias("vPortFree")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Limited 2-part wrapper for vPortFree calls made in SDK module `eap.o` from
|
||||||
|
* archive `libwpa2.a`.
|
||||||
|
*
|
||||||
|
* vPortFree calls from eap.o are monitored for calls from line 799. This is
|
||||||
|
* the location of the memory leak. At entry register a12 contains the structure
|
||||||
|
* address which has the addresses of the allocations that will be leaked.
|
||||||
|
*
|
||||||
|
* Part 1 of this wrapper, z2EapFree, appends the value of register a12 as a
|
||||||
|
* 4th argument to part2 of this wrapper, patch_wpa2_eap_vPortFree_a12(). Which
|
||||||
|
* in turn checks and frees the additional allocations, that would have been
|
||||||
|
* lost.
|
||||||
|
*
|
||||||
|
* extern "C" z2EapFree(void*);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Part 1 of Limited vPortFree Wrapper
|
||||||
|
*/
|
||||||
|
asm(
|
||||||
|
// ".section .iram.text.z2EapFree,\"ax\",@progbits\n\t"
|
||||||
|
// Since all the possible callers in eap.o are in sections starting with
|
||||||
|
// .text and not .iram.text we should be safe putting these wrappers in .text.
|
||||||
|
".section .text.z2EapFree,\"ax\",@progbits\n\t"
|
||||||
|
".literal_position\n\t"
|
||||||
|
".literal .patch_wpa2_eap_vPortFree_a12, patch_wpa2_eap_vPortFree_a12\n\t"
|
||||||
|
".align 4\n\t"
|
||||||
|
".global z2EapFree\n\t"
|
||||||
|
".type z2EapFree, @function\n\t"
|
||||||
|
"\n"
|
||||||
|
"z2EapFree:\n\t"
|
||||||
|
"addi a1, a1, -16\n\t"
|
||||||
|
"s32i a0, a1, 0\n\t"
|
||||||
|
"mov a5, a12\n\t"
|
||||||
|
"l32r a0, .patch_wpa2_eap_vPortFree_a12\n\t"
|
||||||
|
"callx0 a0\n\t"
|
||||||
|
"l32i a0, a1, 0\n\t"
|
||||||
|
"addi a1, a1, 16\n\t"
|
||||||
|
"ret\n\t"
|
||||||
|
".size z2EapFree, .-z2EapFree\n\t"
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* While some insight can be gained from the ESP32 repo for this structure.
|
||||||
|
* It does not match exactly. This alternate structure focuses on correct offset
|
||||||
|
* rather than trying to exactly reconstruct the original labels.
|
||||||
|
* These offset were found in libwpa2.a:eap.o .text.eap_peer_config_init
|
||||||
|
*/
|
||||||
|
struct StateMachine { // size 200 bytes
|
||||||
|
void* beforeConfig[16];
|
||||||
|
void* config[26];
|
||||||
|
// 0 - s32i a2, a12, 64 // username / Identity
|
||||||
|
// 1 - s32i a2, a12, 68 // length
|
||||||
|
// 2 - s32i a2, a12, 72 // anonymous Identity
|
||||||
|
// 3 - s32i a2, a12, 76
|
||||||
|
// 4 - s32i a2, a12, 80 // password
|
||||||
|
// 5 - s32i a2, a12, 84
|
||||||
|
//
|
||||||
|
// "new password" - From wifi_station_set_enterprise_new_password(), we see
|
||||||
|
// global saved value .bss+32 and .bss+36 which are later used to populate
|
||||||
|
// ".config" in eap_peer_config_init(). I do not have an environment to
|
||||||
|
// exercise this parameter. In my tests, the "new password" element in the
|
||||||
|
// ".config" is never initialized. At the moment, I don't see any code that
|
||||||
|
// would free the allocation.
|
||||||
|
// allocated via pvPortZalloc from line 0x30f, 783
|
||||||
|
// 21 - s32i a2, a12, 148 // new password
|
||||||
|
// 22 - s32i a2, a12, 152
|
||||||
|
|
||||||
|
void* afterConfig[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Part 2 of Limited vPortFree Wrapper
|
||||||
|
*
|
||||||
|
* Presently, all SDKs have the same memory leaks in the same module at the
|
||||||
|
* same line.
|
||||||
|
*/
|
||||||
|
void patch_wpa2_eap_vPortFree_a12(void *ptr, const char* file, int line, void* a12) {
|
||||||
|
if (799 == line) {
|
||||||
|
// This caller is eap_peer_config_deinit()
|
||||||
|
struct StateMachine* sm = (struct StateMachine*)a12;
|
||||||
|
if (ptr == sm->config[0]) {
|
||||||
|
// Fix leaky frunction - eap.o only frees one out of 4 config items
|
||||||
|
// finish the other 3 first
|
||||||
|
vPortFree(sm->config[2], file, line);
|
||||||
|
vPortFree(sm->config[4], file, line);
|
||||||
|
vPortFree(sm->config[21], file, line);
|
||||||
|
// ptr is sm->config[0], let fall through handle it
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_WPA2_EAP_PATCH
|
||||||
|
DEBUG_PRINTF("\nz2EapFree/vPortFree patch struct StateMachine * = %8p\n", a12);
|
||||||
|
DEBUG_PRINTF(" config[0] vPortFree(%8p, file, line);\n", ptr);
|
||||||
|
DEBUG_PRINTF(" config[2] vPortFree(%8p, file, line);\n", sm->config[2]);
|
||||||
|
DEBUG_PRINTF(" config[4] vPortFree(%8p, file, line);\n", sm->config[4]);
|
||||||
|
DEBUG_PRINTF(" config[21] vPortFree(%8p, file, line);\n", sm->config[21]);
|
||||||
|
if (a12) {
|
||||||
|
void** pw = (void**)a12;
|
||||||
|
DEBUG_PRINTF("\nhexdump struct StateMachine:\n");
|
||||||
|
for (size_t i=0; i<200/4; i+=4) {
|
||||||
|
DEBUG_PRINTF("%03u: %8p %8p %8p %8p\n", i*4, pw[i], pw[i+1], pw[i+2], pw[i+3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
// This is not needed because the call was NO-OPed in the library. This code
|
||||||
|
// snippit is just to show how a future memory free issue might be resolved.
|
||||||
|
else if (672 == line) {
|
||||||
|
// This caller is wpa2_sm_rx_eapol()
|
||||||
|
// 1st of a double free
|
||||||
|
// let the 2nd free handle it.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
vPortFree(ptr, file, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This will minimize code space for non-wifi enterprise sketches which do not
|
||||||
|
* need the patch and disable_extra4k_at_link_time().
|
||||||
|
*/
|
||||||
|
void enable_wifi_enterprise_patch(void) {
|
||||||
|
/*
|
||||||
|
* Calling this from setup or anywhere ensures that the patch code is
|
||||||
|
* included in the build.
|
||||||
|
*
|
||||||
|
* Also, WiFi Enterprise uses a lot of system stack space and may crash
|
||||||
|
* unless we:
|
||||||
|
*/
|
||||||
|
disable_extra4k_at_link_time();
|
||||||
|
}
|
@@ -54,7 +54,7 @@
|
|||||||
* stack frame is limited to 128 bytes (currently at 64).
|
* stack frame is limited to 128 bytes (currently at 64).
|
||||||
*/
|
*/
|
||||||
STRUCT_BEGIN
|
STRUCT_BEGIN
|
||||||
STRUCT_FIELD (long,4,KEXC_,pc) /* "parm" */
|
STRUCT_FIELD (long,4,KEXC_,pc) /* "param" */
|
||||||
STRUCT_FIELD (long,4,KEXC_,ps)
|
STRUCT_FIELD (long,4,KEXC_,ps)
|
||||||
STRUCT_AFIELD(long,4,KEXC_,areg, 4) /* a12 .. a15 */
|
STRUCT_AFIELD(long,4,KEXC_,areg, 4) /* a12 .. a15 */
|
||||||
STRUCT_FIELD (long,4,KEXC_,sar) /* "save" */
|
STRUCT_FIELD (long,4,KEXC_,sar) /* "save" */
|
||||||
|
@@ -5,7 +5,7 @@ Intro
|
|||||||
-----
|
-----
|
||||||
|
|
||||||
PROGMEM is a Arduino AVR feature that has been ported to ESP8266 to
|
PROGMEM is a Arduino AVR feature that has been ported to ESP8266 to
|
||||||
ensure compatability with existing Arduino libraries, as well as, saving
|
ensure compatibility with existing Arduino libraries, as well as, saving
|
||||||
RAM. On the esp8266 declaring a string such as ``const char * xyz =
|
RAM. On the esp8266 declaring a string such as ``const char * xyz =
|
||||||
"this is a string"`` will place this string in RAM, not flash. It is
|
"this is a string"`` will place this string in RAM, not flash. It is
|
||||||
possible to place a String into flash, and then load it into RAM when
|
possible to place a String into flash, and then load it into RAM when
|
||||||
@@ -283,7 +283,7 @@ generate as they are basically ``const char *``. On the other hand
|
|||||||
conversions from, very useful when overloading functions, and doing
|
conversions from, very useful when overloading functions, and doing
|
||||||
implicit type conversions. It is worth adding that if you wish to store
|
implicit type conversions. It is worth adding that if you wish to store
|
||||||
an ``int``, ``float`` or pointer these can be stored and read back
|
an ``int``, ``float`` or pointer these can be stored and read back
|
||||||
directly as they are 4 bytes in size and therefor will be always
|
directly as they are 4 bytes in size and therefore will be always
|
||||||
aligned!
|
aligned!
|
||||||
|
|
||||||
Hope this helps.
|
Hope this helps.
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user