1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-08-05 13:16:13 +03:00

Merge branch 'master' into master

This commit is contained in:
david gauchard
2022-06-11 22:23:53 +02:00
committed by GitHub
535 changed files with 27764 additions and 21136 deletions

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

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

View File

@@ -15,6 +15,9 @@ jobs:
build-linux:
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

View File

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

View File

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

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

View File

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

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -186,6 +186,9 @@ void attachInterrupt(uint8_t pin, void (*)(void), int mode);
void detachInterrupt(uint8_t pin);
void 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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@@ -0,0 +1,59 @@
// - do not edit - autogenerated by boards.txt.py
#ifndef __FLASH_MAP_H
#define __FLASH_MAP_H
#include <stdint.h>
#include <stddef.h>
typedef struct
{
uint32_t eeprom_start;
uint32_t fs_start;
uint32_t fs_end;
uint32_t fs_block_size;
uint32_t fs_page_size;
uint32_t flash_size_kb;
} flash_map_s;
/*
Following definitions map the above structure, one per line.
FLASH_MAP_* is a user choice in sketch:
`FLASH_MAP_SETUP_CONFIG(FLASH_MAP_OTA_FS)`
Configuration is made at boot with detected flash chip size (last argument 512..16384)
Other values are defined from `tools/boards.txt.py`.
*/
#define FLASH_MAP_OTA_FS \
{ \
{ .eeprom_start = 0x402fb000, .fs_start = 0x402eb000, .fs_end = 0x402fb000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 1024 }, \
{ .eeprom_start = 0x403fb000, .fs_start = 0x403c0000, .fs_end = 0x403fb000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 2048 }, \
{ .eeprom_start = 0x405fb000, .fs_start = 0x40400000, .fs_end = 0x405fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 4096 }, \
{ .eeprom_start = 0x409fb000, .fs_start = 0x40400000, .fs_end = 0x409fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 8192 }, \
{ .eeprom_start = 0x411fb000, .fs_start = 0x40400000, .fs_end = 0x411fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 16384 }, \
{ .eeprom_start = 0x4027b000, .fs_start = 0x40273000, .fs_end = 0x4027b000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 512 }, \
}
#define FLASH_MAP_MAX_FS \
{ \
{ .eeprom_start = 0x402fb000, .fs_start = 0x4027b000, .fs_end = 0x402fb000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 1024 }, \
{ .eeprom_start = 0x403fb000, .fs_start = 0x40300000, .fs_end = 0x403fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 2048 }, \
{ .eeprom_start = 0x405fb000, .fs_start = 0x40300000, .fs_end = 0x405fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 4096 }, \
{ .eeprom_start = 0x409fb000, .fs_start = 0x40300000, .fs_end = 0x409fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 8192 }, \
{ .eeprom_start = 0x411fb000, .fs_start = 0x40300000, .fs_end = 0x411fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 16384 }, \
{ .eeprom_start = 0x4027b000, .fs_start = 0x4025b000, .fs_end = 0x4027b000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 512 }, \
}
#define FLASH_MAP_NO_FS \
{ \
{ .eeprom_start = 0x402fb000, .fs_start = 0x402fb000, .fs_end = 0x402fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 1024 }, \
{ .eeprom_start = 0x403fb000, .fs_start = 0x403fb000, .fs_end = 0x403fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 2048 }, \
{ .eeprom_start = 0x405fb000, .fs_start = 0x405fb000, .fs_end = 0x405fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 4096 }, \
{ .eeprom_start = 0x409fb000, .fs_start = 0x409fb000, .fs_end = 0x409fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 8192 }, \
{ .eeprom_start = 0x411fb000, .fs_start = 0x411fb000, .fs_end = 0x411fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 16384 }, \
{ .eeprom_start = 0x4027b000, .fs_start = 0x4027b000, .fs_end = 0x4027b000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 512 }, \
}
#endif // __FLASH_MAP_H

View File

@@ -35,9 +35,6 @@
// SerialEvent functions are weak, so when the user doesn't define them,
// 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;
}

View File

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

View File

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

View File

@@ -1,64 +1,97 @@
/*
lwIPDhcpServer-NonOS.cpp - DHCP server wrapper
NonOS DHCP server helpers
Copyright (c) 2020 esp8266 arduino. All rights reserved.
Copyright (c) 2020-2022 esp8266 arduino. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This 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"

View File

@@ -0,0 +1,24 @@
/*
NonOS DHCP server helpers
Copyright (c) 2020-2022 esp8266 arduino. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "LwipDhcpServer.h"
// Global static DHCP instance for softAP interface
DhcpServer& getNonOSDhcpServer();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
/*
core_esp8266_noniso.c - nonstandard (but usefull) conversion functions
core_esp8266_noniso.c - nonstandard (but useful) conversion functions
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.

View File

@@ -289,7 +289,7 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] =
};
// These functions will be overriden from C++ code.
// These functions will be overridden from C++ code.
// Unfortunately, we can't use extern "C" because Arduino preprocessor
// doesn't generate forward declarations for extern "C" functions correctly,
// so we use mangled names here.

View File

@@ -245,12 +245,12 @@ static void print_stack(uint32_t start, uint32_t end) {
}
}
static void uart_write_char_d(char c) {
static void IRAM_ATTR uart_write_char_d(char c) {
uart0_write_char_d(c);
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') {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -47,7 +47,7 @@
* https://github.com/qca/open-ath9k-htc-firmware/blob/master/sboot/magpie_1_1/sboot/athos/src/xtos/exc-sethandler.c
*
* It has been revised to use Arduino ESP8266 core includes, types, and
* formating.
* formatting.
*/
/* exc-sethandler.c - register an exception handler in XTOS */

View File

@@ -68,3 +68,13 @@ int32_t flash_hal_erase(uint32_t addr, uint32_t size) {
}
return FLASH_HAL_OK;
}
#if FLASH_MAP_SUPPORT
// default weak configuration:
FLASH_MAP_SETUP_CONFIG_ATTR(__attribute__((weak)), FLASH_MAP_OTA_FS)
// can be overridden by user with:
//FLASH_MAP_SETUP_CONFIG(FLASH_MAP_some_configuration)
#endif

View File

@@ -24,18 +24,58 @@
License along with this library; if not, write to the Free Software
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)

View File

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

View File

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

View File

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

View File

@@ -1,12 +0,0 @@
// This include file is a hack to ensure backward compatibility with
// pre 3.0.0 versions of the core. There was a *lowercase* "i2s.h"
// header which was in this directory, now renamed to "core_esp82i66s.h"
// But, the I2S class has a header, "I2S.h" in uppercase. On Linux
// the two names are different, but on Windows it's case-insensitive
// so the names conflict.
//
// Avoid the issue by preserving the old i2s.h file and have it redirect
// to I2S.h which will give the ESP8266-specific functions as well as
// the generic I2S class.
#include "../../libraries/I2S/src/I2S.h"

View File

@@ -3,7 +3,7 @@
#include <Arduino.h>
// 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -695,7 +695,7 @@ s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
* in this callback will mess things up for sure - do not do this.
* 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).

View File

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

View File

@@ -1,5 +1,5 @@
/*
stdlib_noniso.h - nonstandard (but usefull) conversion functions
stdlib_noniso.h - nonstandard (but useful) conversion functions
Copyright (c) 2021 David Gauchard. All rights reserved.
This file is part of the esp8266 core for Arduino environment.

View File

@@ -1,5 +1,5 @@
/*
stdlib_noniso.h - nonstandard (but usefull) conversion functions
stdlib_noniso.h - nonstandard (but useful) conversion functions
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,16 @@
/*
* Configuration for umm_malloc - target Arduino ESP8266 core
*
* Changes specific to a target platform go here.
*
* This comment section changed to below in the upstream version, keeping old method for now.
*
* Configuration for umm_malloc - DO NOT EDIT THIS FILE BY HAND!
*
* 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__); })

View File

@@ -0,0 +1,91 @@
#ifndef _UMM_MALLOC_CFGPORT_H
#define _UMM_MALLOC_CFGPORT_H
#ifndef _UMM_MALLOC_CFG_H
#error "This include file must be used with umm_malloc_cfg.h"
#endif
/*
* Arduino ESP8266 core umm_malloc port config
*/
#include <pgmspace.h>
#include <mmu_iram.h>
#include "../debug.h"
#include "../esp8266_undocumented.h"
#include <core_esp8266_features.h>
#include <stdlib.h>
#include <osapi.h>
#include "c_types.h"
/*
* -DUMM_INIT_USE_IRAM
*
* Historically, the umm_init() call path has been in IRAM. The umm_init() call
* path is now in ICACHE (flash). Use the build option UMM_INIT_USE_IRAM to
* restore the legacy behavor.
*
* If you have your own app_entry_redefinable() function, see
* app_entry_redefinable() in core_esp8266_app_entry_noextra4k.cpp for an
* example of how to toggle between ICACHE and IRAM in your build.
*
* The default is to use ICACHE.
*/
// #define UMM_INIT_USE_IRAM 1
/*
* Start addresses and the size of the heap
*/
extern char _heap_start[];
#define UMM_HEAP_END_ADDR 0x3FFFC000UL
#define UMM_MALLOC_CFG_HEAP_ADDR ((uint32_t)&_heap_start[0])
#define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(UMM_HEAP_END_ADDR - UMM_MALLOC_CFG_HEAP_ADDR))
/*
* Define active Heaps
*/
#if defined(MMU_IRAM_HEAP)
#define UMM_HEAP_IRAM
#else
#undef UMM_HEAP_IRAM
#endif
#if defined(MMU_EXTERNAL_HEAP)
#define UMM_HEAP_EXTERNAL
#else
#undef UMM_HEAP_EXTERNAL
#endif
/*
* Assign IDs to active Heaps and tally. DRAM is always active.
*/
#define UMM_HEAP_DRAM 0
#define UMM_HEAP_DRAM_DEFINED 1
#ifdef UMM_HEAP_IRAM
#undef UMM_HEAP_IRAM
#define UMM_HEAP_IRAM_DEFINED 1
#define UMM_HEAP_IRAM UMM_HEAP_DRAM_DEFINED
#else
#define UMM_HEAP_IRAM_DEFINED 0
#endif
#ifdef UMM_HEAP_EXTERNAL
#undef UMM_HEAP_EXTERNAL
#define UMM_HEAP_EXTERNAL_DEFINED 1
#define UMM_HEAP_EXTERNAL (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED)
#else
#define UMM_HEAP_EXTERNAL_DEFINED 0
#endif
#define UMM_NUM_HEAPS (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED + UMM_HEAP_EXTERNAL_DEFINED)
#if (UMM_NUM_HEAPS == 1)
#else
#define UMM_HEAP_STACK_DEPTH 32
#endif
#endif

View File

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

View File

@@ -0,0 +1,157 @@
/*
* To implement this patch, the SDK module `eap.o` from archive `libwpa2.a` must
* be patched to call `z2EapFree` instead of `vPortFree`. This limits extending
* the execution time of vPortFree to that module only. Not impacting other
* modules.
*
*/
#include <string.h>
#include <ets_sys.h>
#include <pgmspace.h>
#include "coredecls.h"
#ifdef DEBUG_WPA2_EAP_PATCH
#include "esp8266_undocumented.h"
#define DEBUG_PRINTF ets_uart_printf
#else
#define DEBUG_PRINTF(...)
#endif
extern "C" {
// extern "C" void z2EapFree(void *ptr, const char* file, int line) __attribute__((weak, alias("vPortFree")));
/*
* Limited 2-part wrapper for vPortFree calls made in SDK module `eap.o` from
* archive `libwpa2.a`.
*
* vPortFree calls from eap.o are monitored for calls from line 799. This is
* the location of the memory leak. At entry register a12 contains the structure
* address which has the addresses of the allocations that will be leaked.
*
* Part 1 of this wrapper, z2EapFree, appends the value of register a12 as a
* 4th argument to part2 of this wrapper, patch_wpa2_eap_vPortFree_a12(). Which
* in turn checks and frees the additional allocations, that would have been
* lost.
*
* extern "C" z2EapFree(void*);
*/
/*
* Part 1 of Limited vPortFree Wrapper
*/
asm(
// ".section .iram.text.z2EapFree,\"ax\",@progbits\n\t"
// Since all the possible callers in eap.o are in sections starting with
// .text and not .iram.text we should be safe putting these wrappers in .text.
".section .text.z2EapFree,\"ax\",@progbits\n\t"
".literal_position\n\t"
".literal .patch_wpa2_eap_vPortFree_a12, patch_wpa2_eap_vPortFree_a12\n\t"
".align 4\n\t"
".global z2EapFree\n\t"
".type z2EapFree, @function\n\t"
"\n"
"z2EapFree:\n\t"
"addi a1, a1, -16\n\t"
"s32i a0, a1, 0\n\t"
"mov a5, a12\n\t"
"l32r a0, .patch_wpa2_eap_vPortFree_a12\n\t"
"callx0 a0\n\t"
"l32i a0, a1, 0\n\t"
"addi a1, a1, 16\n\t"
"ret\n\t"
".size z2EapFree, .-z2EapFree\n\t"
);
/*
* While some insight can be gained from the ESP32 repo for this structure.
* It does not match exactly. This alternate structure focuses on correct offset
* rather than trying to exactly reconstruct the original labels.
* These offset were found in libwpa2.a:eap.o .text.eap_peer_config_init
*/
struct StateMachine { // size 200 bytes
void* beforeConfig[16];
void* config[26];
// 0 - s32i a2, a12, 64 // username / Identity
// 1 - s32i a2, a12, 68 // length
// 2 - s32i a2, a12, 72 // anonymous Identity
// 3 - s32i a2, a12, 76
// 4 - s32i a2, a12, 80 // password
// 5 - s32i a2, a12, 84
//
// "new password" - From wifi_station_set_enterprise_new_password(), we see
// global saved value .bss+32 and .bss+36 which are later used to populate
// ".config" in eap_peer_config_init(). I do not have an environment to
// exercise this parameter. In my tests, the "new password" element in the
// ".config" is never initialized. At the moment, I don't see any code that
// would free the allocation.
// allocated via pvPortZalloc from line 0x30f, 783
// 21 - s32i a2, a12, 148 // new password
// 22 - s32i a2, a12, 152
void* afterConfig[8];
};
/*
* Part 2 of Limited vPortFree Wrapper
*
* Presently, all SDKs have the same memory leaks in the same module at the
* same line.
*/
void patch_wpa2_eap_vPortFree_a12(void *ptr, const char* file, int line, void* a12) {
if (799 == line) {
// This caller is eap_peer_config_deinit()
struct StateMachine* sm = (struct StateMachine*)a12;
if (ptr == sm->config[0]) {
// Fix leaky frunction - eap.o only frees one out of 4 config items
// finish the other 3 first
vPortFree(sm->config[2], file, line);
vPortFree(sm->config[4], file, line);
vPortFree(sm->config[21], file, line);
// ptr is sm->config[0], let fall through handle it
}
#ifdef DEBUG_WPA2_EAP_PATCH
DEBUG_PRINTF("\nz2EapFree/vPortFree patch struct StateMachine * = %8p\n", a12);
DEBUG_PRINTF(" config[0] vPortFree(%8p, file, line);\n", ptr);
DEBUG_PRINTF(" config[2] vPortFree(%8p, file, line);\n", sm->config[2]);
DEBUG_PRINTF(" config[4] vPortFree(%8p, file, line);\n", sm->config[4]);
DEBUG_PRINTF(" config[21] vPortFree(%8p, file, line);\n", sm->config[21]);
if (a12) {
void** pw = (void**)a12;
DEBUG_PRINTF("\nhexdump struct StateMachine:\n");
for (size_t i=0; i<200/4; i+=4) {
DEBUG_PRINTF("%03u: %8p %8p %8p %8p\n", i*4, pw[i], pw[i+1], pw[i+2], pw[i+3]);
}
}
#endif
}
#if 0
// This is not needed because the call was NO-OPed in the library. This code
// snippit is just to show how a future memory free issue might be resolved.
else if (672 == line) {
// This caller is wpa2_sm_rx_eapol()
// 1st of a double free
// let the 2nd free handle it.
return;
}
#endif
vPortFree(ptr, file, line);
}
};
/*
* This will minimize code space for non-wifi enterprise sketches which do not
* need the patch and disable_extra4k_at_link_time().
*/
void enable_wifi_enterprise_patch(void) {
/*
* Calling this from setup or anywhere ensures that the patch code is
* included in the build.
*
* Also, WiFi Enterprise uses a lot of system stack space and may crash
* unless we:
*/
disable_extra4k_at_link_time();
}

View File

@@ -54,7 +54,7 @@
* stack frame is limited to 128 bytes (currently at 64).
*/
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" */

View File

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

View File

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