mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-21 10:26:06 +03:00
Merge branch 'master' into wifi_mesh_update_2.2
This commit is contained in:
commit
b0ef9195b5
17
.travis.yml
17
.travis.yml
@ -1,19 +1,11 @@
|
|||||||
language: bash
|
language: bash
|
||||||
os: linux
|
os: linux
|
||||||
dist: trusty
|
dist: bionic
|
||||||
|
|
||||||
git:
|
git:
|
||||||
depth: 1
|
depth: 1
|
||||||
submodules: false
|
submodules: false
|
||||||
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages:
|
|
||||||
- g++-7
|
|
||||||
- gcc-7
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- git submodule update --init # no recursive update
|
- git submodule update --init # no recursive update
|
||||||
|
|
||||||
@ -87,14 +79,14 @@ jobs:
|
|||||||
- name: "Host tests"
|
- name: "Host tests"
|
||||||
stage: build
|
stage: build
|
||||||
script: $TRAVIS_BUILD_DIR/tests/ci/host_test.sh
|
script: $TRAVIS_BUILD_DIR/tests/ci/host_test.sh
|
||||||
install: sudo apt-get install valgrind lcov
|
install:
|
||||||
env: CC=gcc-7 CXX=g++-7
|
- sudo apt-get install valgrind lcov
|
||||||
|
|
||||||
- name: "Docs"
|
- name: "Docs"
|
||||||
stage: build
|
stage: build
|
||||||
script: $TRAVIS_BUILD_DIR/tests/ci/build_docs.sh
|
script: $TRAVIS_BUILD_DIR/tests/ci/build_docs.sh
|
||||||
install:
|
install:
|
||||||
- sudo apt-get install python3-pip
|
- sudo apt-get install python3-pip python3-setuptools
|
||||||
- pip3 install --user -r doc/requirements.txt;
|
- pip3 install --user -r doc/requirements.txt;
|
||||||
|
|
||||||
- name: "Style check"
|
- name: "Style check"
|
||||||
@ -105,7 +97,6 @@ jobs:
|
|||||||
- name: "Mock trivial test"
|
- name: "Mock trivial test"
|
||||||
stage: build
|
stage: build
|
||||||
script: $TRAVIS_BUILD_DIR/tests/buildm.sh
|
script: $TRAVIS_BUILD_DIR/tests/buildm.sh
|
||||||
env: CC=gcc-7 CXX=g++-7
|
|
||||||
|
|
||||||
- name: "Mac OSX can build sketches"
|
- name: "Mac OSX can build sketches"
|
||||||
os: osx
|
os: osx
|
||||||
|
32
README.md
32
README.md
@ -16,7 +16,7 @@ ESP8266 Arduino core comes with libraries to communicate over WiFi using TCP and
|
|||||||
# Contents
|
# Contents
|
||||||
- Installing options:
|
- Installing options:
|
||||||
- [Using Boards Manager](#installing-with-boards-manager)
|
- [Using Boards Manager](#installing-with-boards-manager)
|
||||||
- [Using git version](#using-git-version-basic-instructions)
|
- [Using git version](#using-git-version)
|
||||||
- [Using PlatformIO](#using-platformio)
|
- [Using PlatformIO](#using-platformio)
|
||||||
- [Building with make](#building-with-make)
|
- [Building with make](#building-with-make)
|
||||||
- [Documentation](#documentation)
|
- [Documentation](#documentation)
|
||||||
@ -38,35 +38,13 @@ Boards manager link: `https://arduino.esp8266.com/stable/package_esp8266com_inde
|
|||||||
|
|
||||||
Documentation: [https://arduino-esp8266.readthedocs.io/en/2.5.2/](https://arduino-esp8266.readthedocs.io/en/2.5.2/)
|
Documentation: [https://arduino-esp8266.readthedocs.io/en/2.5.2/](https://arduino-esp8266.readthedocs.io/en/2.5.2/)
|
||||||
|
|
||||||
### Using git version (basic instructions)
|
### Using git version
|
||||||
[](https://travis-ci.org/esp8266/Arduino)
|
[](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/main/software).
|
||||||
- Go to Arduino directory
|
- Follow the [instructions in the documentation](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version).
|
||||||
- For Mac OS X, it is `Arduino.app` showing as the Arduino icon.
|
|
||||||
This location may be your `~/Downloads`, `~/Desktop` or even `/Applications`.
|
|
||||||
```bash
|
|
||||||
cd <application-directory>/Arduino.app/Contents/Java
|
|
||||||
```
|
|
||||||
- For Linux, it is ~/Arduino by default.
|
|
||||||
```bash
|
|
||||||
cd ~/Arduino
|
|
||||||
```
|
|
||||||
- Clone this repository into hardware/esp8266com/esp8266 directory (or clone it elsewhere and create a symlink)
|
|
||||||
```bash
|
|
||||||
cd hardware
|
|
||||||
mkdir esp8266com
|
|
||||||
cd esp8266com
|
|
||||||
git clone https://github.com/esp8266/Arduino.git esp8266
|
|
||||||
cd esp8266
|
|
||||||
git submodule update --init
|
|
||||||
```
|
|
||||||
- Download binary tools (you need Python 2.7)
|
|
||||||
```bash
|
|
||||||
cd esp8266/tools
|
|
||||||
python get.py
|
|
||||||
```
|
|
||||||
- Restart Arduino
|
|
||||||
|
|
||||||
### Using PlatformIO
|
### Using PlatformIO
|
||||||
|
|
||||||
|
20
boards.txt
20
boards.txt
@ -76,6 +76,10 @@ generic.menu.FlashFreq.40=40MHz
|
|||||||
generic.menu.FlashFreq.40.build.flash_freq=40
|
generic.menu.FlashFreq.40.build.flash_freq=40
|
||||||
generic.menu.FlashFreq.80=80MHz
|
generic.menu.FlashFreq.80=80MHz
|
||||||
generic.menu.FlashFreq.80.build.flash_freq=80
|
generic.menu.FlashFreq.80.build.flash_freq=80
|
||||||
|
generic.menu.FlashFreq.20=20MHz
|
||||||
|
generic.menu.FlashFreq.20.build.flash_freq=20
|
||||||
|
generic.menu.FlashFreq.26=26MHz
|
||||||
|
generic.menu.FlashFreq.26.build.flash_freq=26
|
||||||
generic.menu.FlashMode.dout=DOUT (compatible)
|
generic.menu.FlashMode.dout=DOUT (compatible)
|
||||||
generic.menu.FlashMode.dout.build.flash_mode=dout
|
generic.menu.FlashMode.dout.build.flash_mode=dout
|
||||||
generic.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT
|
generic.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT
|
||||||
@ -370,11 +374,13 @@ generic.menu.led.15=15
|
|||||||
generic.menu.led.15.build.led=-DLED_BUILTIN=15
|
generic.menu.led.15.build.led=-DLED_BUILTIN=15
|
||||||
generic.menu.led.16=16
|
generic.menu.led.16=16
|
||||||
generic.menu.led.16.build.led=-DLED_BUILTIN=16
|
generic.menu.led.16.build.led=-DLED_BUILTIN=16
|
||||||
generic.menu.sdk.nonosdk222_100=nonos-sdk 2.2.1+100 (testing)
|
generic.menu.sdk.nonosdk222_100=nonos-sdk 2.2.1+100 (190703 approved)
|
||||||
generic.menu.sdk.nonosdk222_100.build.sdk=NONOSDK22y
|
generic.menu.sdk.nonosdk222_100.build.sdk=NONOSDK22x_190703
|
||||||
|
generic.menu.sdk.nonosdk222_111=nonos-sdk 2.2.1+111 (191024 testing)
|
||||||
|
generic.menu.sdk.nonosdk222_111.build.sdk=NONOSDK22x_191024
|
||||||
generic.menu.sdk.nonosdk221=nonos-sdk 2.2.1 (legacy)
|
generic.menu.sdk.nonosdk221=nonos-sdk 2.2.1 (legacy)
|
||||||
generic.menu.sdk.nonosdk221.build.sdk=NONOSDK221
|
generic.menu.sdk.nonosdk221.build.sdk=NONOSDK221
|
||||||
generic.menu.sdk.nonosdk3v0=nonos-sdk pre-3 (known issues)
|
generic.menu.sdk.nonosdk3v0=nonos-sdk pre-3 (180626 known issues)
|
||||||
generic.menu.sdk.nonosdk3v0.build.sdk=NONOSDK3V0
|
generic.menu.sdk.nonosdk3v0.build.sdk=NONOSDK3V0
|
||||||
generic.menu.ip.lm2f=v2 Lower Memory
|
generic.menu.ip.lm2f=v2 Lower Memory
|
||||||
generic.menu.ip.lm2f.build.lwip_include=lwip2/include
|
generic.menu.ip.lm2f.build.lwip_include=lwip2/include
|
||||||
@ -4758,6 +4764,10 @@ wifinfo.menu.FlashFreq.40=40MHz
|
|||||||
wifinfo.menu.FlashFreq.40.build.flash_freq=40
|
wifinfo.menu.FlashFreq.40.build.flash_freq=40
|
||||||
wifinfo.menu.FlashFreq.80=80MHz
|
wifinfo.menu.FlashFreq.80=80MHz
|
||||||
wifinfo.menu.FlashFreq.80.build.flash_freq=80
|
wifinfo.menu.FlashFreq.80.build.flash_freq=80
|
||||||
|
wifinfo.menu.FlashFreq.20=20MHz
|
||||||
|
wifinfo.menu.FlashFreq.20.build.flash_freq=20
|
||||||
|
wifinfo.menu.FlashFreq.26=26MHz
|
||||||
|
wifinfo.menu.FlashFreq.26.build.flash_freq=26
|
||||||
wifinfo.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB)
|
wifinfo.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB)
|
||||||
wifinfo.menu.eesz.1M64.build.flash_size=1M
|
wifinfo.menu.eesz.1M64.build.flash_size=1M
|
||||||
wifinfo.menu.eesz.1M64.build.flash_size_bytes=0x100000
|
wifinfo.menu.eesz.1M64.build.flash_size_bytes=0x100000
|
||||||
@ -5778,6 +5788,10 @@ wifi_slot.menu.FlashFreq.40=40MHz
|
|||||||
wifi_slot.menu.FlashFreq.40.build.flash_freq=40
|
wifi_slot.menu.FlashFreq.40.build.flash_freq=40
|
||||||
wifi_slot.menu.FlashFreq.80=80MHz
|
wifi_slot.menu.FlashFreq.80=80MHz
|
||||||
wifi_slot.menu.FlashFreq.80.build.flash_freq=80
|
wifi_slot.menu.FlashFreq.80.build.flash_freq=80
|
||||||
|
wifi_slot.menu.FlashFreq.20=20MHz
|
||||||
|
wifi_slot.menu.FlashFreq.20.build.flash_freq=20
|
||||||
|
wifi_slot.menu.FlashFreq.26=26MHz
|
||||||
|
wifi_slot.menu.FlashFreq.26.build.flash_freq=26
|
||||||
wifi_slot.menu.FlashMode.dout=DOUT (compatible)
|
wifi_slot.menu.FlashMode.dout=DOUT (compatible)
|
||||||
wifi_slot.menu.FlashMode.dout.build.flash_mode=dout
|
wifi_slot.menu.FlashMode.dout.build.flash_mode=dout
|
||||||
wifi_slot.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT
|
wifi_slot.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT
|
||||||
|
@ -275,17 +275,16 @@ long secureRandom(long);
|
|||||||
long secureRandom(long, long);
|
long secureRandom(long, long);
|
||||||
long map(long, long, long, long, long);
|
long map(long, long, long, long, long);
|
||||||
|
|
||||||
extern "C" void configTime(long timezone, int daylightOffset_sec,
|
void configTime(int timezone, int daylightOffset_sec, const char* server1,
|
||||||
const char* server1, const char* server2 = nullptr, const char* server3 = nullptr);
|
const char* server2 = nullptr, const char* server3 = nullptr);
|
||||||
|
|
||||||
#endif
|
void configTime(const char* tz, const char* server1,
|
||||||
|
const char* server2 = nullptr, const char* server3 = nullptr);
|
||||||
|
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
#include "pins_arduino.h"
|
#include "pins_arduino.h"
|
||||||
|
|
||||||
#ifndef PUYA_SUPPORT
|
|
||||||
#define PUYA_SUPPORT 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_OOM
|
#ifdef DEBUG_ESP_OOM
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Arduino.h"
|
#include "Esp.h"
|
||||||
#include "flash_utils.h"
|
#include "flash_utils.h"
|
||||||
#include "eboot_command.h"
|
#include "eboot_command.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -36,6 +36,14 @@ extern struct rst_info resetInfo;
|
|||||||
|
|
||||||
//#define DEBUG_SERIAL Serial
|
//#define DEBUG_SERIAL Serial
|
||||||
|
|
||||||
|
#ifndef PUYA_SUPPORT
|
||||||
|
#define PUYA_SUPPORT 1
|
||||||
|
#endif
|
||||||
|
#ifndef PUYA_BUFFER_SIZE
|
||||||
|
// Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
|
||||||
|
// Always use a multiple of flash page size (256 bytes)
|
||||||
|
#define PUYA_BUFFER_SIZE 256
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User-defined Literals
|
* User-defined Literals
|
||||||
@ -494,7 +502,7 @@ uint32_t EspClass::getSketchSize() {
|
|||||||
|
|
||||||
image_header_t image_header;
|
image_header_t image_header;
|
||||||
uint32_t pos = APP_START_OFFSET;
|
uint32_t pos = APP_START_OFFSET;
|
||||||
if (spi_flash_read(pos, (uint32_t*) &image_header, sizeof(image_header))) {
|
if (spi_flash_read(pos, (uint32_t*) &image_header, sizeof(image_header)) != SPI_FLASH_RESULT_OK) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
pos += sizeof(image_header);
|
pos += sizeof(image_header);
|
||||||
@ -506,7 +514,7 @@ uint32_t EspClass::getSketchSize() {
|
|||||||
++section_index)
|
++section_index)
|
||||||
{
|
{
|
||||||
section_header_t section_header = {0, 0};
|
section_header_t section_header = {0, 0};
|
||||||
if (spi_flash_read(pos, (uint32_t*) §ion_header, sizeof(section_header))) {
|
if (spi_flash_read(pos, (uint32_t*) §ion_header, sizeof(section_header)) != SPI_FLASH_RESULT_OK) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
pos += sizeof(section_header);
|
pos += sizeof(section_header);
|
||||||
@ -577,26 +585,27 @@ bool EspClass::flashEraseSector(uint32_t sector) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if PUYA_SUPPORT
|
#if PUYA_SUPPORT
|
||||||
static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
|
static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
return 1; // SPI_FLASH_RESULT_ERR
|
return SPI_FLASH_RESULT_ERR;
|
||||||
}
|
}
|
||||||
// PUYA flash chips need to read existing data, update in memory and write modified data again.
|
// PUYA flash chips need to read existing data, update in memory and write modified data again.
|
||||||
static uint32_t *flash_write_puya_buf = nullptr;
|
static uint32_t *flash_write_puya_buf = nullptr;
|
||||||
int rc = 0;
|
|
||||||
uint32_t* ptr = data;
|
|
||||||
|
|
||||||
if (flash_write_puya_buf == nullptr) {
|
if (flash_write_puya_buf == nullptr) {
|
||||||
flash_write_puya_buf = (uint32_t*) malloc(PUYA_BUFFER_SIZE);
|
flash_write_puya_buf = (uint32_t*) malloc(PUYA_BUFFER_SIZE);
|
||||||
// No need to ever free this, since the flash chip will never change at runtime.
|
// No need to ever free this, since the flash chip will never change at runtime.
|
||||||
if (flash_write_puya_buf == nullptr) {
|
if (flash_write_puya_buf == nullptr) {
|
||||||
// Memory could not be allocated.
|
// Memory could not be allocated.
|
||||||
return 1; // SPI_FLASH_RESULT_ERR
|
return SPI_FLASH_RESULT_ERR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
|
||||||
|
uint32_t* ptr = data;
|
||||||
size_t bytesLeft = size;
|
size_t bytesLeft = size;
|
||||||
uint32_t pos = offset;
|
uint32_t pos = offset;
|
||||||
while (bytesLeft > 0 && rc == 0) {
|
while (bytesLeft > 0 && rc == SPI_FLASH_RESULT_OK) {
|
||||||
size_t bytesNow = bytesLeft;
|
size_t bytesNow = bytesLeft;
|
||||||
if (bytesNow > PUYA_BUFFER_SIZE) {
|
if (bytesNow > PUYA_BUFFER_SIZE) {
|
||||||
bytesNow = PUYA_BUFFER_SIZE;
|
bytesNow = PUYA_BUFFER_SIZE;
|
||||||
@ -605,7 +614,7 @@ static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
|
|||||||
bytesLeft = 0;
|
bytesLeft = 0;
|
||||||
}
|
}
|
||||||
rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow);
|
rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow);
|
||||||
if (rc != 0) {
|
if (rc != SPI_FLASH_RESULT_OK) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < bytesNow / 4; ++i) {
|
for (size_t i = 0; i < bytesNow / 4; ++i) {
|
||||||
@ -620,7 +629,7 @@ static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
|
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
|
||||||
int rc = 0;
|
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
|
||||||
#if PUYA_SUPPORT
|
#if PUYA_SUPPORT
|
||||||
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
|
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
|
||||||
rc = spi_flash_write_puya(offset, data, size);
|
rc = spi_flash_write_puya(offset, data, size);
|
||||||
@ -630,12 +639,12 @@ bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
|
|||||||
{
|
{
|
||||||
rc = spi_flash_write(offset, data, size);
|
rc = spi_flash_write(offset, data, size);
|
||||||
}
|
}
|
||||||
return rc == 0;
|
return rc == SPI_FLASH_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
|
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
|
||||||
int rc = spi_flash_read(offset, (uint32_t*) data, size);
|
auto rc = spi_flash_read(offset, (uint32_t*) data, size);
|
||||||
return rc == 0;
|
return rc == SPI_FLASH_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
String EspClass::getSketchMD5()
|
String EspClass::getSketchMD5()
|
||||||
|
@ -23,15 +23,6 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#ifndef PUYA_SUPPORT
|
|
||||||
#define PUYA_SUPPORT 0
|
|
||||||
#endif
|
|
||||||
#ifndef PUYA_BUFFER_SIZE
|
|
||||||
// Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
|
|
||||||
// Always use a multiple of flash page size (256 bytes)
|
|
||||||
#define PUYA_BUFFER_SIZE 256
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Vendor IDs taken from Flashrom project
|
// Vendor IDs taken from Flashrom project
|
||||||
// https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h?h=1.0.x
|
// https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h?h=1.0.x
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -108,14 +108,16 @@ int HardwareSerial::available(void)
|
|||||||
|
|
||||||
void HardwareSerial::flush()
|
void HardwareSerial::flush()
|
||||||
{
|
{
|
||||||
|
uint8_t bit_length = 0;
|
||||||
if(!_uart || !uart_tx_enabled(_uart)) {
|
if(!_uart || !uart_tx_enabled(_uart)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bit_length = uart_get_bit_length(_uart_nr); // data width, parity and stop
|
||||||
uart_wait_tx_empty(_uart);
|
uart_wait_tx_empty(_uart);
|
||||||
//Workaround for a bug in serial not actually being finished yet
|
//Workaround for a bug in serial not actually being finished yet
|
||||||
//Wait for 8 data bits, 1 parity and 2 stop bits, just in case
|
//Wait for 8 data bits, 1 parity and 2 stop bits, just in case
|
||||||
delayMicroseconds(11000000 / uart_get_baudrate(_uart) + 1);
|
delayMicroseconds(bit_length * 1000000 / uart_get_baudrate(_uart) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HardwareSerial::startDetectBaudrate()
|
void HardwareSerial::startDetectBaudrate()
|
||||||
|
@ -24,16 +24,18 @@ struct recurrent_fn_t
|
|||||||
recurrent_fn_t* mNext = nullptr;
|
recurrent_fn_t* mNext = nullptr;
|
||||||
mRecFuncT mFunc;
|
mRecFuncT mFunc;
|
||||||
esp8266::polledTimeout::periodicFastUs callNow;
|
esp8266::polledTimeout::periodicFastUs callNow;
|
||||||
recurrent_fn_t (esp8266::polledTimeout::periodicFastUs interval): callNow(interval) { }
|
std::function<bool(void)> alarm = nullptr;
|
||||||
|
recurrent_fn_t(esp8266::polledTimeout::periodicFastUs interval) : callNow(interval) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
static recurrent_fn_t* rFirst = nullptr; // fifo not needed
|
static recurrent_fn_t* rFirst = nullptr;
|
||||||
|
static recurrent_fn_t* rLast = nullptr;
|
||||||
|
|
||||||
// Returns a pointer to an unused sched_fn_t,
|
// Returns a pointer to an unused sched_fn_t,
|
||||||
// or if none are available allocates a new one,
|
// or if none are available allocates a new one,
|
||||||
// or nullptr if limit is reached
|
// or nullptr if limit is reached
|
||||||
IRAM_ATTR // called from ISR
|
IRAM_ATTR // called from ISR
|
||||||
static scheduled_fn_t* get_fn_unsafe ()
|
static scheduled_fn_t* get_fn_unsafe()
|
||||||
{
|
{
|
||||||
scheduled_fn_t* result = nullptr;
|
scheduled_fn_t* result = nullptr;
|
||||||
// try to get an item from unused items list
|
// try to get an item from unused items list
|
||||||
@ -52,7 +54,7 @@ static scheduled_fn_t* get_fn_unsafe ()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recycle_fn_unsafe (scheduled_fn_t* fn)
|
static void recycle_fn_unsafe(scheduled_fn_t* fn)
|
||||||
{
|
{
|
||||||
fn->mFunc = nullptr; // special overload in c++ std lib
|
fn->mFunc = nullptr; // special overload in c++ std lib
|
||||||
fn->mNext = sUnused;
|
fn->mNext = sUnused;
|
||||||
@ -60,8 +62,11 @@ static void recycle_fn_unsafe (scheduled_fn_t* fn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
IRAM_ATTR // (not only) called from ISR
|
IRAM_ATTR // (not only) called from ISR
|
||||||
bool schedule_function (const std::function<void(void)>& fn)
|
bool schedule_function(const std::function<void(void)>& fn)
|
||||||
{
|
{
|
||||||
|
if (!fn)
|
||||||
|
return false;
|
||||||
|
|
||||||
esp8266::InterruptLock lockAllInterruptsInThisScope;
|
esp8266::InterruptLock lockAllInterruptsInThisScope;
|
||||||
|
|
||||||
scheduled_fn_t* item = get_fn_unsafe();
|
scheduled_fn_t* item = get_fn_unsafe();
|
||||||
@ -80,27 +85,37 @@ bool schedule_function (const std::function<void(void)>& fn)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool schedule_recurrent_function_us (const std::function<bool(void)>& fn, uint32_t repeat_us)
|
bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,
|
||||||
|
uint32_t repeat_us, const std::function<bool(void)>& alarm)
|
||||||
{
|
{
|
||||||
assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s)
|
assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s)
|
||||||
|
|
||||||
esp8266::InterruptLock lockAllInterruptsInThisScope;
|
if (!fn)
|
||||||
|
return false;
|
||||||
|
|
||||||
recurrent_fn_t* item = new (std::nothrow) recurrent_fn_t(repeat_us);
|
recurrent_fn_t* item = new (std::nothrow) recurrent_fn_t(repeat_us);
|
||||||
if (!item)
|
if (!item)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
item->mFunc = fn;
|
item->mFunc = fn;
|
||||||
|
item->alarm = alarm;
|
||||||
|
|
||||||
if (rFirst)
|
esp8266::InterruptLock lockAllInterruptsInThisScope;
|
||||||
item->mNext = rFirst;
|
|
||||||
|
|
||||||
|
if (rLast)
|
||||||
|
{
|
||||||
|
rLast->mNext = item;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
rFirst = item;
|
rFirst = item;
|
||||||
|
}
|
||||||
|
rLast = item;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_scheduled_functions ()
|
void run_scheduled_functions()
|
||||||
{
|
{
|
||||||
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
|
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
|
||||||
|
|
||||||
@ -128,15 +143,18 @@ void run_scheduled_functions ()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_scheduled_recurrent_functions ()
|
void run_scheduled_recurrent_functions()
|
||||||
{
|
{
|
||||||
|
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
|
||||||
|
|
||||||
// Note to the reader:
|
// Note to the reader:
|
||||||
// There is no exposed API to remove a scheduled function:
|
// There is no exposed API to remove a scheduled function:
|
||||||
// Scheduled functions are removed only from this function, and
|
// Scheduled functions are removed only from this function, and
|
||||||
// its purpose is that it is never called from an interrupt
|
// its purpose is that it is never called from an interrupt
|
||||||
// (always on cont stack).
|
// (always on cont stack).
|
||||||
|
|
||||||
if (!rFirst)
|
auto current = rFirst;
|
||||||
|
if (!current)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
static bool fence = false;
|
static bool fence = false;
|
||||||
@ -153,26 +171,35 @@ void run_scheduled_recurrent_functions ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
recurrent_fn_t* prev = nullptr;
|
recurrent_fn_t* prev = nullptr;
|
||||||
recurrent_fn_t* current = rFirst;
|
// prevent scheduling of new functions during this run
|
||||||
|
auto stop = rLast;
|
||||||
|
|
||||||
while (current)
|
bool done;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
if (current->callNow && !current->mFunc())
|
done = current == stop;
|
||||||
|
const bool wakeup = current->alarm && current->alarm();
|
||||||
|
bool callNow = current->callNow;
|
||||||
|
|
||||||
|
if ((wakeup || callNow) && !current->mFunc())
|
||||||
{
|
{
|
||||||
// remove function from stack
|
// remove function from stack
|
||||||
esp8266::InterruptLock lockAllInterruptsInThisScope;
|
esp8266::InterruptLock lockAllInterruptsInThisScope;
|
||||||
|
|
||||||
auto to_ditch = current;
|
auto to_ditch = current;
|
||||||
|
|
||||||
|
// removing rLast
|
||||||
|
if (rLast == current)
|
||||||
|
rLast = prev;
|
||||||
|
|
||||||
|
current = current->mNext;
|
||||||
if (prev)
|
if (prev)
|
||||||
{
|
{
|
||||||
current = current->mNext;
|
|
||||||
prev->mNext = current;
|
prev->mNext = current;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rFirst = rFirst->mNext;
|
rFirst = current;
|
||||||
current = rFirst;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(to_ditch);
|
delete(to_ditch);
|
||||||
@ -182,7 +209,15 @@ void run_scheduled_recurrent_functions ()
|
|||||||
prev = current;
|
prev = current;
|
||||||
current = current->mNext;
|
current = current->mNext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (yieldNow)
|
||||||
|
{
|
||||||
|
// because scheduled functions might last too long for watchdog etc,
|
||||||
|
// this is yield() in cont stack:
|
||||||
|
esp_schedule();
|
||||||
|
cont_yield(g_pcont);
|
||||||
}
|
}
|
||||||
|
} while (current && !done);
|
||||||
|
|
||||||
fence = false;
|
fence = false;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
// in user stack (called CONT stack) without the common restrictions from
|
// in user stack (called CONT stack) without the common restrictions from
|
||||||
// system context. Details are below.
|
// system context. Details are below.
|
||||||
|
|
||||||
// The purpose of recurrent scheduled function is to independantly execute
|
// The purpose of recurrent scheduled function is to independently execute
|
||||||
// user code in CONT stack on a regular basis.
|
// user code in CONT stack on a regular basis.
|
||||||
// It has been introduced with ethernet service in mind, it can also be used
|
// It has been introduced with ethernet service in mind, it can also be used
|
||||||
// for all libraries in the need of a regular `libdaemon_handlestuff()`.
|
// for all libraries in the need of a regular `libdaemon_handlestuff()`.
|
||||||
@ -58,14 +58,14 @@ void run_scheduled_functions();
|
|||||||
// functions. However a user function returning false will cancel itself.
|
// functions. However a user function returning false will cancel itself.
|
||||||
// * Long running operations or yield() or delay() are not allowed in the
|
// * Long running operations or yield() or delay() are not allowed in the
|
||||||
// recurrent function.
|
// recurrent function.
|
||||||
// * A recurrent function currently must not schedule another recurrent
|
// * If alarm is used, anytime during scheduling when it returns true,
|
||||||
// functions.
|
// any remaining delay from repeat_us is disregarded, and fn is executed.
|
||||||
|
bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,
|
||||||
bool schedule_recurrent_function_us (const std::function<bool(void)>& fn, uint32_t repeat_us);
|
uint32_t repeat_us, const std::function<bool(void)>& alarm = nullptr);
|
||||||
|
|
||||||
// Test recurrence and run recurrent scheduled functions.
|
// Test recurrence and run recurrent scheduled functions.
|
||||||
// (internally called at every `yield()` and `loop()`)
|
// (internally called at every `yield()` and `loop()`)
|
||||||
|
|
||||||
void run_scheduled_recurrent_functions ();
|
void run_scheduled_recurrent_functions();
|
||||||
|
|
||||||
#endif // ESP_SCHEDULE_H
|
#endif // ESP_SCHEDULE_H
|
||||||
|
475
cores/esp8266/TZ.h
Normal file
475
cores/esp8266/TZ.h
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
|
||||||
|
// autogenerated from https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv
|
||||||
|
// by script <esp8266 arduino core>/tools/TZupdate.sh
|
||||||
|
// Mon Sep 9 20:58:30 UTC 2019
|
||||||
|
//
|
||||||
|
// This database is autogenerated from IANA timezone database
|
||||||
|
// https://www.iana.org/time-zones
|
||||||
|
// and can be updated on demand in this repository
|
||||||
|
// or by yourself using the above script
|
||||||
|
|
||||||
|
#ifndef TZDB_H
|
||||||
|
#define TZDB_H
|
||||||
|
|
||||||
|
#define TZ_Africa_Abidjan PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Accra PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Addis_Ababa PSTR("EAT-3")
|
||||||
|
#define TZ_Africa_Algiers PSTR("CET-1")
|
||||||
|
#define TZ_Africa_Asmara PSTR("EAT-3")
|
||||||
|
#define TZ_Africa_Bamako PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Bangui PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_Banjul PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Bissau PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Blantyre PSTR("CAT-2")
|
||||||
|
#define TZ_Africa_Brazzaville PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_Bujumbura PSTR("CAT-2")
|
||||||
|
#define TZ_Africa_Cairo PSTR("EET-2")
|
||||||
|
#define TZ_Africa_Casablanca PSTR("<+01>-1")
|
||||||
|
#define TZ_Africa_Ceuta PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Africa_Conakry PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Dakar PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Dar_es_Salaam PSTR("EAT-3")
|
||||||
|
#define TZ_Africa_Djibouti PSTR("EAT-3")
|
||||||
|
#define TZ_Africa_Douala PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_El_Aaiun PSTR("<+01>-1")
|
||||||
|
#define TZ_Africa_Freetown PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Gaborone PSTR("CAT-2")
|
||||||
|
#define TZ_Africa_Harare PSTR("CAT-2")
|
||||||
|
#define TZ_Africa_Johannesburg PSTR("SAST-2")
|
||||||
|
#define TZ_Africa_Juba PSTR("EAT-3")
|
||||||
|
#define TZ_Africa_Kampala PSTR("EAT-3")
|
||||||
|
#define TZ_Africa_Khartoum PSTR("CAT-2")
|
||||||
|
#define TZ_Africa_Kigali PSTR("CAT-2")
|
||||||
|
#define TZ_Africa_Kinshasa PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_Lagos PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_Libreville PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_Lome PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Luanda PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_Lubumbashi PSTR("CAT-2")
|
||||||
|
#define TZ_Africa_Lusaka PSTR("CAT-2")
|
||||||
|
#define TZ_Africa_Malabo PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_Maputo PSTR("CAT-2")
|
||||||
|
#define TZ_Africa_Maseru PSTR("SAST-2")
|
||||||
|
#define TZ_Africa_Mbabane PSTR("SAST-2")
|
||||||
|
#define TZ_Africa_Mogadishu PSTR("EAT-3")
|
||||||
|
#define TZ_Africa_Monrovia PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Nairobi PSTR("EAT-3")
|
||||||
|
#define TZ_Africa_Ndjamena PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_Niamey PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_Nouakchott PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Ouagadougou PSTR("GMT0")
|
||||||
|
#define TZ_Africa_PortomNovo PSTR("WAT-1")
|
||||||
|
#define TZ_Africa_Sao_Tome PSTR("GMT0")
|
||||||
|
#define TZ_Africa_Tripoli PSTR("EET-2")
|
||||||
|
#define TZ_Africa_Tunis PSTR("CET-1")
|
||||||
|
#define TZ_Africa_Windhoek PSTR("CAT-2")
|
||||||
|
#define TZ_America_Adak PSTR("HST10HDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Anchorage PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Anguilla PSTR("AST4")
|
||||||
|
#define TZ_America_Antigua PSTR("AST4")
|
||||||
|
#define TZ_America_Araguaina PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_Buenos_Aires PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_Catamarca PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_Cordoba PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_Jujuy PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_La_Rioja PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_Mendoza PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_Rio_Gallegos PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_Salta PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_San_Juan PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_San_Luis PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_Tucuman PSTR("<-03>3")
|
||||||
|
#define TZ_America_Argentina_Ushuaia PSTR("<-03>3")
|
||||||
|
#define TZ_America_Aruba PSTR("AST4")
|
||||||
|
#define TZ_America_Asuncion PSTR("<-04>4<-03>,M10.1.0/0,M3.4.0/0")
|
||||||
|
#define TZ_America_Atikokan PSTR("EST5")
|
||||||
|
#define TZ_America_Bahia PSTR("<-03>3")
|
||||||
|
#define TZ_America_Bahia_Banderas PSTR("CST6CDT,M4.1.0,M10.5.0")
|
||||||
|
#define TZ_America_Barbados PSTR("AST4")
|
||||||
|
#define TZ_America_Belem PSTR("<-03>3")
|
||||||
|
#define TZ_America_Belize PSTR("CST6")
|
||||||
|
#define TZ_America_BlancmSablon PSTR("AST4")
|
||||||
|
#define TZ_America_Boa_Vista PSTR("<-04>4")
|
||||||
|
#define TZ_America_Bogota PSTR("<-05>5")
|
||||||
|
#define TZ_America_Boise PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Cambridge_Bay PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Campo_Grande PSTR("<-04>4")
|
||||||
|
#define TZ_America_Cancun PSTR("EST5")
|
||||||
|
#define TZ_America_Caracas PSTR("<-04>4")
|
||||||
|
#define TZ_America_Cayenne PSTR("<-03>3")
|
||||||
|
#define TZ_America_Cayman PSTR("EST5")
|
||||||
|
#define TZ_America_Chicago PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Chihuahua PSTR("MST7MDT,M4.1.0,M10.5.0")
|
||||||
|
#define TZ_America_Costa_Rica PSTR("CST6")
|
||||||
|
#define TZ_America_Creston PSTR("MST7")
|
||||||
|
#define TZ_America_Cuiaba PSTR("<-04>4")
|
||||||
|
#define TZ_America_Curacao PSTR("AST4")
|
||||||
|
#define TZ_America_Danmarkshavn PSTR("GMT0")
|
||||||
|
#define TZ_America_Dawson PSTR("PST8PDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Dawson_Creek PSTR("MST7")
|
||||||
|
#define TZ_America_Denver PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Detroit PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Dominica PSTR("AST4")
|
||||||
|
#define TZ_America_Edmonton PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Eirunepe PSTR("<-05>5")
|
||||||
|
#define TZ_America_El_Salvador PSTR("CST6")
|
||||||
|
#define TZ_America_Fortaleza PSTR("<-03>3")
|
||||||
|
#define TZ_America_Fort_Nelson PSTR("MST7")
|
||||||
|
#define TZ_America_Glace_Bay PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Godthab PSTR("<-03>3<-02>,M3.5.0/-2,M10.5.0/-1")
|
||||||
|
#define TZ_America_Goose_Bay PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Grand_Turk PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Grenada PSTR("AST4")
|
||||||
|
#define TZ_America_Guadeloupe PSTR("AST4")
|
||||||
|
#define TZ_America_Guatemala PSTR("CST6")
|
||||||
|
#define TZ_America_Guayaquil PSTR("<-05>5")
|
||||||
|
#define TZ_America_Guyana PSTR("<-04>4")
|
||||||
|
#define TZ_America_Halifax PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Havana PSTR("CST5CDT,M3.2.0/0,M11.1.0/1")
|
||||||
|
#define TZ_America_Hermosillo PSTR("MST7")
|
||||||
|
#define TZ_America_Indiana_Indianapolis PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Indiana_Knox PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Indiana_Marengo PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Indiana_Petersburg PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Indiana_Tell_City PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Indiana_Vevay PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Indiana_Vincennes PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Indiana_Winamac PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Inuvik PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Iqaluit PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Jamaica PSTR("EST5")
|
||||||
|
#define TZ_America_Juneau PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Kentucky_Louisville PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Kentucky_Monticello PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Kralendijk PSTR("AST4")
|
||||||
|
#define TZ_America_La_Paz PSTR("<-04>4")
|
||||||
|
#define TZ_America_Lima PSTR("<-05>5")
|
||||||
|
#define TZ_America_Los_Angeles PSTR("PST8PDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Lower_Princes PSTR("AST4")
|
||||||
|
#define TZ_America_Maceio PSTR("<-03>3")
|
||||||
|
#define TZ_America_Managua PSTR("CST6")
|
||||||
|
#define TZ_America_Manaus PSTR("<-04>4")
|
||||||
|
#define TZ_America_Marigot PSTR("AST4")
|
||||||
|
#define TZ_America_Martinique PSTR("AST4")
|
||||||
|
#define TZ_America_Matamoros PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Mazatlan PSTR("MST7MDT,M4.1.0,M10.5.0")
|
||||||
|
#define TZ_America_Menominee PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Merida PSTR("CST6CDT,M4.1.0,M10.5.0")
|
||||||
|
#define TZ_America_Metlakatla PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Mexico_City PSTR("CST6CDT,M4.1.0,M10.5.0")
|
||||||
|
#define TZ_America_Miquelon PSTR("<-03>3<-02>,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Moncton PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Monterrey PSTR("CST6CDT,M4.1.0,M10.5.0")
|
||||||
|
#define TZ_America_Montevideo PSTR("<-03>3")
|
||||||
|
#define TZ_America_Montreal PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Montserrat PSTR("AST4")
|
||||||
|
#define TZ_America_Nassau PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_New_York PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Nipigon PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Nome PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Noronha PSTR("<-02>2")
|
||||||
|
#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_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")
|
||||||
|
#define TZ_America_Paramaribo PSTR("<-03>3")
|
||||||
|
#define TZ_America_Phoenix PSTR("MST7")
|
||||||
|
#define TZ_America_PortmaumPrince PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Port_of_Spain PSTR("AST4")
|
||||||
|
#define TZ_America_Porto_Velho PSTR("<-04>4")
|
||||||
|
#define TZ_America_Puerto_Rico PSTR("AST4")
|
||||||
|
#define TZ_America_Punta_Arenas PSTR("<-03>3")
|
||||||
|
#define TZ_America_Rainy_River PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Rankin_Inlet PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Recife PSTR("<-03>3")
|
||||||
|
#define TZ_America_Regina PSTR("CST6")
|
||||||
|
#define TZ_America_Resolute PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Rio_Branco PSTR("<-05>5")
|
||||||
|
#define TZ_America_Santarem PSTR("<-03>3")
|
||||||
|
#define TZ_America_Santiago PSTR("<-04>4<-03>,M9.1.6/24,M4.1.6/24")
|
||||||
|
#define TZ_America_Santo_Domingo PSTR("AST4")
|
||||||
|
#define TZ_America_Sao_Paulo PSTR("<-03>3")
|
||||||
|
#define TZ_America_Scoresbysund PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
|
||||||
|
#define TZ_America_Sitka PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_St_Barthelemy PSTR("AST4")
|
||||||
|
#define TZ_America_St_Johns PSTR("NST3:30NDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_St_Kitts PSTR("AST4")
|
||||||
|
#define TZ_America_St_Lucia PSTR("AST4")
|
||||||
|
#define TZ_America_St_Thomas PSTR("AST4")
|
||||||
|
#define TZ_America_St_Vincent PSTR("AST4")
|
||||||
|
#define TZ_America_Swift_Current PSTR("CST6")
|
||||||
|
#define TZ_America_Tegucigalpa PSTR("CST6")
|
||||||
|
#define TZ_America_Thule PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Thunder_Bay PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Tijuana PSTR("PST8PDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Toronto PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Tortola PSTR("AST4")
|
||||||
|
#define TZ_America_Vancouver PSTR("PST8PDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Whitehorse PSTR("PST8PDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Winnipeg PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Yakutat PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_America_Yellowknife PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_Antarctica_Casey PSTR("<+08>-8")
|
||||||
|
#define TZ_Antarctica_Davis PSTR("<+07>-7")
|
||||||
|
#define TZ_Antarctica_DumontDUrville PSTR("<+10>-10")
|
||||||
|
#define TZ_Antarctica_Macquarie PSTR("<+11>-11")
|
||||||
|
#define TZ_Antarctica_Mawson PSTR("<+05>-5")
|
||||||
|
#define TZ_Antarctica_McMurdo PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3")
|
||||||
|
#define TZ_Antarctica_Palmer PSTR("<-03>3")
|
||||||
|
#define TZ_Antarctica_Rothera PSTR("<-03>3")
|
||||||
|
#define TZ_Antarctica_Syowa PSTR("<+03>-3")
|
||||||
|
#define TZ_Antarctica_Troll PSTR("<+00>0<+02>-2,M3.5.0/1,M10.5.0/3")
|
||||||
|
#define TZ_Antarctica_Vostok PSTR("<+06>-6")
|
||||||
|
#define TZ_Arctic_Longyearbyen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Asia_Aden PSTR("<+03>-3")
|
||||||
|
#define TZ_Asia_Almaty PSTR("<+06>-6")
|
||||||
|
#define TZ_Asia_Amman PSTR("EET-2EEST,M3.5.4/24,M10.5.5/1")
|
||||||
|
#define TZ_Asia_Anadyr PSTR("<+12>-12")
|
||||||
|
#define TZ_Asia_Aqtau PSTR("<+05>-5")
|
||||||
|
#define TZ_Asia_Aqtobe PSTR("<+05>-5")
|
||||||
|
#define TZ_Asia_Ashgabat PSTR("<+05>-5")
|
||||||
|
#define TZ_Asia_Atyrau PSTR("<+05>-5")
|
||||||
|
#define TZ_Asia_Baghdad PSTR("<+03>-3")
|
||||||
|
#define TZ_Asia_Bahrain PSTR("<+03>-3")
|
||||||
|
#define TZ_Asia_Baku PSTR("<+04>-4")
|
||||||
|
#define TZ_Asia_Bangkok PSTR("<+07>-7")
|
||||||
|
#define TZ_Asia_Barnaul PSTR("<+07>-7")
|
||||||
|
#define TZ_Asia_Beirut PSTR("EET-2EEST,M3.5.0/0,M10.5.0/0")
|
||||||
|
#define TZ_Asia_Bishkek PSTR("<+06>-6")
|
||||||
|
#define TZ_Asia_Brunei PSTR("<+08>-8")
|
||||||
|
#define TZ_Asia_Chita PSTR("<+09>-9")
|
||||||
|
#define TZ_Asia_Choibalsan PSTR("<+08>-8")
|
||||||
|
#define TZ_Asia_Colombo PSTR("<+0530>-5:30")
|
||||||
|
#define TZ_Asia_Damascus PSTR("EET-2EEST,M3.5.5/0,M10.5.5/0")
|
||||||
|
#define TZ_Asia_Dhaka PSTR("<+06>-6")
|
||||||
|
#define TZ_Asia_Dili PSTR("<+09>-9")
|
||||||
|
#define TZ_Asia_Dubai PSTR("<+04>-4")
|
||||||
|
#define TZ_Asia_Dushanbe PSTR("<+05>-5")
|
||||||
|
#define TZ_Asia_Famagusta PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Asia_Gaza PSTR("EET-2EEST,M3.5.5/0,M10.5.6/1")
|
||||||
|
#define TZ_Asia_Hebron PSTR("EET-2EEST,M3.5.5/0,M10.5.6/1")
|
||||||
|
#define TZ_Asia_Ho_Chi_Minh PSTR("<+07>-7")
|
||||||
|
#define TZ_Asia_Hong_Kong PSTR("HKT-8")
|
||||||
|
#define TZ_Asia_Hovd PSTR("<+07>-7")
|
||||||
|
#define TZ_Asia_Irkutsk PSTR("<+08>-8")
|
||||||
|
#define TZ_Asia_Jakarta PSTR("WIB-7")
|
||||||
|
#define TZ_Asia_Jayapura PSTR("WIT-9")
|
||||||
|
#define TZ_Asia_Jerusalem PSTR("IST-2IDT,M3.4.4/26,M10.5.0")
|
||||||
|
#define TZ_Asia_Kabul PSTR("<+0430>-4:30")
|
||||||
|
#define TZ_Asia_Kamchatka PSTR("<+12>-12")
|
||||||
|
#define TZ_Asia_Karachi PSTR("PKT-5")
|
||||||
|
#define TZ_Asia_Kathmandu PSTR("<+0545>-5:45")
|
||||||
|
#define TZ_Asia_Khandyga PSTR("<+09>-9")
|
||||||
|
#define TZ_Asia_Kolkata PSTR("IST-5:30")
|
||||||
|
#define TZ_Asia_Krasnoyarsk PSTR("<+07>-7")
|
||||||
|
#define TZ_Asia_Kuala_Lumpur PSTR("<+08>-8")
|
||||||
|
#define TZ_Asia_Kuching PSTR("<+08>-8")
|
||||||
|
#define TZ_Asia_Kuwait PSTR("<+03>-3")
|
||||||
|
#define TZ_Asia_Macau PSTR("CST-8")
|
||||||
|
#define TZ_Asia_Magadan PSTR("<+11>-11")
|
||||||
|
#define TZ_Asia_Makassar PSTR("WITA-8")
|
||||||
|
#define TZ_Asia_Manila PSTR("PST-8")
|
||||||
|
#define TZ_Asia_Muscat PSTR("<+04>-4")
|
||||||
|
#define TZ_Asia_Nicosia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Asia_Novokuznetsk PSTR("<+07>-7")
|
||||||
|
#define TZ_Asia_Novosibirsk PSTR("<+07>-7")
|
||||||
|
#define TZ_Asia_Omsk PSTR("<+06>-6")
|
||||||
|
#define TZ_Asia_Oral PSTR("<+05>-5")
|
||||||
|
#define TZ_Asia_Phnom_Penh PSTR("<+07>-7")
|
||||||
|
#define TZ_Asia_Pontianak PSTR("WIB-7")
|
||||||
|
#define TZ_Asia_Pyongyang PSTR("KST-9")
|
||||||
|
#define TZ_Asia_Qatar PSTR("<+03>-3")
|
||||||
|
#define TZ_Asia_Qyzylorda PSTR("<+05>-5")
|
||||||
|
#define TZ_Asia_Riyadh PSTR("<+03>-3")
|
||||||
|
#define TZ_Asia_Sakhalin PSTR("<+11>-11")
|
||||||
|
#define TZ_Asia_Samarkand PSTR("<+05>-5")
|
||||||
|
#define TZ_Asia_Seoul PSTR("KST-9")
|
||||||
|
#define TZ_Asia_Shanghai PSTR("CST-8")
|
||||||
|
#define TZ_Asia_Singapore PSTR("<+08>-8")
|
||||||
|
#define TZ_Asia_Srednekolymsk PSTR("<+11>-11")
|
||||||
|
#define TZ_Asia_Taipei PSTR("CST-8")
|
||||||
|
#define TZ_Asia_Tashkent PSTR("<+05>-5")
|
||||||
|
#define TZ_Asia_Tbilisi PSTR("<+04>-4")
|
||||||
|
#define TZ_Asia_Tehran PSTR("<+0330>-3:30<+0430>,J79/24,J263/24")
|
||||||
|
#define TZ_Asia_Thimphu PSTR("<+06>-6")
|
||||||
|
#define TZ_Asia_Tokyo PSTR("JST-9")
|
||||||
|
#define TZ_Asia_Tomsk PSTR("<+07>-7")
|
||||||
|
#define TZ_Asia_Ulaanbaatar PSTR("<+08>-8")
|
||||||
|
#define TZ_Asia_Urumqi PSTR("<+06>-6")
|
||||||
|
#define TZ_Asia_UstmNera PSTR("<+10>-10")
|
||||||
|
#define TZ_Asia_Vientiane PSTR("<+07>-7")
|
||||||
|
#define TZ_Asia_Vladivostok PSTR("<+10>-10")
|
||||||
|
#define TZ_Asia_Yakutsk PSTR("<+09>-9")
|
||||||
|
#define TZ_Asia_Yangon PSTR("<+0630>-6:30")
|
||||||
|
#define TZ_Asia_Yekaterinburg PSTR("<+05>-5")
|
||||||
|
#define TZ_Asia_Yerevan PSTR("<+04>-4")
|
||||||
|
#define TZ_Atlantic_Azores PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
|
||||||
|
#define TZ_Atlantic_Bermuda PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||||
|
#define TZ_Atlantic_Canary PSTR("WET0WEST,M3.5.0/1,M10.5.0")
|
||||||
|
#define TZ_Atlantic_Cape_Verde PSTR("<-01>1")
|
||||||
|
#define TZ_Atlantic_Faroe PSTR("WET0WEST,M3.5.0/1,M10.5.0")
|
||||||
|
#define TZ_Atlantic_Madeira PSTR("WET0WEST,M3.5.0/1,M10.5.0")
|
||||||
|
#define TZ_Atlantic_Reykjavik PSTR("GMT0")
|
||||||
|
#define TZ_Atlantic_South_Georgia PSTR("<-02>2")
|
||||||
|
#define TZ_Atlantic_Stanley PSTR("<-03>3")
|
||||||
|
#define TZ_Atlantic_St_Helena PSTR("GMT0")
|
||||||
|
#define TZ_Australia_Adelaide PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
|
||||||
|
#define TZ_Australia_Brisbane PSTR("AEST-10")
|
||||||
|
#define TZ_Australia_Broken_Hill PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
|
||||||
|
#define TZ_Australia_Currie PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||||
|
#define TZ_Australia_Darwin PSTR("ACST-9:30")
|
||||||
|
#define TZ_Australia_Eucla PSTR("<+0845>-8:45")
|
||||||
|
#define TZ_Australia_Hobart PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||||
|
#define TZ_Australia_Lindeman PSTR("AEST-10")
|
||||||
|
#define TZ_Australia_Lord_Howe PSTR("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0")
|
||||||
|
#define TZ_Australia_Melbourne PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||||
|
#define TZ_Australia_Perth PSTR("AWST-8")
|
||||||
|
#define TZ_Australia_Sydney PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||||
|
#define TZ_Europe_Amsterdam PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Andorra PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Astrakhan PSTR("<+04>-4")
|
||||||
|
#define TZ_Europe_Athens PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Europe_Belgrade PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Berlin PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Bratislava PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Brussels PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Bucharest PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Europe_Budapest PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Busingen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Chisinau PSTR("EET-2EEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Copenhagen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Dublin PSTR("IST-1GMT0,M10.5.0,M3.5.0/1")
|
||||||
|
#define TZ_Europe_Gibraltar PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Guernsey PSTR("GMT0BST,M3.5.0/1,M10.5.0")
|
||||||
|
#define TZ_Europe_Helsinki PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Europe_Isle_of_Man PSTR("GMT0BST,M3.5.0/1,M10.5.0")
|
||||||
|
#define TZ_Europe_Istanbul PSTR("<+03>-3")
|
||||||
|
#define TZ_Europe_Jersey PSTR("GMT0BST,M3.5.0/1,M10.5.0")
|
||||||
|
#define TZ_Europe_Kaliningrad PSTR("EET-2")
|
||||||
|
#define TZ_Europe_Kiev PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Europe_Kirov PSTR("<+03>-3")
|
||||||
|
#define TZ_Europe_Lisbon PSTR("WET0WEST,M3.5.0/1,M10.5.0")
|
||||||
|
#define TZ_Europe_Ljubljana PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_London PSTR("GMT0BST,M3.5.0/1,M10.5.0")
|
||||||
|
#define TZ_Europe_Luxembourg PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Madrid PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Malta PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Mariehamn PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Europe_Minsk PSTR("<+03>-3")
|
||||||
|
#define TZ_Europe_Monaco PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Moscow PSTR("MSK-3")
|
||||||
|
#define TZ_Europe_Oslo PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Paris PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Podgorica PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Prague PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Riga PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Europe_Rome PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Samara PSTR("<+04>-4")
|
||||||
|
#define TZ_Europe_San_Marino PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Sarajevo PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Saratov PSTR("<+04>-4")
|
||||||
|
#define TZ_Europe_Simferopol PSTR("MSK-3")
|
||||||
|
#define TZ_Europe_Skopje PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Sofia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Europe_Stockholm PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Tallinn PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Europe_Tirane PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Ulyanovsk PSTR("<+04>-4")
|
||||||
|
#define TZ_Europe_Uzhgorod PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Europe_Vaduz PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Vatican PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Vienna PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Europe_Vilnius PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||||
|
#define TZ_Europe_Volgograd PSTR("<+04>-4")
|
||||||
|
#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")
|
||||||
|
#define TZ_Europe_Zurich PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||||
|
#define TZ_Indian_Antananarivo PSTR("EAT-3")
|
||||||
|
#define TZ_Indian_Chagos PSTR("<+06>-6")
|
||||||
|
#define TZ_Indian_Christmas PSTR("<+07>-7")
|
||||||
|
#define TZ_Indian_Cocos PSTR("<+0630>-6:30")
|
||||||
|
#define TZ_Indian_Comoro PSTR("EAT-3")
|
||||||
|
#define TZ_Indian_Kerguelen PSTR("<+05>-5")
|
||||||
|
#define TZ_Indian_Mahe PSTR("<+04>-4")
|
||||||
|
#define TZ_Indian_Maldives PSTR("<+05>-5")
|
||||||
|
#define TZ_Indian_Mauritius PSTR("<+04>-4")
|
||||||
|
#define TZ_Indian_Mayotte PSTR("EAT-3")
|
||||||
|
#define TZ_Indian_Reunion PSTR("<+04>-4")
|
||||||
|
#define TZ_Pacific_Apia PSTR("<+13>-13<+14>,M9.5.0/3,M4.1.0/4")
|
||||||
|
#define TZ_Pacific_Auckland PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3")
|
||||||
|
#define TZ_Pacific_Bougainville PSTR("<+11>-11")
|
||||||
|
#define TZ_Pacific_Chatham PSTR("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45")
|
||||||
|
#define TZ_Pacific_Chuuk PSTR("<+10>-10")
|
||||||
|
#define TZ_Pacific_Easter PSTR("<-06>6<-05>,M9.1.6/22,M4.1.6/22")
|
||||||
|
#define TZ_Pacific_Efate PSTR("<+11>-11")
|
||||||
|
#define TZ_Pacific_Enderbury PSTR("<+13>-13")
|
||||||
|
#define TZ_Pacific_Fakaofo PSTR("<+13>-13")
|
||||||
|
#define TZ_Pacific_Fiji PSTR("<+12>-12<+13>,M11.1.0,M1.2.2/123")
|
||||||
|
#define TZ_Pacific_Funafuti PSTR("<+12>-12")
|
||||||
|
#define TZ_Pacific_Galapagos PSTR("<-06>6")
|
||||||
|
#define TZ_Pacific_Gambier PSTR("<-09>9")
|
||||||
|
#define TZ_Pacific_Guadalcanal PSTR("<+11>-11")
|
||||||
|
#define TZ_Pacific_Guam PSTR("ChST-10")
|
||||||
|
#define TZ_Pacific_Honolulu PSTR("HST10")
|
||||||
|
#define TZ_Pacific_Kiritimati PSTR("<+14>-14")
|
||||||
|
#define TZ_Pacific_Kosrae PSTR("<+11>-11")
|
||||||
|
#define TZ_Pacific_Kwajalein PSTR("<+12>-12")
|
||||||
|
#define TZ_Pacific_Majuro PSTR("<+12>-12")
|
||||||
|
#define TZ_Pacific_Marquesas PSTR("<-0930>9:30")
|
||||||
|
#define TZ_Pacific_Midway PSTR("SST11")
|
||||||
|
#define TZ_Pacific_Nauru PSTR("<+12>-12")
|
||||||
|
#define TZ_Pacific_Niue PSTR("<-11>11")
|
||||||
|
#define TZ_Pacific_Norfolk PSTR("<+11>-11")
|
||||||
|
#define TZ_Pacific_Noumea PSTR("<+11>-11")
|
||||||
|
#define TZ_Pacific_Pago_Pago PSTR("SST11")
|
||||||
|
#define TZ_Pacific_Palau PSTR("<+09>-9")
|
||||||
|
#define TZ_Pacific_Pitcairn PSTR("<-08>8")
|
||||||
|
#define TZ_Pacific_Pohnpei PSTR("<+11>-11")
|
||||||
|
#define TZ_Pacific_Port_Moresby PSTR("<+10>-10")
|
||||||
|
#define TZ_Pacific_Rarotonga PSTR("<-10>10")
|
||||||
|
#define TZ_Pacific_Saipan PSTR("ChST-10")
|
||||||
|
#define TZ_Pacific_Tahiti PSTR("<-10>10")
|
||||||
|
#define TZ_Pacific_Tarawa PSTR("<+12>-12")
|
||||||
|
#define TZ_Pacific_Tongatapu PSTR("<+13>-13")
|
||||||
|
#define TZ_Pacific_Wake PSTR("<+12>-12")
|
||||||
|
#define TZ_Pacific_Wallis PSTR("<+12>-12")
|
||||||
|
#define TZ_Etc_GMT PSTR("GMT0")
|
||||||
|
#define TZ_Etc_GMTm0 PSTR("GMT0")
|
||||||
|
#define TZ_Etc_GMTm1 PSTR("<+01>-1")
|
||||||
|
#define TZ_Etc_GMTm2 PSTR("<+02>-2")
|
||||||
|
#define TZ_Etc_GMTm3 PSTR("<+03>-3")
|
||||||
|
#define TZ_Etc_GMTm4 PSTR("<+04>-4")
|
||||||
|
#define TZ_Etc_GMTm5 PSTR("<+05>-5")
|
||||||
|
#define TZ_Etc_GMTm6 PSTR("<+06>-6")
|
||||||
|
#define TZ_Etc_GMTm7 PSTR("<+07>-7")
|
||||||
|
#define TZ_Etc_GMTm8 PSTR("<+08>-8")
|
||||||
|
#define TZ_Etc_GMTm9 PSTR("<+09>-9")
|
||||||
|
#define TZ_Etc_GMTm10 PSTR("<+10>-10")
|
||||||
|
#define TZ_Etc_GMTm11 PSTR("<+11>-11")
|
||||||
|
#define TZ_Etc_GMTm12 PSTR("<+12>-12")
|
||||||
|
#define TZ_Etc_GMTm13 PSTR("<+13>-13")
|
||||||
|
#define TZ_Etc_GMTm14 PSTR("<+14>-14")
|
||||||
|
#define TZ_Etc_GMT0 PSTR("GMT0")
|
||||||
|
#define TZ_Etc_GMTp0 PSTR("GMT0")
|
||||||
|
#define TZ_Etc_GMTp1 PSTR("<-01>1")
|
||||||
|
#define TZ_Etc_GMTp2 PSTR("<-02>2")
|
||||||
|
#define TZ_Etc_GMTp3 PSTR("<-03>3")
|
||||||
|
#define TZ_Etc_GMTp4 PSTR("<-04>4")
|
||||||
|
#define TZ_Etc_GMTp5 PSTR("<-05>5")
|
||||||
|
#define TZ_Etc_GMTp6 PSTR("<-06>6")
|
||||||
|
#define TZ_Etc_GMTp7 PSTR("<-07>7")
|
||||||
|
#define TZ_Etc_GMTp8 PSTR("<-08>8")
|
||||||
|
#define TZ_Etc_GMTp9 PSTR("<-09>9")
|
||||||
|
#define TZ_Etc_GMTp10 PSTR("<-10>10")
|
||||||
|
#define TZ_Etc_GMTp11 PSTR("<-11>11")
|
||||||
|
#define TZ_Etc_GMTp12 PSTR("<-12>12")
|
||||||
|
#define TZ_Etc_UCT PSTR("UTC0")
|
||||||
|
#define TZ_Etc_UTC PSTR("UTC0")
|
||||||
|
#define TZ_Etc_Greenwich PSTR("GMT0")
|
||||||
|
#define TZ_Etc_Universal PSTR("UTC0")
|
||||||
|
#define TZ_Etc_Zulu PSTR("UTC0")
|
||||||
|
|
||||||
|
#endif // TZDB_H
|
@ -23,6 +23,7 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" uint32_t _FS_start;
|
extern "C" uint32_t _FS_start;
|
||||||
|
extern "C" uint32_t _FS_end;
|
||||||
|
|
||||||
UpdaterClass::UpdaterClass()
|
UpdaterClass::UpdaterClass()
|
||||||
: _async(false)
|
: _async(false)
|
||||||
@ -105,15 +106,17 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
|||||||
|
|
||||||
wifi_set_sleep_type(NONE_SLEEP_T);
|
wifi_set_sleep_type(NONE_SLEEP_T);
|
||||||
|
|
||||||
|
//address where we will start writing the update
|
||||||
uintptr_t updateStartAddress = 0;
|
uintptr_t updateStartAddress = 0;
|
||||||
if (command == U_FLASH) {
|
|
||||||
//size of current sketch rounded to a sector
|
//size of current sketch rounded to a sector
|
||||||
size_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
|
size_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
|
||||||
//address of the end of the space available for sketch and update
|
|
||||||
uintptr_t updateEndAddress = (uintptr_t)&_FS_start - 0x40200000;
|
|
||||||
//size of the update rounded to a sector
|
//size of the update rounded to a sector
|
||||||
size_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
|
size_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
|
||||||
//address where we will start writing the update
|
|
||||||
|
if (command == U_FLASH) {
|
||||||
|
//address of the end of the space available for sketch and update
|
||||||
|
uintptr_t updateEndAddress = (uintptr_t)&_FS_start - 0x40200000;
|
||||||
|
|
||||||
updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0;
|
updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0;
|
||||||
|
|
||||||
#ifdef DEBUG_UPDATER
|
#ifdef DEBUG_UPDATER
|
||||||
@ -129,7 +132,24 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (command == U_FS) {
|
else if (command == U_FS) {
|
||||||
|
if((uintptr_t)&_FS_start + roundedSize > (uintptr_t)&_FS_end) {
|
||||||
|
_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;
|
||||||
|
|
||||||
|
updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0;
|
||||||
|
|
||||||
|
if(updateStartAddress < currentSketchSize) {
|
||||||
|
_setError(UPDATE_ERROR_SPACE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
updateStartAddress = (uintptr_t)&_FS_start - 0x40200000;
|
updateStartAddress = (uintptr_t)&_FS_start - 0x40200000;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// unknown command
|
// unknown command
|
||||||
@ -272,8 +292,19 @@ bool UpdaterClass::end(bool evenIfRemaining){
|
|||||||
|
|
||||||
#ifdef DEBUG_UPDATER
|
#ifdef DEBUG_UPDATER
|
||||||
DEBUG_UPDATER.printf_P(PSTR("Staged: address:0x%08X, size:0x%08zX\n"), _startAddress, _size);
|
DEBUG_UPDATER.printf_P(PSTR("Staged: address:0x%08X, size:0x%08zX\n"), _startAddress, _size);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else if (_command == U_FS) {
|
else if (_command == U_FS) {
|
||||||
|
#ifdef ATOMIC_FS_UPDATE
|
||||||
|
eboot_command ebcmd;
|
||||||
|
ebcmd.action = ACTION_COPY_RAW;
|
||||||
|
ebcmd.args[0] = _startAddress;
|
||||||
|
ebcmd.args[1] = (uintptr_t)&_FS_start - 0x40200000;
|
||||||
|
ebcmd.args[2] = _size;
|
||||||
|
eboot_command_write(&ebcmd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG_UPDATER
|
||||||
DEBUG_UPDATER.printf_P(PSTR("Filesystem: address:0x%08X, size:0x%08zX\n"), _startAddress, _size);
|
DEBUG_UPDATER.printf_P(PSTR("Filesystem: address:0x%08X, size:0x%08zX\n"), _startAddress, _size);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -387,7 +418,7 @@ bool UpdaterClass::_verifyHeader(uint8_t data) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if(_command == U_FS) {
|
} else if(_command == U_FS) {
|
||||||
// no check of SPIFFS possible with first byte.
|
// no check of FS possible with first byte.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -421,7 +452,7 @@ bool UpdaterClass::_verifyEnd() {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if(_command == U_FS) {
|
} else if(_command == U_FS) {
|
||||||
// SPIFFS is already over written checks make no sense any more.
|
// FS is already over written checks make no sense any more.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -129,10 +129,9 @@ String::~String() {
|
|||||||
// /*********************************************/
|
// /*********************************************/
|
||||||
|
|
||||||
inline void String::init(void) {
|
inline void String::init(void) {
|
||||||
setSSO(false);
|
setSSO(true);
|
||||||
setCapacity(0);
|
|
||||||
setLen(0);
|
setLen(0);
|
||||||
setBuffer(nullptr);
|
wbuffer()[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void String::invalidate(void) {
|
void String::invalidate(void) {
|
||||||
|
@ -53,7 +53,7 @@ class String {
|
|||||||
// if the initial value is null or invalid, or if memory allocation
|
// if the initial value is null or invalid, or if memory allocation
|
||||||
// fails, the string will be marked as invalid (i.e. "if (s)" will
|
// fails, the string will be marked as invalid (i.e. "if (s)" will
|
||||||
// be false).
|
// be false).
|
||||||
String(const char *cstr = "");
|
String(const char *cstr = nullptr);
|
||||||
String(const String &str);
|
String(const String &str);
|
||||||
String(const __FlashStringHelper *str);
|
String(const __FlashStringHelper *str);
|
||||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||||
|
@ -31,11 +31,11 @@ extern "C" {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* convert input data to base64
|
* convert input data to base64
|
||||||
* @param data uint8_t *
|
* @param data const uint8_t *
|
||||||
* @param length size_t
|
* @param length size_t
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
String base64::encode(uint8_t * data, size_t length, bool doNewLines) {
|
String base64::encode(const uint8_t * data, size_t length, bool doNewLines) {
|
||||||
// base64 needs more size then the source data, use cencode.h macros
|
// base64 needs more size then the source data, use cencode.h macros
|
||||||
size_t size = ((doNewLines ? base64_encode_expected_len(length)
|
size_t size = ((doNewLines ? base64_encode_expected_len(length)
|
||||||
: base64_encode_expected_len_nonewlines(length)) + 1);
|
: base64_encode_expected_len_nonewlines(length)) + 1);
|
||||||
@ -62,10 +62,10 @@ String base64::encode(uint8_t * data, size_t length, bool doNewLines) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* convert input data to base64
|
* convert input data to base64
|
||||||
* @param text String
|
* @param text const String&
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
String base64::encode(String text, bool doNewLines) {
|
String base64::encode(const String& text, bool doNewLines) {
|
||||||
return base64::encode((uint8_t *) text.c_str(), text.length(), doNewLines);
|
return base64::encode((const uint8_t *) text.c_str(), text.length(), doNewLines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@ class base64 {
|
|||||||
// NOTE: The default behaviour of backend (lib64)
|
// NOTE: The default behaviour of backend (lib64)
|
||||||
// is to add a newline every 72 (encoded) characters output.
|
// is to add a newline every 72 (encoded) characters output.
|
||||||
// This may 'break' longer uris and json variables
|
// This may 'break' longer uris and json variables
|
||||||
static String encode(uint8_t * data, size_t length, bool doNewLines = true);
|
static String encode(const uint8_t * data, size_t length, bool doNewLines = true);
|
||||||
static String encode(String text, bool doNewLines = true);
|
static String encode(const String& text, bool doNewLines = true);
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.text
|
.section .irom0.text
|
||||||
.align 4
|
.align 4
|
||||||
.literal_position
|
.literal_position
|
||||||
.global cont_yield
|
.global cont_yield
|
||||||
@ -84,7 +84,7 @@ cont_wrapper:
|
|||||||
|
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
|
|
||||||
.text
|
.section .irom0.text
|
||||||
.align 4
|
.align 4
|
||||||
.literal_position
|
.literal_position
|
||||||
.global cont_run
|
.global cont_run
|
||||||
|
@ -62,6 +62,10 @@ static int s_user_reset_reason = REASON_DEFAULT_RST;
|
|||||||
// From UMM, the last caller of a malloc/realloc/calloc which failed:
|
// From UMM, the last caller of a malloc/realloc/calloc which failed:
|
||||||
extern void *umm_last_fail_alloc_addr;
|
extern void *umm_last_fail_alloc_addr;
|
||||||
extern int umm_last_fail_alloc_size;
|
extern int umm_last_fail_alloc_size;
|
||||||
|
#if defined(DEBUG_ESP_OOM)
|
||||||
|
extern const char *umm_last_fail_alloc_file;
|
||||||
|
extern int umm_last_fail_alloc_line;
|
||||||
|
#endif
|
||||||
|
|
||||||
static void raise_exception() __attribute__((noreturn));
|
static void raise_exception() __attribute__((noreturn));
|
||||||
|
|
||||||
@ -108,8 +112,7 @@ void __wrap_system_restart_local() {
|
|||||||
else
|
else
|
||||||
rst_info.reason = s_user_reset_reason;
|
rst_info.reason = s_user_reset_reason;
|
||||||
|
|
||||||
// TODO: ets_install_putc1 definition is wrong in ets_sys.h, need cast
|
ets_install_putc1(&uart_write_char_d);
|
||||||
ets_install_putc1((void *)&uart_write_char_d);
|
|
||||||
|
|
||||||
if (s_panic_line) {
|
if (s_panic_line) {
|
||||||
ets_printf_P(PSTR("\nPanic %S:%d %S"), s_panic_file, s_panic_line, s_panic_func);
|
ets_printf_P(PSTR("\nPanic %S:%d %S"), s_panic_file, s_panic_line, s_panic_func);
|
||||||
@ -182,7 +185,13 @@ void __wrap_system_restart_local() {
|
|||||||
|
|
||||||
// Use cap-X formatting to ensure the standard EspExceptionDecoder doesn't match the address
|
// Use cap-X formatting to ensure the standard EspExceptionDecoder doesn't match the address
|
||||||
if (umm_last_fail_alloc_addr) {
|
if (umm_last_fail_alloc_addr) {
|
||||||
|
#if defined(DEBUG_ESP_OOM)
|
||||||
|
ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)@%S:%d\n"),
|
||||||
|
(uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size,
|
||||||
|
umm_last_fail_alloc_file, umm_last_fail_alloc_line);
|
||||||
|
#else
|
||||||
ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)\n"), (uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size);
|
ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)\n"), (uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
custom_crash_callback( &rst_info, sp_dump + offset, stack_end );
|
custom_crash_callback( &rst_info, sp_dump + offset, stack_end );
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,6 @@ extern bool timeshift64_is_set;
|
|||||||
void esp_yield();
|
void esp_yield();
|
||||||
void esp_schedule();
|
void esp_schedule();
|
||||||
void tune_timeshift64 (uint64_t now_us);
|
void tune_timeshift64 (uint64_t now_us);
|
||||||
void settimeofday_cb (void (*cb)(void));
|
|
||||||
void disable_extra4k_at_link_time (void) __attribute__((noinline));
|
void disable_extra4k_at_link_time (void) __attribute__((noinline));
|
||||||
|
|
||||||
uint32_t sqrt32 (uint32_t n);
|
uint32_t sqrt32 (uint32_t n);
|
||||||
@ -25,6 +24,14 @@ uint32_t crc32 (const void* data, size_t length, uint32_t crc = 0xffffffff);
|
|||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
using TrivialCB = std::function<void()>;
|
||||||
|
|
||||||
|
void settimeofday_cb (TrivialCB&& cb);
|
||||||
|
void settimeofday_cb (const TrivialCB& cb);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // __COREDECLS_H
|
#endif // __COREDECLS_H
|
||||||
|
@ -11,6 +11,27 @@ extern int rom_i2c_readReg_Mask(int, int, int, int, int);
|
|||||||
|
|
||||||
extern int uart_baudrate_detect(int, int);
|
extern int uart_baudrate_detect(int, int);
|
||||||
|
|
||||||
|
/*
|
||||||
|
ROM function, uart_buff_switch(), is used to switch printing between UART0 and
|
||||||
|
UART1. It updates a structure that only controls a select group of print
|
||||||
|
functions. ets_putc() and ets_uart_printf() are examples and are not affected by
|
||||||
|
calls to ets_install_putc1().
|
||||||
|
|
||||||
|
Use:
|
||||||
|
0 for UART0, also clears RX FIFO
|
||||||
|
1 for UART1
|
||||||
|
*/
|
||||||
|
extern void uart_buff_switch(uint8_t);
|
||||||
|
|
||||||
|
/*
|
||||||
|
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().
|
||||||
|
*/
|
||||||
|
extern int ets_uart_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
|
||||||
|
|
||||||
extern void ets_delay_us(uint32_t us);
|
extern void ets_delay_us(uint32_t us);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -7,38 +7,134 @@
|
|||||||
#include "umm_malloc/umm_malloc.h"
|
#include "umm_malloc/umm_malloc.h"
|
||||||
#include <c_types.h>
|
#include <c_types.h>
|
||||||
#include <sys/reent.h>
|
#include <sys/reent.h>
|
||||||
|
#include <user_interface.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
|
#define UMM_MALLOC(s) umm_poison_malloc(s)
|
||||||
|
#define UMM_CALLOC(n,s) umm_poison_calloc(n,s)
|
||||||
|
#define UMM_REALLOC_FL(p,s,f,l) umm_poison_realloc_fl(p,s,f,l)
|
||||||
|
#define UMM_FREE_FL(p,f,l) umm_poison_free_fl(p,f,l)
|
||||||
|
|
||||||
|
#undef realloc
|
||||||
|
#undef free
|
||||||
|
|
||||||
|
#elif defined(DEBUG_ESP_OOM)
|
||||||
|
#define UMM_MALLOC(s) umm_malloc(s)
|
||||||
|
#define UMM_CALLOC(n,s) umm_calloc(n,s)
|
||||||
|
#define UMM_REALLOC_FL(p,s,f,l) umm_realloc(p,s)
|
||||||
|
#define UMM_FREE_FL(p,f,l) umm_free(p)
|
||||||
|
|
||||||
|
#undef realloc
|
||||||
|
#undef free
|
||||||
|
|
||||||
|
#else // ! UMM_POISON_CHECK && ! DEBUG_ESP_OOM
|
||||||
|
#define UMM_MALLOC(s) malloc(s)
|
||||||
|
#define UMM_CALLOC(n,s) calloc(n,s)
|
||||||
|
#define UMM_REALLOC_FL(p,s,f,l) realloc(p,s)
|
||||||
|
#define UMM_FREE_FL(p,f,l) free(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(UMM_POISON_CHECK)
|
||||||
|
#define POISON_CHECK__ABORT() \
|
||||||
|
do { \
|
||||||
|
if ( ! POISON_CHECK() ) \
|
||||||
|
abort(); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define POISON_CHECK__PANIC_FL(file, line) \
|
||||||
|
do { \
|
||||||
|
if ( ! POISON_CHECK() ) \
|
||||||
|
__panic_func(file, line, ""); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#else // No full heap poison checking.
|
||||||
|
#define POISON_CHECK__ABORT() do {} while(0)
|
||||||
|
#define POISON_CHECK__PANIC_FL(file, line) do { (void)file; (void)line; } while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
// Debugging helper, last allocation which returned NULL
|
// Debugging helper, last allocation which returned NULL
|
||||||
void *umm_last_fail_alloc_addr = NULL;
|
void *umm_last_fail_alloc_addr = NULL;
|
||||||
int umm_last_fail_alloc_size = 0;
|
int umm_last_fail_alloc_size = 0;
|
||||||
|
#if defined(DEBUG_ESP_OOM)
|
||||||
|
const char *umm_last_fail_alloc_file = NULL;
|
||||||
|
int umm_last_fail_alloc_line = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef UMM_INTEGRITY_CHECK
|
||||||
|
#define INTEGRITY_CHECK__ABORT() \
|
||||||
|
do { \
|
||||||
|
if ( ! INTEGRITY_CHECK() ) \
|
||||||
|
abort(); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define INTEGRITY_CHECK__PANIC_FL(file, line) \
|
||||||
|
do { \
|
||||||
|
if ( ! INTEGRITY_CHECK() ) \
|
||||||
|
__panic_func(file, line, ""); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#else // ! UMM_INTEGRITY_CHECK
|
||||||
|
#define INTEGRITY_CHECK__ABORT() do {} while(0)
|
||||||
|
#define INTEGRITY_CHECK__PANIC_FL(file, line) do { (void)file; (void)line; } while(0)
|
||||||
|
|
||||||
|
#endif // UMM_INTEGRITY_CHECK
|
||||||
|
|
||||||
|
#if defined(DEBUG_ESP_OOM)
|
||||||
|
#define PTR_CHECK__LOG_LAST_FAIL_FL(p, s, f, l) \
|
||||||
|
if(0 != (s) && 0 == p)\
|
||||||
|
{\
|
||||||
|
umm_last_fail_alloc_addr = __builtin_return_address(0);\
|
||||||
|
umm_last_fail_alloc_size = s;\
|
||||||
|
umm_last_fail_alloc_file = f;\
|
||||||
|
umm_last_fail_alloc_line = l;\
|
||||||
|
}
|
||||||
|
#define PTR_CHECK__LOG_LAST_FAIL(p, s) \
|
||||||
|
if(0 != (s) && 0 == p)\
|
||||||
|
{\
|
||||||
|
umm_last_fail_alloc_addr = __builtin_return_address(0);\
|
||||||
|
umm_last_fail_alloc_size = s;\
|
||||||
|
umm_last_fail_alloc_file = NULL;\
|
||||||
|
umm_last_fail_alloc_line = 0;\
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define PTR_CHECK__LOG_LAST_FAIL_FL(p, s, f, l) \
|
||||||
|
(void)f;\
|
||||||
|
(void)l;\
|
||||||
|
if(0 != (s) && 0 == p)\
|
||||||
|
{\
|
||||||
|
umm_last_fail_alloc_addr = __builtin_return_address(0);\
|
||||||
|
umm_last_fail_alloc_size = s;\
|
||||||
|
}
|
||||||
|
#define PTR_CHECK__LOG_LAST_FAIL(p, s) \
|
||||||
|
if(0 != (s) && 0 == p)\
|
||||||
|
{\
|
||||||
|
umm_last_fail_alloc_addr = __builtin_return_address(0);\
|
||||||
|
umm_last_fail_alloc_size = s;\
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void* _malloc_r(struct _reent* unused, size_t size)
|
void* _malloc_r(struct _reent* unused, size_t size)
|
||||||
{
|
{
|
||||||
(void) unused;
|
(void) unused;
|
||||||
void *ret = malloc(size);
|
void *ret = malloc(size);
|
||||||
if (0 != size && 0 == ret) {
|
PTR_CHECK__LOG_LAST_FAIL(ret, size);
|
||||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
|
||||||
umm_last_fail_alloc_size = size;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _free_r(struct _reent* unused, void* ptr)
|
void _free_r(struct _reent* unused, void* ptr)
|
||||||
{
|
{
|
||||||
(void) unused;
|
(void) unused;
|
||||||
return free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* _realloc_r(struct _reent* unused, void* ptr, size_t size)
|
void* _realloc_r(struct _reent* unused, void* ptr, size_t size)
|
||||||
{
|
{
|
||||||
(void) unused;
|
(void) unused;
|
||||||
void *ret = realloc(ptr, size);
|
void *ret = realloc(ptr, size);
|
||||||
if (0 != size && 0 == ret) {
|
PTR_CHECK__LOG_LAST_FAIL(ret, size);
|
||||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
|
||||||
umm_last_fail_alloc_size = size;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,140 +142,169 @@ void* _calloc_r(struct _reent* unused, size_t count, size_t size)
|
|||||||
{
|
{
|
||||||
(void) unused;
|
(void) unused;
|
||||||
void *ret = calloc(count, size);
|
void *ret = calloc(count, size);
|
||||||
if (0 != (count * size) && 0 == ret) {
|
PTR_CHECK__LOG_LAST_FAIL(ret, count * size);
|
||||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
return ret;
|
||||||
umm_last_fail_alloc_size = count * size;
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_OOM
|
||||||
|
#undef malloc
|
||||||
|
#undef calloc
|
||||||
|
#undef realloc
|
||||||
|
|
||||||
|
#define DEBUG_HEAP_PRINTF ets_uart_printf
|
||||||
|
|
||||||
|
void ICACHE_RAM_ATTR print_loc(size_t size, const char* file, int line)
|
||||||
|
{
|
||||||
|
(void)size;
|
||||||
|
(void)line;
|
||||||
|
if (system_get_os_print()) {
|
||||||
|
DEBUG_HEAP_PRINTF(":oom(%d)@", (int)size);
|
||||||
|
|
||||||
|
bool inISR = ETS_INTR_WITHINISR();
|
||||||
|
if (inISR && (uint32_t)file >= 0x40200000) {
|
||||||
|
DEBUG_HEAP_PRINTF("File: %p", file);
|
||||||
|
} else if (!inISR && (uint32_t)file >= 0x40200000) {
|
||||||
|
char buf[ets_strlen(file)] __attribute__ ((aligned(4)));
|
||||||
|
ets_strcpy(buf, file);
|
||||||
|
DEBUG_HEAP_PRINTF(buf);
|
||||||
|
} else {
|
||||||
|
DEBUG_HEAP_PRINTF(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_HEAP_PRINTF(":%d\n", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_RAM_ATTR print_oom_size(size_t size)
|
||||||
|
{
|
||||||
|
(void)size;
|
||||||
|
if (system_get_os_print()) {
|
||||||
|
DEBUG_HEAP_PRINTF(":oom(%d)@?\n", (int)size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OOM_CHECK__PRINT_OOM(p, s) if (!p) print_oom_size(s)
|
||||||
|
#define OOM_CHECK__PRINT_LOC(p, s, f, l) if (!p) print_loc(s, f, l)
|
||||||
|
|
||||||
|
#else // ! DEBUG_ESP_OOM
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
//C - to be discussed - is this what you want?
|
||||||
|
//C Skip OOM logging of last fail for malloc/... and pvPort... .
|
||||||
|
//C It cost 64 more bytes of IRAM to turn on. And was not previously enabled.
|
||||||
|
#undef PTR_CHECK__LOG_LAST_FAIL_FL
|
||||||
|
#define PTR_CHECK__LOG_LAST_FAIL_FL(p, s, f, l)
|
||||||
|
#undef PTR_CHECK__LOG_LAST_FAIL
|
||||||
|
#define PTR_CHECK__LOG_LAST_FAIL(p, s)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define OOM_CHECK__PRINT_OOM(p, s)
|
||||||
|
#define OOM_CHECK__PRINT_LOC(p, s, f, l)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DEBUG_ESP_OOM) || defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || defined(UMM_INTEGRITY_CHECK)
|
||||||
|
/*
|
||||||
|
The thinking behind the ordering of Integrity Check, Full Poison Check, and
|
||||||
|
the specific *alloc function.
|
||||||
|
|
||||||
|
1. Integrity Check - verifies the heap management information is not corrupt.
|
||||||
|
This allows any other testing, that walks the heap, to run safely.
|
||||||
|
|
||||||
|
2. Place Full Poison Check before or after a specific *alloc function?
|
||||||
|
a. After, when the *alloc function operates on an existing allocation.
|
||||||
|
b. Before, when the *alloc function creates a new, not modified, allocation.
|
||||||
|
|
||||||
|
In a free() or realloc() call, the focus is on their allocation. It is
|
||||||
|
checked 1st and reported on 1ST if an error exists. Full Posion Check is
|
||||||
|
done after.
|
||||||
|
|
||||||
|
For malloc(), calloc(), and zalloc() Full Posion Check is done 1st since
|
||||||
|
these functions do not modify an existing allocation.
|
||||||
|
*/
|
||||||
|
void* ICACHE_RAM_ATTR malloc(size_t size)
|
||||||
|
{
|
||||||
|
INTEGRITY_CHECK__ABORT();
|
||||||
|
POISON_CHECK__ABORT();
|
||||||
|
void* ret = UMM_MALLOC(size);
|
||||||
|
PTR_CHECK__LOG_LAST_FAIL(ret, size);
|
||||||
|
OOM_CHECK__PRINT_OOM(ret, size);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ICACHE_RAM_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);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ICACHE_RAM_ATTR realloc(void* ptr, size_t size)
|
||||||
|
{
|
||||||
|
INTEGRITY_CHECK__ABORT();
|
||||||
|
void* ret = UMM_REALLOC_FL(ptr, size, NULL, 0);
|
||||||
|
POISON_CHECK__ABORT();
|
||||||
|
PTR_CHECK__LOG_LAST_FAIL(ret, size);
|
||||||
|
OOM_CHECK__PRINT_OOM(ret, size);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_RAM_ATTR free(void* p)
|
||||||
|
{
|
||||||
|
INTEGRITY_CHECK__ABORT();
|
||||||
|
UMM_FREE_FL(p, NULL, 0);
|
||||||
|
POISON_CHECK__ABORT();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line)
|
||||||
|
{
|
||||||
|
INTEGRITY_CHECK__PANIC_FL(file, line);
|
||||||
|
POISON_CHECK__PANIC_FL(file, line);
|
||||||
|
void* ret = UMM_MALLOC(size);
|
||||||
|
PTR_CHECK__LOG_LAST_FAIL_FL(ret, size, file, line);
|
||||||
|
OOM_CHECK__PRINT_LOC(ret, size, file, line);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line)
|
||||||
|
{
|
||||||
|
INTEGRITY_CHECK__PANIC_FL(file, line);
|
||||||
|
void* ret = UMM_REALLOC_FL(ptr, size, file, line);
|
||||||
|
POISON_CHECK__PANIC_FL(file, line);
|
||||||
|
PTR_CHECK__LOG_LAST_FAIL_FL(ret, size, file, line);
|
||||||
|
OOM_CHECK__PRINT_LOC(ret, size, file, line);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line)
|
||||||
|
{
|
||||||
|
INTEGRITY_CHECK__PANIC_FL(file, line);
|
||||||
|
POISON_CHECK__PANIC_FL(file, line);
|
||||||
|
void* ret = UMM_CALLOC(1, size);
|
||||||
|
PTR_CHECK__LOG_LAST_FAIL_FL(ret, size, file, line);
|
||||||
|
OOM_CHECK__PRINT_LOC(ret, size, file, line);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line)
|
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line)
|
||||||
{
|
{
|
||||||
(void) file;
|
INTEGRITY_CHECK__PANIC_FL(file, line);
|
||||||
(void) line;
|
UMM_FREE_FL(ptr, file, line);
|
||||||
free(ptr);
|
POISON_CHECK__PANIC_FL(file, line);
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_OOM
|
|
||||||
|
|
||||||
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line)
|
|
||||||
{
|
|
||||||
return malloc_loc(size, file, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line)
|
|
||||||
{
|
|
||||||
return calloc_loc(count, size, file, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line)
|
|
||||||
{
|
|
||||||
return realloc_loc(ptr, size, file, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line)
|
|
||||||
{
|
|
||||||
return calloc_loc(1, size, file, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef malloc
|
|
||||||
#undef calloc
|
|
||||||
#undef realloc
|
|
||||||
|
|
||||||
static const char oom_fmt[] PROGMEM STORE_ATTR = ":oom(%d)@?\n";
|
|
||||||
static const char oom_fmt_1[] PROGMEM STORE_ATTR = ":oom(%d)@";
|
|
||||||
static const char oom_fmt_2[] PROGMEM STORE_ATTR = ":%d\n";
|
|
||||||
|
|
||||||
void* malloc (size_t s)
|
|
||||||
{
|
|
||||||
void* ret = umm_malloc(s);
|
|
||||||
if (!ret)
|
|
||||||
os_printf(oom_fmt, (int)s);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* calloc (size_t n, size_t s)
|
|
||||||
{
|
|
||||||
void* ret = umm_calloc(n, s);
|
|
||||||
if (!ret)
|
|
||||||
os_printf(oom_fmt, (int)s);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* realloc (void* p, size_t s)
|
|
||||||
{
|
|
||||||
void* ret = umm_realloc(p, s);
|
|
||||||
if (!ret)
|
|
||||||
os_printf(oom_fmt, (int)s);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_loc (size_t s, const char* file, int line)
|
|
||||||
{
|
|
||||||
os_printf(oom_fmt_1, (int)s);
|
|
||||||
os_printf(file);
|
|
||||||
os_printf(oom_fmt_2, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* malloc_loc (size_t s, const char* file, int line)
|
|
||||||
{
|
|
||||||
void* ret = umm_malloc(s);
|
|
||||||
if (!ret)
|
|
||||||
print_loc(s, file, line);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* calloc_loc (size_t n, size_t s, const char* file, int line)
|
|
||||||
{
|
|
||||||
void* ret = umm_calloc(n, s);
|
|
||||||
if (!ret)
|
|
||||||
print_loc(s, file, line);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* realloc_loc (void* p, size_t s, const char* file, int line)
|
|
||||||
{
|
|
||||||
void* ret = umm_realloc(p, s);
|
|
||||||
if (!ret)
|
|
||||||
print_loc(s, file, line);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line)
|
|
||||||
{
|
|
||||||
(void) file;
|
|
||||||
(void) line;
|
|
||||||
return malloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line)
|
|
||||||
{
|
|
||||||
(void) file;
|
|
||||||
(void) line;
|
|
||||||
return calloc(count, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line)
|
|
||||||
{
|
|
||||||
(void) file;
|
|
||||||
(void) line;
|
|
||||||
return realloc(ptr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line)
|
|
||||||
{
|
|
||||||
(void) file;
|
|
||||||
(void) line;
|
|
||||||
return calloc(1, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !defined(DEBUG_ESP_OOM)
|
|
||||||
|
|
||||||
size_t xPortGetFreeHeapSize(void)
|
|
||||||
{
|
|
||||||
return umm_free_heap_size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ICACHE_RAM_ATTR xPortWantedSizeAlign(size_t size)
|
size_t ICACHE_RAM_ATTR xPortWantedSizeAlign(size_t size)
|
||||||
|
@ -100,7 +100,8 @@ int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) __attribute__((
|
|||||||
int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) {
|
int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) {
|
||||||
(void) r;
|
(void) r;
|
||||||
if (file->_file == STDOUT_FILENO) {
|
if (file->_file == STDOUT_FILENO) {
|
||||||
return ets_putc(c);
|
ets_putc(c);
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
@ -42,16 +42,22 @@
|
|||||||
#include <osapi.h>
|
#include <osapi.h>
|
||||||
#include <os_type.h>
|
#include <os_type.h>
|
||||||
#include "coredecls.h"
|
#include "coredecls.h"
|
||||||
|
#include "Schedule.h"
|
||||||
|
|
||||||
extern "C" {
|
static TrivialCB _settimeofday_cb;
|
||||||
|
|
||||||
static void (*_settimeofday_cb)(void) = NULL;
|
void settimeofday_cb (TrivialCB&& cb)
|
||||||
|
{
|
||||||
|
_settimeofday_cb = std::move(cb);
|
||||||
|
}
|
||||||
|
|
||||||
void settimeofday_cb (void (*cb)(void))
|
void settimeofday_cb (const TrivialCB& cb)
|
||||||
{
|
{
|
||||||
_settimeofday_cb = cb;
|
_settimeofday_cb = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
#if LWIP_VERSION_MAJOR == 1
|
#if LWIP_VERSION_MAJOR == 1
|
||||||
|
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
@ -478,7 +484,7 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz)
|
|||||||
sntp_set_system_time(tv->tv_sec);
|
sntp_set_system_time(tv->tv_sec);
|
||||||
|
|
||||||
if (_settimeofday_cb)
|
if (_settimeofday_cb)
|
||||||
_settimeofday_cb();
|
schedule_function(_settimeofday_cb);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,21 @@
|
|||||||
|
|
||||||
using namespace fs;
|
using namespace fs;
|
||||||
|
|
||||||
|
|
||||||
|
// Deprecated functions, to be deleted in next release
|
||||||
|
int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) {
|
||||||
|
return flash_hal_write(addr, size, src);
|
||||||
|
}
|
||||||
|
int32_t spiffs_hal_erase(uint32_t addr, uint32_t size) {
|
||||||
|
return flash_hal_erase(addr, size);
|
||||||
|
}
|
||||||
|
int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
|
||||||
|
return flash_hal_read(addr, size, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace spiffs_impl {
|
namespace spiffs_impl {
|
||||||
|
|
||||||
FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode)
|
FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode)
|
||||||
|
@ -39,6 +39,26 @@ extern "C" {
|
|||||||
|
|
||||||
using namespace fs;
|
using namespace fs;
|
||||||
|
|
||||||
|
// The following are deprecated symbols and functions, to be removed at the next major release.
|
||||||
|
// They are provided only for backwards compatibility and to give libs a chance to update.
|
||||||
|
|
||||||
|
extern "C" uint32_t _SPIFFS_start __attribute__((deprecated));
|
||||||
|
extern "C" uint32_t _SPIFFS_end __attribute__((deprecated));
|
||||||
|
extern "C" uint32_t _SPIFFS_page __attribute__((deprecated));
|
||||||
|
extern "C" uint32_t _SPIFFS_block __attribute__((deprecated));
|
||||||
|
|
||||||
|
#define SPIFFS_PHYS_ADDR ((uint32_t) (&_SPIFFS_start) - 0x40200000)
|
||||||
|
#define SPIFFS_PHYS_SIZE ((uint32_t) (&_SPIFFS_end) - (uint32_t) (&_SPIFFS_start))
|
||||||
|
#define SPIFFS_PHYS_PAGE ((uint32_t) &_SPIFFS_page)
|
||||||
|
#define SPIFFS_PHYS_BLOCK ((uint32_t) &_SPIFFS_block)
|
||||||
|
|
||||||
|
extern int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) __attribute__((deprecated));
|
||||||
|
extern int32_t spiffs_hal_erase(uint32_t addr, uint32_t size) __attribute__((deprecated));
|
||||||
|
extern int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) __attribute__((deprecated));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace spiffs_impl {
|
namespace spiffs_impl {
|
||||||
|
|
||||||
int getSpiffsMode(OpenMode openMode, AccessMode accessMode);
|
int getSpiffsMode(OpenMode openMode, AccessMode accessMode);
|
||||||
@ -151,11 +171,6 @@ public:
|
|||||||
|
|
||||||
bool begin() override
|
bool begin() override
|
||||||
{
|
{
|
||||||
#if defined(ARDUINO) && !defined(CORE_MOCK)
|
|
||||||
if (&_FS_end <= &_FS_start)
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (SPIFFS_mounted(&_fs) != 0) {
|
if (SPIFFS_mounted(&_fs) != 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/reent.h>
|
#include <sys/reent.h>
|
||||||
@ -55,25 +56,12 @@ static void setServer(int id, const char* name_or_ip)
|
|||||||
{
|
{
|
||||||
if (name_or_ip)
|
if (name_or_ip)
|
||||||
{
|
{
|
||||||
//TODO: check whether server is given by name or IP
|
// per current configuration,
|
||||||
|
// lwIP can receive an IP address or a fqdn
|
||||||
sntp_setservername(id, (char*) name_or_ip);
|
sntp_setservername(id, (char*) name_or_ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
|
|
||||||
{
|
|
||||||
sntp_stop();
|
|
||||||
|
|
||||||
setServer(0, server1);
|
|
||||||
setServer(1, server2);
|
|
||||||
setServer(2, server3);
|
|
||||||
|
|
||||||
sntp_set_timezone_in_seconds(timezone_sec);
|
|
||||||
sntp_set_daylight(daylightOffset_sec);
|
|
||||||
sntp_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
int clock_gettime(clockid_t unused, struct timespec *tp)
|
int clock_gettime(clockid_t unused, struct timespec *tp)
|
||||||
{
|
{
|
||||||
(void) unused;
|
(void) unused;
|
||||||
@ -108,4 +96,33 @@ int _gettimeofday_r(struct _reent* unused, struct timeval *tp, void *tzp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}; // extern "C"
|
||||||
|
|
||||||
|
void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
|
||||||
|
{
|
||||||
|
sntp_stop();
|
||||||
|
|
||||||
|
setServer(0, server1);
|
||||||
|
setServer(1, server2);
|
||||||
|
setServer(2, server3);
|
||||||
|
|
||||||
|
sntp_set_timezone_in_seconds(timezone_sec);
|
||||||
|
sntp_set_daylight(daylightOffset_sec);
|
||||||
|
sntp_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void configTime(const char* tz, const char* server1, const char* server2, const char* server3)
|
||||||
|
{
|
||||||
|
sntp_stop();
|
||||||
|
|
||||||
|
setServer(0, server1);
|
||||||
|
setServer(1, server2);
|
||||||
|
setServer(2, server3);
|
||||||
|
|
||||||
|
char tzram[strlen_P(tz) + 1];
|
||||||
|
memcpy_P(tzram, tz, sizeof(tzram));
|
||||||
|
setenv("TZ", tzram, 1/*overwrite*/);
|
||||||
|
tzset();
|
||||||
|
|
||||||
|
sntp_init();
|
||||||
|
}
|
||||||
|
@ -48,12 +48,13 @@ uint8_t twi_status();
|
|||||||
|
|
||||||
uint8_t twi_transmit(const uint8_t*, uint8_t);
|
uint8_t twi_transmit(const uint8_t*, uint8_t);
|
||||||
|
|
||||||
void twi_attachSlaveRxEvent( void (*)(uint8_t*, size_t) );
|
void twi_attachSlaveRxEvent(void (*)(uint8_t*, size_t));
|
||||||
void twi_attachSlaveTxEvent( void (*)(void) );
|
void twi_attachSlaveTxEvent(void (*)(void));
|
||||||
void twi_reply(uint8_t);
|
void twi_reply(uint8_t);
|
||||||
//void twi_stop(void);
|
//void twi_stop(void);
|
||||||
void twi_releaseBus(void);
|
void twi_releaseBus(void);
|
||||||
|
|
||||||
|
void twi_enableSlaveMode(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,10 @@
|
|||||||
#include "user_interface.h"
|
#include "user_interface.h"
|
||||||
#include "uart_register.h"
|
#include "uart_register.h"
|
||||||
|
|
||||||
|
#define MODE2WIDTH(mode) (((mode%16)>>2)+5)
|
||||||
|
#define MODE2STOP(mode) (((mode)>>5)+1)
|
||||||
|
#define MODE2PARITY(mode) (mode%4)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Some general architecture for GDB integration with the UART to enable
|
Some general architecture for GDB integration with the UART to enable
|
||||||
serial debugging.
|
serial debugging.
|
||||||
@ -204,6 +208,13 @@ uart_read_char_unsafe(uart_t* uart)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
uart_get_bit_length(const int uart_nr)
|
||||||
|
{
|
||||||
|
// return bit length from uart mode, +1 for the start bit which is always there.
|
||||||
|
return MODE2WIDTH(USC0(uart_nr)) + MODE2PARITY(USC0(uart_nr)) + MODE2STOP(USC0(uart_nr)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
uart_rx_available(uart_t* uart)
|
uart_rx_available(uart_t* uart)
|
||||||
{
|
{
|
||||||
@ -899,18 +910,26 @@ void
|
|||||||
uart_set_debug(int uart_nr)
|
uart_set_debug(int uart_nr)
|
||||||
{
|
{
|
||||||
s_uart_debug_nr = uart_nr;
|
s_uart_debug_nr = uart_nr;
|
||||||
void (*func)(char) = NULL;
|
fp_putc_t func = NULL;
|
||||||
switch(s_uart_debug_nr)
|
switch(s_uart_debug_nr)
|
||||||
{
|
{
|
||||||
case UART0:
|
case UART0:
|
||||||
func = &uart0_write_char;
|
func = &uart0_write_char;
|
||||||
|
// This selects the UART for ROM ets_putc which is used by
|
||||||
|
// ::printf, ets_printf_P in core_esp_postmortem.cpp and others.
|
||||||
|
// Has a side effect of clearing RX FIFO for UART0
|
||||||
|
uart_buff_switch(0);
|
||||||
break;
|
break;
|
||||||
case UART1:
|
case UART1:
|
||||||
func = &uart1_write_char;
|
func = &uart1_write_char;
|
||||||
|
uart_buff_switch(1);
|
||||||
break;
|
break;
|
||||||
case UART_NO:
|
case UART_NO:
|
||||||
default:
|
default:
|
||||||
func = &uart_ignore_char;
|
func = &uart_ignore_char;
|
||||||
|
// There is no disable option for ets_putc,
|
||||||
|
// we switch to UART0 for disable case.
|
||||||
|
uart_buff_switch(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(gdbstub_has_putc1_control()) {
|
if(gdbstub_has_putc1_control()) {
|
||||||
@ -921,7 +940,7 @@ uart_set_debug(int uart_nr)
|
|||||||
} else {
|
} else {
|
||||||
system_set_os_print(0);
|
system_set_os_print(0);
|
||||||
}
|
}
|
||||||
ets_install_putc1((void *) func);
|
ets_install_putc1(func);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +147,7 @@ int uart_get_debug();
|
|||||||
void uart_start_detect_baudrate(int uart_nr);
|
void uart_start_detect_baudrate(int uart_nr);
|
||||||
int uart_detect_baudrate(int uart_nr);
|
int uart_detect_baudrate(int uart_nr);
|
||||||
|
|
||||||
|
uint8_t uart_get_bit_length(const int uart_nr);
|
||||||
|
|
||||||
#if defined (__cplusplus)
|
#if defined (__cplusplus)
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
251
cores/esp8266/umm_malloc/Notes.h
Normal file
251
cores/esp8266/umm_malloc/Notes.h
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
#if 0
|
||||||
|
/*
|
||||||
|
* This .h is nothing but comments about thoughts and observations made while
|
||||||
|
* updating the Arduino ESP8266 Core, with the new upstream umm_malloc. It is
|
||||||
|
* added as a .h so that it does not get lost and to avoid cluttering up the
|
||||||
|
* code with a huge block comment.
|
||||||
|
|
||||||
|
|
||||||
|
PR text description:
|
||||||
|
|
||||||
|
upstream version of `umm_malloc` customized for Arduino ESP8266 Core
|
||||||
|
|
||||||
|
This updates the heap management library, umm_malloc, to the current upstream
|
||||||
|
version at https://github.com/rhempel/umm_malloc. Some reorganizing and new code
|
||||||
|
was needed to use the new version.
|
||||||
|
|
||||||
|
This is a list of noteworthy changes:
|
||||||
|
|
||||||
|
UMM_POISON - now has a lite option as well as the previous intensive check
|
||||||
|
option. The code for running the full poison test at the call of the various
|
||||||
|
alloc functions was removed in the upstream version. In this port, the missing
|
||||||
|
code was added to heap.cpp and umm_local.cpp.
|
||||||
|
* UMM_POISON - appears to have been partially changed to UMM_POISON_CHECK,
|
||||||
|
I treat it as deprecated and used UMM_POISON_CHECK when needed.
|
||||||
|
However, the Arduino Core's references to UMM_POISON were replaced with
|
||||||
|
UMM_POISON_CHECK_LITE.
|
||||||
|
* UMM_POISON_CHECK_LITE - Less intense, it just checks poison on active
|
||||||
|
neighboring allocations.
|
||||||
|
* UMM_POISON_CHECK - Full heap intensive check of poison
|
||||||
|
|
||||||
|
A cautionary note, on the use of UMM_INTEGRITY_CHECK and UMM_POISON_CHECK, and
|
||||||
|
umm_info(). All of these run with IRQs disabled, for periods that can go into
|
||||||
|
100's of us. With umm_info(NULL, true) that may go into seconds, depending on
|
||||||
|
the serial interface speed and the number of memory allocations present. Use
|
||||||
|
UMM_INTEGRITY_CHECK, UMM_POISON_CHECK, and umm_info() sparingly. If you want to
|
||||||
|
see numbers for the disabled time, explore using UMM_CRITICAL_METRICS in
|
||||||
|
umm_malloc_cfg.h.
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
|
||||||
|
New upstream umm_malloc feature delta's from the old umm_malloc we were using:
|
||||||
|
|
||||||
|
umm_posion check for a given *alloc - failure - no longer panics.
|
||||||
|
|
||||||
|
option to run full poison check at each *alloc call, not present
|
||||||
|
|
||||||
|
option to run full interity check at each *alloc call, not present
|
||||||
|
|
||||||
|
upstream code does not call panic from poison_check_block.
|
||||||
|
|
||||||
|
Defragmenting effect of realloc is gone. It now minimizes copy. This
|
||||||
|
may have been an accident during code cleanup.
|
||||||
|
|
||||||
|
In one form or another these features have been restored in the
|
||||||
|
reintegration of the upstream umm_malloc into the Arduino ESP8266 Core.
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
|
||||||
|
A list of changes made for local adaptation of newer upstream umm_malloc.
|
||||||
|
|
||||||
|
In umm_malloc.c
|
||||||
|
Renamed to umm_malloc.cpp
|
||||||
|
Added `extern "C" { ... };` around code.
|
||||||
|
Surround DBGLOG_LEVEL with #ifndef... Define value of DBGLOG_LEVEL from
|
||||||
|
umm_malloc_cfg.h
|
||||||
|
umm_realloc() - Added UMM_CRITICAL_SUSPEND()/UMM_CRITICAL_RESUME() for when
|
||||||
|
lightweight locks are available. eg. sti/cli. Single threaded single CPU
|
||||||
|
case.
|
||||||
|
umm_realloc() - appears to have been refactored to minimize memmove and
|
||||||
|
memcpy. The old version would always combine an adjacent block in the
|
||||||
|
direction of the start of the heap when available and do a memmove. This
|
||||||
|
had a defragging effect. This appears to have been replaced with an attempt
|
||||||
|
to minimize copy when possible.
|
||||||
|
Added heap stats tracking.
|
||||||
|
|
||||||
|
In umm_info.c
|
||||||
|
umm_info() - Added UMM_CRITICAL_DECL(id_info), updated critical sections
|
||||||
|
with tag.
|
||||||
|
Carried forward: Added NULL ptr check at beginning (umm_malloc.c).
|
||||||
|
|
||||||
|
In umm_poison.c:
|
||||||
|
Resolved C++ compiler error reported on get_poisoned(), and get_unpoisoned().
|
||||||
|
They now take in void * arg instead of unsigned char *.
|
||||||
|
Added #if ... || defined(UMM_POISON_CHECK_LITE) to the conditional.
|
||||||
|
|
||||||
|
In umm_integrity.c:
|
||||||
|
Replaced printf with DBGLOG_FUNCTION. This needs to be a malloc free
|
||||||
|
function and ISR safe.
|
||||||
|
Added critical sections.
|
||||||
|
|
||||||
|
In umm_malloc_cfg.h:
|
||||||
|
Added macro UMM_CRITICAL_SUSPEND()/UMM_CRITICAL_RESUME()
|
||||||
|
|
||||||
|
Globally change across all files %i to %d: umm_info.c, umm_malloc.c,
|
||||||
|
Added a #ifdef BUILD_UMM_MALLOC_C fence to prevent Arduino IDE from building
|
||||||
|
the various .c files that are #included into umm_malloc.cpp. They are
|
||||||
|
normally enabled by #define <feature name> in umm_malloc_cfg.h. In this
|
||||||
|
case it builds fine; however, if the define is global, the IDE will try and
|
||||||
|
build the .c by itself.
|
||||||
|
|
||||||
|
Notes,
|
||||||
|
umm_integrity_check() is called by macro INTEGRITY_CHECK which returns 1
|
||||||
|
on success. No corruption. Does a time consuming scan of the whole heap.
|
||||||
|
It will call UMM_HEAP_CORRUPTION_CB if an error is found.
|
||||||
|
|
||||||
|
umm_poison_check(), formerly known as check_poison_all_blocks(),
|
||||||
|
is called by macro POISON_CHECK which returns 1 on success for no
|
||||||
|
corruption. Does a time consuming scan of all active allocations for
|
||||||
|
modified poison. The new upstream version does *NOT* call
|
||||||
|
UMM_HEAP_CORRUPTION_CB if an error is found. The option description says
|
||||||
|
it does!
|
||||||
|
|
||||||
|
umm_poison_realloc() and umm_poison_free() no longer call the macro
|
||||||
|
UMM_HEAP_CORRUPTION_CB on poison error. Just a printf message is
|
||||||
|
generated. I have added alternative functions umm_poison_free_fl,
|
||||||
|
umm_poison_realloc_fl, and get_unpoisoned_check_neighbors in
|
||||||
|
umm_local.cpp. These expand the poison check on the current allocation to
|
||||||
|
include its nearest allocated neighbors in the heap.
|
||||||
|
|
||||||
|
umm_malloc() has been extended to call check_poison_neighbors for the
|
||||||
|
allocation it selects, conditionally for UMM_POISON_CHECK_LITE.
|
||||||
|
|
||||||
|
For upstream umm_malloc "# define POISON_CHECK() 0" should have been 1
|
||||||
|
add to list to report.
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Notes from searching for the best print option
|
||||||
|
|
||||||
|
Printing from the malloc routines is tricky. Since a print library
|
||||||
|
might call *alloc. Then recursion may follow as each error call may fail
|
||||||
|
into another error and so on.
|
||||||
|
|
||||||
|
Objective: To be able to print "last gasp" diagnostic messages
|
||||||
|
when interrupts are disabled and w/o availability of heap resources.
|
||||||
|
|
||||||
|
It turns out things are more complicated than that. These are three cases for
|
||||||
|
printing from the heap and the current solution.:
|
||||||
|
|
||||||
|
1. Printing detailed heap info through `umm_info(NULL, 1);`. This function
|
||||||
|
resides in flash and can only be called from non-ISR context. It can use
|
||||||
|
PROGMEM strings. Because SPI bus will not be busy when called from foreground.
|
||||||
|
|
||||||
|
At this time it is believed that, while running from foreground, a cache-miss
|
||||||
|
at INTLEVEL 15 can be handled. The key factor being the SPI bus is not
|
||||||
|
busy at the time of the cache-miss. It is not clear what gets invoked to
|
||||||
|
process the cache-miss. A software vector call? A hardware assisted transfer?
|
||||||
|
In any case `umm_info_safe_printf_P()` is also in flash.
|
||||||
|
|
||||||
|
The focus here is to print w/o allocating memory and use strings
|
||||||
|
in flash to preserve DRAM.
|
||||||
|
|
||||||
|
2. Printing diagnostic messages possibly from from ISR context.
|
||||||
|
|
||||||
|
Use `ets_uart_printf()` in boot ROM.
|
||||||
|
|
||||||
|
3. Printing diagnostic messages from `heap.cpp` these printf's need to check
|
||||||
|
`system_get_os_print()` to confirm debug-output is enabled just as
|
||||||
|
`os_printf()` did.
|
||||||
|
|
||||||
|
Do test calls to `system_get_os_print()` and call `ets_uart_printf()`
|
||||||
|
in boot ROM when debug print allowed.
|
||||||
|
|
||||||
|
Considerations:
|
||||||
|
* can be called from ISR
|
||||||
|
* can be called from malloc code, cannot use malloc
|
||||||
|
* can be called from malloc code that was called from an ISR
|
||||||
|
* can be called from within a critical section, eg. xt_rsil(15);
|
||||||
|
* this may be effectively the same as being called from an ISR?
|
||||||
|
Update: Current thinking is that from foreground we have more leeway
|
||||||
|
than an ISR.
|
||||||
|
|
||||||
|
Knowns:
|
||||||
|
* ets_printf - For RTOS SDK they replaced this function with one in the SDK.
|
||||||
|
Most of the problems I can see with ets_printf center around not being
|
||||||
|
able to maintain a port to thread context. That is you cannot have one
|
||||||
|
thread using one port while another thread uses the other. In the no OS
|
||||||
|
case we cannot have one area of code using one port and another area of
|
||||||
|
code using the other port. Most of the ROM printf functions are not built
|
||||||
|
to support this kind of usage. Things get especially dangerous when you
|
||||||
|
try to use the ets_external_printf stuff.
|
||||||
|
* ets_vprintf - by itself is safe.
|
||||||
|
* newlibc printf - not safe - lives in flash.
|
||||||
|
* newlibc snprintf - not safe - lives in flash.
|
||||||
|
* builtin putc1 print function - Is installed when you use
|
||||||
|
ets_install_uart_printf. Which calls ets_install_putc1. The selection of UART
|
||||||
|
is performed by calling uart_buff_switch with 0 for UART0 and 1 for UART1.
|
||||||
|
This should work for our purpose here, if handled as follows:
|
||||||
|
* call uart_buff_switch at each printf call to reselect UART
|
||||||
|
* Update: uart_buff_switch is now updated by uart_set_debug() in uart.cpp
|
||||||
|
* use a stack buffer to hold a copy the PROGMEM string to print from.
|
||||||
|
* use ets_vprintf for printing with putc1 function.
|
||||||
|
* os_printf_plus looks interesting. It is in IRAM. If no heap is available it
|
||||||
|
will use up to 64 bytes of stack space to copy a PROGMEM fmt for printing.
|
||||||
|
Issues:
|
||||||
|
* Printing is turned off by system_set_os_print
|
||||||
|
* putc1 needs to be in IRAM - this is a uart.cpp issue
|
||||||
|
* Need to force system_get_free_heap_size to return 0 during critical periods.
|
||||||
|
* won't work for umm_info if it prints over 64 characters.
|
||||||
|
* along with umm_info there are other debug messages that exceed 64 characters.
|
||||||
|
* ets_uart_printf - Appears safe. Just no PROGMEM support. Uses
|
||||||
|
uart_buff_switch to select UART.
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
|
||||||
|
heap.cpp is the entry point for most of the heap API calls.
|
||||||
|
It is a merge point for abstracted heap API calls, such as _malloc_r,
|
||||||
|
pvPortMalloc, and malloc. Thin wrappers are created here for these entry points
|
||||||
|
and others. The wrappers call through to equivalent umm_malloc entry-point.
|
||||||
|
These wrappers also provide the access points to do debug options, like OOM,
|
||||||
|
Integrity Check, and Poison Check.
|
||||||
|
|
||||||
|
-DEBUG_ESP_OOM or select `Debug Level: "OOM"` from the IDE.
|
||||||
|
This option will add extra code to save information on the last OOM event. If
|
||||||
|
your code is built with the `Debug port: "Serial"` option, debug messages will
|
||||||
|
print on OOM events. You do not have to do `Debug port: "Serial"` to get OOM
|
||||||
|
debug messages. From within your code, you can make a call to
|
||||||
|
`Serial.debugOutput(true);` to enable OOM printing. Of course for this to work
|
||||||
|
your code must be built with `Debug Level: "OOM"` or equal.
|
||||||
|
|
||||||
|
-DUUM_POISON is now the same as -DUMM_POISON_CHECK_LITE
|
||||||
|
This is new behavior with this updated. UMM_POISON_CHECK_LITE - checks the
|
||||||
|
allocation presented at realloc() and free(). Expands the poison check on the
|
||||||
|
current allocation to include its nearest allocated neighbors in the heap.
|
||||||
|
umm_malloc() will also check the neighbors of the selected allocation before
|
||||||
|
use.
|
||||||
|
|
||||||
|
For more details and options search on UMM_POISON_CHECK in `umm_malloc_cfg.h`
|
||||||
|
|
||||||
|
TODO: provide some interesting numbers on the time to perform:
|
||||||
|
* UMM_POISON_CHECK
|
||||||
|
* UMM_INTEGRITY_CHECK
|
||||||
|
* umm_info(NUll, 0) built with and without print capability
|
||||||
|
* umm_info(NUll, 1) printing a report to Serial device.
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
|
||||||
|
Enhancement ideas:
|
||||||
|
1. Add tagging to heap allocations. Redefine UMM_POISONED_BLOCK_LEN_TYPE,
|
||||||
|
expand it to include an element for the calling address of allocating
|
||||||
|
requester. Expand umm_info(NULL, 1) to print the respective address with each
|
||||||
|
active allocation. The difficulty here will be the ever-growing complexity of
|
||||||
|
overlapping build options. I think it would be easiest to build this in with
|
||||||
|
and expand the UMM_POISON_CHECK_LITE option.
|
||||||
|
|
||||||
|
2. A build option to not have printing, from umm_info() compiled in. This can
|
||||||
|
save on the execution time spent with interrupts disabled.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#endif
|
@ -10,18 +10,18 @@ might get expensive.
|
|||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
Joerg Wunsch and the avr-libc provided the first malloc() implementation
|
Joerg Wunsch and the avr-libc provided the first `malloc()` implementation
|
||||||
that I examined in detail.
|
that I examined in detail.
|
||||||
|
|
||||||
http://www.nongnu.org/avr-libc
|
`http://www.nongnu.org/avr-libc`
|
||||||
|
|
||||||
Doug Lea's paper on malloc() was another excellent reference and provides
|
Doug Lea's paper on malloc() was another excellent reference and provides
|
||||||
a lot of detail on advanced memory management techniques such as binning.
|
a lot of detail on advanced memory management techniques such as binning.
|
||||||
|
|
||||||
http://g.oswego.edu/dl/html/malloc.html
|
`http://g.oswego.edu/dl/html/malloc.html`
|
||||||
|
|
||||||
Bill Dittman provided excellent suggestions, including macros to support
|
Bill Dittman provided excellent suggestions, including macros to support
|
||||||
using these functions in critical sections, and for optimizing realloc()
|
using these functions in critical sections, and for optimizing `realloc()`
|
||||||
further by checking to see if the previous block was free and could be
|
further by checking to see if the previous block was free and could be
|
||||||
used for the new block size. This can help to reduce heap fragmentation
|
used for the new block size. This can help to reduce heap fragmentation
|
||||||
significantly.
|
significantly.
|
||||||
@ -30,11 +30,73 @@ Yaniv Ankin suggested that a way to dump the current heap condition
|
|||||||
might be useful. I combined this with an idea from plarroy to also
|
might be useful. I combined this with an idea from plarroy to also
|
||||||
allow checking a free pointer to make sure it's valid.
|
allow checking a free pointer to make sure it's valid.
|
||||||
|
|
||||||
|
Dimitry Frank contributed many helpful additions to make things more
|
||||||
|
robust including a user specified config file and a method of testing
|
||||||
|
the integrity of the data structures.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Copy the `umm_malloc_cfg_example.h` file to `umm_malloc_cfg.h` and
|
||||||
|
make the changes required to support your application.
|
||||||
|
|
||||||
|
The following `#define`s must be set to something useful for the
|
||||||
|
library to work at all
|
||||||
|
|
||||||
|
- `UMM_MALLOC_CFG_HEAP_ADDR` must be set to the symbol representing
|
||||||
|
the starting address of the heap. The heap must be
|
||||||
|
aligned on the natural boundary size of the processor.
|
||||||
|
- `UMM_MALLOC_CFG_HEAP_SIZE` must be set to the size of the heap.
|
||||||
|
The heap size must be a multiple of the natural boundary size of
|
||||||
|
the processor.
|
||||||
|
|
||||||
|
The fit algorithm is defined as either:
|
||||||
|
|
||||||
|
- `UMM_BEST_FIT` which scans the entire free list and looks
|
||||||
|
for either an exact fit or the smallest block that will
|
||||||
|
satisfy the request. This is the default fit method.
|
||||||
|
- `UMM_FIRST_FIT` which scans the entire free list and looks
|
||||||
|
for the first block that satisfies the request.
|
||||||
|
|
||||||
|
The following `#define`s are disabled by default and should
|
||||||
|
remain disabled for production use. They are helpful when
|
||||||
|
testing allocation errors (which are normally due to bugs in
|
||||||
|
the application code) or for running the test suite when
|
||||||
|
making changes to the code.
|
||||||
|
|
||||||
|
You can define them in your compiler command line or uncomment
|
||||||
|
the corresponding entries is `umm_malloc_cfg.h`:
|
||||||
|
|
||||||
|
- `UMM_INFO` is used to include code that allows dumping
|
||||||
|
the entire heap structure (helpful when there's a problem).
|
||||||
|
|
||||||
|
- `UMM_INTEGRITY_CHECK` is used to include code that
|
||||||
|
performs an integrity check on the heap structure. It's
|
||||||
|
up to you to call the `umm_integrity_check()` function.
|
||||||
|
|
||||||
|
- `UMM_POISON_CHECK` is used to include code that
|
||||||
|
adds some bytes around the memory being allocated that
|
||||||
|
are filled with known data. If the data is not intact
|
||||||
|
when the block is checked, then somone has written outside
|
||||||
|
of the memory block they have been allocated. It is up
|
||||||
|
to you to call the `umm_poison_check()` function.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
The following functions are available for your application:
|
||||||
|
|
||||||
|
- `void *umm_malloc( size_t size );`
|
||||||
|
- `void *umm_calloc( size_t num, size_t size );`
|
||||||
|
- `void *umm_realloc( void *ptr, size_t size );`
|
||||||
|
- `void umm_free( void *ptr );`
|
||||||
|
|
||||||
|
They have exactly the same semantics as the corresponding standard library
|
||||||
|
functions.
|
||||||
|
|
||||||
## Background
|
## Background
|
||||||
|
|
||||||
The memory manager assumes the following things:
|
The memory manager assumes the following things:
|
||||||
|
|
||||||
1. The standard POSIX compliant malloc/realloc/free semantics are used
|
1. The standard POSIX compliant malloc/calloc/realloc/free semantics are used
|
||||||
1. All memory used by the manager is allocated at link time, it is aligned
|
1. All memory used by the manager is allocated at link time, it is aligned
|
||||||
on a 32 bit boundary, it is contiguous, and its extent (start and end
|
on a 32 bit boundary, it is contiguous, and its extent (start and end
|
||||||
address) is filled in by the linker.
|
address) is filled in by the linker.
|
||||||
@ -45,17 +107,17 @@ The fastest linked list implementations use doubly linked lists so that
|
|||||||
its possible to insert and delete blocks in constant time. This memory
|
its possible to insert and delete blocks in constant time. This memory
|
||||||
manager keeps track of both free and used blocks in a doubly linked list.
|
manager keeps track of both free and used blocks in a doubly linked list.
|
||||||
|
|
||||||
Most memory managers use some kind of list structure made up of pointers
|
Most memory managers use a list structure made up of pointers
|
||||||
to keep track of used - and sometimes free - blocks of memory. In an
|
to keep track of used - and sometimes free - blocks of memory. In an
|
||||||
embedded system, this can get pretty expensive as each pointer can use
|
embedded system, this can get pretty expensive as each pointer can use
|
||||||
up to 32 bits.
|
up to 32 bits.
|
||||||
|
|
||||||
In most embedded systems there is no need for managing large blocks
|
In most embedded systems there is no need for managing a large quantity
|
||||||
of memory dynamically, so a full 32 bit pointer based data structure
|
of memory blocks dynamically, so a full 32 bit pointer based data structure
|
||||||
for the free and used block lists is wasteful. A block of memory on
|
for the free and used block lists is wasteful. A block of memory on
|
||||||
the free list would use 16 bytes just for the pointers!
|
the free list would use 16 bytes just for the pointers!
|
||||||
|
|
||||||
This memory management library sees the malloc heap as an array of blocks,
|
This memory management library sees the heap as an array of blocks,
|
||||||
and uses block numbers to keep track of locations. The block numbers are
|
and uses block numbers to keep track of locations. The block numbers are
|
||||||
15 bits - which allows for up to 32767 blocks of memory. The high order
|
15 bits - which allows for up to 32767 blocks of memory. The high order
|
||||||
bit marks a block as being either free or in use, which will be explained
|
bit marks a block as being either free or in use, which will be explained
|
||||||
@ -75,7 +137,7 @@ can always add more data bytes to the body of the memory block
|
|||||||
at the expense of free block size overhead.
|
at the expense of free block size overhead.
|
||||||
|
|
||||||
There are a lot of little features and optimizations in this memory
|
There are a lot of little features and optimizations in this memory
|
||||||
management system that makes it especially suited to small embedded, but
|
management system that makes it especially suited to small systems, and
|
||||||
the best way to appreciate them is to review the data structures and
|
the best way to appreciate them is to review the data structures and
|
||||||
algorithms used, so let's get started.
|
algorithms used, so let's get started.
|
||||||
|
|
||||||
@ -124,10 +186,9 @@ Where:
|
|||||||
- n is the index of the next block in the heap
|
- n is the index of the next block in the heap
|
||||||
- p is the index of the previous block in the heap
|
- p is the index of the previous block in the heap
|
||||||
|
|
||||||
Note that the free list information is gone, because it's now being used to
|
Note that the free list information is gone because it's now
|
||||||
store actual data for the application. It would have been nice to store
|
being used to store actual data for the application. If we had
|
||||||
the next and previous free list indexes as well, but that would be a waste
|
even 500 items in use, that would be 2,000 bytes for
|
||||||
of space. If we had even 500 items in use, that would be 2,000 bytes for
|
|
||||||
free list information. We simply can't afford to waste that much.
|
free list information. We simply can't afford to waste that much.
|
||||||
|
|
||||||
The address of the `...` area is what is returned to the application
|
The address of the `...` area is what is returned to the application
|
||||||
@ -143,7 +204,7 @@ described.
|
|||||||
`...` between memory blocks indicates zero or more additional blocks are
|
`...` between memory blocks indicates zero or more additional blocks are
|
||||||
allocated for use by the upper block.
|
allocated for use by the upper block.
|
||||||
|
|
||||||
And while we're talking about "upper" and "lower" blocks, we should make
|
While we're talking about "upper" and "lower" blocks, we should make
|
||||||
a comment about adresses. In the diagrams, a block higher up in the
|
a comment about adresses. In the diagrams, a block higher up in the
|
||||||
picture is at a lower address. And the blocks grow downwards their
|
picture is at a lower address. And the blocks grow downwards their
|
||||||
block index increases as does their physical address.
|
block index increases as does their physical address.
|
||||||
@ -166,24 +227,27 @@ we're at the end of the list!
|
|||||||
|
|
||||||
At this point, the malloc has a special test that checks if the current
|
At this point, the malloc has a special test that checks if the current
|
||||||
block index is 0, which it is. This special case initializes the free
|
block index is 0, which it is. This special case initializes the free
|
||||||
list to point at block index 1.
|
list to point at block index 1 and then points block 1 to the
|
||||||
|
last block (lf) on the heap.
|
||||||
|
|
||||||
```
|
```
|
||||||
BEFORE AFTER
|
BEFORE AFTER
|
||||||
|
|
||||||
+----+----+----+----+ +----+----+----+----+
|
+----+----+----+----+ +----+----+----+----+
|
||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
|
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 |
|
||||||
+----+----+----+----+ +----+----+----+----+
|
+----+----+----+----+ +----+----+----+----+
|
||||||
+----+----+----+----+
|
+----+----+----+----+
|
||||||
1 | 0 | 0 | 0 | 0 |
|
1 |*lf | 0 | 0 | 0 |
|
||||||
|
+----+----+----+----+
|
||||||
|
...
|
||||||
|
+----+----+----+----+
|
||||||
|
lf | 0 | 1 | 0 | 0 |
|
||||||
+----+----+----+----+
|
+----+----+----+----+
|
||||||
```
|
```
|
||||||
|
|
||||||
The heap is now ready to complete the first malloc operation.
|
The heap is now ready to complete the first malloc operation.
|
||||||
|
|
||||||
|
### Operation of malloc when we have reached the end of the free list and there is no block large enough to accommodate the request.
|
||||||
### Operation of malloc when we have reached the end of the free list and
|
|
||||||
there is no block large enough to accommodate the request.
|
|
||||||
|
|
||||||
This happens at the very first malloc operation, or any time the free
|
This happens at the very first malloc operation, or any time the free
|
||||||
list is traversed and no free block large enough for the request is
|
list is traversed and no free block large enough for the request is
|
||||||
@ -306,8 +370,7 @@ else
|
|||||||
```
|
```
|
||||||
|
|
||||||
Step 1 of the free operation checks if the next block is free, and if it
|
Step 1 of the free operation checks if the next block is free, and if it
|
||||||
is then insert this block into the free list and assimilate the next block
|
is assimilate the next block with this one.
|
||||||
with this one.
|
|
||||||
|
|
||||||
Note that c is the block we are freeing up, cf is the free block that
|
Note that c is the block we are freeing up, cf is the free block that
|
||||||
follows it.
|
follows it.
|
||||||
|
21
cores/esp8266/umm_malloc/dbglog/LICENSE
Normal file
21
cores/esp8266/umm_malloc/dbglog/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 Ralph Hempel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
1
cores/esp8266/umm_malloc/dbglog/README.txt
Normal file
1
cores/esp8266/umm_malloc/dbglog/README.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Downloaded from: https://github.com/rhempel/c-helper-macros/tree/develop
|
98
cores/esp8266/umm_malloc/dbglog/dbglog.h
Normal file
98
cores/esp8266/umm_malloc/dbglog/dbglog.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* dbglog.h - A set of macros that cleans up code that needs to produce debug
|
||||||
|
* or log information.
|
||||||
|
*
|
||||||
|
* Many embedded systems still put a premium on code space and therefore need
|
||||||
|
* a way to conditionally compile in debug code. Yes, it can lead to code that
|
||||||
|
* runs differently depending on whether the debug code is cmpiled in or not
|
||||||
|
* but you need to be able to evaluate the tradeoff.
|
||||||
|
*
|
||||||
|
* See copyright notice in LICENSE.TXT
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
* NOTE WELL that this file may be included multiple times - this allows you
|
||||||
|
* 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:
|
||||||
|
*
|
||||||
|
* #undef DBGLOG_LEVEL
|
||||||
|
* #define DBGLOG_LEVEL 6
|
||||||
|
* #include "dbglog/dbglog.txt"
|
||||||
|
*
|
||||||
|
* To handle multiple inclusion, we need to first undefine any macros we define
|
||||||
|
* so that the compiler does not warn us that we are changing a macro.
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
* The DBGLOG_LEVEL and DBGLOG_FUNCTION should be defined BEFORE this
|
||||||
|
* file is included or else the following defaults are used:
|
||||||
|
*
|
||||||
|
* #define DBGLOG_LEVEL 0
|
||||||
|
* #define DBGLOG_FUNCTION printf
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
* There are macros to handle the following decreasing levels of detail:
|
||||||
|
*
|
||||||
|
* 6 = TRACE
|
||||||
|
* 5 = DEBUG
|
||||||
|
* 4 = CRITICAL
|
||||||
|
* 3 = ERROR
|
||||||
|
* 2 = WARNING
|
||||||
|
* 1 = INFO
|
||||||
|
* 0 = FORCE - The DBGLOG_FUNCTION is always compiled in and is called only when
|
||||||
|
* the first parameter to the macro is non-0
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#undef DBGLOG_TRACE
|
||||||
|
#undef DBGLOG_DEBUG
|
||||||
|
#undef DBGLOG_CRITICAL
|
||||||
|
#undef DBGLOG_ERROR
|
||||||
|
#undef DBGLOG_WARNING
|
||||||
|
#undef DBGLOG_INFO
|
||||||
|
#undef DBGLOG_FORCE
|
||||||
|
|
||||||
|
#ifndef DBGLOG_LEVEL
|
||||||
|
# define DBGLOG_LEVEL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DBGLOG_FUNCTION
|
||||||
|
# define DBGLOG_FUNCTION printf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#if DBGLOG_LEVEL >= 6
|
||||||
|
# define DBGLOG_TRACE(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
# define DBGLOG_TRACE(format, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DBGLOG_LEVEL >= 5
|
||||||
|
# define DBGLOG_DEBUG(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
# define DBGLOG_DEBUG(format, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DBGLOG_LEVEL >= 4
|
||||||
|
# define DBGLOG_CRITICAL(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
# define DBGLOG_CRITICAL(format, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DBGLOG_LEVEL >= 3
|
||||||
|
# define DBGLOG_ERROR(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
# define DBGLOG_ERROR(format, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DBGLOG_LEVEL >= 2
|
||||||
|
# define DBGLOG_WARNING(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
# define DBGLOG_WARNING(format, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DBGLOG_LEVEL >= 1
|
||||||
|
# define DBGLOG_INFO(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
# define DBGLOG_INFO(format, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DBGLOG_FORCE(force, format, ...) {if(force) {DBGLOG_FUNCTION(format, ## __VA_ARGS__);}}
|
185
cores/esp8266/umm_malloc/umm_info.c
Normal file
185
cores/esp8266/umm_malloc/umm_info.c
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
#if defined(BUILD_UMM_MALLOC_C)
|
||||||
|
|
||||||
|
#ifdef UMM_INFO
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* One of the coolest things about this little library is that it's VERY
|
||||||
|
* easy to get debug information about the memory heap by simply iterating
|
||||||
|
* through all of the memory blocks.
|
||||||
|
*
|
||||||
|
* As you go through all the blocks, you can check to see if it's a free
|
||||||
|
* block by looking at the high order bit of the next block index. You can
|
||||||
|
* also see how big the block is by subtracting the next block index from
|
||||||
|
* the current block number.
|
||||||
|
*
|
||||||
|
* The umm_info function does all of that and makes the results available
|
||||||
|
* in the ummHeapInfo structure.
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
UMM_HEAP_INFO ummHeapInfo;
|
||||||
|
|
||||||
|
void *umm_info( void *ptr, int force ) {
|
||||||
|
UMM_CRITICAL_DECL(id_info);
|
||||||
|
|
||||||
|
unsigned short int blockNo = 0;
|
||||||
|
|
||||||
|
if (umm_heap == NULL) {
|
||||||
|
umm_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Protect the critical section... */
|
||||||
|
UMM_CRITICAL_ENTRY(id_info);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear out all of the entries in the ummHeapInfo structure before doing
|
||||||
|
* any calculations..
|
||||||
|
*/
|
||||||
|
memset( &ummHeapInfo, 0, sizeof( ummHeapInfo ) );
|
||||||
|
|
||||||
|
DBGLOG_FORCE( force, "\n" );
|
||||||
|
DBGLOG_FORCE( force, "+----------+-------+--------+--------+-------+--------+--------+\n" );
|
||||||
|
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n",
|
||||||
|
(unsigned long)(&UMM_BLOCK(blockNo)),
|
||||||
|
blockNo,
|
||||||
|
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
||||||
|
UMM_PBLOCK(blockNo),
|
||||||
|
(UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo,
|
||||||
|
UMM_NFREE(blockNo),
|
||||||
|
UMM_PFREE(blockNo) );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now loop through the block lists, and keep track of the number and size
|
||||||
|
* of used and free blocks. The terminating condition is an nb pointer with
|
||||||
|
* a value of zero...
|
||||||
|
*/
|
||||||
|
|
||||||
|
blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK;
|
||||||
|
|
||||||
|
while( UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK ) {
|
||||||
|
size_t curBlocks = (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo;
|
||||||
|
|
||||||
|
++ummHeapInfo.totalEntries;
|
||||||
|
ummHeapInfo.totalBlocks += curBlocks;
|
||||||
|
|
||||||
|
/* Is this a free block? */
|
||||||
|
|
||||||
|
if( UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK ) {
|
||||||
|
++ummHeapInfo.freeEntries;
|
||||||
|
ummHeapInfo.freeBlocks += curBlocks;
|
||||||
|
ummHeapInfo.freeSize2 += (unsigned int)curBlocks
|
||||||
|
* (unsigned int)sizeof(umm_block)
|
||||||
|
* (unsigned int)curBlocks
|
||||||
|
* (unsigned int)sizeof(umm_block);
|
||||||
|
|
||||||
|
if (ummHeapInfo.maxFreeContiguousBlocks < curBlocks) {
|
||||||
|
ummHeapInfo.maxFreeContiguousBlocks = curBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|NF %5d|PF %5d|\n",
|
||||||
|
(unsigned long)(&UMM_BLOCK(blockNo)),
|
||||||
|
blockNo,
|
||||||
|
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
||||||
|
UMM_PBLOCK(blockNo),
|
||||||
|
(unsigned int)curBlocks,
|
||||||
|
UMM_NFREE(blockNo),
|
||||||
|
UMM_PFREE(blockNo) );
|
||||||
|
|
||||||
|
/* Does this block address match the ptr we may be trying to free? */
|
||||||
|
|
||||||
|
if( ptr == &UMM_BLOCK(blockNo) ) {
|
||||||
|
|
||||||
|
/* Release the critical section... */
|
||||||
|
UMM_CRITICAL_EXIT(id_info);
|
||||||
|
|
||||||
|
return( ptr );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
++ummHeapInfo.usedEntries;
|
||||||
|
ummHeapInfo.usedBlocks += curBlocks;
|
||||||
|
|
||||||
|
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|\n",
|
||||||
|
(unsigned long)(&UMM_BLOCK(blockNo)),
|
||||||
|
blockNo,
|
||||||
|
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
||||||
|
UMM_PBLOCK(blockNo),
|
||||||
|
(unsigned int)curBlocks );
|
||||||
|
}
|
||||||
|
|
||||||
|
blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the accounting totals with information from the last block, the
|
||||||
|
* rest must be free!
|
||||||
|
*/
|
||||||
|
|
||||||
|
{
|
||||||
|
size_t curBlocks = UMM_NUMBLOCKS-blockNo;
|
||||||
|
ummHeapInfo.freeBlocks += curBlocks;
|
||||||
|
ummHeapInfo.totalBlocks += curBlocks;
|
||||||
|
|
||||||
|
if (ummHeapInfo.maxFreeContiguousBlocks < curBlocks) {
|
||||||
|
ummHeapInfo.maxFreeContiguousBlocks = curBlocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n",
|
||||||
|
(unsigned long)(&UMM_BLOCK(blockNo)),
|
||||||
|
blockNo,
|
||||||
|
UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK,
|
||||||
|
UMM_PBLOCK(blockNo),
|
||||||
|
UMM_NUMBLOCKS-blockNo,
|
||||||
|
UMM_NFREE(blockNo),
|
||||||
|
UMM_PFREE(blockNo) );
|
||||||
|
|
||||||
|
DBGLOG_FORCE( force, "+----------+-------+--------+--------+-------+--------+--------+\n" );
|
||||||
|
|
||||||
|
DBGLOG_FORCE( force, "Total Entries %5d Used Entries %5d Free Entries %5d\n",
|
||||||
|
ummHeapInfo.totalEntries,
|
||||||
|
ummHeapInfo.usedEntries,
|
||||||
|
ummHeapInfo.freeEntries );
|
||||||
|
|
||||||
|
DBGLOG_FORCE( force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n",
|
||||||
|
ummHeapInfo.totalBlocks,
|
||||||
|
ummHeapInfo.usedBlocks,
|
||||||
|
ummHeapInfo.freeBlocks );
|
||||||
|
|
||||||
|
DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" );
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
|
if (ummHeapInfo.freeBlocks == ummStats.free_blocks) {
|
||||||
|
DBGLOG_FORCE( force, "heap info Free blocks and heap statistics Free blocks match.\n");
|
||||||
|
} else {
|
||||||
|
DBGLOG_FORCE( force, "\nheap info Free blocks %5d != heap statistics Free Blocks %5d\n\n",
|
||||||
|
ummHeapInfo.freeBlocks,
|
||||||
|
ummStats.free_blocks );
|
||||||
|
}
|
||||||
|
DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" );
|
||||||
|
|
||||||
|
print_stats(force);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Release the critical section... */
|
||||||
|
UMM_CRITICAL_EXIT(id_info);
|
||||||
|
|
||||||
|
return( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
size_t umm_free_heap_size( void ) {
|
||||||
|
umm_info(NULL, 0);
|
||||||
|
return (size_t)ummHeapInfo.freeBlocks * sizeof(umm_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t umm_max_block_size( void ) {
|
||||||
|
umm_info(NULL, 0);
|
||||||
|
return ummHeapInfo.maxFreeContiguousBlocks * sizeof(umm_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // defined(BUILD_UMM_MALLOC_C)
|
134
cores/esp8266/umm_malloc/umm_integrity.c
Normal file
134
cores/esp8266/umm_malloc/umm_integrity.c
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
#if defined(BUILD_UMM_MALLOC_C)
|
||||||
|
/* integrity check (UMM_INTEGRITY_CHECK) {{{ */
|
||||||
|
#if defined(UMM_INTEGRITY_CHECK)
|
||||||
|
/*
|
||||||
|
* Perform integrity check of the whole heap data. Returns 1 in case of
|
||||||
|
* success, 0 otherwise.
|
||||||
|
*
|
||||||
|
* First of all, iterate through all free blocks, and check that all backlinks
|
||||||
|
* match (i.e. if block X has next free block Y, then the block Y should have
|
||||||
|
* previous free block set to X).
|
||||||
|
*
|
||||||
|
* Additionally, we check that each free block is correctly marked with
|
||||||
|
* `UMM_FREELIST_MASK` on the `next` pointer: during iteration through free
|
||||||
|
* list, we mark each free block by the same flag `UMM_FREELIST_MASK`, but
|
||||||
|
* on `prev` pointer. We'll check and unmark it later.
|
||||||
|
*
|
||||||
|
* Then, we iterate through all blocks in the heap, and similarly check that
|
||||||
|
* all backlinks match (i.e. if block X has next block Y, then the block Y
|
||||||
|
* should have previous block set to X).
|
||||||
|
*
|
||||||
|
* But before checking each backlink, we check that the `next` and `prev`
|
||||||
|
* pointers are both marked with `UMM_FREELIST_MASK`, or both unmarked.
|
||||||
|
* This way, we ensure that the free flag is in sync with the free pointers
|
||||||
|
* chain.
|
||||||
|
*/
|
||||||
|
int umm_integrity_check(void) {
|
||||||
|
UMM_CRITICAL_DECL(id_integrity);
|
||||||
|
int ok = 1;
|
||||||
|
unsigned short int prev;
|
||||||
|
unsigned short int cur;
|
||||||
|
|
||||||
|
if (umm_heap == NULL) {
|
||||||
|
umm_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate through all free blocks */
|
||||||
|
prev = 0;
|
||||||
|
UMM_CRITICAL_ENTRY(id_integrity);
|
||||||
|
while(1) {
|
||||||
|
cur = UMM_NFREE(prev);
|
||||||
|
|
||||||
|
/* Check that next free block number is valid */
|
||||||
|
if (cur >= UMM_NUMBLOCKS) {
|
||||||
|
DBGLOG_FUNCTION("heap integrity broken: too large next free num: %d "
|
||||||
|
"(in block %d, addr 0x%lx)\n", cur, prev,
|
||||||
|
(unsigned long)&UMM_NBLOCK(prev));
|
||||||
|
ok = 0;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
if (cur == 0) {
|
||||||
|
/* No more free blocks */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if prev free block number matches */
|
||||||
|
if (UMM_PFREE(cur) != prev) {
|
||||||
|
DBGLOG_FUNCTION("heap integrity broken: free links don't match: "
|
||||||
|
"%d -> %d, but %d -> %d\n",
|
||||||
|
prev, cur, cur, UMM_PFREE(cur));
|
||||||
|
ok = 0;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
UMM_PBLOCK(cur) |= UMM_FREELIST_MASK;
|
||||||
|
|
||||||
|
prev = cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate through all blocks */
|
||||||
|
prev = 0;
|
||||||
|
while(1) {
|
||||||
|
cur = UMM_NBLOCK(prev) & UMM_BLOCKNO_MASK;
|
||||||
|
|
||||||
|
/* Check that next block number is valid */
|
||||||
|
if (cur >= UMM_NUMBLOCKS) {
|
||||||
|
DBGLOG_FUNCTION("heap integrity broken: too large next block num: %d "
|
||||||
|
"(in block %d, addr 0x%lx)\n", cur, prev,
|
||||||
|
(unsigned long)&UMM_NBLOCK(prev));
|
||||||
|
ok = 0;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
if (cur == 0) {
|
||||||
|
/* No more blocks */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure the free mark is appropriate, and unmark it */
|
||||||
|
if ((UMM_NBLOCK(cur) & UMM_FREELIST_MASK)
|
||||||
|
!= (UMM_PBLOCK(cur) & UMM_FREELIST_MASK))
|
||||||
|
{
|
||||||
|
DBGLOG_FUNCTION("heap integrity broken: mask wrong at addr 0x%lx: n=0x%x, p=0x%x\n",
|
||||||
|
(unsigned long)&UMM_NBLOCK(cur),
|
||||||
|
(UMM_NBLOCK(cur) & UMM_FREELIST_MASK),
|
||||||
|
(UMM_PBLOCK(cur) & UMM_FREELIST_MASK)
|
||||||
|
);
|
||||||
|
ok = 0;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure the block list is sequential */
|
||||||
|
if (cur <= prev ) {
|
||||||
|
DBGLOG_FUNCTION("heap integrity broken: next block %d is before prev this one "
|
||||||
|
"(in block %d, addr 0x%lx)\n", cur, prev,
|
||||||
|
(unsigned long)&UMM_NBLOCK(prev));
|
||||||
|
ok = 0;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unmark */
|
||||||
|
UMM_PBLOCK(cur) &= UMM_BLOCKNO_MASK;
|
||||||
|
|
||||||
|
/* Check if prev block number matches */
|
||||||
|
if (UMM_PBLOCK(cur) != prev) {
|
||||||
|
DBGLOG_FUNCTION("heap integrity broken: block links don't match: "
|
||||||
|
"%d -> %d, but %d -> %d\n",
|
||||||
|
prev, cur, cur, UMM_PBLOCK(cur));
|
||||||
|
ok = 0;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
clean:
|
||||||
|
UMM_CRITICAL_EXIT(id_integrity);
|
||||||
|
if (!ok){
|
||||||
|
UMM_HEAP_CORRUPTION_CB();
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
/* }}} */
|
||||||
|
#endif // defined(BUILD_UMM_MALLOC_C)
|
215
cores/esp8266/umm_malloc/umm_local.c
Normal file
215
cores/esp8266/umm_malloc/umm_local.c
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* Local Additions/Enhancements
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if defined(BUILD_UMM_MALLOC_C)
|
||||||
|
|
||||||
|
#if defined(UMM_CRITICAL_METRICS)
|
||||||
|
/*
|
||||||
|
* umm_malloc performance measurments for critical sections
|
||||||
|
*/
|
||||||
|
UMM_TIME_STATS time_stats = {
|
||||||
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
|
#ifdef UMM_INFO
|
||||||
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
|
#endif
|
||||||
|
#ifdef UMM_POISON_CHECK
|
||||||
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
|
#endif
|
||||||
|
#ifdef UMM_INTEGRITY_CHECK
|
||||||
|
{0xFFFFFFFF, 0U, 0U, 0U},
|
||||||
|
#endif
|
||||||
|
{0xFFFFFFFF, 0U, 0U, 0U} };
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
UMM_CRITICAL_ENTRY(id_no_tag);
|
||||||
|
memcpy(p, &time_stats, size);
|
||||||
|
UMM_CRITICAL_EXIT(id_no_tag);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Alternate Poison functions
|
||||||
|
|
||||||
|
#if defined(UMM_POISON_CHECK_LITE)
|
||||||
|
// We skip this when doing the full poison check.
|
||||||
|
|
||||||
|
static int check_poison_neighbors( unsigned short cur ) {
|
||||||
|
unsigned short int c;
|
||||||
|
|
||||||
|
if ( 0 == cur )
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
c = UMM_PBLOCK(cur) & UMM_BLOCKNO_MASK;
|
||||||
|
while( c && (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) ) {
|
||||||
|
/*
|
||||||
|
There can be up to 1 free block neighbor in either direction.
|
||||||
|
This loop should self limit to 2 passes, due to heap design.
|
||||||
|
i.e. Adjacent free space is always consolidated.
|
||||||
|
*/
|
||||||
|
if ( !(UMM_NBLOCK(c) & UMM_FREELIST_MASK) ) {
|
||||||
|
if ( !check_poison_block(&UMM_BLOCK(c)) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = UMM_PBLOCK(c) & UMM_BLOCKNO_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK;
|
||||||
|
while( (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) ) {
|
||||||
|
if ( !(UMM_NBLOCK(c) & UMM_FREELIST_MASK) ) {
|
||||||
|
if ( !check_poison_block(&UMM_BLOCK(c)) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = UMM_NBLOCK(c) & UMM_BLOCKNO_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void *get_unpoisoned_check_neighbors( void *v_ptr, const char* file, int line ) {
|
||||||
|
unsigned char *ptr = (unsigned char *)v_ptr;
|
||||||
|
|
||||||
|
if (ptr != NULL) {
|
||||||
|
|
||||||
|
ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
|
||||||
|
|
||||||
|
#if defined(UMM_POISON_CHECK_LITE)
|
||||||
|
UMM_CRITICAL_DECL(id_poison);
|
||||||
|
unsigned short int c;
|
||||||
|
bool poison = false;
|
||||||
|
|
||||||
|
/* Figure out which block we're in. Note the use of truncated division... */
|
||||||
|
c = (((char *)ptr)-(char *)(&(umm_heap[0])))/sizeof(umm_block);
|
||||||
|
|
||||||
|
UMM_CRITICAL_ENTRY(id_poison);
|
||||||
|
poison = check_poison_block(&UMM_BLOCK(c)) && check_poison_neighbors(c);
|
||||||
|
UMM_CRITICAL_EXIT(id_poison);
|
||||||
|
|
||||||
|
if (!poison) {
|
||||||
|
if (file) {
|
||||||
|
__panic_func(file, line, "");
|
||||||
|
} else {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* No need to check poison here. POISON_CHECK() has already done a
|
||||||
|
* full heap check.
|
||||||
|
*/
|
||||||
|
(void)file;
|
||||||
|
(void)line;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void *)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void *umm_poison_realloc_fl(void *ptr, size_t size, const char* file, int line) {
|
||||||
|
void *ret;
|
||||||
|
|
||||||
|
ptr = get_unpoisoned_check_neighbors(ptr, file, line);
|
||||||
|
|
||||||
|
size += poison_size(size);
|
||||||
|
ret = umm_realloc(ptr, size);
|
||||||
|
|
||||||
|
ret = get_poisoned(ret, size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void umm_poison_free_fl(void *ptr, const char* file, int line) {
|
||||||
|
|
||||||
|
ptr = get_unpoisoned_check_neighbors(ptr, file, line);
|
||||||
|
|
||||||
|
umm_free(ptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
|
||||||
|
size_t umm_block_size( void ) {
|
||||||
|
return sizeof(umm_block);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
|
UMM_STATISTICS ummStats;
|
||||||
|
|
||||||
|
// Keep complete call path in IRAM
|
||||||
|
size_t umm_free_heap_size_lw( void ) {
|
||||||
|
return (size_t)ummStats.free_blocks * sizeof(umm_block);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
I assume xPortGetFreeHeapSize needs to be in IRAM. Since
|
||||||
|
system_get_free_heap_size is in IRAM. Which would mean, umm_free_heap_size()
|
||||||
|
in flash, was not a safe alternative for returning the same information.
|
||||||
|
*/
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
|
size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size_lw")));
|
||||||
|
#elif defined(UMM_INFO)
|
||||||
|
#warning "No ISR safe function available to implement xPortGetFreeHeapSize()"
|
||||||
|
size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size")));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
|
void print_stats(int force) {
|
||||||
|
DBGLOG_FORCE( force, "umm heap statistics:\n");
|
||||||
|
DBGLOG_FORCE( force, " Free Space %5u\n", ummStats.free_blocks * sizeof(umm_block));
|
||||||
|
DBGLOG_FORCE( force, " OOM Count %5u\n", ummStats.oom_count);
|
||||||
|
#if defined(UMM_STATS_FULL)
|
||||||
|
DBGLOG_FORCE( force, " Low Watermark %5u\n", ummStats.free_blocks_min * sizeof(umm_block));
|
||||||
|
DBGLOG_FORCE( force, " Low Watermark ISR %5u\n", ummStats.free_blocks_isr_min * sizeof(umm_block));
|
||||||
|
DBGLOG_FORCE( force, " MAX Alloc Request %5u\n", ummStats.alloc_max_size);
|
||||||
|
#endif
|
||||||
|
DBGLOG_FORCE( force, " Size of umm_block %5u\n", sizeof(umm_block));
|
||||||
|
DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) {
|
||||||
|
/*
|
||||||
|
To use ets_strlen() and ets_strcpy() safely with PROGMEM, flash storage,
|
||||||
|
the PROGMEM address must be word (4 bytes) aligned. The destination
|
||||||
|
address for ets_memcpy must also be word-aligned.
|
||||||
|
*/
|
||||||
|
char ram_buf[ets_strlen(fmt)] __attribute__ ((aligned(4)));
|
||||||
|
ets_strcpy(ram_buf, fmt);
|
||||||
|
va_list argPtr;
|
||||||
|
va_start(argPtr, fmt);
|
||||||
|
int result = ets_vprintf(ets_uart_putc1, ram_buf, argPtr);
|
||||||
|
va_end(argPtr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BUILD_UMM_MALLOC_C
|
||||||
|
|
||||||
|
|
54
cores/esp8266/umm_malloc/umm_local.h
Normal file
54
cores/esp8266/umm_malloc/umm_local.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#ifndef _UMM_LOCAL_H
|
||||||
|
#define _UMM_LOCAL_H
|
||||||
|
/*
|
||||||
|
* A home for local items exclusive to umm_malloc.c and not to be shared in
|
||||||
|
* umm_malloc_cfg.h. And, not for upstream version.
|
||||||
|
* Also used to redefine defines made in upstream files we donet want to edit.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#undef memcpy
|
||||||
|
#undef memmove
|
||||||
|
#undef memset
|
||||||
|
#define memcpy ets_memcpy
|
||||||
|
#define memmove ets_memmove
|
||||||
|
#define memset ets_memset
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This redefines DBGLOG_FORCE defined in dbglog/dbglog.h
|
||||||
|
* Just for printing from umm_info() which is assumed to always be called from
|
||||||
|
* non-ISR. Thus SPI bus is available to handle cache-miss and reading a flash
|
||||||
|
* string while INTLEVEL is non-zero.
|
||||||
|
*/
|
||||||
|
#undef DBGLOG_FORCE
|
||||||
|
#define DBGLOG_FORCE(force, format, ...) {if(force) {UMM_INFO_PRINTF(format, ## __VA_ARGS__);}}
|
||||||
|
// #define DBGLOG_FORCE(force, format, ...) {if(force) {::printf(PSTR(format), ## __VA_ARGS__);}}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(DEBUG_ESP_OOM) || defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || defined(UMM_INTEGRITY_CHECK)
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define umm_malloc(s) malloc(s)
|
||||||
|
#define umm_calloc(n,s) calloc(n,s)
|
||||||
|
#define umm_realloc(p,s) realloc(p,s)
|
||||||
|
#define umm_free(p) free(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(UMM_POISON_CHECK_LITE)
|
||||||
|
static int check_poison_neighbors( unsigned short cur );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
|
void ICACHE_FLASH_ATTR print_stats(int force);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||||
|
#define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR(fmt), ##__VA_ARGS__)
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
@ -10,42 +10,19 @@
|
|||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
//C This include is not in upstream neither are the #ifdef __cplusplus
|
||||||
#include "umm_malloc_cfg.h" /* user-dependent */
|
#include "umm_malloc_cfg.h" /* user-dependent */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct UMM_HEAP_INFO_t {
|
|
||||||
unsigned short int totalEntries;
|
|
||||||
unsigned short int usedEntries;
|
|
||||||
unsigned short int freeEntries;
|
|
||||||
|
|
||||||
unsigned short int totalBlocks;
|
|
||||||
unsigned short int usedBlocks;
|
|
||||||
unsigned short int freeBlocks;
|
|
||||||
|
|
||||||
unsigned short int maxFreeContiguousBlocks;
|
|
||||||
|
|
||||||
unsigned int freeSize2;
|
|
||||||
}
|
|
||||||
UMM_HEAP_INFO;
|
|
||||||
|
|
||||||
extern UMM_HEAP_INFO ummHeapInfo;
|
|
||||||
|
|
||||||
void umm_init( void );
|
void umm_init( void );
|
||||||
|
|
||||||
void *umm_info( void *ptr, int force );
|
|
||||||
|
|
||||||
void *umm_malloc( size_t size );
|
void *umm_malloc( size_t size );
|
||||||
void *umm_calloc( size_t num, size_t size );
|
void *umm_calloc( size_t num, size_t size );
|
||||||
void *umm_realloc( void *ptr, size_t size );
|
void *umm_realloc( void *ptr, size_t size );
|
||||||
void umm_free( void *ptr );
|
void umm_free( void *ptr );
|
||||||
|
|
||||||
size_t umm_free_heap_size( void );
|
|
||||||
size_t umm_max_block_size( void );
|
|
||||||
size_t umm_block_size( void );
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Configuration for umm_malloc
|
* Configuration for umm_malloc - target Arduino ESP8266 core
|
||||||
|
*
|
||||||
|
* Changes specific to a target platform go here.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _UMM_MALLOC_CFG_H
|
#ifndef _UMM_MALLOC_CFG_H
|
||||||
#define _UMM_MALLOC_CFG_H
|
#define _UMM_MALLOC_CFG_H
|
||||||
|
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
#include <pgmspace.h>
|
||||||
|
#include <esp8266_undocumented.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@ -15,19 +21,6 @@ extern "C" {
|
|||||||
#include <osapi.h>
|
#include <osapi.h>
|
||||||
|
|
||||||
#include "c_types.h"
|
#include "c_types.h"
|
||||||
#include "umm_performance.h"
|
|
||||||
#include "umm_stats.h"
|
|
||||||
|
|
||||||
#undef DBGLOG_FUNCTION
|
|
||||||
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_ISR)
|
|
||||||
int _isr_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
|
||||||
// Note, _isr_safe_printf_P will not handle additional string arguments in
|
|
||||||
// PROGMEM. Only the 1st parameter, fmt, is supported in PROGMEM.
|
|
||||||
#define DBGLOG_FUNCTION(fmt, ...) _isr_safe_printf_P(PSTR(fmt), ##__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
// Macro to place constant strings into PROGMEM and print them properly
|
|
||||||
#define DBGLOG_FUNCTION(fmt, ...) printf(PSTR(fmt), ## __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are a number of defines you can set at compile time that affect how
|
* There are a number of defines you can set at compile time that affect how
|
||||||
@ -37,19 +30,7 @@ int _isr_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)
|
|||||||
*
|
*
|
||||||
* -D UMM_TEST_MAIN
|
* -D UMM_TEST_MAIN
|
||||||
*
|
*
|
||||||
* Set this if you want to compile in the test suite at the end of this file.
|
* Set this if you want to compile in the test suite
|
||||||
*
|
|
||||||
* If you leave this define unset, then you might want to set another one:
|
|
||||||
*
|
|
||||||
* -D UMM_REDEFINE_MEM_FUNCTIONS
|
|
||||||
*
|
|
||||||
* If you leave this define unset, then the function names are left alone as
|
|
||||||
* umm_malloc() umm_free() and umm_realloc() so that they cannot be confused
|
|
||||||
* with the C runtime functions malloc() free() and realloc()
|
|
||||||
*
|
|
||||||
* If you do set this define, then the function names become malloc()
|
|
||||||
* free() and realloc() so that they can be used as the C runtime functions
|
|
||||||
* in an embedded environment.
|
|
||||||
*
|
*
|
||||||
* -D UMM_BEST_FIT (defualt)
|
* -D UMM_BEST_FIT (defualt)
|
||||||
*
|
*
|
||||||
@ -75,46 +56,300 @@ int _isr_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)
|
|||||||
* ----------------------------------------------------------------------------
|
* ----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
#ifdef TEST_BUILD
|
||||||
#ifdef DEBUG_ESP_OOM
|
extern char test_umm_heap[];
|
||||||
|
|
||||||
#define MEMLEAK_DEBUG
|
|
||||||
|
|
||||||
// umm_*alloc are not renamed to *alloc
|
|
||||||
|
|
||||||
void *umm_malloc( size_t size );
|
|
||||||
void *umm_calloc( size_t num, size_t size );
|
|
||||||
void *umm_realloc( void *ptr, size_t size );
|
|
||||||
#define umm_free free
|
|
||||||
#define umm_zalloc(s) umm_calloc(1,s)
|
|
||||||
|
|
||||||
void* malloc_loc (size_t s, const char* file, int line);
|
|
||||||
void* calloc_loc (size_t n, size_t s, const char* file, int line);
|
|
||||||
void* realloc_loc (void* p, size_t s, const char* file, int line);
|
|
||||||
|
|
||||||
// *alloc are macro calling *alloc_loc calling+checking umm_*alloc()
|
|
||||||
// they are defined at the bottom of this file
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
|
||||||
#else // !defined(ESP_DEBUG_OOM)
|
|
||||||
|
|
||||||
// umm_*alloc are renamed to *alloc
|
|
||||||
#define UMM_REDEFINE_MEM_FUNCTIONS
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define UMM_BEST_FIT
|
#ifdef 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 */
|
/* Start addresses and the size of the heap */
|
||||||
extern char _heap_start[];
|
extern char _heap_start[];
|
||||||
#define UMM_MALLOC_CFG__HEAP_ADDR ((uint32_t)&_heap_start)
|
#define UMM_MALLOC_CFG_HEAP_ADDR ((uint32_t)&_heap_start[0])
|
||||||
#define UMM_MALLOC_CFG__HEAP_SIZE ((size_t)(0x3fffc000 - UMM_MALLOC_CFG__HEAP_ADDR))
|
#define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(0x3fffc000 - UMM_MALLOC_CFG_HEAP_ADDR))
|
||||||
|
#endif
|
||||||
|
|
||||||
/* A couple of macros to make packing structures less compiler dependent */
|
/* A couple of macros to make packing structures less compiler dependent */
|
||||||
|
|
||||||
#define UMM_H_ATTPACKPRE
|
#define UMM_H_ATTPACKPRE
|
||||||
#define UMM_H_ATTPACKSUF __attribute__((__packed__))
|
#define UMM_H_ATTPACKSUF __attribute__((__packed__))
|
||||||
|
|
||||||
|
#define UMM_BEST_FIT
|
||||||
|
#undef UMM_FIRST_FIT
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -D UMM_INFO :
|
||||||
|
*
|
||||||
|
* Enables a dup of the heap contents and a function to return the total
|
||||||
|
* heap size that is unallocated - note this is not the same as the largest
|
||||||
|
* unallocated block on the heap!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define UMM_INFO
|
||||||
|
|
||||||
|
#ifdef UMM_INFO
|
||||||
|
typedef struct UMM_HEAP_INFO_t {
|
||||||
|
unsigned short int totalEntries;
|
||||||
|
unsigned short int usedEntries;
|
||||||
|
unsigned short int freeEntries;
|
||||||
|
|
||||||
|
unsigned short int totalBlocks;
|
||||||
|
unsigned short int usedBlocks;
|
||||||
|
unsigned short int freeBlocks;
|
||||||
|
|
||||||
|
unsigned short int maxFreeContiguousBlocks;
|
||||||
|
|
||||||
|
unsigned int freeSize2;
|
||||||
|
}
|
||||||
|
UMM_HEAP_INFO;
|
||||||
|
|
||||||
|
extern UMM_HEAP_INFO ummHeapInfo;
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR *umm_info( void *ptr, int force );
|
||||||
|
size_t ICACHE_FLASH_ATTR umm_free_heap_size( void );
|
||||||
|
size_t ICACHE_FLASH_ATTR umm_max_block_size( void );
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -D UMM_STATS :
|
||||||
|
* -D UMM_STATS_FULL
|
||||||
|
*
|
||||||
|
* This option provides a lightweight alternative to using `umm_info` just for
|
||||||
|
* getting `umm_free_heap_size`. With this option, a "free blocks" value is
|
||||||
|
* updated on each call to malloc/free/realloc. This option does not offer all
|
||||||
|
* the information that `umm_info` would have generated.
|
||||||
|
*
|
||||||
|
* This option is good for cases where the free heap is checked frequently. An
|
||||||
|
* example is when an app closely monitors free heap to detect memory leaks. In
|
||||||
|
* this case a single-core CPUs interrupt processing would have suffered the
|
||||||
|
* most.
|
||||||
|
*
|
||||||
|
* UMM_STATS_FULL provides additional heap statistics. It can be used to gain
|
||||||
|
* additional insight into heap usage. This option would add an additional 132
|
||||||
|
* bytes of IRAM.
|
||||||
|
*
|
||||||
|
* Status: TODO: Needs to be proposed for upstream.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#define UMM_STATS
|
||||||
|
#define UMM_STATS_FULL
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For the ESP8266 we want at lest UMM_STATS built, so we have an ISR safe
|
||||||
|
* function to call for implementing xPortGetFreeHeapSize(), because umm_info()
|
||||||
|
* is in flash.
|
||||||
|
*/
|
||||||
|
#if !defined(UMM_STATS) && !defined(UMM_STATS_FULL)
|
||||||
|
#define UMM_STATS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) && defined(UMM_STATS_FULL)
|
||||||
|
#undef UMM_STATS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
|
||||||
|
|
||||||
|
typedef struct UMM_STATISTICS_t {
|
||||||
|
unsigned short int free_blocks;
|
||||||
|
size_t oom_count;
|
||||||
|
#ifdef UMM_STATS_FULL
|
||||||
|
unsigned short int free_blocks_min;
|
||||||
|
unsigned short int free_blocks_isr_min;
|
||||||
|
size_t alloc_max_size;
|
||||||
|
size_t last_alloc_size;
|
||||||
|
size_t id_malloc_count;
|
||||||
|
size_t id_malloc_zero_count;
|
||||||
|
size_t id_realloc_count;
|
||||||
|
size_t id_realloc_zero_count;
|
||||||
|
size_t id_free_count;
|
||||||
|
size_t id_free_null_count;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
UMM_STATISTICS;
|
||||||
|
extern UMM_STATISTICS ummStats;
|
||||||
|
|
||||||
|
#define STATS__FREE_BLOCKS_UPDATE(s) ummStats.free_blocks += (s)
|
||||||
|
#define STATS__OOM_UPDATE() ummStats.oom_count += 1
|
||||||
|
|
||||||
|
size_t umm_free_heap_size_lw( void );
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_get_oom_count( void ) {
|
||||||
|
return ummStats.oom_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // not UMM_STATS or UMM_STATS_FULL
|
||||||
|
#define STATS__FREE_BLOCKS_UPDATE(s) (void)(s)
|
||||||
|
#define STATS__OOM_UPDATE() (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
|
||||||
|
size_t ICACHE_FLASH_ATTR umm_block_size( void );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef UMM_STATS_FULL
|
||||||
|
#define STATS__FREE_BLOCKS_MIN() \
|
||||||
|
do { \
|
||||||
|
if (ummStats.free_blocks < ummStats.free_blocks_min) \
|
||||||
|
ummStats.free_blocks_min = ummStats.free_blocks; \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define STATS__FREE_BLOCKS_ISR_MIN() \
|
||||||
|
do { \
|
||||||
|
if (ummStats.free_blocks < ummStats.free_blocks_isr_min) \
|
||||||
|
ummStats.free_blocks_isr_min = ummStats.free_blocks; \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define STATS__ALLOC_REQUEST(tag, s) \
|
||||||
|
do { \
|
||||||
|
ummStats.tag##_count += 1; \
|
||||||
|
ummStats.last_alloc_size = s; \
|
||||||
|
if (ummStats.alloc_max_size < s) \
|
||||||
|
ummStats.alloc_max_size = s; \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define STATS__ZERO_ALLOC_REQUEST(tag, s) \
|
||||||
|
do { \
|
||||||
|
ummStats.tag##_zero_count += 1; \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define STATS__NULL_FREE_REQUEST(tag) \
|
||||||
|
do { \
|
||||||
|
ummStats.tag##_null_count += 1; \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
#define STATS__FREE_REQUEST(tag) \
|
||||||
|
do { \
|
||||||
|
ummStats.tag##_count += 1; \
|
||||||
|
} while(false)
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_lw_min( void ) {
|
||||||
|
return (size_t)ummStats.free_blocks_min * umm_block_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset( void ) {
|
||||||
|
ummStats.free_blocks_min = ummStats.free_blocks;
|
||||||
|
return (size_t)ummStats.free_blocks_min * umm_block_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_min( void ) {
|
||||||
|
return ummStats.free_blocks_min * umm_block_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_isr_min( void ) {
|
||||||
|
return ummStats.free_blocks_isr_min * umm_block_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_get_max_alloc_size( void ) {
|
||||||
|
return ummStats.alloc_max_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_get_last_alloc_size( void ) {
|
||||||
|
return ummStats.last_alloc_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_get_malloc_count( void ) {
|
||||||
|
return ummStats.id_malloc_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_get_malloc_zero_count( void ) {
|
||||||
|
return ummStats.id_malloc_zero_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_get_realloc_count( void ) {
|
||||||
|
return ummStats.id_realloc_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_get_realloc_zero_count( void ) {
|
||||||
|
return ummStats.id_realloc_zero_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_get_free_count( void ) {
|
||||||
|
return ummStats.id_free_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t ICACHE_FLASH_ATTR umm_get_free_null_count( void ) {
|
||||||
|
return ummStats.id_free_null_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // Not UMM_STATS_FULL
|
||||||
|
#define STATS__FREE_BLOCKS_MIN() (void)0
|
||||||
|
#define STATS__FREE_BLOCKS_ISR_MIN() (void)0
|
||||||
|
#define STATS__ALLOC_REQUEST(tag, s) (void)(s)
|
||||||
|
#define STATS__ZERO_ALLOC_REQUEST(tag, s) (void)(s)
|
||||||
|
#define STATS__NULL_FREE_REQUEST(tag) (void)0
|
||||||
|
#define STATS__FREE_REQUEST(tag) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Per Devyte, the core currently doesn't support masking a specific interrupt
|
||||||
|
level. That doesn't mean it can't be implemented, only that at this time
|
||||||
|
locking is implemented as all or nothing.
|
||||||
|
https://github.com/esp8266/Arduino/issues/6246#issuecomment-508612609
|
||||||
|
|
||||||
|
So for now we default to all, 15.
|
||||||
|
*/
|
||||||
|
#ifndef DEFAULT_CRITICAL_SECTION_INTLEVEL
|
||||||
|
#define DEFAULT_CRITICAL_SECTION_INTLEVEL 15
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -D UMM_CRITICAL_METRICS
|
||||||
|
*
|
||||||
|
* Build option to collect timing usage data on critical section usage in
|
||||||
|
* functions: info, malloc, realloc. Collects MIN, MAX, and number of time IRQs
|
||||||
|
* were disabled at request time. Note, for realloc MAX disabled time will
|
||||||
|
* include the time spent in calling malloc and/or free. Examine code for
|
||||||
|
* specifics on what info is available and how to access.
|
||||||
|
*
|
||||||
|
* Status: TODO: Needs to be proposed for upstream. Also should include updates
|
||||||
|
* to UMM_POISON_CHECK and UMM_INTEGRITY_CHECK to include a critical section.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#define UMM_CRITICAL_METRICS
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(UMM_CRITICAL_METRICS)
|
||||||
|
// This option adds support for gathering time locked data
|
||||||
|
|
||||||
|
typedef struct UMM_TIME_STAT_t {
|
||||||
|
uint32_t min;
|
||||||
|
uint32_t max;
|
||||||
|
uint32_t start;
|
||||||
|
uint32_t intlevel;
|
||||||
|
}
|
||||||
|
UMM_TIME_STAT;
|
||||||
|
|
||||||
|
typedef struct UMM_TIME_STATS_t UMM_TIME_STATS;
|
||||||
|
|
||||||
|
extern UMM_TIME_STATS time_stats;
|
||||||
|
|
||||||
|
bool get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size);
|
||||||
|
|
||||||
|
static inline void _critical_entry(UMM_TIME_STAT *p, uint32_t *saved_ps) {
|
||||||
|
*saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL);
|
||||||
|
if (0U != (*saved_ps & 0x0FU)) {
|
||||||
|
p->intlevel += 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->start = esp_get_cycle_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
p->min = elapse;
|
||||||
|
|
||||||
|
if (elapse > p->max)
|
||||||
|
p->max = elapse;
|
||||||
|
|
||||||
|
xt_wsr_ps(*saved_ps);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A couple of macros to make it easier to protect the memory allocator
|
* A couple of macros to make it easier to protect the memory allocator
|
||||||
* in a multitasking system. You should set these macros up to use whatever
|
* in a multitasking system. You should set these macros up to use whatever
|
||||||
@ -125,23 +360,82 @@ extern char _heap_start[];
|
|||||||
* called from within umm_malloc()
|
* called from within umm_malloc()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef TEST_BUILD
|
||||||
#if defined(UMM_CRITICAL_PERIOD_ANALYZE)
|
extern int umm_critical_depth;
|
||||||
|
extern int umm_max_critical_depth;
|
||||||
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
|
#define UMM_CRITICAL_ENTRY() {\
|
||||||
#define UMM_CRITICAL_ENTRY(tag) _critical_entry(&time_stats.tag, &_saved_ps_##tag)
|
++umm_critical_depth; \
|
||||||
#define UMM_CRITICAL_EXIT(tag) _critical_exit(&time_stats.tag, &_saved_ps_##tag)
|
if (umm_critical_depth > umm_max_critical_depth) { \
|
||||||
|
umm_max_critical_depth = umm_critical_depth; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#define UMM_CRITICAL_EXIT() (umm_critical_depth--)
|
||||||
#else
|
#else
|
||||||
|
#if defined(UMM_CRITICAL_METRICS)
|
||||||
|
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
|
||||||
|
#define UMM_CRITICAL_ENTRY(tag)_critical_entry(&time_stats.tag, &_saved_ps_##tag)
|
||||||
|
#define UMM_CRITICAL_EXIT(tag) _critical_exit(&time_stats.tag, &_saved_ps_##tag)
|
||||||
|
|
||||||
// This method preserves the intlevel on entry and restores the
|
#else // ! UMM_CRITICAL_METRICS
|
||||||
// original intlevel at exit.
|
// This method preserves the intlevel on entry and restores the
|
||||||
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
|
// original intlevel at exit.
|
||||||
#define UMM_CRITICAL_ENTRY(tag) _saved_ps_##tag = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL)
|
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
|
||||||
#define UMM_CRITICAL_EXIT(tag) xt_wsr_ps(_saved_ps_##tag)
|
#define UMM_CRITICAL_ENTRY(tag) _saved_ps_##tag = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL)
|
||||||
|
#define UMM_CRITICAL_EXIT(tag) xt_wsr_ps(_saved_ps_##tag)
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -D UMM_LIGHTWEIGHT_CPU
|
||||||
|
*
|
||||||
|
* The use of this macro is hardware/application specific.
|
||||||
|
*
|
||||||
|
* With some CPUs, the only available method for locking are the instructions
|
||||||
|
* for interrupts disable/enable. These macros are meant for lightweight single
|
||||||
|
* CPU systems that are sensitive to interrupts being turned off for too long. A
|
||||||
|
* typically UMM_CRITICAL_ENTRY would save current IRQ state then disable IRQs.
|
||||||
|
* Then UMM_CRITICAL_EXIT would restore previous IRQ state. This option adds
|
||||||
|
* additional critical entry/exit points by the method of defining the macros
|
||||||
|
* UMM_CRITICAL_SUSPEND and UMM_CRITICAL_RESUME to the values of
|
||||||
|
* UMM_CRITICAL_EXIT and UMM_CRITICAL_ENTRY. These additional exit/entries
|
||||||
|
* allow time to service interrupts during the reentrant sections of the code.
|
||||||
|
*
|
||||||
|
* Performance may be impacked if used with multicore CPUs. The higher frquency
|
||||||
|
* of locking and unlocking may be an issue with locking methods that have a
|
||||||
|
* high overhead.
|
||||||
|
*
|
||||||
|
* Status: TODO: Needs to be proposed for upstream.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
*/
|
||||||
|
#define UMM_LIGHTWEIGHT_CPU
|
||||||
|
|
||||||
|
#ifdef UMM_LIGHTWEIGHT_CPU
|
||||||
|
#define UMM_CRITICAL_SUSPEND(tag) UMM_CRITICAL_EXIT(tag)
|
||||||
|
#define UMM_CRITICAL_RESUME(tag) UMM_CRITICAL_ENTRY(tag)
|
||||||
|
#else
|
||||||
|
#define UMM_CRITICAL_SUSPEND(tag) do {} while(0)
|
||||||
|
#define UMM_CRITICAL_RESUME(tag) do {} while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -D UMM_REALLOC_MINIMIZE_COPY or
|
||||||
|
* -D UMM_REALLOC_DEFRAG
|
||||||
|
*
|
||||||
|
* Pick one of these two stratagies. UMM_REALLOC_MINIMIZE_COPY grows upward or
|
||||||
|
* shrinks an allocation, avoiding copy when possible. UMM_REALLOC_DEFRAG gives
|
||||||
|
* priority with growing the revised allocation toward an adjacent hole in the
|
||||||
|
* 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
|
||||||
|
* repo. No response at this time. Based on response, may propose for upstream.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#define UMM_REALLOC_MINIMIZE_COPY
|
||||||
|
*/
|
||||||
|
#define UMM_REALLOC_DEFRAG
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* -D UMM_INTEGRITY_CHECK :
|
* -D UMM_INTEGRITY_CHECK :
|
||||||
*
|
*
|
||||||
@ -155,12 +449,28 @@ extern char _heap_start[];
|
|||||||
* 4 bytes, so there might be some trailing "extra" bytes which are not checked
|
* 4 bytes, so there might be some trailing "extra" bytes which are not checked
|
||||||
* for corruption.
|
* for corruption.
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
#define UMM_INTEGRITY_CHECK
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* -D UMM_POISON :
|
* Not normally enabled. Full intergity check may exceed 10us.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#define UMM_INTEGRITY_CHECK
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef UMM_INTEGRITY_CHECK
|
||||||
|
int umm_integrity_check( void );
|
||||||
|
# define INTEGRITY_CHECK() umm_integrity_check()
|
||||||
|
extern void umm_corruption(void);
|
||||||
|
# define UMM_HEAP_CORRUPTION_CB() DBGLOG_FUNCTION( "Heap Corruption!" )
|
||||||
|
#else
|
||||||
|
# define INTEGRITY_CHECK() 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -D UMM_POISON_CHECK :
|
||||||
|
* -D UMM_POISON_CHECK_LITE
|
||||||
*
|
*
|
||||||
* Enables heap poisoning: add predefined value (poison) before and after each
|
* Enables heap poisoning: add predefined value (poison) before and after each
|
||||||
* allocation, and check before each heap operation that no poison is
|
* allocation, and check before each heap operation that no poison is
|
||||||
@ -185,17 +495,135 @@ extern char _heap_start[];
|
|||||||
*
|
*
|
||||||
* If poison corruption is detected, the message is printed and user-provided
|
* If poison corruption is detected, the message is printed and user-provided
|
||||||
* callback is called: `UMM_HEAP_CORRUPTION_CB()`
|
* callback is called: `UMM_HEAP_CORRUPTION_CB()`
|
||||||
|
*
|
||||||
|
* UMM_POISON_CHECK - does a global heap check on all active allocation at
|
||||||
|
* every alloc API call. May exceed 10us due to critical section with IRQs
|
||||||
|
* disabled.
|
||||||
|
*
|
||||||
|
* UMM_POISON_CHECK_LITE - checks the allocation presented at realloc()
|
||||||
|
* and free(). Expands the poison check on the current allocation to
|
||||||
|
* include its nearest allocated neighbors in the heap.
|
||||||
|
* umm_malloc() will also checks the neighbors of the selected allocation
|
||||||
|
* before use.
|
||||||
|
*
|
||||||
|
* Status: TODO?: UMM_POISON_CHECK_LITE is a new option. We could propose for
|
||||||
|
* upstream; however, the upstream version has much of the framework for calling
|
||||||
|
* poison check on each alloc call refactored out. Not sure how this will be
|
||||||
|
* received.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compatibility for deprecated UMM_POISON
|
||||||
|
*/
|
||||||
|
#if defined(UMM_POISON) && !defined(UMM_POISON_CHECK)
|
||||||
|
#define UMM_POISON_CHECK_LITE
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_CORE)
|
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_CORE)
|
||||||
#define UMM_POISON
|
#if !defined(UMM_POISON_CHECK) && !defined(UMM_POISON_CHECK_LITE)
|
||||||
|
/*
|
||||||
|
#define UMM_POISON_CHECK
|
||||||
|
*/
|
||||||
|
#define UMM_POISON_CHECK_LITE
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define UMM_POISON_SIZE_BEFORE 4
|
#define UMM_POISON_SIZE_BEFORE 4
|
||||||
#define UMM_POISON_SIZE_AFTER 4
|
#define UMM_POISON_SIZE_AFTER 4
|
||||||
#define UMM_POISONED_BLOCK_LEN_TYPE uint32_t
|
#define UMM_POISONED_BLOCK_LEN_TYPE uint32_t
|
||||||
|
|
||||||
#define UMM_HEAP_CORRUPTION_CB() panic()
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
|
void *umm_poison_malloc( size_t size );
|
||||||
|
void *umm_poison_calloc( size_t num, size_t size );
|
||||||
|
void *umm_poison_realloc( void *ptr, size_t size );
|
||||||
|
void umm_poison_free( void *ptr );
|
||||||
|
int umm_poison_check( void );
|
||||||
|
// Local Additions to better report location in code of the caller.
|
||||||
|
void *umm_poison_realloc_fl( void *ptr, size_t size, const char* file, int line );
|
||||||
|
void umm_poison_free_fl( void *ptr, const char* file, int line );
|
||||||
|
#if defined(UMM_POISON_CHECK_LITE)
|
||||||
|
/*
|
||||||
|
* We can safely do individual poison checks at free and realloc and stay
|
||||||
|
* under 10us or close.
|
||||||
|
*/
|
||||||
|
# define POISON_CHECK() 1
|
||||||
|
# define POISON_CHECK_NEIGHBORS(c) \
|
||||||
|
do {\
|
||||||
|
if(!check_poison_neighbors(c)) \
|
||||||
|
panic();\
|
||||||
|
} while(false)
|
||||||
|
#else
|
||||||
|
/* Not normally enabled. A full heap poison check may exceed 10us. */
|
||||||
|
# define POISON_CHECK() umm_poison_check()
|
||||||
|
# define POISON_CHECK_NEIGHBORS(c) do{}while(false)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
# define POISON_CHECK() 1
|
||||||
|
# define POISON_CHECK_NEIGHBORS(c) do{}while(false)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
#undef DBGLOG_FUNCTION
|
||||||
|
#undef DBGLOG_FUNCTION_P
|
||||||
|
|
||||||
|
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_OOM) || \
|
||||||
|
defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || \
|
||||||
|
defined(UMM_INTEGRITY_CHECK)
|
||||||
|
#define DBGLOG_FUNCTION(fmt, ...) ets_uart_printf(fmt, ##__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define DBGLOG_FUNCTION(fmt, ...) do { (void)fmt; } while(false)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || defined(UMM_INTEGRITY_CHECK)
|
||||||
|
#if !defined(DBGLOG_LEVEL) || DBGLOG_LEVEL < 3
|
||||||
|
// All debug prints in UMM_POISON_CHECK are level 3
|
||||||
|
#undef DBGLOG_LEVEL
|
||||||
|
#define DBGLOG_LEVEL 3
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(UMM_CRITICAL_METRICS)
|
||||||
|
struct UMM_TIME_STATS_t {
|
||||||
|
UMM_TIME_STAT id_malloc;
|
||||||
|
UMM_TIME_STAT id_realloc;
|
||||||
|
UMM_TIME_STAT id_free;
|
||||||
|
#ifdef UMM_INFO
|
||||||
|
UMM_TIME_STAT id_info;
|
||||||
|
#endif
|
||||||
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
|
UMM_TIME_STAT id_poison;
|
||||||
|
#endif
|
||||||
|
#ifdef UMM_INTEGRITY_CHECK
|
||||||
|
UMM_TIME_STAT id_integrity;
|
||||||
|
#endif
|
||||||
|
UMM_TIME_STAT id_no_tag;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
#ifdef DEBUG_ESP_OOM
|
||||||
|
|
||||||
|
#define MEMLEAK_DEBUG
|
||||||
|
|
||||||
|
// umm_*alloc are not renamed to *alloc
|
||||||
|
// Assumes umm_malloc.h has already been included.
|
||||||
|
|
||||||
|
#define umm_zalloc(s) umm_calloc(1,s)
|
||||||
|
|
||||||
|
void* malloc_loc (size_t s, const char* file, int line);
|
||||||
|
void* calloc_loc (size_t n, size_t s, const char* file, int line);
|
||||||
|
void* realloc_loc (void* p, size_t s, const char* file, int line);
|
||||||
|
// *alloc are macro calling *alloc_loc calling+checking umm_*alloc()
|
||||||
|
// they are defined at the bottom of this file
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#elif defined(UMM_POISON_CHECK)
|
||||||
|
void* realloc_loc (void* p, size_t s, const char* file, int line);
|
||||||
|
void free_loc (void* p, const char* file, int line);
|
||||||
|
#else // !defined(ESP_DEBUG_OOM)
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
@ -203,12 +631,57 @@ extern char _heap_start[];
|
|||||||
|
|
||||||
#endif /* _UMM_MALLOC_CFG_H */
|
#endif /* _UMM_MALLOC_CFG_H */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
#ifdef DEBUG_ESP_OOM
|
#ifdef DEBUG_ESP_OOM
|
||||||
// this must be outside from "#ifndef _UMM_MALLOC_CFG_H"
|
// this must be outside from "#ifndef _UMM_MALLOC_CFG_H"
|
||||||
// because Arduino.h's <cstdlib> does #undef *alloc
|
// because Arduino.h's <cstdlib> does #undef *alloc
|
||||||
// Arduino.h recall us to redefine them
|
// Arduino.h recall us to redefine them
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; malloc_loc(s, mem_debug_file, __LINE__); })
|
// Reuse pvPort* calls, since they already support passing location information.
|
||||||
#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; calloc_loc(n, s, mem_debug_file, __LINE__); })
|
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line);
|
||||||
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; realloc_loc(p, s, mem_debug_file, __LINE__); })
|
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line);
|
||||||
|
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line);
|
||||||
|
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line);
|
||||||
|
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line);
|
||||||
|
|
||||||
|
#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortMalloc(s, mem_debug_file, __LINE__); })
|
||||||
|
#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortCalloc(n, s, mem_debug_file, __LINE__); })
|
||||||
|
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortRealloc(p, s, mem_debug_file, __LINE__); })
|
||||||
|
|
||||||
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
|
#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; vPortFree(p, mem_debug_file, __LINE__); })
|
||||||
|
#else
|
||||||
|
#define dbg_heap_free(p) free(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
|
#include <pgmspace.h>
|
||||||
|
void* ICACHE_RAM_ATTR 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__; pvPortRealloc(p, s, mem_debug_file, __LINE__); })
|
||||||
|
|
||||||
|
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line);
|
||||||
|
//C - to be discussed
|
||||||
|
/*
|
||||||
|
Problem, I would like to report the file and line number with the umm poison
|
||||||
|
event as close as possible to the event. The #define method works for malloc,
|
||||||
|
calloc, and realloc those names are not as generic as free. A #define free
|
||||||
|
captures too much. Classes with methods called free are included :(
|
||||||
|
Inline functions would report the address of the inline function in the .h
|
||||||
|
not where they are called.
|
||||||
|
|
||||||
|
Anybody know a trick to make this work?
|
||||||
|
|
||||||
|
Create dbg_heap_free() as an alternative for free() when you need a little
|
||||||
|
more help in debugging the more challenging problems.
|
||||||
|
*/
|
||||||
|
#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; vPortFree(p, mem_debug_file, __LINE__); })
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define dbg_heap_free(p) free(p)
|
||||||
#endif /* DEBUG_ESP_OOM */
|
#endif /* DEBUG_ESP_OOM */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* umm_malloc performance measurments and ESP specifics
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <pgmspace.h>
|
|
||||||
#include <core_esp8266_features.h>
|
|
||||||
#include "umm_performance.h"
|
|
||||||
#include "umm_stats.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
UMM_STATS ummStats = {0, 0, 0, 0};
|
|
||||||
|
|
||||||
#ifdef UMM_CRITICAL_PERIOD_ANALYZE
|
|
||||||
struct _UMM_TIME_STATS time_stats = {
|
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U},
|
|
||||||
{0xFFFFFFFF, 0U, 0U, 0U} };
|
|
||||||
|
|
||||||
bool ICACHE_FLASH_ATTR get_umm_get_perf_data(struct _UMM_TIME_STATS *p, size_t size) {
|
|
||||||
if (p && sizeof(time_stats) == size) {
|
|
||||||
uint32_t save_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL);
|
|
||||||
memcpy(p, &time_stats, size);
|
|
||||||
xt_wsr_ps(save_ps);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_ISR)
|
|
||||||
/*
|
|
||||||
Printing from the malloc routines is tricky. Since a lot of library calls
|
|
||||||
will want to do malloc.
|
|
||||||
|
|
||||||
Objective: To be able to print "last gasp" diagnostic messages
|
|
||||||
when interrupts are disabled and w/o availability of heap resources.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ROM _putc1, ignores CRs and sends CR/LF for LF, newline.
|
|
||||||
// Always returns character sent.
|
|
||||||
int constexpr (*_rom_putc1)(int) = (int (*)(int))0x40001dcc;
|
|
||||||
void uart_buff_switch(uint8_t);
|
|
||||||
|
|
||||||
int _isr_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
|
||||||
int ICACHE_RAM_ATTR _isr_safe_printf_P(const char *fmt, ...) {
|
|
||||||
#ifdef DEBUG_ESP_PORT
|
|
||||||
#define VALUE(x) __STRINGIFY(x)
|
|
||||||
// Preprocessor and compiler together will optimize away the if.
|
|
||||||
if (strcmp("Serial1", VALUE(DEBUG_ESP_PORT)) == 0) {
|
|
||||||
uart_buff_switch(1U);
|
|
||||||
} else {
|
|
||||||
uart_buff_switch(0U);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
uart_buff_switch(0U); // Side effect, clears RX FIFO
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
To use ets_strlen() and ets_memcpy() safely with PROGMEM, flash storage,
|
|
||||||
the PROGMEM address must be word (4 bytes) aligned. The destination
|
|
||||||
address for ets_memcpy must also be word-aligned. We also round the
|
|
||||||
buf_len up to the nearest word boundary. So that all transfers will be
|
|
||||||
whole words.
|
|
||||||
*/
|
|
||||||
size_t str_len = ets_strlen(fmt);
|
|
||||||
size_t buf_len = (str_len + 1 + 3) & ~0x03U;
|
|
||||||
char ram_buf[buf_len] __attribute__ ((aligned(4)));
|
|
||||||
ets_memcpy(ram_buf, fmt, buf_len);
|
|
||||||
va_list argPtr;
|
|
||||||
va_start(argPtr, fmt);
|
|
||||||
int result = ets_vprintf(_rom_putc1, ram_buf, argPtr);
|
|
||||||
va_end(argPtr);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
};
|
|
@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* umm_malloc performance measurments and ESP specifics
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _UMM_PERFORMANCE_H
|
|
||||||
#define _UMM_PERFORMANCE_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* -D UMM_CRITICAL_PERIOD_ANALYZE :
|
|
||||||
*
|
|
||||||
* Build option to collect timing usage data on critical section usage in
|
|
||||||
* functions: info, malloc, realloc. Collects MIN, MAX, and number of time
|
|
||||||
* IRQs were disabled at request time. Note, for realloc MAX disabled time
|
|
||||||
* will not include the time from calling malloc and/or free.
|
|
||||||
* Examine code for specifics on what info is available and how to access.
|
|
||||||
*/
|
|
||||||
// #define UMM_CRITICAL_PERIOD_ANALYZE
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Per Devyte, the core currently doesn't support masking a specific interrupt
|
|
||||||
level. That doesn't mean it can't be implemented, only that at this time
|
|
||||||
locking is implemented as all or nothing.
|
|
||||||
https://github.com/esp8266/Arduino/issues/6246#issuecomment-508612609
|
|
||||||
|
|
||||||
So for now we default to all, 15.
|
|
||||||
*/
|
|
||||||
#ifndef DEFAULT_CRITICAL_SECTION_INTLEVEL
|
|
||||||
#define DEFAULT_CRITICAL_SECTION_INTLEVEL 15
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(UMM_CRITICAL_PERIOD_ANALYZE)
|
|
||||||
// This option adds support for gathering time locked data
|
|
||||||
typedef struct _TIME_STAT {
|
|
||||||
uint32_t min;
|
|
||||||
uint32_t max;
|
|
||||||
uint32_t start;
|
|
||||||
uint32_t intlevel;
|
|
||||||
} time_stat_t;
|
|
||||||
|
|
||||||
struct _UMM_TIME_STATS {
|
|
||||||
time_stat_t id_malloc;
|
|
||||||
time_stat_t id_realloc;
|
|
||||||
time_stat_t id_free;
|
|
||||||
time_stat_t id_info;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct _UMM_TIME_STATS time_stats;
|
|
||||||
|
|
||||||
bool get_umm_get_perf_data(struct _UMM_TIME_STATS *p, size_t size);
|
|
||||||
|
|
||||||
static inline void _critical_entry(time_stat_t *p, uint32_t *saved_ps) {
|
|
||||||
*saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL);
|
|
||||||
if (0U != (*saved_ps & 0x0FU)) {
|
|
||||||
p->intlevel += 1U;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->start = esp_get_cycle_count();
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _critical_exit(time_stat_t *p, uint32_t *saved_ps) {
|
|
||||||
uint32_t elapse = esp_get_cycle_count() - p->start;
|
|
||||||
if (elapse < p->min)
|
|
||||||
p->min = elapse;
|
|
||||||
|
|
||||||
if (elapse > p->max)
|
|
||||||
p->max = elapse;
|
|
||||||
|
|
||||||
xt_wsr_ps(*saved_ps);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_ISR)
|
|
||||||
int _isr_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _UMM_PERFORMANCE_H */
|
|
241
cores/esp8266/umm_malloc/umm_poison.c
Normal file
241
cores/esp8266/umm_malloc/umm_poison.c
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
#if defined(BUILD_UMM_MALLOC_C)
|
||||||
|
|
||||||
|
/* poisoning (UMM_POISON_CHECK) {{{ */
|
||||||
|
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
|
||||||
|
#define POISON_BYTE (0xa5)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Yields a size of the poison for the block of size `s`.
|
||||||
|
* If `s` is 0, returns 0.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print memory contents starting from given `ptr`
|
||||||
|
*/
|
||||||
|
static void dump_mem ( const unsigned char *ptr, size_t len ) {
|
||||||
|
while (len--) {
|
||||||
|
DBGLOG_ERROR(" 0x%.2x", (unsigned int)(*ptr++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put poison data at given `ptr` and `poison_size`
|
||||||
|
*/
|
||||||
|
static void put_poison( unsigned char *ptr, size_t poison_size ) {
|
||||||
|
memset(ptr, POISON_BYTE, poison_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check poison data at given `ptr` and `poison_size`. `where` is a pointer to
|
||||||
|
* a string, either "before" or "after", meaning, before or after the block.
|
||||||
|
*
|
||||||
|
* If poison is there, returns 1.
|
||||||
|
* Otherwise, prints the appropriate message, and returns 0.
|
||||||
|
*/
|
||||||
|
static int check_poison( const unsigned char *ptr, size_t poison_size,
|
||||||
|
const char *where) {
|
||||||
|
size_t i;
|
||||||
|
int ok = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < poison_size; i++) {
|
||||||
|
if (ptr[i] != POISON_BYTE) {
|
||||||
|
ok = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
DBGLOG_ERROR( "No poison %s block at: 0x%lx, actual data:", where, (unsigned long)ptr);
|
||||||
|
dump_mem(ptr, poison_size);
|
||||||
|
DBGLOG_ERROR( "\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if a block is properly poisoned. Must be called only for non-free
|
||||||
|
* blocks.
|
||||||
|
*/
|
||||||
|
static int check_poison_block( umm_block *pblock ) {
|
||||||
|
int ok = 1;
|
||||||
|
|
||||||
|
if (pblock->header.used.next & UMM_FREELIST_MASK) {
|
||||||
|
DBGLOG_ERROR( "check_poison_block is called for free block 0x%lx\n", (unsigned long)pblock);
|
||||||
|
} else {
|
||||||
|
/* the block is used; let's check poison */
|
||||||
|
unsigned char *pc = (unsigned char *)pblock->body.data;
|
||||||
|
unsigned char *pc_cur;
|
||||||
|
|
||||||
|
pc_cur = pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE);
|
||||||
|
if (!check_poison(pc_cur, UMM_POISON_SIZE_BEFORE, "before")) {
|
||||||
|
ok = 0;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
pc_cur = pc + *((UMM_POISONED_BLOCK_LEN_TYPE *)pc) - UMM_POISON_SIZE_AFTER;
|
||||||
|
if (!check_poison(pc_cur, UMM_POISON_SIZE_AFTER, "after")) {
|
||||||
|
ok = 0;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clean:
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Takes a pointer returned by actual allocator function (`umm_malloc` or
|
||||||
|
* `umm_realloc`), puts appropriate poison, and returns adjusted pointer that
|
||||||
|
* should be returned to the user.
|
||||||
|
*
|
||||||
|
* `size_w_poison` is a size of the whole block, including a poison.
|
||||||
|
*/
|
||||||
|
static void *get_poisoned( void *v_ptr, size_t size_w_poison ) {
|
||||||
|
unsigned char *ptr = (unsigned char *)v_ptr;
|
||||||
|
|
||||||
|
if (size_w_poison != 0 && ptr != NULL) {
|
||||||
|
|
||||||
|
/* Poison beginning and the end of the allocated chunk */
|
||||||
|
put_poison(ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE),
|
||||||
|
UMM_POISON_SIZE_BEFORE);
|
||||||
|
put_poison(ptr + size_w_poison - UMM_POISON_SIZE_AFTER,
|
||||||
|
UMM_POISON_SIZE_AFTER);
|
||||||
|
|
||||||
|
/* Put exact length of the user's chunk of memory */
|
||||||
|
*(UMM_POISONED_BLOCK_LEN_TYPE *)ptr = (UMM_POISONED_BLOCK_LEN_TYPE)size_w_poison;
|
||||||
|
|
||||||
|
/* Return pointer at the first non-poisoned byte */
|
||||||
|
ptr += sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void *)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Takes "poisoned" pointer (i.e. pointer returned from `get_poisoned()`),
|
||||||
|
* and checks that the poison of this particular block is still there.
|
||||||
|
*
|
||||||
|
* Returns unpoisoned pointer, i.e. actual pointer to the allocated memory.
|
||||||
|
*/
|
||||||
|
static void *get_unpoisoned( void *v_ptr ) {
|
||||||
|
unsigned char *ptr = (unsigned char *)v_ptr;
|
||||||
|
|
||||||
|
if (ptr != NULL) {
|
||||||
|
unsigned short int c;
|
||||||
|
|
||||||
|
ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
|
||||||
|
|
||||||
|
/* Figure out which block we're in. Note the use of truncated division... */
|
||||||
|
c = (((char *)ptr)-(char *)(&(umm_heap[0])))/sizeof(umm_block);
|
||||||
|
|
||||||
|
check_poison_block(&UMM_BLOCK(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void *)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void *umm_poison_malloc( size_t size ) {
|
||||||
|
void *ret;
|
||||||
|
|
||||||
|
size += poison_size(size);
|
||||||
|
|
||||||
|
ret = umm_malloc( size );
|
||||||
|
|
||||||
|
ret = get_poisoned(ret, size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void *umm_poison_calloc( size_t num, size_t item_size ) {
|
||||||
|
void *ret;
|
||||||
|
size_t size = item_size * num;
|
||||||
|
|
||||||
|
size += poison_size(size);
|
||||||
|
|
||||||
|
ret = umm_malloc(size);
|
||||||
|
|
||||||
|
if (NULL != ret)
|
||||||
|
memset(ret, 0x00, size);
|
||||||
|
|
||||||
|
ret = get_poisoned(ret, size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void *umm_poison_realloc( void *ptr, size_t size ) {
|
||||||
|
void *ret;
|
||||||
|
|
||||||
|
ptr = get_unpoisoned(ptr);
|
||||||
|
|
||||||
|
size += poison_size(size);
|
||||||
|
ret = umm_realloc( ptr, size );
|
||||||
|
|
||||||
|
ret = get_poisoned(ret, size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void umm_poison_free( void *ptr ) {
|
||||||
|
|
||||||
|
ptr = get_unpoisoned(ptr);
|
||||||
|
|
||||||
|
umm_free( ptr );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterates through all blocks in the heap, and checks poison for all used
|
||||||
|
* blocks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int umm_poison_check(void) {
|
||||||
|
UMM_CRITICAL_DECL(id_poison);
|
||||||
|
int ok = 1;
|
||||||
|
unsigned short int cur;
|
||||||
|
|
||||||
|
if (umm_heap == NULL) {
|
||||||
|
umm_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
UMM_CRITICAL_ENTRY(id_poison);
|
||||||
|
|
||||||
|
/* Now iterate through the blocks list */
|
||||||
|
cur = UMM_NBLOCK(0) & UMM_BLOCKNO_MASK;
|
||||||
|
|
||||||
|
while( UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK ) {
|
||||||
|
if ( !(UMM_NBLOCK(cur) & UMM_FREELIST_MASK) ) {
|
||||||
|
/* This is a used block (not free), so, check its poison */
|
||||||
|
ok = check_poison_block(&UMM_BLOCK(cur));
|
||||||
|
if (!ok){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK;
|
||||||
|
}
|
||||||
|
UMM_CRITICAL_EXIT(id_poison);
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // defined(BUILD_UMM_MALLOC_C)
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* umm_malloc heap statistics
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _UMM_STATS_H
|
|
||||||
#define _UMM_STATS_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct UMM_STATS_t {
|
|
||||||
unsigned short int free_blocks;
|
|
||||||
unsigned short int free_blocks_min;
|
|
||||||
size_t alloc_max_size;
|
|
||||||
size_t oom_count;
|
|
||||||
} UMM_STATS;
|
|
||||||
extern UMM_STATS ummStats;
|
|
||||||
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_free_heap_size_min( void );
|
|
||||||
size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset( void );
|
|
||||||
|
|
||||||
inline size_t umm_get_max_alloc_size( void ) {
|
|
||||||
return ummStats.alloc_max_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t umm_get_oom_count( void ) {
|
|
||||||
return ummStats.oom_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _UMM_STATS_H */
|
|
@ -175,16 +175,18 @@ use esptool.py.
|
|||||||
|
|
||||||
- Download the tool: https://github.com/esp8266/arduino-esp8266fs-plugin/releases/download/0.4.0/ESP8266FS-0.4.0.zip
|
- Download the tool: https://github.com/esp8266/arduino-esp8266fs-plugin/releases/download/0.4.0/ESP8266FS-0.4.0.zip
|
||||||
- In your Arduino sketchbook directory, create ``tools`` directory if
|
- In your Arduino sketchbook directory, create ``tools`` directory if
|
||||||
it doesn't exist yet
|
it doesn't exist yet.
|
||||||
- Unpack the tool into ``tools`` directory (the path will look like
|
- Unpack the tool into ``tools`` directory (the path will look like
|
||||||
``<home_dir>/Arduino/tools/ESP8266FS/tool/esp8266fs.jar``)
|
``<home_dir>/Arduino/tools/ESP8266FS/tool/esp8266fs.jar``)
|
||||||
If upgrading, overwrite the existing JAR file with the newer version.
|
If upgrading, overwrite the existing JAR file with the newer version.
|
||||||
- Restart Arduino IDE
|
- Restart Arduino IDE.
|
||||||
- Open a sketch (or create a new one and save it)
|
- Open a sketch (or create a new one and save it).
|
||||||
- Go to sketch directory (choose Sketch > Show Sketch Folder)
|
- Go to sketch directory (choose Sketch > Show Sketch Folder).
|
||||||
- Create a directory named ``data`` and any files you want in the file
|
- Create a directory named ``data`` and any files you want in the file
|
||||||
system there
|
system there.
|
||||||
- Make sure you have selected a board, port, and closed Serial Monitor
|
- Make sure you have selected a board, port, and closed Serial Monitor.
|
||||||
|
- If your board requires you to press a button (or other action) to enter
|
||||||
|
bootload mode for flashing a sketch, do that now.
|
||||||
- Select Tools > ESP8266 Sketch Data Upload. This should start
|
- Select Tools > ESP8266 Sketch Data Upload. This should start
|
||||||
uploading the files into ESP8266 flash file system. When done, IDE
|
uploading the files into ESP8266 flash file system. When done, IDE
|
||||||
status bar will display ``SPIFFS Image Uploaded`` message.
|
status bar will display ``SPIFFS Image Uploaded`` message.
|
||||||
|
@ -12,6 +12,7 @@ Prerequisites
|
|||||||
- Arduino 1.6.8, get it from `Arduino
|
- Arduino 1.6.8, get it from `Arduino
|
||||||
website <https://www.arduino.cc/en/Main/OldSoftwareReleases#previous>`__.
|
website <https://www.arduino.cc/en/Main/OldSoftwareReleases#previous>`__.
|
||||||
- Internet connection
|
- Internet connection
|
||||||
|
- Python 3 interpreter (Mac/Linux only, Windows installation supplies its own)
|
||||||
|
|
||||||
Instructions
|
Instructions
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
@ -46,10 +47,13 @@ Prerequisites
|
|||||||
- Python 3.x (https://python.org)
|
- Python 3.x (https://python.org)
|
||||||
- terminal, console, or command prompt (depending on your OS)
|
- terminal, console, or command prompt (depending on your OS)
|
||||||
- Internet connection
|
- Internet connection
|
||||||
|
- Uninstalling any core version installed via Board Manager
|
||||||
|
|
||||||
Instructions - Windows 10
|
Instructions - Windows 10
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
- First, make sure you don't already have the ESP8266 library installed using the Board Manager (see above)
|
- First, make sure you don't already have an ESP8266 core version installed
|
||||||
|
using the Board Manager (see above). If you do, uninstall it from the
|
||||||
|
Board Manager before proceeding.
|
||||||
|
|
||||||
- Install git for Windows (if not already; see https://git-scm.com/download/win)
|
- Install git for Windows (if not already; see https://git-scm.com/download/win)
|
||||||
|
|
||||||
@ -130,9 +134,14 @@ Note that you could, in theory install in ``C:\Program Files (x86)\Arduino\hardw
|
|||||||
Instructions - Other OS
|
Instructions - Other OS
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- First, make sure you don't already have an ESP8266 core version installed
|
||||||
|
using the Board Manager (see above). If you do, uninstall it from the
|
||||||
|
Board Manager before proceeding.
|
||||||
|
|
||||||
- Open the console and go to Arduino directory. This can be either your
|
- Open the console and go to Arduino directory. This can be either your
|
||||||
*sketchbook* directory (usually ``<Documents>/Arduino``), or the
|
*sketchbook* directory (usually ``<Documents>/Arduino``), or the
|
||||||
directory of Arduino application itself, the choice is up to you.
|
directory of Arduino application itself, the choice is up to you.
|
||||||
|
|
||||||
- Clone this repository into hardware/esp8266com/esp8266 directory.
|
- Clone this repository into hardware/esp8266com/esp8266 directory.
|
||||||
Alternatively, clone it elsewhere and create a symlink, if your OS
|
Alternatively, clone it elsewhere and create a symlink, if your OS
|
||||||
supports them.
|
supports them.
|
||||||
@ -186,6 +195,30 @@ Instructions - Other OS
|
|||||||
cd esp8266/tools
|
cd esp8266/tools
|
||||||
python3 get.py
|
python3 get.py
|
||||||
|
|
||||||
|
If you get an error message stating that python3 is not found, you will need to install it (most modern UNIX-like OSes provide Python 3 as
|
||||||
|
part of the default install). To install you will need to use ``sudo yum install python3``, ``sudo apt install python3``, or ``brew install python3``
|
||||||
|
as appropriate. On the Mac you may get an error message like:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
python3 get.py
|
||||||
|
Platform: x86_64-apple-darwin
|
||||||
|
Downloading python3-macosx-placeholder.tar.gz
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/urllib/request.py", line 1317, in do_open
|
||||||
|
encode_chunked=req.has_header('Transfer-encoding'))
|
||||||
|
...
|
||||||
|
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 1117, in do_handshake
|
||||||
|
self._sslobj.do_handshake()
|
||||||
|
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
|
||||||
|
|
||||||
|
This is because Homebrew on the Mac does not always install the required SSL certificates by default. Install them manually (adjust the Python 3.7 as needed) with:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
cd "/Applications/Python 3.7/" && sudo "./Install Certificates.command"
|
||||||
|
|
||||||
|
|
||||||
- Restart Arduino
|
- Restart Arduino
|
||||||
|
|
||||||
- When later updating your local library, goto the esp8266 directory and do a git pull
|
- When later updating your local library, goto the esp8266 directory and do a git pull
|
||||||
|
@ -29,6 +29,8 @@ EEPROM library uses one sector of flash located just after the SPIFFS.
|
|||||||
|
|
||||||
`Three examples <https://github.com/esp8266/Arduino/tree/master/libraries/EEPROM>`__ included.
|
`Three examples <https://github.com/esp8266/Arduino/tree/master/libraries/EEPROM>`__ included.
|
||||||
|
|
||||||
|
Note that the sector needs to be re-flashed every time the changed EEPROM data needs to be saved, thus will wear out the flash memory very quickly even if small amounts of data are written. Consider using one of the EEPROM libraries mentioned down below.
|
||||||
|
|
||||||
I2C (Wire library)
|
I2C (Wire library)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@ -141,14 +143,6 @@ Servo
|
|||||||
|
|
||||||
This library exposes the ability to control RC (hobby) servo motors. It will support up to 24 servos on any available output pin. By default the first 12 servos will use Timer0 and currently this will not interfere with any other support. Servo counts above 12 will use Timer1 and features that use it will be affected. While many RC servo motors will accept the 3.3V IO data pin from a ESP8266, most will not be able to run off 3.3v and will require another power source that matches their specifications. Make sure to connect the grounds between the ESP8266 and the servo motor power supply.
|
This library exposes the ability to control RC (hobby) servo motors. It will support up to 24 servos on any available output pin. By default the first 12 servos will use Timer0 and currently this will not interfere with any other support. Servo counts above 12 will use Timer1 and features that use it will be affected. While many RC servo motors will accept the 3.3V IO data pin from a ESP8266, most will not be able to run off 3.3v and will require another power source that matches their specifications. Make sure to connect the grounds between the ESP8266 and the servo motor power supply.
|
||||||
|
|
||||||
Improved EEPROM library for ESP (ESP_EEPROM)
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
An improved EEPROM library for ESPxxxx. Uses flash memory as per the standard ESP EEPROM library but reduces reflash - so reducing wear and improving commit() performance.
|
|
||||||
|
|
||||||
As actions on the flash need to stop the interrupts, an EEPROM reflash could noticably affect anything using PWM, etc.
|
|
||||||
|
|
||||||
|
|
||||||
Other libraries (not included with the IDE)
|
Other libraries (not included with the IDE)
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
@ -189,3 +183,5 @@ Libraries that don't rely on low-level access to AVR registers should work well.
|
|||||||
- `MFRC522 <https://github.com/miguelbalboa/rfid>`__ - A library for using the Mifare RC522 RFID-tag reader/writer.
|
- `MFRC522 <https://github.com/miguelbalboa/rfid>`__ - A library for using the Mifare RC522 RFID-tag reader/writer.
|
||||||
- `Ping <https://github.com/dancol90/ESP8266Ping>`__ - lets the ESP8266 ping a remote machine.
|
- `Ping <https://github.com/dancol90/ESP8266Ping>`__ - lets the ESP8266 ping a remote machine.
|
||||||
- `AsyncPing <https://github.com/akaJes/AsyncPing>`__ - fully asynchronous Ping library (have full ping statistic and hardware MAC address).
|
- `AsyncPing <https://github.com/akaJes/AsyncPing>`__ - fully asynchronous Ping library (have full ping statistic and hardware MAC address).
|
||||||
|
- `ESP_EEPROM <https://github.com/jwrw/ESP_EEPROM>`__ - This library writes a new copy of your data when you save (commit) it and keeps track of where in the sector the most recent copy is kept using a bitmap. The flash sector only needs to be erased when there is no more space for copies in the flash sector.
|
||||||
|
- `EEPROM Rotate <https://github.com/xoseperez/eeprom_rotate>`__ - Instead of using a single sector to persist the data from the emulated EEPROM, this library uses a number of sectors to do so: a sector pool.
|
||||||
|
@ -646,6 +646,8 @@ Update process - memory view
|
|||||||
- the new sketch is now copied "over" the old one.
|
- the new sketch is now copied "over" the old one.
|
||||||
- the new sketch is started.
|
- the new sketch is started.
|
||||||
|
|
||||||
|
By default, filesystem updates are overriding the target flash directly. In order to use the same two step process set the `ATOMIC_FS_UPDATE` flag.
|
||||||
|
|
||||||
.. figure:: update_memory_copy.png
|
.. figure:: update_memory_copy.png
|
||||||
:alt: Memory layout for OTA updates
|
:alt: Memory layout for OTA updates
|
||||||
|
|
||||||
|
@ -1,6 +1,42 @@
|
|||||||
Reference
|
Reference
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
Interrupts
|
||||||
|
----------
|
||||||
|
|
||||||
|
Interrupts can be used on the ESP8266, but they must be used with care
|
||||||
|
and have several limitations:
|
||||||
|
|
||||||
|
* Interrupt callback functions must be in IRAM, because the flash may be
|
||||||
|
in the middle of other operations when they occur. Do this by adding
|
||||||
|
the ``ICACHE_RAM_ATTR`` attribute on the function definition. If this
|
||||||
|
attribute is not present, the sketch will crash when it attempts to
|
||||||
|
``attachInterrupt`` with an error message.
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
ICACHE_RAM_ATTR void gpio_change_handler(void *data) {...
|
||||||
|
|
||||||
|
* Interrupts must not call ``delay()`` or ``yield()``, or call any routines
|
||||||
|
which internally use ``delay()`` or ``yield()`` either.
|
||||||
|
|
||||||
|
* Long-running (>1ms) tasks in interrupts will cause instabilty or crashes.
|
||||||
|
WiFi and other portions of the core can become unstable if interrupts
|
||||||
|
are blocked by a long-running interrupt. If you have much to do, you can
|
||||||
|
set a volatile global flag that your main ``loop()`` can check each pass
|
||||||
|
or use a scheduled function (which will be called outside of the interrupt
|
||||||
|
context when it is safe) to do long-running work.
|
||||||
|
|
||||||
|
* Memory operations can be dangerous and should be avoided in interrupts.
|
||||||
|
Calls to ``new`` or ``malloc`` should be minimized because they may require
|
||||||
|
a long running time if memory is fragmented. Calls to ``realloc`` and
|
||||||
|
``free`` must NEVER be called. Using any routines or objects which call
|
||||||
|
``free`` or ``realloc`` themselves is also forbidden for the same reason.
|
||||||
|
This means that ``String``, ``std::string``, ``std::vector`` and other
|
||||||
|
classes which use contiguous memory that may be resized must be used with
|
||||||
|
extreme care (ensuring strings aren't changed, vector elements aren't
|
||||||
|
added, etc.).
|
||||||
|
|
||||||
Digital IO
|
Digital IO
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "EEPROM.h"
|
#include "EEPROM.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "c_types.h"
|
#include "c_types.h"
|
||||||
@ -30,7 +31,7 @@ extern "C" {
|
|||||||
#include "spi_flash.h"
|
#include "spi_flash.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" uint32_t _FS_end;
|
extern "C" uint32_t _EEPROM_start;
|
||||||
|
|
||||||
EEPROMClass::EEPROMClass(uint32_t sector)
|
EEPROMClass::EEPROMClass(uint32_t sector)
|
||||||
: _sector(sector)
|
: _sector(sector)
|
||||||
@ -41,7 +42,7 @@ EEPROMClass::EEPROMClass(uint32_t sector)
|
|||||||
}
|
}
|
||||||
|
|
||||||
EEPROMClass::EEPROMClass(void)
|
EEPROMClass::EEPROMClass(void)
|
||||||
: _sector((((uint32_t)&_FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE))
|
: _sector((((uint32_t)&_EEPROM_start - 0x40200000) / SPI_FLASH_SEC_SIZE))
|
||||||
, _data(0)
|
, _data(0)
|
||||||
, _size(0)
|
, _size(0)
|
||||||
, _dirty(false)
|
, _dirty(false)
|
||||||
@ -49,10 +50,14 @@ EEPROMClass::EEPROMClass(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EEPROMClass::begin(size_t size) {
|
void EEPROMClass::begin(size_t size) {
|
||||||
if (size <= 0)
|
if (size <= 0) {
|
||||||
|
DEBUGV("EEPROMClass::begin error, size == 0\n");
|
||||||
return;
|
return;
|
||||||
if (size > SPI_FLASH_SEC_SIZE)
|
}
|
||||||
|
if (size > SPI_FLASH_SEC_SIZE) {
|
||||||
|
DEBUGV("EEPROMClass::begin error, %d > %d\n", size, SPI_FLASH_SEC_SIZE);
|
||||||
size = SPI_FLASH_SEC_SIZE;
|
size = SPI_FLASH_SEC_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
size = (size + 3) & (~3);
|
size = (size + 3) & (~3);
|
||||||
|
|
||||||
@ -66,9 +71,9 @@ void EEPROMClass::begin(size_t size) {
|
|||||||
|
|
||||||
_size = size;
|
_size = size;
|
||||||
|
|
||||||
noInterrupts();
|
if (!ESP.flashRead(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(_data), _size)) {
|
||||||
spi_flash_read(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(_data), _size);
|
DEBUGV("EEPROMClass::begin flash read failed\n");
|
||||||
interrupts();
|
}
|
||||||
|
|
||||||
_dirty = false; //make sure dirty is cleared in case begin() is called 2nd+ time
|
_dirty = false; //make sure dirty is cleared in case begin() is called 2nd+ time
|
||||||
}
|
}
|
||||||
@ -88,19 +93,27 @@ void EEPROMClass::end() {
|
|||||||
|
|
||||||
|
|
||||||
uint8_t EEPROMClass::read(int const address) {
|
uint8_t EEPROMClass::read(int const address) {
|
||||||
if (address < 0 || (size_t)address >= _size)
|
if (address < 0 || (size_t)address >= _size) {
|
||||||
|
DEBUGV("EEPROMClass::read error, address %d > %d or %d < 0\n", address, _size, address);
|
||||||
return 0;
|
return 0;
|
||||||
if(!_data)
|
}
|
||||||
|
if (!_data) {
|
||||||
|
DEBUGV("EEPROMClass::read without ::begin\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return _data[address];
|
return _data[address];
|
||||||
}
|
}
|
||||||
|
|
||||||
void EEPROMClass::write(int const address, uint8_t const value) {
|
void EEPROMClass::write(int const address, uint8_t const value) {
|
||||||
if (address < 0 || (size_t)address >= _size)
|
if (address < 0 || (size_t)address >= _size) {
|
||||||
|
DEBUGV("EEPROMClass::write error, address %d > %d or %d < 0\n", address, _size, address);
|
||||||
return;
|
return;
|
||||||
if(!_data)
|
}
|
||||||
|
if(!_data) {
|
||||||
|
DEBUGV("EEPROMClass::read without ::begin\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Optimise _dirty. Only flagged if data written is different.
|
// Optimise _dirty. Only flagged if data written is different.
|
||||||
uint8_t* pData = &_data[address];
|
uint8_t* pData = &_data[address];
|
||||||
@ -112,7 +125,6 @@ void EEPROMClass::write(int const address, uint8_t const value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool EEPROMClass::commit() {
|
bool EEPROMClass::commit() {
|
||||||
bool ret = false;
|
|
||||||
if (!_size)
|
if (!_size)
|
||||||
return false;
|
return false;
|
||||||
if(!_dirty)
|
if(!_dirty)
|
||||||
@ -120,16 +132,15 @@ bool EEPROMClass::commit() {
|
|||||||
if(!_data)
|
if(!_data)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
noInterrupts();
|
if (ESP.flashEraseSector(_sector)) {
|
||||||
if(spi_flash_erase_sector(_sector) == SPI_FLASH_RESULT_OK) {
|
if (ESP.flashWrite(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(_data), _size)) {
|
||||||
if(spi_flash_write(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(_data), _size) == SPI_FLASH_RESULT_OK) {
|
|
||||||
_dirty = false;
|
_dirty = false;
|
||||||
ret = true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
interrupts();
|
|
||||||
|
|
||||||
return ret;
|
DEBUGV("EEPROMClass::commit failed\n");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t * EEPROMClass::getDataPtr() {
|
uint8_t * EEPROMClass::getDataPtr() {
|
||||||
|
@ -14,7 +14,7 @@ byte value;
|
|||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// initialize serial and wait for port to open:
|
// initialize serial and wait for port to open:
|
||||||
Serial.begin(9600);
|
Serial.begin(115200);
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
; // wait for serial port to connect. Needed for Leonardo only
|
; // wait for serial port to connect. Needed for Leonardo only
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
int addr = 0;
|
int addr = 0;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
EEPROM.begin(512);
|
EEPROM.begin(512);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +34,11 @@ void loop() {
|
|||||||
addr = addr + 1;
|
addr = addr + 1;
|
||||||
if (addr == 512) {
|
if (addr == 512) {
|
||||||
addr = 0;
|
addr = 0;
|
||||||
EEPROM.commit();
|
if (EEPROM.commit()) {
|
||||||
|
Serial.println("EEPROM successfully committed");
|
||||||
|
} else {
|
||||||
|
Serial.println("ERROR! EEPROM commit failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(100);
|
delay(100);
|
||||||
|
@ -113,8 +113,8 @@ protected:
|
|||||||
* constructor
|
* constructor
|
||||||
*/
|
*/
|
||||||
HTTPClient::HTTPClient()
|
HTTPClient::HTTPClient()
|
||||||
|
: _client(nullptr), _userAgent(F("ESP8266HTTPClient"))
|
||||||
{
|
{
|
||||||
_client = nullptr;
|
|
||||||
#if HTTPCLIENT_1_1_COMPATIBLE
|
#if HTTPCLIENT_1_1_COMPATIBLE
|
||||||
_tcpDeprecated.reset(nullptr);
|
_tcpDeprecated.reset(nullptr);
|
||||||
#endif
|
#endif
|
||||||
@ -536,8 +536,8 @@ void HTTPClient::setTimeout(uint16_t timeout)
|
|||||||
bool HTTPClient::setURL(String url)
|
bool HTTPClient::setURL(String url)
|
||||||
{
|
{
|
||||||
// if the new location is only a path then only update the URI
|
// if the new location is only a path then only update the URI
|
||||||
if (_location.startsWith("/")) {
|
if (url && url[0] == '/') {
|
||||||
_uri = _location;
|
_uri = url;
|
||||||
clear();
|
clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1294,21 +1294,21 @@ int HTTPClient::handleHeaderResponse()
|
|||||||
String headerValue = headerLine.substring(headerLine.indexOf(':') + 1);
|
String headerValue = headerLine.substring(headerLine.indexOf(':') + 1);
|
||||||
headerValue.trim();
|
headerValue.trim();
|
||||||
|
|
||||||
if(headerName.equalsIgnoreCase("Content-Length")) {
|
if(headerName.equalsIgnoreCase(F("Content-Length"))) {
|
||||||
_size = headerValue.toInt();
|
_size = headerValue.toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_canReuse && headerName.equalsIgnoreCase("Connection")) {
|
if(_canReuse && headerName.equalsIgnoreCase(F("Connection"))) {
|
||||||
if(headerValue.indexOf("close") >= 0 && headerValue.indexOf("keep-alive") < 0) {
|
if(headerValue.indexOf("close") >= 0 && headerValue.indexOf("keep-alive") < 0) {
|
||||||
_canReuse = false;
|
_canReuse = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(headerName.equalsIgnoreCase("Transfer-Encoding")) {
|
if(headerName.equalsIgnoreCase(F("Transfer-Encoding"))) {
|
||||||
transferEncoding = headerValue;
|
transferEncoding = headerValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(headerName.equalsIgnoreCase("Location")) {
|
if(headerName.equalsIgnoreCase(F("Location"))) {
|
||||||
_location = headerValue;
|
_location = headerValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1334,7 +1334,7 @@ int HTTPClient::handleHeaderResponse()
|
|||||||
|
|
||||||
if(transferEncoding.length() > 0) {
|
if(transferEncoding.length() > 0) {
|
||||||
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] Transfer-Encoding: %s\n", transferEncoding.c_str());
|
DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] Transfer-Encoding: %s\n", transferEncoding.c_str());
|
||||||
if(transferEncoding.equalsIgnoreCase("chunked")) {
|
if(transferEncoding.equalsIgnoreCase(F("chunked"))) {
|
||||||
_transferEncoding = HTTPC_TE_CHUNKED;
|
_transferEncoding = HTTPC_TE_CHUNKED;
|
||||||
} else {
|
} else {
|
||||||
return HTTPC_ERROR_ENCODING;
|
return HTTPC_ERROR_ENCODING;
|
||||||
|
@ -242,7 +242,7 @@ protected:
|
|||||||
String _uri;
|
String _uri;
|
||||||
String _protocol;
|
String _protocol;
|
||||||
String _headers;
|
String _headers;
|
||||||
String _userAgent = "ESP8266HTTPClient";
|
String _userAgent;
|
||||||
String _base64Authorization;
|
String _base64Authorization;
|
||||||
|
|
||||||
/// Response handling
|
/// Response handling
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
name=ESP8266HTTPUpdateServer
|
name=ESP8266HTTPUpdateServer
|
||||||
version=1.0
|
version=1.0
|
||||||
author=Ivan Grokhotkov, Miguel Ángel Ajo
|
author=Ivan Grokhotkov, Miguel Angel Ajo
|
||||||
maintainer=Ivan Grokhtkov <ivan@esp8266.com>
|
maintainer=Ivan Grokhtkov <ivan@esp8266.com>
|
||||||
sentence=Simple HTTP Update server based on the ESP8266WebServer
|
sentence=Simple HTTP Update server based on the ESP8266WebServer
|
||||||
paragraph=The library accepts HTTP post requests to the /update url, and updates the ESP8266 firmware.
|
paragraph=The library accepts HTTP post requests to the /update url, and updates the ESP8266 firmware.
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
#include <WiFiServer.h>
|
#include <WiFiServer.h>
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
#include <WiFiUdp.h>
|
#include <WiFiUdp.h>
|
||||||
|
#include <flash_hal.h>
|
||||||
|
#include <FS.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
#include "StreamString.h"
|
#include "StreamString.h"
|
||||||
#include "ESP8266HTTPUpdateServer.h"
|
#include "ESP8266HTTPUpdateServer.h"
|
||||||
|
|
||||||
@ -10,11 +13,25 @@ namespace esp8266httpupdateserver {
|
|||||||
using namespace esp8266webserver;
|
using namespace esp8266webserver;
|
||||||
|
|
||||||
static const char serverIndex[] PROGMEM =
|
static const char serverIndex[] PROGMEM =
|
||||||
R"(<html><body><form method='POST' action='' enctype='multipart/form-data'>
|
R"(<!DOCTYPE html>
|
||||||
<input type='file' name='update'>
|
<html lang='en'>
|
||||||
<input type='submit' value='Update'>
|
<head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
<meta name='viewport' content='width=device-width,initial-scale=1'/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form method='POST' action='' enctype='multipart/form-data'>
|
||||||
|
Firmware:<br>
|
||||||
|
<input type='file' accept='.bin' name='firmware'>
|
||||||
|
<input type='submit' value='Update Firmware'>
|
||||||
</form>
|
</form>
|
||||||
</body></html>)";
|
<form method='POST' action='' enctype='multipart/form-data'>
|
||||||
|
FileSystem:<br>
|
||||||
|
<input type='file' accept='.bin' name='filesystem'>
|
||||||
|
<input type='submit' value='Update FileSystem'>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>)";
|
||||||
static const char successResponse[] PROGMEM =
|
static const char successResponse[] PROGMEM =
|
||||||
"<META http-equiv=\"refresh\" content=\"15;URL=/\">Update Success! Rebooting...";
|
"<META http-equiv=\"refresh\" content=\"15;URL=/\">Update Success! Rebooting...";
|
||||||
|
|
||||||
@ -75,10 +92,19 @@ void ESP8266HTTPUpdateServerTemplate<ServerType>::setup(ESP8266WebServerTemplate
|
|||||||
WiFiUDP::stopAll();
|
WiFiUDP::stopAll();
|
||||||
if (_serial_output)
|
if (_serial_output)
|
||||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||||
|
if (upload.name == "filesystem") {
|
||||||
|
size_t fsSize = ((size_t) &_FS_end - (size_t) &_FS_start);
|
||||||
|
SPIFFS.end();
|
||||||
|
LittleFS.end();
|
||||||
|
if (!Update.begin(fsSize, U_FS)){//start with max available size
|
||||||
|
if (_serial_output) Update.printError(Serial);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||||
if(!Update.begin(maxSketchSpace)){//start with max available size
|
if (!Update.begin(maxSketchSpace, U_FLASH)){//start with max available size
|
||||||
_setUpdaterError();
|
_setUpdaterError();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if(_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()){
|
} else if(_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()){
|
||||||
if (_serial_output) Serial.printf(".");
|
if (_serial_output) Serial.printf(".");
|
||||||
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
|
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
|
||||||
|
@ -238,7 +238,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
|||||||
DEBUG_OUTPUT.println(headerValue);
|
DEBUG_OUTPUT.println(headerValue);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (headerName.equalsIgnoreCase("Host")){
|
if (headerName.equalsIgnoreCase(F("Host"))){
|
||||||
_hostHeader = headerValue;
|
_hostHeader = headerValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,8 +85,9 @@ void setup() {
|
|||||||
Serial.swap();
|
Serial.swap();
|
||||||
// Hardware serial is now on RX:GPIO13 TX:GPIO15
|
// Hardware serial is now on RX:GPIO13 TX:GPIO15
|
||||||
// use SoftwareSerial on regular RX(3)/TX(1) for logging
|
// use SoftwareSerial on regular RX(3)/TX(1) for logging
|
||||||
logger = new SoftwareSerial(3, 1);
|
logger = new SoftwareSerial();
|
||||||
logger->begin(BAUD_LOGGER);
|
logger->begin(BAUD_LOGGER, 3, 1);
|
||||||
|
logger->enableIntTx(false);
|
||||||
logger->println("\n\nUsing SoftwareSerial for logging");
|
logger->println("\n\nUsing SoftwareSerial for logging");
|
||||||
#else
|
#else
|
||||||
logger->begin(BAUD_LOGGER);
|
logger->begin(BAUD_LOGGER);
|
||||||
|
@ -49,24 +49,24 @@ extern "C" {
|
|||||||
* @param p Print interface
|
* @param p Print interface
|
||||||
*/
|
*/
|
||||||
void ESP8266WiFiClass::printDiag(Print& p) {
|
void ESP8266WiFiClass::printDiag(Print& p) {
|
||||||
const char* modes[] = { "NULL", "STA", "AP", "STA+AP" };
|
const char* const modes[] = { "NULL", "STA", "AP", "STA+AP" };
|
||||||
p.print("Mode: ");
|
p.print(F("Mode: "));
|
||||||
p.println(modes[wifi_get_opmode()]);
|
p.println(modes[wifi_get_opmode()]);
|
||||||
|
|
||||||
const char* phymodes[] = { "", "B", "G", "N" };
|
const char* const phymodes[] = { "", "B", "G", "N" };
|
||||||
p.print("PHY mode: ");
|
p.print(F("PHY mode: "));
|
||||||
p.println(phymodes[(int) wifi_get_phy_mode()]);
|
p.println(phymodes[(int) wifi_get_phy_mode()]);
|
||||||
|
|
||||||
p.print("Channel: ");
|
p.print(F("Channel: "));
|
||||||
p.println(wifi_get_channel());
|
p.println(wifi_get_channel());
|
||||||
|
|
||||||
p.print("AP id: ");
|
p.print(F("AP id: "));
|
||||||
p.println(wifi_station_get_current_ap_id());
|
p.println(wifi_station_get_current_ap_id());
|
||||||
|
|
||||||
p.print("Status: ");
|
p.print(F("Status: "));
|
||||||
p.println(wifi_station_get_connect_status());
|
p.println(wifi_station_get_connect_status());
|
||||||
|
|
||||||
p.print("Auto connect: ");
|
p.print(F("Auto connect: "));
|
||||||
p.println(wifi_station_get_auto_connect());
|
p.println(wifi_station_get_auto_connect());
|
||||||
|
|
||||||
struct station_config conf;
|
struct station_config conf;
|
||||||
@ -75,22 +75,14 @@ void ESP8266WiFiClass::printDiag(Print& p) {
|
|||||||
char ssid[33]; //ssid can be up to 32chars, => plus null term
|
char ssid[33]; //ssid can be up to 32chars, => plus null term
|
||||||
memcpy(ssid, conf.ssid, sizeof(conf.ssid));
|
memcpy(ssid, conf.ssid, sizeof(conf.ssid));
|
||||||
ssid[32] = 0; //nullterm in case of 32 char ssid
|
ssid[32] = 0; //nullterm in case of 32 char ssid
|
||||||
|
p.printf_P(PSTR("SSID (%d): %s\n"), strlen(ssid), ssid);
|
||||||
p.print("SSID (");
|
|
||||||
p.print(strlen(ssid));
|
|
||||||
p.print("): ");
|
|
||||||
p.println(ssid);
|
|
||||||
|
|
||||||
char passphrase[65];
|
char passphrase[65];
|
||||||
memcpy(passphrase, conf.password, sizeof(conf.password));
|
memcpy(passphrase, conf.password, sizeof(conf.password));
|
||||||
passphrase[64] = 0;
|
passphrase[64] = 0;
|
||||||
|
p.printf_P(PSTR("Passphrase (%d): %s\n"), strlen(passphrase), passphrase);
|
||||||
|
|
||||||
p.print("Passphrase (");
|
p.print(F("BSSID set: "));
|
||||||
p.print(strlen(passphrase));
|
|
||||||
p.print("): ");
|
|
||||||
p.println(passphrase);
|
|
||||||
|
|
||||||
p.print("BSSID set: ");
|
|
||||||
p.println(conf.bssid_set);
|
p.println(conf.bssid_set);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HTTPUPDATE_1_2_COMPATIBLE
|
#if HTTPUPDATE_1_2_COMPATIBLE
|
||||||
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion,
|
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion,
|
||||||
const String& httpsFingerprint, bool reboot)
|
const String& httpsFingerprint, bool reboot)
|
||||||
{
|
{
|
||||||
@ -94,7 +94,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::update(WiFiClient& client, const String& url
|
|||||||
return handleUpdate(http, currentVersion, false);
|
return handleUpdate(http, currentVersion, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HTTPUPDATE_1_2_COMPATIBLE
|
#if HTTPUPDATE_1_2_COMPATIBLE
|
||||||
HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint)
|
HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint)
|
||||||
{
|
{
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
@ -133,7 +133,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(WiFiClient& client, const Strin
|
|||||||
return handleUpdate(http, currentVersion, true);
|
return handleUpdate(http, currentVersion, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HTTPUPDATE_1_2_COMPATIBLE
|
#if HTTPUPDATE_1_2_COMPATIBLE
|
||||||
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion,
|
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion,
|
||||||
bool https, const String& httpsFingerprint, bool reboot)
|
bool https, const String& httpsFingerprint, bool reboot)
|
||||||
{
|
{
|
||||||
@ -263,6 +263,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String&
|
|||||||
http.setTimeout(_httpClientTimeout);
|
http.setTimeout(_httpClientTimeout);
|
||||||
http.setFollowRedirects(_followRedirects);
|
http.setFollowRedirects(_followRedirects);
|
||||||
http.setUserAgent(F("ESP8266-http-Update"));
|
http.setUserAgent(F("ESP8266-http-Update"));
|
||||||
|
http.addHeader(F("x-ESP8266-Chip-ID"), String(ESP.getChipId()));
|
||||||
http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress());
|
http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress());
|
||||||
http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress());
|
http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress());
|
||||||
http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace()));
|
http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace()));
|
||||||
@ -388,7 +389,11 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String&
|
|||||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n");
|
DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n");
|
||||||
http.end();
|
http.end();
|
||||||
|
|
||||||
|
#ifdef ATOMIC_FS_UPDATE
|
||||||
|
if(_rebootOnUpdate) {
|
||||||
|
#else
|
||||||
if(_rebootOnUpdate && !spiffs) {
|
if(_rebootOnUpdate && !spiffs) {
|
||||||
|
#endif
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,14 +26,16 @@
|
|||||||
#ifndef ESP8266HTTPUPDATE_H_
|
#ifndef ESP8266HTTPUPDATE_H_
|
||||||
#define ESP8266HTTPUPDATE_H_
|
#define ESP8266HTTPUPDATE_H_
|
||||||
|
|
||||||
#define HTTPUPDATE_1_2_COMPATIBLE
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <WiFiClient.h>
|
#include <WiFiClient.h>
|
||||||
#include <WiFiUdp.h>
|
#include <WiFiUdp.h>
|
||||||
#include <ESP8266HTTPClient.h>
|
#include <ESP8266HTTPClient.h>
|
||||||
|
|
||||||
|
#ifndef HTTPUPDATE_1_2_COMPATIBLE
|
||||||
|
#define HTTPUPDATE_1_2_COMPATIBLE HTTPCLIENT_1_1_COMPATIBLE
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_HTTP_UPDATE
|
#ifdef DEBUG_ESP_HTTP_UPDATE
|
||||||
#ifdef DEBUG_ESP_PORT
|
#ifdef DEBUG_ESP_PORT
|
||||||
#define DEBUG_HTTP_UPDATE(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ )
|
#define DEBUG_HTTP_UPDATE(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ )
|
||||||
@ -85,7 +87,7 @@ public:
|
|||||||
_ledOn = ledOn;
|
_ledOn = ledOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HTTPUPDATE_1_2_COMPATIBLE
|
#if HTTPUPDATE_1_2_COMPATIBLE
|
||||||
// This function is deprecated, use rebootOnUpdate and the next one instead
|
// This function is deprecated, use rebootOnUpdate and the next one instead
|
||||||
t_httpUpdate_return update(const String& url, const String& currentVersion,
|
t_httpUpdate_return update(const String& url, const String& currentVersion,
|
||||||
const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
|
const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
|
||||||
@ -97,7 +99,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
t_httpUpdate_return update(WiFiClient& client, const String& url, const String& currentVersion = "");
|
t_httpUpdate_return update(WiFiClient& client, const String& url, const String& currentVersion = "");
|
||||||
|
|
||||||
#ifdef HTTPUPDATE_1_2_COMPATIBLE
|
#if HTTPUPDATE_1_2_COMPATIBLE
|
||||||
// This function is deprecated, use one of the overloads below along with rebootOnUpdate
|
// This function is deprecated, use one of the overloads below along with rebootOnUpdate
|
||||||
t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion,
|
t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion,
|
||||||
bool https, const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
|
bool https, const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
|
||||||
@ -112,7 +114,7 @@ public:
|
|||||||
t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/",
|
t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/",
|
||||||
const String& currentVersion = "");
|
const String& currentVersion = "");
|
||||||
|
|
||||||
#ifdef HTTPUPDATE_1_2_COMPATIBLE
|
#if HTTPUPDATE_1_2_COMPATIBLE
|
||||||
// This function is deprecated, use rebootOnUpdate and the next one instead
|
// This function is deprecated, use rebootOnUpdate and the next one instead
|
||||||
t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion,
|
t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion,
|
||||||
const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
|
const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#include <ESP8266mDNS.h>
|
#include <ESP8266mDNS.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNS responder global instance
|
MDNS responder global instance
|
||||||
*
|
|
||||||
* Class type that is instantiated depends on the type mapping in ESP8266mDNS.h
|
Class type that is instantiated depends on the type mapping in ESP8266mDNS.h
|
||||||
*/
|
*/
|
||||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||||
MDNSResponder MDNS;
|
MDNSResponder MDNS;
|
||||||
#endif
|
#endif
|
||||||
|
@ -47,10 +47,10 @@
|
|||||||
|
|
||||||
|
|
||||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||||
// Maps the implementation to use to the global namespace type
|
// Maps the implementation to use to the global namespace type
|
||||||
//using MDNSResponder = Legacy_MDNSResponder::MDNSResponder; //legacy
|
//using MDNSResponder = Legacy_MDNSResponder::MDNSResponder; //legacy
|
||||||
using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; //new
|
using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; //new
|
||||||
|
|
||||||
extern MDNSResponder MDNS;
|
extern MDNSResponder MDNS;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,26 @@
|
|||||||
/*
|
/*
|
||||||
ESP8266 Multicast DNS (port of CC3000 Multicast DNS library)
|
ESP8266 Multicast DNS (port of CC3000 Multicast DNS library)
|
||||||
Version 1.1
|
Version 1.1
|
||||||
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
|
Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)
|
||||||
ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
|
ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)
|
||||||
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
|
Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)
|
||||||
|
|
||||||
This is a simple implementation of multicast DNS query support for an Arduino
|
This is a simple implementation of multicast DNS query support for an Arduino
|
||||||
running on ESP8266 chip. Only support for resolving address queries is currently
|
running on ESP8266 chip. Only support for resolving address queries is currently
|
||||||
implemented.
|
implemented.
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
- ESP8266WiFi library
|
- ESP8266WiFi library
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
- Include the ESP8266 Multicast DNS library in the sketch.
|
- Include the ESP8266 Multicast DNS library in the sketch.
|
||||||
- Call the begin method in the sketch's setup and provide a domain name (without
|
- Call the begin method in the sketch's setup and provide a domain name (without
|
||||||
the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the
|
the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the
|
||||||
Adafruit CC3000 class instance. Optionally provide a time to live (in seconds)
|
Adafruit CC3000 class instance. Optionally provide a time to live (in seconds)
|
||||||
for the DNS record--the default is 1 hour.
|
for the DNS record--the default is 1 hour.
|
||||||
- Call the update method in each iteration of the sketch's loop function.
|
- Call the update method in each iteration of the sketch's loop function.
|
||||||
|
|
||||||
License (MIT license):
|
License (MIT license):
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
@ -54,28 +54,33 @@ License (MIT license):
|
|||||||
class UdpContext;
|
class UdpContext;
|
||||||
|
|
||||||
|
|
||||||
namespace Legacy_MDNSResponder {
|
namespace Legacy_MDNSResponder
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
struct MDNSService;
|
struct MDNSService;
|
||||||
struct MDNSTxt;
|
struct MDNSTxt;
|
||||||
struct MDNSAnswer;
|
struct MDNSAnswer;
|
||||||
|
|
||||||
class MDNSResponder {
|
class MDNSResponder
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
MDNSResponder();
|
MDNSResponder();
|
||||||
~MDNSResponder();
|
~MDNSResponder();
|
||||||
bool begin(const char* hostName);
|
bool begin(const char* hostName);
|
||||||
bool begin(const String& hostName) {
|
bool begin(const String& hostName)
|
||||||
|
{
|
||||||
return begin(hostName.c_str());
|
return begin(hostName.c_str());
|
||||||
}
|
}
|
||||||
//for compatibility
|
//for compatibility
|
||||||
bool begin(const char* hostName, IPAddress ip, uint32_t ttl=120){
|
bool begin(const char* hostName, IPAddress ip, uint32_t ttl = 120)
|
||||||
|
{
|
||||||
(void) ip;
|
(void) ip;
|
||||||
(void) ttl;
|
(void) ttl;
|
||||||
return begin(hostName);
|
return begin(hostName);
|
||||||
}
|
}
|
||||||
bool begin(const String& hostName, IPAddress ip, uint32_t ttl=120) {
|
bool begin(const String& hostName, IPAddress ip, uint32_t ttl = 120)
|
||||||
|
{
|
||||||
return begin(hostName.c_str(), ip, ttl);
|
return begin(hostName.c_str(), ip, ttl);
|
||||||
}
|
}
|
||||||
/* Application should call this whenever AP is configured/disabled */
|
/* Application should call this whenever AP is configured/disabled */
|
||||||
@ -83,39 +88,47 @@ public:
|
|||||||
void update();
|
void update();
|
||||||
|
|
||||||
void addService(char *service, char *proto, uint16_t port);
|
void addService(char *service, char *proto, uint16_t port);
|
||||||
void addService(const char *service, const char *proto, uint16_t port){
|
void addService(const char *service, const char *proto, uint16_t port)
|
||||||
|
{
|
||||||
addService((char *)service, (char *)proto, port);
|
addService((char *)service, (char *)proto, port);
|
||||||
}
|
}
|
||||||
void addService(const String& service, const String& proto, uint16_t port){
|
void addService(const String& service, const String& proto, uint16_t port)
|
||||||
|
{
|
||||||
addService(service.c_str(), proto.c_str(), port);
|
addService(service.c_str(), proto.c_str(), port);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool addServiceTxt(char *name, char *proto, char * key, char * value);
|
bool addServiceTxt(char *name, char *proto, char * key, char * value);
|
||||||
bool addServiceTxt(const char *name, const char *proto, const char *key,const char * value){
|
bool addServiceTxt(const char *name, const char *proto, const char *key, const char * value)
|
||||||
|
{
|
||||||
return addServiceTxt((char *)name, (char *)proto, (char *)key, (char *)value);
|
return addServiceTxt((char *)name, (char *)proto, (char *)key, (char *)value);
|
||||||
}
|
}
|
||||||
bool addServiceTxt(const String& name, const String& proto, const String& key, const String& value){
|
bool addServiceTxt(const String& name, const String& proto, const String& key, const String& value)
|
||||||
|
{
|
||||||
return addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str());
|
return addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
int queryService(char *service, char *proto);
|
int queryService(char *service, char *proto);
|
||||||
int queryService(const char *service, const char *proto){
|
int queryService(const char *service, const char *proto)
|
||||||
|
{
|
||||||
return queryService((char *)service, (char *)proto);
|
return queryService((char *)service, (char *)proto);
|
||||||
}
|
}
|
||||||
int queryService(const String& service, const String& proto){
|
int queryService(const String& service, const String& proto)
|
||||||
|
{
|
||||||
return queryService(service.c_str(), proto.c_str());
|
return queryService(service.c_str(), proto.c_str());
|
||||||
}
|
}
|
||||||
String hostname(int idx);
|
String hostname(int idx);
|
||||||
IPAddress IP(int idx);
|
IPAddress IP(int idx);
|
||||||
uint16_t port(int idx);
|
uint16_t port(int idx);
|
||||||
|
|
||||||
void enableArduino(uint16_t port, bool auth=false);
|
void enableArduino(uint16_t port, bool auth = false);
|
||||||
|
|
||||||
void setInstanceName(String name);
|
void setInstanceName(String name);
|
||||||
void setInstanceName(const char * name){
|
void setInstanceName(const char * name)
|
||||||
|
{
|
||||||
setInstanceName(String(name));
|
setInstanceName(String(name));
|
||||||
}
|
}
|
||||||
void setInstanceName(char * name){
|
void setInstanceName(char * name)
|
||||||
|
{
|
||||||
setInstanceName(String(name));
|
setInstanceName(String(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,103 +1,103 @@
|
|||||||
/*
|
/*
|
||||||
* LEAmDNS.h
|
LEAmDNS.h
|
||||||
* (c) 2018, LaborEtArs
|
(c) 2018, LaborEtArs
|
||||||
*
|
|
||||||
* Version 0.9 beta
|
Version 0.9 beta
|
||||||
*
|
|
||||||
* Some notes (from LaborEtArs, 2018):
|
Some notes (from LaborEtArs, 2018):
|
||||||
* Essentially, this is an rewrite of the original EPS8266 Multicast DNS code (ESP8266mDNS).
|
Essentially, this is an rewrite of the original EPS8266 Multicast DNS code (ESP8266mDNS).
|
||||||
* The target of this rewrite was to keep the existing interface as stable as possible while
|
The target of this rewrite was to keep the existing interface as stable as possible while
|
||||||
* adding and extending the supported set of mDNS features.
|
adding and extending the supported set of mDNS features.
|
||||||
* A lot of the additions were basicly taken from Erik Ekman's lwIP mdns app code.
|
A lot of the additions were basicly taken from Erik Ekman's lwIP mdns app code.
|
||||||
*
|
|
||||||
* Supported mDNS features (in some cases somewhat limited):
|
Supported mDNS features (in some cases somewhat limited):
|
||||||
* - Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service
|
- Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service
|
||||||
* - Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented
|
- Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented
|
||||||
* - Probing host and service domains for uniqueness in the local network
|
- Probing host and service domains for uniqueness in the local network
|
||||||
* - Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak)
|
- Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak)
|
||||||
* - Announcing available services after successful probing
|
- Announcing available services after successful probing
|
||||||
* - Using fixed service TXT items or
|
- Using fixed service TXT items or
|
||||||
* - Using dynamic service TXT items for presented services (via callback)
|
- Using dynamic service TXT items for presented services (via callback)
|
||||||
* - Remove services (and un-announcing them to the observers by sending goodbye-messages)
|
- Remove services (and un-announcing them to the observers by sending goodbye-messages)
|
||||||
* - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period)
|
- Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period)
|
||||||
* - Dynamic queries for DNS-SD services with cached and updated answers and user notifications
|
- Dynamic queries for DNS-SD services with cached and updated answers and user notifications
|
||||||
*
|
|
||||||
*
|
|
||||||
* Usage:
|
Usage:
|
||||||
* In most cases, this implementation should work as a 'drop-in' replacement for the original
|
In most cases, this implementation should work as a 'drop-in' replacement for the original
|
||||||
* ESP8266 Multicast DNS code. Adjustments to the existing code would only be needed, if some
|
ESP8266 Multicast DNS code. Adjustments to the existing code would only be needed, if some
|
||||||
* of the new features should be used.
|
of the new features should be used.
|
||||||
*
|
|
||||||
* For presenting services:
|
For presenting services:
|
||||||
* In 'setup()':
|
In 'setup()':
|
||||||
* Install a callback for the probing of host (and service) domains via 'MDNS.setProbeResultCallback(probeResultCallback, &userData);'
|
Install a callback for the probing of host (and service) domains via 'MDNS.setProbeResultCallback(probeResultCallback, &userData);'
|
||||||
* Register DNS-SD services with 'MDNSResponder::hMDNSService hService = MDNS.addService("MyESP", "http", "tcp", 5000);'
|
Register DNS-SD services with 'MDNSResponder::hMDNSService hService = MDNS.addService("MyESP", "http", "tcp", 5000);'
|
||||||
* (Install additional callbacks for the probing of these service domains via 'MDNS.setServiceProbeResultCallback(hService, probeResultCallback, &userData);')
|
(Install additional callbacks for the probing of these service domains via 'MDNS.setServiceProbeResultCallback(hService, probeResultCallback, &userData);')
|
||||||
* Add service TXT items with 'MDNS.addServiceTxt(hService, "c#", "1");' or by installing a service TXT callback
|
Add service TXT items with 'MDNS.addServiceTxt(hService, "c#", "1");' or by installing a service TXT callback
|
||||||
* using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service specific
|
using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service specific
|
||||||
* 'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);'
|
'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);'
|
||||||
* Call MDNS.begin("MyHostname");
|
Call MDNS.begin("MyHostname");
|
||||||
*
|
|
||||||
* In 'probeResultCallback(MDNSResponder* p_MDNSResponder, const char* p_pcDomain, MDNSResponder:hMDNSService p_hService, bool p_bProbeResult, void* p_pUserdata)':
|
In 'probeResultCallback(MDNSResponder* p_MDNSResponder, const char* p_pcDomain, MDNSResponder:hMDNSService p_hService, bool p_bProbeResult, void* p_pUserdata)':
|
||||||
* Check the probe result and update the host or service domain name if the probe failed
|
Check the probe result and update the host or service domain name if the probe failed
|
||||||
*
|
|
||||||
* In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, void* p_pUserdata)':
|
In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, void* p_pUserdata)':
|
||||||
* Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, "c#", "1");'
|
Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, "c#", "1");'
|
||||||
*
|
|
||||||
* In loop():
|
In loop():
|
||||||
* Call 'MDNS.update();'
|
Call 'MDNS.update();'
|
||||||
*
|
|
||||||
*
|
|
||||||
* For querying services:
|
For querying services:
|
||||||
* Static:
|
Static:
|
||||||
* Call 'uint32_t u32AnswerCount = MDNS.queryService("http", "tcp");'
|
Call 'uint32_t u32AnswerCount = MDNS.queryService("http", "tcp");'
|
||||||
* Iterate answers by: 'for (uint32_t u=0; u<u32AnswerCount; ++u) { const char* pHostname = MDNS.answerHostname(u); }'
|
Iterate answers by: 'for (uint32_t u=0; u<u32AnswerCount; ++u) { const char* pHostname = MDNS.answerHostname(u); }'
|
||||||
* You should call MDNS.removeQuery() sometimes later (when the answers are nott needed anymore)
|
You should call MDNS.removeQuery() sometimes later (when the answers are nott needed anymore)
|
||||||
*
|
|
||||||
* Dynamic:
|
Dynamic:
|
||||||
* Install a dynamic query by calling 'DNSResponder::hMDNSServiceQuery hServiceQuery = MDNS.installServiceQuery("http", "tcp", serviceQueryCallback, &userData);'
|
Install a dynamic query by calling 'DNSResponder::hMDNSServiceQuery hServiceQuery = MDNS.installServiceQuery("http", "tcp", serviceQueryCallback, &userData);'
|
||||||
* The callback 'serviceQueryCallback(MDNSResponder* p_MDNSResponder, const hMDNSServiceQuery p_hServiceQuery, uint32_t p_u32AnswerIndex,
|
The callback 'serviceQueryCallback(MDNSResponder* p_MDNSResponder, const hMDNSServiceQuery p_hServiceQuery, uint32_t p_u32AnswerIndex,
|
||||||
* enuServiceQueryAnswerType p_ServiceQueryAnswerType, bool p_bSetContent, void* p_pUserdata)'
|
enuServiceQueryAnswerType p_ServiceQueryAnswerType, bool p_bSetContent, void* p_pUserdata)'
|
||||||
* is called for any change in the answer set.
|
is called for any change in the answer set.
|
||||||
* Call 'MDNS.removeServiceQuery(hServiceQuery);' when the answers are not needed anymore
|
Call 'MDNS.removeServiceQuery(hServiceQuery);' when the answers are not needed anymore
|
||||||
*
|
|
||||||
*
|
|
||||||
* Reference:
|
Reference:
|
||||||
* Used mDNS messages:
|
Used mDNS messages:
|
||||||
* A (0x01): eg. esp8266.local A OP TTL 123.456.789.012
|
A (0x01): eg. esp8266.local A OP TTL 123.456.789.012
|
||||||
* AAAA (0x1C): eg. esp8266.local AAAA OP TTL 1234:5678::90
|
AAAA (0x1C): eg. esp8266.local AAAA OP TTL 1234:5678::90
|
||||||
* PTR (0x0C, srv name): eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local
|
PTR (0x0C, srv name): eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local
|
||||||
* PTR (0x0C, srv type): eg. _services._dns-sd._udp.local PTR OP TTL _http._tcp.local
|
PTR (0x0C, srv type): eg. _services._dns-sd._udp.local PTR OP TTL _http._tcp.local
|
||||||
* PTR (0x0C, IP4): eg. 012.789.456.123.in-addr.arpa PTR OP TTL esp8266.local
|
PTR (0x0C, IP4): eg. 012.789.456.123.in-addr.arpa PTR OP TTL esp8266.local
|
||||||
* PTR (0x0C, IP6): eg. 90.0.0.0.0.0.0.0.0.0.0.0.78.56.34.12.ip6.arpa PTR OP TTL esp8266.local
|
PTR (0x0C, IP6): eg. 90.0.0.0.0.0.0.0.0.0.0.0.78.56.34.12.ip6.arpa PTR OP TTL esp8266.local
|
||||||
* SRV (0x21): eg. MyESP._http._tcp.local SRV OP TTL PRIORITY WEIGHT PORT esp8266.local
|
SRV (0x21): eg. MyESP._http._tcp.local SRV OP TTL PRIORITY WEIGHT PORT esp8266.local
|
||||||
* TXT (0x10): eg. MyESP._http._tcp.local TXT OP TTL c#=1
|
TXT (0x10): eg. MyESP._http._tcp.local TXT OP TTL c#=1
|
||||||
*
|
|
||||||
* Some NOT used message types:
|
Some NOT used message types:
|
||||||
* OPT (0x29): eDNS
|
OPT (0x29): eDNS
|
||||||
* NSEC (0x2F): DNSSEC
|
NSEC (0x2F): DNSSEC
|
||||||
*
|
|
||||||
*
|
|
||||||
* License (MIT license):
|
License (MIT license):
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
* in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
* furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in
|
||||||
* all copies or substantial portions of the Software.
|
all copies or substantial portions of the Software.
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MDNS_H
|
#ifndef MDNS_H
|
||||||
#define MDNS_H
|
#define MDNS_H
|
||||||
@ -115,12 +115,14 @@
|
|||||||
#include "ESP8266WiFi.h"
|
#include "ESP8266WiFi.h"
|
||||||
|
|
||||||
|
|
||||||
namespace esp8266 {
|
namespace esp8266
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LEAmDNS
|
LEAmDNS
|
||||||
*/
|
*/
|
||||||
namespace MDNSImplementation {
|
namespace MDNSImplementation
|
||||||
|
{
|
||||||
|
|
||||||
//this should be defined at build time
|
//this should be defined at build time
|
||||||
#ifndef ARDUINO_BOARD
|
#ifndef ARDUINO_BOARD
|
||||||
@ -132,41 +134,42 @@ namespace MDNSImplementation {
|
|||||||
|
|
||||||
|
|
||||||
#ifdef MDNS_IP4_SUPPORT
|
#ifdef MDNS_IP4_SUPPORT
|
||||||
#define MDNS_IP4_SIZE 4
|
#define MDNS_IP4_SIZE 4
|
||||||
#endif
|
#endif
|
||||||
#ifdef MDNS_IP6_SUPPORT
|
#ifdef MDNS_IP6_SUPPORT
|
||||||
#define MDNS_IP6_SIZE 16
|
#define MDNS_IP6_SIZE 16
|
||||||
#endif
|
#endif
|
||||||
/*
|
/*
|
||||||
* Maximum length for all service txts for one service
|
Maximum length for all service txts for one service
|
||||||
*/
|
*/
|
||||||
#define MDNS_SERVICE_TXT_MAXLENGTH 1300
|
#define MDNS_SERVICE_TXT_MAXLENGTH 1300
|
||||||
/*
|
/*
|
||||||
* Maximum length for a full domain name eg. MyESP._http._tcp.local
|
Maximum length for a full domain name eg. MyESP._http._tcp.local
|
||||||
*/
|
*/
|
||||||
#define MDNS_DOMAIN_MAXLENGTH 256
|
#define MDNS_DOMAIN_MAXLENGTH 256
|
||||||
/*
|
/*
|
||||||
* Maximum length of on label in a domain name (length info fits into 6 bits)
|
Maximum length of on label in a domain name (length info fits into 6 bits)
|
||||||
*/
|
*/
|
||||||
#define MDNS_DOMAIN_LABEL_MAXLENGTH 63
|
#define MDNS_DOMAIN_LABEL_MAXLENGTH 63
|
||||||
/*
|
/*
|
||||||
* Maximum length of a service name eg. http
|
Maximum length of a service name eg. http
|
||||||
*/
|
*/
|
||||||
#define MDNS_SERVICE_NAME_LENGTH 15
|
#define MDNS_SERVICE_NAME_LENGTH 15
|
||||||
/*
|
/*
|
||||||
* Maximum length of a service protocol name eg. tcp
|
Maximum length of a service protocol name eg. tcp
|
||||||
*/
|
*/
|
||||||
#define MDNS_SERVICE_PROTOCOL_LENGTH 3
|
#define MDNS_SERVICE_PROTOCOL_LENGTH 3
|
||||||
/*
|
/*
|
||||||
* Default timeout for static service queries
|
Default timeout for static service queries
|
||||||
*/
|
*/
|
||||||
#define MDNS_QUERYSERVICES_WAIT_TIME 1000
|
#define MDNS_QUERYSERVICES_WAIT_TIME 1000
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MDNSResponder
|
MDNSResponder
|
||||||
*/
|
*/
|
||||||
class MDNSResponder {
|
class MDNSResponder
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
/* INTERFACE */
|
/* INTERFACE */
|
||||||
MDNSResponder(void);
|
MDNSResponder(void);
|
||||||
@ -177,7 +180,10 @@ public:
|
|||||||
// (probing, announcing, responding, ...)
|
// (probing, announcing, responding, ...)
|
||||||
// if interfaceAddress is not specified, default interface is STA, or AP when STA is not set
|
// if interfaceAddress is not specified, default interface is STA, or AP when STA is not set
|
||||||
bool begin(const char* p_pcHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/);
|
bool begin(const char* p_pcHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/);
|
||||||
bool begin(const String& p_strHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/) {return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL);}
|
bool begin(const String& p_strHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/)
|
||||||
|
{
|
||||||
|
return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL);
|
||||||
|
}
|
||||||
|
|
||||||
// Finish MDNS processing
|
// Finish MDNS processing
|
||||||
bool close(void);
|
bool close(void);
|
||||||
@ -189,7 +195,7 @@ public:
|
|||||||
bool setHostname(String p_strHostname);
|
bool setHostname(String p_strHostname);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hMDNSService (opaque handle to access the service)
|
hMDNSService (opaque handle to access the service)
|
||||||
*/
|
*/
|
||||||
typedef const void* hMDNSService;
|
typedef const void* hMDNSService;
|
||||||
|
|
||||||
@ -218,12 +224,18 @@ public:
|
|||||||
//for compatibility
|
//for compatibility
|
||||||
//Warning: this has the side effect of changing the hostname.
|
//Warning: this has the side effect of changing the hostname.
|
||||||
//TODO: implement instancename different from hostname
|
//TODO: implement instancename different from hostname
|
||||||
void setInstanceName(const char* p_pcHostname) {setHostname(p_pcHostname);}
|
void setInstanceName(const char* p_pcHostname)
|
||||||
|
{
|
||||||
|
setHostname(p_pcHostname);
|
||||||
|
}
|
||||||
// for esp32 compatibilty
|
// for esp32 compatibilty
|
||||||
void setInstanceName(const String& s_pcHostname) {setInstanceName(s_pcHostname.c_str());}
|
void setInstanceName(const String& s_pcHostname)
|
||||||
|
{
|
||||||
|
setInstanceName(s_pcHostname.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hMDNSTxt (opaque handle to access the TXT items)
|
hMDNSTxt (opaque handle to access the TXT items)
|
||||||
*/
|
*/
|
||||||
typedef void* hMDNSTxt;
|
typedef void* hMDNSTxt;
|
||||||
|
|
||||||
@ -270,8 +282,8 @@ public:
|
|||||||
String p_strValue);
|
String p_strValue);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MDNSDynamicServiceTxtCallbackFn
|
MDNSDynamicServiceTxtCallbackFn
|
||||||
* Callback function for dynamic MDNS TXT items
|
Callback function for dynamic MDNS TXT items
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef std::function<void(const hMDNSService p_hService)> MDNSDynamicServiceTxtCallbackFunc;
|
typedef std::function<void(const hMDNSService p_hService)> MDNSDynamicServiceTxtCallbackFunc;
|
||||||
@ -331,14 +343,15 @@ public:
|
|||||||
uint16_t port(const uint32_t p_u32AnswerIndex);
|
uint16_t port(const uint32_t p_u32AnswerIndex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hMDNSServiceQuery (opaque handle to access dynamic service queries)
|
hMDNSServiceQuery (opaque handle to access dynamic service queries)
|
||||||
*/
|
*/
|
||||||
typedef const void* hMDNSServiceQuery;
|
typedef const void* hMDNSServiceQuery;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enuServiceQueryAnswerType
|
enuServiceQueryAnswerType
|
||||||
*/
|
*/
|
||||||
typedef enum _enuServiceQueryAnswerType {
|
typedef enum _enuServiceQueryAnswerType
|
||||||
|
{
|
||||||
ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name
|
ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name
|
||||||
ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port
|
ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port
|
||||||
ServiceQueryAnswerType_Txts = (1 << 2), // TXT items
|
ServiceQueryAnswerType_Txts = (1 << 2), // TXT items
|
||||||
@ -350,7 +363,8 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
} enuServiceQueryAnswerType;
|
} enuServiceQueryAnswerType;
|
||||||
|
|
||||||
enum class AnswerType : uint32_t {
|
enum class AnswerType : uint32_t
|
||||||
|
{
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
ServiceDomain = ServiceQueryAnswerType_ServiceDomain,
|
ServiceDomain = ServiceQueryAnswerType_ServiceDomain,
|
||||||
HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort,
|
HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort,
|
||||||
@ -364,12 +378,12 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MDNSServiceQueryCallbackFn
|
MDNSServiceQueryCallbackFn
|
||||||
* Callback function for received answers for dynamic service queries
|
Callback function for received answers for dynamic service queries
|
||||||
*/
|
*/
|
||||||
struct MDNSServiceInfo; // forward declaration
|
struct MDNSServiceInfo; // forward declaration
|
||||||
typedef std::function<void(const MDNSServiceInfo& mdnsServiceInfo,
|
typedef std::function<void(const MDNSServiceInfo& mdnsServiceInfo,
|
||||||
AnswerType answerType , // flag for the updated answer item
|
AnswerType answerType, // flag for the updated answer item
|
||||||
bool p_bSetContent // true: Answer component set, false: component deleted
|
bool p_bSetContent // true: Answer component set, false: component deleted
|
||||||
)> MDNSServiceQueryCallbackFunc;
|
)> MDNSServiceQueryCallbackFunc;
|
||||||
|
|
||||||
@ -391,7 +405,7 @@ public:
|
|||||||
bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery);
|
bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery);
|
||||||
|
|
||||||
uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery);
|
uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery);
|
||||||
std::vector<MDNSResponder::MDNSServiceInfo> answerInfo (const MDNSResponder::hMDNSServiceQuery p_hServiceQuery);
|
std::vector<MDNSResponder::MDNSServiceInfo> answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery);
|
||||||
|
|
||||||
const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery,
|
const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery,
|
||||||
const uint32_t p_u32AnswerIndex);
|
const uint32_t p_u32AnswerIndex);
|
||||||
@ -428,8 +442,8 @@ public:
|
|||||||
const uint32_t p_u32AnswerIndex);
|
const uint32_t p_u32AnswerIndex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MDNSProbeResultCallbackFn
|
MDNSProbeResultCallbackFn
|
||||||
* Callback function for (host and service domain) probe results
|
Callback function for (host and service domain) probe results
|
||||||
*/
|
*/
|
||||||
typedef std::function<void(const char* p_pcDomainName,
|
typedef std::function<void(const char* p_pcDomainName,
|
||||||
bool p_bProbeResult)> MDNSHostProbeFn;
|
bool p_bProbeResult)> MDNSHostProbeFn;
|
||||||
@ -484,11 +498,11 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* MDNSServiceInfo, used in application callbacks
|
MDNSServiceInfo, used in application callbacks
|
||||||
*/
|
*/
|
||||||
struct MDNSServiceInfo
|
struct MDNSServiceInfo
|
||||||
{
|
{
|
||||||
MDNSServiceInfo(MDNSResponder& p_pM,MDNSResponder::hMDNSServiceQuery p_hS,uint32_t p_u32A)
|
MDNSServiceInfo(MDNSResponder& p_pM, MDNSResponder::hMDNSServiceQuery p_hS, uint32_t p_u32A)
|
||||||
: p_pMDNSResponder(p_pM),
|
: p_pMDNSResponder(p_pM),
|
||||||
p_hServiceQuery(p_hS),
|
p_hServiceQuery(p_hS),
|
||||||
p_u32AnswerIndex(p_u32A)
|
p_u32AnswerIndex(p_u32A)
|
||||||
@ -507,14 +521,16 @@ public:
|
|||||||
uint32_t p_u32AnswerIndex;
|
uint32_t p_u32AnswerIndex;
|
||||||
KeyValueMap keyValueMap;
|
KeyValueMap keyValueMap;
|
||||||
public:
|
public:
|
||||||
const char* serviceDomain(){
|
const char* serviceDomain()
|
||||||
|
{
|
||||||
return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex);
|
return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex);
|
||||||
};
|
};
|
||||||
bool hostDomainAvailable()
|
bool hostDomainAvailable()
|
||||||
{
|
{
|
||||||
return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex));
|
return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex));
|
||||||
}
|
}
|
||||||
const char* hostDomain(){
|
const char* hostDomain()
|
||||||
|
{
|
||||||
return (hostDomainAvailable()) ?
|
return (hostDomainAvailable()) ?
|
||||||
p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) : nullptr;
|
p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) : nullptr;
|
||||||
};
|
};
|
||||||
@ -522,19 +538,23 @@ public:
|
|||||||
{
|
{
|
||||||
return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex));
|
return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex));
|
||||||
}
|
}
|
||||||
uint16_t hostPort(){
|
uint16_t hostPort()
|
||||||
|
{
|
||||||
return (hostPortAvailable()) ?
|
return (hostPortAvailable()) ?
|
||||||
p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) : 0;
|
p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) : 0;
|
||||||
};
|
};
|
||||||
bool IP4AddressAvailable()
|
bool IP4AddressAvailable()
|
||||||
{
|
{
|
||||||
return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery,p_u32AnswerIndex ));
|
return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery, p_u32AnswerIndex));
|
||||||
}
|
}
|
||||||
std::vector<IPAddress> IP4Adresses(){
|
std::vector<IPAddress> IP4Adresses()
|
||||||
|
{
|
||||||
std::vector<IPAddress> internalIP;
|
std::vector<IPAddress> internalIP;
|
||||||
if (IP4AddressAvailable()) {
|
if (IP4AddressAvailable())
|
||||||
|
{
|
||||||
uint16_t cntIP4Adress = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex);
|
uint16_t cntIP4Adress = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex);
|
||||||
for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2) {
|
for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2)
|
||||||
|
{
|
||||||
internalIP.emplace_back(p_pMDNSResponder.answerIP4Address(p_hServiceQuery, p_u32AnswerIndex, u2));
|
internalIP.emplace_back(p_pMDNSResponder.answerIP4Address(p_hServiceQuery, p_u32AnswerIndex, u2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -544,7 +564,8 @@ public:
|
|||||||
{
|
{
|
||||||
return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex));
|
return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex));
|
||||||
}
|
}
|
||||||
const char* strKeyValue (){
|
const char* strKeyValue()
|
||||||
|
{
|
||||||
return (txtAvailable()) ?
|
return (txtAvailable()) ?
|
||||||
p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) : nullptr;
|
p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) : nullptr;
|
||||||
};
|
};
|
||||||
@ -552,8 +573,9 @@ public:
|
|||||||
{
|
{
|
||||||
if (txtAvailable() && keyValueMap.size() == 0)
|
if (txtAvailable() && keyValueMap.size() == 0)
|
||||||
{
|
{
|
||||||
for (auto kv = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex);kv != nullptr;kv = kv->m_pNext) {
|
for (auto kv = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); kv != nullptr; kv = kv->m_pNext)
|
||||||
keyValueMap.emplace(std::pair<const char*,const char*>(kv->m_pcKey,kv->m_pcValue));
|
{
|
||||||
|
keyValueMap.emplace(std::pair<const char*, const char*>(kv->m_pcKey, kv->m_pcValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return keyValueMap;
|
return keyValueMap;
|
||||||
@ -562,9 +584,11 @@ public:
|
|||||||
{
|
{
|
||||||
char* result = nullptr;
|
char* result = nullptr;
|
||||||
|
|
||||||
for (stcMDNSServiceTxt* pTxt=p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); pTxt; pTxt=pTxt->m_pNext) {
|
for (stcMDNSServiceTxt* pTxt = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); pTxt; pTxt = pTxt->m_pNext)
|
||||||
|
{
|
||||||
if ((key) &&
|
if ((key) &&
|
||||||
(0 == strcmp(pTxt->m_pcKey, key))) {
|
(0 == strcmp(pTxt->m_pcKey, key)))
|
||||||
|
{
|
||||||
result = pTxt->m_pcValue;
|
result = pTxt->m_pcValue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -575,9 +599,10 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNSServiceTxt
|
stcMDNSServiceTxt
|
||||||
*/
|
*/
|
||||||
struct stcMDNSServiceTxt {
|
struct stcMDNSServiceTxt
|
||||||
|
{
|
||||||
stcMDNSServiceTxt* m_pNext;
|
stcMDNSServiceTxt* m_pNext;
|
||||||
char* m_pcKey;
|
char* m_pcKey;
|
||||||
char* m_pcValue;
|
char* m_pcValue;
|
||||||
@ -614,9 +639,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNSTxts
|
stcMDNSTxts
|
||||||
*/
|
*/
|
||||||
struct stcMDNSServiceTxts {
|
struct stcMDNSServiceTxts
|
||||||
|
{
|
||||||
stcMDNSServiceTxt* m_pTxts;
|
stcMDNSServiceTxt* m_pTxts;
|
||||||
|
|
||||||
stcMDNSServiceTxts(void);
|
stcMDNSServiceTxts(void);
|
||||||
@ -650,9 +676,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enuContentFlags
|
enuContentFlags
|
||||||
*/
|
*/
|
||||||
typedef enum _enuContentFlags {
|
typedef enum _enuContentFlags
|
||||||
|
{
|
||||||
// Host
|
// Host
|
||||||
ContentFlag_A = 0x01,
|
ContentFlag_A = 0x01,
|
||||||
ContentFlag_PTR_IP4 = 0x02,
|
ContentFlag_PTR_IP4 = 0x02,
|
||||||
@ -666,9 +693,10 @@ protected:
|
|||||||
} enuContentFlags;
|
} enuContentFlags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNS_MsgHeader
|
stcMDNS_MsgHeader
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_MsgHeader {
|
struct stcMDNS_MsgHeader
|
||||||
|
{
|
||||||
uint16_t m_u16ID; // Identifier
|
uint16_t m_u16ID; // Identifier
|
||||||
bool m_1bQR : 1; // Query/Response flag
|
bool m_1bQR : 1; // Query/Response flag
|
||||||
unsigned char m_4bOpcode : 4; // Operation code
|
unsigned char m_4bOpcode : 4; // Operation code
|
||||||
@ -698,9 +726,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRDomain
|
stcMDNS_RRDomain
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRDomain {
|
struct stcMDNS_RRDomain
|
||||||
|
{
|
||||||
char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name
|
char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name
|
||||||
uint16_t m_u16NameLength; // Length (incl. '\0')
|
uint16_t m_u16NameLength; // Length (incl. '\0')
|
||||||
|
|
||||||
@ -724,9 +753,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRAttributes
|
stcMDNS_RRAttributes
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRAttributes {
|
struct stcMDNS_RRAttributes
|
||||||
|
{
|
||||||
uint16_t m_u16Type; // Type
|
uint16_t m_u16Type; // Type
|
||||||
uint16_t m_u16Class; // Class, nearly always 'IN'
|
uint16_t m_u16Class; // Class, nearly always 'IN'
|
||||||
|
|
||||||
@ -738,9 +768,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRHeader
|
stcMDNS_RRHeader
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRHeader {
|
struct stcMDNS_RRHeader
|
||||||
|
{
|
||||||
stcMDNS_RRDomain m_Domain;
|
stcMDNS_RRDomain m_Domain;
|
||||||
stcMDNS_RRAttributes m_Attributes;
|
stcMDNS_RRAttributes m_Attributes;
|
||||||
|
|
||||||
@ -753,9 +784,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRQuestion
|
stcMDNS_RRQuestion
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRQuestion {
|
struct stcMDNS_RRQuestion
|
||||||
|
{
|
||||||
stcMDNS_RRQuestion* m_pNext;
|
stcMDNS_RRQuestion* m_pNext;
|
||||||
stcMDNS_RRHeader m_Header;
|
stcMDNS_RRHeader m_Header;
|
||||||
bool m_bUnicast; // Unicast reply requested
|
bool m_bUnicast; // Unicast reply requested
|
||||||
@ -764,9 +796,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enuAnswerType
|
enuAnswerType
|
||||||
*/
|
*/
|
||||||
typedef enum _enuAnswerType {
|
typedef enum _enuAnswerType
|
||||||
|
{
|
||||||
AnswerType_A,
|
AnswerType_A,
|
||||||
AnswerType_PTR,
|
AnswerType_PTR,
|
||||||
AnswerType_TXT,
|
AnswerType_TXT,
|
||||||
@ -776,9 +809,10 @@ protected:
|
|||||||
} enuAnswerType;
|
} enuAnswerType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRAnswer
|
stcMDNS_RRAnswer
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRAnswer {
|
struct stcMDNS_RRAnswer
|
||||||
|
{
|
||||||
stcMDNS_RRAnswer* m_pNext;
|
stcMDNS_RRAnswer* m_pNext;
|
||||||
const enuAnswerType m_AnswerType;
|
const enuAnswerType m_AnswerType;
|
||||||
stcMDNS_RRHeader m_Header;
|
stcMDNS_RRHeader m_Header;
|
||||||
@ -799,9 +833,10 @@ protected:
|
|||||||
|
|
||||||
#ifdef MDNS_IP4_SUPPORT
|
#ifdef MDNS_IP4_SUPPORT
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRAnswerA
|
stcMDNS_RRAnswerA
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRAnswerA : public stcMDNS_RRAnswer {
|
struct stcMDNS_RRAnswerA : public stcMDNS_RRAnswer
|
||||||
|
{
|
||||||
IPAddress m_IPAddress;
|
IPAddress m_IPAddress;
|
||||||
|
|
||||||
stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header,
|
stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header,
|
||||||
@ -813,9 +848,10 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRAnswerPTR
|
stcMDNS_RRAnswerPTR
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRAnswerPTR : public stcMDNS_RRAnswer {
|
struct stcMDNS_RRAnswerPTR : public stcMDNS_RRAnswer
|
||||||
|
{
|
||||||
stcMDNS_RRDomain m_PTRDomain;
|
stcMDNS_RRDomain m_PTRDomain;
|
||||||
|
|
||||||
stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header,
|
stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header,
|
||||||
@ -826,9 +862,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRAnswerTXT
|
stcMDNS_RRAnswerTXT
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRAnswerTXT : public stcMDNS_RRAnswer {
|
struct stcMDNS_RRAnswerTXT : public stcMDNS_RRAnswer
|
||||||
|
{
|
||||||
stcMDNSServiceTxts m_Txts;
|
stcMDNSServiceTxts m_Txts;
|
||||||
|
|
||||||
stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header,
|
stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header,
|
||||||
@ -840,9 +877,10 @@ protected:
|
|||||||
|
|
||||||
#ifdef MDNS_IP6_SUPPORT
|
#ifdef MDNS_IP6_SUPPORT
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRAnswerAAAA
|
stcMDNS_RRAnswerAAAA
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRAnswerAAAA : public stcMDNS_RRAnswer {
|
struct stcMDNS_RRAnswerAAAA : public stcMDNS_RRAnswer
|
||||||
|
{
|
||||||
//TODO: IP6Address m_IPAddress;
|
//TODO: IP6Address m_IPAddress;
|
||||||
|
|
||||||
stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header,
|
stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header,
|
||||||
@ -854,9 +892,10 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRAnswerSRV
|
stcMDNS_RRAnswerSRV
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRAnswerSRV : public stcMDNS_RRAnswer {
|
struct stcMDNS_RRAnswerSRV : public stcMDNS_RRAnswer
|
||||||
|
{
|
||||||
uint16_t m_u16Priority;
|
uint16_t m_u16Priority;
|
||||||
uint16_t m_u16Weight;
|
uint16_t m_u16Weight;
|
||||||
uint16_t m_u16Port;
|
uint16_t m_u16Port;
|
||||||
@ -870,9 +909,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNS_RRAnswerGeneric
|
stcMDNS_RRAnswerGeneric
|
||||||
*/
|
*/
|
||||||
struct stcMDNS_RRAnswerGeneric : public stcMDNS_RRAnswer {
|
struct stcMDNS_RRAnswerGeneric : public stcMDNS_RRAnswer
|
||||||
|
{
|
||||||
uint16_t m_u16RDLength; // Length of variable answer
|
uint16_t m_u16RDLength; // Length of variable answer
|
||||||
uint8_t* m_pu8RDData; // Offset of start of variable answer in packet
|
uint8_t* m_pu8RDData; // Offset of start of variable answer in packet
|
||||||
|
|
||||||
@ -885,9 +925,10 @@ protected:
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enuProbingStatus
|
enuProbingStatus
|
||||||
*/
|
*/
|
||||||
typedef enum _enuProbingStatus {
|
typedef enum _enuProbingStatus
|
||||||
|
{
|
||||||
ProbingStatus_WaitingForData,
|
ProbingStatus_WaitingForData,
|
||||||
ProbingStatus_ReadyToStart,
|
ProbingStatus_ReadyToStart,
|
||||||
ProbingStatus_InProgress,
|
ProbingStatus_InProgress,
|
||||||
@ -895,9 +936,10 @@ protected:
|
|||||||
} enuProbingStatus;
|
} enuProbingStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcProbeInformation
|
stcProbeInformation
|
||||||
*/
|
*/
|
||||||
struct stcProbeInformation {
|
struct stcProbeInformation
|
||||||
|
{
|
||||||
enuProbingStatus m_ProbingStatus;
|
enuProbingStatus m_ProbingStatus;
|
||||||
uint8_t m_u8SentCount; // Used for probes and announcements
|
uint8_t m_u8SentCount; // Used for probes and announcements
|
||||||
esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements
|
esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements
|
||||||
@ -914,9 +956,10 @@ protected:
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNSService
|
stcMDNSService
|
||||||
*/
|
*/
|
||||||
struct stcMDNSService {
|
struct stcMDNSService
|
||||||
|
{
|
||||||
stcMDNSService* m_pNext;
|
stcMDNSService* m_pNext;
|
||||||
char* m_pcName;
|
char* m_pcName;
|
||||||
bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied)
|
bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied)
|
||||||
@ -944,23 +987,26 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNSServiceQuery
|
stcMDNSServiceQuery
|
||||||
*/
|
*/
|
||||||
struct stcMDNSServiceQuery {
|
struct stcMDNSServiceQuery
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* stcAnswer
|
stcAnswer
|
||||||
*/
|
*/
|
||||||
struct stcAnswer {
|
struct stcAnswer
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* stcTTL
|
stcTTL
|
||||||
*/
|
*/
|
||||||
struct stcTTL {
|
struct stcTTL
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* timeoutLevel_t
|
timeoutLevel_t
|
||||||
*/
|
*/
|
||||||
typedef uint8_t timeoutLevel_t;
|
typedef uint8_t timeoutLevel_t;
|
||||||
/**
|
/**
|
||||||
* TIMEOUTLEVELs
|
TIMEOUTLEVELs
|
||||||
*/
|
*/
|
||||||
const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0;
|
const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0;
|
||||||
const timeoutLevel_t TIMEOUTLEVEL_BASE = 80;
|
const timeoutLevel_t TIMEOUTLEVEL_BASE = 80;
|
||||||
@ -984,9 +1030,10 @@ protected:
|
|||||||
};
|
};
|
||||||
#ifdef MDNS_IP4_SUPPORT
|
#ifdef MDNS_IP4_SUPPORT
|
||||||
/**
|
/**
|
||||||
* stcIP4Address
|
stcIP4Address
|
||||||
*/
|
*/
|
||||||
struct stcIP4Address {
|
struct stcIP4Address
|
||||||
|
{
|
||||||
stcIP4Address* m_pNext;
|
stcIP4Address* m_pNext;
|
||||||
IPAddress m_IPAddress;
|
IPAddress m_IPAddress;
|
||||||
stcTTL m_TTL;
|
stcTTL m_TTL;
|
||||||
@ -997,9 +1044,10 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
#ifdef MDNS_IP6_SUPPORT
|
#ifdef MDNS_IP6_SUPPORT
|
||||||
/**
|
/**
|
||||||
* stcIP6Address
|
stcIP6Address
|
||||||
*/
|
*/
|
||||||
struct stcIP6Address {
|
struct stcIP6Address
|
||||||
|
{
|
||||||
stcIP6Address* m_pNext;
|
stcIP6Address* m_pNext;
|
||||||
IP6Address m_IPAddress;
|
IP6Address m_IPAddress;
|
||||||
stcTTL m_TTL;
|
stcTTL m_TTL;
|
||||||
@ -1093,14 +1141,16 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stcMDNSSendParameter
|
stcMDNSSendParameter
|
||||||
*/
|
*/
|
||||||
struct stcMDNSSendParameter {
|
struct stcMDNSSendParameter
|
||||||
|
{
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* stcDomainCacheItem
|
stcDomainCacheItem
|
||||||
*/
|
*/
|
||||||
struct stcDomainCacheItem {
|
struct stcDomainCacheItem
|
||||||
|
{
|
||||||
stcDomainCacheItem* m_pNext;
|
stcDomainCacheItem* m_pNext;
|
||||||
const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer)
|
const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer)
|
||||||
bool m_bAdditionalData; // Opaque flag for special info (service domain included)
|
bool m_bAdditionalData; // Opaque flag for special info (service domain included)
|
||||||
@ -1203,7 +1253,10 @@ protected:
|
|||||||
uint16_t p_u16QueryType,
|
uint16_t p_u16QueryType,
|
||||||
stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0);
|
stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0);
|
||||||
|
|
||||||
const IPAddress _getResponseMulticastInterface() const { return IPAddress(m_netif->ip_addr); }
|
const IPAddress _getResponseMulticastInterface() const
|
||||||
|
{
|
||||||
|
return IPAddress(m_netif->ip_addr);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader,
|
uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader,
|
||||||
bool* p_pbFullNameMatch = 0) const;
|
bool* p_pbFullNameMatch = 0) const;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,26 @@
|
|||||||
/*
|
/*
|
||||||
* LEAmDNS_Helpers.cpp
|
LEAmDNS_Helpers.cpp
|
||||||
*
|
|
||||||
* License (MIT license):
|
License (MIT license):
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
* in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
* furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in
|
||||||
* all copies or substantial portions of the Software.
|
all copies or substantial portions of the Software.
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "lwip/igmp.h"
|
#include "lwip/igmp.h"
|
||||||
|
|
||||||
@ -28,16 +28,18 @@
|
|||||||
#include "LEAmDNS_Priv.h"
|
#include "LEAmDNS_Priv.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* strrstr (static)
|
strrstr (static)
|
||||||
*
|
|
||||||
* Backwards search for p_pcPattern in p_pcString
|
Backwards search for p_pcPattern in p_pcString
|
||||||
* Based on: https://stackoverflow.com/a/1634398/2778898
|
Based on: https://stackoverflow.com/a/1634398/2778898
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pcPattern) {
|
const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pcPattern)
|
||||||
|
{
|
||||||
|
|
||||||
const char* pcResult = 0;
|
const char* pcResult = 0;
|
||||||
|
|
||||||
@ -46,11 +48,14 @@ const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pc
|
|||||||
|
|
||||||
if ((stStringLength) &&
|
if ((stStringLength) &&
|
||||||
(stPatternLength) &&
|
(stPatternLength) &&
|
||||||
(stPatternLength <= stStringLength)) {
|
(stPatternLength <= stStringLength))
|
||||||
|
{
|
||||||
// Pattern is shorter or has the same length tham the string
|
// Pattern is shorter or has the same length tham the string
|
||||||
|
|
||||||
for (const char* s=(p_pcString + stStringLength - stPatternLength); s>=p_pcString; --s) {
|
for (const char* s = (p_pcString + stStringLength - stPatternLength); s >= p_pcString; --s)
|
||||||
if (0 == strncmp(s, p_pcPattern, stPatternLength)) {
|
{
|
||||||
|
if (0 == strncmp(s, p_pcPattern, stPatternLength))
|
||||||
|
{
|
||||||
pcResult = s;
|
pcResult = s;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -66,52 +71,59 @@ const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pc
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace esp8266 {
|
namespace esp8266
|
||||||
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* LEAmDNS
|
LEAmDNS
|
||||||
*/
|
*/
|
||||||
namespace MDNSImplementation {
|
namespace MDNSImplementation
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HELPERS
|
HELPERS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::indexDomain (static)
|
MDNSResponder::indexDomain (static)
|
||||||
*
|
|
||||||
* Updates the given domain 'p_rpcHostname' by appending a delimiter and an index number.
|
Updates the given domain 'p_rpcHostname' by appending a delimiter and an index number.
|
||||||
*
|
|
||||||
* If the given domain already hasa numeric index (after the given delimiter), this index
|
If the given domain already hasa numeric index (after the given delimiter), this index
|
||||||
* incremented. If not, the delimiter and index '2' is added.
|
incremented. If not, the delimiter and index '2' is added.
|
||||||
*
|
|
||||||
* If 'p_rpcHostname' is empty (==0), the given default name 'p_pcDefaultHostname' is used,
|
If 'p_rpcHostname' is empty (==0), the given default name 'p_pcDefaultHostname' is used,
|
||||||
* if no default is given, 'esp8266' is used.
|
if no default is given, 'esp8266' is used.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
/*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain,
|
/*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain,
|
||||||
const char* p_pcDivider /*= "-"*/,
|
const char* p_pcDivider /*= "-"*/,
|
||||||
const char* p_pcDefaultDomain /*= 0*/) {
|
const char* p_pcDefaultDomain /*= 0*/)
|
||||||
|
{
|
||||||
|
|
||||||
bool bResult = false;
|
bool bResult = false;
|
||||||
|
|
||||||
// Ensure a divider exists; use '-' as default
|
// Ensure a divider exists; use '-' as default
|
||||||
const char* pcDivider = (p_pcDivider ?: "-");
|
const char* pcDivider = (p_pcDivider ? : "-");
|
||||||
|
|
||||||
if (p_rpcDomain) {
|
if (p_rpcDomain)
|
||||||
|
{
|
||||||
const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider);
|
const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider);
|
||||||
if (pFoundDivider) { // maybe already extended
|
if (pFoundDivider) // maybe already extended
|
||||||
|
{
|
||||||
char* pEnd = 0;
|
char* pEnd = 0;
|
||||||
unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10);
|
unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10);
|
||||||
if ((ulIndex) &&
|
if ((ulIndex) &&
|
||||||
((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain)) &&
|
((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain)) &&
|
||||||
(!*pEnd)) { // Valid (old) index found
|
(!*pEnd)) // Valid (old) index found
|
||||||
|
{
|
||||||
|
|
||||||
char acIndexBuffer[16];
|
char acIndexBuffer[16];
|
||||||
sprintf(acIndexBuffer, "%lu", (++ulIndex));
|
sprintf(acIndexBuffer, "%lu", (++ulIndex));
|
||||||
size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider)) + strlen(acIndexBuffer) + 1);
|
size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider)) + strlen(acIndexBuffer) + 1);
|
||||||
char* pNewHostname = new char[stLength];
|
char* pNewHostname = new char[stLength];
|
||||||
if (pNewHostname) {
|
if (pNewHostname)
|
||||||
|
{
|
||||||
memcpy(pNewHostname, p_rpcDomain, (pFoundDivider - p_rpcDomain + strlen(pcDivider)));
|
memcpy(pNewHostname, p_rpcDomain, (pFoundDivider - p_rpcDomain + strlen(pcDivider)));
|
||||||
pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0;
|
pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0;
|
||||||
strcat(pNewHostname, acIndexBuffer);
|
strcat(pNewHostname, acIndexBuffer);
|
||||||
@ -121,19 +133,23 @@ namespace MDNSImplementation {
|
|||||||
|
|
||||||
bResult = true;
|
bResult = true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
|
DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
pFoundDivider = 0; // Flag the need to (base) extend the hostname
|
pFoundDivider = 0; // Flag the need to (base) extend the hostname
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pFoundDivider) { // not yet extended (or failed to increment extension) -> start indexing
|
if (!pFoundDivider) // not yet extended (or failed to increment extension) -> start indexing
|
||||||
|
{
|
||||||
size_t stLength = strlen(p_rpcDomain) + (strlen(pcDivider) + 1 + 1); // Name + Divider + '2' + '\0'
|
size_t stLength = strlen(p_rpcDomain) + (strlen(pcDivider) + 1 + 1); // Name + Divider + '2' + '\0'
|
||||||
char* pNewHostname = new char[stLength];
|
char* pNewHostname = new char[stLength];
|
||||||
if (pNewHostname) {
|
if (pNewHostname)
|
||||||
|
{
|
||||||
sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider);
|
sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider);
|
||||||
|
|
||||||
delete[] p_rpcDomain;
|
delete[] p_rpcDomain;
|
||||||
@ -141,22 +157,26 @@ namespace MDNSImplementation {
|
|||||||
|
|
||||||
bResult = true;
|
bResult = true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
|
DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// No given host domain, use base or default
|
// No given host domain, use base or default
|
||||||
const char* cpcDefaultName = (p_pcDefaultDomain ?: "esp8266");
|
const char* cpcDefaultName = (p_pcDefaultDomain ? : "esp8266");
|
||||||
|
|
||||||
size_t stLength = strlen(cpcDefaultName) + 1; // '\0'
|
size_t stLength = strlen(cpcDefaultName) + 1; // '\0'
|
||||||
p_rpcDomain = new char[stLength];
|
p_rpcDomain = new char[stLength];
|
||||||
if (p_rpcDomain) {
|
if (p_rpcDomain)
|
||||||
|
{
|
||||||
strncpy(p_rpcDomain, cpcDefaultName, stLength);
|
strncpy(p_rpcDomain, cpcDefaultName, stLength);
|
||||||
bResult = true;
|
bResult = true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
|
DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!")););
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,26 +186,28 @@ namespace MDNSImplementation {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UDP CONTEXT
|
UDP CONTEXT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool MDNSResponder::_callProcess(void) {
|
bool MDNSResponder::_callProcess(void)
|
||||||
|
{
|
||||||
DEBUG_EX_INFO(DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str()););
|
DEBUG_EX_INFO(DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str()););
|
||||||
|
|
||||||
return _process(false);
|
return _process(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_allocUDPContext
|
MDNSResponder::_allocUDPContext
|
||||||
*
|
|
||||||
* (Re-)Creates the one-and-only UDP context for the MDNS responder.
|
(Re-)Creates the one-and-only UDP context for the MDNS responder.
|
||||||
* The context is added to the 'multicast'-group and listens to the MDNS port (5353).
|
The context is added to the 'multicast'-group and listens to the MDNS port (5353).
|
||||||
* The travel-distance for multicast messages is set to 1 (local, via MDNS_MULTICAST_TTL).
|
The travel-distance for multicast messages is set to 1 (local, via MDNS_MULTICAST_TTL).
|
||||||
* Messages are received via the MDNSResponder '_update' function. CAUTION: This function
|
Messages are received via the MDNSResponder '_update' function. CAUTION: This function
|
||||||
* is called from the WiFi stack side of the ESP stack system.
|
is called from the WiFi stack side of the ESP stack system.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_allocUDPContext(void) {
|
bool MDNSResponder::_allocUDPContext(void)
|
||||||
|
{
|
||||||
DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext"););
|
DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext"););
|
||||||
|
|
||||||
bool bResult = false;
|
bool bResult = false;
|
||||||
@ -199,11 +221,13 @@ bool MDNSResponder::_allocUDPContext(void) {
|
|||||||
//TODO: set multicast address (lwip_joingroup() is IPv4 only at the time of writing)
|
//TODO: set multicast address (lwip_joingroup() is IPv4 only at the time of writing)
|
||||||
multicast_addr.addr = DNS_MQUERY_IPV6_GROUP_INIT;
|
multicast_addr.addr = DNS_MQUERY_IPV6_GROUP_INIT;
|
||||||
#endif
|
#endif
|
||||||
if (ERR_OK == igmp_joingroup(ip_2_ip4(&m_netif->ip_addr), ip_2_ip4(&multicast_addr))) {
|
if (ERR_OK == igmp_joingroup(ip_2_ip4(&m_netif->ip_addr), ip_2_ip4(&multicast_addr)))
|
||||||
|
{
|
||||||
m_pUDPContext = new UdpContext;
|
m_pUDPContext = new UdpContext;
|
||||||
m_pUDPContext->ref();
|
m_pUDPContext->ref();
|
||||||
|
|
||||||
if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT)) {
|
if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT))
|
||||||
|
{
|
||||||
m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL);
|
m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL);
|
||||||
m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this));
|
m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this));
|
||||||
|
|
||||||
@ -214,11 +238,13 @@ bool MDNSResponder::_allocUDPContext(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_releaseUDPContext
|
MDNSResponder::_releaseUDPContext
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_releaseUDPContext(void) {
|
bool MDNSResponder::_releaseUDPContext(void)
|
||||||
|
{
|
||||||
|
|
||||||
if (m_pUDPContext) {
|
if (m_pUDPContext)
|
||||||
|
{
|
||||||
m_pUDPContext->unref();
|
m_pUDPContext->unref();
|
||||||
m_pUDPContext = 0;
|
m_pUDPContext = 0;
|
||||||
}
|
}
|
||||||
@ -227,16 +253,18 @@ bool MDNSResponder::_releaseUDPContext(void) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SERVICE QUERY
|
SERVICE QUERY
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_allocServiceQuery
|
MDNSResponder::_allocServiceQuery
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void) {
|
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void)
|
||||||
|
{
|
||||||
|
|
||||||
stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery;
|
stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery;
|
||||||
if (pServiceQuery) {
|
if (pServiceQuery)
|
||||||
|
{
|
||||||
// Link to query list
|
// Link to query list
|
||||||
pServiceQuery->m_pNext = m_pServiceQueries;
|
pServiceQuery->m_pNext = m_pServiceQueries;
|
||||||
m_pServiceQueries = pServiceQuery;
|
m_pServiceQueries = pServiceQuery;
|
||||||
@ -245,30 +273,37 @@ MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_removeServiceQuery
|
MDNSResponder::_removeServiceQuery
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery) {
|
bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery)
|
||||||
|
{
|
||||||
|
|
||||||
bool bResult = false;
|
bool bResult = false;
|
||||||
|
|
||||||
if (p_pServiceQuery) {
|
if (p_pServiceQuery)
|
||||||
|
{
|
||||||
stcMDNSServiceQuery* pPred = m_pServiceQueries;
|
stcMDNSServiceQuery* pPred = m_pServiceQueries;
|
||||||
while ((pPred) &&
|
while ((pPred) &&
|
||||||
(pPred->m_pNext != p_pServiceQuery)) {
|
(pPred->m_pNext != p_pServiceQuery))
|
||||||
|
{
|
||||||
pPred = pPred->m_pNext;
|
pPred = pPred->m_pNext;
|
||||||
}
|
}
|
||||||
if (pPred) {
|
if (pPred)
|
||||||
|
{
|
||||||
pPred->m_pNext = p_pServiceQuery->m_pNext;
|
pPred->m_pNext = p_pServiceQuery->m_pNext;
|
||||||
delete p_pServiceQuery;
|
delete p_pServiceQuery;
|
||||||
bResult = true;
|
bResult = true;
|
||||||
}
|
}
|
||||||
else { // No predecesor
|
else // No predecesor
|
||||||
if (m_pServiceQueries == p_pServiceQuery) {
|
{
|
||||||
|
if (m_pServiceQueries == p_pServiceQuery)
|
||||||
|
{
|
||||||
m_pServiceQueries = p_pServiceQuery->m_pNext;
|
m_pServiceQueries = p_pServiceQuery->m_pNext;
|
||||||
delete p_pServiceQuery;
|
delete p_pServiceQuery;
|
||||||
bResult = true;
|
bResult = true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseServiceQuery: INVALID service query!"););
|
DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseServiceQuery: INVALID service query!"););
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,25 +312,29 @@ bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pS
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_removeLegacyServiceQuery
|
MDNSResponder::_removeLegacyServiceQuery
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_removeLegacyServiceQuery(void) {
|
bool MDNSResponder::_removeLegacyServiceQuery(void)
|
||||||
|
{
|
||||||
|
|
||||||
stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery();
|
stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery();
|
||||||
return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true);
|
return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_findServiceQuery
|
MDNSResponder::_findServiceQuery
|
||||||
*
|
|
||||||
* 'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existance)
|
'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existance)
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) {
|
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery)
|
||||||
|
{
|
||||||
|
|
||||||
stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries;
|
stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries;
|
||||||
while (pServiceQuery) {
|
while (pServiceQuery)
|
||||||
if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery) {
|
{
|
||||||
|
if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pServiceQuery = pServiceQuery->m_pNext;
|
pServiceQuery = pServiceQuery->m_pNext;
|
||||||
@ -304,13 +343,16 @@ MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSRespond
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_findLegacyServiceQuery
|
MDNSResponder::_findLegacyServiceQuery
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void) {
|
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void)
|
||||||
|
{
|
||||||
|
|
||||||
stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries;
|
stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries;
|
||||||
while (pServiceQuery) {
|
while (pServiceQuery)
|
||||||
if (pServiceQuery->m_bLegacyQuery) {
|
{
|
||||||
|
if (pServiceQuery->m_bLegacyQuery)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pServiceQuery = pServiceQuery->m_pNext;
|
pServiceQuery = pServiceQuery->m_pNext;
|
||||||
@ -319,10 +361,12 @@ MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_releaseServiceQueries
|
MDNSResponder::_releaseServiceQueries
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_releaseServiceQueries(void) {
|
bool MDNSResponder::_releaseServiceQueries(void)
|
||||||
while (m_pServiceQueries) {
|
{
|
||||||
|
while (m_pServiceQueries)
|
||||||
|
{
|
||||||
stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext;
|
stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext;
|
||||||
delete m_pServiceQueries;
|
delete m_pServiceQueries;
|
||||||
m_pServiceQueries = pNext;
|
m_pServiceQueries = pNext;
|
||||||
@ -331,15 +375,18 @@ bool MDNSResponder::_releaseServiceQueries(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_findNextServiceQueryByServiceType
|
MDNSResponder::_findNextServiceQueryByServiceType
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceTypeDomain,
|
MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceTypeDomain,
|
||||||
const stcMDNSServiceQuery* p_pPrevServiceQuery) {
|
const stcMDNSServiceQuery* p_pPrevServiceQuery)
|
||||||
|
{
|
||||||
stcMDNSServiceQuery* pMatchingServiceQuery = 0;
|
stcMDNSServiceQuery* pMatchingServiceQuery = 0;
|
||||||
|
|
||||||
stcMDNSServiceQuery* pServiceQuery = (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries);
|
stcMDNSServiceQuery* pServiceQuery = (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries);
|
||||||
while (pServiceQuery) {
|
while (pServiceQuery)
|
||||||
if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain) {
|
{
|
||||||
|
if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain)
|
||||||
|
{
|
||||||
pMatchingServiceQuery = pServiceQuery;
|
pMatchingServiceQuery = pServiceQuery;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -350,13 +397,14 @@ MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServic
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HOSTNAME
|
HOSTNAME
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_setHostname
|
MDNSResponder::_setHostname
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_setHostname(const char* p_pcHostname) {
|
bool MDNSResponder::_setHostname(const char* p_pcHostname)
|
||||||
|
{
|
||||||
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"), p_pcHostname););
|
//DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"), p_pcHostname););
|
||||||
|
|
||||||
bool bResult = false;
|
bool bResult = false;
|
||||||
@ -365,12 +413,15 @@ bool MDNSResponder::_setHostname(const char* p_pcHostname) {
|
|||||||
|
|
||||||
size_t stLength = 0;
|
size_t stLength = 0;
|
||||||
if ((p_pcHostname) &&
|
if ((p_pcHostname) &&
|
||||||
(MDNS_DOMAIN_LABEL_MAXLENGTH >= (stLength = strlen(p_pcHostname)))) { // char max size for a single label
|
(MDNS_DOMAIN_LABEL_MAXLENGTH >= (stLength = strlen(p_pcHostname)))) // char max size for a single label
|
||||||
|
{
|
||||||
// Copy in hostname characters as lowercase
|
// Copy in hostname characters as lowercase
|
||||||
if ((bResult = (0 != (m_pcHostname = new char[stLength + 1])))) {
|
if ((bResult = (0 != (m_pcHostname = new char[stLength + 1]))))
|
||||||
|
{
|
||||||
#ifdef MDNS_FORCE_LOWERCASE_HOSTNAME
|
#ifdef MDNS_FORCE_LOWERCASE_HOSTNAME
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (; i<stLength; ++i) {
|
for (; i < stLength; ++i)
|
||||||
|
{
|
||||||
m_pcHostname[i] = (isupper(p_pcHostname[i]) ? tolower(p_pcHostname[i]) : p_pcHostname[i]);
|
m_pcHostname[i] = (isupper(p_pcHostname[i]) ? tolower(p_pcHostname[i]) : p_pcHostname[i]);
|
||||||
}
|
}
|
||||||
m_pcHostname[i] = 0;
|
m_pcHostname[i] = 0;
|
||||||
@ -383,11 +434,13 @@ bool MDNSResponder::_setHostname(const char* p_pcHostname) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_releaseHostname
|
MDNSResponder::_releaseHostname
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_releaseHostname(void) {
|
bool MDNSResponder::_releaseHostname(void)
|
||||||
|
{
|
||||||
|
|
||||||
if (m_pcHostname) {
|
if (m_pcHostname)
|
||||||
|
{
|
||||||
delete[] m_pcHostname;
|
delete[] m_pcHostname;
|
||||||
m_pcHostname = 0;
|
m_pcHostname = 0;
|
||||||
}
|
}
|
||||||
@ -396,16 +449,17 @@ bool MDNSResponder::_releaseHostname(void) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SERVICE
|
SERVICE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_allocService
|
MDNSResponder::_allocService
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName,
|
MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName,
|
||||||
const char* p_pcService,
|
const char* p_pcService,
|
||||||
const char* p_pcProtocol,
|
const char* p_pcProtocol,
|
||||||
uint16_t p_u16Port) {
|
uint16_t p_u16Port)
|
||||||
|
{
|
||||||
|
|
||||||
stcMDNSService* pService = 0;
|
stcMDNSService* pService = 0;
|
||||||
if (((!p_pcName) ||
|
if (((!p_pcName) ||
|
||||||
@ -416,9 +470,10 @@ MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName
|
|||||||
(MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) &&
|
(MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) &&
|
||||||
(p_u16Port) &&
|
(p_u16Port) &&
|
||||||
(0 != (pService = new stcMDNSService)) &&
|
(0 != (pService = new stcMDNSService)) &&
|
||||||
(pService->setName(p_pcName ?: m_pcHostname)) &&
|
(pService->setName(p_pcName ? : m_pcHostname)) &&
|
||||||
(pService->setService(p_pcService)) &&
|
(pService->setService(p_pcService)) &&
|
||||||
(pService->setProtocol(p_pcProtocol))) {
|
(pService->setProtocol(p_pcProtocol)))
|
||||||
|
{
|
||||||
|
|
||||||
pService->m_bAutoName = (0 == p_pcName);
|
pService->m_bAutoName = (0 == p_pcName);
|
||||||
pService->m_u16Port = p_u16Port;
|
pService->m_u16Port = p_u16Port;
|
||||||
@ -431,30 +486,37 @@ MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_releaseService
|
MDNSResponder::_releaseService
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService) {
|
bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService)
|
||||||
|
{
|
||||||
|
|
||||||
bool bResult = false;
|
bool bResult = false;
|
||||||
|
|
||||||
if (p_pService) {
|
if (p_pService)
|
||||||
|
{
|
||||||
stcMDNSService* pPred = m_pServices;
|
stcMDNSService* pPred = m_pServices;
|
||||||
while ((pPred) &&
|
while ((pPred) &&
|
||||||
(pPred->m_pNext != p_pService)) {
|
(pPred->m_pNext != p_pService))
|
||||||
|
{
|
||||||
pPred = pPred->m_pNext;
|
pPred = pPred->m_pNext;
|
||||||
}
|
}
|
||||||
if (pPred) {
|
if (pPred)
|
||||||
|
{
|
||||||
pPred->m_pNext = p_pService->m_pNext;
|
pPred->m_pNext = p_pService->m_pNext;
|
||||||
delete p_pService;
|
delete p_pService;
|
||||||
bResult = true;
|
bResult = true;
|
||||||
}
|
}
|
||||||
else { // No predecesor
|
else // No predecesor
|
||||||
if (m_pServices == p_pService) {
|
{
|
||||||
|
if (m_pServices == p_pService)
|
||||||
|
{
|
||||||
m_pServices = p_pService->m_pNext;
|
m_pServices = p_pService->m_pNext;
|
||||||
delete p_pService;
|
delete p_pService;
|
||||||
bResult = true;
|
bResult = true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!"););
|
DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!"););
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -463,12 +525,14 @@ bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_releaseServices
|
MDNSResponder::_releaseServices
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_releaseServices(void) {
|
bool MDNSResponder::_releaseServices(void)
|
||||||
|
{
|
||||||
|
|
||||||
stcMDNSService* pService = m_pServices;
|
stcMDNSService* pService = m_pServices;
|
||||||
while (pService) {
|
while (pService)
|
||||||
|
{
|
||||||
_releaseService(pService);
|
_releaseService(pService);
|
||||||
pService = m_pServices;
|
pService = m_pServices;
|
||||||
}
|
}
|
||||||
@ -476,17 +540,20 @@ bool MDNSResponder::_releaseServices(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_findService
|
MDNSResponder::_findService
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName,
|
MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName,
|
||||||
const char* p_pcService,
|
const char* p_pcService,
|
||||||
const char* p_pcProtocol) {
|
const char* p_pcProtocol)
|
||||||
|
{
|
||||||
|
|
||||||
stcMDNSService* pService = m_pServices;
|
stcMDNSService* pService = m_pServices;
|
||||||
while (pService) {
|
while (pService)
|
||||||
|
{
|
||||||
if ((0 == strcmp(pService->m_pcName, p_pcName)) &&
|
if ((0 == strcmp(pService->m_pcName, p_pcName)) &&
|
||||||
(0 == strcmp(pService->m_pcService, p_pcService)) &&
|
(0 == strcmp(pService->m_pcService, p_pcService)) &&
|
||||||
(0 == strcmp(pService->m_pcProtocol, p_pcProtocol))) {
|
(0 == strcmp(pService->m_pcProtocol, p_pcProtocol)))
|
||||||
|
{
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -496,13 +563,16 @@ MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_findService
|
MDNSResponder::_findService
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService) {
|
MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService)
|
||||||
|
{
|
||||||
|
|
||||||
stcMDNSService* pService = m_pServices;
|
stcMDNSService* pService = m_pServices;
|
||||||
while (pService) {
|
while (pService)
|
||||||
if (p_hService == (hMDNSService)pService) {
|
{
|
||||||
|
if (p_hService == (hMDNSService)pService)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pService = pService->m_pNext;
|
pService = pService->m_pNext;
|
||||||
@ -512,16 +582,17 @@ MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SERVICE TXT
|
SERVICE TXT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_allocServiceTxt
|
MDNSResponder::_allocServiceTxt
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
||||||
const char* p_pcKey,
|
const char* p_pcKey,
|
||||||
const char* p_pcValue,
|
const char* p_pcValue,
|
||||||
bool p_bTemp) {
|
bool p_bTemp)
|
||||||
|
{
|
||||||
|
|
||||||
stcMDNSServiceTxt* pTxt = 0;
|
stcMDNSServiceTxt* pTxt = 0;
|
||||||
|
|
||||||
@ -531,20 +602,25 @@ MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder:
|
|||||||
1 + // Length byte
|
1 + // Length byte
|
||||||
(p_pcKey ? strlen(p_pcKey) : 0) +
|
(p_pcKey ? strlen(p_pcKey) : 0) +
|
||||||
1 + // '='
|
1 + // '='
|
||||||
(p_pcValue ? strlen(p_pcValue) : 0)))) {
|
(p_pcValue ? strlen(p_pcValue) : 0))))
|
||||||
|
{
|
||||||
|
|
||||||
pTxt = new stcMDNSServiceTxt;
|
pTxt = new stcMDNSServiceTxt;
|
||||||
if (pTxt) {
|
if (pTxt)
|
||||||
|
{
|
||||||
size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0);
|
size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0);
|
||||||
pTxt->m_pcKey = new char[stLength + 1];
|
pTxt->m_pcKey = new char[stLength + 1];
|
||||||
if (pTxt->m_pcKey) {
|
if (pTxt->m_pcKey)
|
||||||
|
{
|
||||||
strncpy(pTxt->m_pcKey, p_pcKey, stLength); pTxt->m_pcKey[stLength] = 0;
|
strncpy(pTxt->m_pcKey, p_pcKey, stLength); pTxt->m_pcKey[stLength] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_pcValue) {
|
if (p_pcValue)
|
||||||
|
{
|
||||||
stLength = (p_pcValue ? strlen(p_pcValue) : 0);
|
stLength = (p_pcValue ? strlen(p_pcValue) : 0);
|
||||||
pTxt->m_pcValue = new char[stLength + 1];
|
pTxt->m_pcValue = new char[stLength + 1];
|
||||||
if (pTxt->m_pcValue) {
|
if (pTxt->m_pcValue)
|
||||||
|
{
|
||||||
strncpy(pTxt->m_pcValue, p_pcValue, stLength); pTxt->m_pcValue[stLength] = 0;
|
strncpy(pTxt->m_pcValue, p_pcValue, stLength); pTxt->m_pcValue[stLength] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -558,10 +634,11 @@ MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_releaseServiceTxt
|
MDNSResponder::_releaseServiceTxt
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
||||||
MDNSResponder::stcMDNSServiceTxt* p_pTxt) {
|
MDNSResponder::stcMDNSServiceTxt* p_pTxt)
|
||||||
|
{
|
||||||
|
|
||||||
return ((p_pService) &&
|
return ((p_pService) &&
|
||||||
(p_pTxt) &&
|
(p_pTxt) &&
|
||||||
@ -569,18 +646,20 @@ bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_updateServiceTxt
|
MDNSResponder::_updateServiceTxt
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
||||||
MDNSResponder::stcMDNSServiceTxt* p_pTxt,
|
MDNSResponder::stcMDNSServiceTxt* p_pTxt,
|
||||||
const char* p_pcValue,
|
const char* p_pcValue,
|
||||||
bool p_bTemp) {
|
bool p_bTemp)
|
||||||
|
{
|
||||||
|
|
||||||
if ((p_pService) &&
|
if ((p_pService) &&
|
||||||
(p_pTxt) &&
|
(p_pTxt) &&
|
||||||
(MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() -
|
(MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() -
|
||||||
(p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0) +
|
(p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0) +
|
||||||
(p_pcValue ? strlen(p_pcValue) : 0)))) {
|
(p_pcValue ? strlen(p_pcValue) : 0))))
|
||||||
|
{
|
||||||
p_pTxt->update(p_pcValue);
|
p_pTxt->update(p_pcValue);
|
||||||
p_pTxt->m_bTemp = p_bTemp;
|
p_pTxt->m_bTemp = p_bTemp;
|
||||||
}
|
}
|
||||||
@ -588,41 +667,47 @@ MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_findServiceTxt
|
MDNSResponder::_findServiceTxt
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
||||||
const char* p_pcKey) {
|
const char* p_pcKey)
|
||||||
|
{
|
||||||
|
|
||||||
return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0);
|
return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_findServiceTxt
|
MDNSResponder::_findServiceTxt
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
||||||
const hMDNSTxt p_hTxt) {
|
const hMDNSTxt p_hTxt)
|
||||||
|
{
|
||||||
|
|
||||||
return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt) : 0);
|
return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_addServiceTxt
|
MDNSResponder::_addServiceTxt
|
||||||
*/
|
*/
|
||||||
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService,
|
||||||
const char* p_pcKey,
|
const char* p_pcKey,
|
||||||
const char* p_pcValue,
|
const char* p_pcValue,
|
||||||
bool p_bTemp) {
|
bool p_bTemp)
|
||||||
|
{
|
||||||
stcMDNSServiceTxt* pResult = 0;
|
stcMDNSServiceTxt* pResult = 0;
|
||||||
|
|
||||||
if ((p_pService) &&
|
if ((p_pService) &&
|
||||||
(p_pcKey) &&
|
(p_pcKey) &&
|
||||||
(strlen(p_pcKey))) {
|
(strlen(p_pcKey)))
|
||||||
|
{
|
||||||
|
|
||||||
stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey);
|
stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey);
|
||||||
if (pTxt) {
|
if (pTxt)
|
||||||
|
{
|
||||||
pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp);
|
pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp);
|
pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,7 +715,8 @@ MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::s
|
|||||||
}
|
}
|
||||||
|
|
||||||
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery,
|
MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery,
|
||||||
const uint32_t p_u32AnswerIndex) {
|
const uint32_t p_u32AnswerIndex)
|
||||||
|
{
|
||||||
stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery);
|
stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery);
|
||||||
stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0);
|
stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0);
|
||||||
// Fill m_pcTxts (if not already done)
|
// Fill m_pcTxts (if not already done)
|
||||||
@ -638,71 +724,83 @@ MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServ
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_collectServiceTxts
|
MDNSResponder::_collectServiceTxts
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService) {
|
bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService)
|
||||||
|
{
|
||||||
|
|
||||||
// Call Dynamic service callbacks
|
// Call Dynamic service callbacks
|
||||||
if (m_fnServiceTxtCallback) {
|
if (m_fnServiceTxtCallback)
|
||||||
|
{
|
||||||
m_fnServiceTxtCallback((hMDNSService)&p_rService);
|
m_fnServiceTxtCallback((hMDNSService)&p_rService);
|
||||||
}
|
}
|
||||||
if (p_rService.m_fnTxtCallback) {
|
if (p_rService.m_fnTxtCallback)
|
||||||
|
{
|
||||||
p_rService.m_fnTxtCallback((hMDNSService)&p_rService);
|
p_rService.m_fnTxtCallback((hMDNSService)&p_rService);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_releaseTempServiceTxts
|
MDNSResponder::_releaseTempServiceTxts
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService) {
|
bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService)
|
||||||
|
{
|
||||||
|
|
||||||
return (p_rService.m_Txts.removeTempTxts());
|
return (p_rService.m_Txts.removeTempTxts());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MISC
|
MISC
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_MDNS_RESPONDER
|
#ifdef DEBUG_ESP_MDNS_RESPONDER
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_printRRDomain
|
MDNSResponder::_printRRDomain
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const {
|
bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const
|
||||||
|
{
|
||||||
|
|
||||||
//DEBUG_OUTPUT.printf_P(PSTR("Domain: "));
|
//DEBUG_OUTPUT.printf_P(PSTR("Domain: "));
|
||||||
|
|
||||||
const char* pCursor = p_RRDomain.m_acName;
|
const char* pCursor = p_RRDomain.m_acName;
|
||||||
uint8_t u8Length = *pCursor++;
|
uint8_t u8Length = *pCursor++;
|
||||||
if (u8Length) {
|
if (u8Length)
|
||||||
while (u8Length) {
|
{
|
||||||
for (uint8_t u=0; u<u8Length; ++u) {
|
while (u8Length)
|
||||||
|
{
|
||||||
|
for (uint8_t u = 0; u < u8Length; ++u)
|
||||||
|
{
|
||||||
DEBUG_OUTPUT.printf_P(PSTR("%c"), *(pCursor++));
|
DEBUG_OUTPUT.printf_P(PSTR("%c"), *(pCursor++));
|
||||||
}
|
}
|
||||||
u8Length = *pCursor++;
|
u8Length = *pCursor++;
|
||||||
if (u8Length) {
|
if (u8Length)
|
||||||
|
{
|
||||||
DEBUG_OUTPUT.printf_P(PSTR("."));
|
DEBUG_OUTPUT.printf_P(PSTR("."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // empty domain
|
else // empty domain
|
||||||
|
{
|
||||||
DEBUG_OUTPUT.printf_P(PSTR("-empty-"));
|
DEBUG_OUTPUT.printf_P(PSTR("-empty-"));
|
||||||
}
|
}
|
||||||
//DEBUG_OUTPUT.printf_P(PSTR("\n"));
|
//DEBUG_OUTPUT.printf_P(PSTR("\n"));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MDNSResponder::_printRRAnswer
|
MDNSResponder::_printRRAnswer
|
||||||
*/
|
*/
|
||||||
bool MDNSResponder::_printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const {
|
bool MDNSResponder::_printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const
|
||||||
|
{
|
||||||
|
|
||||||
DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] RRAnswer: "));
|
DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] RRAnswer: "));
|
||||||
_printRRDomain(p_RRAnswer.m_Header.m_Domain);
|
_printRRDomain(p_RRAnswer.m_Header.m_Domain);
|
||||||
DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, "), p_RRAnswer.m_Header.m_Attributes.m_u16Type, p_RRAnswer.m_Header.m_Attributes.m_u16Class, p_RRAnswer.m_u32TTL);
|
DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, "), p_RRAnswer.m_Header.m_Attributes.m_u16Type, p_RRAnswer.m_Header.m_Attributes.m_u16Class, p_RRAnswer.m_u32TTL);
|
||||||
switch (p_RRAnswer.m_Header.m_Attributes.m_u16Type & (~0x8000)) { // Topmost bit might carry 'cache flush' flag
|
switch (p_RRAnswer.m_Header.m_Attributes.m_u16Type & (~0x8000)) // Topmost bit might carry 'cache flush' flag
|
||||||
|
{
|
||||||
#ifdef MDNS_IP4_SUPPORT
|
#ifdef MDNS_IP4_SUPPORT
|
||||||
case DNS_RRTYPE_A:
|
case DNS_RRTYPE_A:
|
||||||
DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((const stcMDNS_RRAnswerA*)&p_RRAnswer)->m_IPAddress.toString().c_str());
|
DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((const stcMDNS_RRAnswerA*)&p_RRAnswer)->m_IPAddress.toString().c_str());
|
||||||
@ -712,10 +810,12 @@ bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rSe
|
|||||||
DEBUG_OUTPUT.printf_P(PSTR("PTR "));
|
DEBUG_OUTPUT.printf_P(PSTR("PTR "));
|
||||||
_printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain);
|
_printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain);
|
||||||
break;
|
break;
|
||||||
case DNS_RRTYPE_TXT: {
|
case DNS_RRTYPE_TXT:
|
||||||
|
{
|
||||||
size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength();
|
size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength();
|
||||||
char* pTxts = new char[stTxtLength];
|
char* pTxts = new char[stTxtLength];
|
||||||
if (pTxts) {
|
if (pTxts)
|
||||||
|
{
|
||||||
((/*const c_str()!!*/stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts);
|
((/*const c_str()!!*/stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts);
|
||||||
DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts);
|
DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts);
|
||||||
delete[] pTxts;
|
delete[] pTxts;
|
||||||
@ -738,7 +838,7 @@ bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rSe
|
|||||||
DEBUG_OUTPUT.printf_P(PSTR("\n"));
|
DEBUG_OUTPUT.printf_P(PSTR("\n"));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace MDNSImplementation
|
} // namespace MDNSImplementation
|
||||||
|
@ -1,37 +1,39 @@
|
|||||||
/*
|
/*
|
||||||
* LEAmDNS_Priv.h
|
LEAmDNS_Priv.h
|
||||||
*
|
|
||||||
* License (MIT license):
|
License (MIT license):
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
* in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
* furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in
|
||||||
* all copies or substantial portions of the Software.
|
all copies or substantial portions of the Software.
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MDNS_PRIV_H
|
#ifndef MDNS_PRIV_H
|
||||||
#define MDNS_PRIV_H
|
#define MDNS_PRIV_H
|
||||||
|
|
||||||
namespace esp8266 {
|
namespace esp8266
|
||||||
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* LEAmDNS
|
LEAmDNS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace MDNSImplementation {
|
namespace MDNSImplementation
|
||||||
|
{
|
||||||
|
|
||||||
// Enable class debug functions
|
// Enable class debug functions
|
||||||
#define ESP_8266_MDNS_INCLUDE
|
#define ESP_8266_MDNS_INCLUDE
|
||||||
@ -42,7 +44,7 @@ namespace MDNSImplementation {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LWIP_OPEN_SRC
|
#ifndef LWIP_OPEN_SRC
|
||||||
#define LWIP_OPEN_SRC
|
#define LWIP_OPEN_SRC
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -59,87 +61,87 @@ namespace MDNSImplementation {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_MDNS_RESPONDER
|
#ifdef DEBUG_ESP_MDNS_RESPONDER
|
||||||
#ifdef DEBUG_ESP_MDNS_INFO
|
#ifdef DEBUG_ESP_MDNS_INFO
|
||||||
#define DEBUG_EX_INFO(A) A
|
#define DEBUG_EX_INFO(A) A
|
||||||
#else
|
|
||||||
#define DEBUG_EX_INFO(A) do { (void)0; } while (0)
|
|
||||||
#endif
|
|
||||||
#ifdef DEBUG_ESP_MDNS_ERR
|
|
||||||
#define DEBUG_EX_ERR(A) A
|
|
||||||
#else
|
|
||||||
#define DEBUG_EX_ERR(A) do { (void)0; } while (0)
|
|
||||||
#endif
|
|
||||||
#ifdef DEBUG_ESP_MDNS_TX
|
|
||||||
#define DEBUG_EX_TX(A) A
|
|
||||||
#else
|
|
||||||
#define DEBUG_EX_TX(A) do { (void)0; } while (0)
|
|
||||||
#endif
|
|
||||||
#ifdef DEBUG_ESP_MDNS_RX
|
|
||||||
#define DEBUG_EX_RX(A) A
|
|
||||||
#else
|
|
||||||
#define DEBUG_EX_RX(A) do { (void)0; } while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_PORT
|
|
||||||
#define DEBUG_OUTPUT DEBUG_ESP_PORT
|
|
||||||
#else
|
|
||||||
#define DEBUG_OUTPUT Serial
|
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
#define DEBUG_EX_INFO(A) do { (void)0; } while (0)
|
#define DEBUG_EX_INFO(A) do { (void)0; } while (0)
|
||||||
#define DEBUG_EX_ERR(A) do { (void)0; } while (0)
|
#endif
|
||||||
#define DEBUG_EX_TX(A) do { (void)0; } while (0)
|
#ifdef DEBUG_ESP_MDNS_ERR
|
||||||
#define DEBUG_EX_RX(A) do { (void)0; } while (0)
|
#define DEBUG_EX_ERR(A) A
|
||||||
|
#else
|
||||||
|
#define DEBUG_EX_ERR(A) do { (void)0; } while (0)
|
||||||
|
#endif
|
||||||
|
#ifdef DEBUG_ESP_MDNS_TX
|
||||||
|
#define DEBUG_EX_TX(A) A
|
||||||
|
#else
|
||||||
|
#define DEBUG_EX_TX(A) do { (void)0; } while (0)
|
||||||
|
#endif
|
||||||
|
#ifdef DEBUG_ESP_MDNS_RX
|
||||||
|
#define DEBUG_EX_RX(A) A
|
||||||
|
#else
|
||||||
|
#define DEBUG_EX_RX(A) do { (void)0; } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_PORT
|
||||||
|
#define DEBUG_OUTPUT DEBUG_ESP_PORT
|
||||||
|
#else
|
||||||
|
#define DEBUG_OUTPUT Serial
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define DEBUG_EX_INFO(A) do { (void)0; } while (0)
|
||||||
|
#define DEBUG_EX_ERR(A) do { (void)0; } while (0)
|
||||||
|
#define DEBUG_EX_TX(A) do { (void)0; } while (0)
|
||||||
|
#define DEBUG_EX_RX(A) do { (void)0; } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Replaced by 'lwip/prot/dns.h' definitions
|
/* Replaced by 'lwip/prot/dns.h' definitions
|
||||||
#ifdef MDNS_IP4_SUPPORT
|
#ifdef MDNS_IP4_SUPPORT
|
||||||
#define MDNS_MULTICAST_ADDR_IP4 (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT
|
#define MDNS_MULTICAST_ADDR_IP4 (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT
|
||||||
#endif
|
#endif
|
||||||
#ifdef MDNS_IP6_SUPPORT
|
#ifdef MDNS_IP6_SUPPORT
|
||||||
#define MDNS_MULTICAST_ADDR_IP6 (IPAddress("FF02::FB")) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT
|
#define MDNS_MULTICAST_ADDR_IP6 (IPAddress("FF02::FB")) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT
|
||||||
#endif*/
|
#endif*/
|
||||||
//#define MDNS_MULTICAST_PORT 5353
|
//#define MDNS_MULTICAST_PORT 5353
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is NOT the TTL (Time-To-Live) for MDNS records, but the
|
This is NOT the TTL (Time-To-Live) for MDNS records, but the
|
||||||
* subnet level distance MDNS records should travel.
|
subnet level distance MDNS records should travel.
|
||||||
* 1 sets the subnet distance to 'local', which is default for MDNS.
|
1 sets the subnet distance to 'local', which is default for MDNS.
|
||||||
* (Btw.: 255 would set it to 'as far as possible' -> internet)
|
(Btw.: 255 would set it to 'as far as possible' -> internet)
|
||||||
*
|
|
||||||
* However, RFC 3171 seems to force 255 instead
|
However, RFC 3171 seems to force 255 instead
|
||||||
*/
|
*/
|
||||||
#define MDNS_MULTICAST_TTL 255/*1*/
|
#define MDNS_MULTICAST_TTL 255/*1*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the MDNS record TTL
|
This is the MDNS record TTL
|
||||||
* Host level records are set to 2min (120s)
|
Host level records are set to 2min (120s)
|
||||||
* service level records are set to 75min (4500s)
|
service level records are set to 75min (4500s)
|
||||||
*/
|
*/
|
||||||
#define MDNS_HOST_TTL 120
|
#define MDNS_HOST_TTL 120
|
||||||
#define MDNS_SERVICE_TTL 4500
|
#define MDNS_SERVICE_TTL 4500
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compressed labels are flaged by the two topmost bits of the length byte being set
|
Compressed labels are flaged by the two topmost bits of the length byte being set
|
||||||
*/
|
*/
|
||||||
#define MDNS_DOMAIN_COMPRESS_MARK 0xC0
|
#define MDNS_DOMAIN_COMPRESS_MARK 0xC0
|
||||||
/*
|
/*
|
||||||
* Avoid endless recursion because of malformed compressed labels
|
Avoid endless recursion because of malformed compressed labels
|
||||||
*/
|
*/
|
||||||
#define MDNS_DOMAIN_MAX_REDIRCTION 6
|
#define MDNS_DOMAIN_MAX_REDIRCTION 6
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default service priority and weight in SRV answers
|
Default service priority and weight in SRV answers
|
||||||
*/
|
*/
|
||||||
#define MDNS_SRV_PRIORITY 0
|
#define MDNS_SRV_PRIORITY 0
|
||||||
#define MDNS_SRV_WEIGHT 0
|
#define MDNS_SRV_WEIGHT 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Delay between and number of probes for host and service domains
|
Delay between and number of probes for host and service domains
|
||||||
* Delay between and number of announces for host and service domains
|
Delay between and number of announces for host and service domains
|
||||||
* Delay between and number of service queries; the delay is multiplied by the resent number in '_checkServiceQueryCache'
|
Delay between and number of service queries; the delay is multiplied by the resent number in '_checkServiceQueryCache'
|
||||||
*/
|
*/
|
||||||
#define MDNS_PROBE_DELAY 250
|
#define MDNS_PROBE_DELAY 250
|
||||||
#define MDNS_PROBE_COUNT 3
|
#define MDNS_PROBE_COUNT 3
|
||||||
#define MDNS_ANNOUNCE_DELAY 1000
|
#define MDNS_ANNOUNCE_DELAY 1000
|
||||||
@ -149,24 +151,24 @@ namespace MDNSImplementation {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Force host domain to use only lowercase letters
|
Force host domain to use only lowercase letters
|
||||||
*/
|
*/
|
||||||
//#define MDNS_FORCE_LOWERCASE_HOSTNAME
|
//#define MDNS_FORCE_LOWERCASE_HOSTNAME
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enable/disable the usage of the F() macro in debug trace printf calls.
|
Enable/disable the usage of the F() macro in debug trace printf calls.
|
||||||
* There needs to be an PGM comptible printf function to use this.
|
There needs to be an PGM comptible printf function to use this.
|
||||||
*
|
|
||||||
* USE_PGM_PRINTF and F
|
USE_PGM_PRINTF and F
|
||||||
*/
|
*/
|
||||||
#define USE_PGM_PRINTF
|
#define USE_PGM_PRINTF
|
||||||
|
|
||||||
#ifdef USE_PGM_PRINTF
|
#ifdef USE_PGM_PRINTF
|
||||||
#else
|
#else
|
||||||
#ifdef F
|
#ifdef F
|
||||||
#undef F
|
#undef F
|
||||||
#endif
|
#endif
|
||||||
#define F(A) A
|
#define F(A) A
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace MDNSImplementation
|
} // namespace MDNSImplementation
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,26 @@
|
|||||||
/*
|
/*
|
||||||
* LEAmDNS_Priv.h
|
LEAmDNS_Priv.h
|
||||||
*
|
|
||||||
* License (MIT license):
|
License (MIT license):
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
* in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
* furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in
|
||||||
* all copies or substantial portions of the Software.
|
all copies or substantial portions of the Software.
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MDNS_LWIPDEFS_H
|
#ifndef MDNS_LWIPDEFS_H
|
||||||
#define MDNS_LWIPDEFS_H
|
#define MDNS_LWIPDEFS_H
|
||||||
|
@ -23,22 +23,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <bearssl/bearssl_hash.h>
|
||||||
|
|
||||||
#include "Hash.h"
|
#include "Hash.h"
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "sha1/sha1.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create a sha1 hash from data
|
* create a sha1 hash from data
|
||||||
* @param data uint8_t *
|
* @param data uint8_t *
|
||||||
* @param size uint32_t
|
* @param size uint32_t
|
||||||
* @param hash uint8_t[20]
|
* @param hash uint8_t[20]
|
||||||
*/
|
*/
|
||||||
void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]) {
|
void sha1(const uint8_t* data, uint32_t size, uint8_t hash[20]) {
|
||||||
|
br_sha1_context ctx;
|
||||||
SHA1_CTX ctx;
|
|
||||||
|
|
||||||
#ifdef DEBUG_SHA1
|
#ifdef DEBUG_SHA1
|
||||||
os_printf("DATA:");
|
os_printf("DATA:");
|
||||||
@ -53,9 +49,9 @@ void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]) {
|
|||||||
os_printf("\n");
|
os_printf("\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SHA1Init(&ctx);
|
br_sha1_init(&ctx);
|
||||||
SHA1Update(&ctx, data, size);
|
br_sha1_update(&ctx, data, size);
|
||||||
SHA1Final(hash, &ctx);
|
br_sha1_out(&ctx, hash);
|
||||||
|
|
||||||
#ifdef DEBUG_SHA1
|
#ifdef DEBUG_SHA1
|
||||||
os_printf("SHA1:");
|
os_printf("SHA1:");
|
||||||
@ -66,52 +62,35 @@ void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void sha1(char * data, uint32_t size, uint8_t hash[20]) {
|
void sha1(const char* data, uint32_t size, uint8_t hash[20]) {
|
||||||
sha1((uint8_t *) data, size, hash);
|
sha1((const uint8_t *) data, size, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sha1(const uint8_t * data, uint32_t size, uint8_t hash[20]) {
|
void sha1(const String& data, uint8_t hash[20]) {
|
||||||
sha1((uint8_t *) data, size, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sha1(const char * data, uint32_t size, uint8_t hash[20]) {
|
|
||||||
sha1((uint8_t *) data, size, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sha1(String data, uint8_t hash[20]) {
|
|
||||||
sha1(data.c_str(), data.length(), hash);
|
sha1(data.c_str(), data.length(), hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
String sha1(uint8_t* data, uint32_t size) {
|
String sha1(const uint8_t* data, uint32_t size) {
|
||||||
uint8_t hash[20];
|
uint8_t hash[20];
|
||||||
String hashStr = "";
|
String hashStr((const char*)nullptr);
|
||||||
|
hashStr.reserve(20 * 2 + 1);
|
||||||
|
|
||||||
sha1(&data[0], size, &hash[0]);
|
sha1(&data[0], size, &hash[0]);
|
||||||
|
|
||||||
for(uint16_t i = 0; i < 20; i++) {
|
for(uint16_t i = 0; i < 20; i++) {
|
||||||
String hex = String(hash[i], HEX);
|
char hex[3];
|
||||||
if(hex.length() < 2) {
|
snprintf(hex, sizeof(hex), "%02x", hash[i]);
|
||||||
hex = "0" + hex;
|
|
||||||
}
|
|
||||||
hashStr += hex;
|
hashStr += hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hashStr;
|
return hashStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
String sha1(char* data, uint32_t size) {
|
|
||||||
return sha1((uint8_t*) data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
String sha1(const uint8_t* data, uint32_t size) {
|
|
||||||
return sha1((uint8_t*) data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
String sha1(const char* data, uint32_t size) {
|
String sha1(const char* data, uint32_t size) {
|
||||||
return sha1((uint8_t*) data, size);
|
return sha1((const uint8_t*) data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
String sha1(String data) {
|
String sha1(const String& data) {
|
||||||
return sha1(data.c_str(), data.length());
|
return sha1(data.c_str(), data.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,16 +27,12 @@
|
|||||||
|
|
||||||
//#define DEBUG_SHA1
|
//#define DEBUG_SHA1
|
||||||
|
|
||||||
void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]);
|
void sha1(const uint8_t* data, uint32_t size, uint8_t hash[20]);
|
||||||
void sha1(char * data, uint32_t size, uint8_t hash[20]);
|
void sha1(const char* data, uint32_t size, uint8_t hash[20]);
|
||||||
void sha1(const uint8_t * data, uint32_t size, uint8_t hash[20]);
|
void sha1(const String& data, uint8_t hash[20]);
|
||||||
void sha1(const char * data, uint32_t size, uint8_t hash[20]);
|
|
||||||
void sha1(String data, uint8_t hash[20]);
|
|
||||||
|
|
||||||
String sha1(uint8_t* data, uint32_t size);
|
|
||||||
String sha1(char* data, uint32_t size);
|
|
||||||
String sha1(const uint8_t* data, uint32_t size);
|
String sha1(const uint8_t* data, uint32_t size);
|
||||||
String sha1(const char* data, uint32_t size);
|
String sha1(const char* data, uint32_t size);
|
||||||
String sha1(String data);
|
String sha1(const String& data);
|
||||||
|
|
||||||
#endif /* HASH_H_ */
|
#endif /* HASH_H_ */
|
||||||
|
@ -1,208 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file sha1.c
|
|
||||||
* @date 20.05.2015
|
|
||||||
* @author Steve Reid <steve@edmweb.com>
|
|
||||||
*
|
|
||||||
* from: http://www.virtualbox.org/svn/vbox/trunk/src/recompiler/tests/sha1.c
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* from valgrind tests */
|
|
||||||
|
|
||||||
/* ================ sha1.c ================ */
|
|
||||||
/*
|
|
||||||
SHA-1 in C
|
|
||||||
By Steve Reid <steve@edmweb.com>
|
|
||||||
100% Public Domain
|
|
||||||
|
|
||||||
Test Vectors (from FIPS PUB 180-1)
|
|
||||||
"abc"
|
|
||||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
|
||||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
|
||||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
|
||||||
A million repetitions of "a"
|
|
||||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
|
|
||||||
/* #define SHA1HANDSOFF * Copies data before messing with it. */
|
|
||||||
|
|
||||||
#define SHA1HANDSOFF
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <c_types.h>
|
|
||||||
|
|
||||||
#include "sha1.h"
|
|
||||||
|
|
||||||
//#include <endian.h>
|
|
||||||
|
|
||||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
|
||||||
|
|
||||||
/* blk0() and blk() perform the initial expand. */
|
|
||||||
/* I got the idea of expanding during the round function from SSLeay */
|
|
||||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
||||||
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
|
||||||
|(rol(block->l[i],8)&0x00FF00FF))
|
|
||||||
#elif BYTE_ORDER == BIG_ENDIAN
|
|
||||||
#define blk0(i) block->l[i]
|
|
||||||
#else
|
|
||||||
#error "Endianness not defined!"
|
|
||||||
#endif
|
|
||||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
|
||||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
|
||||||
|
|
||||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
|
||||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
|
||||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
|
||||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
|
||||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
|
||||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
|
||||||
|
|
||||||
|
|
||||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
|
||||||
|
|
||||||
void ICACHE_FLASH_ATTR SHA1Transform(uint32_t state[5], uint8_t buffer[64])
|
|
||||||
{
|
|
||||||
uint32_t a, b, c, d, e;
|
|
||||||
typedef union {
|
|
||||||
unsigned char c[64];
|
|
||||||
uint32_t l[16];
|
|
||||||
} CHAR64LONG16;
|
|
||||||
#ifdef SHA1HANDSOFF
|
|
||||||
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
|
||||||
memcpy(block, buffer, 64);
|
|
||||||
#else
|
|
||||||
/* The following had better never be used because it causes the
|
|
||||||
* pointer-to-const buffer to be cast into a pointer to non-const.
|
|
||||||
* And the result is written through. I threw a "const" in, hoping
|
|
||||||
* this will cause a diagnostic.
|
|
||||||
*/
|
|
||||||
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
|
|
||||||
#endif
|
|
||||||
/* Copy context->state[] to working vars */
|
|
||||||
a = state[0];
|
|
||||||
b = state[1];
|
|
||||||
c = state[2];
|
|
||||||
d = state[3];
|
|
||||||
e = state[4];
|
|
||||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
|
||||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
|
||||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
|
||||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
|
||||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
|
||||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
|
||||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
|
||||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
|
||||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
|
||||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
|
||||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
|
||||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
|
||||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
|
||||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
|
||||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
|
||||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
|
||||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
|
||||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
|
||||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
|
||||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
|
||||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
|
||||||
/* Add the working vars back into context.state[] */
|
|
||||||
state[0] += a;
|
|
||||||
state[1] += b;
|
|
||||||
state[2] += c;
|
|
||||||
state[3] += d;
|
|
||||||
state[4] += e;
|
|
||||||
/* Wipe variables */
|
|
||||||
a = b = c = d = e = 0;
|
|
||||||
#ifdef SHA1HANDSOFF
|
|
||||||
memset(block, '\0', sizeof(block));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* SHA1Init - Initialize new context */
|
|
||||||
|
|
||||||
void ICACHE_FLASH_ATTR SHA1Init(SHA1_CTX* context)
|
|
||||||
{
|
|
||||||
/* SHA1 initialization constants */
|
|
||||||
context->state[0] = 0x67452301;
|
|
||||||
context->state[1] = 0xEFCDAB89;
|
|
||||||
context->state[2] = 0x98BADCFE;
|
|
||||||
context->state[3] = 0x10325476;
|
|
||||||
context->state[4] = 0xC3D2E1F0;
|
|
||||||
context->count[0] = context->count[1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Run your data through this. */
|
|
||||||
|
|
||||||
void ICACHE_FLASH_ATTR SHA1Update(SHA1_CTX* context, uint8_t* data, uint32_t len)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
uint32_t j;
|
|
||||||
|
|
||||||
j = context->count[0];
|
|
||||||
if ((context->count[0] += len << 3) < j)
|
|
||||||
context->count[1]++;
|
|
||||||
context->count[1] += (len>>29);
|
|
||||||
j = (j >> 3) & 63;
|
|
||||||
if ((j + len) > 63) {
|
|
||||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
|
||||||
SHA1Transform(context->state, context->buffer);
|
|
||||||
for ( ; i + 63 < len; i += 64) {
|
|
||||||
SHA1Transform(context->state, &data[i]);
|
|
||||||
}
|
|
||||||
j = 0;
|
|
||||||
}
|
|
||||||
else i = 0;
|
|
||||||
memcpy(&context->buffer[j], &data[i], len - i);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Add padding and return the message digest. */
|
|
||||||
|
|
||||||
void ICACHE_FLASH_ATTR SHA1Final(unsigned char digest[20], SHA1_CTX* context)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
unsigned char finalcount[8];
|
|
||||||
unsigned char c;
|
|
||||||
|
|
||||||
#if 0 /* untested "improvement" by DHR */
|
|
||||||
/* Convert context->count to a sequence of bytes
|
|
||||||
* in finalcount. Second element first, but
|
|
||||||
* big-endian order within element.
|
|
||||||
* But we do it all backwards.
|
|
||||||
*/
|
|
||||||
unsigned char *fcp = &finalcount[8];
|
|
||||||
|
|
||||||
for (i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
uint32_t t = context->count[i];
|
|
||||||
int j;
|
|
||||||
|
|
||||||
for (j = 0; j < 4; t >>= 8, j++)
|
|
||||||
*--fcp = (unsigned char) t;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
|
||||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
c = 0200;
|
|
||||||
SHA1Update(context, &c, 1);
|
|
||||||
while ((context->count[0] & 504) != 448) {
|
|
||||||
c = 0000;
|
|
||||||
SHA1Update(context, &c, 1);
|
|
||||||
}
|
|
||||||
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
|
|
||||||
for (i = 0; i < 20; i++) {
|
|
||||||
digest[i] = (unsigned char)
|
|
||||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
|
||||||
}
|
|
||||||
/* Wipe variables */
|
|
||||||
memset(context, '\0', sizeof(*context));
|
|
||||||
memset(&finalcount, '\0', sizeof(finalcount));
|
|
||||||
}
|
|
||||||
/* ================ end of sha1.c ================ */
|
|
@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file sha1.h
|
|
||||||
* @date 20.05.2015
|
|
||||||
* @author Steve Reid <steve@edmweb.com>
|
|
||||||
*
|
|
||||||
* from: http://www.virtualbox.org/svn/vbox/trunk/src/recompiler/tests/sha1.c
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* ================ sha1.h ================ */
|
|
||||||
/*
|
|
||||||
SHA-1 in C
|
|
||||||
By Steve Reid <steve@edmweb.com>
|
|
||||||
100% Public Domain
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SHA1_H_
|
|
||||||
#define SHA1_H_
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t state[5];
|
|
||||||
uint32_t count[2];
|
|
||||||
unsigned char buffer[64];
|
|
||||||
} SHA1_CTX;
|
|
||||||
|
|
||||||
void SHA1Transform(uint32_t state[5], uint8_t buffer[64]);
|
|
||||||
void SHA1Init(SHA1_CTX* context);
|
|
||||||
void SHA1Update(SHA1_CTX* context, uint8_t* data, uint32_t len);
|
|
||||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
|
|
||||||
|
|
||||||
#endif /* SHA1_H_ */
|
|
||||||
|
|
||||||
/* ================ end of sha1.h ================ */
|
|
@ -24,7 +24,7 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#define SPI_HAS_TRANSACTION
|
#define SPI_HAS_TRANSACTION 1
|
||||||
|
|
||||||
// This defines are not representing the real Divider of the ESP8266
|
// This defines are not representing the real Divider of the ESP8266
|
||||||
// the Defines match to an AVR Arduino on 16MHz for better compatibility
|
// the Defines match to an AVR Arduino on 16MHz for better compatibility
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 776d49b2f570b93fe88ad7a082519b4fb56be817
|
Subproject commit 8f85d649000b5bbdde1862d879dba4f225dd3a51
|
@ -23,9 +23,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "twi.h"
|
#include "twi.h"
|
||||||
@ -58,18 +58,20 @@ static int default_scl_pin = SCL;
|
|||||||
|
|
||||||
// Constructors ////////////////////////////////////////////////////////////////
|
// Constructors ////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
TwoWire::TwoWire(){}
|
TwoWire::TwoWire() {}
|
||||||
|
|
||||||
// Public Methods //////////////////////////////////////////////////////////////
|
// Public Methods //////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void TwoWire::begin(int sda, int scl){
|
void TwoWire::begin(int sda, int scl)
|
||||||
|
{
|
||||||
default_sda_pin = sda;
|
default_sda_pin = sda;
|
||||||
default_scl_pin = scl;
|
default_scl_pin = scl;
|
||||||
twi_init(sda, scl);
|
twi_init(sda, scl);
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::begin(int sda, int scl, uint8_t address){
|
void TwoWire::begin(int sda, int scl, uint8_t address)
|
||||||
|
{
|
||||||
default_sda_pin = sda;
|
default_sda_pin = sda;
|
||||||
default_scl_pin = scl;
|
default_scl_pin = scl;
|
||||||
twi_setAddress(address);
|
twi_setAddress(address);
|
||||||
@ -79,76 +81,92 @@ void TwoWire::begin(int sda, int scl, uint8_t address){
|
|||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::pins(int sda, int scl){
|
void TwoWire::pins(int sda, int scl)
|
||||||
|
{
|
||||||
default_sda_pin = sda;
|
default_sda_pin = sda;
|
||||||
default_scl_pin = scl;
|
default_scl_pin = scl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::begin(void){
|
void TwoWire::begin(void)
|
||||||
|
{
|
||||||
begin(default_sda_pin, default_scl_pin);
|
begin(default_sda_pin, default_scl_pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::begin(uint8_t address){
|
void TwoWire::begin(uint8_t address)
|
||||||
|
{
|
||||||
twi_setAddress(address);
|
twi_setAddress(address);
|
||||||
twi_attachSlaveTxEvent(onRequestService);
|
twi_attachSlaveTxEvent(onRequestService);
|
||||||
twi_attachSlaveRxEvent(onReceiveService);
|
twi_attachSlaveRxEvent(onReceiveService);
|
||||||
begin();
|
begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t TwoWire::status(){
|
uint8_t TwoWire::status()
|
||||||
|
{
|
||||||
return twi_status();
|
return twi_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::begin(int address){
|
void TwoWire::begin(int address)
|
||||||
|
{
|
||||||
begin((uint8_t)address);
|
begin((uint8_t)address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::setClock(uint32_t frequency){
|
void TwoWire::setClock(uint32_t frequency)
|
||||||
|
{
|
||||||
twi_setClock(frequency);
|
twi_setClock(frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::setClockStretchLimit(uint32_t limit){
|
void TwoWire::setClockStretchLimit(uint32_t limit)
|
||||||
|
{
|
||||||
twi_setClockStretchLimit(limit);
|
twi_setClockStretchLimit(limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){
|
size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop)
|
||||||
if(size > BUFFER_LENGTH){
|
{
|
||||||
|
if (size > BUFFER_LENGTH)
|
||||||
|
{
|
||||||
size = BUFFER_LENGTH;
|
size = BUFFER_LENGTH;
|
||||||
}
|
}
|
||||||
size_t read = (twi_readFrom(address, rxBuffer, size, sendStop) == 0)?size:0;
|
size_t read = (twi_readFrom(address, rxBuffer, size, sendStop) == 0) ? size : 0;
|
||||||
rxBufferIndex = 0;
|
rxBufferIndex = 0;
|
||||||
rxBufferLength = read;
|
rxBufferLength = read;
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop){
|
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)
|
||||||
|
{
|
||||||
return requestFrom(address, static_cast<size_t>(quantity), static_cast<bool>(sendStop));
|
return requestFrom(address, static_cast<size_t>(quantity), static_cast<bool>(sendStop));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity){
|
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity)
|
||||||
|
{
|
||||||
return requestFrom(address, static_cast<size_t>(quantity), true);
|
return requestFrom(address, static_cast<size_t>(quantity), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t TwoWire::requestFrom(int address, int quantity){
|
uint8_t TwoWire::requestFrom(int address, int quantity)
|
||||||
|
{
|
||||||
return requestFrom(static_cast<uint8_t>(address), static_cast<size_t>(quantity), true);
|
return requestFrom(static_cast<uint8_t>(address), static_cast<size_t>(quantity), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop){
|
uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop)
|
||||||
|
{
|
||||||
return requestFrom(static_cast<uint8_t>(address), static_cast<size_t>(quantity), static_cast<bool>(sendStop));
|
return requestFrom(static_cast<uint8_t>(address), static_cast<size_t>(quantity), static_cast<bool>(sendStop));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::beginTransmission(uint8_t address){
|
void TwoWire::beginTransmission(uint8_t address)
|
||||||
|
{
|
||||||
transmitting = 1;
|
transmitting = 1;
|
||||||
txAddress = address;
|
txAddress = address;
|
||||||
txBufferIndex = 0;
|
txBufferIndex = 0;
|
||||||
txBufferLength = 0;
|
txBufferLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::beginTransmission(int address){
|
void TwoWire::beginTransmission(int address)
|
||||||
|
{
|
||||||
beginTransmission((uint8_t)address);
|
beginTransmission((uint8_t)address);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t TwoWire::endTransmission(uint8_t sendStop){
|
uint8_t TwoWire::endTransmission(uint8_t sendStop)
|
||||||
|
{
|
||||||
int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, sendStop);
|
int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, sendStop);
|
||||||
txBufferIndex = 0;
|
txBufferIndex = 0;
|
||||||
txBufferLength = 0;
|
txBufferLength = 0;
|
||||||
@ -156,40 +174,56 @@ uint8_t TwoWire::endTransmission(uint8_t sendStop){
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t TwoWire::endTransmission(void){
|
uint8_t TwoWire::endTransmission(void)
|
||||||
|
{
|
||||||
return endTransmission(true);
|
return endTransmission(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TwoWire::write(uint8_t data){
|
size_t TwoWire::write(uint8_t data)
|
||||||
if(transmitting){
|
{
|
||||||
if(txBufferLength >= BUFFER_LENGTH){
|
if (transmitting)
|
||||||
|
{
|
||||||
|
if (txBufferLength >= BUFFER_LENGTH)
|
||||||
|
{
|
||||||
setWriteError();
|
setWriteError();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
txBuffer[txBufferIndex] = data;
|
txBuffer[txBufferIndex] = data;
|
||||||
++txBufferIndex;
|
++txBufferIndex;
|
||||||
txBufferLength = txBufferIndex;
|
txBufferLength = txBufferIndex;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
twi_transmit(&data, 1);
|
twi_transmit(&data, 1);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TwoWire::write(const uint8_t *data, size_t quantity){
|
size_t TwoWire::write(const uint8_t *data, size_t quantity)
|
||||||
if(transmitting){
|
{
|
||||||
for(size_t i = 0; i < quantity; ++i){
|
if (transmitting)
|
||||||
if(!write(data[i])) return i;
|
{
|
||||||
|
for (size_t i = 0; i < quantity; ++i)
|
||||||
|
{
|
||||||
|
if (!write(data[i]))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
}else{
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
twi_transmit(data, quantity);
|
twi_transmit(data, quantity);
|
||||||
}
|
}
|
||||||
return quantity;
|
return quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TwoWire::available(void){
|
int TwoWire::available(void)
|
||||||
|
{
|
||||||
int result = rxBufferLength - rxBufferIndex;
|
int result = rxBufferLength - rxBufferIndex;
|
||||||
|
|
||||||
if (!result) {
|
if (!result)
|
||||||
|
{
|
||||||
// yielding here will not make more data "available",
|
// yielding here will not make more data "available",
|
||||||
// but it will prevent the system from going into WDT reset
|
// but it will prevent the system from going into WDT reset
|
||||||
optimistic_yield(1000);
|
optimistic_yield(1000);
|
||||||
@ -198,24 +232,29 @@ int TwoWire::available(void){
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TwoWire::read(void){
|
int TwoWire::read(void)
|
||||||
|
{
|
||||||
int value = -1;
|
int value = -1;
|
||||||
if(rxBufferIndex < rxBufferLength){
|
if (rxBufferIndex < rxBufferLength)
|
||||||
|
{
|
||||||
value = rxBuffer[rxBufferIndex];
|
value = rxBuffer[rxBufferIndex];
|
||||||
++rxBufferIndex;
|
++rxBufferIndex;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TwoWire::peek(void){
|
int TwoWire::peek(void)
|
||||||
|
{
|
||||||
int value = -1;
|
int value = -1;
|
||||||
if(rxBufferIndex < rxBufferLength){
|
if (rxBufferIndex < rxBufferLength)
|
||||||
|
{
|
||||||
value = rxBuffer[rxBufferIndex];
|
value = rxBuffer[rxBufferIndex];
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::flush(void){
|
void TwoWire::flush(void)
|
||||||
|
{
|
||||||
rxBufferIndex = 0;
|
rxBufferIndex = 0;
|
||||||
rxBufferLength = 0;
|
rxBufferLength = 0;
|
||||||
txBufferIndex = 0;
|
txBufferIndex = 0;
|
||||||
@ -225,7 +264,8 @@ void TwoWire::flush(void){
|
|||||||
void TwoWire::onReceiveService(uint8_t* inBytes, size_t numBytes)
|
void TwoWire::onReceiveService(uint8_t* inBytes, size_t numBytes)
|
||||||
{
|
{
|
||||||
// don't bother if user hasn't registered a callback
|
// don't bother if user hasn't registered a callback
|
||||||
if (!user_onReceive) {
|
if (!user_onReceive)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// // don't bother if rx buffer is in use by a master requestFrom() op
|
// // don't bother if rx buffer is in use by a master requestFrom() op
|
||||||
@ -237,7 +277,8 @@ void TwoWire::onReceiveService(uint8_t* inBytes, size_t numBytes)
|
|||||||
|
|
||||||
// copy twi rx buffer into local read buffer
|
// copy twi rx buffer into local read buffer
|
||||||
// this enables new reads to happen in parallel
|
// this enables new reads to happen in parallel
|
||||||
for (uint8_t i = 0; i < numBytes; ++i) {
|
for (uint8_t i = 0; i < numBytes; ++i)
|
||||||
|
{
|
||||||
rxBuffer[i] = inBytes[i];
|
rxBuffer[i] = inBytes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +293,8 @@ void TwoWire::onReceiveService(uint8_t* inBytes, size_t numBytes)
|
|||||||
void TwoWire::onRequestService(void)
|
void TwoWire::onRequestService(void)
|
||||||
{
|
{
|
||||||
// don't bother if user hasn't registered a callback
|
// don't bother if user hasn't registered a callback
|
||||||
if (!user_onRequest) {
|
if (!user_onRequest)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,19 +307,24 @@ void TwoWire::onRequestService(void)
|
|||||||
user_onRequest();
|
user_onRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::onReceive( void (*function)(int) ) {
|
void TwoWire::onReceive(void (*function)(int))
|
||||||
|
{
|
||||||
// arduino api compatibility fixer:
|
// arduino api compatibility fixer:
|
||||||
// really hope size parameter will not exceed 2^31 :)
|
// really hope size parameter will not exceed 2^31 :)
|
||||||
static_assert(sizeof(int) == sizeof(size_t), "something is wrong in Arduino kingdom");
|
static_assert(sizeof(int) == sizeof(size_t), "something is wrong in Arduino kingdom");
|
||||||
user_onReceive = reinterpret_cast<void(*)(size_t)>(function);
|
user_onReceive = reinterpret_cast<void(*)(size_t)>(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::onReceive( void (*function)(size_t) ) {
|
void TwoWire::onReceive(void (*function)(size_t))
|
||||||
|
{
|
||||||
user_onReceive = function;
|
user_onReceive = function;
|
||||||
|
twi_enableSlaveMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwoWire::onRequest( void (*function)(void) ){
|
void TwoWire::onRequest(void (*function)(void))
|
||||||
|
{
|
||||||
user_onRequest = function;
|
user_onRequest = function;
|
||||||
|
twi_enableSlaveMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preinstantiate Objects //////////////////////////////////////////////////////
|
// Preinstantiate Objects //////////////////////////////////////////////////////
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
class TwoWire : public Stream
|
class TwoWire : public Stream
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static uint8_t rxBuffer[];
|
static uint8_t rxBuffer[];
|
||||||
static uint8_t rxBufferIndex;
|
static uint8_t rxBufferIndex;
|
||||||
static uint8_t rxBufferLength;
|
static uint8_t rxBufferLength;
|
||||||
@ -48,7 +48,7 @@ class TwoWire : public Stream
|
|||||||
static void (*user_onReceive)(size_t);
|
static void (*user_onReceive)(size_t);
|
||||||
static void onRequestService(void);
|
static void onRequestService(void);
|
||||||
static void onReceiveService(uint8_t*, size_t);
|
static void onReceiveService(uint8_t*, size_t);
|
||||||
public:
|
public:
|
||||||
TwoWire();
|
TwoWire();
|
||||||
void begin(int sda, int scl);
|
void begin(int sda, int scl);
|
||||||
void begin(int sda, int scl, uint8_t address);
|
void begin(int sda, int scl, uint8_t address);
|
||||||
@ -76,9 +76,9 @@ class TwoWire : public Stream
|
|||||||
virtual int read(void);
|
virtual int read(void);
|
||||||
virtual int peek(void);
|
virtual int peek(void);
|
||||||
virtual void flush(void);
|
virtual void flush(void);
|
||||||
void onReceive( void (*)(int) ); // arduino api
|
void onReceive(void (*)(int)); // arduino api
|
||||||
void onReceive( void (*)(size_t) ); // legacy esp8266 backward compatibility
|
void onReceive(void (*)(size_t)); // legacy esp8266 backward compatibility
|
||||||
void onRequest( void (*)(void) );
|
void onRequest(void (*)(void));
|
||||||
|
|
||||||
using Print::write;
|
using Print::write;
|
||||||
};
|
};
|
||||||
|
@ -1,80 +1,160 @@
|
|||||||
/*
|
/*
|
||||||
NTP-TZ-DST
|
NTP-TZ-DST (v2)
|
||||||
NetWork Time Protocol - Time Zone - Daylight Saving Time
|
NetWork Time Protocol - Time Zone - Daylight Saving Time
|
||||||
|
|
||||||
This example shows how to read and set time,
|
This example shows:
|
||||||
and how to use NTP (set NTP0_OR_LOCAL1 to 0 below)
|
- how to read and set time
|
||||||
or an external RTC (set NTP0_OR_LOCAL1 to 1 below)
|
- how to set timezone per country/city
|
||||||
|
- how is local time automatically handled per official timezone definitions
|
||||||
TZ and DST below have to be manually set
|
- how to change internal sntp start and update delay
|
||||||
according to your local settings.
|
- how to use callbacks when time is updated
|
||||||
|
|
||||||
This example code is in the public domain.
|
This example code is in the public domain.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <time.h> // time() ctime()
|
|
||||||
#include <sys/time.h> // struct timeval
|
|
||||||
#include <coredecls.h> // settimeofday_cb()
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef STASSID
|
#ifndef STASSID
|
||||||
#define STASSID "your-ssid"
|
#define STASSID "your-ssid"
|
||||||
#define STAPSK "your-password"
|
#define STAPSK "your-password"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SSID STASSID
|
// initial time (possibly given by an external RTC)
|
||||||
#define SSIDPWD STAPSK
|
#define RTC_UTC_TEST 1510592825 // 1510592825 = Monday 13 November 2017 17:07:05 UTC
|
||||||
#define TZ 1 // (utc+) TZ in hours
|
|
||||||
#define DST_MN 60 // use 60mn for summer time in some countries
|
|
||||||
|
|
||||||
#define NTP0_OR_LOCAL1 1 // 0:use NTP 1:fake external RTC
|
|
||||||
#define RTC_TEST 1510592825 // 1510592825 = Monday 13 November 2017 17:07:05 UTC
|
// This database is autogenerated from IANA timezone database
|
||||||
|
// https://www.iana.org/time-zones
|
||||||
|
// and can be updated on demand in this repository
|
||||||
|
#include <TZ.h>
|
||||||
|
|
||||||
|
// "TZ_" macros follow DST change across seasons without source code change
|
||||||
|
// check for your nearest city in TZ.h
|
||||||
|
|
||||||
|
// espressif headquarter TZ
|
||||||
|
//#define MYTZ TZ_Asia_Shanghai
|
||||||
|
|
||||||
|
// example for "Not Only Whole Hours" timezones:
|
||||||
|
// Kolkata/Calcutta is shifted by 30mn
|
||||||
|
//#define MYTZ TZ_Asia_Kolkata
|
||||||
|
|
||||||
|
// example of a timezone with a variable Daylight-Saving-Time:
|
||||||
|
// demo: watch automatic time adjustment on Summer/Winter change (DST)
|
||||||
|
#define MYTZ TZ_Europe_London
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#define TZ_MN ((TZ)*60)
|
#include <ESP8266WiFi.h>
|
||||||
#define TZ_SEC ((TZ)*3600)
|
#include <coredecls.h> // settimeofday_cb()
|
||||||
#define DST_SEC ((DST_MN)*60)
|
#include <Schedule.h>
|
||||||
|
#include <PolledTimeout.h>
|
||||||
|
|
||||||
timeval cbtime; // time set in callback
|
#include <time.h> // time() ctime()
|
||||||
bool cbtime_set = false;
|
#include <sys/time.h> // struct timeval
|
||||||
|
|
||||||
void time_is_set(void) {
|
#include <sntp.h> // sntp_servermode_dhcp()
|
||||||
gettimeofday(&cbtime, NULL);
|
|
||||||
cbtime_set = true;
|
|
||||||
Serial.println("------------------ settimeofday() was called ------------------");
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
settimeofday_cb(time_is_set);
|
|
||||||
|
|
||||||
#if NTP0_OR_LOCAL1
|
|
||||||
// local
|
|
||||||
|
|
||||||
ESP.eraseConfig();
|
|
||||||
time_t rtc = RTC_TEST;
|
|
||||||
timeval tv = { rtc, 0 };
|
|
||||||
timezone tz = { TZ_MN + DST_MN, 0 };
|
|
||||||
settimeofday(&tv, &tz);
|
|
||||||
|
|
||||||
#else // ntp
|
|
||||||
|
|
||||||
configTime(TZ_SEC, DST_SEC, "pool.ntp.org");
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
WiFi.begin(SSID, SSIDPWD);
|
|
||||||
// don't wait, observe time changing when ntp timestamp is received
|
|
||||||
|
|
||||||
#endif // ntp
|
|
||||||
}
|
|
||||||
|
|
||||||
// for testing purpose:
|
// for testing purpose:
|
||||||
extern "C" int clock_gettime(clockid_t unused, struct timespec *tp);
|
extern "C" int clock_gettime(clockid_t unused, struct timespec *tp);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static timeval tv;
|
||||||
|
static timespec tp;
|
||||||
|
static time_t now;
|
||||||
|
static uint32_t now_ms, now_us;
|
||||||
|
|
||||||
|
static esp8266::polledTimeout::periodicMs showTimeNow(60000);
|
||||||
|
static int time_machine_days = 0; // 0 = now
|
||||||
|
static bool time_machine_running = false;
|
||||||
|
|
||||||
|
// OPTIONAL: change SNTP startup delay
|
||||||
|
// a weak function is already defined and returns 0 (RFC violation)
|
||||||
|
// it can be redefined:
|
||||||
|
//uint32_t sntp_startup_delay_MS_rfc_not_less_than_60000 ()
|
||||||
|
//{
|
||||||
|
// //info_sntp_startup_delay_MS_rfc_not_less_than_60000_has_been_called = true;
|
||||||
|
// return 60000; // 60s (or lwIP's original default: (random() % 5000))
|
||||||
|
//}
|
||||||
|
|
||||||
|
// OPTIONAL: change SNTP update delay
|
||||||
|
// a weak function is already defined and returns 1 hour
|
||||||
|
// it can be redefined:
|
||||||
|
//uint32_t sntp_update_delay_MS_rfc_not_less_than_15000 ()
|
||||||
|
//{
|
||||||
|
// //info_sntp_update_delay_MS_rfc_not_less_than_15000_has_been_called = true;
|
||||||
|
// return 15000; // 15s
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
void showTime() {
|
||||||
|
gettimeofday(&tv, nullptr);
|
||||||
|
clock_gettime(0, &tp);
|
||||||
|
now = time(nullptr);
|
||||||
|
now_ms = millis();
|
||||||
|
now_us = micros();
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
printTm("localtime:", localtime(&now));
|
||||||
|
Serial.println();
|
||||||
|
printTm("gmtime: ", gmtime(&now));
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
// time from boot
|
||||||
|
Serial.print("clock: ");
|
||||||
|
Serial.print((uint32_t)tp.tv_sec);
|
||||||
|
Serial.print("s / ");
|
||||||
|
Serial.print((uint32_t)tp.tv_nsec);
|
||||||
|
Serial.println("ns");
|
||||||
|
|
||||||
|
// time from boot
|
||||||
|
Serial.print("millis: ");
|
||||||
|
Serial.println(now_ms);
|
||||||
|
Serial.print("micros: ");
|
||||||
|
Serial.println(now_us);
|
||||||
|
|
||||||
|
// EPOCH+tz+dst
|
||||||
|
Serial.print("gtod: ");
|
||||||
|
Serial.print((uint32_t)tv.tv_sec);
|
||||||
|
Serial.print("s / ");
|
||||||
|
Serial.print((uint32_t)tv.tv_usec);
|
||||||
|
Serial.println("us");
|
||||||
|
|
||||||
|
// EPOCH+tz+dst
|
||||||
|
Serial.print("time: ");
|
||||||
|
Serial.println((uint32_t)now);
|
||||||
|
|
||||||
|
// timezone and demo in the future
|
||||||
|
Serial.printf("timezone: %s\n", MYTZ);
|
||||||
|
|
||||||
|
// human readable
|
||||||
|
Serial.print("ctime: ");
|
||||||
|
Serial.print(ctime(&now));
|
||||||
|
|
||||||
|
#if LWIP_VERSION_MAJOR > 1
|
||||||
|
// LwIP v2 is able to list more details about the currently configured SNTP servers
|
||||||
|
for (int i = 0; i < SNTP_MAX_SERVERS; i++) {
|
||||||
|
IPAddress sntp = *sntp_getserver(i);
|
||||||
|
const char* name = sntp_getservername(i);
|
||||||
|
if (sntp.isSet()) {
|
||||||
|
Serial.printf("sntp%d: ", i);
|
||||||
|
if (name) {
|
||||||
|
Serial.printf("%s (%s) ", name, sntp.toString().c_str());
|
||||||
|
} else {
|
||||||
|
Serial.printf("%s ", sntp.toString().c_str());
|
||||||
|
}
|
||||||
|
Serial.printf("IPv6: %s Reachability: %o\n",
|
||||||
|
sntp.isV6() ? "Yes" : "No",
|
||||||
|
sntp_getreachability(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define PTM(w) \
|
#define PTM(w) \
|
||||||
Serial.print(":" #w "="); \
|
Serial.print(" " #w "="); \
|
||||||
Serial.print(tm->tm_##w);
|
Serial.print(tm->tm_##w);
|
||||||
|
|
||||||
void printTm(const char* what, const tm* tm) {
|
void printTm(const char* what, const tm* tm) {
|
||||||
@ -84,61 +164,74 @@ void printTm(const char* what, const tm* tm) {
|
|||||||
PTM(hour); PTM(min); PTM(sec);
|
PTM(hour); PTM(min); PTM(sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
timeval tv;
|
void time_is_set_scheduled() {
|
||||||
timespec tp;
|
// everything is allowed in this function
|
||||||
time_t now;
|
|
||||||
uint32_t now_ms, now_us;
|
|
||||||
|
|
||||||
void loop() {
|
if (time_machine_days == 0) {
|
||||||
|
time_machine_running = !time_machine_running;
|
||||||
gettimeofday(&tv, nullptr);
|
|
||||||
clock_gettime(0, &tp);
|
|
||||||
now = time(nullptr);
|
|
||||||
now_ms = millis();
|
|
||||||
now_us = micros();
|
|
||||||
|
|
||||||
// localtime / gmtime every second change
|
|
||||||
static time_t lastv = 0;
|
|
||||||
if (lastv != tv.tv_sec) {
|
|
||||||
lastv = tv.tv_sec;
|
|
||||||
Serial.println();
|
|
||||||
printTm("localtime", localtime(&now));
|
|
||||||
Serial.println();
|
|
||||||
printTm("gmtime ", gmtime(&now));
|
|
||||||
Serial.println();
|
|
||||||
Serial.println();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// time from boot
|
// time machine demo
|
||||||
Serial.print("clock:");
|
if (time_machine_running) {
|
||||||
Serial.print((uint32_t)tp.tv_sec);
|
if (time_machine_days == 0)
|
||||||
Serial.print("/");
|
Serial.printf("---- settimeofday() has been called - possibly from SNTP\n"
|
||||||
Serial.print((uint32_t)tp.tv_nsec);
|
" (starting time machine demo to show libc's automatic DST handling)\n\n");
|
||||||
Serial.print("ns");
|
now = time(nullptr);
|
||||||
|
const tm* tm = localtime(&now);
|
||||||
// time from boot
|
Serial.printf("future=%3ddays: DST=%s - ",
|
||||||
Serial.print(" millis:");
|
time_machine_days,
|
||||||
Serial.print(now_ms);
|
tm->tm_isdst ? "true " : "false");
|
||||||
Serial.print(" micros:");
|
|
||||||
Serial.print(now_us);
|
|
||||||
|
|
||||||
// EPOCH+tz+dst
|
|
||||||
Serial.print(" gtod:");
|
|
||||||
Serial.print((uint32_t)tv.tv_sec);
|
|
||||||
Serial.print("/");
|
|
||||||
Serial.print((uint32_t)tv.tv_usec);
|
|
||||||
Serial.print("us");
|
|
||||||
|
|
||||||
// EPOCH+tz+dst
|
|
||||||
Serial.print(" time:");
|
|
||||||
Serial.print((uint32_t)now);
|
|
||||||
|
|
||||||
// human readable
|
|
||||||
Serial.print(" ctime:(UTC+");
|
|
||||||
Serial.print((uint32_t)(TZ * 60 + DST_MN));
|
|
||||||
Serial.print("mn)");
|
|
||||||
Serial.print(ctime(&now));
|
Serial.print(ctime(&now));
|
||||||
|
gettimeofday(&tv, nullptr);
|
||||||
// simple drifting loop
|
constexpr int days = 30;
|
||||||
delay(100);
|
time_machine_days += days;
|
||||||
|
if (time_machine_days > 360) {
|
||||||
|
tv.tv_sec -= (time_machine_days - days) * 60 * 60 * 24;
|
||||||
|
time_machine_days = 0;
|
||||||
|
} else {
|
||||||
|
tv.tv_sec += days * 60 * 60 * 24;
|
||||||
|
}
|
||||||
|
settimeofday(&tv, nullptr);
|
||||||
|
} else {
|
||||||
|
showTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("\nStarting...\n");
|
||||||
|
|
||||||
|
// setup RTC time
|
||||||
|
// it will be used until NTP server will send us real current time
|
||||||
|
time_t rtc = RTC_UTC_TEST;
|
||||||
|
timeval tv = { rtc, 0 };
|
||||||
|
timezone tz = { 0, 0 };
|
||||||
|
settimeofday(&tv, &tz);
|
||||||
|
|
||||||
|
// install callback - called when settimeofday is called (by SNTP or us)
|
||||||
|
// once enabled (by DHCP), SNTP is updated every hour
|
||||||
|
settimeofday_cb(time_is_set_scheduled);
|
||||||
|
|
||||||
|
// NTP servers may be overriden by your DHCP server for a more local one
|
||||||
|
// (see below)
|
||||||
|
configTime(MYTZ, "pool.ntp.org");
|
||||||
|
|
||||||
|
// OPTIONAL: disable obtaining SNTP servers from DHCP
|
||||||
|
//sntp_servermode_dhcp(0); // 0: disable obtaining SNTP servers from DHCP (enabled by default)
|
||||||
|
|
||||||
|
// start network
|
||||||
|
WiFi.persistent(false);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(STASSID, STAPSK);
|
||||||
|
|
||||||
|
// don't wait for network, observe time changing
|
||||||
|
// when NTP timestamp is received
|
||||||
|
Serial.printf("Time is currently set by a constant:\n");
|
||||||
|
showTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (showTimeNow) {
|
||||||
|
showTime();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,4 +7,4 @@ paragraph=
|
|||||||
category=Other
|
category=Other
|
||||||
url=
|
url=
|
||||||
architectures=esp8266
|
architectures=esp8266
|
||||||
dot_a_linkage=true
|
dot_a_linkage=false
|
||||||
|
@ -107,18 +107,23 @@ Here is an overview of the release process. See the section below for detailed i
|
|||||||
|
|
||||||
The following points assume work in a direct clone of the repository, and not in a personal fork.
|
The following points assume work in a direct clone of the repository, and not in a personal fork.
|
||||||
|
|
||||||
2. Update `version` to the release in platform.txt and commit. E.g. `2.5.0`. Make a PR, wait for Travis CI, and merge.
|
2. Make a PR with the following, wait for Travis CI, and merge.
|
||||||
|
|
||||||
3. Tag the latest commit on the master branch. In this project, tags have form `X.Y.Z`, e.g. `2.4.0`, or `X.Y.Z-betaN` for release candiate versions. Notice that there's no `v`at the beginning of the tag. Tags must be annotated, not lightweight tags. To create a tag, use git command (assuming that the master branch is checked out):
|
* platform.txt: update `version` to the release E.g. `3.0.0`,
|
||||||
|
|
||||||
|
* `cores/esp8266/TZ.h`: import the latest database with the following shell command:\
|
||||||
|
`$ cd tools; sh TZupdate.sh`.
|
||||||
|
|
||||||
|
3. Tag the latest commit on the master branch. In this project, tags have form `X.Y.Z`, e.g. `3.0.0`, or `X.Y.Z-betaN` for release candiate versions. Notice that there's no `v`at the beginning of the tag. Tags must be annotated, not lightweight tags. To create a tag, use git command (assuming that the master branch is checked out):
|
||||||
|
|
||||||
```
|
```
|
||||||
git tag -a -m "Release 2.5.0" 2.5.0
|
git tag -a -m "Release 3.0.0" 3.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
then push the tag created in step 3 to esp8266/Arduino Github repository:
|
then push the tag created in step 3 to esp8266/Arduino Github repository:
|
||||||
|
|
||||||
```
|
```
|
||||||
git push origin 2.5.0
|
git push origin 3.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
4. In case something goes wrong, release can be canceled at any time:
|
4. In case something goes wrong, release can be canceled at any time:
|
||||||
@ -141,7 +146,7 @@ The following points assume work in a direct clone of the repository, and not in
|
|||||||
|
|
||||||
11. Create a commit to the master branch, updating:
|
11. Create a commit to the master branch, updating:
|
||||||
|
|
||||||
* The version in platform.txt file. This should correspond to the version of the *next* milestone, plus `-dev` suffix. E.g. `2.5.0-dev`.
|
* The version in platform.txt file. This should correspond to the version of the *next* milestone, plus `-dev` suffix. E.g. `3.1.0-dev`.
|
||||||
|
|
||||||
* In main README.md:
|
* In main README.md:
|
||||||
|
|
||||||
|
@ -3,25 +3,37 @@
|
|||||||
|
|
||||||
#set -x
|
#set -x
|
||||||
|
|
||||||
# Extract next version from platform.txt
|
ver=`git describe --tag`
|
||||||
next=`sed -n -E 's/version=([0-9.]+)/\1/p' ../platform.txt`
|
visiblever=$ver
|
||||||
|
if [ "$ver" = 0.0.1 ]; then
|
||||||
|
|
||||||
# Figure out how will the package be called
|
git tag -d 0.0.1
|
||||||
ver=`git describe --exact-match`
|
ver=`git describe --tag HEAD`
|
||||||
if [ $? -ne 0 ]; then
|
plain_ver=$ver
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
# Extract next version from platform.txt
|
||||||
|
next=`sed -n -E 's/version=([0-9.]+)/\1/p' ../platform.txt`
|
||||||
|
|
||||||
|
# Figure out how will the package be called
|
||||||
|
ver=`git describe --exact-match`
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
# not tagged version; generate nightly package
|
# not tagged version; generate nightly package
|
||||||
date_str=`date +"%Y%m%d"`
|
date_str=`date +"%Y%m%d"`
|
||||||
is_nightly=1
|
is_nightly=1
|
||||||
plain_ver="${next}-nightly"
|
plain_ver="${next}-nightly"
|
||||||
ver="${plain_ver}+${date_str}"
|
ver="${plain_ver}+${date_str}"
|
||||||
else
|
else
|
||||||
plain_ver=$ver
|
plain_ver=$ver
|
||||||
|
fi
|
||||||
|
visiblever=$ver
|
||||||
fi
|
fi
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
package_name=esp8266-$ver
|
package_name=esp8266-$visiblever
|
||||||
echo "Version: $ver"
|
echo "Version: $visiblever ($ver)"
|
||||||
echo "Package name: $package_name"
|
echo "Package name: $package_name"
|
||||||
|
|
||||||
# Set REMOTE_URL environment variable to the address where the package will be
|
# Set REMOTE_URL environment variable to the address where the package will be
|
||||||
@ -34,7 +46,7 @@ echo "Remote: $REMOTE_URL"
|
|||||||
|
|
||||||
if [ -z "$PKG_URL" ]; then
|
if [ -z "$PKG_URL" ]; then
|
||||||
if [ -z "$PKG_URL_PREFIX" ]; then
|
if [ -z "$PKG_URL_PREFIX" ]; then
|
||||||
PKG_URL_PREFIX="$REMOTE_URL/versions/$ver"
|
PKG_URL_PREFIX="$REMOTE_URL/versions/$visiblever"
|
||||||
fi
|
fi
|
||||||
PKG_URL="$PKG_URL_PREFIX/$package_name.zip"
|
PKG_URL="$PKG_URL_PREFIX/$package_name.zip"
|
||||||
fi
|
fi
|
||||||
@ -43,9 +55,9 @@ echo "Docs: $DOC_URL"
|
|||||||
|
|
||||||
pushd ..
|
pushd ..
|
||||||
# Create directory for the package
|
# Create directory for the package
|
||||||
outdir=package/versions/$ver/$package_name
|
outdir=package/versions/$visiblever/$package_name
|
||||||
srcdir=$PWD
|
srcdir=$PWD
|
||||||
rm -rf package/versions/$ver
|
rm -rf package/versions/$visiblever
|
||||||
mkdir -p $outdir
|
mkdir -p $outdir
|
||||||
|
|
||||||
# Some files should be excluded from the package
|
# Some files should be excluded from the package
|
||||||
@ -96,7 +108,7 @@ echo \#define ARDUINO_ESP8266_RELEASE_$ver_define >>$outdir/cores/esp8266/core_v
|
|||||||
echo \#define ARDUINO_ESP8266_RELEASE \"$ver_define\" >>$outdir/cores/esp8266/core_version.h
|
echo \#define ARDUINO_ESP8266_RELEASE \"$ver_define\" >>$outdir/cores/esp8266/core_version.h
|
||||||
|
|
||||||
# Zip the package
|
# Zip the package
|
||||||
pushd package/versions/$ver
|
pushd package/versions/$visiblever
|
||||||
echo "Making $package_name.zip"
|
echo "Making $package_name.zip"
|
||||||
zip -qr $package_name.zip $package_name
|
zip -qr $package_name.zip $package_name
|
||||||
rm -rf $package_name
|
rm -rf $package_name
|
||||||
@ -109,7 +121,7 @@ echo SHA-256: $sha
|
|||||||
|
|
||||||
echo "Making package_esp8266com_index.json"
|
echo "Making package_esp8266com_index.json"
|
||||||
|
|
||||||
jq_arg=".packages[0].platforms[0].version = \"$ver\" | \
|
jq_arg=".packages[0].platforms[0].version = \"$visiblever\" | \
|
||||||
.packages[0].platforms[0].url = \"$PKG_URL\" |\
|
.packages[0].platforms[0].url = \"$PKG_URL\" |\
|
||||||
.packages[0].platforms[0].archiveFileName = \"$package_name.zip\""
|
.packages[0].platforms[0].archiveFileName = \"$package_name.zip\""
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ build.stdcpp_level=-std=gnu++11
|
|||||||
|
|
||||||
build.float=-u _printf_float -u _scanf_float
|
build.float=-u _printf_float -u _scanf_float
|
||||||
build.led=
|
build.led=
|
||||||
build.sdk=NONOSDK22y
|
build.sdk=NONOSDK22x_190703
|
||||||
|
|
||||||
compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/
|
compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/
|
||||||
compiler.sdk.path={runtime.platform.path}/tools/sdk
|
compiler.sdk.path={runtime.platform.path}/tools/sdk
|
||||||
@ -137,7 +137,8 @@ tools.esptool.cmd={runtime.platform.path}/tools/python3/python3
|
|||||||
tools.esptool.network_cmd={runtime.platform.path}/tools/python3/python3
|
tools.esptool.network_cmd={runtime.platform.path}/tools/python3/python3
|
||||||
|
|
||||||
tools.esptool.upload.protocol=esp
|
tools.esptool.upload.protocol=esp
|
||||||
tools.esptool.upload.params.verbose=--trace
|
# esptool.py --trace option is a debug option, not a verbose option
|
||||||
|
tools.esptool.upload.params.verbose=
|
||||||
tools.esptool.upload.params.quiet=
|
tools.esptool.upload.params.quiet=
|
||||||
|
|
||||||
# First, potentially perform an erase or nothing
|
# First, potentially perform an erase or nothing
|
||||||
|
@ -9,6 +9,7 @@ ${org}/../restyle.sh
|
|||||||
|
|
||||||
# Revert changes which astyle might have done to the submodules,
|
# Revert changes which astyle might have done to the submodules,
|
||||||
# as we don't want to fail the build because of the 3rd party libraries
|
# as we don't want to fail the build because of the 3rd party libraries
|
||||||
git submodule foreach --recursive git reset --hard
|
git --version || true
|
||||||
|
git submodule foreach --recursive 'git reset --hard'
|
||||||
|
|
||||||
git diff --exit-code -- $TRAVIS_BUILD_DIR/libraries
|
git diff --exit-code -- $TRAVIS_BUILD_DIR/libraries
|
||||||
|
@ -153,19 +153,31 @@ function install_libraries()
|
|||||||
|
|
||||||
function install_ide()
|
function install_ide()
|
||||||
{
|
{
|
||||||
|
#local idever='nightly'
|
||||||
|
#local ideurl='https://www.arduino.cc/download.php?f=/arduino-nightly'
|
||||||
|
|
||||||
|
local idever='1.8.10'
|
||||||
|
local ideurl="https://downloads.arduino.cc/arduino-$idever"
|
||||||
|
|
||||||
|
echo "using Arduino IDE distribution ${idever}"
|
||||||
|
|
||||||
local ide_path=$1
|
local ide_path=$1
|
||||||
local core_path=$2
|
local core_path=$2
|
||||||
local debug=$3
|
local debug=$3
|
||||||
if [ "$WINDOWS" = "1" ]; then
|
if [ "$WINDOWS" = "1" ]; then
|
||||||
# Acquire needed packages from Windows package manager
|
# Acquire needed packages from Windows package manager
|
||||||
choco install --no-progress python3
|
choco install --no-progress python3 >& pylog.txt
|
||||||
export PATH="/c/Python37:$PATH" # Ensure it's live from now on...
|
# Parse the python instrall dir from the output log. Sorry, can't set it via choco on the free version
|
||||||
cp /c/Python37/python.exe /c/Python37/python3.exe
|
PYDIR=$(cat pylog.txt | grep "^Installed to:" | cut -f2 -d"'" | sed 's/C:\\/\/c\//')
|
||||||
|
echo "Detected python3 install dir: $PYDIR"
|
||||||
|
export PATH="$PYDIR:$PATH" # Ensure it's live from now on...
|
||||||
|
cp "$PYDIR/python.exe" "$PYDIR/python3.exe"
|
||||||
choco install --no-progress unzip
|
choco install --no-progress unzip
|
||||||
choco install --no-progress sed
|
choco install --no-progress sed
|
||||||
#choco install --no-progress golang
|
#choco install --no-progress golang
|
||||||
test -r arduino-nightly-windows.zip || wget -nv -O arduino-nightly-windows.zip https://www.arduino.cc/download.php?f=/arduino-nightly-windows.zip
|
test -r arduino-windows.zip || wget -nv -O arduino-windows.zip "${ideurl}-windows.zip"
|
||||||
unzip -q arduino-nightly-windows.zip
|
unzip -q arduino-windows.zip
|
||||||
|
mv arduino-${idever} arduino-distrib
|
||||||
elif [ "$MACOSX" = "1" ]; then
|
elif [ "$MACOSX" = "1" ]; then
|
||||||
# MACOS only has next-to-obsolete Python2 installed. Install Python 3 from python.org
|
# MACOS only has next-to-obsolete Python2 installed. Install Python 3 from python.org
|
||||||
wget https://www.python.org/ftp/python/3.7.4/python-3.7.4-macosx10.9.pkg
|
wget https://www.python.org/ftp/python/3.7.4/python-3.7.4-macosx10.9.pkg
|
||||||
@ -173,15 +185,17 @@ function install_ide()
|
|||||||
# Install the Python3 certificates, because SSL connections fail w/o them and of course they aren't installed by default.
|
# Install the Python3 certificates, because SSL connections fail w/o them and of course they aren't installed by default.
|
||||||
( cd "/Applications/Python 3.7/" && sudo "./Install Certificates.command" )
|
( cd "/Applications/Python 3.7/" && sudo "./Install Certificates.command" )
|
||||||
# Hack to place arduino-builder in the same spot as sane OSes
|
# Hack to place arduino-builder in the same spot as sane OSes
|
||||||
test -r arduino.zip || wget -O arduino.zip https://downloads.arduino.cc/arduino-nightly-macosx.zip
|
test -r arduino-macos.zip || wget -O arduino-macos.zip "${ideurl}-macosx.zip"
|
||||||
unzip -q arduino.zip
|
unzip -q arduino-macos.zip
|
||||||
mv Arduino.app arduino-nightly
|
mv Arduino.app arduino-distrib
|
||||||
mv arduino-nightly/Contents/Java/* arduino-nightly/.
|
mv arduino-distrib/Contents/Java/* arduino-distrib/.
|
||||||
else
|
else
|
||||||
test -r arduino.tar.xz || wget -O arduino.tar.xz https://www.arduino.cc/download.php?f=/arduino-nightly-linux64.tar.xz
|
#test -r arduino.tar.xz || wget -O arduino.tar.xz https://www.arduino.cc/download.php?f=/arduino-nightly-linux64.tar.xz
|
||||||
tar xf arduino.tar.xz
|
test -r arduino-linux.tar.xz || wget -O arduino-linux.tar.xz "${ideurl}-linux64.tar.xz"
|
||||||
|
tar xf arduino-linux.tar.xz
|
||||||
|
mv arduino-${idever} arduino-distrib
|
||||||
fi
|
fi
|
||||||
mv arduino-nightly $ide_path
|
mv arduino-distrib $ide_path
|
||||||
cd $ide_path/hardware
|
cd $ide_path/hardware
|
||||||
mkdir esp8266com
|
mkdir esp8266com
|
||||||
cd esp8266com
|
cd esp8266com
|
||||||
|
@ -11,9 +11,9 @@ UPLOAD_PORT ?= $(shell ls /dev/tty* | grep -m 1 -i USB)
|
|||||||
UPLOAD_BAUD ?= 460800
|
UPLOAD_BAUD ?= 460800
|
||||||
UPLOAD_BOARD ?= nodemcu
|
UPLOAD_BOARD ?= nodemcu
|
||||||
BS_DIR ?= libraries/BSTest
|
BS_DIR ?= libraries/BSTest
|
||||||
DEBUG_LEVEL ?= DebugLevel=None____
|
DEBUG_LEVEL ?= lvl=None____
|
||||||
#FQBN ?= esp8266com:esp8266:generic:CpuFrequency=80,FlashFreq=40,FlashMode=dio,UploadSpeed=115200,FlashSize=4M1M,LwIPVariant=v2mss536,ResetMethod=none,Debug=Serial,$(DEBUG_LEVEL)
|
#FQBN ?= esp8266com:esp8266:generic:CpuFrequency=80,FlashFreq=40,FlashMode=dio,UploadSpeed=115200,FlashSize=4M1M,LwIPVariant=v2mss536,ResetMethod=none,Debug=Serial,$(DEBUG_LEVEL)
|
||||||
FQBN ?= esp8266com:esp8266:generic:xtal=80,FlashFreq=40,FlashMode=dio,baud=115200,eesz=4M1M,ip=lm2f,ResetMethod=none,dbg=Serial,$(DEBUG_LEVEL)
|
FQBN ?= esp8266com:esp8266:generic:xtal=160,FlashFreq=40,FlashMode=dio,baud=115200,eesz=4M1M,ip=lm2f,ResetMethod=none,dbg=Serial,$(DEBUG_LEVEL)
|
||||||
BUILD_TOOL := $(ARDUINO_IDE_PATH)/arduino-builder
|
BUILD_TOOL := $(ARDUINO_IDE_PATH)/arduino-builder
|
||||||
TEST_CONFIG := test_env.cfg
|
TEST_CONFIG := test_env.cfg
|
||||||
TEST_REPORT_XML := test_report.xml
|
TEST_REPORT_XML := test_report.xml
|
||||||
@ -104,7 +104,7 @@ ifneq ("$(NO_RUN)","1")
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
$(TEST_REPORT_XML): $(HARDWARE_DIR) virtualenv
|
$(TEST_REPORT_XML): $(HARDWARE_DIR) virtualenv
|
||||||
$(SILENT)$(BS_DIR)/virtualenv/bin/xunitmerge $(shell find $(BUILD_DIR) -name 'test_result.xml' | xargs echo) $(TEST_REPORT_XML)
|
$(SILENT)$(BS_DIR)/xunitmerge $(shell find $(BUILD_DIR) -name 'test_result.xml' | xargs echo) $(TEST_REPORT_XML)
|
||||||
|
|
||||||
$(TEST_REPORT_HTML): $(TEST_REPORT_XML) | virtualenv
|
$(TEST_REPORT_HTML): $(TEST_REPORT_XML) | virtualenv
|
||||||
$(SILENT)$(BS_DIR)/virtualenv/bin/junit2html $< $@
|
$(SILENT)$(BS_DIR)/virtualenv/bin/junit2html $< $@
|
||||||
@ -125,6 +125,7 @@ virtualenv:
|
|||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
rm -rf $(HARDWARE_DIR)
|
rm -rf $(HARDWARE_DIR)
|
||||||
|
rm -rf $(BS_DIR)/virtualenv
|
||||||
rm -f $(TEST_REPORT_HTML) $(TEST_REPORT_XML)
|
rm -f $(TEST_REPORT_HTML) $(TEST_REPORT_XML)
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
|
@ -3,6 +3,5 @@ junit-xml
|
|||||||
MarkupSafe
|
MarkupSafe
|
||||||
pexpect
|
pexpect
|
||||||
pyserial
|
pyserial
|
||||||
xunitmerge
|
|
||||||
junit2html
|
junit2html
|
||||||
poster
|
poster3
|
||||||
|
@ -236,10 +236,10 @@ ser = None
|
|||||||
def spawn_port(port_name, baudrate=115200):
|
def spawn_port(port_name, baudrate=115200):
|
||||||
global ser
|
global ser
|
||||||
ser = serial.serial_for_url(port_name, baudrate=baudrate)
|
ser = serial.serial_for_url(port_name, baudrate=baudrate)
|
||||||
return fdpexpect.fdspawn(ser, 'wb', timeout=0)
|
return fdpexpect.fdspawn(ser, 'wb', timeout=0, encoding='cp437')
|
||||||
|
|
||||||
def spawn_exec(name):
|
def spawn_exec(name):
|
||||||
return pexpect.spawn(name, timeout=0)
|
return pexpect.spawn(name, timeout=0, encoding='cp437')
|
||||||
|
|
||||||
def run_tests(spawn, name, mocks, env_vars):
|
def run_tests(spawn, name, mocks, env_vars):
|
||||||
tw = BSTestRunner(spawn, name, mocks, env_vars)
|
tw = BSTestRunner(spawn, name, mocks, env_vars)
|
||||||
|
154
tests/device/libraries/BSTest/xmerge.py
Normal file
154
tests/device/libraries/BSTest/xmerge.py
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
# Cloned from https://github.com/miki725/xunitmerge
|
||||||
|
# to fix a Python3 error.
|
||||||
|
#
|
||||||
|
# xunitmerge is MIT licensed by Miroslav Shubernetskiy https://github.com/miki725
|
||||||
|
#
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from xml.etree import ElementTree as etree
|
||||||
|
from xml.sax.saxutils import quoteattr
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
|
CNAME_TAGS = ('system-out', 'skipped', 'error', 'failure')
|
||||||
|
CNAME_PATTERN = '<![CDATA[{}]]>'
|
||||||
|
TAG_PATTERN = '<{tag}{attrs}>{text}</{tag}>'
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def patch_etree_cname(etree):
|
||||||
|
"""
|
||||||
|
Patch ElementTree's _serialize_xml function so that it will
|
||||||
|
write text as CDATA tag for tags tags defined in CNAME_TAGS.
|
||||||
|
|
||||||
|
>>> import re
|
||||||
|
>>> from xml.etree import ElementTree
|
||||||
|
>>> xml_string = '''
|
||||||
|
... <testsuite name="nosetests" tests="1" errors="0" failures="0" skip="0">
|
||||||
|
... <testcase classname="some.class.Foo" name="test_system_out" time="0.001">
|
||||||
|
... <system-out>Some output here</system-out>
|
||||||
|
... </testcase>
|
||||||
|
... <testcase classname="some.class.Foo" name="test_skipped" time="0.001">
|
||||||
|
... <skipped type="unittest.case.SkipTest" message="Skipped">Skipped</skipped>
|
||||||
|
... </testcase>
|
||||||
|
... <testcase classname="some.class.Foo" name="test_error" time="0.001">
|
||||||
|
... <error type="KeyError" message="Error here">Error here</error>
|
||||||
|
... </testcase>
|
||||||
|
... <testcase classname="some.class.Foo" name="test_failure" time="0.001">
|
||||||
|
... <failure type="AssertionError" message="Failure here">Failure here</failure>
|
||||||
|
... </testcase>
|
||||||
|
... </testsuite>
|
||||||
|
... '''
|
||||||
|
>>> tree = ElementTree.fromstring(xml_string)
|
||||||
|
>>> with patch_etree_cname(ElementTree):
|
||||||
|
... saved = str(ElementTree.tostring(tree))
|
||||||
|
>>> systemout = re.findall(r'(<system-out>.*?</system-out>)', saved)[0]
|
||||||
|
>>> print(systemout)
|
||||||
|
<system-out><![CDATA[Some output here]]></system-out>
|
||||||
|
>>> skipped = re.findall(r'(<skipped.*?</skipped>)', saved)[0]
|
||||||
|
>>> print(skipped)
|
||||||
|
<skipped message="Skipped" type="unittest.case.SkipTest"><![CDATA[Skipped]]></skipped>
|
||||||
|
>>> error = re.findall(r'(<error.*?</error>)', saved)[0]
|
||||||
|
>>> print(error)
|
||||||
|
<error message="Error here" type="KeyError"><![CDATA[Error here]]></error>
|
||||||
|
>>> failure = re.findall(r'(<failure.*?</failure>)', saved)[0]
|
||||||
|
>>> print(failure)
|
||||||
|
<failure message="Failure here" type="AssertionError"><![CDATA[Failure here]]></failure>
|
||||||
|
"""
|
||||||
|
original_serialize = etree._serialize_xml
|
||||||
|
|
||||||
|
def _serialize_xml(write, elem, *args, **kwargs):
|
||||||
|
if elem.tag in CNAME_TAGS:
|
||||||
|
attrs = ' '.join(
|
||||||
|
['{}={}'.format(k, quoteattr(v))
|
||||||
|
for k, v in sorted(elem.attrib.items())]
|
||||||
|
)
|
||||||
|
attrs = ' ' + attrs if attrs else ''
|
||||||
|
text = CNAME_PATTERN.format(elem.text)
|
||||||
|
write(TAG_PATTERN.format(
|
||||||
|
tag=elem.tag,
|
||||||
|
attrs=attrs,
|
||||||
|
text=text
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
original_serialize(write, elem, *args, **kwargs)
|
||||||
|
|
||||||
|
etree._serialize_xml = etree._serialize['xml'] = _serialize_xml
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
etree._serialize_xml = etree._serialize['xml'] = original_serialize
|
||||||
|
|
||||||
|
|
||||||
|
def merge_trees(*trees):
|
||||||
|
"""
|
||||||
|
Merge all given XUnit ElementTrees into a single ElementTree.
|
||||||
|
This combines all of the children test-cases and also merges
|
||||||
|
all of the metadata of how many tests were executed, etc.
|
||||||
|
"""
|
||||||
|
first_tree = trees[0]
|
||||||
|
first_root = first_tree.getroot()
|
||||||
|
|
||||||
|
if len(trees) == 0:
|
||||||
|
return first_tree
|
||||||
|
|
||||||
|
for tree in trees[1:]:
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
# append children elements (testcases)
|
||||||
|
first_root.extend(root.getchildren())
|
||||||
|
|
||||||
|
# combine root attributes which stores the number
|
||||||
|
# of executed tests, skipped tests, etc
|
||||||
|
for key, value in first_root.attrib.items():
|
||||||
|
if not value.isdigit():
|
||||||
|
continue
|
||||||
|
combined = six.text_type(int(value) + int(root.attrib.get(key, '0')))
|
||||||
|
first_root.set(key, combined)
|
||||||
|
|
||||||
|
return first_tree
|
||||||
|
|
||||||
|
|
||||||
|
def merge_xunit(files, output, callback=None):
|
||||||
|
"""
|
||||||
|
Merge the given xunit xml files into a single output xml file.
|
||||||
|
|
||||||
|
If callback is not None, it will be called with the merged ElementTree
|
||||||
|
before the output file is written (useful for applying other fixes to
|
||||||
|
the merged file). This can either modify the element tree in place (and
|
||||||
|
return None) or return a completely new ElementTree to be written.
|
||||||
|
"""
|
||||||
|
trees = []
|
||||||
|
|
||||||
|
for f in files:
|
||||||
|
trees.append(etree.parse(f))
|
||||||
|
|
||||||
|
merged = merge_trees(*trees)
|
||||||
|
|
||||||
|
if callback is not None:
|
||||||
|
result = callback(merged)
|
||||||
|
if result is not None:
|
||||||
|
merged = result
|
||||||
|
|
||||||
|
with patch_etree_cname(etree):
|
||||||
|
merged.write(output, encoding='utf-8', xml_declaration=True)
|
50
tests/device/libraries/BSTest/xunitmerge
Executable file
50
tests/device/libraries/BSTest/xunitmerge
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Cloned from https://github.com/miki725/xunitmerge
|
||||||
|
# to fix a Python3 error.
|
||||||
|
#
|
||||||
|
# xunitmerge is MIT licensed by Miroslav Shubernetskiy https://github.com/miki725
|
||||||
|
#
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from xmerge import merge_xunit
|
||||||
|
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Utility for merging multiple XUnit xml reports '
|
||||||
|
'into a single xml report.',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'report',
|
||||||
|
nargs='+',
|
||||||
|
type=argparse.FileType('r'),
|
||||||
|
help='Path of XUnit xml report. Multiple can be provided.',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'output',
|
||||||
|
help='Path where merged of XUnit will be saved.',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = parser.parse_args()
|
||||||
|
merge_xunit(args.report, args.output)
|
@ -1,3 +1,5 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from mock_decorators import setup, teardown
|
from mock_decorators import setup, teardown
|
||||||
from flask import Flask, request
|
from flask import Flask, request
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@ -21,7 +23,7 @@ def setup_tcpsrv(e):
|
|||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
for port in range(8266, 8285 + 1):
|
for port in range(8266, 8285 + 1):
|
||||||
try:
|
try:
|
||||||
print >>sys.stderr, 'trying port', port
|
print ('trying port %d' %port, file=sys.stderr)
|
||||||
server_address = ("0.0.0.0", port)
|
server_address = ("0.0.0.0", port)
|
||||||
sock.bind(server_address)
|
sock.bind(server_address)
|
||||||
sock.listen(1)
|
sock.listen(1)
|
||||||
@ -31,17 +33,17 @@ def setup_tcpsrv(e):
|
|||||||
print >>sys.stderr, 'busy'
|
print >>sys.stderr, 'busy'
|
||||||
if not running:
|
if not running:
|
||||||
return
|
return
|
||||||
print >>sys.stderr, 'starting up on %s port %s' % server_address
|
print ('starting up on %s port %s' % server_address, file=sys.stderr)
|
||||||
print >>sys.stderr, 'waiting for connections'
|
print ( 'waiting for connections', file=sys.stderr)
|
||||||
while running:
|
while running:
|
||||||
print >>sys.stderr, 'loop'
|
print ('loop', file=sys.stderr)
|
||||||
readable, writable, errored = select.select([sock], [], [], 1.0)
|
readable, writable, errored = select.select([sock], [], [], 1.0)
|
||||||
if readable:
|
if readable:
|
||||||
connection, client_address = sock.accept()
|
connection, client_address = sock.accept()
|
||||||
try:
|
try:
|
||||||
print >>sys.stderr, 'client connected:', client_address
|
print('client connected: %s' % str(client_address), file=sys.stderr)
|
||||||
finally:
|
finally:
|
||||||
print >>sys.stderr, 'close'
|
print ('close', file=sys.stderr)
|
||||||
connection.shutdown(socket.SHUT_RDWR)
|
connection.shutdown(socket.SHUT_RDWR)
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
@ -54,7 +56,7 @@ def teardown_tcpsrv(e):
|
|||||||
global thread
|
global thread
|
||||||
global running
|
global running
|
||||||
|
|
||||||
print >>sys.stderr, 'closing'
|
print ('closing', file=sys.stderr)
|
||||||
running = False
|
running = False
|
||||||
thread.join()
|
thread.join()
|
||||||
return 0
|
return 0
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from mock_decorators import setup, teardown
|
from mock_decorators import setup, teardown
|
||||||
from flask import Flask, request, redirect
|
from flask import Flask, request, redirect
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import urllib2
|
import urllib
|
||||||
import os
|
import os
|
||||||
import ssl
|
import ssl
|
||||||
import time
|
import time
|
||||||
@ -20,7 +20,7 @@ def setup_http_get(e):
|
|||||||
return 'Server shutting down...'
|
return 'Server shutting down...'
|
||||||
@app.route("/", methods = ['GET', 'POST'])
|
@app.route("/", methods = ['GET', 'POST'])
|
||||||
def root():
|
def root():
|
||||||
print('Got data: ' + request.data);
|
print('Got data: ' + request.data.decode());
|
||||||
return 'hello!!!'
|
return 'hello!!!'
|
||||||
@app.route("/data")
|
@app.route("/data")
|
||||||
def get_data():
|
def get_data():
|
||||||
@ -48,7 +48,7 @@ def setup_http_get(e):
|
|||||||
|
|
||||||
@teardown('HTTP GET & POST requests')
|
@teardown('HTTP GET & POST requests')
|
||||||
def teardown_http_get(e):
|
def teardown_http_get(e):
|
||||||
response = urllib2.urlopen('http://localhost:8088/shutdown')
|
response = urllib.request.urlopen('http://localhost:8088/shutdown')
|
||||||
html = response.read()
|
html = response.read()
|
||||||
time.sleep(1) # avoid address in use error on macOS
|
time.sleep(1) # avoid address in use error on macOS
|
||||||
|
|
||||||
@ -86,6 +86,6 @@ def teardown_http_get(e):
|
|||||||
ctx.check_hostname = False
|
ctx.check_hostname = False
|
||||||
ctx.verify_mode = ssl.CERT_NONE
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
p = os.path.dirname(os.path.abspath(__file__))
|
p = os.path.dirname(os.path.abspath(__file__))
|
||||||
response = urllib2.urlopen('https://localhost:8088/shutdown', context=ctx)
|
response = urllib.request.urlopen('https://localhost:8088/shutdown', context=ctx)
|
||||||
html = response.read()
|
html = response.read()
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from mock_decorators import setup, teardown
|
from mock_decorators import setup, teardown
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from poster.encode import MultipartParam
|
from poster3.encode import MultipartParam
|
||||||
from poster.encode import multipart_encode
|
from poster3.encode import multipart_encode
|
||||||
from poster.streaminghttp import register_openers
|
from poster3.streaminghttp import register_openers
|
||||||
import urllib2
|
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
def http_test(res, url, get=None, post=None):
|
def http_test(res, url, get=None, post=None):
|
||||||
@ -13,8 +12,8 @@ def http_test(res, url, get=None, post=None):
|
|||||||
if get:
|
if get:
|
||||||
url += '?' + urllib.urlencode(get)
|
url += '?' + urllib.urlencode(get)
|
||||||
if post:
|
if post:
|
||||||
post = urllib.urlencode(post)
|
post = urllib.parse.quote(post)
|
||||||
request = urllib2.urlopen(url, post, 2)
|
request = urllib.request.urlopen(url, post, 2)
|
||||||
response = request.read()
|
response = request.read()
|
||||||
except:
|
except:
|
||||||
return 1
|
return 1
|
||||||
@ -60,8 +59,8 @@ def setup_http_upload(e):
|
|||||||
register_openers()
|
register_openers()
|
||||||
p = MultipartParam("file", "0123456789abcdef", "test.txt", "text/plain; charset=utf8")
|
p = MultipartParam("file", "0123456789abcdef", "test.txt", "text/plain; charset=utf8")
|
||||||
datagen, headers = multipart_encode( [("var4", "val with spaces"), p] )
|
datagen, headers = multipart_encode( [("var4", "val with spaces"), p] )
|
||||||
request = urllib2.Request('http://etd.local/upload', datagen, headers)
|
request = urllib.request('http://etd.local/upload', datagen, headers)
|
||||||
response = urllib2.urlopen(request, None, 2).read()
|
response = urllib.request.urlopen(request, None, 2).read()
|
||||||
except:
|
except:
|
||||||
return 1
|
return 1
|
||||||
if response != 'test.txt:16\nvar4 = val with spaces':
|
if response != 'test.txt:16\nvar4 = val with spaces':
|
||||||
|
@ -313,6 +313,15 @@ uart_get_baudrate(uart_t* uart)
|
|||||||
return uart->baud_rate;
|
return uart->baud_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
uart_get_bit_length(const int uart_nr)
|
||||||
|
{
|
||||||
|
uint8_t width = ((uart_nr % 16) >> 2) + 5;
|
||||||
|
uint8_t parity = (uart_nr >> 5) + 1;
|
||||||
|
uint8_t stop = uart_nr % 4;
|
||||||
|
return (width + parity + stop + 1);
|
||||||
|
}
|
||||||
|
|
||||||
uart_t*
|
uart_t*
|
||||||
uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size)
|
uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size)
|
||||||
{
|
{
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
org=$(cd ${0%/*}; pwd)
|
|
||||||
cd ${org}/..
|
|
||||||
pwd
|
|
||||||
test -d cores/esp8266
|
|
||||||
test -d libraries
|
|
||||||
|
|
||||||
# this warning question will be removed after restyle-all.sh is renamed to restyle.sh
|
|
||||||
echo "This is dangerous if you have modified your local repository"
|
|
||||||
echo "type iknowwhatido to continue"
|
|
||||||
read ans
|
|
||||||
test "$ans" = iknowwhatido || exit 1
|
|
||||||
|
|
||||||
for d in cores/esp8266 libraries; do
|
|
||||||
for e in c cpp h; do
|
|
||||||
find $d -name "*.$e" -exec \
|
|
||||||
astyle \
|
|
||||||
--suffix=none \
|
|
||||||
--options=${org}/astyle_core.conf {} \;
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
for d in libraries; do
|
|
||||||
find $d -name "*.ino" -exec \
|
|
||||||
astyle \
|
|
||||||
--suffix=none \
|
|
||||||
--options=${org}/astyle_examples.conf {} \;
|
|
||||||
done
|
|
19
tests/restyle-examples-only.sh
Executable file
19
tests/restyle-examples-only.sh
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
org=$(cd ${0%/*}; pwd)
|
||||||
|
cd ${org}/..
|
||||||
|
pwd
|
||||||
|
test -d cores/esp8266
|
||||||
|
test -d libraries
|
||||||
|
|
||||||
|
# in a near future, restyle-all.sh will be renamed to restyle.sh
|
||||||
|
# and will be checked against CI
|
||||||
|
|
||||||
|
for d in libraries; do
|
||||||
|
find $d -name "*.ino" -exec \
|
||||||
|
astyle \
|
||||||
|
--suffix=none \
|
||||||
|
--options=${org}/astyle_examples.conf {} \;
|
||||||
|
done
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user