1
0
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:
david gauchard
2022-06-11 22:23:53 +02:00
committed by GitHub
535 changed files with 27764 additions and 21136 deletions

4
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,4 @@
9bdcd4f36a2e5285267b69b17e8fc26482dc1c72
eea9999dc5eaf464a432f77d5b65269f9baf198d
98125f88605cd7e46e9be4e1b3ad0600dd5d2b51
19b7a29720a6f2c95d06e2ea4baa335dcf32e68f

View File

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

View File

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

View File

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

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

View File

@@ -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 [![Latest release](https://img.shields.io/github/release/esp8266/Arduino.svg)](https://github.com/esp8266/Arduino/releases/latest/) #### Latest release [![Latest release](https://img.shields.io/github/release/esp8266/Arduino.svg)](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
[![Linux build status](https://travis-ci.org/esp8266/Arduino.svg)](https://travis-ci.org/esp8266/Arduino) [![Linux build status](https://travis-ci.org/esp8266/Arduino.svg)](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

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 &copy(const char *cstr, unsigned int length); String &copy(const char *cstr, unsigned int length);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

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

View File

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

View File

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