mirror of
https://github.com/esp8266/Arduino.git
synced 2025-08-05 13:16:13 +03:00
Merge branch 'master' into master
This commit is contained in:
4
.git-blame-ignore-revs
Normal file
4
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,4 @@
|
||||
9bdcd4f36a2e5285267b69b17e8fc26482dc1c72
|
||||
eea9999dc5eaf464a432f77d5b65269f9baf198d
|
||||
98125f88605cd7e46e9be4e1b3ad0600dd5d2b51
|
||||
19b7a29720a6f2c95d06e2ea4baa335dcf32e68f
|
57
.github/workflows/pull-request.yml
vendored
57
.github/workflows/pull-request.yml
vendored
@@ -15,6 +15,9 @@ jobs:
|
||||
build-linux:
|
||||
name: Build ${{ matrix.chunk }}
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
strategy:
|
||||
matrix:
|
||||
chunk: [0, 1, 2, 3, 4, 5, 6, 7]
|
||||
@@ -47,6 +50,9 @@ jobs:
|
||||
build-debug-ipv6:
|
||||
name: Debug IPv6 ${{ matrix.chunk }}
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
strategy:
|
||||
matrix:
|
||||
chunk: [0, 1, 2, 3, 4, 5, 6, 7]
|
||||
@@ -110,6 +116,9 @@ jobs:
|
||||
build-mac:
|
||||
name: Mac
|
||||
runs-on: macOS-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -139,6 +148,9 @@ jobs:
|
||||
build-pio:
|
||||
name: Build Platform.IO
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -163,6 +175,9 @@ jobs:
|
||||
host-tests:
|
||||
name: Host tests
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -184,6 +199,9 @@ jobs:
|
||||
documentation:
|
||||
name: Documentation
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -208,6 +226,9 @@ jobs:
|
||||
style-check:
|
||||
name: Style and formatting
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -217,11 +238,21 @@ jobs:
|
||||
python-version: '3.x'
|
||||
- name: Style check
|
||||
env:
|
||||
LLVM_SNAPSHOT_KEY: "6084F3CF814B57C1CF12EFD515CF4D18AF4F7421"
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
TRAVIS_TAG: ${{ github.ref }}
|
||||
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 install astyle
|
||||
sudo apt install clang-format-13
|
||||
pip3 install pyyaml
|
||||
bash ./tests/ci/style_check.sh
|
||||
|
||||
|
||||
@@ -229,6 +260,9 @@ jobs:
|
||||
mock-check:
|
||||
name: Mock trivial test
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -248,6 +282,9 @@ jobs:
|
||||
boards-check:
|
||||
name: Boards.txt check
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -269,3 +306,21 @@ jobs:
|
||||
bash ./tests/ci/build_boards.sh
|
||||
bash ./tests/ci/eboot_test.sh
|
||||
bash ./tests/ci/pkgrefs_test.sh
|
||||
|
||||
|
||||
# Validate orthography
|
||||
code-spell:
|
||||
name: Check spelling
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Run codespell
|
||||
uses: codespell-project/actions-codespell@master
|
||||
with:
|
||||
skip: ./libraries/ESP8266SdFat,./libraries/LittleFS/lib,./tools/pyserial,./tools/sdk,./tools/esptool,./libraries/SoftwareSerial,./libraries/Ethernet,./github/workflows,./libraries/ESP8266HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino,./libraries/esp8266/examples/RTCUserMemory/RTCUserMemory.ino,./libraries/esp8266/examples/StreamString/StreamString.ino,./libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino,./libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino,./libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/ESP8266WebServer/examples/HttpHashCredAuth/HttpHashCredAuth.ino,./cores/esp8266/spiffs,./tests/device/test_libc/libm_string.c, ./libraries/Netdump/examples/Netdump/Netdump.ino,./libraries/ESP8266WiFi/examples/BearSSL_Server,./cores/esp8266/LwipIntfDev.h
|
||||
ignore_words_list: ESP8266,esp8266,esp,dout,DOUT,ser,ans
|
||||
|
16
.github/workflows/release-to-publish.yml
vendored
16
.github/workflows/release-to-publish.yml
vendored
@@ -32,16 +32,14 @@ jobs:
|
||||
package:
|
||||
name: Update master JSON file
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Set GIT tag name
|
||||
run: |
|
||||
echo "::set-env name=TRAVIS_TAG::$(git describe --exact-match --tags)"
|
||||
submodules: false
|
||||
fetch-depth: 0
|
||||
- name: Deploy updated JSON
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
@@ -49,8 +47,4 @@ jobs:
|
||||
CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
|
||||
GHCI_DEPLOY_KEY: ${{ secrets.GHCI_DEPLOY_KEY }}
|
||||
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
|
||||
|
||||
|
7
.github/workflows/tag-to-draft-release.yml
vendored
7
.github/workflows/tag-to-draft-release.yml
vendored
@@ -14,17 +14,22 @@ jobs:
|
||||
package:
|
||||
name: Package
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Set GIT tag name
|
||||
run: |
|
||||
# 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
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,15 +3,9 @@ tools/dist/
|
||||
tools/xtensa-lx106-elf/
|
||||
tools/mkspiffs/
|
||||
tools/mklittlefs/
|
||||
tools/python/
|
||||
tools/python3/
|
||||
package/versions/
|
||||
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/
|
||||
|
||||
|
@@ -3,7 +3,7 @@ Arduino core for ESP8266 WiFi chip
|
||||
|
||||
# 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/)
|
||||
- [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).
|
||||
|
||||
- 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.
|
||||
- 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).
|
||||
@@ -36,14 +36,14 @@ Starting with 1.6.4, Arduino allows installation of third-party platform package
|
||||
#### Latest release [](https://github.com/esp8266/Arduino/releases/latest/)
|
||||
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
|
||||
[](https://travis-ci.org/esp8266/Arduino)
|
||||
|
||||
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).
|
||||
|
||||
### Using PlatformIO
|
||||
|
1084
boards.txt
1084
boards.txt
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -186,6 +186,9 @@ void attachInterrupt(uint8_t pin, void (*)(void), int mode);
|
||||
void detachInterrupt(uint8_t pin);
|
||||
void attachInterruptArg(uint8_t pin, void (*)(void*), void* arg, int mode);
|
||||
|
||||
#if FLASH_MAP_SUPPORT
|
||||
#include "flash_hal.h"
|
||||
#endif
|
||||
void preinit(void);
|
||||
void setup(void);
|
||||
void loop(void);
|
||||
@@ -279,6 +282,8 @@ inline void configTzTime(const char* tz, const char* server1,
|
||||
configTime(tz, server1, server2, server3);
|
||||
}
|
||||
|
||||
bool getLocalTime(struct tm * info, uint32_t ms = 5000);
|
||||
|
||||
// Everything we expect to be implicitly loaded for the sketch
|
||||
#include <pgmspace.h>
|
||||
|
||||
|
@@ -1,9 +1,8 @@
|
||||
#ifndef __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.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
@@ -23,19 +23,18 @@
|
||||
#include "coredecls.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
|
||||
// 100 * (1 - sqrt(sum(hole-size²)) / sum(hole-size))
|
||||
|
||||
umm_info(NULL, false);
|
||||
|
||||
uint32_t free_size = umm_free_heap_size_core(umm_get_current_heap());
|
||||
if (hfree)
|
||||
*hfree = free_size;
|
||||
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 (free_size) {
|
||||
*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()
|
||||
{
|
||||
return (uint8_t)umm_fragmentation_metric();
|
||||
|
@@ -26,7 +26,7 @@
|
||||
#include "MD5Builder.h"
|
||||
#include "umm_malloc/umm_malloc.h"
|
||||
#include "cont.h"
|
||||
|
||||
#include "flash_hal.h"
|
||||
#include "coredecls.h"
|
||||
#include "umm_malloc/umm_malloc.h"
|
||||
#include <pgmspace.h>
|
||||
@@ -115,20 +115,18 @@ void EspClass::wdtFeed(void)
|
||||
system_soft_wdt_feed();
|
||||
}
|
||||
|
||||
extern "C" void esp_yield();
|
||||
|
||||
void EspClass::deepSleep(uint64_t time_us, WakeMode mode)
|
||||
{
|
||||
system_deep_sleep_set_option(static_cast<int>(mode));
|
||||
system_deep_sleep(time_us);
|
||||
esp_yield();
|
||||
esp_suspend();
|
||||
}
|
||||
|
||||
void EspClass::deepSleepInstant(uint64_t time_us, WakeMode mode)
|
||||
{
|
||||
system_deep_sleep_set_option(static_cast<int>(mode));
|
||||
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.
|
||||
@@ -200,7 +198,7 @@ void EspClass::reset(void)
|
||||
void EspClass::restart(void)
|
||||
{
|
||||
system_restart();
|
||||
esp_yield();
|
||||
esp_suspend();
|
||||
}
|
||||
|
||||
[[noreturn]] void EspClass::rebootIntoUartDownloadMode()
|
||||
@@ -224,7 +222,7 @@ uint32_t EspClass::getFreeHeap(void)
|
||||
return system_get_free_heap_size();
|
||||
}
|
||||
|
||||
uint16_t EspClass::getMaxFreeBlockSize(void)
|
||||
uint32_t EspClass::getMaxFreeBlockSize(void)
|
||||
{
|
||||
return umm_max_block_size();
|
||||
}
|
||||
@@ -293,6 +291,9 @@ uint32_t EspClass::getFlashChipRealSize(void)
|
||||
|
||||
uint32_t EspClass::getFlashChipSize(void)
|
||||
{
|
||||
#if FLASH_MAP_SUPPORT
|
||||
return getFlashChipRealSize();
|
||||
#else
|
||||
uint32_t data;
|
||||
uint8_t * bytes = (uint8_t *) &data;
|
||||
// read first 4 byte (magic byte + flash config)
|
||||
@@ -300,6 +301,7 @@ uint32_t EspClass::getFlashChipSize(void)
|
||||
return magicFlashChipSize((bytes[3] & 0xf0) >> 4);
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFlashChipSpeed(void)
|
||||
@@ -325,6 +327,7 @@ FlashMode_t EspClass::getFlashChipMode(void)
|
||||
return mode;
|
||||
}
|
||||
|
||||
#if !FLASH_MAP_SUPPORT
|
||||
uint32_t EspClass::magicFlashChipSize(uint8_t byte) {
|
||||
switch(byte & 0x0F) {
|
||||
case 0x0: // 4 Mbit (512KB)
|
||||
@@ -345,6 +348,7 @@ uint32_t EspClass::magicFlashChipSize(uint8_t byte) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) {
|
||||
switch(byte & 0x0F) {
|
||||
@@ -614,14 +618,12 @@ uint32_t EspClass::getSketchSize() {
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C" uint32_t _FS_start;
|
||||
|
||||
uint32_t EspClass::getFreeSketchSpace() {
|
||||
|
||||
uint32_t usedSize = getSketchSize();
|
||||
// round one sector up
|
||||
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
|
||||
DEBUG_SERIAL.printf("usedSize=%u freeSpaceStart=%u freeSpaceEnd=%u\r\n", usedSize, freeSpaceStart, freeSpaceEnd);
|
||||
|
@@ -26,7 +26,7 @@
|
||||
#include "spi_vendors.h"
|
||||
|
||||
/**
|
||||
* AVR macros for WDT managment
|
||||
* AVR macros for WDT management
|
||||
*/
|
||||
typedef enum {
|
||||
WDTO_0MS = 0, //!< WDTO_0MS
|
||||
@@ -114,9 +114,10 @@ class EspClass {
|
||||
static uint32_t getChipId();
|
||||
|
||||
static uint32_t getFreeHeap();
|
||||
static uint16_t getMaxFreeBlockSize();
|
||||
static uint32_t getMaxFreeBlockSize();
|
||||
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 void resetFreeContStack();
|
||||
@@ -264,7 +265,7 @@ class EspClass {
|
||||
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
|
||||
* 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.
|
||||
* 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
|
||||
|
@@ -117,6 +117,18 @@ public:
|
||||
time_t getCreationTime();
|
||||
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:
|
||||
FileImplPtr _p;
|
||||
time_t (*_timeCallback)(void) = nullptr;
|
||||
|
@@ -15,7 +15,7 @@ void close_all_fs(void)
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
void littlefs_request_end(void) __attribute__((weak));
|
||||
|
59
cores/esp8266/FlashMap.h
Normal file
59
cores/esp8266/FlashMap.h
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
// - do not edit - autogenerated by boards.txt.py
|
||||
|
||||
#ifndef __FLASH_MAP_H
|
||||
#define __FLASH_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t eeprom_start;
|
||||
uint32_t fs_start;
|
||||
uint32_t fs_end;
|
||||
uint32_t fs_block_size;
|
||||
uint32_t fs_page_size;
|
||||
uint32_t flash_size_kb;
|
||||
} flash_map_s;
|
||||
|
||||
/*
|
||||
Following definitions map the above structure, one per line.
|
||||
FLASH_MAP_* is a user choice in sketch:
|
||||
`FLASH_MAP_SETUP_CONFIG(FLASH_MAP_OTA_FS)`
|
||||
Configuration is made at boot with detected flash chip size (last argument 512..16384)
|
||||
Other values are defined from `tools/boards.txt.py`.
|
||||
*/
|
||||
|
||||
#define FLASH_MAP_OTA_FS \
|
||||
{ \
|
||||
{ .eeprom_start = 0x402fb000, .fs_start = 0x402eb000, .fs_end = 0x402fb000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 1024 }, \
|
||||
{ .eeprom_start = 0x403fb000, .fs_start = 0x403c0000, .fs_end = 0x403fb000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 2048 }, \
|
||||
{ .eeprom_start = 0x405fb000, .fs_start = 0x40400000, .fs_end = 0x405fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 4096 }, \
|
||||
{ .eeprom_start = 0x409fb000, .fs_start = 0x40400000, .fs_end = 0x409fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 8192 }, \
|
||||
{ .eeprom_start = 0x411fb000, .fs_start = 0x40400000, .fs_end = 0x411fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 16384 }, \
|
||||
{ .eeprom_start = 0x4027b000, .fs_start = 0x40273000, .fs_end = 0x4027b000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 512 }, \
|
||||
}
|
||||
|
||||
#define FLASH_MAP_MAX_FS \
|
||||
{ \
|
||||
{ .eeprom_start = 0x402fb000, .fs_start = 0x4027b000, .fs_end = 0x402fb000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 1024 }, \
|
||||
{ .eeprom_start = 0x403fb000, .fs_start = 0x40300000, .fs_end = 0x403fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 2048 }, \
|
||||
{ .eeprom_start = 0x405fb000, .fs_start = 0x40300000, .fs_end = 0x405fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 4096 }, \
|
||||
{ .eeprom_start = 0x409fb000, .fs_start = 0x40300000, .fs_end = 0x409fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 8192 }, \
|
||||
{ .eeprom_start = 0x411fb000, .fs_start = 0x40300000, .fs_end = 0x411fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 16384 }, \
|
||||
{ .eeprom_start = 0x4027b000, .fs_start = 0x4025b000, .fs_end = 0x4027b000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 512 }, \
|
||||
}
|
||||
|
||||
#define FLASH_MAP_NO_FS \
|
||||
{ \
|
||||
{ .eeprom_start = 0x402fb000, .fs_start = 0x402fb000, .fs_end = 0x402fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 1024 }, \
|
||||
{ .eeprom_start = 0x403fb000, .fs_start = 0x403fb000, .fs_end = 0x403fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 2048 }, \
|
||||
{ .eeprom_start = 0x405fb000, .fs_start = 0x405fb000, .fs_end = 0x405fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 4096 }, \
|
||||
{ .eeprom_start = 0x409fb000, .fs_start = 0x409fb000, .fs_end = 0x409fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 8192 }, \
|
||||
{ .eeprom_start = 0x411fb000, .fs_start = 0x411fb000, .fs_end = 0x411fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 16384 }, \
|
||||
{ .eeprom_start = 0x4027b000, .fs_start = 0x4027b000, .fs_end = 0x4027b000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 512 }, \
|
||||
}
|
||||
|
||||
#endif // __FLASH_MAP_H
|
||||
|
@@ -35,9 +35,6 @@
|
||||
|
||||
// SerialEvent functions are weak, so when the user doesn't define them,
|
||||
// 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));
|
||||
|
||||
HardwareSerial::HardwareSerial(int uart_nr)
|
||||
@@ -146,8 +143,7 @@ unsigned long HardwareSerial::detectBaudrate(time_t timeoutMillis)
|
||||
if ((detectedBaudrate = testBaudrate())) {
|
||||
break;
|
||||
}
|
||||
yield();
|
||||
delay(100);
|
||||
esp_delay(100);
|
||||
}
|
||||
return detectedBaudrate;
|
||||
}
|
||||
|
@@ -132,7 +132,7 @@ public:
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
|
||||
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);
|
||||
}
|
||||
// ::read(buffer, size): same as readBytes without timeout
|
||||
@@ -183,7 +183,7 @@ public:
|
||||
{
|
||||
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
|
||||
{
|
||||
return uart_write_char(_uart, c);
|
||||
@@ -233,8 +233,12 @@ protected:
|
||||
size_t _rx_size;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
extern HardwareSerial Serial;
|
||||
#endif
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL1)
|
||||
extern HardwareSerial Serial1;
|
||||
#endif
|
||||
|
||||
extern void serialEventRun(void) __attribute__((weak));
|
||||
|
||||
|
@@ -69,7 +69,7 @@ class IPAddress: public Printable {
|
||||
IPAddress(const IPAddress& from);
|
||||
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
||||
IPAddress(uint32_t address) { ctor32(address); }
|
||||
IPAddress(u32_t address) { ctor32(address); }
|
||||
IPAddress(unsigned long address) { ctor32(address); }
|
||||
IPAddress(int address) { ctor32(address); }
|
||||
IPAddress(const uint8_t *address);
|
||||
|
||||
@@ -80,16 +80,14 @@ class IPAddress: public Printable {
|
||||
// to a four-byte uint8_t array is expected
|
||||
operator uint32_t() const { 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;
|
||||
operator bool () const { return isSet(); } // <-
|
||||
operator bool () { return isSet(); } // <- both are needed
|
||||
|
||||
// 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)
|
||||
u32_t& v4() { return ip_2_ip4(&_ip)->addr; }
|
||||
const uint32_t& v4() const { return ip_2_ip4(&_ip)->addr; } // for raw_address(const)
|
||||
uint32_t& v4() { return ip_2_ip4(&_ip)->addr; }
|
||||
|
||||
bool operator==(const IPAddress& addr) const {
|
||||
return ip_addr_cmp(&_ip, &addr._ip);
|
||||
@@ -100,14 +98,14 @@ class IPAddress: public Printable {
|
||||
bool operator==(uint32_t addr) const {
|
||||
return isV4() && v4() == addr;
|
||||
}
|
||||
bool operator==(u32_t addr) const {
|
||||
return isV4() && v4() == addr;
|
||||
bool operator==(unsigned long addr) const {
|
||||
return isV4() && v4() == (uint32_t)addr;
|
||||
}
|
||||
bool operator!=(uint32_t addr) const {
|
||||
return !(isV4() && v4() == addr);
|
||||
}
|
||||
bool operator!=(u32_t addr) const {
|
||||
return !(isV4() && v4() == addr);
|
||||
bool operator!=(unsigned long addr) const {
|
||||
return isV4() && v4() != (uint32_t)addr;
|
||||
}
|
||||
bool operator==(const uint8_t* addr) const;
|
||||
|
||||
|
@@ -1,64 +1,97 @@
|
||||
/*
|
||||
lwIPDhcpServer-NonOS.cpp - DHCP server wrapper
|
||||
NonOS DHCP server helpers
|
||||
|
||||
Copyright (c) 2020 esp8266 arduino. All rights reserved.
|
||||
Copyright (c) 2020-2022 esp8266 arduino. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This 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
|
||||
*/
|
||||
|
||||
// STARTS/STOPS DHCP SERVER ON WIFI AP INTERFACE
|
||||
// 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 "LwipDhcpServer-NonOS.h"
|
||||
|
||||
#include <lwip/init.h>
|
||||
#include <lwip/netif.h>
|
||||
#include "LwipDhcpServer.h"
|
||||
|
||||
// Global static DHCP instance for softAP interface
|
||||
// (since the netif object never goes away, even when AP is disabled)
|
||||
// Initial version fully emulates nonos-sdk api in DhcpServer class,
|
||||
// before trying to further change it and possibly break legacy behaviour
|
||||
DhcpServer& getNonOSDhcpServer()
|
||||
{
|
||||
extern netif netif_git[2];
|
||||
|
||||
// global DHCP instance for softAP interface
|
||||
DhcpServer dhcpSoftAP(&netif_git[SOFTAP_IF]);
|
||||
static DhcpServer server(&netif_git[SOFTAP_IF]);
|
||||
return server;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
void dhcps_start(struct ip_info *info, netif* apnetif)
|
||||
// `ip_info` is useless, since we get the information from the netif directly
|
||||
// `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
|
||||
// netif_git[SOFTAP_IF] interface in constructor
|
||||
(void)apnetif;
|
||||
|
||||
#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
|
||||
auto& server = getNonOSDhcpServer();
|
||||
if (!server.isRunning())
|
||||
{
|
||||
server.begin();
|
||||
}
|
||||
}
|
||||
|
||||
void dhcps_stop()
|
||||
{
|
||||
dhcpSoftAP.end();
|
||||
auto& server = getNonOSDhcpServer();
|
||||
if (server.isRunning())
|
||||
{
|
||||
server.end();
|
||||
}
|
||||
}
|
||||
|
||||
// providing the rest of the nonos-sdk API, which was originally removed in 3.0.0
|
||||
|
||||
bool wifi_softap_set_dhcps_lease(dhcps_lease* please)
|
||||
{
|
||||
auto& server = getNonOSDhcpServer();
|
||||
return server.set_dhcps_lease(please);
|
||||
}
|
||||
|
||||
bool wifi_softap_get_dhcps_lease(dhcps_lease* please)
|
||||
{
|
||||
auto& server = getNonOSDhcpServer();
|
||||
return server.get_dhcps_lease(please);
|
||||
}
|
||||
|
||||
uint32 wifi_softap_get_dhcps_lease_time()
|
||||
{
|
||||
auto& server = getNonOSDhcpServer();
|
||||
return server.getLeaseTime();
|
||||
}
|
||||
|
||||
bool wifi_softap_set_dhcps_lease_time(uint32 minutes)
|
||||
{
|
||||
auto& server = getNonOSDhcpServer();
|
||||
server.setLeaseTime(minutes);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wifi_softap_reset_dhcps_lease_time()
|
||||
{
|
||||
auto& server = getNonOSDhcpServer();
|
||||
server.resetLeaseTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wifi_softap_add_dhcps_lease(uint8* macaddr)
|
||||
{
|
||||
auto& server = getNonOSDhcpServer();
|
||||
return server.add_dhcps_lease(macaddr);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
24
cores/esp8266/LwipDhcpServer-NonOS.h
Normal file
24
cores/esp8266/LwipDhcpServer-NonOS.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
NonOS DHCP server helpers
|
||||
|
||||
Copyright (c) 2020-2022 esp8266 arduino. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LwipDhcpServer.h"
|
||||
|
||||
// Global static DHCP instance for softAP interface
|
||||
DhcpServer& getNonOSDhcpServer();
|
@@ -36,8 +36,6 @@
|
||||
|
||||
#include <lwip/init.h> // LWIP_VERSION
|
||||
|
||||
#define DHCPS_LEASE_TIME_DEF (120)
|
||||
|
||||
#define USE_DNS
|
||||
|
||||
#include "lwip/inet.h"
|
||||
@@ -52,6 +50,9 @@
|
||||
#include "user_interface.h"
|
||||
#include "mem.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sys/pgmspace.h>
|
||||
|
||||
typedef struct dhcps_state
|
||||
{
|
||||
sint16_t state;
|
||||
@@ -80,12 +81,6 @@ struct dhcps_lease
|
||||
struct ipv4_addr end_ip;
|
||||
};
|
||||
|
||||
enum dhcps_offer_option
|
||||
{
|
||||
OFFER_START = 0x00,
|
||||
OFFER_ROUTER = 0x01,
|
||||
OFFER_END
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
@@ -107,10 +102,8 @@ struct dhcps_pool
|
||||
uint32 lease_timer;
|
||||
dhcps_type_t type;
|
||||
dhcps_state_t state;
|
||||
|
||||
};
|
||||
|
||||
#define DHCPS_LEASE_TIMER dhcps_lease_time //0x05A0
|
||||
#define DHCPS_MAX_LEASE 0x64
|
||||
#define BOOTP_BROADCAST 0x8000
|
||||
|
||||
@@ -123,13 +116,13 @@ struct dhcps_pool
|
||||
#define DHCPS_SERVER_PORT 67
|
||||
#define DHCPS_CLIENT_PORT 68
|
||||
|
||||
#define DHCPDISCOVER 1
|
||||
#define DHCPOFFER 2
|
||||
#define DHCPREQUEST 3
|
||||
#define DHCPDECLINE 4
|
||||
#define DHCPACK 5
|
||||
#define DHCPNAK 6
|
||||
#define DHCPRELEASE 7
|
||||
static constexpr uint8_t DHCPDISCOVER = 1;
|
||||
static constexpr uint8_t DHCPOFFER = 2;
|
||||
static constexpr uint8_t DHCPREQUEST = 3;
|
||||
static constexpr uint8_t DHCPDECLINE = 4;
|
||||
static constexpr uint8_t DHCPACK = 5;
|
||||
static constexpr uint8_t DHCPNAK = 6;
|
||||
static constexpr uint8_t DHCPRELEASE = 7;
|
||||
|
||||
#define DHCP_OPTION_SUBNET_MASK 1
|
||||
#define DHCP_OPTION_ROUTER 3
|
||||
@@ -155,62 +148,56 @@ struct dhcps_pool
|
||||
#define DHCPS_STATE_IDLE 5
|
||||
#define DHCPS_STATE_RELEASE 6
|
||||
|
||||
#define dhcps_router_enabled(offer) ((offer & OFFER_ROUTER) != 0)
|
||||
|
||||
#ifdef MEMLEAK_DEBUG
|
||||
const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
|
||||
#endif
|
||||
|
||||
#if DHCPS_DEBUG
|
||||
#define LWIP_IS_OK(what,err) ({ int ret = 1, errval = (err); if (errval != ERR_OK) { os_printf("DHCPS ERROR: %s (lwip:%d)\n", what, errval); ret = 0; } ret; })
|
||||
#define LWIP_IS_OK(what, err) \
|
||||
({ \
|
||||
int ret = 1, errval = (err); \
|
||||
if (errval != ERR_OK) \
|
||||
{ \
|
||||
os_printf("DHCPS ERROR: %s (lwip:%s(%d))\n", what, lwip_strerr(errval), errval); \
|
||||
ret = 0; \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
#else
|
||||
#define LWIP_IS_OK(what, err) ((err) == ERR_OK)
|
||||
#endif
|
||||
|
||||
const uint32 DhcpServer::magic_cookie = 0x63538263; // https://tools.ietf.org/html/rfc1497
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int fw_has_started_softap_dhcps = 0;
|
||||
DhcpServer::OptionsBuffer& DhcpServer::OptionsBuffer::add(uint8_t code, const uint8_t* data,
|
||||
size_t size)
|
||||
{
|
||||
if (size >= UINT8_MAX)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
if ((size_t)(_end - _it) < (size + 2))
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
*_it++ = code;
|
||||
*_it++ = size;
|
||||
|
||||
memcpy_P(_it, data, size);
|
||||
_it += size;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DhcpServer::DhcpServer(netif* netif): _netif(netif)
|
||||
{
|
||||
pcb_dhcps = nullptr;
|
||||
dns_address.addr = 0;
|
||||
plist = nullptr;
|
||||
offer = 0xFF;
|
||||
renew = false;
|
||||
dhcps_lease_time = DHCPS_LEASE_TIME_DEF; //minute
|
||||
|
||||
if (netif->num == SOFTAP_IF && fw_has_started_softap_dhcps == 1)
|
||||
{
|
||||
// When nonos-sdk starts DHCPS at boot:
|
||||
// 1. `fw_has_started_softap_dhcps` is already initialized to 1
|
||||
// 2. global ctor DhcpServer's `dhcpSoftAP(&netif_git[SOFTAP_IF])` is called
|
||||
// 3. (that's here) => begin(legacy-values) is called
|
||||
ip_info ip =
|
||||
{
|
||||
{ 0x0104a8c0 }, // IP 192.168.4.1
|
||||
{ 0x00ffffff }, // netmask 255.255.255.0
|
||||
{ 0 } // gateway 0.0.0.0
|
||||
};
|
||||
begin(&ip);
|
||||
fw_has_started_softap_dhcps = 2; // not 1, ending intial boot sequence
|
||||
}
|
||||
};
|
||||
DhcpServer::DhcpServer(netif* netif) : _netif(netif) { }
|
||||
|
||||
// wifi_softap_set_station_info is missing in user_interface.h:
|
||||
extern "C" void wifi_softap_set_station_info(uint8_t* mac, struct ipv4_addr*);
|
||||
|
||||
void DhcpServer::dhcps_set_dns(int num, const ipv4_addr_t* dns)
|
||||
{
|
||||
(void)num;
|
||||
if (!ip4_addr_isany(dns))
|
||||
{
|
||||
ip4_addr_copy(dns_address, *dns);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
FunctionName : node_insert_to_list
|
||||
Description : insert the node to the list
|
||||
@@ -308,8 +295,8 @@ bool DhcpServer::add_dhcps_lease(uint8 *macaddr)
|
||||
struct dhcps_pool* pdhcps_pool = nullptr;
|
||||
list_node* pback_node = nullptr;
|
||||
|
||||
uint32 start_ip = dhcps_lease.start_ip.addr;
|
||||
uint32 end_ip = dhcps_lease.end_ip.addr;
|
||||
uint32 start_ip = lease.start_ip.addr;
|
||||
uint32 end_ip = lease.end_ip.addr;
|
||||
|
||||
for (pback_node = plist; pback_node != nullptr; pback_node = pback_node->pnext)
|
||||
{
|
||||
@@ -338,7 +325,7 @@ bool DhcpServer::add_dhcps_lease(uint8 *macaddr)
|
||||
pdhcps_pool = (struct dhcps_pool*)zalloc(sizeof(struct dhcps_pool));
|
||||
pdhcps_pool->ip.addr = start_ip;
|
||||
memcpy(pdhcps_pool->mac, macaddr, sizeof(pdhcps_pool->mac));
|
||||
pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
|
||||
pdhcps_pool->lease_timer = this->lease_time;
|
||||
pdhcps_pool->type = DHCPS_TYPE_STATIC;
|
||||
pdhcps_pool->state = DHCPS_STATE_ONLINE;
|
||||
pback_node = (list_node*)zalloc(sizeof(list_node));
|
||||
@@ -351,147 +338,50 @@ bool DhcpServer::add_dhcps_lease(uint8 *macaddr)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
DHCP msg
|
||||
Set DHCP msg offer options for the given server
|
||||
|
||||
@param optptr -- DHCP msg
|
||||
@param type -- option
|
||||
|
||||
@return uint8_t* DHCP msg
|
||||
@param buffer -- DHCP options buffer
|
||||
@param server -- DHCP server instance
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t* DhcpServer::add_msg_type(uint8_t *optptr, uint8_t type)
|
||||
void DhcpServer::add_offer_options(OptionsBuffer& options)
|
||||
{
|
||||
options.add(DHCP_OPTION_SUBNET_MASK, ip_2_ip4(&_netif->netmask))
|
||||
.add(DHCP_OPTION_SERVER_ID, ip_2_ip4(&_netif->ip_addr));
|
||||
|
||||
*optptr++ = DHCP_OPTION_MSG_TYPE;
|
||||
*optptr++ = 1;
|
||||
*optptr++ = type;
|
||||
return optptr;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
DHCP msg offer
|
||||
// option units are seconds, while server opt is minutes
|
||||
const uint32_t lease_time_seconds = lease_time * 60;
|
||||
options.add(DHCP_OPTION_LEASE_TIME, lease_time_seconds);
|
||||
|
||||
@param optptr -- DHCP msg
|
||||
|
||||
@return uint8_t* DHCP msg
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t* DhcpServer::add_offer_options(uint8_t *optptr)
|
||||
if (offer_router && !ip4_addr_isany_val(*ip_2_ip4(&_netif->gw)))
|
||||
{
|
||||
//struct ipv4_addr ipadd;
|
||||
//ipadd.addr = server_address.addr;
|
||||
#define ipadd (_netif->ip_addr)
|
||||
|
||||
//struct ip_info if_ip;
|
||||
//bzero(&if_ip, sizeof(struct ip_info));
|
||||
//wifi_get_ip_info(SOFTAP_IF, &if_ip);
|
||||
#define if_ip (*_netif)
|
||||
|
||||
*optptr++ = DHCP_OPTION_SUBNET_MASK;
|
||||
*optptr++ = 4;
|
||||
*optptr++ = ip4_addr1(ip_2_ip4(&if_ip.netmask));
|
||||
*optptr++ = ip4_addr2(ip_2_ip4(&if_ip.netmask));
|
||||
*optptr++ = ip4_addr3(ip_2_ip4(&if_ip.netmask));
|
||||
*optptr++ = ip4_addr4(ip_2_ip4(&if_ip.netmask));
|
||||
|
||||
*optptr++ = DHCP_OPTION_LEASE_TIME;
|
||||
*optptr++ = 4;
|
||||
*optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 24) & 0xFF;
|
||||
*optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 16) & 0xFF;
|
||||
*optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 8) & 0xFF;
|
||||
*optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 0) & 0xFF;
|
||||
|
||||
*optptr++ = DHCP_OPTION_SERVER_ID;
|
||||
*optptr++ = 4;
|
||||
*optptr++ = ip4_addr1(ip_2_ip4(&ipadd));
|
||||
*optptr++ = ip4_addr2(ip_2_ip4(&ipadd));
|
||||
*optptr++ = ip4_addr3(ip_2_ip4(&ipadd));
|
||||
*optptr++ = ip4_addr4(ip_2_ip4(&ipadd));
|
||||
|
||||
if (dhcps_router_enabled(offer) && ip_2_ip4(&if_ip.gw)->addr)
|
||||
{
|
||||
*optptr++ = DHCP_OPTION_ROUTER;
|
||||
*optptr++ = 4;
|
||||
*optptr++ = ip4_addr1(ip_2_ip4(&if_ip.gw));
|
||||
*optptr++ = ip4_addr2(ip_2_ip4(&if_ip.gw));
|
||||
*optptr++ = ip4_addr3(ip_2_ip4(&if_ip.gw));
|
||||
*optptr++ = ip4_addr4(ip_2_ip4(&if_ip.gw));
|
||||
options.add(DHCP_OPTION_ROUTER, ip_2_ip4(&_netif->gw));
|
||||
}
|
||||
|
||||
#ifdef USE_DNS
|
||||
*optptr++ = DHCP_OPTION_DNS_SERVER;
|
||||
*optptr++ = 4;
|
||||
if (dns_address.addr == 0)
|
||||
{
|
||||
*optptr++ = ip4_addr1(ip_2_ip4(&ipadd));
|
||||
*optptr++ = ip4_addr2(ip_2_ip4(&ipadd));
|
||||
*optptr++ = ip4_addr3(ip_2_ip4(&ipadd));
|
||||
*optptr++ = ip4_addr4(ip_2_ip4(&ipadd));
|
||||
}
|
||||
else
|
||||
{
|
||||
*optptr++ = ip4_addr1(&dns_address);
|
||||
*optptr++ = ip4_addr2(&dns_address);
|
||||
*optptr++ = ip4_addr3(&dns_address);
|
||||
*optptr++ = ip4_addr4(&dns_address);
|
||||
}
|
||||
options.add(DHCP_OPTION_DNS_SERVER,
|
||||
!ip4_addr_isany_val(dns_address) ? &dns_address : ip_2_ip4(&_netif->ip_addr));
|
||||
#endif
|
||||
|
||||
*optptr++ = DHCP_OPTION_BROADCAST_ADDRESS;
|
||||
*optptr++ = 4;
|
||||
// XXXFIXME do better than that, we have netmask
|
||||
*optptr++ = ip4_addr1(ip_2_ip4(&ipadd));
|
||||
*optptr++ = ip4_addr2(ip_2_ip4(&ipadd));
|
||||
*optptr++ = ip4_addr3(ip_2_ip4(&ipadd));
|
||||
*optptr++ = 255;
|
||||
|
||||
*optptr++ = DHCP_OPTION_INTERFACE_MTU;
|
||||
*optptr++ = 2;
|
||||
*optptr++ = 0x05;
|
||||
*optptr++ = 0xdc; // 1500
|
||||
|
||||
*optptr++ = DHCP_OPTION_PERFORM_ROUTER_DISCOVERY;
|
||||
*optptr++ = 1;
|
||||
*optptr++ = 0x00;
|
||||
|
||||
#if 0 // vendor specific unititialized (??)
|
||||
*optptr++ = 43; // vendor specific
|
||||
*optptr++ = 6;
|
||||
// unitialized ?
|
||||
#endif
|
||||
|
||||
#if 0 // already set (DHCP_OPTION_SUBNET_MASK==1) (??)
|
||||
*optptr++ = 0x01;
|
||||
*optptr++ = 4;
|
||||
*optptr++ = 0;
|
||||
*optptr++ = 0;
|
||||
*optptr++ = 0;
|
||||
*optptr++ = 2;
|
||||
#endif
|
||||
|
||||
return optptr;
|
||||
|
||||
#undef ipadd
|
||||
#undef if_ip
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
DHCP msg
|
||||
|
||||
@param optptr -- DHCP msg
|
||||
|
||||
@return uint8_t* DHCP msg
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t* DhcpServer::add_end(uint8_t *optptr)
|
||||
{
|
||||
const auto* addr = ip_2_ip4(&_netif->ip_addr);
|
||||
const auto* mask = ip_2_ip4(&_netif->netmask);
|
||||
const auto broadcast = ip4_addr_t { .addr = (addr->addr & mask->addr) | ~mask->addr };
|
||||
|
||||
*optptr++ = DHCP_OPTION_END;
|
||||
return optptr;
|
||||
options.add(DHCP_OPTION_BROADCAST_ADDRESS, &broadcast);
|
||||
}
|
||||
|
||||
// TODO: _netif->mtu ?
|
||||
static constexpr uint16_t Mtu { 1500 };
|
||||
options.add(DHCP_OPTION_INTERFACE_MTU, Mtu);
|
||||
|
||||
static constexpr uint8_t RouterDiscovery { 0 };
|
||||
options.add(DHCP_OPTION_PERFORM_ROUTER_DISCOVERY, RouterDiscovery);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
void DhcpServer::create_msg(struct dhcps_msg *m)
|
||||
DhcpServer::OptionsBuffer DhcpServer::create_msg(struct dhcps_msg* m)
|
||||
{
|
||||
struct ipv4_addr client;
|
||||
|
||||
@@ -511,7 +401,9 @@ void DhcpServer::create_msg(struct dhcps_msg *m)
|
||||
memset((char*)m->sname, 0, sizeof(m->sname));
|
||||
memset((char*)m->file, 0, sizeof(m->file));
|
||||
memset((char*)m->options, 0, sizeof(m->options));
|
||||
memcpy((char *) m->options, &magic_cookie, sizeof(magic_cookie));
|
||||
memcpy((char*)m->options, &MagicCookie, sizeof(MagicCookie));
|
||||
|
||||
return { &m->options[sizeof(magic_cookie)], std::end(m->options) };
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
@@ -522,16 +414,18 @@ void DhcpServer::create_msg(struct dhcps_msg *m)
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
void DhcpServer::send_offer(struct dhcps_msg* m)
|
||||
{
|
||||
uint8_t *end;
|
||||
struct pbuf *p, *q;
|
||||
u8_t *data;
|
||||
u16_t cnt = 0;
|
||||
u16_t i;
|
||||
create_msg(m);
|
||||
|
||||
end = add_msg_type(&m->options[4], DHCPOFFER);
|
||||
end = add_offer_options(end);
|
||||
end = add_end(end);
|
||||
auto options = create_msg(m);
|
||||
options.add(DHCP_OPTION_MSG_TYPE, DHCPOFFER);
|
||||
|
||||
add_offer_options(options);
|
||||
if (custom_offer_options)
|
||||
{
|
||||
custom_offer_options(*this, options);
|
||||
}
|
||||
|
||||
options.add(DHCP_OPTION_END);
|
||||
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM);
|
||||
#if DHCPS_DEBUG
|
||||
@@ -539,7 +433,6 @@ void DhcpServer::send_offer(struct dhcps_msg *m)
|
||||
#endif
|
||||
if (p != nullptr)
|
||||
{
|
||||
|
||||
#if DHCPS_DEBUG
|
||||
os_printf("dhcps: send_offer>>pbuf_alloc succeed\n");
|
||||
os_printf("dhcps: send_offer>>p->tot_len = %d\n", p->tot_len);
|
||||
@@ -548,18 +441,12 @@ void DhcpServer::send_offer(struct dhcps_msg *m)
|
||||
q = p;
|
||||
while (q != nullptr)
|
||||
{
|
||||
data = (u8_t *)q->payload;
|
||||
for (i = 0; i < q->len; i++)
|
||||
{
|
||||
data[i] = ((u8_t *) m)[cnt++];
|
||||
}
|
||||
|
||||
std::memcpy((u8_t*)q->payload, reinterpret_cast<u8_t*>(&m), q->len);
|
||||
q = q->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if DHCPS_DEBUG
|
||||
os_printf("dhcps: send_offer>>pbuf_alloc failed\n");
|
||||
#endif
|
||||
@@ -588,16 +475,11 @@ void DhcpServer::send_offer(struct dhcps_msg *m)
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
void DhcpServer::send_nak(struct dhcps_msg* m)
|
||||
{
|
||||
|
||||
u8_t *end;
|
||||
struct pbuf *p, *q;
|
||||
u8_t *data;
|
||||
u16_t cnt = 0;
|
||||
u16_t i;
|
||||
create_msg(m);
|
||||
|
||||
end = add_msg_type(&m->options[4], DHCPNAK);
|
||||
end = add_end(end);
|
||||
auto options = create_msg(m);
|
||||
options.add(DHCP_OPTION_MSG_TYPE, DHCPNAK);
|
||||
options.add(DHCP_OPTION_END);
|
||||
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM);
|
||||
#if DHCPS_DEBUG
|
||||
@@ -605,7 +487,6 @@ void DhcpServer::send_nak(struct dhcps_msg *m)
|
||||
#endif
|
||||
if (p != nullptr)
|
||||
{
|
||||
|
||||
#if DHCPS_DEBUG
|
||||
os_printf("dhcps: send_nak>>pbuf_alloc succeed\n");
|
||||
os_printf("dhcps: send_nak>>p->tot_len = %d\n", p->tot_len);
|
||||
@@ -614,18 +495,12 @@ void DhcpServer::send_nak(struct dhcps_msg *m)
|
||||
q = p;
|
||||
while (q != nullptr)
|
||||
{
|
||||
data = (u8_t *)q->payload;
|
||||
for (i = 0; i < q->len; i++)
|
||||
{
|
||||
data[i] = ((u8_t *) m)[cnt++];
|
||||
}
|
||||
|
||||
std::memcpy((u8_t*)q->payload, (u8_t*)m, q->len);
|
||||
q = q->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if DHCPS_DEBUG
|
||||
os_printf("dhcps: send_nak>>pbuf_alloc failed\n");
|
||||
#endif
|
||||
@@ -649,17 +524,18 @@ void DhcpServer::send_nak(struct dhcps_msg *m)
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
void DhcpServer::send_ack(struct dhcps_msg* m)
|
||||
{
|
||||
|
||||
u8_t *end;
|
||||
struct pbuf *p, *q;
|
||||
u8_t *data;
|
||||
u16_t cnt = 0;
|
||||
u16_t i;
|
||||
create_msg(m);
|
||||
|
||||
end = add_msg_type(&m->options[4], DHCPACK);
|
||||
end = add_offer_options(end);
|
||||
end = add_end(end);
|
||||
auto options = create_msg(m);
|
||||
options.add(DHCP_OPTION_MSG_TYPE, DHCPACK);
|
||||
|
||||
add_offer_options(options);
|
||||
if (custom_offer_options)
|
||||
{
|
||||
custom_offer_options(*this, options);
|
||||
}
|
||||
|
||||
options.add(DHCP_OPTION_END);
|
||||
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM);
|
||||
#if DHCPS_DEBUG
|
||||
@@ -667,7 +543,6 @@ void DhcpServer::send_ack(struct dhcps_msg *m)
|
||||
#endif
|
||||
if (p != nullptr)
|
||||
{
|
||||
|
||||
#if DHCPS_DEBUG
|
||||
os_printf("dhcps: send_ack>>pbuf_alloc succeed\n");
|
||||
os_printf("dhcps: send_ack>>p->tot_len = %d\n", p->tot_len);
|
||||
@@ -676,24 +551,19 @@ void DhcpServer::send_ack(struct dhcps_msg *m)
|
||||
q = p;
|
||||
while (q != nullptr)
|
||||
{
|
||||
data = (u8_t *)q->payload;
|
||||
for (i = 0; i < q->len; i++)
|
||||
{
|
||||
data[i] = ((u8_t *) m)[cnt++];
|
||||
}
|
||||
|
||||
std::memcpy((u8_t*)q->payload, (u8_t*)m, q->len);
|
||||
q = q->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if DHCPS_DEBUG
|
||||
os_printf("dhcps: send_ack>>pbuf_alloc failed\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (!LWIP_IS_OK("dhcps send ack", udp_sendto(pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT)))
|
||||
if (!LWIP_IS_OK("dhcps send ack",
|
||||
udp_sendto(pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT)))
|
||||
{
|
||||
#if DHCPS_DEBUG
|
||||
os_printf("dhcps: send_ack>>udp_sendto\n");
|
||||
@@ -738,7 +608,6 @@ uint8_t DhcpServer::parse_options(uint8_t *optptr, sint16_t len)
|
||||
#endif
|
||||
switch ((sint16_t)*optptr)
|
||||
{
|
||||
|
||||
case DHCP_OPTION_MSG_TYPE: // 53
|
||||
type = *(optptr + 2);
|
||||
break;
|
||||
@@ -824,9 +693,7 @@ uint8_t DhcpServer::parse_options(uint8_t *optptr, sint16_t len)
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
sint16_t DhcpServer::parse_msg(struct dhcps_msg* m, u16_t len)
|
||||
{
|
||||
if (memcmp((char *)m->options,
|
||||
&magic_cookie,
|
||||
sizeof(magic_cookie)) == 0)
|
||||
if (memcmp((char*)m->options, &MagicCookie, sizeof(MagicCookie)) == 0)
|
||||
{
|
||||
struct ipv4_addr ip;
|
||||
memcpy(&ip.addr, m->ciaddr, sizeof(ip.addr));
|
||||
@@ -857,20 +724,14 @@ sint16_t DhcpServer::parse_msg(struct dhcps_msg *m, u16_t len)
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DhcpServer::S_handle_dhcp(void *arg,
|
||||
struct udp_pcb *pcb,
|
||||
struct pbuf *p,
|
||||
const ip_addr_t *addr,
|
||||
uint16_t port)
|
||||
void DhcpServer::S_handle_dhcp(void* arg, struct udp_pcb* pcb, struct pbuf* p,
|
||||
const ip_addr_t* addr, uint16_t port)
|
||||
{
|
||||
DhcpServer* instance = reinterpret_cast<DhcpServer*>(arg);
|
||||
instance->handle_dhcp(pcb, p, addr, port);
|
||||
}
|
||||
|
||||
void DhcpServer::handle_dhcp(
|
||||
struct udp_pcb *pcb,
|
||||
struct pbuf *p,
|
||||
const ip_addr_t *addr,
|
||||
void DhcpServer::handle_dhcp(struct udp_pcb* pcb, struct pbuf* p, const ip_addr_t* addr,
|
||||
uint16_t port)
|
||||
{
|
||||
(void)pcb;
|
||||
@@ -933,7 +794,6 @@ void DhcpServer::handle_dhcp(
|
||||
|
||||
switch (parse_msg(pmsg_dhcps, tlen - 240))
|
||||
{
|
||||
|
||||
case DHCPS_STATE_OFFER: // 1
|
||||
#if DHCPS_DEBUG
|
||||
os_printf("dhcps: handle_dhcp-> DHCPD_STATE_OFFER\n");
|
||||
@@ -972,15 +832,15 @@ void DhcpServer::init_dhcps_lease(uint32 ip)
|
||||
uint32 softap_ip = 0, local_ip = 0;
|
||||
uint32 start_ip = 0;
|
||||
uint32 end_ip = 0;
|
||||
if (dhcps_lease.enable == true)
|
||||
if (lease.enable == true)
|
||||
{
|
||||
softap_ip = htonl(ip);
|
||||
start_ip = htonl(dhcps_lease.start_ip.addr);
|
||||
end_ip = htonl(dhcps_lease.end_ip.addr);
|
||||
start_ip = htonl(lease.start_ip.addr);
|
||||
end_ip = htonl(lease.end_ip.addr);
|
||||
/*config ip information can't contain local ip*/
|
||||
if ((start_ip <= softap_ip) && (softap_ip <= end_ip))
|
||||
{
|
||||
dhcps_lease.enable = false;
|
||||
lease.enable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -989,12 +849,12 @@ void DhcpServer::init_dhcps_lease(uint32 ip)
|
||||
if (((start_ip >> 8 != softap_ip) || (end_ip >> 8 != softap_ip))
|
||||
|| (end_ip - start_ip > DHCPS_MAX_LEASE))
|
||||
{
|
||||
dhcps_lease.enable = false;
|
||||
lease.enable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dhcps_lease.enable == false)
|
||||
if (lease.enable == false)
|
||||
{
|
||||
local_ip = softap_ip = htonl(ip);
|
||||
softap_ip &= 0xFFFFFF00;
|
||||
@@ -1008,19 +868,19 @@ void DhcpServer::init_dhcps_lease(uint32 ip)
|
||||
local_ip++;
|
||||
}
|
||||
|
||||
bzero(&dhcps_lease, sizeof(dhcps_lease));
|
||||
dhcps_lease.start_ip.addr = softap_ip | local_ip;
|
||||
dhcps_lease.end_ip.addr = softap_ip | (local_ip + DHCPS_MAX_LEASE - 1);
|
||||
dhcps_lease.start_ip.addr = htonl(dhcps_lease.start_ip.addr);
|
||||
dhcps_lease.end_ip.addr = htonl(dhcps_lease.end_ip.addr);
|
||||
bzero(&lease, sizeof(lease));
|
||||
lease.start_ip.addr = softap_ip | local_ip;
|
||||
lease.end_ip.addr = softap_ip | (local_ip + DHCPS_MAX_LEASE - 1);
|
||||
lease.start_ip.addr = htonl(lease.start_ip.addr);
|
||||
lease.end_ip.addr = htonl(lease.end_ip.addr);
|
||||
}
|
||||
// dhcps_lease.start_ip.addr = htonl(dhcps_lease.start_ip.addr);
|
||||
// dhcps_lease.end_ip.addr= htonl(dhcps_lease.end_ip.addr);
|
||||
// os_printf("start_ip = 0x%x, end_ip = 0x%x\n",dhcps_lease.start_ip, dhcps_lease.end_ip);
|
||||
// lease.start_ip.addr = htonl(lease.start_ip.addr);
|
||||
// lease.end_ip.addr= htonl(lease.end_ip.addr);
|
||||
// os_printf("start_ip = 0x%x, end_ip = 0x%x\n",lease.start_ip, lease.end_ip);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool DhcpServer::begin(struct ip_info *info)
|
||||
bool DhcpServer::begin()
|
||||
{
|
||||
if (pcb_dhcps != nullptr)
|
||||
{
|
||||
@@ -1028,9 +888,11 @@ bool DhcpServer::begin(struct ip_info *info)
|
||||
}
|
||||
|
||||
pcb_dhcps = udp_new();
|
||||
if (pcb_dhcps == nullptr || info == nullptr)
|
||||
if (pcb_dhcps == nullptr)
|
||||
{
|
||||
#if DHCPS_DEBUG
|
||||
os_printf("dhcps_start(): could not obtain pcb\n");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1042,21 +904,16 @@ bool DhcpServer::begin(struct ip_info *info)
|
||||
ip_2_ip4(&broadcast_dhcps)->addr |= ~ip_2_ip4(&_netif->netmask)->addr;
|
||||
// XXXFIXMEIPV6 broadcast address?
|
||||
|
||||
server_address = info->ip;
|
||||
server_address = *ip_2_ip4(&_netif->ip_addr);
|
||||
init_dhcps_lease(server_address.addr);
|
||||
|
||||
udp_bind(pcb_dhcps, IP_ADDR_ANY, DHCPS_SERVER_PORT);
|
||||
udp_recv(pcb_dhcps, S_handle_dhcp, this);
|
||||
#if DHCPS_DEBUG
|
||||
os_printf("dhcps:dhcps_start->udp_recv function Set a receive callback handle_dhcp for UDP_PCB pcb_dhcps\n");
|
||||
os_printf("dhcps:dhcps_start->udp_recv function Set a receive callback handle_dhcp for UDP_PCB "
|
||||
"pcb_dhcps\n");
|
||||
#endif
|
||||
|
||||
if (_netif->num == SOFTAP_IF)
|
||||
{
|
||||
wifi_set_ip_info(SOFTAP_IF, info); // added for lwip-git, not sure whether useful
|
||||
}
|
||||
_netif->flags |= NETIF_FLAG_UP | NETIF_FLAG_LINK_UP; // added for lwip-git
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1102,12 +959,11 @@ void DhcpServer::end()
|
||||
}
|
||||
}
|
||||
|
||||
bool DhcpServer::isRunning()
|
||||
bool DhcpServer::isRunning() const
|
||||
{
|
||||
return !!_netif->state;
|
||||
return pcb_dhcps != nullptr;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
FunctionName : set_dhcps_lease
|
||||
Description : set the lease information of DHCP server
|
||||
@@ -1151,8 +1007,7 @@ bool DhcpServer::set_dhcps_lease(struct dhcps_lease *please)
|
||||
|
||||
/*config ip information must be in the same segment as the local ip*/
|
||||
softap_ip >>= 8;
|
||||
if ((start_ip >> 8 != softap_ip)
|
||||
|| (end_ip >> 8 != softap_ip))
|
||||
if ((start_ip >> 8 != softap_ip) || (end_ip >> 8 != softap_ip))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1162,13 +1017,13 @@ bool DhcpServer::set_dhcps_lease(struct dhcps_lease *please)
|
||||
return false;
|
||||
}
|
||||
|
||||
bzero(&dhcps_lease, sizeof(dhcps_lease));
|
||||
// dhcps_lease.start_ip.addr = start_ip;
|
||||
// dhcps_lease.end_ip.addr = end_ip;
|
||||
dhcps_lease.start_ip.addr = please->start_ip.addr;
|
||||
dhcps_lease.end_ip.addr = please->end_ip.addr;
|
||||
bzero(&lease, sizeof(lease));
|
||||
// lease.start_ip.addr = start_ip;
|
||||
// lease.end_ip.addr = end_ip;
|
||||
lease.start_ip.addr = please->start_ip.addr;
|
||||
lease.end_ip.addr = please->end_ip.addr;
|
||||
}
|
||||
dhcps_lease.enable = please->enable;
|
||||
lease.enable = please->enable;
|
||||
// dhcps_lease_flag = false;
|
||||
return true;
|
||||
}
|
||||
@@ -1197,7 +1052,7 @@ bool DhcpServer::get_dhcps_lease(struct dhcps_lease *please)
|
||||
}
|
||||
|
||||
// if (dhcps_lease_flag){
|
||||
if (dhcps_lease.enable == false)
|
||||
if (lease.enable == false)
|
||||
{
|
||||
if (isRunning())
|
||||
{
|
||||
@@ -1206,20 +1061,20 @@ bool DhcpServer::get_dhcps_lease(struct dhcps_lease *please)
|
||||
}
|
||||
else
|
||||
{
|
||||
// bzero(please, sizeof(dhcps_lease));
|
||||
// bzero(please, sizeof(*please));
|
||||
// if (!isRunning()){
|
||||
// please->start_ip.addr = htonl(dhcps_lease.start_ip.addr);
|
||||
// please->end_ip.addr = htonl(dhcps_lease.end_ip.addr);
|
||||
// please->start_ip.addr = htonl(lease.start_ip.addr);
|
||||
// please->end_ip.addr = htonl(lease.end_ip.addr);
|
||||
// }
|
||||
}
|
||||
|
||||
// if (isRunning()){
|
||||
// bzero(please, sizeof(dhcps_lease));
|
||||
// please->start_ip.addr = dhcps_lease.start_ip.addr;
|
||||
// please->end_ip.addr = dhcps_lease.end_ip.addr;
|
||||
// bzero(please, sizeof(*please));
|
||||
// please->start_ip.addr = lease.start_ip.addr;
|
||||
// please->end_ip.addr = lease.end_ip.addr;
|
||||
// }
|
||||
please->start_ip.addr = dhcps_lease.start_ip.addr;
|
||||
please->end_ip.addr = dhcps_lease.end_ip.addr;
|
||||
please->start_ip.addr = lease.start_ip.addr;
|
||||
please->end_ip.addr = lease.end_ip.addr;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1244,7 +1099,8 @@ void DhcpServer::kill_oldest_dhcps_pool(void)
|
||||
pre = p;
|
||||
p = p->pnext;
|
||||
}
|
||||
minpre->pnext = minp->pnext; pdhcps_pool->state = DHCPS_STATE_OFFLINE;
|
||||
minpre->pnext = minp->pnext;
|
||||
pdhcps_pool->state = DHCPS_STATE_OFFLINE;
|
||||
free(minp->pnode);
|
||||
minp->pnode = nullptr;
|
||||
free(minp);
|
||||
@@ -1288,81 +1144,6 @@ void DhcpServer::dhcps_coarse_tmr(void)
|
||||
}
|
||||
}
|
||||
|
||||
bool DhcpServer::set_dhcps_offer_option(uint8 level, void* optarg)
|
||||
{
|
||||
bool offer_flag = true;
|
||||
//uint8 option = 0;
|
||||
if (optarg == nullptr && !isRunning())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (level <= OFFER_START || level >= OFFER_END)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (level)
|
||||
{
|
||||
case OFFER_ROUTER:
|
||||
offer = (*(uint8 *)optarg) & 0x01;
|
||||
offer_flag = true;
|
||||
break;
|
||||
default :
|
||||
offer_flag = false;
|
||||
break;
|
||||
}
|
||||
return offer_flag;
|
||||
}
|
||||
|
||||
bool DhcpServer::set_dhcps_lease_time(uint32 minute)
|
||||
{
|
||||
if (_netif->num == SOFTAP_IF)
|
||||
{
|
||||
uint8 opmode = wifi_get_opmode();
|
||||
if (opmode == STATION_MODE || opmode == NULL_MODE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRunning())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minute == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
dhcps_lease_time = minute;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DhcpServer::reset_dhcps_lease_time(void)
|
||||
{
|
||||
if (_netif->num == SOFTAP_IF)
|
||||
{
|
||||
uint8 opmode = wifi_get_opmode();
|
||||
if (opmode == STATION_MODE || opmode == NULL_MODE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRunning())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
dhcps_lease_time = DHCPS_LEASE_TIME_DEF;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 DhcpServer::get_dhcps_lease_time(void) // minute
|
||||
{
|
||||
return dhcps_lease_time;
|
||||
}
|
||||
|
||||
void DhcpServer::dhcps_client_leave(u8* bssid, struct ipv4_addr* ip, bool force)
|
||||
{
|
||||
struct dhcps_pool* pdhcps_pool = nullptr;
|
||||
@@ -1419,8 +1200,8 @@ uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip)
|
||||
list_node* pmac_node = nullptr;
|
||||
list_node* pip_node = nullptr;
|
||||
bool flag = false;
|
||||
uint32 start_ip = dhcps_lease.start_ip.addr;
|
||||
uint32 end_ip = dhcps_lease.end_ip.addr;
|
||||
uint32 start_ip = lease.start_ip.addr;
|
||||
uint32 end_ip = lease.end_ip.addr;
|
||||
dhcps_type_t type = DHCPS_TYPE_DYNAMIC;
|
||||
if (bssid == nullptr)
|
||||
{
|
||||
@@ -1528,10 +1309,9 @@ uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip)
|
||||
type = DHCPS_TYPE_DYNAMIC;
|
||||
}
|
||||
|
||||
pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
|
||||
pdhcps_pool->lease_timer = this->lease_time;
|
||||
pdhcps_pool->type = type;
|
||||
pdhcps_pool->state = DHCPS_STATE_ONLINE;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1550,7 +1330,7 @@ uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip)
|
||||
}
|
||||
|
||||
node_remove_from_list(&plist, pmac_node);
|
||||
pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
|
||||
pdhcps_pool->lease_timer = this->lease_time;
|
||||
pdhcps_pool->type = type;
|
||||
pdhcps_pool->state = DHCPS_STATE_ONLINE;
|
||||
node_insert_to_list(&plist, pmac_node);
|
||||
@@ -1566,7 +1346,7 @@ uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip)
|
||||
return IPADDR_ANY;
|
||||
}
|
||||
memcpy(pdhcps_pool->mac, bssid, sizeof(pdhcps_pool->mac));
|
||||
pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
|
||||
pdhcps_pool->lease_timer = this->lease_time;
|
||||
pdhcps_pool->type = type;
|
||||
pdhcps_pool->state = DHCPS_STATE_ONLINE;
|
||||
}
|
||||
@@ -1592,7 +1372,7 @@ uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip)
|
||||
return IPADDR_ANY;
|
||||
}
|
||||
memcpy(pdhcps_pool->mac, bssid, sizeof(pdhcps_pool->mac));
|
||||
pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
|
||||
pdhcps_pool->lease_timer = this->lease_time;
|
||||
pdhcps_pool->type = type;
|
||||
pdhcps_pool->state = DHCPS_STATE_ONLINE;
|
||||
pback_node = (list_node*)zalloc(sizeof(list_node));
|
||||
|
@@ -28,23 +28,147 @@
|
||||
// nearly as-is. This is an initial version to guaranty legacy behavior
|
||||
// with same default values.
|
||||
|
||||
#ifndef __DHCPS_H__
|
||||
#define __DHCPS_H__
|
||||
#pragma once
|
||||
|
||||
#include <lwip/init.h> // LWIP_VERSION
|
||||
#include <lwip/init.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
|
||||
class DhcpServer
|
||||
{
|
||||
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();
|
||||
|
||||
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();
|
||||
bool isRunning();
|
||||
bool isRunning() const;
|
||||
|
||||
// this is the C interface encapsulated in a class
|
||||
// (originally dhcpserver.c in lwIP-v1.4 in NonOS-SDK)
|
||||
@@ -56,15 +180,12 @@ public:
|
||||
void init_dhcps_lease(uint32 ip);
|
||||
bool set_dhcps_lease(struct dhcps_lease* please);
|
||||
bool get_dhcps_lease(struct dhcps_lease* please);
|
||||
bool set_dhcps_offer_option(uint8 level, void* optarg);
|
||||
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:
|
||||
void add_offer_options(OptionsBuffer&);
|
||||
|
||||
// legacy C structure and API to eventually turn into C++
|
||||
|
||||
@@ -76,49 +197,40 @@ protected:
|
||||
|
||||
void node_insert_to_list(list_node** phead, list_node* pinsert);
|
||||
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);
|
||||
uint8_t* add_end(uint8_t *optptr);
|
||||
void create_msg(struct dhcps_msg *m);
|
||||
|
||||
OptionsBuffer create_msg(struct dhcps_msg* m);
|
||||
|
||||
void send_offer(struct dhcps_msg* m);
|
||||
void send_nak(struct dhcps_msg* m);
|
||||
void send_ack(struct dhcps_msg* m);
|
||||
uint8_t parse_options(uint8_t* optptr, sint16_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,
|
||||
uint16_t port);
|
||||
void handle_dhcp(
|
||||
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);
|
||||
void handle_dhcp(struct udp_pcb* pcb, struct pbuf* p, const ip_addr_t* addr, uint16_t port);
|
||||
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;
|
||||
ip_addr_t broadcast_dhcps;
|
||||
struct ipv4_addr server_address;
|
||||
struct ipv4_addr client_address;
|
||||
struct ipv4_addr dns_address;
|
||||
uint32 dhcps_lease_time;
|
||||
struct udp_pcb* pcb_dhcps = nullptr;
|
||||
ip_addr_t broadcast_dhcps {};
|
||||
ip4_addr_t server_address {};
|
||||
ip4_addr_t client_address {};
|
||||
|
||||
struct dhcps_lease dhcps_lease;
|
||||
list_node *plist;
|
||||
uint8 offer;
|
||||
bool renew;
|
||||
uint32_t lease_time = DefaultLeaseTime;
|
||||
|
||||
bool offer_router = true;
|
||||
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;
|
||||
};
|
||||
|
||||
// SoftAP DHCP server always exists and is started on boot
|
||||
extern DhcpServer dhcpSoftAP;
|
||||
extern "C" int fw_has_started_softap_dhcps;
|
||||
|
||||
#endif // __DHCPS_H__
|
||||
|
@@ -1,5 +1,6 @@
|
||||
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/dns.h"
|
||||
@@ -24,10 +25,12 @@ extern "C" {
|
||||
//
|
||||
// result stored into gateway/netmask/dns1
|
||||
|
||||
bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1, const IPAddress& arg2, const IPAddress& arg3,
|
||||
IPAddress& gateway, IPAddress& netmask, IPAddress& dns1)
|
||||
bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1,
|
||||
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;
|
||||
netmask = arg2;
|
||||
dns1 = arg3;
|
||||
@@ -36,7 +39,9 @@ bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1
|
||||
{
|
||||
// octet is not 255 => interpret as Arduino order
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -143,7 +148,6 @@ bool LwipIntf::hostname(const char* aHostname)
|
||||
// harmless for AP, also compatible with ethernet adapters (to come)
|
||||
for (netif* intf = netif_list; intf; intf = intf->next)
|
||||
{
|
||||
|
||||
// unconditionally update all known interfaces
|
||||
intf->hostname = wifi_station_get_hostname();
|
||||
|
||||
@@ -162,4 +166,3 @@ bool LwipIntf::hostname(const char* aHostname)
|
||||
|
||||
return ret && compliant;
|
||||
}
|
||||
|
||||
|
@@ -10,7 +10,6 @@
|
||||
class LwipIntf
|
||||
{
|
||||
public:
|
||||
|
||||
using CBType = std::function<void(netif*)>;
|
||||
|
||||
static bool stateUpCB(LwipIntf::CBType&& cb);
|
||||
@@ -24,9 +23,9 @@ public:
|
||||
// arg3 | dns1 netmask
|
||||
//
|
||||
// result stored into gateway/netmask/dns1
|
||||
static
|
||||
bool ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1, const IPAddress& arg2, const IPAddress& arg3,
|
||||
IPAddress& gateway, IPAddress& netmask, IPAddress& dns1);
|
||||
static bool ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1,
|
||||
const IPAddress& arg2, const IPAddress& arg3, IPAddress& gateway,
|
||||
IPAddress& netmask, IPAddress& dns1);
|
||||
|
||||
String hostname();
|
||||
bool hostname(const String& aHostname)
|
||||
@@ -42,7 +41,6 @@ public:
|
||||
const char* getHostname();
|
||||
|
||||
protected:
|
||||
|
||||
static bool stateChangeSysCB(LwipIntf::CBType&& cb);
|
||||
};
|
||||
|
||||
|
@@ -33,10 +33,12 @@ bool LwipIntf::stateChangeSysCB(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]()
|
||||
schedule_function(
|
||||
[cb, nif]()
|
||||
{
|
||||
cb(nif);
|
||||
});
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include <lwip/netif.h>
|
||||
#include <lwip/etharp.h>
|
||||
#include <lwip/dhcp.h>
|
||||
#include <lwip/dns.h>
|
||||
#include <lwip/apps/sntp.h>
|
||||
|
||||
#include <user_interface.h> // wifi_get_macaddr()
|
||||
@@ -27,20 +28,15 @@
|
||||
template<class RawDev>
|
||||
class LwipIntfDev: public LwipIntf, public RawDev
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
boolean begin(const uint8_t* macAddress = nullptr, const uint16_t mtu = DEFAULT_MTU);
|
||||
@@ -63,20 +59,36 @@ public:
|
||||
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()
|
||||
{
|
||||
return !!ip4_addr_get_u32(ip_2_ip4(&_netif.ip_addr));
|
||||
}
|
||||
|
||||
bool routable()
|
||||
{
|
||||
return !ip_addr_isany(&_netif.gw);
|
||||
}
|
||||
|
||||
// ESP8266WiFi API compatibility
|
||||
|
||||
wl_status_t status();
|
||||
|
||||
protected:
|
||||
|
||||
err_t netif_init();
|
||||
void check_route();
|
||||
void netif_status_callback();
|
||||
|
||||
static err_t netif_init_s(netif* netif);
|
||||
@@ -95,11 +107,12 @@ protected:
|
||||
uint8_t _macAddress[6];
|
||||
bool _started;
|
||||
bool _default;
|
||||
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -116,6 +129,17 @@ 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.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;
|
||||
}
|
||||
|
||||
@@ -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(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;
|
||||
}
|
||||
|
||||
_netif.flags |= NETIF_FLAG_UP;
|
||||
|
||||
if (localIP().v4() == 0)
|
||||
{
|
||||
// IP not set, starting DHCP
|
||||
_netif.flags |= NETIF_FLAG_UP;
|
||||
switch (dhcp_start(&_netif))
|
||||
{
|
||||
case ERR_OK:
|
||||
@@ -192,6 +217,12 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// IP is set, static config
|
||||
netif_set_link_up(&_netif);
|
||||
netif_set_up(&_netif);
|
||||
}
|
||||
|
||||
_started = true;
|
||||
|
||||
@@ -203,16 +234,20 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (_intrPin < 0 && !schedule_recurrent_function_us([&]()
|
||||
if (_intrPin < 0
|
||||
&& !schedule_recurrent_function_us(
|
||||
[&]()
|
||||
{
|
||||
this->handlePackets();
|
||||
return true;
|
||||
}, 100))
|
||||
},
|
||||
100))
|
||||
{
|
||||
netif_remove(&_netif);
|
||||
return false;
|
||||
@@ -242,7 +277,8 @@ err_t LwipIntfDev<RawDev>::linkoutput_s(netif *netif, struct pbuf *pbuf)
|
||||
#if PHY_HAS_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
|
||||
|
||||
@@ -268,11 +304,7 @@ err_t LwipIntfDev<RawDev>::netif_init()
|
||||
_netif.name[1] = '0' + _netif.num;
|
||||
_netif.mtu = _mtu;
|
||||
_netif.chksum_flags = NETIF_CHECKSUM_ENABLE_ALL;
|
||||
_netif.flags =
|
||||
NETIF_FLAG_ETHARP
|
||||
| NETIF_FLAG_IGMP
|
||||
| NETIF_FLAG_BROADCAST
|
||||
| NETIF_FLAG_LINK_UP;
|
||||
_netif.flags = NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP | NETIF_FLAG_BROADCAST | NETIF_FLAG_LINK_UP;
|
||||
|
||||
// lwIP's doc: This function typically first resolves the hardware
|
||||
// address, then sends the packet. For ethernet physical layer, this is
|
||||
@@ -291,15 +323,26 @@ err_t LwipIntfDev<RawDev>::netif_init()
|
||||
template<class RawDev>
|
||||
void LwipIntfDev<RawDev>::netif_status_callback()
|
||||
{
|
||||
check_route();
|
||||
if (connected())
|
||||
{
|
||||
if (_default)
|
||||
{
|
||||
netif_set_default(&_netif);
|
||||
}
|
||||
sntp_stop();
|
||||
sntp_init();
|
||||
}
|
||||
}
|
||||
|
||||
template<class RawDev>
|
||||
void LwipIntfDev<RawDev>::check_route()
|
||||
{
|
||||
if (connected())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else if (netif_default == &_netif)
|
||||
{
|
||||
netif_set_default(nullptr);
|
||||
@@ -359,7 +402,8 @@ err_t LwipIntfDev<RawDev>::handlePackets()
|
||||
#if PHY_HAS_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
|
||||
|
||||
@@ -369,18 +413,14 @@ err_t LwipIntfDev<RawDev>::handlePackets()
|
||||
return err;
|
||||
}
|
||||
// (else) allocated pbuf is now lwIP's responsibility
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template<class RawDev>
|
||||
void LwipIntfDev<RawDev>::setDefault()
|
||||
void LwipIntfDev<RawDev>::setDefault(bool deflt)
|
||||
{
|
||||
_default = true;
|
||||
if (connected())
|
||||
{
|
||||
netif_set_default(&_netif);
|
||||
}
|
||||
_default = deflt;
|
||||
check_route();
|
||||
}
|
||||
|
||||
#endif // _LWIPINTFDEV_H
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <type_traits> // std::is_unsigned
|
||||
#include <core_esp8266_features.h>
|
||||
#include <coredecls.h>
|
||||
|
||||
namespace esp8266
|
||||
{
|
||||
@@ -45,7 +46,7 @@ struct DoNothing
|
||||
|
||||
struct YieldOrSkip
|
||||
{
|
||||
static void execute() {delay(0);}
|
||||
static void execute() {esp_yield();}
|
||||
};
|
||||
|
||||
template <unsigned long delayMs>
|
||||
|
@@ -76,7 +76,7 @@ class Print {
|
||||
inline size_t write(int8_t c) { return write((uint8_t) c); }
|
||||
|
||||
// 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; }
|
||||
|
||||
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(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..)
|
||||
// (children can override to false (like String))
|
||||
|
@@ -135,8 +135,6 @@ bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,
|
||||
|
||||
void run_scheduled_functions()
|
||||
{
|
||||
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
|
||||
|
||||
// prevent scheduling of new functions during this run
|
||||
auto stop = sLast;
|
||||
bool done = false;
|
||||
@@ -161,13 +159,10 @@ void run_scheduled_functions()
|
||||
recycle_fn_unsafe(to_recycle);
|
||||
}
|
||||
|
||||
if (yieldNow)
|
||||
{
|
||||
// because scheduled functions might last too long for watchdog etc,
|
||||
// this is yield() in cont stack:
|
||||
esp_schedule();
|
||||
cont_yield(g_pcont);
|
||||
}
|
||||
// scheduled functions might last too long for watchdog etc.
|
||||
// yield() is allowed in scheduled functions, therefore
|
||||
// recursion into run_scheduled_recurrent_functions() is permitted
|
||||
optimistic_yield(100000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,9 +236,10 @@ void run_scheduled_recurrent_functions()
|
||||
if (yieldNow)
|
||||
{
|
||||
// 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();
|
||||
cont_yield(g_pcont);
|
||||
cont_suspend(g_pcont);
|
||||
}
|
||||
} while (current && !done);
|
||||
|
||||
|
@@ -148,7 +148,7 @@ long Stream::parseInt(char skipChar) {
|
||||
|
||||
do {
|
||||
if(c == skipChar)
|
||||
; // ignore this charactor
|
||||
; // ignore this character
|
||||
else if(c == '-')
|
||||
isNegative = true;
|
||||
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();
|
||||
if (c == -1)
|
||||
break;
|
||||
buffer[nbread++] = read();
|
||||
buffer[nbread++] = c;
|
||||
}
|
||||
return nbread;
|
||||
}
|
||||
|
@@ -61,6 +61,7 @@ class Stream: public Print {
|
||||
virtual int peek() = 0;
|
||||
|
||||
Stream() {}
|
||||
virtual ~Stream() {}
|
||||
|
||||
// parsing methods
|
||||
|
||||
@@ -162,27 +163,27 @@ class Stream: public Print {
|
||||
// ::send*() methods:
|
||||
// - always stop before timeout when "no-more-input-possible-data"
|
||||
// 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()
|
||||
// contains an error reason.
|
||||
|
||||
// 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 sendAvailable(&to); }
|
||||
|
||||
// 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 sendAll(&to, timeoutMs); }
|
||||
|
||||
// transfers data until a char is encountered (the char is swallowed but not transfered) with timeout
|
||||
// returns number of transfered bytes
|
||||
// transfers data until a char is encountered (the char is swallowed but not transferred) with timeout
|
||||
// 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 sendUntil(&to, readUntilChar, timeoutMs); }
|
||||
|
||||
// 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 sendSize(&to, maxLen, timeoutMs); }
|
||||
|
||||
|
@@ -160,6 +160,7 @@ protected:
|
||||
size_t _peekPointer = 0;
|
||||
|
||||
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 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)) { }
|
||||
|
@@ -19,13 +19,10 @@
|
||||
parsing functions based on TextFinder library by Michael Margolis
|
||||
*/
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <StreamDev.h>
|
||||
|
||||
size_t Stream::sendGeneric(Print* to,
|
||||
const ssize_t len,
|
||||
const int readUntilChar,
|
||||
size_t Stream::sendGeneric(Print* to, const ssize_t len, const int readUntilChar,
|
||||
const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||
{
|
||||
setReport(Report::Success);
|
||||
@@ -57,11 +54,14 @@ size_t Stream::sendGeneric(Print* to,
|
||||
return SendGenericRegular(to, len, timeoutMs);
|
||||
}
|
||||
|
||||
|
||||
size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs)
|
||||
size_t
|
||||
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
|
||||
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
|
||||
const size_t maxLen = std::max((ssize_t)0, len);
|
||||
size_t written = 0;
|
||||
@@ -145,13 +145,17 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea
|
||||
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
|
||||
// no other choice than reading byte by byte
|
||||
|
||||
// "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
|
||||
const size_t maxLen = std::max((ssize_t)0, len);
|
||||
size_t written = 0;
|
||||
@@ -221,13 +225,16 @@ size_t Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int r
|
||||
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
|
||||
// use an intermediary buffer
|
||||
|
||||
// "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
|
||||
const size_t maxLen = std::max((ssize_t)0, len);
|
||||
size_t written = 0;
|
||||
@@ -341,7 +348,7 @@ Stream& operator << (Stream& out, Stream& stream)
|
||||
|
||||
Stream& operator<<(Stream& out, const char* text)
|
||||
{
|
||||
StreamConstPtr(text).sendAll(out);
|
||||
StreamConstPtr(text, strlen_P(text)).sendAll(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@@ -24,6 +24,8 @@
|
||||
#define __STREAMSTRING_H
|
||||
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include "Stream.h"
|
||||
#include "WString.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
@@ -33,16 +35,9 @@
|
||||
class S2Stream: public Stream
|
||||
{
|
||||
public:
|
||||
S2Stream(String& string, int peekPointer = -1) : string(&string), peekPointer(peekPointer) { }
|
||||
|
||||
S2Stream(String& string, int peekPointer = -1):
|
||||
string(&string), peekPointer(peekPointer)
|
||||
{
|
||||
}
|
||||
|
||||
S2Stream(String* string, int peekPointer = -1):
|
||||
string(string), peekPointer(peekPointer)
|
||||
{
|
||||
}
|
||||
S2Stream(String* string, int peekPointer = -1) : string(string), peekPointer(peekPointer) { }
|
||||
|
||||
virtual int available() override
|
||||
{
|
||||
@@ -205,18 +200,15 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
String* string;
|
||||
int peekPointer; // -1:String is consumed / >=0:resettable pointer
|
||||
};
|
||||
|
||||
|
||||
// StreamString is a S2Stream holding the String
|
||||
|
||||
class StreamString: public String, public S2Stream
|
||||
{
|
||||
protected:
|
||||
|
||||
void resetpp()
|
||||
{
|
||||
if (peekPointer > 0)
|
||||
@@ -226,11 +218,10 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
StreamString(StreamString&& bro) : String(bro), S2Stream(this) { }
|
||||
StreamString(const StreamString& bro) : String(bro), S2Stream(this) { }
|
||||
|
||||
// duplicate String contructors and operator=:
|
||||
// duplicate String constructors and operator=:
|
||||
|
||||
StreamString(const char* text = nullptr) : String(text), S2Stream(this) { }
|
||||
StreamString(const String& string) : String(string), S2Stream(this) { }
|
||||
@@ -238,13 +229,27 @@ public:
|
||||
StreamString(String&& string) : String(string), S2Stream(this) { }
|
||||
|
||||
explicit StreamString(char c) : String(c), S2Stream(this) { }
|
||||
explicit StreamString(unsigned char c, unsigned char base = 10): String(c, base), S2Stream(this) { }
|
||||
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(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(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)
|
||||
{
|
||||
|
@@ -1,7 +1,7 @@
|
||||
|
||||
// autogenerated from https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv
|
||||
// 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
|
||||
// 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_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_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_Panama PSTR("EST5")
|
||||
#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_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_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_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")
|
||||
|
@@ -24,8 +24,7 @@ extern "C" {
|
||||
#include "user_interface.h"
|
||||
}
|
||||
|
||||
extern "C" uint32_t _FS_start;
|
||||
extern "C" uint32_t _FS_end;
|
||||
#include <flash_hal.h> // not "flash_hal.h": can use hijacked MOCK version
|
||||
|
||||
UpdaterClass::UpdaterClass()
|
||||
{
|
||||
@@ -118,7 +117,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
|
||||
if (command == U_FLASH) {
|
||||
//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;
|
||||
|
||||
@@ -135,14 +134,14 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
}
|
||||
}
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ATOMIC_FS_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;
|
||||
|
||||
@@ -151,7 +150,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
updateStartAddress = (uintptr_t)&_FS_start - 0x40200000;
|
||||
updateStartAddress = FS_start - 0x40200000;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
@@ -224,19 +223,29 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
_size = progress();
|
||||
}
|
||||
|
||||
uint32_t sigLen = 0;
|
||||
if (_verify) {
|
||||
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
|
||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen: %d\n"), sigLen);
|
||||
#endif
|
||||
if (sigLen != _verify->length()) {
|
||||
if (sigLen != expectedSigLen) {
|
||||
_setError(UPDATE_ERROR_SIGN);
|
||||
_reset();
|
||||
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();
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted binsize: %d\n"), binSize);
|
||||
@@ -255,7 +264,10 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
for (int i=0; i<_hash->len(); i++) DEBUG_UPDATER.printf(" %02x", ret[i]);
|
||||
DEBUG_UPDATER.printf("\n");
|
||||
#endif
|
||||
uint8_t *sig = (uint8_t*)malloc(sigLen);
|
||||
|
||||
uint8_t *sig = nullptr; // Safe to free if we don't actually malloc
|
||||
if (expectedSigLen > 0) {
|
||||
sig = (uint8_t*)malloc(sigLen);
|
||||
if (!sig) {
|
||||
_setError(UPDATE_ERROR_SIGN);
|
||||
_reset();
|
||||
@@ -269,6 +281,7 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
}
|
||||
DEBUG_UPDATER.printf("\n");
|
||||
#endif
|
||||
}
|
||||
if (!_verify->verify(_hash, (void *)sig, sigLen)) {
|
||||
free(sig);
|
||||
_setError(UPDATE_ERROR_SIGN);
|
||||
@@ -314,7 +327,7 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
||||
eboot_command ebcmd;
|
||||
ebcmd.action = ACTION_COPY_RAW;
|
||||
ebcmd.args[0] = _startAddress;
|
||||
ebcmd.args[1] = (uintptr_t)&_FS_start - 0x40200000;
|
||||
ebcmd.args[1] = FS_start - 0x40200000;
|
||||
ebcmd.args[2] = _size;
|
||||
eboot_command_write(&ebcmd);
|
||||
#endif
|
||||
@@ -410,7 +423,7 @@ size_t UpdaterClass::write(uint8_t *data, size_t len) {
|
||||
left -= toBuff;
|
||||
if(!_async) yield();
|
||||
}
|
||||
//lets see whats left
|
||||
//lets see what's left
|
||||
memcpy(_buffer + _bufferLen, data + (len - left), left);
|
||||
_bufferLen += left;
|
||||
if(_bufferLen == remaining()){
|
||||
@@ -460,6 +473,9 @@ bool UpdaterClass::_verifyEnd() {
|
||||
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);
|
||||
|
||||
// check if new bin fits to SPI flash
|
||||
@@ -468,6 +484,7 @@ bool UpdaterClass::_verifyEnd() {
|
||||
_setError(UPDATE_ERROR_NEW_FLASH_CONFIG);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
} else if(_command == U_FS) {
|
||||
|
@@ -108,12 +108,12 @@ class UpdaterClass {
|
||||
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(); }
|
||||
|
||||
/*
|
||||
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); }
|
||||
|
||||
|
@@ -23,6 +23,8 @@
|
||||
$Id$
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
extern "C" {
|
||||
#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;
|
||||
}
|
||||
|
||||
unsigned int makeWord(unsigned int w) {
|
||||
uint16_t makeWord(uint16_t w) {
|
||||
return w;
|
||||
}
|
||||
|
||||
unsigned int makeWord(unsigned char h, unsigned char l) {
|
||||
uint16_t makeWord(byte h, byte l) {
|
||||
return (h << 8) | l;
|
||||
}
|
||||
|
@@ -25,12 +25,96 @@
|
||||
#include "WString.h"
|
||||
#include "stdlib_noniso.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#define OOM_STRING_BORDER_DISPLAY 10
|
||||
#define OOM_STRING_THRESHOLD_REALLOC_WARN 128
|
||||
|
||||
#define __STRHELPER(x) #x
|
||||
#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 */
|
||||
/*********************************************/
|
||||
@@ -56,86 +140,41 @@ String::String(String &&rval) noexcept {
|
||||
move(rval);
|
||||
}
|
||||
|
||||
String::String(unsigned char value, unsigned char base) {
|
||||
init();
|
||||
char buf[1 + 8 * sizeof(unsigned char)];
|
||||
utoa(value, buf, base);
|
||||
*this = buf;
|
||||
}
|
||||
String::String(unsigned char value, unsigned char base) :
|
||||
String(toString(value, base))
|
||||
{}
|
||||
|
||||
String::String(int value, unsigned char base) {
|
||||
init();
|
||||
char buf[2 + 8 * sizeof(int)];
|
||||
if (base == 10) {
|
||||
sprintf(buf, "%d", value);
|
||||
} else {
|
||||
itoa(value, buf, base);
|
||||
}
|
||||
*this = buf;
|
||||
}
|
||||
String::String(int value, unsigned char base) :
|
||||
String(toString(value, base))
|
||||
{}
|
||||
|
||||
String::String(unsigned int value, unsigned char base) {
|
||||
init();
|
||||
char buf[1 + 8 * sizeof(unsigned int)];
|
||||
utoa(value, buf, base);
|
||||
*this = buf;
|
||||
}
|
||||
String::String(unsigned int value, unsigned char base) :
|
||||
String(toString(value, base))
|
||||
{}
|
||||
|
||||
String::String(long value, unsigned char base) {
|
||||
init();
|
||||
char buf[2 + 8 * sizeof(long)];
|
||||
if (base == 10) {
|
||||
sprintf(buf, "%ld", value);
|
||||
} else {
|
||||
ltoa(value, buf, base);
|
||||
}
|
||||
*this = buf;
|
||||
}
|
||||
String::String(long value, unsigned char base) :
|
||||
String(toString(value, base))
|
||||
{}
|
||||
|
||||
String::String(unsigned long value, unsigned char base) {
|
||||
init();
|
||||
char buf[1 + 8 * sizeof(unsigned long)];
|
||||
ultoa(value, buf, base);
|
||||
*this = buf;
|
||||
}
|
||||
String::String(unsigned long value, unsigned char base) :
|
||||
String(toString(value, base))
|
||||
{}
|
||||
|
||||
String::String(long long value) {
|
||||
init();
|
||||
char buf[2 + 8 * sizeof(long long)];
|
||||
sprintf(buf, "%lld", value);
|
||||
*this = buf;
|
||||
}
|
||||
String::String(long long value, unsigned char base) :
|
||||
String(toString(value, base))
|
||||
{}
|
||||
|
||||
String::String(unsigned long long value) {
|
||||
init();
|
||||
char buf[1 + 8 * sizeof(unsigned long long)];
|
||||
sprintf(buf, "%llu", value);
|
||||
*this = buf;
|
||||
}
|
||||
String::String(unsigned long long value, unsigned char base) :
|
||||
String(toString(value, base))
|
||||
{}
|
||||
|
||||
String::String(long long value, unsigned char base) {
|
||||
init();
|
||||
char buf[2 + 8 * sizeof(long long)];
|
||||
*this = lltoa(value, buf, sizeof(buf), base);
|
||||
}
|
||||
String::String(float value, unsigned char decimalPlaces) :
|
||||
String(toString(value, decimalPlaces))
|
||||
{}
|
||||
|
||||
String::String(unsigned long long value, unsigned char base) {
|
||||
init();
|
||||
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);
|
||||
}
|
||||
String::String(double value, unsigned char decimalPlaces) :
|
||||
String(toString(value, decimalPlaces))
|
||||
{}
|
||||
|
||||
/*********************************************/
|
||||
/* Memory Management */
|
||||
@@ -147,18 +186,18 @@ void String::invalidate(void) {
|
||||
init();
|
||||
}
|
||||
|
||||
unsigned char String::reserve(unsigned int size) {
|
||||
bool String::reserve(unsigned int size) {
|
||||
if (buffer() && capacity() >= size)
|
||||
return 1;
|
||||
return true;
|
||||
if (changeBuffer(size)) {
|
||||
if (len() == 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?
|
||||
if (maxStrLen < sizeof(sso.buff) - 1) {
|
||||
if (isSSO() || !buffer()) {
|
||||
@@ -175,21 +214,21 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
|
||||
memcpy(wbuffer(), temp, maxStrLen);
|
||||
free((void *)temp);
|
||||
}
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
// Fallthrough to normal allocator
|
||||
size_t newSize = (maxStrLen + 16) & (~0xf);
|
||||
#ifdef DEBUG_ESP_OOM
|
||||
if (!isSSO() && capacity() >= OOM_STRING_THRESHOLD_REALLOC_WARN && maxStrLen > capacity()) {
|
||||
// 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() > OOM_STRING_BORDER_DISPLAY? c_str() + std::max((int)len() - OOM_STRING_BORDER_DISPLAY, OOM_STRING_BORDER_DISPLAY): "");
|
||||
}
|
||||
#endif
|
||||
// Make sure we can fit newsize in the buffer
|
||||
if (newSize > CAPACITY_MAX) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
uint16_t oldLen = len();
|
||||
char *newbuffer = (char *)realloc(isSSO() ? nullptr : wbuffer(), newSize);
|
||||
@@ -206,9 +245,9 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
|
||||
setCapacity(newSize - 1);
|
||||
setLen(oldLen); // Needed in case of SSO where len() never existed
|
||||
setBuffer(newbuffer);
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
@@ -273,115 +312,108 @@ String &String::operator =(const __FlashStringHelper *pstr) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
String &String::operator =(char c) {
|
||||
char buffer[2] { c, '\0' };
|
||||
*this = buffer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* 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
|
||||
// realloc'ing the buffer and moving s.buffer in the method called
|
||||
if (&s == this) {
|
||||
unsigned int newlen = 2 * len();
|
||||
if (!s.buffer())
|
||||
return 0;
|
||||
return false;
|
||||
if (s.len() == 0)
|
||||
return 1;
|
||||
return true;
|
||||
if (!reserve(newlen))
|
||||
return 0;
|
||||
return false;
|
||||
memmove_P(wbuffer() + len(), buffer(), len());
|
||||
setLen(newlen);
|
||||
wbuffer()[newlen] = 0;
|
||||
return 1;
|
||||
return true;
|
||||
} else {
|
||||
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;
|
||||
if (!cstr)
|
||||
return 0;
|
||||
return false;
|
||||
if (length == 0)
|
||||
return 1;
|
||||
return true;
|
||||
if (!reserve(newlen))
|
||||
return 0;
|
||||
memmove_P(wbuffer() + len(), cstr, length + 1);
|
||||
return false;
|
||||
memmove_P(wbuffer() + len(), cstr, length);
|
||||
setLen(newlen);
|
||||
wbuffer()[newlen] = 0;
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char String::concat(const char *cstr) {
|
||||
bool String::concat(const char *cstr) {
|
||||
if (!cstr)
|
||||
return 0;
|
||||
return false;
|
||||
return concat(cstr, strlen(cstr));
|
||||
}
|
||||
|
||||
unsigned char String::concat(char c) {
|
||||
bool String::concat(char c) {
|
||||
return concat(&c, 1);
|
||||
}
|
||||
|
||||
unsigned char String::concat(unsigned char num) {
|
||||
char buf[1 + 3 * sizeof(unsigned char)];
|
||||
return concat(buf, sprintf(buf, "%d", num));
|
||||
bool String::concat(unsigned char num) {
|
||||
return concat(String(num));
|
||||
}
|
||||
|
||||
unsigned char String::concat(int num) {
|
||||
char buf[2 + 3 * sizeof(int)];
|
||||
return concat(buf, sprintf(buf, "%d", num));
|
||||
bool String::concat(int num) {
|
||||
return concat(String(num));
|
||||
}
|
||||
|
||||
unsigned char String::concat(unsigned int num) {
|
||||
char buf[1 + 3 * sizeof(unsigned int)];
|
||||
utoa(num, buf, 10);
|
||||
return concat(buf, strlen(buf));
|
||||
bool String::concat(unsigned int num) {
|
||||
return concat(String(num));
|
||||
}
|
||||
|
||||
unsigned char String::concat(long num) {
|
||||
char buf[2 + 3 * sizeof(long)];
|
||||
return concat(buf, sprintf(buf, "%ld", num));
|
||||
bool String::concat(long num) {
|
||||
return concat(String(num));
|
||||
}
|
||||
|
||||
unsigned char String::concat(unsigned long num) {
|
||||
char buf[1 + 3 * sizeof(unsigned long)];
|
||||
ultoa(num, buf, 10);
|
||||
return concat(buf, strlen(buf));
|
||||
bool String::concat(unsigned long num) {
|
||||
return concat(String(num));
|
||||
}
|
||||
|
||||
unsigned char String::concat(long long num) {
|
||||
char buf[2 + 3 * sizeof(long long)];
|
||||
return concat(buf, sprintf(buf, "%lld", num));
|
||||
bool String::concat(long long num) {
|
||||
return concat(String(num));
|
||||
}
|
||||
|
||||
unsigned char String::concat(unsigned long long num) {
|
||||
char buf[1 + 3 * sizeof(unsigned long long)];
|
||||
return concat(buf, sprintf(buf, "%llu", num));
|
||||
bool String::concat(unsigned long long num) {
|
||||
return concat(String(num));
|
||||
}
|
||||
|
||||
unsigned char String::concat(float num) {
|
||||
char buf[20];
|
||||
char *string = dtostrf(num, 4, 2, buf);
|
||||
return concat(string, strlen(string));
|
||||
bool String::concat(float num) {
|
||||
return concat(String(num));
|
||||
}
|
||||
|
||||
unsigned char String::concat(double num) {
|
||||
char buf[20];
|
||||
char *string = dtostrf(num, 4, 2, buf);
|
||||
return concat(string, strlen(string));
|
||||
bool String::concat(double num) {
|
||||
return concat(String(num));
|
||||
}
|
||||
|
||||
unsigned char String::concat(const __FlashStringHelper *str) {
|
||||
bool String::concat(const __FlashStringHelper *str) {
|
||||
if (!str)
|
||||
return 0;
|
||||
return false;
|
||||
int length = strlen_P((PGM_P)str);
|
||||
if (length == 0)
|
||||
return 1;
|
||||
return true;
|
||||
unsigned int newlen = len() + length;
|
||||
if (!reserve(newlen))
|
||||
return 0;
|
||||
return false;
|
||||
memcpy_P(wbuffer() + len(), (PGM_P)str, length + 1);
|
||||
setLen(newlen);
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
@@ -488,11 +520,11 @@ int String::compareTo(const String &s) const {
|
||||
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);
|
||||
}
|
||||
|
||||
unsigned char String::equals(const char *cstr) const {
|
||||
bool String::equals(const char *cstr) const {
|
||||
if (len() == 0)
|
||||
return (cstr == NULL || *cstr == 0);
|
||||
if (cstr == NULL)
|
||||
@@ -500,36 +532,44 @@ unsigned char String::equals(const char *cstr) const {
|
||||
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;
|
||||
}
|
||||
|
||||
unsigned char String::operator>(const String &rhs) const {
|
||||
bool String::operator>(const String &rhs) const {
|
||||
return compareTo(rhs) > 0;
|
||||
}
|
||||
|
||||
unsigned char String::operator<=(const String &rhs) const {
|
||||
bool String::operator<=(const String &rhs) const {
|
||||
return compareTo(rhs) <= 0;
|
||||
}
|
||||
|
||||
unsigned char String::operator>=(const String &rhs) const {
|
||||
bool String::operator>=(const String &rhs) const {
|
||||
return compareTo(rhs) >= 0;
|
||||
}
|
||||
|
||||
unsigned char String::equalsIgnoreCase(const String &s2) const {
|
||||
bool String::equalsIgnoreCase(const String &s2) const {
|
||||
if (this == &s2)
|
||||
return 1;
|
||||
return true;
|
||||
if (len() != s2.len())
|
||||
return 0;
|
||||
return false;
|
||||
if (len() == 0)
|
||||
return 1;
|
||||
return true;
|
||||
const char *p1 = buffer();
|
||||
const char *p2 = s2.buffer();
|
||||
while (*p1) {
|
||||
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 {
|
||||
@@ -559,24 +599,43 @@ unsigned char String::equalsConstantTime(const String &s2) const {
|
||||
return (equalcond & diffcond); //bitwise AND
|
||||
}
|
||||
|
||||
unsigned char String::startsWith(const String &s2) const {
|
||||
bool String::startsWith(const String &s2) const {
|
||||
if (len() < s2.len())
|
||||
return 0;
|
||||
return false;
|
||||
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())
|
||||
return 0;
|
||||
return false;
|
||||
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())
|
||||
return 0;
|
||||
return false;
|
||||
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 */
|
||||
/*********************************************/
|
||||
@@ -597,7 +656,7 @@ char &String::operator[](unsigned int index) {
|
||||
|
||||
char String::operator[](unsigned int index) const {
|
||||
if (index >= len() || !buffer())
|
||||
return 0;
|
||||
return '\0';
|
||||
return buffer()[index];
|
||||
}
|
||||
|
||||
@@ -648,14 +707,9 @@ int String::lastIndexOf(char ch) const {
|
||||
int String::lastIndexOf(char ch, unsigned int fromIndex) const {
|
||||
if (fromIndex >= len())
|
||||
return -1;
|
||||
char *writeTo = wbuffer();
|
||||
char tempchar = writeTo[fromIndex + 1]; // save the replaced character
|
||||
writeTo[fromIndex + 1] = '\0';
|
||||
char *temp = strrchr(writeTo, ch);
|
||||
writeTo[fromIndex + 1] = tempchar; // restore character
|
||||
if (temp == NULL)
|
||||
return -1;
|
||||
return temp - writeTo;
|
||||
int index = fromIndex + 1;
|
||||
while (index-- > 0 && buffer()[index] != ch);
|
||||
return index;
|
||||
}
|
||||
|
||||
int String::lastIndexOf(const String &s2) const {
|
||||
@@ -678,6 +732,15 @@ int String::lastIndexOf(const String &s2, unsigned int fromIndex) const {
|
||||
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 {
|
||||
if (left > right) {
|
||||
unsigned int temp = right;
|
||||
@@ -689,11 +752,7 @@ String String::substring(unsigned int left, unsigned int right) const {
|
||||
return out;
|
||||
if (right > len())
|
||||
right = len();
|
||||
char *writeTo = wbuffer();
|
||||
char tempchar = writeTo[right]; // save the replaced character
|
||||
writeTo[right] = '\0';
|
||||
out = writeTo + left; // pointer arithmetic
|
||||
writeTo[right] = tempchar; // restore character
|
||||
out.concat(buffer() + left, right - left);
|
||||
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) {
|
||||
if (index >= len()) {
|
||||
return;
|
||||
|
@@ -45,13 +45,6 @@ class StringSumHelper;
|
||||
|
||||
// The string class
|
||||
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:
|
||||
// constructors
|
||||
// creates a copy of the initial value.
|
||||
@@ -65,23 +58,59 @@ class String {
|
||||
String(const String &str);
|
||||
String(const __FlashStringHelper *str);
|
||||
String(String &&rval) noexcept;
|
||||
|
||||
explicit String(char c) {
|
||||
sso.buff[0] = c;
|
||||
sso.buff[1] = 0;
|
||||
sso.len = 1;
|
||||
sso.isHeap = 0;
|
||||
}
|
||||
explicit String(unsigned char, unsigned char base = 10);
|
||||
explicit String(int, unsigned char base = 10);
|
||||
explicit String(unsigned int, unsigned char base = 10);
|
||||
explicit String(long, unsigned char base = 10);
|
||||
explicit String(unsigned long, unsigned char base = 10);
|
||||
explicit String(long long /* base 10 */);
|
||||
explicit String(long long, unsigned char base);
|
||||
explicit String(unsigned long long /* base 10 */);
|
||||
explicit String(unsigned long long, unsigned char base);
|
||||
explicit String(float, unsigned char decimalPlaces = 2);
|
||||
explicit String(double, unsigned char decimalPlaces = 2);
|
||||
|
||||
String(unsigned char, unsigned char base);
|
||||
explicit String(unsigned char value) :
|
||||
String(value, 10)
|
||||
{}
|
||||
|
||||
String(int, unsigned char base);
|
||||
explicit String(int value) :
|
||||
String(value, 10)
|
||||
{}
|
||||
|
||||
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() {
|
||||
invalidate();
|
||||
}
|
||||
@@ -90,7 +119,7 @@ class String {
|
||||
// return true on success, false on failure (in which case, the string
|
||||
// is left unchanged). reserve(0), if successful, will validate an
|
||||
// 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 {
|
||||
return buffer() ? len() : 0;
|
||||
}
|
||||
@@ -101,38 +130,78 @@ class String {
|
||||
return length() == 0;
|
||||
}
|
||||
|
||||
// creates a copy of the assigned value. if the value is null or
|
||||
// invalid, or if the memory allocation fails, the string will be
|
||||
// marked as invalid ("if (s)" will be false).
|
||||
// assign string types as well as built-in numeric types
|
||||
String &operator =(const String &rhs);
|
||||
String &operator =(String &&rval) noexcept;
|
||||
String &operator =(const char *cstr);
|
||||
String &operator =(const __FlashStringHelper *str);
|
||||
String &operator =(String &&rval) noexcept;
|
||||
String &operator =(char c) {
|
||||
char buffer[2] { c, '\0' };
|
||||
*this = buffer;
|
||||
String &operator =(char c);
|
||||
|
||||
String &operator =(unsigned char value) {
|
||||
*this = String(value);
|
||||
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
|
||||
// is left unchanged). if the argument is null or invalid, the
|
||||
// concatenation is considered unsuccessful.
|
||||
unsigned char concat(const String &str);
|
||||
unsigned char concat(const char *cstr);
|
||||
unsigned char concat(char c);
|
||||
unsigned char concat(unsigned char c);
|
||||
unsigned char concat(int num);
|
||||
unsigned char concat(unsigned int num);
|
||||
unsigned char concat(long num);
|
||||
unsigned char concat(unsigned long num);
|
||||
unsigned char concat(long long num);
|
||||
unsigned char concat(unsigned long long num);
|
||||
unsigned char concat(float num);
|
||||
unsigned char concat(double num);
|
||||
unsigned char concat(const __FlashStringHelper *str);
|
||||
unsigned char concat(const char *cstr, unsigned int length);
|
||||
bool concat(const String &str);
|
||||
bool concat(const char *cstr);
|
||||
bool concat(const char *cstr, unsigned int length);
|
||||
bool concat(const __FlashStringHelper *str);
|
||||
bool concat(char c);
|
||||
|
||||
bool concat(unsigned char c);
|
||||
bool concat(int num);
|
||||
bool concat(unsigned int num);
|
||||
bool concat(long num);
|
||||
bool concat(unsigned long num);
|
||||
bool concat(long long num);
|
||||
bool concat(unsigned long long num);
|
||||
bool concat(float num);
|
||||
bool concat(double num);
|
||||
|
||||
// if there's not enough memory for the concatenated value, the string
|
||||
// will be left unchanged (but this isn't signalled in any way)
|
||||
@@ -142,46 +211,43 @@ class String {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// comparison (only works w/ Strings and "strings")
|
||||
operator StringIfHelperType() const {
|
||||
return buffer() ? &String::StringIfHelper : 0;
|
||||
// checks whether the internal buffer pointer is set.
|
||||
// (should not be the case for us, since we always reset the pointer to the SSO buffer instead of setting it to nullptr)
|
||||
explicit operator bool() const {
|
||||
return buffer() != nullptr;
|
||||
}
|
||||
|
||||
int compareTo(const String &s) const;
|
||||
unsigned char equals(const String &s) const;
|
||||
unsigned char equals(const char *cstr) const;
|
||||
unsigned char operator ==(const String &rhs) const {
|
||||
bool equals(const String &s) const;
|
||||
bool equals(const char *cstr) const;
|
||||
bool equals(const __FlashStringHelper *s) const;
|
||||
bool operator ==(const String &rhs) const {
|
||||
return equals(rhs);
|
||||
}
|
||||
unsigned char operator ==(const char *cstr) const {
|
||||
bool operator ==(const char *cstr) const {
|
||||
return equals(cstr);
|
||||
}
|
||||
unsigned char operator !=(const String &rhs) const {
|
||||
bool operator !=(const String &rhs) const {
|
||||
return !equals(rhs);
|
||||
}
|
||||
unsigned char operator !=(const char *cstr) const {
|
||||
bool operator !=(const char *cstr) const {
|
||||
return !equals(cstr);
|
||||
}
|
||||
unsigned char operator <(const String &rhs) const;
|
||||
unsigned char operator >(const String &rhs) const;
|
||||
unsigned char operator <=(const String &rhs) const;
|
||||
unsigned char operator >=(const String &rhs) const;
|
||||
unsigned char equalsIgnoreCase(const String &s) const;
|
||||
bool operator <(const String &rhs) const;
|
||||
bool operator >(const String &rhs) const;
|
||||
bool operator <=(const String &rhs) const;
|
||||
bool operator >=(const String &rhs) const;
|
||||
bool equalsIgnoreCase(const String &s) const;
|
||||
bool equalsIgnoreCase(const __FlashStringHelper *s) const;
|
||||
unsigned char equalsConstantTime(const String &s) const;
|
||||
unsigned char startsWith(const String &prefix) const;
|
||||
unsigned char startsWith(const char *prefix) const {
|
||||
return this->startsWith(String(prefix));
|
||||
}
|
||||
unsigned char startsWith(const __FlashStringHelper *prefix) const {
|
||||
return this->startsWith(String(prefix));
|
||||
}
|
||||
unsigned char startsWith(const String &prefix, unsigned int offset) 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));
|
||||
}
|
||||
bool startsWith(const String &prefix) const;
|
||||
bool startsWith(const char *prefix) const;
|
||||
bool startsWith(const __FlashStringHelper *prefix) const;
|
||||
bool startsWith(const String &prefix, unsigned int offset) const;
|
||||
bool startsWith(const __FlashStringHelper *prefix, unsigned int offset) const;
|
||||
bool endsWith(const String &suffix) const;
|
||||
bool endsWith(const char *suffix) const;
|
||||
bool endsWith(const __FlashStringHelper *suffix) const;
|
||||
|
||||
// character access
|
||||
char charAt(unsigned int index) const {
|
||||
@@ -211,6 +277,8 @@ class String {
|
||||
int lastIndexOf(char ch, unsigned int fromIndex) const;
|
||||
int lastIndexOf(const String &str) 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 {
|
||||
return substring(beginIndex, len());
|
||||
}
|
||||
@@ -219,21 +287,12 @@ class String {
|
||||
// modification
|
||||
void replace(char find, char replace);
|
||||
void replace(const String &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) {
|
||||
this->replace(String(find), 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));
|
||||
}
|
||||
void replace(const char *find, const String &replace);
|
||||
void replace(const __FlashStringHelper *find, const String &replace);
|
||||
void replace(const char *find, const char *replace);
|
||||
void replace(const __FlashStringHelper *find, const char *replace);
|
||||
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace);
|
||||
|
||||
// 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.
|
||||
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 setBuffer(char *buff) { if (!isSSO()) ptr.buff = buff; }
|
||||
// Buffer accessor functions
|
||||
const char *buffer() const { return wbuffer(); }
|
||||
char *wbuffer() const { return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff; } // Writable version of buffer
|
||||
const char *buffer() const { return isSSO() ? sso.buff : ptr.buff; }
|
||||
char *wbuffer() { return const_cast<char *>(buffer()); } // Writable version of buffer
|
||||
|
||||
// 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
|
||||
@@ -292,6 +351,8 @@ class String {
|
||||
friend String operator +(const __FlashStringHelper *lhs, String &&rhs);
|
||||
|
||||
protected:
|
||||
// TODO: replace init() with a union constructor, so it's called implicitly
|
||||
|
||||
void init(void) __attribute__((always_inline)) {
|
||||
sso.buff[0] = 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,
|
||||
// `always_inline` attribute is necessary in order to keep inlining.
|
||||
}
|
||||
|
||||
// resets the string storage to the initial state
|
||||
void invalidate(void);
|
||||
unsigned char changeBuffer(unsigned int maxStrLen);
|
||||
bool changeBuffer(unsigned int maxStrLen);
|
||||
|
||||
// copy or insert at a specific position
|
||||
String ©(const char *cstr, unsigned int length);
|
||||
|
@@ -21,9 +21,9 @@
|
||||
.section .irom0.text
|
||||
.align 4
|
||||
.literal_position
|
||||
.global cont_yield
|
||||
.type cont_yield, @function
|
||||
cont_yield:
|
||||
.global cont_suspend
|
||||
.type cont_suspend, @function
|
||||
cont_suspend:
|
||||
/* a1: sp */
|
||||
/* a2: void* cont_ctx */
|
||||
/* adjust stack and save registers */
|
||||
@@ -35,10 +35,10 @@ cont_yield:
|
||||
s32i a0, a1, 16
|
||||
s32i a2, a1, 20
|
||||
|
||||
/* &cont_continue -> cont_ctx.pc_yield */
|
||||
/* &cont_continue -> cont_ctx.pc_suspend */
|
||||
movi a3, cont_continue
|
||||
s32i a3, a2, 8
|
||||
/* sp -> cont_ctx.sp_yield */
|
||||
/* sp -> cont_ctx.sp_suspend */
|
||||
s32i a1, a2, 12
|
||||
|
||||
/* a0 <- cont_ctx.pc_ret */
|
||||
@@ -56,7 +56,7 @@ cont_continue:
|
||||
l32i a2, a1, 20
|
||||
addi a1, a1, 24
|
||||
ret
|
||||
.size cont_yield, . - cont_yield
|
||||
.size cont_suspend, . - cont_suspend
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
@@ -108,7 +108,7 @@ cont_run:
|
||||
/* sp -> cont_ctx.sp_ret */
|
||||
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
|
||||
bnez a4, cont_resume
|
||||
/* else */
|
||||
@@ -119,12 +119,12 @@ cont_run:
|
||||
jx a2
|
||||
|
||||
cont_resume:
|
||||
/* a1 <- cont_ctx.sp_yield */
|
||||
/* a1 <- cont_ctx.sp_suspend */
|
||||
l32i a1, a2, 12
|
||||
/* reset yield flag, 0 -> cont_ctx.pc_yield */
|
||||
/* reset yield flag, 0 -> cont_ctx.pc_suspend */
|
||||
movi a3, 0
|
||||
s32i a3, a2, 8
|
||||
/* jump to saved cont_ctx.pc_yield */
|
||||
/* jump to saved cont_ctx.pc_suspend */
|
||||
movi a0, cont_ret
|
||||
jx a4
|
||||
|
||||
|
@@ -35,8 +35,8 @@ typedef struct cont_ {
|
||||
void (*pc_ret)(void);
|
||||
unsigned* sp_ret;
|
||||
|
||||
void (*pc_yield)(void);
|
||||
unsigned* sp_yield;
|
||||
void (*pc_suspend)(void);
|
||||
unsigned* sp_suspend;
|
||||
|
||||
unsigned* stack_end;
|
||||
unsigned unused1;
|
||||
@@ -55,12 +55,12 @@ extern cont_t* g_pcont;
|
||||
void cont_init(cont_t*);
|
||||
|
||||
// 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));
|
||||
|
||||
// Return to the point where cont_run was called, saving the
|
||||
// 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,
|
||||
// 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)
|
||||
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
|
||||
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
|
||||
// routines' stack usages to be calculated by re-painting, checking current
|
||||
|
@@ -62,9 +62,9 @@ int cont_get_free_stack(cont_t* cont) {
|
||||
return freeWords * 4;
|
||||
}
|
||||
|
||||
bool IRAM_ATTR cont_can_yield(cont_t* cont) {
|
||||
bool IRAM_ATTR cont_can_suspend(cont_t* cont) {
|
||||
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
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <c_types.h>
|
||||
#include "cont.h"
|
||||
#include "coredecls.h"
|
||||
#include <umm_malloc/umm_malloc.h>
|
||||
|
||||
void disable_extra4k_at_link_time (void)
|
||||
{
|
||||
@@ -38,6 +39,19 @@ extern "C" void app_entry_redefinable(void)
|
||||
{
|
||||
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_user_start();
|
||||
}
|
||||
|
@@ -483,7 +483,7 @@ void i2s_set_dividers(uint8_t div1, uint8_t div2) {
|
||||
i2sc_temp |= (I2STXR); // Hold transmitter in reset
|
||||
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));
|
||||
|
||||
// I2SRF = Send/recv right channel first (? may be swapped form I2S spec of WS=0 => left)
|
||||
|
@@ -18,8 +18,8 @@
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef I2S_h
|
||||
#define I2S_h
|
||||
#ifndef CORE_ESP8266_I2S_H
|
||||
#define CORE_ESP8266_I2S_H
|
||||
|
||||
#define I2S_HAS_BEGIN_RXTX_DRIVE_CLOCKS 1
|
||||
|
||||
|
@@ -62,14 +62,14 @@ cont_t* g_pcont __attribute__((section(".noinit")));
|
||||
static os_event_t s_loop_queue[LOOP_QUEUE_SIZE];
|
||||
|
||||
/* 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
|
||||
* Max nesting seen by SDK so far is 2.
|
||||
*/
|
||||
#define ETS_INTR_LOCK_NEST_MAX 7
|
||||
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" {
|
||||
@@ -80,6 +80,10 @@ const char* core_release =
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
|
||||
static os_timer_t delay_timer;
|
||||
#define ONCE 0
|
||||
#define REPEAT 1
|
||||
} // extern "C"
|
||||
|
||||
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" 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 void esp_yield_within_cont() {
|
||||
cont_yield(g_pcont);
|
||||
s_cycles_at_yield_start = ESP.getCycleCount();
|
||||
static inline void esp_suspend_within_cont() __attribute__((always_inline));
|
||||
static void esp_suspend_within_cont() {
|
||||
cont_suspend(g_pcont);
|
||||
s_cycles_at_resume = ESP.getCycleCount();
|
||||
run_scheduled_recurrent_functions();
|
||||
}
|
||||
|
||||
extern "C" void __esp_yield() {
|
||||
if (can_yield()) {
|
||||
esp_yield_within_cont();
|
||||
extern "C" void __esp_suspend() {
|
||||
if (cont_can_suspend(g_pcont)) {
|
||||
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() {
|
||||
ets_post(LOOP_TASK_PRIORITY, 0, 0);
|
||||
}
|
||||
|
||||
extern "C" void __yield() {
|
||||
if (can_yield()) {
|
||||
// Replacement for delay(0). In CONT, same as yield(). Whereas yield() panics
|
||||
// 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_yield_within_cont();
|
||||
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_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 {
|
||||
panic();
|
||||
@@ -140,6 +183,9 @@ extern "C" void __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) {
|
||||
const uint32_t intvl_cycles = interval_us *
|
||||
#if defined(F_CPU)
|
||||
@@ -147,7 +193,7 @@ extern "C" void optimistic_yield(uint32_t interval_us) {
|
||||
#else
|
||||
ESP.getCpuFreqMHz();
|
||||
#endif
|
||||
if ((ESP.getCycleCount() - s_cycles_at_yield_start) > intvl_cycles &&
|
||||
if ((ESP.getCycleCount() - s_cycles_at_resume) > intvl_cycles &&
|
||||
can_yield())
|
||||
{
|
||||
yield();
|
||||
@@ -207,7 +253,7 @@ static void loop_wrapper() {
|
||||
|
||||
static void loop_task(os_event_t *events) {
|
||||
(void) events;
|
||||
s_cycles_at_yield_start = ESP.getCycleCount();
|
||||
s_cycles_at_resume = ESP.getCycleCount();
|
||||
ESP.resetHeap();
|
||||
cont_run(g_pcont, &loop_wrapper);
|
||||
ESP.setDramHeap();
|
||||
@@ -215,8 +261,8 @@ static void loop_task(os_event_t *events) {
|
||||
panic();
|
||||
}
|
||||
}
|
||||
extern "C" {
|
||||
|
||||
extern "C" {
|
||||
struct object { long placeholder[ 10 ]; };
|
||||
void __register_frame_info (const void *begin, struct object *ob);
|
||||
extern char __eh_frame[];
|
||||
@@ -253,7 +299,6 @@ static void __unhandled_exception_cpp()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
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)));
|
||||
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_user_start();
|
||||
}
|
||||
@@ -325,7 +378,6 @@ static void app_entry_custom (void) __attribute__((weakref("app_entry_redefinabl
|
||||
|
||||
extern "C" void app_entry (void)
|
||||
{
|
||||
umm_init();
|
||||
return app_entry_custom();
|
||||
}
|
||||
|
||||
@@ -346,6 +398,12 @@ extern "C" void __disableWiFiAtBootTime (void)
|
||||
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) {
|
||||
struct rst_info *rtc_info_ptr = system_get_rst_info();
|
||||
memcpy((void *) &resetInfo, (void *) rtc_info_ptr, sizeof(resetInfo));
|
||||
@@ -374,6 +432,9 @@ extern "C" void user_init(void) {
|
||||
|
||||
#if defined(MMU_IRAM_HEAP)
|
||||
umm_init_iram();
|
||||
#endif
|
||||
#if FLASH_MAP_SUPPORT
|
||||
flashinit();
|
||||
#endif
|
||||
preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable.
|
||||
__disableWiFiAtBootTime(); // default weak function disables WiFi
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
core_esp8266_noniso.c - nonstandard (but usefull) conversion functions
|
||||
core_esp8266_noniso.c - nonstandard (but useful) conversion functions
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
@@ -289,7 +289,7 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] =
|
||||
};
|
||||
|
||||
|
||||
// These functions will be overriden from C++ code.
|
||||
// These functions will be overridden from C++ code.
|
||||
// Unfortunately, we can't use extern "C" because Arduino preprocessor
|
||||
// doesn't generate forward declarations for extern "C" functions correctly,
|
||||
// so we use mangled names here.
|
||||
|
@@ -245,12 +245,12 @@ static void print_stack(uint32_t start, uint32_t end) {
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_write_char_d(char c) {
|
||||
static void IRAM_ATTR uart_write_char_d(char c) {
|
||||
uart0_write_char_d(c);
|
||||
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)) { }
|
||||
|
||||
if (c == '\n') {
|
||||
@@ -259,7 +259,7 @@ static void uart0_write_char_d(char 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) { }
|
||||
|
||||
if (c == '\n') {
|
||||
|
@@ -24,9 +24,8 @@
|
||||
#include "wiring_private.h"
|
||||
#include "PolledTimeout.h"
|
||||
|
||||
|
||||
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#include "twi_util.h"
|
||||
#include "ets_sys.h"
|
||||
};
|
||||
@@ -57,8 +56,8 @@ static inline __attribute__((always_inline)) bool SCL_READ(const int twi_scl)
|
||||
return (GPI & (1 << twi_scl)) != 0;
|
||||
}
|
||||
|
||||
|
||||
// Implement as a class to reduce code size by allowing access to many global variables with a single base pointer
|
||||
// Implement as a class to reduce code size by allowing access to many global variables with a
|
||||
// single base pointer
|
||||
class Twi
|
||||
{
|
||||
private:
|
||||
@@ -69,12 +68,37 @@ private:
|
||||
unsigned char twi_addr = 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
|
||||
// issues about RmW on packed bytes. The int-wide variations of asm instructions are smaller than the equivalent
|
||||
// byte-wide ones, and since these emums are used everywhere, the difference adds up fast. There is only a single
|
||||
// instance of the class, though, so the extra 12 bytes of RAM used here saves a lot more IRAM.
|
||||
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;
|
||||
// These are int-wide, even though they could all fit in a byte, to reduce code size and avoid
|
||||
// any potential issues about RmW on packed bytes. The int-wide variations of asm instructions
|
||||
// are smaller than the equivalent byte-wide ones, and since these emums are used everywhere,
|
||||
// the difference adds up fast. There is only a single instance of the class, though, so the
|
||||
// extra 12 bytes of RAM used here saves a lot more IRAM.
|
||||
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 bitCount = 0;
|
||||
|
||||
@@ -97,8 +121,17 @@ private:
|
||||
void (*twi_onSlaveReceive)(uint8_t*, size_t);
|
||||
|
||||
// ETS queue/timer interfaces
|
||||
enum { EVENTTASK_QUEUE_SIZE = 1, EVENTTASK_QUEUE_PRIO = 2 };
|
||||
enum { TWI_SIG_RANGE = 0x00000100, TWI_SIG_RX = 0x00000101, TWI_SIG_TX = 0x00000102 };
|
||||
enum
|
||||
{
|
||||
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];
|
||||
ETSTimer timer;
|
||||
|
||||
@@ -126,7 +159,8 @@ private:
|
||||
{
|
||||
esp8266::polledTimeout::oneShotFastUs timeout(twi_clockStretchLimit);
|
||||
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
|
||||
{
|
||||
@@ -143,8 +177,10 @@ public:
|
||||
void setClockStretchLimit(uint32_t limit);
|
||||
void init(unsigned char sda, unsigned char scl);
|
||||
void setAddress(uint8_t address);
|
||||
unsigned char writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop);
|
||||
unsigned char readFrom(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 sendStop);
|
||||
unsigned char readFrom(unsigned char address, unsigned char* buf, unsigned int len,
|
||||
unsigned char sendStop);
|
||||
uint8_t status();
|
||||
uint8_t transmit(const uint8_t* data, uint8_t length);
|
||||
void attachSlaveRxEvent(void (*function)(uint8_t*, size_t));
|
||||
@@ -176,7 +212,8 @@ void Twi::setClock(unsigned int freq)
|
||||
freq = 400000;
|
||||
}
|
||||
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
|
||||
|
||||
@@ -185,7 +222,8 @@ void Twi::setClock(unsigned int freq)
|
||||
freq = 800000;
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -195,8 +233,6 @@ void Twi::setClockStretchLimit(uint32_t limit)
|
||||
twi_clockStretchLimit = limit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Twi::init(unsigned char sda, unsigned char scl)
|
||||
{
|
||||
// set timer function
|
||||
@@ -234,7 +270,8 @@ void IRAM_ATTR Twi::busywait(unsigned int v)
|
||||
unsigned int i;
|
||||
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;
|
||||
}
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -260,6 +302,7 @@ bool Twi::write_stop(void)
|
||||
SCL_HIGH(twi_scl);
|
||||
WAIT_CLOCK_STRETCH();
|
||||
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);
|
||||
busywait(twi_dcount);
|
||||
return true;
|
||||
@@ -318,7 +361,8 @@ unsigned char Twi::read_byte(bool nack)
|
||||
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;
|
||||
if (!write_start())
|
||||
@@ -363,7 +407,8 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned
|
||||
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;
|
||||
if (!write_start())
|
||||
@@ -415,21 +460,25 @@ uint8_t Twi::status()
|
||||
WAIT_CLOCK_STRETCH(); // wait for a slow slave to finish
|
||||
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;
|
||||
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();
|
||||
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))
|
||||
{
|
||||
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;
|
||||
@@ -471,8 +520,9 @@ void Twi::attachSlaveTxEvent(void (*function)(void))
|
||||
twi_onSlaveTransmit = function;
|
||||
}
|
||||
|
||||
// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function breakup into
|
||||
// parts and the IRAM_ATTR isn't propagated correctly to all parts, which of course causes crashes.
|
||||
// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function
|
||||
// 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
|
||||
void IRAM_ATTR Twi::reply(uint8_t ack)
|
||||
{
|
||||
@@ -491,7 +541,6 @@ void IRAM_ATTR Twi::reply(uint8_t ack)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IRAM_ATTR Twi::releaseBus(void)
|
||||
{
|
||||
// release bus
|
||||
@@ -504,7 +553,6 @@ void IRAM_ATTR Twi::releaseBus(void)
|
||||
twi_state = TWI_READY;
|
||||
}
|
||||
|
||||
|
||||
void IRAM_ATTR Twi::onTwipEvent(uint8_t status)
|
||||
{
|
||||
twip_status = status;
|
||||
@@ -623,7 +671,6 @@ void IRAM_ATTR Twi::onTimer(void *unused)
|
||||
|
||||
void Twi::eventTask(ETSEvent* e)
|
||||
{
|
||||
|
||||
if (e == NULL)
|
||||
{
|
||||
return;
|
||||
@@ -886,7 +933,9 @@ void IRAM_ATTR Twi::onSdaChange(void)
|
||||
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
|
||||
SDA_HIGH(twi.twi_sda); // Should not be necessary
|
||||
@@ -956,8 +1005,8 @@ void IRAM_ATTR Twi::onSdaChange(void)
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
return twi.init(sda, scl);
|
||||
@@ -978,12 +1027,14 @@ extern "C" {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1022,5 +1073,4 @@ extern "C" {
|
||||
{
|
||||
twi.enableSlave();
|
||||
}
|
||||
|
||||
};
|
||||
|
@@ -56,7 +56,7 @@ _SPICommand(volatile uint32_t spiIfNum,
|
||||
if (spiIfNum>1)
|
||||
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.
|
||||
uint32_t *spibase = (uint32_t*)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD));
|
||||
#define SPIREG(reg) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD)))))
|
||||
@@ -141,7 +141,7 @@ _SPICommand(volatile uint32_t spiIfNum,
|
||||
* data has been sent.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits) {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
core_esp8266_version.h - parse "git describe" at compile time
|
||||
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
|
||||
int coreVersionNumeric ()
|
||||
|
@@ -71,6 +71,8 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
#define VM_OFFSET_MASK 0x007fffffu
|
||||
|
||||
#define SHORT_MASK 0x000008u
|
||||
#define LOAD_MASK 0x00f00fu
|
||||
#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 what = insn & STORE_MASK;
|
||||
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) {
|
||||
spi_ramwrite(spi1, excvaddr & 0x1ffff, 16-1, val);
|
||||
spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 16-1, val);
|
||||
} else {
|
||||
spi_ramwrite(spi1, excvaddr & 0x1ffff, 32-1, val);
|
||||
spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 32-1, val);
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
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))
|
||||
ef->a_reg[regno] |= 0xffff0000;
|
||||
} 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;
|
||||
}
|
||||
|
||||
// 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
|
||||
umm_init_vm( (void *)0x10000000, MMU_EXTERNAL_HEAP * 1024);
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ extern "C" void enablePhaseLockedWaveform (void)
|
||||
|
||||
// No-op calls to override the PWM implementation
|
||||
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; }
|
||||
|
||||
|
||||
@@ -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);
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
|
@@ -40,6 +40,7 @@
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <coredecls.h>
|
||||
#include "ets_sys.h"
|
||||
#include "core_esp8266_waveform.h"
|
||||
#include "user_interface.h"
|
||||
@@ -162,7 +163,7 @@ static IRAM_ATTR void _notifyPWM(PWMState *p, bool idle) {
|
||||
forceTimerInterrupt();
|
||||
while (pwmState.pwmUpdate) {
|
||||
if (idle) {
|
||||
delay(0);
|
||||
esp_yield();
|
||||
}
|
||||
MEMBARRIER();
|
||||
}
|
||||
@@ -260,7 +261,7 @@ IRAM_ATTR bool _stopPWM_weak(uint8_t pin) {
|
||||
return true;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -372,8 +373,8 @@ int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t
|
||||
if (wvfState.waveformEnabled & mask) {
|
||||
// Make sure no waveform changes are waiting to be applied
|
||||
while (wvfState.waveformToChange) {
|
||||
delay(0); // Wait for waveform to update
|
||||
// No mem barrier here, the call to a global function implies global state updated
|
||||
esp_yield(); // Wait for waveform to update
|
||||
MEMBARRIER();
|
||||
}
|
||||
wvfState.waveformNewHigh = timeHighCycles;
|
||||
wvfState.waveformNewLow = timeLowCycles;
|
||||
@@ -392,8 +393,8 @@ int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t
|
||||
initTimer();
|
||||
forceTimerInterrupt();
|
||||
while (wvfState.waveformToEnable) {
|
||||
delay(0); // Wait for waveform to update
|
||||
// No mem barrier here, the call to a global function implies global state updated
|
||||
esp_yield(); // Wait for waveform to update
|
||||
MEMBARRIER();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,37 +23,18 @@
|
||||
#include "ets_sys.h"
|
||||
#include "osapi.h"
|
||||
#include "user_interface.h"
|
||||
#include "cont.h"
|
||||
#include "coredecls.h"
|
||||
|
||||
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 uint32_t micros_at_last_overflow_tick = 0;
|
||||
static uint32_t micros_overflow_count = 0;
|
||||
#define ONCE 0
|
||||
#define REPEAT 1
|
||||
|
||||
void delay_end(void* arg) {
|
||||
(void) arg;
|
||||
esp_schedule();
|
||||
}
|
||||
|
||||
void __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_yield();
|
||||
if(ms) {
|
||||
os_timer_disarm(&delay_timer);
|
||||
}
|
||||
esp_delay(ms);
|
||||
}
|
||||
|
||||
void delay(unsigned long ms) __attribute__ ((weak, alias("__delay")));
|
||||
@@ -69,8 +50,8 @@ void micros_overflow_tick(void* arg) {
|
||||
//---------------------------------------------------------------------------
|
||||
// millis() 'magic multiplier' approximation
|
||||
//
|
||||
// This function corrects the cumlative (296us / usec overflow) drift
|
||||
// seen in the orignal 'millis()' function.
|
||||
// This function corrects the cumulative (296us / usec overflow) drift
|
||||
// seen in the original 'millis()' function.
|
||||
//
|
||||
// Input:
|
||||
// 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF
|
||||
|
@@ -6,6 +6,18 @@
|
||||
#define ARDUINO_ESP8266_GIT_DESC unspecified
|
||||
#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 used in the core internally. Please use ESP.getCoreVersion() function instead.
|
||||
|
||||
|
@@ -2,6 +2,8 @@
|
||||
#ifndef __COREDECLS_H
|
||||
#define __COREDECLS_H
|
||||
|
||||
#include "core_esp8266_features.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -13,10 +15,13 @@ extern "C" {
|
||||
#include <cont.h> // g_pcont declaration
|
||||
|
||||
bool can_yield();
|
||||
void esp_yield();
|
||||
void esp_suspend();
|
||||
void esp_delay(unsigned long ms);
|
||||
void esp_schedule();
|
||||
void esp_yield();
|
||||
void tune_timeshift64 (uint64_t now_us);
|
||||
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);
|
||||
void __disableWiFiAtBootTime (void) __attribute__((noinline));
|
||||
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 TrivialCB = std::function<void()>;
|
||||
|
||||
void settimeofday_cb (BoolCB&& cb);
|
||||
void settimeofday_cb (const BoolCB& 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 // __COREDECLS_H
|
||||
|
@@ -54,7 +54,7 @@ void hexdump(const void *mem, uint32_t len, uint8_t cols)
|
||||
}
|
||||
src += linesize;
|
||||
len -= linesize;
|
||||
yield();
|
||||
optimistic_yield(10000);
|
||||
}
|
||||
os_printf("\n");
|
||||
}
|
||||
|
@@ -9,7 +9,11 @@
|
||||
#endif
|
||||
|
||||
#ifndef DEBUGV
|
||||
#define DEBUGV(...) do { (void)0; } while (0)
|
||||
#define DEBUGV(...) \
|
||||
do \
|
||||
{ \
|
||||
(void)0; \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -19,7 +23,8 @@ void hexdump(const void *mem, uint32_t len, uint8_t cols);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
void __unhandled_exception(const char* str) __attribute__((noreturn));
|
||||
@@ -29,20 +34,25 @@ void __panic_func(const char* file, int line, const char* func) __attribute__((n
|
||||
#ifdef DEBUG_ESP_CORE
|
||||
extern void __iamslow(const char* what);
|
||||
#define IAMSLOW() \
|
||||
do { \
|
||||
do \
|
||||
{ \
|
||||
static bool once = false; \
|
||||
if (!once) { \
|
||||
if (!once) \
|
||||
{ \
|
||||
once = true; \
|
||||
__iamslow((PGM_P)FPSTR(__FUNCTION__)); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define IAMSLOW() do { (void)0; } while (0)
|
||||
#define IAMSLOW() \
|
||||
do \
|
||||
{ \
|
||||
(void)0; \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // ARD_DEBUG_H
|
||||
|
@@ -163,7 +163,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16];
|
||||
#define TCIS 8 //Interrupt Status
|
||||
#define TCTE 7 //Timer Enable
|
||||
#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
|
||||
|
||||
//RTC Registers
|
||||
@@ -255,7 +255,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16];
|
||||
//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 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 USRXD 15 //RX 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 UCTXRST 18 //Reset TX 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 UCBRK 8 //Send Break on the TX line
|
||||
#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
|
||||
#define UCTOE 31 //RX TimeOut Enable
|
||||
#define UCTOT 24 //RX TimeOut Treshold (7bit)
|
||||
#define UCRXHFE 23 //RX Harware Flow Enable
|
||||
#define UCRXHFT 16 //RX Harware Flow Treshold (7bit)
|
||||
#define UCFET 8 //TX FIFO Empty Treshold (7bit)
|
||||
#define UCFFT 0 //RX FIFO Full Treshold (7bit)
|
||||
#define UCTOT 24 //RX TimeOut Threshold (7bit)
|
||||
#define UCRXHFE 23 //RX Hardware Flow Enable
|
||||
#define UCRXHFT 16 //RX Hardware Flow Threshold (7bit)
|
||||
#define UCFET 8 //TX FIFO Empty Threshold (7bit)
|
||||
#define UCFFT 0 //RX FIFO Full Threshold (7bit)
|
||||
|
||||
//WDT Feed (the dog) Register
|
||||
#define WDTFEED ESP8266_REG(0x914)
|
||||
@@ -372,7 +372,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16];
|
||||
#define SPI1E3 ESP8266_REG(0x1FC)
|
||||
#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 SPII0 4 //SPI0 Interrupt
|
||||
#define SPII1 7 //SPI1 Interrupt
|
||||
|
@@ -52,12 +52,22 @@ calls to ets_install_putc1().
|
||||
*/
|
||||
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
|
||||
uart_buff_switch(). Supported format options are the same as vprintf(). Also
|
||||
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
|
||||
controlled by uart_buff_switch().
|
||||
It also uses a static function in ROM to print characters. The UART selection
|
||||
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)));
|
||||
|
||||
@@ -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
|
||||
registered. It simply consist of a single instruction, `ret`.
|
||||
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`.
|
||||
Note, if nesting handlers is desired this must be implemented in the new "C"
|
||||
exception handler(s) being registered.
|
||||
@@ -236,7 +246,6 @@ extern void Cache_Read_Disable();
|
||||
extern int32_t system_func1(uint32_t);
|
||||
extern void clockgate_watchdog(uint32_t);
|
||||
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 int boot_from_flash();
|
||||
extern void ets_run() __attribute__((noreturn));
|
||||
|
@@ -47,7 +47,7 @@
|
||||
* https://github.com/qca/open-ath9k-htc-firmware/blob/master/sboot/magpie_1_1/sboot/athos/src/xtos/exc-sethandler.c
|
||||
*
|
||||
* It has been revised to use Arduino ESP8266 core includes, types, and
|
||||
* formating.
|
||||
* formatting.
|
||||
*/
|
||||
|
||||
/* exc-sethandler.c - register an exception handler in XTOS */
|
||||
|
@@ -68,3 +68,13 @@ int32_t flash_hal_erase(uint32_t addr, uint32_t size) {
|
||||
}
|
||||
return FLASH_HAL_OK;
|
||||
}
|
||||
|
||||
#if FLASH_MAP_SUPPORT
|
||||
|
||||
// default weak configuration:
|
||||
FLASH_MAP_SETUP_CONFIG_ATTR(__attribute__((weak)), FLASH_MAP_OTA_FS)
|
||||
|
||||
// can be overridden by user with:
|
||||
//FLASH_MAP_SETUP_CONFIG(FLASH_MAP_some_configuration)
|
||||
|
||||
#endif
|
||||
|
@@ -24,18 +24,58 @@
|
||||
License along with this library; if not, write to the Free Software
|
||||
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)
|
||||
#define FS_PHYS_SIZE ((uint32_t) (&_FS_end) - (uint32_t) (&_FS_start))
|
||||
#define FS_PHYS_PAGE ((uint32_t) &_FS_page)
|
||||
#define FS_PHYS_BLOCK ((uint32_t) &_FS_block)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#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
|
||||
#define FLASH_HAL_OK (0)
|
||||
#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_read(uint32_t addr, uint32_t size, uint8_t *dst);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // !defined(flash_hal_h)
|
||||
|
@@ -27,7 +27,7 @@ extern "C" {
|
||||
* @brief Initialize GDB stub, if present
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void gdb_init(void);
|
||||
@@ -36,7 +36,7 @@ void gdb_init(void);
|
||||
* @brief Break into GDB, if present
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void gdb_do_break(void);
|
||||
@@ -45,7 +45,7 @@ void gdb_do_break(void);
|
||||
* @brief Check if GDB stub is present.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @return true if GDB stub is present
|
||||
@@ -58,7 +58,7 @@ bool gdb_present(void);
|
||||
* @brief Check if GDB is installing a putc1 callback.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
@@ -69,7 +69,7 @@ bool gdbstub_has_putc1_control(void);
|
||||
* @param func function GDB will proxy putc1 data to
|
||||
*
|
||||
* 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,
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
@@ -90,7 +90,7 @@ bool gdbstub_has_uart_isr_control(void);
|
||||
* @param func function GDB will proxy uart0 isr data to
|
||||
*
|
||||
* 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,
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void gdbstub_write_char(char c);
|
||||
@@ -112,7 +112,7 @@ void gdbstub_write_char(char c);
|
||||
* @param size length of buffer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void gdbstub_write(const char* buf, size_t size);
|
||||
|
@@ -5,6 +5,13 @@
|
||||
|
||||
#include <stdlib.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
|
||||
#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_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.
|
||||
#define STATIC_ALWAYS_INLINE static ALWAYS_INLINE
|
||||
#endif
|
||||
@@ -153,7 +160,7 @@ void* _calloc_r(struct _reent* unused, size_t count, size_t size)
|
||||
{
|
||||
(void) unused;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -172,7 +179,7 @@ void IRAM_ATTR print_loc(size_t size, const char* file, int line)
|
||||
DEBUG_HEAP_PRINTF(":oom(%d)@", (int)size);
|
||||
|
||||
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);
|
||||
} else if (!inISR && (uint32_t)file >= 0x40200000) {
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
*/
|
||||
void* IRAM_ATTR malloc(size_t size)
|
||||
@@ -247,8 +254,11 @@ void* IRAM_ATTR calloc(size_t count, size_t size)
|
||||
INTEGRITY_CHECK__ABORT();
|
||||
POISON_CHECK__ABORT();
|
||||
void* ret = UMM_CALLOC(count, size);
|
||||
PTR_CHECK__LOG_LAST_FAIL(ret, count * size);
|
||||
OOM_CHECK__PRINT_OOM(ret, size);
|
||||
#if defined(DEBUG_ESP_OOM)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
POISON_CHECK__PANIC_FL(file, line);
|
||||
void* ret = UMM_CALLOC(count, size);
|
||||
PTR_CHECK__LOG_LAST_FAIL_FL(ret, count * size, file, line);
|
||||
OOM_CHECK__PRINT_LOC(ret, size, file, line);
|
||||
#if defined(DEBUG_ESP_OOM)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -275,6 +275,7 @@
|
||||
#include <esp8266_peri.h>
|
||||
#include <uart.h>
|
||||
#include <pgmspace.h>
|
||||
#include "mmu_iram.h"
|
||||
|
||||
extern "C" {
|
||||
#include <user_interface.h>
|
||||
@@ -376,7 +377,7 @@ extern hwdt_info_t hwdt_info;
|
||||
#undef hwdt_info_
|
||||
#undef 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
|
||||
|
||||
|
||||
@@ -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)))
|
||||
/*
|
||||
* 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.
|
||||
* 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
|
||||
* continuing.
|
||||
*
|
||||
* Load a Rubout character for the final charcter shifting out to stop
|
||||
* the last charcter from getting crunched during the speed change.
|
||||
* Load a Rubout character for the final character shifting out to stop
|
||||
* the last character from getting crunched during the speed change.
|
||||
*
|
||||
* 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
|
||||
@@ -952,13 +953,13 @@ STATIC void IRAM_MAYBE handle_hwdt(void) {
|
||||
/* Print separate ctx: cont stack */
|
||||
|
||||
/* Check if cont stack is yielding to SYS */
|
||||
if (0 == hwdt_info.cont_integrity && 0 != g_pcont->pc_yield) {
|
||||
ctx_cont_ptr = (const uint32_t *)((uintptr_t)g_pcont->sp_yield - 8u);
|
||||
if (0 == hwdt_info.cont_integrity && 0 != g_pcont->pc_suspend) {
|
||||
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);
|
||||
} else {
|
||||
if (0 == hwdt_info.cont_integrity && 0 != g_pcont->pc_yield) {
|
||||
ETS_PRINTF("\nCont stack is yielding. Active stack starts at 0x%08X.\n", (uint32_t)g_pcont->sp_yield - 8u);
|
||||
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_suspend - 8u);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1007,35 +1008,6 @@ STATIC void IRAM_MAYBE handle_hwdt(void) {
|
||||
#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)
|
||||
static void printSanityCheck() {
|
||||
ETS_PRINTF("\n\nsys_stack_first: %p\n", sys_stack_first);
|
||||
@@ -1048,6 +1020,40 @@ static void printSanityCheck() {
|
||||
}
|
||||
#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) {
|
||||
#if defined(DEBUG_ESP_HWDT_DEV_DEBUG) && !defined(USE_IRAM)
|
||||
@@ -1055,9 +1061,8 @@ void hwdt_pre_sdk_init(void) {
|
||||
#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) {
|
||||
Cache_Read_Enable(0, 0, ICACHE_SIZE_16);
|
||||
#ifdef DEBUG_ESP_HWDT_UART_SPEED
|
||||
const uint32_t uart_divisor = set_uart_speed(0, DEBUG_ESP_HWDT_UART_SPEED);
|
||||
#endif
|
||||
@@ -1069,17 +1074,14 @@ void hwdt_pre_sdk_init_icache(void) {
|
||||
adjust_uart_speed(uart_divisor);
|
||||
}
|
||||
#endif
|
||||
Cache_Read_Disable();
|
||||
}
|
||||
|
||||
|
||||
#if 1
|
||||
/*
|
||||
An asm function alternative to the function with inline asm at the #else. I
|
||||
find the inline asm requires constant inspection to verify that the compiler
|
||||
optimizer does not clobber needed registers, after small changes in code or
|
||||
compiler updates. Hints to the compiler don't always work for me. Last I
|
||||
checked, the inline version below was working.
|
||||
For app_entry_redefinable, use Basic ASM instead of "C" with Extended ASM. The
|
||||
(inline) Extended ASM approach required constant inspection to verify that the
|
||||
compiler's optimizer did not clobber needed registers or do something weird
|
||||
after minor changes in code or compiler updates. Also, I think Basic ASM is
|
||||
the safer route when changing the stack pointer multiple times.
|
||||
*/
|
||||
cont_t *hwdt_app_entry__cont_stack __attribute__((used)) = CONT_STACK;
|
||||
|
||||
@@ -1089,8 +1091,10 @@ asm (
|
||||
".literal .g_pcont, g_pcont\n\t"
|
||||
".literal .pcont_stack, hwdt_app_entry__cont_stack\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 .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"
|
||||
".global app_entry_redefinable\n\t"
|
||||
".type app_entry_redefinable, @function\n\t"
|
||||
@@ -1114,7 +1118,9 @@ asm (
|
||||
#ifdef USE_IRAM
|
||||
"call0 handle_hwdt\n\t"
|
||||
#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
|
||||
/*
|
||||
* Use new calculated SYS stack from top.
|
||||
@@ -1135,90 +1141,46 @@ asm (
|
||||
"l32r a13, .pcont_stack\n\t"
|
||||
"l32r a0, .get_noextra4k_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 a13, a13, 0\n\t"
|
||||
"callx0 a0\n\t"
|
||||
"moveqz a2, a13, a2\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 a3, 0x0b30\n\t" // ROM BSS Size
|
||||
"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"
|
||||
"movi a0, 0x4000044c\n\t"
|
||||
"jx a3\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)
|
||||
void debug_hwdt_init(void) {
|
||||
/*
|
||||
|
@@ -1,12 +0,0 @@
|
||||
// This include file is a hack to ensure backward compatibility with
|
||||
// pre 3.0.0 versions of the core. There was a *lowercase* "i2s.h"
|
||||
// header which was in this directory, now renamed to "core_esp82i66s.h"
|
||||
// But, the I2S class has a header, "I2S.h" in uppercase. On Linux
|
||||
// the two names are different, but on Windows it's case-insensitive
|
||||
// so the names conflict.
|
||||
//
|
||||
// Avoid the issue by preserving the old i2s.h file and have it redirect
|
||||
// to I2S.h which will give the ESP8266-specific functions as well as
|
||||
// the generic I2S class.
|
||||
|
||||
#include "../../libraries/I2S/src/I2S.h"
|
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// 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.
|
||||
|
||||
// InterruptLock is used when you want to completely disable interrupts
|
||||
|
@@ -19,13 +19,16 @@
|
||||
#include "mmu_iram.h"
|
||||
#include <user_interface.h>
|
||||
|
||||
#define ICACHE_SIZE_32 1
|
||||
#define ICACHE_SIZE_16 0
|
||||
|
||||
extern "C" {
|
||||
|
||||
#if (MMU_ICACHE_SIZE == 0x4000)
|
||||
#define SOC_CACHE_SIZE 0 // 16KB
|
||||
#define SOC_CACHE_SIZE ICACHE_SIZE_16
|
||||
#pragma message("ICACHE size 16K")
|
||||
#else
|
||||
#define SOC_CACHE_SIZE 1 // 32KB
|
||||
#define SOC_CACHE_SIZE ICACHE_SIZE_32
|
||||
#endif
|
||||
|
||||
#if (MMU_ICACHE_SIZE == 0x4000)
|
||||
@@ -80,7 +83,7 @@ extern "C" {
|
||||
*
|
||||
* "Cache_Read_Enable" is underdocumented. Main sources of information were from
|
||||
* 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
|
||||
* 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 );
|
||||
}
|
||||
#else // #ifdef DEV_DEBUG_PRINT
|
||||
extern void Cache_Read_Disable(void);
|
||||
#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)
|
||||
|
||||
/*
|
||||
* This wrapper is for running code from IROM (flash) before the SDK starts.
|
||||
*/
|
||||
void IRAM_ATTR mmu_wrap_irom_fn(void (*fn)(void)) {
|
||||
Cache_Read_Enable(0, 0, ICACHE_SIZE_16);
|
||||
fn();
|
||||
Cache_Read_Disable();
|
||||
}
|
||||
|
||||
};
|
||||
|
@@ -26,13 +26,24 @@
|
||||
extern "C" {
|
||||
#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
|
||||
#define DEBUG_ESP_MMU
|
||||
#endif
|
||||
|
||||
#if defined(CORE_MOCK)
|
||||
#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
|
||||
|
||||
/*
|
||||
@@ -69,34 +80,54 @@ DBG_MMU_FLUSH(0)
|
||||
#define DBG_MMU_PRINTF(...) do {} while(false)
|
||||
#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))
|
||||
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
|
||||
#if defined(__GNUC__) && !defined(CORE_MOCK)
|
||||
#warning "MMU_IRAM_SIZE was undefined, setting to 0x8000UL!"
|
||||
#endif
|
||||
#define MMU_IRAM_SIZE 0x8000UL
|
||||
#define MMU_IRAM_SIZE 0x8000ul
|
||||
#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))
|
||||
bool mmu_is_dram(const void *addr) {
|
||||
#define DRAM_START 0x3FF80000UL
|
||||
#define DRAM_END 0x40000000UL
|
||||
const uintptr_t dram_start = 0x3FFE8000ul;
|
||||
// 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))
|
||||
bool mmu_is_icache(const void *addr) {
|
||||
#define ICACHE_START 0x40200000UL
|
||||
#define ICACHE_END (ICACHE_START + 0x100000UL)
|
||||
extern void _irom0_text_end(void);
|
||||
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
|
||||
@@ -122,13 +153,31 @@ bool mmu_is_icache(const void *addr) {
|
||||
/*
|
||||
* 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
|
||||
* have occured by relying on exception processing.
|
||||
* have occurred by relying on exception processing.
|
||||
*/
|
||||
static inline __attribute__((always_inline))
|
||||
uint8_t mmu_get_uint8(const void *p8) {
|
||||
ASSERT_RANGE_TEST_READ(p8);
|
||||
uint32_t val = (*(uint32_t *)((uintptr_t)p8 & ~0x3));
|
||||
uint32_t pos = ((uintptr_t)p8 & 0x3) * 8;
|
||||
// https://gist.github.com/shafik/848ae25ee209f698763cffee272a58f8#how-do-we-type-pun-correctly
|
||||
// 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;
|
||||
return (uint8_t)val;
|
||||
}
|
||||
@@ -136,8 +185,11 @@ uint8_t mmu_get_uint8(const void *p8) {
|
||||
static inline __attribute__((always_inline))
|
||||
uint16_t mmu_get_uint16(const uint16_t *p16) {
|
||||
ASSERT_RANGE_TEST_READ(p16);
|
||||
uint32_t val = (*(uint32_t *)((uintptr_t)p16 & ~0x3));
|
||||
uint32_t pos = ((uintptr_t)p16 & 0x3) * 8;
|
||||
void *v32 = (void *)((uintptr_t)p16 & ~(uintptr_t)0x3u);
|
||||
uint32_t val;
|
||||
__builtin_memcpy(&val, v32, sizeof(uint32_t));
|
||||
asm volatile ("" :"+r"(val));
|
||||
uint32_t pos = ((uintptr_t)p16 & 3u) * 8u;
|
||||
val >>= pos;
|
||||
return (uint16_t)val;
|
||||
}
|
||||
@@ -145,8 +197,11 @@ uint16_t mmu_get_uint16(const uint16_t *p16) {
|
||||
static inline __attribute__((always_inline))
|
||||
int16_t mmu_get_int16(const int16_t *p16) {
|
||||
ASSERT_RANGE_TEST_READ(p16);
|
||||
uint32_t val = (*(uint32_t *)((uintptr_t)p16 & ~0x3));
|
||||
uint32_t pos = ((uintptr_t)p16 & 0x3) * 8;
|
||||
void *v32 = (void *)((uintptr_t)p16 & ~(uintptr_t)3u);
|
||||
uint32_t val;
|
||||
__builtin_memcpy(&val, v32, sizeof(uint32_t));
|
||||
asm volatile ("" :"+r"(val));
|
||||
uint32_t pos = ((uintptr_t)p16 & 3u) * 8u;
|
||||
val >>= pos;
|
||||
return (int16_t)val;
|
||||
}
|
||||
@@ -154,30 +209,43 @@ int16_t mmu_get_int16(const int16_t *p16) {
|
||||
static inline __attribute__((always_inline))
|
||||
uint8_t mmu_set_uint8(void *p8, const uint8_t val) {
|
||||
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 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 |= 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;
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
uint16_t mmu_set_uint16(uint16_t *p16, const uint16_t val) {
|
||||
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 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 |= sval;
|
||||
*p32 = ival;
|
||||
asm volatile ("" :"+r"(ival));
|
||||
__builtin_memcpy(v32, &ival, sizeof(uint32_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -185,32 +253,36 @@ static inline __attribute__((always_inline))
|
||||
int16_t mmu_set_int16(int16_t *p16, const int16_t val) {
|
||||
ASSERT_RANGE_TEST_WRITE(p16);
|
||||
uint32_t sval = (uint16_t)val;
|
||||
uint32_t pos = ((uintptr_t)p16 & 0x3) * 8;
|
||||
uint32_t pos = ((uintptr_t)p16 & 3u) * 8u;
|
||||
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 |= sval;
|
||||
*p32 = ival;
|
||||
asm volatile ("" :"+r"(ival));
|
||||
__builtin_memcpy(v32, &ival, sizeof(uint32_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
#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_SIZE mmu_sec_heap_size()
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
void *mmu_sec_heap(void) {
|
||||
uint32_t sec_heap = (uint32_t)_text_end + 32;
|
||||
return (void *)(sec_heap &= ~7);
|
||||
extern void _text_end(void);
|
||||
uintptr_t sec_heap = (uintptr_t)_text_end + (uintptr_t)32u;
|
||||
return (void *)(sec_heap &= ~(uintptr_t)7u);
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
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
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
// This file's contents have been moved to newlib. This file simply
|
||||
// includes the newlib pgmspace file as well as some ets headers
|
||||
// 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>
|
||||
|
||||
|
@@ -19,10 +19,10 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
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:
|
||||
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 <stdnoreturn.h>
|
||||
@@ -106,7 +106,7 @@ static inline void __wsr_vecbase(uint32_t vector_base) {
|
||||
const uint32_t uart_no = 0;
|
||||
uartAttach();
|
||||
Uart_Init(uart_no);
|
||||
ets_install_uart_printf(uart_no);
|
||||
ets_install_uart_printf();
|
||||
|
||||
/* reverse engineered from boot_from_something() */
|
||||
const uint16_t divlatch = uart_baudrate_detect(uart_no, 0);
|
||||
@@ -148,4 +148,3 @@ static inline void __wsr_vecbase(uint32_t vector_base) {
|
||||
|
||||
esp8266UartDownloadMode();
|
||||
}
|
||||
|
||||
|
@@ -113,7 +113,7 @@ Choosing the page size for the system involves many factors:
|
||||
- How fast must spiffs be
|
||||
- Other things impossible to find out
|
||||
|
||||
So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't
|
||||
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
|
||||
spiffs. Use the golden rule:
|
||||
|
||||
|
@@ -695,7 +695,7 @@ s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
|
||||
* in this callback will mess things up for sure - do not do this.
|
||||
* This can be used to track where files are and move around during garbage
|
||||
* collection, which in turn can be used to build location tables in ram.
|
||||
* Used in conjuction with SPIFFS_open_by_page this may improve performance
|
||||
* Used in conjunction with SPIFFS_open_by_page this may improve performance
|
||||
* when opening a lot of files.
|
||||
* 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.
|
||||
* This will make reading big files faster, as the memory map will be used for
|
||||
* looking up data pages instead of searching for the indices on the physical
|
||||
* medium. When mapping, all affected indicies are found and the information is
|
||||
* medium. When mapping, all affected indices are found and the information is
|
||||
* copied to the array.
|
||||
* Whole file or only parts of it may be mapped. The index map will cover file
|
||||
* contents from argument offset until and including arguments (offset+len).
|
||||
|
@@ -353,7 +353,7 @@ extern "C" {
|
||||
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)
|
||||
// report argument object id to visitor - else object lookup id is reported
|
||||
#define SPIFFS_VIS_CHECK_PH (1<<1)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
stdlib_noniso.h - nonstandard (but usefull) conversion functions
|
||||
stdlib_noniso.h - nonstandard (but useful) conversion functions
|
||||
|
||||
Copyright (c) 2021 David Gauchard. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
stdlib_noniso.h - nonstandard (but usefull) conversion functions
|
||||
stdlib_noniso.h - nonstandard (but useful) conversion functions
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
@@ -20,6 +20,28 @@
|
||||
* 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 <../include/time.h> // See issue #6714
|
||||
#include <sys/time.h>
|
||||
@@ -33,7 +55,6 @@ extern "C" {
|
||||
#include <coredecls.h>
|
||||
#include <Schedule.h>
|
||||
|
||||
#include <Arduino.h> // configTime()
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -214,6 +235,11 @@ void settimeofday_cb (const TrivialCB& 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)
|
||||
{
|
||||
_settimeofday_cb = cb;
|
||||
@@ -252,4 +278,6 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
}; // extern "C"
|
||||
|
||||
#endif // !defined(CORE_MOCK)
|
||||
|
@@ -41,6 +41,7 @@
|
||||
*
|
||||
*/
|
||||
#include "Arduino.h"
|
||||
#include "coredecls.h"
|
||||
#include <pgmspace.h>
|
||||
#include "gdb_hooks.h"
|
||||
#include "uart.h"
|
||||
@@ -493,13 +494,13 @@ uart_stop_isr(uart_t* uart)
|
||||
-tools/sdk/uart_register.h
|
||||
-cores/esp8266/esp8266_peri.h
|
||||
*/
|
||||
inline size_t
|
||||
inline __attribute__((always_inline)) size_t
|
||||
uart_tx_fifo_available(const int uart_nr)
|
||||
{
|
||||
return (USS(uart_nr) >> USTXC) & 0xff;
|
||||
}
|
||||
|
||||
inline bool
|
||||
inline __attribute__((always_inline)) bool
|
||||
uart_tx_fifo_full(const int uart_nr)
|
||||
{
|
||||
return uart_tx_fifo_available(uart_nr) >= 0x7f;
|
||||
@@ -566,7 +567,7 @@ uart_wait_tx_empty(uart_t* uart)
|
||||
return;
|
||||
|
||||
while(uart_tx_fifo_available(uart->uart_nr) > 0)
|
||||
delay(0);
|
||||
esp_yield();
|
||||
|
||||
}
|
||||
|
||||
@@ -943,23 +944,23 @@ uart_ignore_char(char c)
|
||||
(void) c;
|
||||
}
|
||||
|
||||
inline void
|
||||
inline __attribute__((always_inline)) void
|
||||
uart_write_char_delay(const int uart_nr, char c)
|
||||
{
|
||||
while(uart_tx_fifo_full(uart_nr))
|
||||
delay(0);
|
||||
esp_yield();
|
||||
|
||||
USF(uart_nr) = c;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
static void IRAM_ATTR
|
||||
uart0_write_char(char c)
|
||||
{
|
||||
uart_write_char_delay(0, c);
|
||||
}
|
||||
|
||||
static void
|
||||
static void IRAM_ATTR
|
||||
uart1_write_char(char c)
|
||||
{
|
||||
uart_write_char_delay(1, c);
|
||||
|
@@ -248,4 +248,32 @@ Enhancement ideas:
|
||||
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
|
||||
|
@@ -126,7 +126,7 @@ later.
|
||||
The result is that a block of memory on the free list uses just 8 bytes
|
||||
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.
|
||||
|
||||
The overhead of an allocated block is therefore just 4 bytes.
|
||||
@@ -205,7 +205,7 @@ described.
|
||||
allocated for use by the upper block.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
@@ -1 +1,2 @@
|
||||
Downloaded from: https://github.com/rhempel/c-helper-macros/tree/develop
|
||||
Applied uncrustify to be consistent with the rest of the umm_malloc files.
|
||||
|
@@ -13,7 +13,7 @@
|
||||
* to set the trace level #define DBGLOG_LEVEL x
|
||||
*
|
||||
* To 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
|
||||
* #define DBGLOG_LEVEL 6
|
||||
|
@@ -7,7 +7,7 @@
|
||||
#define ALWAYS_INLINE inline __attribute__ ((always_inline))
|
||||
#endif
|
||||
|
||||
// Use FORCE_ALWAYS_INLINE to ensure HeapSelect... construtor/deconstructor
|
||||
// Use FORCE_ALWAYS_INLINE to ensure HeapSelect... constructor/deconstructor
|
||||
// are placed in IRAM
|
||||
#ifdef FORCE_ALWAYS_INLINE_HEAP_SELECT
|
||||
#define MAYBE_ALWAYS_INLINE ALWAYS_INLINE
|
||||
@@ -33,9 +33,12 @@ class HeapSelect {
|
||||
public:
|
||||
#if (UMM_NUM_HEAPS == 1)
|
||||
MAYBE_ALWAYS_INLINE
|
||||
HeapSelect(size_t id) { (void)id; }
|
||||
HeapSelect(size_t id) {
|
||||
(void)id;
|
||||
}
|
||||
MAYBE_ALWAYS_INLINE
|
||||
~HeapSelect() {}
|
||||
~HeapSelect() {
|
||||
}
|
||||
#else
|
||||
MAYBE_ALWAYS_INLINE
|
||||
HeapSelect(size_t id) : _heap_id(umm_get_current_heap_id()) {
|
||||
@@ -70,9 +73,11 @@ protected:
|
||||
|
||||
#else
|
||||
MAYBE_ALWAYS_INLINE
|
||||
HeapSelectIram() {}
|
||||
HeapSelectIram() {
|
||||
}
|
||||
MAYBE_ALWAYS_INLINE
|
||||
~HeapSelectIram() {}
|
||||
~HeapSelectIram() {
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -80,9 +85,11 @@ class HeapSelectDram {
|
||||
public:
|
||||
#if (UMM_NUM_HEAPS == 1)
|
||||
MAYBE_ALWAYS_INLINE
|
||||
HeapSelectDram() {}
|
||||
HeapSelectDram() {
|
||||
}
|
||||
MAYBE_ALWAYS_INLINE
|
||||
~HeapSelectDram() {}
|
||||
~HeapSelectDram() {
|
||||
}
|
||||
#else
|
||||
MAYBE_ALWAYS_INLINE
|
||||
HeapSelectDram() : _heap_id(umm_get_current_heap_id()) {
|
||||
|
@@ -28,7 +28,7 @@
|
||||
void *umm_info(void *ptr, bool force) {
|
||||
UMM_CRITICAL_DECL(id_info);
|
||||
|
||||
UMM_INIT_HEAP;
|
||||
UMM_CHECK_INITIALIZED();
|
||||
|
||||
uint16_t blockNo = 0;
|
||||
|
||||
@@ -95,7 +95,7 @@ void *umm_info( void *ptr, bool force ) {
|
||||
/* Release the critical section... */
|
||||
UMM_CRITICAL_EXIT(id_info);
|
||||
|
||||
return( ptr );
|
||||
return ptr;
|
||||
}
|
||||
} else {
|
||||
++_context->info.usedEntries;
|
||||
@@ -165,7 +165,7 @@ void *umm_info( void *ptr, bool force ) {
|
||||
/* Release the critical section... */
|
||||
UMM_CRITICAL_EXIT(id_info);
|
||||
|
||||
return( NULL );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
@@ -197,15 +197,16 @@ size_t umm_max_block_size( void ) {
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
// 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);
|
||||
if (_context->info.freeBlocks)
|
||||
if (_context->info.freeBlocks) {
|
||||
return (int)((_context->info.usedBlocks * 100) / (_context->info.freeBlocks));
|
||||
}
|
||||
|
||||
return -1; // no freeBlocks
|
||||
}
|
||||
@@ -226,7 +227,7 @@ int umm_fragmentation_metric_core( umm_heap_context_t *_context ) {
|
||||
return 0;
|
||||
} else {
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,7 @@ bool umm_integrity_check(void) {
|
||||
uint16_t prev;
|
||||
uint16_t cur;
|
||||
|
||||
UMM_INIT_HEAP;
|
||||
UMM_CHECK_INITIALIZED();
|
||||
|
||||
/* Iterate through all free blocks */
|
||||
prev = 0;
|
||||
@@ -91,8 +91,7 @@ bool umm_integrity_check(void) {
|
||||
|
||||
/* make sure the free mark is appropriate, and unmark it */
|
||||
if ((UMM_NBLOCK(cur) & UMM_FREELIST_MASK)
|
||||
!= (UMM_PBLOCK(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),
|
||||
|
@@ -6,7 +6,7 @@
|
||||
|
||||
#if defined(UMM_CRITICAL_METRICS)
|
||||
/*
|
||||
* umm_malloc performance measurments for critical sections
|
||||
* umm_malloc performance measurements for critical sections
|
||||
*/
|
||||
UMM_TIME_STATS time_stats = {
|
||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||
@@ -21,13 +21,12 @@ UMM_TIME_STATS time_stats = {
|
||||
#ifdef UMM_INTEGRITY_CHECK
|
||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||
#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);
|
||||
if (p && sizeof(time_stats) == size)
|
||||
{
|
||||
if (p && sizeof(time_stats) == size) {
|
||||
UMM_CRITICAL_ENTRY(id_no_tag);
|
||||
memcpy(p, &time_stats, size);
|
||||
UMM_CRITICAL_EXIT(id_no_tag);
|
||||
@@ -45,8 +44,9 @@ bool ICACHE_FLASH_ATTR get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size)
|
||||
static bool check_poison_neighbors(umm_heap_context_t *_context, uint16_t cur) {
|
||||
uint16_t c;
|
||||
|
||||
if ( 0 == cur )
|
||||
if (0 == cur) {
|
||||
return true;
|
||||
}
|
||||
|
||||
c = UMM_PBLOCK(cur) & UMM_BLOCKNO_MASK;
|
||||
while (c && (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK)) {
|
||||
@@ -56,8 +56,9 @@ static bool check_poison_neighbors( umm_heap_context_t *_context, uint16_t cur )
|
||||
i.e. Adjacent free space is always consolidated.
|
||||
*/
|
||||
if (!(UMM_NBLOCK(c) & UMM_FREELIST_MASK)) {
|
||||
if ( !check_poison_block(&UMM_BLOCK(c)) )
|
||||
if (!check_poison_block(&UMM_BLOCK(c))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -68,8 +69,9 @@ static bool check_poison_neighbors( umm_heap_context_t *_context, uint16_t cur )
|
||||
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)) )
|
||||
if (!check_poison_block(&UMM_BLOCK(c))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -96,17 +98,20 @@ static void *get_unpoisoned_check_neighbors( void *vptr, const char* file, int l
|
||||
UMM_CRITICAL_DECL(id_poison);
|
||||
uint16_t c;
|
||||
bool poison = false;
|
||||
umm_heap_context_t *_context = umm_get_ptr_context( vptr );
|
||||
if (NULL == _context) {
|
||||
panic();
|
||||
return NULL;
|
||||
}
|
||||
umm_heap_context_t *_context = _umm_get_ptr_context((void *)ptr);
|
||||
if (_context) {
|
||||
|
||||
/* 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);
|
||||
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) {
|
||||
@@ -135,7 +140,7 @@ void *umm_poison_realloc_fl(void *ptr, size_t size, const char* file, int line)
|
||||
|
||||
ptr = get_unpoisoned_check_neighbors(ptr, file, line);
|
||||
|
||||
size += poison_size(size);
|
||||
add_poison_size(&size);
|
||||
ret = umm_realloc(ptr, size);
|
||||
|
||||
ret = get_poisoned(ret, size);
|
||||
@@ -164,7 +169,7 @@ size_t umm_block_size( void ) {
|
||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||
// Keep complete call path in IRAM
|
||||
size_t umm_free_heap_size_lw(void) {
|
||||
UMM_INIT_HEAP;
|
||||
UMM_CHECK_INITIALIZED();
|
||||
|
||||
umm_heap_context_t *_context = umm_get_current_heap();
|
||||
return (size_t)_context->UMM_FREE_BLOCKS * sizeof(umm_block);
|
||||
@@ -190,15 +195,15 @@ void umm_print_stats(int force) {
|
||||
umm_heap_context_t *_context = umm_get_current_heap();
|
||||
|
||||
DBGLOG_FORCE(force, "umm heap statistics:\n");
|
||||
DBGLOG_FORCE( force, " Heap ID %5u\n", _context->id);
|
||||
DBGLOG_FORCE( force, " Free Space %5u\n", _context->UMM_FREE_BLOCKS * sizeof(umm_block));
|
||||
DBGLOG_FORCE( force, " OOM Count %5u\n", _context->UMM_OOM_COUNT);
|
||||
DBGLOG_FORCE(force, " Heap ID %7u\n", _context->id);
|
||||
DBGLOG_FORCE(force, " Free Space %7u\n", _context->UMM_FREE_BLOCKS * sizeof(umm_block));
|
||||
DBGLOG_FORCE(force, " OOM Count %7u\n", _context->UMM_OOM_COUNT);
|
||||
#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 ISR %5u\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, " Low Watermark %7u\n", _context->stats.free_blocks_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 %7u\n", _context->stats.alloc_max_size);
|
||||
#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");
|
||||
}
|
||||
#endif
|
||||
@@ -294,4 +299,32 @@ size_t ICACHE_FLASH_ATTR umm_get_free_null_count( void ) {
|
||||
}
|
||||
#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
|
||||
|
@@ -15,6 +15,14 @@
|
||||
#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
|
||||
* Just for printing from umm_info() which is assumed to always be called from
|
||||
|
@@ -63,24 +63,39 @@ extern "C" {
|
||||
#define DBGLOG_LEVEL 0
|
||||
#endif
|
||||
|
||||
// Save 104 bytes by calling umm_init() early once from app_entry()
|
||||
// Some minor UMM_CRITICAL_METRICS counts will be lost through CRT0 init.
|
||||
// #define UMM_INIT_HEAP if (!umm_heap) { umm_init(); }
|
||||
#define UMM_INIT_HEAP (void)0
|
||||
|
||||
#include "dbglog/dbglog.h"
|
||||
|
||||
//C This change is new in upstream umm_malloc.I think this would have created a
|
||||
//C breaking change. Keeping the old #define method in umm_malloc_cfg.h.
|
||||
//C I don't see a simple way of making it work. We would have to run code before
|
||||
//C the SDK has run to set a value for uint32_t UMM_MALLOC_CFG_HEAP_SIZE.
|
||||
//C On the other hand, a manual call to umm_init() before anything else has had a
|
||||
//C chance to run would mean that all those calls testing to see if the heap has
|
||||
//C been initialized at every umm_malloc API could be removed.
|
||||
//C
|
||||
//C before starting the NON OS SDK
|
||||
//C extern void *UMM_MALLOC_CFG_HEAP_ADDR;
|
||||
//C extern uint32_t UMM_MALLOC_CFG_HEAP_SIZE;
|
||||
/*
|
||||
* These variables are used in upstream umm_malloc for initializing the heap.
|
||||
* Our port initialization is different and does not use them at this time.
|
||||
extern void *UMM_MALLOC_CFG_HEAP_ADDR;
|
||||
extern uint32_t UMM_MALLOC_CFG_HEAP_SIZE;
|
||||
*/
|
||||
/*
|
||||
* In our port, we leave UMM_CHECK_INITIALIZED unset. Since we initialize the
|
||||
* heap before CRT0 init has run, commonly used testing methods for heap init
|
||||
* may not work. Not using UMM_CHECK_INITIALIZED saves about 104 bytes of IRAM.
|
||||
*
|
||||
* In our configuration app_entry_redefinable() must call umm_init(), before
|
||||
* calling the SDK's app_entry_custom(). The DRAM Heap must be available before
|
||||
* the SDK starts.
|
||||
*
|
||||
* If building with UMM_CRITICAL_METRICS, some minor counts will be lost through
|
||||
* CRT0 init.
|
||||
*/
|
||||
|
||||
#if 0 // Must be zero at release
|
||||
#warning "Macro NON_NULL_CONTEXT_ASSERT() is active!"
|
||||
/*
|
||||
* Keep for future debug/maintenance of umm_malloc. Not needed in a
|
||||
* regular/debug build. Call paths that use NON_NULL_CONTEXT_ASSERT logically
|
||||
* guard against returning NULL. This macro double-checks that assumption during
|
||||
* development.
|
||||
*/
|
||||
#define NON_NULL_CONTEXT_ASSERT() assert((NULL != _context))
|
||||
#else
|
||||
#define NON_NULL_CONTEXT_ASSERT() (void)0
|
||||
#endif
|
||||
|
||||
#include "umm_local.h" // target-dependent supplemental
|
||||
|
||||
@@ -91,7 +106,6 @@ UMM_H_ATTPACKPRE typedef struct umm_ptr_t {
|
||||
uint16_t prev;
|
||||
} UMM_H_ATTPACKSUF umm_ptr;
|
||||
|
||||
|
||||
UMM_H_ATTPACKPRE typedef struct umm_block_t {
|
||||
union {
|
||||
umm_ptr used;
|
||||
@@ -212,21 +226,41 @@ int umm_get_heap_stack_index( void ) {
|
||||
* realloc or free since you may not be in the right heap to handle it.
|
||||
*
|
||||
*/
|
||||
static bool test_ptr_context( size_t which, void *ptr ) {
|
||||
static bool test_ptr_context(const size_t which, const void *const ptr) {
|
||||
return
|
||||
heap_context[which].heap &&
|
||||
ptr >= (void *)heap_context[which].heap &&
|
||||
ptr < heap_context[which].heap_end;
|
||||
}
|
||||
|
||||
static umm_heap_context_t *umm_get_ptr_context(void *ptr) {
|
||||
/*
|
||||
* Find Heap context by allocation address - may return NULL
|
||||
*/
|
||||
umm_heap_context_t *_umm_get_ptr_context(const void *const ptr) {
|
||||
for (size_t i = 0; i < UMM_NUM_HEAPS; i++) {
|
||||
if (test_ptr_context(i, ptr)) {
|
||||
return umm_get_heap_by_id(i);
|
||||
}
|
||||
}
|
||||
|
||||
panic();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find Heap context by allocation address - must either succeed or abort
|
||||
*/
|
||||
static umm_heap_context_t *umm_get_ptr_context(const void *const ptr) {
|
||||
umm_heap_context_t *const _context = _umm_get_ptr_context(ptr);
|
||||
if (_context) {
|
||||
return _context;
|
||||
}
|
||||
|
||||
[[maybe_unused]] uintptr_t sketch_ptr = (uintptr_t)ptr;
|
||||
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||
sketch_ptr += sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE;
|
||||
#endif
|
||||
DBGLOG_ERROR("\nPointer %p is not a Heap address.\n", (void *)sketch_ptr);
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -275,19 +309,59 @@ static uint16_t umm_blocks( size_t size ) {
|
||||
* When a block removed from the free list, the space used by the free
|
||||
* pointers is available for data. That's what the first calculation
|
||||
* of size is doing.
|
||||
*
|
||||
* We don't check for the special case of (size == 0) here as this needs
|
||||
* special handling in the caller depending on context. For example when we
|
||||
* realloc() a block to size 0 it should simply be freed.
|
||||
*
|
||||
* We do NOT need to check for allocating more blocks than the heap can
|
||||
* possibly hold - the allocator figures this out for us.
|
||||
*
|
||||
* There are only two cases left to consider:
|
||||
*
|
||||
* 1. (size <= body) Obviously this is just one block
|
||||
* 2. (blocks > (2^15)) This should return ((2^15)) to force a
|
||||
* failure when the allocator runs
|
||||
*
|
||||
* If the requested size is greater that 32677-2 blocks (max block index
|
||||
* minus the overhead of the top and bottom bookkeeping blocks) then we
|
||||
* will return an incorrectly truncated value when the result is cast to
|
||||
* a uint16_t.
|
||||
*/
|
||||
|
||||
if( size <= (sizeof(((umm_block *)0)->body)) )
|
||||
return( 1 );
|
||||
if (size <= (sizeof(((umm_block *)0)->body))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's for more than that, then we need to figure out the number of
|
||||
* additional whole blocks the size of an umm_block are required.
|
||||
* additional whole blocks the size of an umm_block are required, so
|
||||
* reduce the size request by the number of bytes in the body of the
|
||||
* first block.
|
||||
*/
|
||||
|
||||
size -= ( 1 + (sizeof(((umm_block *)0)->body)) );
|
||||
size -= (sizeof(((umm_block *)0)->body));
|
||||
|
||||
return( 2 + size/(sizeof(umm_block)) );
|
||||
/* NOTE WELL that we take advantage of the fact that INT16_MAX is the
|
||||
* number of blocks that we can index in 15 bits :-)
|
||||
*
|
||||
* The below expression looks wierd, but it's right. Assuming body
|
||||
* size of 4 bytes and a block size of 8 bytes:
|
||||
*
|
||||
* BYTES (BYTES-BODY) (BYTES-BODY-1)/BLOCKSIZE BLOCKS
|
||||
* 1 n/a n/a 1
|
||||
* 5 1 0 2
|
||||
* 12 8 0 2
|
||||
* 13 9 1 3
|
||||
*/
|
||||
|
||||
size_t blocks = (2 + ((size - 1) / sizeof(umm_block)));
|
||||
|
||||
if (blocks > (INT16_MAX)) {
|
||||
blocks = INT16_MAX;
|
||||
}
|
||||
|
||||
return (uint16_t)blocks;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
@@ -381,24 +455,33 @@ static uint16_t umm_assimilate_down( umm_heap_context_t *_context, uint16_t c, u
|
||||
UMM_FRAGMENTATION_METRIC_ADD(UMM_PBLOCK(c));
|
||||
}
|
||||
|
||||
return( UMM_PBLOCK(c) );
|
||||
return UMM_PBLOCK(c);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void umm_init_stage_2( umm_heap_context_t *_context ) {
|
||||
#undef ICACHE_MAYBE
|
||||
#ifdef UMM_INIT_USE_IRAM
|
||||
// umm_init(), ... stays in IRAM
|
||||
#define ICACHE_MAYBE
|
||||
#else
|
||||
// Freeup IRAM
|
||||
#define ICACHE_MAYBE ICACHE_FLASH_ATTR
|
||||
#endif
|
||||
/*
|
||||
* In this port, we split the upstream version of umm_init_heap() into two
|
||||
* parts: _umm_init_heap and umm_init_heap. Then add multiple heap support.
|
||||
*/
|
||||
static void ICACHE_MAYBE _umm_init_heap(umm_heap_context_t *_context) {
|
||||
/* setup initial blank heap structure */
|
||||
UMM_FRAGMENTATION_METRIC_INIT();
|
||||
|
||||
/* init stats.free_blocks */
|
||||
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||
#if defined(UMM_STATS_FULL)
|
||||
_context->stats.free_blocks_min =
|
||||
_context->stats.free_blocks_min = UMM_NUMBLOCKS - 2;
|
||||
_context->stats.free_blocks_isr_min = UMM_NUMBLOCKS - 2;
|
||||
#endif
|
||||
#ifndef UMM_INLINE_METRICS
|
||||
#if (defined(UMM_STATS) || defined(UMM_STATS_FULL)) && !defined(UMM_INLINE_METRICS)
|
||||
_context->stats.free_blocks = UMM_NUMBLOCKS - 2;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Set up umm_block[0], which just points to umm_block[1] */
|
||||
@@ -438,88 +521,98 @@ static void umm_init_stage_2( umm_heap_context_t *_context ) {
|
||||
UMM_PBLOCK(UMM_BLOCK_LAST) = 1;
|
||||
}
|
||||
|
||||
|
||||
void umm_init_common( size_t id, void *start_addr, size_t size, bool zero ) {
|
||||
/* Preserve internal setup */
|
||||
void ICACHE_MAYBE umm_init_heap(size_t id, void *start_addr, size_t size, bool full_init) {
|
||||
/* Check for bad values and block duplicate init attempts. */
|
||||
umm_heap_context_t *_context = umm_get_heap_by_id(id);
|
||||
if (NULL == start_addr || NULL == _context || _context->heap) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* init heap pointer and size, and memset it to 0 */
|
||||
|
||||
_context->id = id;
|
||||
_context->heap = (umm_block *)start_addr;
|
||||
_context->heap_end = (void *)((uintptr_t)start_addr + size);
|
||||
_context->numblocks = (size / sizeof(umm_block));
|
||||
|
||||
// An option for blocking the zeroing of extra heaps allows for performing
|
||||
// post-crash discovery.
|
||||
if (zero) {
|
||||
// An option for blocking the zeroing of extra heaps. This allows for
|
||||
// post-crash debugging after reboot.
|
||||
if (full_init) {
|
||||
memset(_context->heap, 0x00, size);
|
||||
#if (!defined(UMM_INLINE_METRICS) && defined(UMM_STATS)) || defined(UMM_STATS_FULL)
|
||||
memset(&_context->stats, 0x00, sizeof(_context->stats));
|
||||
#endif
|
||||
|
||||
/* Set up internal data structures */
|
||||
umm_init_stage_2(_context);
|
||||
_umm_init_heap(_context);
|
||||
}
|
||||
}
|
||||
|
||||
void umm_init( void ) {
|
||||
// if (umm_heap) {
|
||||
// return;
|
||||
// }
|
||||
void ICACHE_MAYBE umm_init(void) {
|
||||
|
||||
// We can get called before "C" runtime has run. Here we handles that
|
||||
// beginning of time initialization. As such heap_context[] must be
|
||||
// defined with attributes to prevent initialization by the "C" runtime.
|
||||
// A late "C" runtime init would destroy our work.
|
||||
|
||||
// Assume no "C" runtime zero init
|
||||
for (size_t i = 0; i < UMM_NUM_HEAPS; i++) {
|
||||
heap_context[i].heap = NULL;
|
||||
}
|
||||
memset(&heap_context[0], 0, sizeof(heap_context));
|
||||
umm_init_common( UMM_HEAP_DRAM, (void *)UMM_MALLOC_CFG_HEAP_ADDR, UMM_MALLOC_CFG_HEAP_SIZE, true );
|
||||
// umm_heap = (void *)&heap_context;
|
||||
// Note, full_init must be true for the primary heap, DRAM.
|
||||
umm_init_heap(UMM_HEAP_DRAM, (void *)UMM_MALLOC_CFG_HEAP_ADDR, UMM_MALLOC_CFG_HEAP_SIZE, true);
|
||||
|
||||
// upstream ref:
|
||||
// Initialize the heap from linker supplied values */
|
||||
// umm_init_heap(UMM_MALLOC_CFG_HEAP_ADDR, UMM_MALLOC_CFG_HEAP_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only the Internal DRAM init, needs (or maybe not) to be called from IRAM.
|
||||
* umm_init_iram and umm_init_vm are called from user_init() after the SDK has
|
||||
* inited and ICACHE has been enabled.
|
||||
*/
|
||||
#ifdef UMM_HEAP_IRAM
|
||||
void umm_init_iram_ex( void *addr, unsigned int size, bool zero ) {
|
||||
void ICACHE_FLASH_ATTR umm_init_iram_ex(void *addr, unsigned int size, bool full_init) {
|
||||
/* We need the main, internal heap set up first */
|
||||
UMM_INIT_HEAP;
|
||||
UMM_CHECK_INITIALIZED();
|
||||
|
||||
umm_init_common(UMM_HEAP_IRAM, addr, size, zero);
|
||||
umm_init_heap(UMM_HEAP_IRAM, addr, size, full_init);
|
||||
}
|
||||
|
||||
void _text_end(void);
|
||||
void umm_init_iram(void) __attribute__((weak));
|
||||
void ICACHE_FLASH_ATTR umm_init_iram(void) __attribute__((weak));
|
||||
|
||||
/*
|
||||
By using a weak link, it is possible to reduce the IRAM heap size with a
|
||||
user-supplied init function. This would allow the creation of a block of IRAM
|
||||
dedicated to a sketch and possibly used/preserved across reboots.
|
||||
*/
|
||||
void umm_init_iram(void) {
|
||||
void ICACHE_FLASH_ATTR umm_init_iram(void) {
|
||||
umm_init_iram_ex(mmu_sec_heap(), mmu_sec_heap_size(), true);
|
||||
}
|
||||
#endif // #ifdef UMM_HEAP_IRAM
|
||||
|
||||
#ifdef UMM_HEAP_EXTERNAL
|
||||
void umm_init_vm( void *vmaddr, unsigned int vmsize ) {
|
||||
void ICACHE_FLASH_ATTR umm_init_vm(void *vmaddr, unsigned int vmsize) {
|
||||
/* We need the main, internal (DRAM) heap set up first */
|
||||
UMM_INIT_HEAP;
|
||||
UMM_CHECK_INITIALIZED();
|
||||
|
||||
umm_init_common(UMM_HEAP_EXTERNAL, vmaddr, vmsize, true);
|
||||
umm_init_heap(UMM_HEAP_EXTERNAL, vmaddr, vmsize, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Must be called only from within critical sections guarded by
|
||||
* UMM_CRITICAL_ENTRY() and UMM_CRITICAL_EXIT().
|
||||
* UMM_CRITICAL_ENTRY(id) and UMM_CRITICAL_EXIT(id).
|
||||
*/
|
||||
|
||||
static void umm_free_core(umm_heap_context_t *_context, void *ptr) {
|
||||
|
||||
uint16_t c;
|
||||
|
||||
if (NULL == _context) {
|
||||
panic();
|
||||
return;
|
||||
}
|
||||
NON_NULL_CONTEXT_ASSERT();
|
||||
|
||||
STATS__FREE_REQUEST(id_free);
|
||||
/*
|
||||
@@ -574,7 +667,7 @@ static void umm_free_core( umm_heap_context_t *_context, void *ptr ) {
|
||||
void umm_free(void *ptr) {
|
||||
UMM_CRITICAL_DECL(id_free);
|
||||
|
||||
UMM_INIT_HEAP;
|
||||
UMM_CHECK_INITIALIZED();
|
||||
|
||||
/* If we're being asked to free a NULL pointer, well that's just silly! */
|
||||
|
||||
@@ -585,7 +678,7 @@ void umm_free( void *ptr ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Free the memory withing a protected critical section */
|
||||
/* Free the memory within a protected critical section */
|
||||
|
||||
UMM_CRITICAL_ENTRY(id_free);
|
||||
|
||||
@@ -609,12 +702,9 @@ static void *umm_malloc_core( umm_heap_context_t *_context, size_t size ) {
|
||||
|
||||
uint16_t cf;
|
||||
|
||||
STATS__ALLOC_REQUEST(id_malloc, size);
|
||||
NON_NULL_CONTEXT_ASSERT();
|
||||
|
||||
if (NULL == _context) {
|
||||
panic();
|
||||
return NULL;
|
||||
}
|
||||
STATS__ALLOC_REQUEST(id_malloc, size);
|
||||
|
||||
blocks = umm_blocks(size);
|
||||
|
||||
@@ -643,8 +733,9 @@ static void *umm_malloc_core( umm_heap_context_t *_context, size_t size ) {
|
||||
}
|
||||
#elif defined UMM_FIRST_FIT
|
||||
/* This is the first block that fits! */
|
||||
if( (blockSize >= blocks) )
|
||||
if ((blockSize >= blocks)) {
|
||||
break;
|
||||
}
|
||||
#else
|
||||
#error "No UMM_*_FIT is defined - check umm_malloc_cfg.h"
|
||||
#endif
|
||||
@@ -671,7 +762,7 @@ static void *umm_malloc_core( umm_heap_context_t *_context, size_t size ) {
|
||||
*/
|
||||
|
||||
if (blockSize == blocks) {
|
||||
/* It's an exact fit and we don't neet to split off a block. */
|
||||
/* It's an exact fit and we don't need to split off a block. */
|
||||
DBGLOG_DEBUG("Allocating %6d blocks starting at %6d - exact\n", blocks, cf);
|
||||
|
||||
/* Disconnect this block from the FREE list */
|
||||
@@ -715,10 +806,10 @@ static void *umm_malloc_core( umm_heap_context_t *_context, size_t size ) {
|
||||
|
||||
DBGLOG_DEBUG("Can't allocate %5d blocks\n", blocks);
|
||||
|
||||
return( (void *)NULL );
|
||||
return (void *)NULL;
|
||||
}
|
||||
|
||||
return( (void *)&UMM_DATA(cf) );
|
||||
return (void *)&UMM_DATA(cf);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
@@ -728,7 +819,7 @@ void *umm_malloc( size_t size ) {
|
||||
|
||||
void *ptr = NULL;
|
||||
|
||||
UMM_INIT_HEAP;
|
||||
UMM_CHECK_INITIALIZED();
|
||||
|
||||
/*
|
||||
* "Is it safe"
|
||||
@@ -755,7 +846,7 @@ void *umm_malloc( size_t size ) {
|
||||
* For allocating APIs `umm_heap_cur` is used to index and select a value for
|
||||
* `_context`. If an allocation is made from an ISR, this value is ignored and
|
||||
* the heap context for DRAM is loaded. For APIs that require operating on an
|
||||
* existing allcation such as realloc and free, the heap context selected is
|
||||
* existing allocation such as realloc and free, the heap context selected is
|
||||
* done by matching the allocation's address with that of one of the heap
|
||||
* address ranges.
|
||||
*
|
||||
@@ -795,10 +886,10 @@ void *umm_malloc( size_t size ) {
|
||||
DBGLOG_DEBUG("malloc a block of 0 bytes -> do nothing\n");
|
||||
STATS__ZERO_ALLOC_REQUEST(id_malloc, size);
|
||||
|
||||
return( ptr );
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* Allocate the memory withing a protected critical section */
|
||||
/* Allocate the memory within a protected critical section */
|
||||
|
||||
UMM_CRITICAL_ENTRY(id_malloc);
|
||||
|
||||
@@ -820,7 +911,7 @@ void *umm_malloc( size_t size ) {
|
||||
|
||||
UMM_CRITICAL_EXIT(id_malloc);
|
||||
|
||||
return( ptr );
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
@@ -837,7 +928,7 @@ void *umm_realloc( void *ptr, size_t size ) {
|
||||
|
||||
size_t curSize;
|
||||
|
||||
UMM_INIT_HEAP;
|
||||
UMM_CHECK_INITIALIZED();
|
||||
|
||||
/*
|
||||
* This code looks after the case of a NULL value for ptr. The ANSI C
|
||||
@@ -850,7 +941,7 @@ void *umm_realloc( void *ptr, size_t size ) {
|
||||
if (((void *)NULL == ptr)) {
|
||||
DBGLOG_DEBUG("realloc the NULL pointer - call malloc()\n");
|
||||
|
||||
return( umm_malloc(size) );
|
||||
return umm_malloc(size);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -861,10 +952,7 @@ void *umm_realloc( void *ptr, size_t size ) {
|
||||
|
||||
/* Need to be in the heap in which this block lives */
|
||||
umm_heap_context_t *_context = umm_get_ptr_context(ptr);
|
||||
if (NULL == _context) {
|
||||
panic();
|
||||
return NULL;
|
||||
}
|
||||
NON_NULL_CONTEXT_ASSERT();
|
||||
|
||||
if (0 == size) {
|
||||
DBGLOG_DEBUG("realloc to 0 size, just free the block\n");
|
||||
@@ -872,7 +960,7 @@ void *umm_realloc( void *ptr, size_t size ) {
|
||||
|
||||
umm_free(ptr);
|
||||
|
||||
return( (void *)NULL );
|
||||
return (void *)NULL;
|
||||
}
|
||||
|
||||
STATS__ALLOC_REQUEST(id_realloc, size);
|
||||
@@ -1165,7 +1253,7 @@ void *umm_realloc( void *ptr, size_t size ) {
|
||||
/* Release the critical section... */
|
||||
UMM_CRITICAL_EXIT(id_realloc);
|
||||
|
||||
return( ptr );
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
@@ -1173,10 +1261,15 @@ void *umm_realloc( void *ptr, size_t size ) {
|
||||
void *umm_calloc(size_t num, size_t item_size) {
|
||||
void *ret;
|
||||
|
||||
ret = umm_malloc((size_t)(item_size * num));
|
||||
// Use saturated multiply.
|
||||
// Rely on umm_malloc to supply the fail response as needed.
|
||||
size_t size = umm_umul_sat(num, item_size);
|
||||
|
||||
if (ret)
|
||||
memset(ret, 0x00, (size_t)(item_size * num));
|
||||
ret = umm_malloc(size);
|
||||
|
||||
if (ret) {
|
||||
memset(ret, 0x00, size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -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!
|
||||
*
|
||||
* Refer to the notes below for how to configure the build at compile time
|
||||
* using -D to define non-default values
|
||||
* NOTE WELL: Your project MUST have a umm_malloc_cfgport.h - even if
|
||||
* 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
|
||||
@@ -18,65 +20,10 @@
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <pgmspace.h>
|
||||
#include <mmu_iram.h>
|
||||
#include "../debug.h"
|
||||
#include "../esp8266_undocumented.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#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
|
||||
* the memory allocator will operate.
|
||||
@@ -116,6 +63,16 @@ extern "C" {
|
||||
* Setting this at compile time will automatically set UMM_INFO.
|
||||
* 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
|
||||
*
|
||||
* 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_INFO
|
||||
// #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 */
|
||||
|
||||
#define UMM_H_ATTPACKPRE
|
||||
@@ -191,6 +138,20 @@ 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_FIRST_FIT
|
||||
#error Both UMM_BEST_FIT and UMM_FIRST_FIT are defined - pick one!
|
||||
@@ -359,22 +320,25 @@ size_t ICACHE_FLASH_ATTR umm_block_size( void );
|
||||
#ifdef UMM_STATS_FULL
|
||||
#define STATS__FREE_BLOCKS_MIN() \
|
||||
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; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define STATS__FREE_BLOCKS_ISR_MIN() \
|
||||
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; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define STATS__ALLOC_REQUEST(tag, s) \
|
||||
do { \
|
||||
_context->stats.tag##_count += 1; \
|
||||
_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; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define STATS__ZERO_ALLOC_REQUEST(tag, s) \
|
||||
@@ -472,11 +436,13 @@ static inline void _critical_entry(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;
|
||||
if (elapse < p->min)
|
||||
if (elapse < p->min) {
|
||||
p->min = elapse;
|
||||
}
|
||||
|
||||
if (elapse > p->max)
|
||||
if (elapse > p->max) {
|
||||
p->max = elapse;
|
||||
}
|
||||
|
||||
xt_wsr_ps(*saved_ps);
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
@@ -796,7 +762,7 @@ extern "C" {
|
||||
// Arduino.h recall us to redefine them
|
||||
#include <pgmspace.h>
|
||||
// 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_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);
|
||||
@@ -813,7 +779,7 @@ void IRAM_ATTR heap_vPortFree(void *ptr, const char* file, int line);
|
||||
#define dbg_heap_free(p) free(p)
|
||||
#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>
|
||||
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__); })
|
||||
|
91
cores/esp8266/umm_malloc/umm_malloc_cfgport.h
Normal file
91
cores/esp8266/umm_malloc/umm_malloc_cfgport.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifndef _UMM_MALLOC_CFGPORT_H
|
||||
#define _UMM_MALLOC_CFGPORT_H
|
||||
|
||||
#ifndef _UMM_MALLOC_CFG_H
|
||||
#error "This include file must be used with umm_malloc_cfg.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Arduino ESP8266 core umm_malloc port config
|
||||
*/
|
||||
#include <pgmspace.h>
|
||||
#include <mmu_iram.h>
|
||||
#include "../debug.h"
|
||||
#include "../esp8266_undocumented.h"
|
||||
|
||||
#include <core_esp8266_features.h>
|
||||
#include <stdlib.h>
|
||||
#include <osapi.h>
|
||||
|
||||
#include "c_types.h"
|
||||
|
||||
/*
|
||||
* -DUMM_INIT_USE_IRAM
|
||||
*
|
||||
* Historically, the umm_init() call path has been in IRAM. The umm_init() call
|
||||
* path is now in ICACHE (flash). Use the build option UMM_INIT_USE_IRAM to
|
||||
* restore the legacy behavor.
|
||||
*
|
||||
* If you have your own app_entry_redefinable() function, see
|
||||
* app_entry_redefinable() in core_esp8266_app_entry_noextra4k.cpp for an
|
||||
* example of how to toggle between ICACHE and IRAM in your build.
|
||||
*
|
||||
* The default is to use ICACHE.
|
||||
*/
|
||||
// #define UMM_INIT_USE_IRAM 1
|
||||
|
||||
|
||||
/*
|
||||
* Start addresses and the size of the heap
|
||||
*/
|
||||
extern char _heap_start[];
|
||||
#define UMM_HEAP_END_ADDR 0x3FFFC000UL
|
||||
#define UMM_MALLOC_CFG_HEAP_ADDR ((uint32_t)&_heap_start[0])
|
||||
#define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(UMM_HEAP_END_ADDR - UMM_MALLOC_CFG_HEAP_ADDR))
|
||||
|
||||
/*
|
||||
* Define active Heaps
|
||||
*/
|
||||
#if defined(MMU_IRAM_HEAP)
|
||||
#define UMM_HEAP_IRAM
|
||||
#else
|
||||
#undef UMM_HEAP_IRAM
|
||||
#endif
|
||||
|
||||
#if defined(MMU_EXTERNAL_HEAP)
|
||||
#define UMM_HEAP_EXTERNAL
|
||||
#else
|
||||
#undef UMM_HEAP_EXTERNAL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Assign IDs to active Heaps and tally. DRAM is always active.
|
||||
*/
|
||||
#define UMM_HEAP_DRAM 0
|
||||
#define UMM_HEAP_DRAM_DEFINED 1
|
||||
|
||||
#ifdef UMM_HEAP_IRAM
|
||||
#undef UMM_HEAP_IRAM
|
||||
#define UMM_HEAP_IRAM_DEFINED 1
|
||||
#define UMM_HEAP_IRAM UMM_HEAP_DRAM_DEFINED
|
||||
#else
|
||||
#define UMM_HEAP_IRAM_DEFINED 0
|
||||
#endif
|
||||
|
||||
#ifdef UMM_HEAP_EXTERNAL
|
||||
#undef UMM_HEAP_EXTERNAL
|
||||
#define UMM_HEAP_EXTERNAL_DEFINED 1
|
||||
#define UMM_HEAP_EXTERNAL (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED)
|
||||
#else
|
||||
#define UMM_HEAP_EXTERNAL_DEFINED 0
|
||||
#endif
|
||||
|
||||
#define UMM_NUM_HEAPS (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED + UMM_HEAP_EXTERNAL_DEFINED)
|
||||
|
||||
#if (UMM_NUM_HEAPS == 1)
|
||||
#else
|
||||
#define UMM_HEAP_STACK_DEPTH 32
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
@@ -8,15 +8,19 @@
|
||||
#include <stddef.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 result overflows/wraps, return saturation value.
|
||||
*/
|
||||
static size_t poison_size(size_t s) {
|
||||
return(s ? (UMM_POISON_SIZE_BEFORE +
|
||||
sizeof(UMM_POISONED_BLOCK_LEN_TYPE) +
|
||||
UMM_POISON_SIZE_AFTER)
|
||||
: 0);
|
||||
static void add_poison_size(size_t* s) {
|
||||
if (*s == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
*s = umm_uadd_sat(*s, UMM_POISON_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -138,10 +142,8 @@ static void *get_unpoisoned( void *vptr ) {
|
||||
ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
|
||||
|
||||
umm_heap_context_t *_context = umm_get_ptr_context(vptr);
|
||||
if (NULL == _context) {
|
||||
panic();
|
||||
return NULL;
|
||||
}
|
||||
NON_NULL_CONTEXT_ASSERT();
|
||||
|
||||
/* Figure out which block we're in. Note the use of truncated division... */
|
||||
c = (ptr - (uintptr_t)(&(_context->heap[0]))) / sizeof(umm_block);
|
||||
|
||||
@@ -158,7 +160,7 @@ static void *get_unpoisoned( void *vptr ) {
|
||||
void *umm_poison_malloc(size_t size) {
|
||||
void *ret;
|
||||
|
||||
size += poison_size(size);
|
||||
add_poison_size(&size);
|
||||
|
||||
ret = umm_malloc(size);
|
||||
|
||||
@@ -171,14 +173,18 @@ void *umm_poison_malloc( size_t size ) {
|
||||
|
||||
void *umm_poison_calloc(size_t num, size_t item_size) {
|
||||
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);
|
||||
|
||||
add_poison_size(&size);
|
||||
|
||||
ret = umm_malloc(size);
|
||||
|
||||
if (NULL != ret)
|
||||
if (NULL != ret) {
|
||||
memset(ret, 0x00, size);
|
||||
}
|
||||
|
||||
ret = get_poisoned(ret, size);
|
||||
|
||||
@@ -192,7 +198,7 @@ void *umm_poison_realloc( void *ptr, size_t size ) {
|
||||
|
||||
ptr = get_unpoisoned(ptr);
|
||||
|
||||
size += poison_size(size);
|
||||
add_poison_size(&size);
|
||||
ret = umm_realloc(ptr, size);
|
||||
|
||||
ret = get_poisoned(ret, size);
|
||||
@@ -219,7 +225,7 @@ bool umm_poison_check(void) {
|
||||
bool ok = true;
|
||||
uint16_t cur;
|
||||
|
||||
UMM_INIT_HEAP;
|
||||
UMM_CHECK_INITIALIZED();
|
||||
|
||||
UMM_CRITICAL_ENTRY(id_poison);
|
||||
umm_heap_context_t *_context = umm_get_current_heap();
|
||||
|
157
cores/esp8266/wpa2_eap_patch.cpp
Normal file
157
cores/esp8266/wpa2_eap_patch.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* To implement this patch, the SDK module `eap.o` from archive `libwpa2.a` must
|
||||
* be patched to call `z2EapFree` instead of `vPortFree`. This limits extending
|
||||
* the execution time of vPortFree to that module only. Not impacting other
|
||||
* modules.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ets_sys.h>
|
||||
#include <pgmspace.h>
|
||||
#include "coredecls.h"
|
||||
|
||||
#ifdef DEBUG_WPA2_EAP_PATCH
|
||||
#include "esp8266_undocumented.h"
|
||||
#define DEBUG_PRINTF ets_uart_printf
|
||||
#else
|
||||
#define DEBUG_PRINTF(...)
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
// extern "C" void z2EapFree(void *ptr, const char* file, int line) __attribute__((weak, alias("vPortFree")));
|
||||
|
||||
/*
|
||||
* Limited 2-part wrapper for vPortFree calls made in SDK module `eap.o` from
|
||||
* archive `libwpa2.a`.
|
||||
*
|
||||
* vPortFree calls from eap.o are monitored for calls from line 799. This is
|
||||
* the location of the memory leak. At entry register a12 contains the structure
|
||||
* address which has the addresses of the allocations that will be leaked.
|
||||
*
|
||||
* Part 1 of this wrapper, z2EapFree, appends the value of register a12 as a
|
||||
* 4th argument to part2 of this wrapper, patch_wpa2_eap_vPortFree_a12(). Which
|
||||
* in turn checks and frees the additional allocations, that would have been
|
||||
* lost.
|
||||
*
|
||||
* extern "C" z2EapFree(void*);
|
||||
*/
|
||||
|
||||
/*
|
||||
* Part 1 of Limited vPortFree Wrapper
|
||||
*/
|
||||
asm(
|
||||
// ".section .iram.text.z2EapFree,\"ax\",@progbits\n\t"
|
||||
// Since all the possible callers in eap.o are in sections starting with
|
||||
// .text and not .iram.text we should be safe putting these wrappers in .text.
|
||||
".section .text.z2EapFree,\"ax\",@progbits\n\t"
|
||||
".literal_position\n\t"
|
||||
".literal .patch_wpa2_eap_vPortFree_a12, patch_wpa2_eap_vPortFree_a12\n\t"
|
||||
".align 4\n\t"
|
||||
".global z2EapFree\n\t"
|
||||
".type z2EapFree, @function\n\t"
|
||||
"\n"
|
||||
"z2EapFree:\n\t"
|
||||
"addi a1, a1, -16\n\t"
|
||||
"s32i a0, a1, 0\n\t"
|
||||
"mov a5, a12\n\t"
|
||||
"l32r a0, .patch_wpa2_eap_vPortFree_a12\n\t"
|
||||
"callx0 a0\n\t"
|
||||
"l32i a0, a1, 0\n\t"
|
||||
"addi a1, a1, 16\n\t"
|
||||
"ret\n\t"
|
||||
".size z2EapFree, .-z2EapFree\n\t"
|
||||
);
|
||||
|
||||
/*
|
||||
* While some insight can be gained from the ESP32 repo for this structure.
|
||||
* It does not match exactly. This alternate structure focuses on correct offset
|
||||
* rather than trying to exactly reconstruct the original labels.
|
||||
* These offset were found in libwpa2.a:eap.o .text.eap_peer_config_init
|
||||
*/
|
||||
struct StateMachine { // size 200 bytes
|
||||
void* beforeConfig[16];
|
||||
void* config[26];
|
||||
// 0 - s32i a2, a12, 64 // username / Identity
|
||||
// 1 - s32i a2, a12, 68 // length
|
||||
// 2 - s32i a2, a12, 72 // anonymous Identity
|
||||
// 3 - s32i a2, a12, 76
|
||||
// 4 - s32i a2, a12, 80 // password
|
||||
// 5 - s32i a2, a12, 84
|
||||
//
|
||||
// "new password" - From wifi_station_set_enterprise_new_password(), we see
|
||||
// global saved value .bss+32 and .bss+36 which are later used to populate
|
||||
// ".config" in eap_peer_config_init(). I do not have an environment to
|
||||
// exercise this parameter. In my tests, the "new password" element in the
|
||||
// ".config" is never initialized. At the moment, I don't see any code that
|
||||
// would free the allocation.
|
||||
// allocated via pvPortZalloc from line 0x30f, 783
|
||||
// 21 - s32i a2, a12, 148 // new password
|
||||
// 22 - s32i a2, a12, 152
|
||||
|
||||
void* afterConfig[8];
|
||||
};
|
||||
|
||||
/*
|
||||
* Part 2 of Limited vPortFree Wrapper
|
||||
*
|
||||
* Presently, all SDKs have the same memory leaks in the same module at the
|
||||
* same line.
|
||||
*/
|
||||
void patch_wpa2_eap_vPortFree_a12(void *ptr, const char* file, int line, void* a12) {
|
||||
if (799 == line) {
|
||||
// This caller is eap_peer_config_deinit()
|
||||
struct StateMachine* sm = (struct StateMachine*)a12;
|
||||
if (ptr == sm->config[0]) {
|
||||
// Fix leaky frunction - eap.o only frees one out of 4 config items
|
||||
// finish the other 3 first
|
||||
vPortFree(sm->config[2], file, line);
|
||||
vPortFree(sm->config[4], file, line);
|
||||
vPortFree(sm->config[21], file, line);
|
||||
// ptr is sm->config[0], let fall through handle it
|
||||
}
|
||||
#ifdef DEBUG_WPA2_EAP_PATCH
|
||||
DEBUG_PRINTF("\nz2EapFree/vPortFree patch struct StateMachine * = %8p\n", a12);
|
||||
DEBUG_PRINTF(" config[0] vPortFree(%8p, file, line);\n", ptr);
|
||||
DEBUG_PRINTF(" config[2] vPortFree(%8p, file, line);\n", sm->config[2]);
|
||||
DEBUG_PRINTF(" config[4] vPortFree(%8p, file, line);\n", sm->config[4]);
|
||||
DEBUG_PRINTF(" config[21] vPortFree(%8p, file, line);\n", sm->config[21]);
|
||||
if (a12) {
|
||||
void** pw = (void**)a12;
|
||||
DEBUG_PRINTF("\nhexdump struct StateMachine:\n");
|
||||
for (size_t i=0; i<200/4; i+=4) {
|
||||
DEBUG_PRINTF("%03u: %8p %8p %8p %8p\n", i*4, pw[i], pw[i+1], pw[i+2], pw[i+3]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if 0
|
||||
// This is not needed because the call was NO-OPed in the library. This code
|
||||
// snippit is just to show how a future memory free issue might be resolved.
|
||||
else if (672 == line) {
|
||||
// This caller is wpa2_sm_rx_eapol()
|
||||
// 1st of a double free
|
||||
// let the 2nd free handle it.
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
vPortFree(ptr, file, line);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* This will minimize code space for non-wifi enterprise sketches which do not
|
||||
* need the patch and disable_extra4k_at_link_time().
|
||||
*/
|
||||
void enable_wifi_enterprise_patch(void) {
|
||||
/*
|
||||
* Calling this from setup or anywhere ensures that the patch code is
|
||||
* included in the build.
|
||||
*
|
||||
* Also, WiFi Enterprise uses a lot of system stack space and may crash
|
||||
* unless we:
|
||||
*/
|
||||
disable_extra4k_at_link_time();
|
||||
}
|
@@ -54,7 +54,7 @@
|
||||
* stack frame is limited to 128 bytes (currently at 64).
|
||||
*/
|
||||
STRUCT_BEGIN
|
||||
STRUCT_FIELD (long,4,KEXC_,pc) /* "parm" */
|
||||
STRUCT_FIELD (long,4,KEXC_,pc) /* "param" */
|
||||
STRUCT_FIELD (long,4,KEXC_,ps)
|
||||
STRUCT_AFIELD(long,4,KEXC_,areg, 4) /* a12 .. a15 */
|
||||
STRUCT_FIELD (long,4,KEXC_,sar) /* "save" */
|
||||
|
@@ -5,7 +5,7 @@ Intro
|
||||
-----
|
||||
|
||||
PROGMEM is a Arduino AVR feature that has been ported to ESP8266 to
|
||||
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 =
|
||||
"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
|
||||
@@ -283,7 +283,7 @@ generate as they are basically ``const char *``. On the other hand
|
||||
conversions from, very useful when overloading functions, and doing
|
||||
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
|
||||
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!
|
||||
|
||||
Hope this helps.
|
||||
|
@@ -44,8 +44,8 @@ Usage
|
||||
|
||||
5. Check the Serial Output
|
||||
|
||||
Informations
|
||||
------------
|
||||
Information
|
||||
-----------
|
||||
|
||||
It work with every sketch that enables the Serial interface that is
|
||||
selected as debug port.
|
||||
@@ -74,7 +74,7 @@ Debug Level
|
||||
All defines for the different levels starts with ``DEBUG_ESP_``
|
||||
|
||||
a full list can be found here in the
|
||||
`boards.txt <https://github.com/esp8266/Arduino/blob/master/tools/boards.txt.py#L1045-L1047>`__
|
||||
`boards.txt <https://github.com/esp8266/Arduino/blob/04c2322721f6865efe0c518be57e795e8643c183/tools/boards.txt.py#L1308-L1309>`__
|
||||
|
||||
Example for own debug messages
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user