diff --git a/cores/esp8266/AddrList.h b/cores/esp8266/AddrList.h index 88bd8122bb..f87540839b 100644 --- a/cores/esp8266/AddrList.h +++ b/cores/esp8266/AddrList.h @@ -1,27 +1,27 @@ /* - AddrList.h - cycle through lwIP netif's ip addresses like a c++ list - Copyright (c) 2018 david gauchard. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + AddrList.h - cycle through lwIP netif's ip addresses like a c++ list + Copyright (c) 2018 david gauchard. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ /* - This class allows to explore all configured IP addresses - in lwIP netifs, with that kind of c++ loop: + This class allows to explore all configured IP addresses + in lwIP netifs, with that kind of c++ loop: - for (auto a: addrList) + for (auto a: addrList) out.printf("IF='%s' index=%d legacy=%d IPv4=%d local=%d hostname='%s' addr= %s\n", a.iface().c_str(), a.ifnumber(), @@ -31,14 +31,14 @@ a.hostname().c_str(), a.addr().toString().c_str()); - This loop: + This loop: while (WiFi.status() != WL_CONNECTED()) { Serial.print('.'); delay(500); } - can be replaced by: + can be replaced by: for (bool configured = false; !configured; ) { for (auto iface: addrList) @@ -48,7 +48,7 @@ delay(500); } - waiting for an IPv6 global address: + waiting for an IPv6 global address: for (bool configured = false; !configured; ) { for (auto iface: addrList) @@ -59,7 +59,7 @@ delay(500); } - waiting for an IPv6 global address, on a specific interface: + waiting for an IPv6 global address, on a specific interface: for (bool configured = false; !configured; ) { for (auto iface: addrList) @@ -94,8 +94,8 @@ namespace AddressListImplementation struct netifWrapper { - netifWrapper (netif* netif) : _netif(netif), _num(-1) {} - netifWrapper (const netifWrapper& o) : _netif(o._netif), _num(o._num) {} + netifWrapper(netif* netif) : _netif(netif), _num(-1) {} + netifWrapper(const netifWrapper& o) : _netif(o._netif), _num(o._num) {} netifWrapper& operator= (const netifWrapper& o) { @@ -110,25 +110,64 @@ struct netifWrapper } // address properties - IPAddress addr () const { return ipFromNetifNum(); } - bool isLegacy () const { return _num == 0; } - bool isLocal () const { return addr().isLocal(); } - bool isV4 () const { return addr().isV4(); } - bool isV6 () const { return !addr().isV4(); } - String toString() const { return addr().toString(); } + IPAddress addr() const + { + return ipFromNetifNum(); + } + bool isLegacy() const + { + return _num == 0; + } + bool isLocal() const + { + return addr().isLocal(); + } + bool isV4() const + { + return addr().isV4(); + } + bool isV6() const + { + return !addr().isV4(); + } + String toString() const + { + return addr().toString(); + } // related to legacy address (_num=0, ipv4) - IPAddress ipv4 () const { return _netif->ip_addr; } - IPAddress netmask () const { return _netif->netmask; } - IPAddress gw () const { return _netif->gw; } + IPAddress ipv4() const + { + return _netif->ip_addr; + } + IPAddress netmask() const + { + return _netif->netmask; + } + IPAddress gw() const + { + return _netif->gw; + } // common to all addresses of this interface - String ifname () const { return String(_netif->name[0]) + _netif->name[1]; } - const char* ifhostname () const { return _netif->hostname?: emptyString.c_str(); } - const char* ifmac () const { return (const char*)_netif->hwaddr; } - int ifnumber () const { return _netif->num; } + String ifname() const + { + return String(_netif->name[0]) + _netif->name[1]; + } + const char* ifhostname() const + { + return _netif->hostname ? : emptyString.c_str(); + } + const char* ifmac() const + { + return (const char*)_netif->hwaddr; + } + int ifnumber() const + { + return _netif->num; + } - const ip_addr_t* ipFromNetifNum () const + const ip_addr_t* ipFromNetifNum() const { #if LWIP_IPV6 return _num ? &_netif->ip6_addr[_num - 1] : &_netif->ip_addr; @@ -150,8 +189,8 @@ struct netifWrapper class AddressListIterator { public: - AddressListIterator (const netifWrapper& o) : netIf(o) {} - AddressListIterator (netif* netif) : netIf(netif) + AddressListIterator(const netifWrapper& o) : netIf(o) {} + AddressListIterator(netif* netif) : netIf(netif) { // This constructor is called with lwIP's global netif_list, or // nullptr. operator++() is designed to loop through _configured_ @@ -160,13 +199,29 @@ class AddressListIterator (void)operator++(); } - const netifWrapper& operator* () const { return netIf; } - const netifWrapper* operator-> () const { return &netIf; } + const netifWrapper& operator* () const + { + return netIf; + } + const netifWrapper* operator-> () const + { + return &netIf; + } - bool operator== (AddressListIterator& o) { return netIf.equal(*o); } - bool operator!= (AddressListIterator& o) { return !netIf.equal(*o); } + bool operator== (AddressListIterator& o) + { + return netIf.equal(*o); + } + bool operator!= (AddressListIterator& o) + { + return !netIf.equal(*o); + } - AddressListIterator& operator= (const AddressListIterator& o) { netIf = o.netIf; return *this; } + AddressListIterator& operator= (const AddressListIterator& o) + { + netIf = o.netIf; + return *this; + } AddressListIterator operator++ (int) { @@ -188,7 +243,9 @@ class AddressListIterator } if (!ip_addr_isany(netIf.ipFromNetifNum())) // found an initialized address + { break; + } } return *this; } @@ -200,15 +257,27 @@ class AddressListIterator class AddressList { public: - using const_iterator = const AddressListIterator; + using const_iterator = const AddressListIterator; - const_iterator begin () const { return const_iterator(netif_list); } - const_iterator end () const { return const_iterator(nullptr); } + const_iterator begin() const + { + return const_iterator(netif_list); + } + const_iterator end() const + { + return const_iterator(nullptr); + } }; -inline AddressList::const_iterator begin (const AddressList& a) { return a.begin(); } -inline AddressList::const_iterator end (const AddressList& a) { return a.end(); } +inline AddressList::const_iterator begin(const AddressList& a) +{ + return a.begin(); +} +inline AddressList::const_iterator end(const AddressList& a) +{ + return a.end(); +} } // AddressListImplementation diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index 34e5fb8fce..4b3b5a1f40 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -1,21 +1,21 @@ /* - Arduino.h - Main include file for the Arduino SDK - Copyright (c) 2005-2013 Arduino Team. All right reserved. + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef Arduino_h #define Arduino_h @@ -86,10 +86,11 @@ extern "C" { #define EXTERNAL 0 //timer dividers -enum TIM_DIV_ENUM { - TIM_DIV1 = 0, //80MHz (80 ticks/us - 104857.588 us max) - TIM_DIV16 = 1, //5MHz (5 ticks/us - 1677721.4 us max) - TIM_DIV256 = 3 //312.5Khz (1 tick = 3.2us - 26843542.4 us max) +enum TIM_DIV_ENUM +{ + TIM_DIV1 = 0, //80MHz (80 ticks/us - 104857.588 us max) + TIM_DIV16 = 1, //5MHz (5 ticks/us - 1677721.4 us max) + TIM_DIV256 = 3 //312.5Khz (1 tick = 3.2us - 26843542.4 us max) }; @@ -295,7 +296,7 @@ long secureRandom(long, long); long map(long, long, long, long, long); extern "C" void configTime(long timezone, int daylightOffset_sec, - const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); + const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); #endif diff --git a/cores/esp8266/Client.h b/cores/esp8266/Client.h index 6cb99a04b6..eb04f71ad0 100644 --- a/cores/esp8266/Client.h +++ b/cores/esp8266/Client.h @@ -1,21 +1,21 @@ /* - Client.h - Base class that provides Client - Copyright (c) 2011 Adrian McEwen. All right reserved. + Client.h - Base class that provides Client + Copyright (c) 2011 Adrian McEwen. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef client_h #define client_h @@ -23,29 +23,32 @@ #include "Stream.h" #include "IPAddress.h" -class Client: public Stream { +class Client: public Stream +{ - public: - virtual int connect(IPAddress ip, uint16_t port) =0; - virtual int connect(const char *host, uint16_t port) =0; - virtual size_t write(uint8_t) =0; - virtual size_t write(const uint8_t *buf, size_t size) =0; - virtual int available() = 0; - virtual int read() = 0; - virtual int read(uint8_t *buf, size_t size) = 0; - virtual int peek() = 0; - virtual void flush() = 0; - virtual void stop() = 0; - virtual uint8_t connected() = 0; - virtual operator bool() = 0; - protected: - uint8_t* rawIPAddress(IPAddress& addr) { - return addr.raw_address(); - } +public: + virtual int connect(IPAddress ip, uint16_t port) = 0; + virtual int connect(const char *host, uint16_t port) = 0; + virtual size_t write(uint8_t) = 0; + virtual size_t write(const uint8_t *buf, size_t size) = 0; + virtual int available() = 0; + virtual int read() = 0; + virtual int read(uint8_t *buf, size_t size) = 0; + virtual int peek() = 0; + virtual void flush() = 0; + virtual void stop() = 0; + virtual uint8_t connected() = 0; + virtual operator bool() = 0; +protected: + uint8_t* rawIPAddress(IPAddress& addr) + { + return addr.raw_address(); + } #if LWIP_VERSION_MAJOR != 1 - const uint8_t* rawIPAddress(const IPAddress& addr) { - return addr.raw_address(); - } + const uint8_t* rawIPAddress(const IPAddress& addr) + { + return addr.raw_address(); + } #endif }; diff --git a/cores/esp8266/Esp-frag.cpp b/cores/esp8266/Esp-frag.cpp index 34185a4727..785516e49d 100644 --- a/cores/esp8266/Esp-frag.cpp +++ b/cores/esp8266/Esp-frag.cpp @@ -1,22 +1,22 @@ /* - Esp.cpp - ESP8266-specific APIs - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Esp.cpp - ESP8266-specific APIs + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include "umm_malloc/umm_malloc.h" #include "umm_malloc/umm_malloc_cfg.h" @@ -33,11 +33,17 @@ void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag) uint8_t block_size = umm_block_size(); uint32_t fh = ummHeapInfo.freeBlocks * block_size; if (hfree) + { *hfree = fh; + } if (hmax) + { *hmax = ummHeapInfo.maxFreeContiguousBlocks * block_size; + } if (hfrag) + { *hfrag = 100 - (sqrt32(ummHeapInfo.freeSize2) * 100) / fh; + } } uint8_t EspClass::getHeapFragmentation() diff --git a/cores/esp8266/Esp-version.cpp b/cores/esp8266/Esp-version.cpp index bdc6108642..a60aca660e 100644 --- a/cores/esp8266/Esp-version.cpp +++ b/cores/esp8266/Esp-version.cpp @@ -1,22 +1,22 @@ /* - Esp.cpp - ESP8266-specific APIs - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Esp.cpp - ESP8266-specific APIs + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include #include @@ -34,10 +34,10 @@ static const char bearssl_version [] PROGMEM = "/BearSSL:" STR(BEARSSL_GIT); String EspClass::getFullVersion() { return String(F("SDK:")) + system_get_sdk_version() - + F("/Core:") + FPSTR(arduino_esp8266_git_ver) - + F("=") + String(esp8266::coreVersionNumeric()) + + F("/Core:") + FPSTR(arduino_esp8266_git_ver) + + F("=") + String(esp8266::coreVersionNumeric()) #if LWIP_VERSION_MAJOR == 1 - + F("/lwIP:") + String(LWIP_VERSION_MAJOR) + "." + String(LWIP_VERSION_MINOR) + "." + String(LWIP_VERSION_REVISION) + + F("/lwIP:") + String(LWIP_VERSION_MAJOR) + "." + String(LWIP_VERSION_MINOR) + "." + String(LWIP_VERSION_REVISION) #if LWIP_VERSION_IS_DEVELOPMENT + F("-dev") #endif @@ -52,5 +52,5 @@ String EspClass::getFullVersion() + F(LWIP_HASH_STR) #endif // LWIP_VERSION_MAJOR != 1 + FPSTR(bearssl_version) - ; + ; } diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 23d61d33fa..79cdddeea9 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -1,22 +1,22 @@ /* - Esp.cpp - ESP8266-specific APIs - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + Esp.cpp - ESP8266-specific APIs + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include "Arduino.h" #include "flash_utils.h" @@ -30,7 +30,7 @@ extern "C" { #include "user_interface.h" -extern struct rst_info resetInfo; + extern struct rst_info resetInfo; } @@ -38,45 +38,54 @@ extern struct rst_info resetInfo; /** - * User-defined Literals - * usage: - * - * uint32_t = test = 10_MHz; // --> 10000000 - */ + User-defined Literals + usage: -unsigned long long operator"" _kHz(unsigned long long x) { + uint32_t = test = 10_MHz; // --> 10000000 +*/ + +unsigned long long operator"" _kHz(unsigned long long x) +{ return x * 1000; } -unsigned long long operator"" _MHz(unsigned long long x) { +unsigned long long operator"" _MHz(unsigned long long x) +{ return x * 1000 * 1000; } -unsigned long long operator"" _GHz(unsigned long long x) { +unsigned long long operator"" _GHz(unsigned long long x) +{ return x * 1000 * 1000 * 1000; } -unsigned long long operator"" _kBit(unsigned long long x) { +unsigned long long operator"" _kBit(unsigned long long x) +{ return x * 1024; } -unsigned long long operator"" _MBit(unsigned long long x) { +unsigned long long operator"" _MBit(unsigned long long x) +{ return x * 1024 * 1024; } -unsigned long long operator"" _GBit(unsigned long long x) { +unsigned long long operator"" _GBit(unsigned long long x) +{ return x * 1024 * 1024 * 1024; } -unsigned long long operator"" _kB(unsigned long long x) { +unsigned long long operator"" _kB(unsigned long long x) +{ return x * 1024; } -unsigned long long operator"" _MB(unsigned long long x) { +unsigned long long operator"" _MB(unsigned long long x) +{ return x * 1024 * 1024; } -unsigned long long operator"" _GB(unsigned long long x) { +unsigned long long operator"" _GB(unsigned long long x) +{ return x * 1024 * 1024 * 1024; } @@ -127,59 +136,65 @@ void EspClass::deepSleepInstant(uint64_t time_us, WakeMode mode) //Note: system_rtc_clock_cali_proc() returns a uint32_t, even though system_deep_sleep() takes a uint64_t. uint64_t EspClass::deepSleepMax() { - //cali*(2^31-1)/(2^12) - return (uint64_t)system_rtc_clock_cali_proc()*(0x80000000-1)/(0x1000); + //cali*(2^31-1)/(2^12) + return (uint64_t)system_rtc_clock_cali_proc() * (0x80000000 - 1) / (0x1000); } /* -Layout of RTC Memory is as follows: -Ref: Espressif doc 2C-ESP8266_Non_OS_SDK_API_Reference, section 3.3.23 (system_rtc_mem_write) + Layout of RTC Memory is as follows: + Ref: Espressif doc 2C-ESP8266_Non_OS_SDK_API_Reference, section 3.3.23 (system_rtc_mem_write) -|<------system data (256 bytes)------->|<-----------------user data (512 bytes)--------------->| + |<------system data (256 bytes)------->|<-----------------user data (512 bytes)--------------->| -SDK function signature: -bool system_rtc_mem_read ( + SDK function signature: + bool system_rtc_mem_read ( uint32 des_addr, void * src_addr, uint32 save_size -) - -The system data section can't be used by the user, so: -des_addr must be >=64 (i.e.: 256/4) and <192 (i.e.: 768/4) -src_addr is a pointer to data -save_size is the number of bytes to write - -For the method interface: -offset is the user block number (block size is 4 bytes) must be >= 0 and <128 -data is a pointer to data, 4-byte aligned -size is number of bytes in the block pointed to by data - -Same for write - -Note: If the Updater class is in play, e.g.: the application uses OTA, the eboot -command will be stored into the first 128 bytes of user data, then it will be -retrieved by eboot on boot. That means that user data present there will be lost. -Ref: -- discussion in PR #5330. -- https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map#memmory-mapped-io-registers -- Arduino/bootloaders/eboot/eboot_command.h RTC_MEM definition + ) + + The system data section can't be used by the user, so: + des_addr must be >=64 (i.e.: 256/4) and <192 (i.e.: 768/4) + src_addr is a pointer to data + save_size is the number of bytes to write + + For the method interface: + offset is the user block number (block size is 4 bytes) must be >= 0 and <128 + data is a pointer to data, 4-byte aligned + size is number of bytes in the block pointed to by data + + Same for write + + Note: If the Updater class is in play, e.g.: the application uses OTA, the eboot + command will be stored into the first 128 bytes of user data, then it will be + retrieved by eboot on boot. That means that user data present there will be lost. + Ref: + - discussion in PR #5330. + - https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map#memmory-mapped-io-registers + - Arduino/bootloaders/eboot/eboot_command.h RTC_MEM definition */ bool EspClass::rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size) { - if (offset * 4 + size > 512 || size == 0) { + if (offset * 4 + size > 512 || size == 0) + { return false; - } else { + } + else + { return system_rtc_mem_read(64 + offset, data, size); } } bool EspClass::rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size) { - if (offset * 4 + size > 512 || size == 0) { + if (offset * 4 + size > 512 || size == 0) + { return false; - } else { + } + else + { return system_rtc_mem_write(64 + offset, data, size); } } @@ -235,7 +250,8 @@ extern "C" const char* core_release; String EspClass::getCoreVersion() { - if (core_release != NULL) { + if (core_release != NULL) + { return String(core_release); } char buf[12]; @@ -267,7 +283,8 @@ uint8_t EspClass::getCpuFreqMHz(void) uint32_t EspClass::getFlashChipId(void) { static uint32_t flash_chip_id = 0; - if (flash_chip_id == 0) { + if (flash_chip_id == 0) + { flash_chip_id = spi_flash_get_id(); } return flash_chip_id; @@ -288,7 +305,8 @@ uint32_t EspClass::getFlashChipSize(void) uint32_t data; uint8_t * bytes = (uint8_t *) &data; // read first 4 byte (magic byte + flash config) - if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) + { return magicFlashChipSize((bytes[3] & 0xf0) >> 4); } return 0; @@ -299,7 +317,8 @@ uint32_t EspClass::getFlashChipSpeed(void) uint32_t data; uint8_t * bytes = (uint8_t *) &data; // read first 4 byte (magic byte + flash config) - if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) + { return magicFlashChipSpeed(bytes[3] & 0x0F); } return 0; @@ -311,158 +330,191 @@ FlashMode_t EspClass::getFlashChipMode(void) uint32_t data; uint8_t * bytes = (uint8_t *) &data; // read first 4 byte (magic byte + flash config) - if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + if (spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) + { mode = magicFlashChipMode(bytes[2]); } return mode; } -uint32_t EspClass::magicFlashChipSize(uint8_t byte) { - switch(byte & 0x0F) { - case 0x0: // 4 Mbit (512KB) - return (512_kB); - case 0x1: // 2 MBit (256KB) - return (256_kB); - case 0x2: // 8 MBit (1MB) - return (1_MB); - case 0x3: // 16 MBit (2MB) - return (2_MB); - case 0x4: // 32 MBit (4MB) - return (4_MB); - case 0x8: // 64 MBit (8MB) - return (8_MB); - case 0x9: // 128 MBit (16MB) - return (16_MB); - default: // fail? - return 0; +uint32_t EspClass::magicFlashChipSize(uint8_t byte) +{ + switch (byte & 0x0F) + { + case 0x0: // 4 Mbit (512KB) + return (512_kB); + case 0x1: // 2 MBit (256KB) + return (256_kB); + case 0x2: // 8 MBit (1MB) + return (1_MB); + case 0x3: // 16 MBit (2MB) + return (2_MB); + case 0x4: // 32 MBit (4MB) + return (4_MB); + case 0x8: // 64 MBit (8MB) + return (8_MB); + case 0x9: // 128 MBit (16MB) + return (16_MB); + default: // fail? + return 0; } } -uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { - switch(byte & 0x0F) { - case 0x0: // 40 MHz - return (40_MHz); - case 0x1: // 26 MHz - return (26_MHz); - case 0x2: // 20 MHz - return (20_MHz); - case 0xf: // 80 MHz - return (80_MHz); - default: // fail? - return 0; +uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) +{ + switch (byte & 0x0F) + { + case 0x0: // 40 MHz + return (40_MHz); + case 0x1: // 26 MHz + return (26_MHz); + case 0x2: // 20 MHz + return (20_MHz); + case 0xf: // 80 MHz + return (80_MHz); + default: // fail? + return 0; } } -FlashMode_t EspClass::magicFlashChipMode(uint8_t byte) { +FlashMode_t EspClass::magicFlashChipMode(uint8_t byte) +{ FlashMode_t mode = (FlashMode_t) byte; - if(mode > FM_DOUT) { + if (mode > FM_DOUT) + { mode = FM_UNKNOWN; } return mode; } /** - * Infos from - * http://www.wlxmall.com/images/stock_item/att/A1010004.pdf - * http://www.gigadevice.com/product-series/5.html?locale=en_US - * http://www.elinux.org/images/f/f5/Winbond-w25q32.pdf - */ -uint32_t EspClass::getFlashChipSizeByChipId(void) { + Infos from + http://www.wlxmall.com/images/stock_item/att/A1010004.pdf + http://www.gigadevice.com/product-series/5.html?locale=en_US + http://www.elinux.org/images/f/f5/Winbond-w25q32.pdf +*/ +uint32_t EspClass::getFlashChipSizeByChipId(void) +{ uint32_t chipId = getFlashChipId(); /** - * Chip ID - * 00 - always 00 (Chip ID use only 3 byte) - * 17 - ? looks like 2^xx is size in Byte ? //todo: find docu to this - * 40 - ? may be Speed ? //todo: find docu to this - * C8 - manufacturer ID - */ - switch(chipId) { - - // GigaDevice - case 0x1740C8: // GD25Q64B - return (8_MB); - case 0x1640C8: // GD25Q32B - return (4_MB); - case 0x1540C8: // GD25Q16B - return (2_MB); - case 0x1440C8: // GD25Q80 - return (1_MB); - case 0x1340C8: // GD25Q40 - return (512_kB); - case 0x1240C8: // GD25Q20 - return (256_kB); - case 0x1140C8: // GD25Q10 - return (128_kB); - case 0x1040C8: // GD25Q12 - return (64_kB); - - // Winbond - case 0x1640EF: // W25Q32 - return (4_MB); - case 0x1540EF: // W25Q16 - return (2_MB); - case 0x1440EF: // W25Q80 - return (1_MB); - case 0x1340EF: // W25Q40 - return (512_kB); - - // BergMicro - case 0x1640E0: // BG25Q32 - return (4_MB); - case 0x1540E0: // BG25Q16 - return (2_MB); - case 0x1440E0: // BG25Q80 - return (1_MB); - case 0x1340E0: // BG25Q40 - return (512_kB); - - default: - return 0; + Chip ID + 00 - always 00 (Chip ID use only 3 byte) + 17 - ? looks like 2^xx is size in Byte ? //todo: find docu to this + 40 - ? may be Speed ? //todo: find docu to this + C8 - manufacturer ID + */ + switch (chipId) + { + + // GigaDevice + case 0x1740C8: // GD25Q64B + return (8_MB); + case 0x1640C8: // GD25Q32B + return (4_MB); + case 0x1540C8: // GD25Q16B + return (2_MB); + case 0x1440C8: // GD25Q80 + return (1_MB); + case 0x1340C8: // GD25Q40 + return (512_kB); + case 0x1240C8: // GD25Q20 + return (256_kB); + case 0x1140C8: // GD25Q10 + return (128_kB); + case 0x1040C8: // GD25Q12 + return (64_kB); + + // Winbond + case 0x1640EF: // W25Q32 + return (4_MB); + case 0x1540EF: // W25Q16 + return (2_MB); + case 0x1440EF: // W25Q80 + return (1_MB); + case 0x1340EF: // W25Q40 + return (512_kB); + + // BergMicro + case 0x1640E0: // BG25Q32 + return (4_MB); + case 0x1540E0: // BG25Q16 + return (2_MB); + case 0x1440E0: // BG25Q80 + return (1_MB); + case 0x1340E0: // BG25Q40 + return (512_kB); + + default: + return 0; } } /** - * check the Flash settings from IDE against the Real flash size - * @param needsEquals (return only true it equals) - * @return ok or not - */ -bool EspClass::checkFlashConfig(bool needsEquals) { - if(needsEquals) { - if(getFlashChipRealSize() == getFlashChipSize()) { + check the Flash settings from IDE against the Real flash size + @param needsEquals (return only true it equals) + @return ok or not +*/ +bool EspClass::checkFlashConfig(bool needsEquals) +{ + if (needsEquals) + { + if (getFlashChipRealSize() == getFlashChipSize()) + { return true; } - } else { - if(getFlashChipRealSize() >= getFlashChipSize()) { + } + else + { + if (getFlashChipRealSize() >= getFlashChipSize()) + { return true; } } return false; } -String EspClass::getResetReason(void) { +String EspClass::getResetReason(void) +{ char buff[32]; - if (resetInfo.reason == REASON_DEFAULT_RST) { // normal startup by power on - strcpy_P(buff, PSTR("Power on")); - } else if (resetInfo.reason == REASON_WDT_RST) { // hardware watch dog reset - strcpy_P(buff, PSTR("Hardware Watchdog")); - } else if (resetInfo.reason == REASON_EXCEPTION_RST) { // exception reset, GPIO status won’t change - strcpy_P(buff, PSTR("Exception")); - } else if (resetInfo.reason == REASON_SOFT_WDT_RST) { // software watch dog reset, GPIO status won’t change - strcpy_P(buff, PSTR("Software Watchdog")); - } else if (resetInfo.reason == REASON_SOFT_RESTART) { // software restart ,system_restart , GPIO status won’t change - strcpy_P(buff, PSTR("Software/System restart")); - } else if (resetInfo.reason == REASON_DEEP_SLEEP_AWAKE) { // wake up from deep-sleep - strcpy_P(buff, PSTR("Deep-Sleep Wake")); - } else if (resetInfo.reason == REASON_EXT_SYS_RST) { // external system reset - strcpy_P(buff, PSTR("External System")); - } else { - strcpy_P(buff, PSTR("Unknown")); + if (resetInfo.reason == REASON_DEFAULT_RST) // normal startup by power on + { + strcpy_P(buff, PSTR("Power on")); + } + else if (resetInfo.reason == REASON_WDT_RST) // hardware watch dog reset + { + strcpy_P(buff, PSTR("Hardware Watchdog")); + } + else if (resetInfo.reason == REASON_EXCEPTION_RST) // exception reset, GPIO status won’t change + { + strcpy_P(buff, PSTR("Exception")); + } + else if (resetInfo.reason == REASON_SOFT_WDT_RST) // software watch dog reset, GPIO status won’t change + { + strcpy_P(buff, PSTR("Software Watchdog")); + } + else if (resetInfo.reason == REASON_SOFT_RESTART) // software restart ,system_restart , GPIO status won’t change + { + strcpy_P(buff, PSTR("Software/System restart")); + } + else if (resetInfo.reason == REASON_DEEP_SLEEP_AWAKE) // wake up from deep-sleep + { + strcpy_P(buff, PSTR("Deep-Sleep Wake")); + } + else if (resetInfo.reason == REASON_EXT_SYS_RST) // external system reset + { + strcpy_P(buff, PSTR("External System")); + } + else + { + strcpy_P(buff, PSTR("Unknown")); } return String(buff); } -String EspClass::getResetInfo(void) { - if(resetInfo.reason != 0) { +String EspClass::getResetInfo(void) +{ + if (resetInfo.reason != 0) + { char buff[200]; sprintf(&buff[0], "Fatal exception:%d flag:%d (%s) epc1:0x%08x epc2:0x%08x epc3:0x%08x excvaddr:0x%08x depc:0x%08x", resetInfo.exccause, resetInfo.reason, (resetInfo.reason == 0 ? "DEFAULT" : resetInfo.reason == 1 ? "WDT" : resetInfo.reason == 2 ? "EXCEPTION" : resetInfo.reason == 3 ? "SOFT_WDT" : resetInfo.reason == 4 ? "SOFT_RESTART" : resetInfo.reason == 5 ? "DEEP_SLEEP_AWAKE" : resetInfo.reason == 6 ? "EXT_SYS_RST" : "???"), resetInfo.epc1, resetInfo.epc2, resetInfo.epc3, resetInfo.excvaddr, resetInfo.depc); return String(buff); @@ -470,16 +522,20 @@ String EspClass::getResetInfo(void) { return String("flag: 0"); } -struct rst_info * EspClass::getResetInfoPtr(void) { +struct rst_info * EspClass::getResetInfoPtr(void) +{ return &resetInfo; } -bool EspClass::eraseConfig(void) { +bool EspClass::eraseConfig(void) +{ const size_t cfgSize = 0x4000; size_t cfgAddr = ESP.getFlashChipSize() - cfgSize; - for (size_t offset = 0; offset < cfgSize; offset += SPI_FLASH_SEC_SIZE) { - if (!flashEraseSector((cfgAddr + offset) / SPI_FLASH_SEC_SIZE)) { + for (size_t offset = 0; offset < cfgSize; offset += SPI_FLASH_SEC_SIZE) + { + if (!flashEraseSector((cfgAddr + offset) / SPI_FLASH_SEC_SIZE)) + { return false; } } @@ -487,14 +543,18 @@ bool EspClass::eraseConfig(void) { return true; } -uint32_t EspClass::getSketchSize() { +uint32_t EspClass::getSketchSize() +{ static uint32_t result = 0; if (result) + { return result; + } image_header_t image_header; 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))) + { return 0; } pos += sizeof(image_header); @@ -502,11 +562,12 @@ uint32_t EspClass::getSketchSize() { DEBUG_SERIAL.printf("num_segments=%u\r\n", image_header.num_segments); #endif for (uint32_t section_index = 0; - section_index < image_header.num_segments; - ++section_index) + section_index < image_header.num_segments; + ++section_index) { 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))) + { return 0; } pos += sizeof(section_header); @@ -521,7 +582,8 @@ uint32_t EspClass::getSketchSize() { extern "C" uint32_t _SPIFFS_start; -uint32_t EspClass::getFreeSketchSpace() { +uint32_t EspClass::getFreeSketchSpace() +{ uint32_t usedSize = getSketchSize(); // round one sector up @@ -534,81 +596,108 @@ uint32_t EspClass::getFreeSketchSpace() { return freeSpaceEnd - freeSpaceStart; } -bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool restartOnSuccess) { - if(!Update.begin(size)){ +bool EspClass::updateSketch(Stream& in, uint32_t size, bool restartOnFail, bool restartOnSuccess) +{ + if (!Update.begin(size)) + { #ifdef DEBUG_SERIAL - DEBUG_SERIAL.print("Update "); - Update.printError(DEBUG_SERIAL); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif - if(restartOnFail) ESP.restart(); - return false; - } + if (restartOnFail) + { + ESP.restart(); + } + return false; + } - if(Update.writeStream(in) != size){ + if (Update.writeStream(in) != size) + { #ifdef DEBUG_SERIAL - DEBUG_SERIAL.print("Update "); - Update.printError(DEBUG_SERIAL); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif - if(restartOnFail) ESP.restart(); - return false; - } + if (restartOnFail) + { + ESP.restart(); + } + return false; + } - if(!Update.end()){ + if (!Update.end()) + { #ifdef DEBUG_SERIAL - DEBUG_SERIAL.print("Update "); - Update.printError(DEBUG_SERIAL); + DEBUG_SERIAL.print("Update "); + Update.printError(DEBUG_SERIAL); #endif - if(restartOnFail) ESP.restart(); - return false; - } + if (restartOnFail) + { + ESP.restart(); + } + return false; + } #ifdef DEBUG_SERIAL DEBUG_SERIAL.println("Update SUCCESS"); #endif - if(restartOnSuccess) ESP.restart(); + if (restartOnSuccess) + { + ESP.restart(); + } return true; } static const int FLASH_INT_MASK = ((B10 << 8) | B00111010); -bool EspClass::flashEraseSector(uint32_t sector) { +bool EspClass::flashEraseSector(uint32_t sector) +{ int rc = spi_flash_erase_sector(sector); return rc == 0; } #if PUYA_SUPPORT -static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) { - if (data == nullptr) { - return 1; // SPI_FLASH_RESULT_ERR +static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) +{ + if (data == nullptr) + { + return 1; // SPI_FLASH_RESULT_ERR } // PUYA flash chips need to read existing data, update in memory and write modified data again. 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); // 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. return 1; // SPI_FLASH_RESULT_ERR } } size_t bytesLeft = size; uint32_t pos = offset; - while (bytesLeft > 0 && rc == 0) { + while (bytesLeft > 0 && rc == 0) + { size_t bytesNow = bytesLeft; - if (bytesNow > PUYA_BUFFER_SIZE) { + if (bytesNow > PUYA_BUFFER_SIZE) + { bytesNow = PUYA_BUFFER_SIZE; bytesLeft -= PUYA_BUFFER_SIZE; - } else { + } + else + { bytesLeft = 0; } rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow); - if (rc != 0) { + if (rc != 0) + { return rc; } - for (size_t i = 0; i < bytesNow / 4; ++i) { + for (size_t i = 0; i < bytesNow / 4; ++i) + { flash_write_puya_buf[i] &= *ptr; ++ptr; } @@ -619,10 +708,12 @@ static int spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) { } #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; #if PUYA_SUPPORT - if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) { + if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) + { rc = spi_flash_write_puya(offset, data, size); } else @@ -633,7 +724,8 @@ bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) { return rc == 0; } -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); return rc == 0; } @@ -641,21 +733,25 @@ bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) { String EspClass::getSketchMD5() { static String result; - if (result.length()) { + if (result.length()) + { return result; } uint32_t lengthLeft = getSketchSize(); const size_t bufSize = 512; std::unique_ptr buf(new uint8_t[bufSize]); uint32_t offset = 0; - if(!buf.get()) { + if (!buf.get()) + { return String(); } MD5Builder md5; md5.begin(); - while( lengthLeft > 0) { + while (lengthLeft > 0) + { size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize; - if (!flashRead(offset, reinterpret_cast(buf.get()), (readBytes + 3) & ~3)) { + if (!flashRead(offset, reinterpret_cast(buf.get()), (readBytes + 3) & ~3)) + { return String(); } md5.add(buf.get(), readBytes); diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index f717c15881..da3b74c69b 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -1,22 +1,22 @@ /* - Esp.h - ESP8266-specific APIs - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Esp.h - ESP8266-specific APIs + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef ESP_H #define ESP_H @@ -24,17 +24,18 @@ #include #ifndef PUYA_SUPPORT - #define PUYA_SUPPORT 0 +#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 +// 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 // https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h?h=1.0.x -typedef enum { +typedef enum +{ SPI_FLASH_VENDOR_ALLIANCE = 0x52, /* Alliance Semiconductor */ SPI_FLASH_VENDOR_AMD = 0x01, /* AMD */ SPI_FLASH_VENDOR_AMIC = 0x37, /* AMIC */ @@ -70,9 +71,10 @@ typedef enum { } SPI_FLASH_VENDOR_t; /** - * AVR macros for WDT managment - */ -typedef enum { + AVR macros for WDT managment +*/ +typedef enum +{ WDTO_0MS = 0, //!< WDTO_0MS WDTO_15MS = 15, //!< WDTO_15MS WDTO_30MS = 30, //!< WDTO_30MS @@ -94,7 +96,8 @@ typedef enum { #define cli() ets_intr_lock() // IRQ Disable #define sei() ets_intr_unlock() // IRQ Enable -enum RFMode { +enum RFMode +{ RF_DEFAULT = 0, // RF_CAL or not after deep-sleep wake up, depends on init data byte 108. RF_CAL = 1, // RF_CAL after deep-sleep wake up, there will be large current. RF_NO_CAL = 2, // no RF_CAL after deep-sleep wake up, there will only be small current. @@ -111,7 +114,8 @@ enum RFMode { #define WAKE_NO_RFCAL RF_NO_CAL #define WAKE_RF_DISABLED RF_DISABLED -enum ADCMode { +enum ADCMode +{ ADC_TOUT = 33, ADC_TOUT_3V3 = 33, ADC_VCC = 255, @@ -120,97 +124,99 @@ enum ADCMode { #define ADC_MODE(mode) int __get_adc_mode(void) { return (int) (mode); } -typedef enum { - FM_QIO = 0x00, - FM_QOUT = 0x01, - FM_DIO = 0x02, - FM_DOUT = 0x03, - FM_UNKNOWN = 0xff +typedef enum +{ + FM_QIO = 0x00, + FM_QOUT = 0x01, + FM_DIO = 0x02, + FM_DOUT = 0x03, + FM_UNKNOWN = 0xff } FlashMode_t; -class EspClass { - public: - // TODO: figure out how to set WDT timeout - void wdtEnable(uint32_t timeout_ms = 0); - // note: setting the timeout value is not implemented at the moment - void wdtEnable(WDTO_t timeout_ms = WDTO_0MS); +class EspClass +{ +public: + // TODO: figure out how to set WDT timeout + void wdtEnable(uint32_t timeout_ms = 0); + // note: setting the timeout value is not implemented at the moment + void wdtEnable(WDTO_t timeout_ms = WDTO_0MS); - void wdtDisable(); - void wdtFeed(); + void wdtDisable(); + void wdtFeed(); - void deepSleep(uint64_t time_us, RFMode mode = RF_DEFAULT); - void deepSleepInstant(uint64_t time_us, RFMode mode = RF_DEFAULT); - uint64_t deepSleepMax(); + void deepSleep(uint64_t time_us, RFMode mode = RF_DEFAULT); + void deepSleepInstant(uint64_t time_us, RFMode mode = RF_DEFAULT); + uint64_t deepSleepMax(); - bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size); - bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size); + bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size); + bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size); - void reset(); - void restart(); + void reset(); + void restart(); - uint16_t getVcc(); - uint32_t getChipId(); + uint16_t getVcc(); + uint32_t getChipId(); - uint32_t getFreeHeap(); - uint16_t getMaxFreeBlockSize(); - uint8_t getHeapFragmentation(); // in % - void getHeapStats(uint32_t* free = nullptr, uint16_t* max = nullptr, uint8_t* frag = nullptr); + uint32_t getFreeHeap(); + uint16_t getMaxFreeBlockSize(); + uint8_t getHeapFragmentation(); // in % + void getHeapStats(uint32_t* free = nullptr, uint16_t* max = nullptr, uint8_t* frag = nullptr); - uint32_t getFreeContStack(); - void resetFreeContStack(); + uint32_t getFreeContStack(); + void resetFreeContStack(); - const char * getSdkVersion(); - String getCoreVersion(); - String getFullVersion(); + const char * getSdkVersion(); + String getCoreVersion(); + String getFullVersion(); - uint8_t getBootVersion(); - uint8_t getBootMode(); + uint8_t getBootVersion(); + uint8_t getBootMode(); - uint8_t getCpuFreqMHz(); + uint8_t getCpuFreqMHz(); - uint32_t getFlashChipId(); - uint8_t getFlashChipVendorId(); + uint32_t getFlashChipId(); + uint8_t getFlashChipVendorId(); - //gets the actual chip size based on the flash id - uint32_t getFlashChipRealSize(); - //gets the size of the flash as set by the compiler - uint32_t getFlashChipSize(); - uint32_t getFlashChipSpeed(); - FlashMode_t getFlashChipMode(); - uint32_t getFlashChipSizeByChipId(); + //gets the actual chip size based on the flash id + uint32_t getFlashChipRealSize(); + //gets the size of the flash as set by the compiler + uint32_t getFlashChipSize(); + uint32_t getFlashChipSpeed(); + FlashMode_t getFlashChipMode(); + uint32_t getFlashChipSizeByChipId(); - uint32_t magicFlashChipSize(uint8_t byte); - uint32_t magicFlashChipSpeed(uint8_t byte); - FlashMode_t magicFlashChipMode(uint8_t byte); + uint32_t magicFlashChipSize(uint8_t byte); + uint32_t magicFlashChipSpeed(uint8_t byte); + FlashMode_t magicFlashChipMode(uint8_t byte); - bool checkFlashConfig(bool needsEquals = false); + bool checkFlashConfig(bool needsEquals = false); - bool flashEraseSector(uint32_t sector); - bool flashWrite(uint32_t offset, uint32_t *data, size_t size); - bool flashRead(uint32_t offset, uint32_t *data, size_t size); + bool flashEraseSector(uint32_t sector); + bool flashWrite(uint32_t offset, uint32_t *data, size_t size); + bool flashRead(uint32_t offset, uint32_t *data, size_t size); - uint32_t getSketchSize(); - String getSketchMD5(); - uint32_t getFreeSketchSpace(); - bool updateSketch(Stream& in, uint32_t size, bool restartOnFail = false, bool restartOnSuccess = true); + uint32_t getSketchSize(); + String getSketchMD5(); + uint32_t getFreeSketchSpace(); + bool updateSketch(Stream& in, uint32_t size, bool restartOnFail = false, bool restartOnSuccess = true); - String getResetReason(); - String getResetInfo(); - struct rst_info * getResetInfoPtr(); + String getResetReason(); + String getResetInfo(); + struct rst_info * getResetInfoPtr(); - bool eraseConfig(); + bool eraseConfig(); #ifndef CORE_MOCK - inline + inline #endif - uint32_t getCycleCount(); + uint32_t getCycleCount(); }; #ifndef CORE_MOCK uint32_t EspClass::getCycleCount() { uint32_t ccount; - __asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount)); + __asm__ __volatile__("esync; rsr %0,ccount":"=a"(ccount)); return ccount; } #endif diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp index 9461d9fddc..003b44e282 100644 --- a/cores/esp8266/FS.cpp +++ b/cores/esp8266/FS.cpp @@ -1,22 +1,22 @@ /* - FS.cpp - file system wrapper - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + FS.cpp - file system wrapper + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include "FS.h" #include "FSImpl.h" @@ -25,49 +25,68 @@ using namespace fs; static bool sflags(const char* mode, OpenMode& om, AccessMode& am); -size_t File::write(uint8_t c) { +size_t File::write(uint8_t c) +{ if (!_p) + { return 0; + } return _p->write(&c, 1); } -size_t File::write(const uint8_t *buf, size_t size) { +size_t File::write(const uint8_t *buf, size_t size) +{ if (!_p) + { return 0; + } return _p->write(buf, size); } -int File::available() { +int File::available() +{ if (!_p) + { return false; + } return _p->size() - _p->position(); } -int File::read() { +int File::read() +{ if (!_p) + { return -1; + } uint8_t result; - if (_p->read(&result, 1) != 1) { + if (_p->read(&result, 1) != 1) + { return -1; } return result; } -size_t File::read(uint8_t* buf, size_t size) { +size_t File::read(uint8_t* buf, size_t size) +{ if (!_p) + { return -1; + } return _p->read(buf, size); } -int File::peek() { +int File::peek() +{ if (!_p) + { return -1; + } size_t curPos = _p->position(); int result = read(); @@ -75,90 +94,126 @@ int File::peek() { return result; } -void File::flush() { +void File::flush() +{ if (!_p) + { return; + } _p->flush(); } -bool File::seek(uint32_t pos, SeekMode mode) { +bool File::seek(uint32_t pos, SeekMode mode) +{ if (!_p) + { return false; + } return _p->seek(pos, mode); } -size_t File::position() const { +size_t File::position() const +{ if (!_p) + { return 0; + } return _p->position(); } -size_t File::size() const { +size_t File::size() const +{ if (!_p) + { return 0; + } return _p->size(); } -void File::close() { - if (_p) { +void File::close() +{ + if (_p) + { _p->close(); _p = nullptr; } } -File::operator bool() const { +File::operator bool() const +{ return !!_p; } -bool File::truncate(uint32_t size) { +bool File::truncate(uint32_t size) +{ if (!_p) + { return false; + } return _p->truncate(size); } -const char* File::name() const { +const char* File::name() const +{ if (!_p) + { return nullptr; + } return _p->name(); } -const char* File::fullName() const { +const char* File::fullName() const +{ if (!_p) + { return nullptr; + } return _p->fullName(); } -bool File::isFile() const { +bool File::isFile() const +{ if (!_p) + { return false; + } return _p->isFile(); } -bool File::isDirectory() const { +bool File::isDirectory() const +{ if (!_p) + { return false; + } return _p->isDirectory(); } -void File::rewindDirectory() { - if (!_fakeDir) { +void File::rewindDirectory() +{ + if (!_fakeDir) + { _fakeDir = std::make_shared(_baseFS->openDir(fullName())); - } else { + } + else + { _fakeDir->rewind(); - } + } } -File File::openNextFile() { - if (!_fakeDir) { +File File::openNextFile() +{ + if (!_fakeDir) + { _fakeDir = std::make_shared(_baseFS->openDir(fullName())); } _fakeDir->next(); @@ -169,25 +224,28 @@ String File::readString() { String ret; ret.reserve(size() - position()); - char temp[256+1]; - int countRead = readBytes(temp, sizeof(temp)-1); + char temp[256 + 1]; + int countRead = readBytes(temp, sizeof(temp) - 1); while (countRead > 0) { temp[countRead] = 0; ret += temp; - countRead = readBytes(temp, sizeof(temp)-1); + countRead = readBytes(temp, sizeof(temp) - 1); } return ret; } -File Dir::openFile(const char* mode) { - if (!_impl) { +File Dir::openFile(const char* mode) +{ + if (!_impl) + { return File(); } OpenMode om; AccessMode am; - if (!sflags(mode, om, am)) { + if (!sflags(mode, om, am)) + { DEBUGV("Dir::openFile: invalid mode `%s`\r\n", mode); return File(); } @@ -195,206 +253,257 @@ File Dir::openFile(const char* mode) { return File(_impl->openFile(om, am), _baseFS); } -String Dir::fileName() { - if (!_impl) { +String Dir::fileName() +{ + if (!_impl) + { return String(); } return _impl->fileName(); } -size_t Dir::fileSize() { - if (!_impl) { +size_t Dir::fileSize() +{ + if (!_impl) + { return 0; } return _impl->fileSize(); } -bool Dir::isFile() const { +bool Dir::isFile() const +{ if (!_impl) + { return false; + } return _impl->isFile(); } -bool Dir::isDirectory() const { +bool Dir::isDirectory() const +{ if (!_impl) + { return false; + } return _impl->isDirectory(); } -bool Dir::next() { - if (!_impl) { +bool Dir::next() +{ + if (!_impl) + { return false; } return _impl->next(); } -bool Dir::rewind() { - if (!_impl) { +bool Dir::rewind() +{ + if (!_impl) + { return false; } return _impl->rewind(); } -bool FS::setConfig(const FSConfig &cfg) { - if (!_impl) { +bool FS::setConfig(const FSConfig &cfg) +{ + if (!_impl) + { return false; } return _impl->setConfig(cfg); } -bool FS::begin() { - if (!_impl) { +bool FS::begin() +{ + if (!_impl) + { return false; } return _impl->begin(); } -void FS::end() { - if (_impl) { +void FS::end() +{ + if (_impl) + { _impl->end(); } } -bool FS::gc() { - if (!_impl) { +bool FS::gc() +{ + if (!_impl) + { return false; } return _impl->gc(); } -bool FS::format() { - if (!_impl) { +bool FS::format() +{ + if (!_impl) + { return false; } return _impl->format(); } -bool FS::info(FSInfo& info){ - if (!_impl) { +bool FS::info(FSInfo& info) +{ + if (!_impl) + { return false; } return _impl->info(info); } -File FS::open(const String& path, const char* mode) { +File FS::open(const String& path, const char* mode) +{ return open(path.c_str(), mode); } -File FS::open(const char* path, const char* mode) { - if (!_impl) { +File FS::open(const char* path, const char* mode) +{ + if (!_impl) + { return File(); } OpenMode om; AccessMode am; - if (!sflags(mode, om, am)) { + if (!sflags(mode, om, am)) + { DEBUGV("FS::open: invalid mode `%s`\r\n", mode); return File(); } return File(_impl->open(path, om, am), this); } -bool FS::exists(const char* path) { - if (!_impl) { +bool FS::exists(const char* path) +{ + if (!_impl) + { return false; } return _impl->exists(path); } -bool FS::exists(const String& path) { +bool FS::exists(const String& path) +{ return exists(path.c_str()); } -Dir FS::openDir(const char* path) { - if (!_impl) { +Dir FS::openDir(const char* path) +{ + if (!_impl) + { return Dir(); } DirImplPtr p = _impl->openDir(path); return Dir(p, this); } -Dir FS::openDir(const String& path) { +Dir FS::openDir(const String& path) +{ return openDir(path.c_str()); } -bool FS::remove(const char* path) { - if (!_impl) { +bool FS::remove(const char* path) +{ + if (!_impl) + { return false; } return _impl->remove(path); } -bool FS::remove(const String& path) { +bool FS::remove(const String& path) +{ return remove(path.c_str()); } -bool FS::rmdir(const char* path) { - if (!_impl) { +bool FS::rmdir(const char* path) +{ + if (!_impl) + { return false; } return _impl->rmdir(path); } -bool FS::rmdir(const String& path) { +bool FS::rmdir(const String& path) +{ return rmdir(path.c_str()); } -bool FS::mkdir(const char* path) { - if (!_impl) { +bool FS::mkdir(const char* path) +{ + if (!_impl) + { return false; } return _impl->mkdir(path); } -bool FS::mkdir(const String& path) { +bool FS::mkdir(const String& path) +{ return mkdir(path.c_str()); } -bool FS::rename(const char* pathFrom, const char* pathTo) { - if (!_impl) { +bool FS::rename(const char* pathFrom, const char* pathTo) +{ + if (!_impl) + { return false; } return _impl->rename(pathFrom, pathTo); } -bool FS::rename(const String& pathFrom, const String& pathTo) { +bool FS::rename(const String& pathFrom, const String& pathTo) +{ return rename(pathFrom.c_str(), pathTo.c_str()); } -static bool sflags(const char* mode, OpenMode& om, AccessMode& am) { - switch (mode[0]) { - case 'r': - am = AM_READ; - om = OM_DEFAULT; - break; - case 'w': - am = AM_WRITE; - om = (OpenMode) (OM_CREATE | OM_TRUNCATE); - break; - case 'a': - am = AM_WRITE; - om = (OpenMode) (OM_CREATE | OM_APPEND); - break; - default: - return false; - } - switch(mode[1]) { - case '+': - am = (AccessMode) (AM_WRITE | AM_READ); - break; - case 0: - break; - default: - return false; +static bool sflags(const char* mode, OpenMode& om, AccessMode& am) +{ + switch (mode[0]) + { + case 'r': + am = AM_READ; + om = OM_DEFAULT; + break; + case 'w': + am = AM_WRITE; + om = (OpenMode)(OM_CREATE | OM_TRUNCATE); + break; + case 'a': + am = AM_WRITE; + om = (OpenMode)(OM_CREATE | OM_APPEND); + break; + default: + return false; + } + switch (mode[1]) + { + case '+': + am = (AccessMode)(AM_WRITE | AM_READ); + break; + case 0: + break; + default: + return false; } return true; } @@ -403,7 +512,7 @@ static bool sflags(const char* mode, OpenMode& om, AccessMode& am) { #if defined(FS_FREESTANDING_FUNCTIONS) /* -TODO: move these functions to public API: + TODO: move these functions to public API: */ File open(const char* path, const char* mode); File open(String& path, const char* mode); @@ -417,7 +526,8 @@ bool mount(FS& fs, const char* mountPoint); */ -struct MountEntry { +struct MountEntry +{ FSImplPtr fs; String path; MountEntry* next; @@ -426,9 +536,11 @@ struct MountEntry { static MountEntry* s_mounted = nullptr; template<> -bool mount(FS& fs, const char* mountPoint) { +bool mount(FS& fs, const char* mountPoint) +{ FSImplPtr p = fs._impl; - if (!p || !p->mount()) { + if (!p || !p->mount()) + { DEBUGV("FSImpl mount failed\r\n"); return false; } @@ -447,31 +559,39 @@ bool mount(FS& fs, const char* mountPoint) { /* iterate over MountEntries and look for the ones which match the path */ -File open(const char* path, const char* mode) { +File open(const char* path, const char* mode) +{ OpenMode om; AccessMode am; - if (!sflags(mode, om, am)) { + if (!sflags(mode, om, am)) + { DEBUGV("open: invalid mode `%s`\r\n", mode); return File(); } - for (MountEntry* entry = s_mounted; entry; entry = entry->next) { + for (MountEntry* entry = s_mounted; entry; entry = entry->next) + { size_t offset = entry->path.length(); - if (strstr(path, entry->path.c_str())) { + if (strstr(path, entry->path.c_str())) + { File result = entry->fs->open(path + offset); if (result) + { return result; + } } } return File(); } -File open(const String& path, const char* mode) { +File open(const String& path, const char* mode) +{ return FS::open(path.c_str(), mode); } -Dir openDir(const String& path) { +Dir openDir(const String& path) +{ return openDir(path.c_str()); } #endif diff --git a/cores/esp8266/FS.h b/cores/esp8266/FS.h index 2cc6acd124..2ec95c9bf8 100644 --- a/cores/esp8266/FS.h +++ b/cores/esp8266/FS.h @@ -1,22 +1,22 @@ /* - FS.h - file system wrapper - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + FS.h - file system wrapper + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef FS_H #define FS_H @@ -24,7 +24,8 @@ #include #include -namespace fs { +namespace fs +{ class File; class Dir; @@ -40,7 +41,8 @@ typedef std::shared_ptr DirImplPtr; template bool mount(Tfs& fs, const char* mountPoint); -enum SeekMode { +enum SeekMode +{ SeekSet = 0, SeekCur = 1, SeekEnd = 2 @@ -60,12 +62,14 @@ class File : public Stream int read() override; int peek() override; void flush() override; - size_t readBytes(char *buffer, size_t length) override { + size_t readBytes(char *buffer, size_t length) override + { return read((uint8_t*)buffer, length); } size_t read(uint8_t* buf, size_t size); bool seek(uint32_t pos, SeekMode mode); - bool seek(uint32_t pos) { + bool seek(uint32_t pos) + { return seek(pos, SeekSet); } size_t position() const; @@ -80,26 +84,29 @@ class File : public Stream bool isDirectory() const; // Arduino "class SD" methods for compatibility - template size_t write(T &src){ - uint8_t obuf[256]; - size_t doneLen = 0; - size_t sentLen; - int i; - - while (src.available() > sizeof(obuf)){ - src.read(obuf, sizeof(obuf)); - sentLen = write(obuf, sizeof(obuf)); - doneLen = doneLen + sentLen; - if(sentLen != sizeof(obuf)){ - return doneLen; + template size_t write(T &src) + { + uint8_t obuf[256]; + size_t doneLen = 0; + size_t sentLen; + int i; + + while (src.available() > sizeof(obuf)) + { + src.read(obuf, sizeof(obuf)); + sentLen = write(obuf, sizeof(obuf)); + doneLen = doneLen + sentLen; + if (sentLen != sizeof(obuf)) + { + return doneLen; + } } - } - size_t leftLen = src.available(); - src.read(obuf, leftLen); - sentLen = write(obuf, leftLen); - doneLen = doneLen + sentLen; - return doneLen; + size_t leftLen = src.available(); + src.read(obuf, leftLen); + sentLen = write(obuf, leftLen); + doneLen = doneLen + sentLen; + return doneLen; } using Print::write; @@ -116,7 +123,8 @@ class File : public Stream FS *_baseFS; }; -class Dir { +class Dir +{ public: Dir(DirImplPtr impl = DirImplPtr(), FS *baseFS = nullptr): _impl(impl), _baseFS(baseFS) { } @@ -135,7 +143,8 @@ class Dir { FS *_baseFS; }; -struct FSInfo { +struct FSInfo +{ size_t totalBytes; size_t usedBytes; size_t blockSize; @@ -147,13 +156,15 @@ struct FSInfo { class FSConfig { public: - FSConfig(bool autoFormat = true) { + FSConfig(bool autoFormat = true) + { _type = FSConfig::fsid::FSId; - _autoFormat = autoFormat; + _autoFormat = autoFormat; } enum fsid { FSId = 0x00000000 }; - FSConfig setAutoFormat(bool val = true) { + FSConfig setAutoFormat(bool val = true) + { _autoFormat = val; return *this; } @@ -165,9 +176,10 @@ class FSConfig class SPIFFSConfig : public FSConfig { public: - SPIFFSConfig(bool autoFormat = true) { + SPIFFSConfig(bool autoFormat = true) + { _type = SPIFFSConfig::fsid::FSId; - _autoFormat = autoFormat; + _autoFormat = autoFormat; } enum fsid { FSId = 0x53504946 }; }; diff --git a/cores/esp8266/FSImpl.h b/cores/esp8266/FSImpl.h index a668f2887d..f4199a5470 100644 --- a/cores/esp8266/FSImpl.h +++ b/cores/esp8266/FSImpl.h @@ -1,31 +1,33 @@ /* - FSImpl.h - base file system interface - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + FSImpl.h - base file system interface + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef FSIMPL_H #define FSIMPL_H #include #include -namespace fs { +namespace fs +{ -class FileImpl { +class FileImpl +{ public: virtual ~FileImpl() { } virtual size_t write(const uint8_t *buf, size_t size) = 0; @@ -42,20 +44,23 @@ class FileImpl { virtual bool isDirectory() const = 0; }; -enum OpenMode { +enum OpenMode +{ OM_DEFAULT = 0, OM_CREATE = 1, OM_APPEND = 2, OM_TRUNCATE = 4 }; -enum AccessMode { +enum AccessMode +{ AM_READ = 1, AM_WRITE = 2, AM_RW = AM_READ | AM_WRITE }; -class DirImpl { +class DirImpl +{ public: virtual ~DirImpl() { } virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0; @@ -67,9 +72,10 @@ class DirImpl { virtual bool rewind() = 0; }; -class FSImpl { +class FSImpl +{ public: - virtual ~FSImpl () { } + virtual ~FSImpl() { } virtual bool setConfig(const FSConfig &cfg) = 0; virtual bool begin() = 0; virtual void end() = 0; @@ -82,7 +88,10 @@ class FSImpl { virtual bool remove(const char* path) = 0; virtual bool mkdir(const char* path) = 0; virtual bool rmdir(const char* path) = 0; - virtual bool gc() { return true; } // May not be implemented in all file systems. + virtual bool gc() + { + return true; // May not be implemented in all file systems. + } }; } // namespace fs diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp index 2633c63182..c18b2e5221 100644 --- a/cores/esp8266/FunctionalInterrupt.cpp +++ b/cores/esp8266/FunctionalInterrupt.cpp @@ -8,64 +8,64 @@ typedef void (*voidFuncPtr)(void); typedef void (*voidFuncPtrArg)(void*); // Helper functions for Functional interrupt routines -extern "C" void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void*fp , int mode); +extern "C" void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void*fp, int mode); void ICACHE_RAM_ATTR interruptFunctional(void* arg) { ArgStructure* localArg = (ArgStructure*)arg; - if (localArg->functionInfo->reqScheduledFunction) - { - schedule_function(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo)))); -// scheduledInterrupts->scheduleFunctionReg(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo))), false, true); - } - if (localArg->functionInfo->reqFunction) - { - localArg->functionInfo->reqFunction(); - } + if (localArg->functionInfo->reqScheduledFunction) + { + schedule_function(std::bind(localArg->functionInfo->reqScheduledFunction, InterruptInfo(*(localArg->interruptInfo)))); + // scheduledInterrupts->scheduleFunctionReg(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo))), false, true); + } + if (localArg->functionInfo->reqFunction) + { + localArg->functionInfo->reqFunction(); + } } extern "C" { - void cleanupFunctional(void* arg) - { - ArgStructure* localArg = (ArgStructure*)arg; - delete (FunctionInfo*)localArg->functionInfo; - delete (InterruptInfo*)localArg->interruptInfo; - delete localArg; - } + void cleanupFunctional(void* arg) + { + ArgStructure* localArg = (ArgStructure*)arg; + delete (FunctionInfo*)localArg->functionInfo; + delete (InterruptInfo*)localArg->interruptInfo; + delete localArg; + } } void attachInterrupt(uint8_t pin, std::function intRoutine, int mode) { - // use the local interrupt routine which takes the ArgStructure as argument + // use the local interrupt routine which takes the ArgStructure as argument - InterruptInfo* ii = nullptr; + InterruptInfo* ii = nullptr; - FunctionInfo* fi = new FunctionInfo; - fi->reqFunction = intRoutine; + FunctionInfo* fi = new FunctionInfo; + fi->reqFunction = intRoutine; - ArgStructure* as = new ArgStructure; - as->interruptInfo = ii; - as->functionInfo = fi; + ArgStructure* as = new ArgStructure; + as->interruptInfo = ii; + as->functionInfo = fi; - __attachInterruptArg (pin, (voidFuncPtr)interruptFunctional, as, mode); + __attachInterruptArg(pin, (voidFuncPtr)interruptFunctional, as, mode); } void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode) { - if (!scheduledInterrupts) - { - scheduledInterrupts = new ScheduledFunctions(32); - } - InterruptInfo* ii = new InterruptInfo; + if (!scheduledInterrupts) + { + scheduledInterrupts = new ScheduledFunctions(32); + } + InterruptInfo* ii = new InterruptInfo; - FunctionInfo* fi = new FunctionInfo; - fi->reqScheduledFunction = scheduledIntRoutine; + FunctionInfo* fi = new FunctionInfo; + fi->reqScheduledFunction = scheduledIntRoutine; - ArgStructure* as = new ArgStructure; - as->interruptInfo = ii; - as->functionInfo = fi; + ArgStructure* as = new ArgStructure; + as->interruptInfo = ii; + as->functionInfo = fi; - __attachInterruptArg (pin, (voidFuncPtr)interruptFunctional, as, mode); + __attachInterruptArg(pin, (voidFuncPtr)interruptFunctional, as, mode); } diff --git a/cores/esp8266/FunctionalInterrupt.h b/cores/esp8266/FunctionalInterrupt.h index a6d53188ae..f578b74d65 100644 --- a/cores/esp8266/FunctionalInterrupt.h +++ b/cores/esp8266/FunctionalInterrupt.h @@ -13,20 +13,23 @@ extern "C" { // Structures for communication -struct InterruptInfo { - uint8_t pin = 0; - uint8_t value = 0; - uint32_t micro = 0; +struct InterruptInfo +{ + uint8_t pin = 0; + uint8_t value = 0; + uint32_t micro = 0; }; -struct FunctionInfo { +struct FunctionInfo +{ std::function reqFunction = nullptr; - std::function reqScheduledFunction = nullptr; + std::function reqScheduledFunction = nullptr; }; -struct ArgStructure { - InterruptInfo* interruptInfo = nullptr; - FunctionInfo* functionInfo = nullptr; +struct ArgStructure +{ + InterruptInfo* interruptInfo = nullptr; + FunctionInfo* functionInfo = nullptr; }; static ScheduledFunctions* scheduledInterrupts; diff --git a/cores/esp8266/HardwareSerial.cpp b/cores/esp8266/HardwareSerial.cpp index 6ec94500db..5db65ba346 100644 --- a/cores/esp8266/HardwareSerial.cpp +++ b/cores/esp8266/HardwareSerial.cpp @@ -1,157 +1,176 @@ -/* - HardwareSerial.cpp - esp8266 UART support - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266) - Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266) - Modified 3 May 2015 by Hristo Gochkov (change register access methods) - */ - -#include -#include -#include -#include -#include -#include "Arduino.h" -#include "HardwareSerial.h" -#include "Esp.h" - -HardwareSerial::HardwareSerial(int uart_nr) - : _uart_nr(uart_nr), _rx_size(256) -{} - -void HardwareSerial::begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin) -{ - end(); - _uart = uart_init(_uart_nr, baud, (int) config, (int) mode, tx_pin, _rx_size); -#if defined(DEBUG_ESP_PORT) && !defined(NDEBUG) - if (static_cast(this) == static_cast(&DEBUG_ESP_PORT)) - { - setDebugOutput(true); - println(); - println(ESP.getFullVersion()); - } -#endif -} - -void HardwareSerial::end() -{ - if(uart_get_debug() == _uart_nr) { - uart_set_debug(UART_NO); - } - - uart_uninit(_uart); - _uart = NULL; -} - -size_t HardwareSerial::setRxBufferSize(size_t size){ - if(_uart) { - _rx_size = uart_resize_rx_buffer(_uart, size); - } else { - _rx_size = size; - } - return _rx_size; -} - -void HardwareSerial::setDebugOutput(bool en) -{ - if(!_uart) { - return; - } - if(en) { - if(uart_tx_enabled(_uart)) { - uart_set_debug(_uart_nr); - } else { - uart_set_debug(UART_NO); - } - } else { - // disable debug for this interface - if(uart_get_debug() == _uart_nr) { - uart_set_debug(UART_NO); - } - } -} - -int HardwareSerial::available(void) -{ - int result = static_cast(uart_rx_available(_uart)); - if (!result) { - optimistic_yield(10000); - } - return result; -} - -void HardwareSerial::flush() -{ - if(!_uart || !uart_tx_enabled(_uart)) { - return; - } - - uart_wait_tx_empty(_uart); - //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 - delayMicroseconds(11000000 / uart_get_baudrate(_uart) + 1); -} - -void HardwareSerial::startDetectBaudrate() -{ - uart_start_detect_baudrate(_uart_nr); -} - -unsigned long HardwareSerial::testBaudrate() -{ - return uart_detect_baudrate(_uart_nr); -} - -unsigned long HardwareSerial::detectBaudrate(time_t timeoutMillis) -{ - time_t startMillis = millis(); - unsigned long detectedBaudrate; - while ((time_t) millis() - startMillis < timeoutMillis) { - if ((detectedBaudrate = testBaudrate())) { - break; - } - yield(); - delay(100); - } - return detectedBaudrate; -} - +/* + HardwareSerial.cpp - esp8266 UART support + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266) + Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266) + Modified 3 May 2015 by Hristo Gochkov (change register access methods) +*/ + +#include +#include +#include +#include +#include +#include "Arduino.h" +#include "HardwareSerial.h" +#include "Esp.h" + +HardwareSerial::HardwareSerial(int uart_nr) + : _uart_nr(uart_nr), _rx_size(256) +{} + +void HardwareSerial::begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin) +{ + end(); + _uart = uart_init(_uart_nr, baud, (int) config, (int) mode, tx_pin, _rx_size); +#if defined(DEBUG_ESP_PORT) && !defined(NDEBUG) + if (static_cast(this) == static_cast(&DEBUG_ESP_PORT)) + { + setDebugOutput(true); + println(); + println(ESP.getFullVersion()); + } +#endif +} + +void HardwareSerial::end() +{ + if (uart_get_debug() == _uart_nr) + { + uart_set_debug(UART_NO); + } + + uart_uninit(_uart); + _uart = NULL; +} + +size_t HardwareSerial::setRxBufferSize(size_t size) +{ + if (_uart) + { + _rx_size = uart_resize_rx_buffer(_uart, size); + } + else + { + _rx_size = size; + } + return _rx_size; +} + +void HardwareSerial::setDebugOutput(bool en) +{ + if (!_uart) + { + return; + } + if (en) + { + if (uart_tx_enabled(_uart)) + { + uart_set_debug(_uart_nr); + } + else + { + uart_set_debug(UART_NO); + } + } + else + { + // disable debug for this interface + if (uart_get_debug() == _uart_nr) + { + uart_set_debug(UART_NO); + } + } +} + +int HardwareSerial::available(void) +{ + int result = static_cast(uart_rx_available(_uart)); + if (!result) + { + optimistic_yield(10000); + } + return result; +} + +void HardwareSerial::flush() +{ + if (!_uart || !uart_tx_enabled(_uart)) + { + return; + } + + uart_wait_tx_empty(_uart); + //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 + delayMicroseconds(11000000 / uart_get_baudrate(_uart) + 1); +} + +void HardwareSerial::startDetectBaudrate() +{ + uart_start_detect_baudrate(_uart_nr); +} + +unsigned long HardwareSerial::testBaudrate() +{ + return uart_detect_baudrate(_uart_nr); +} + +unsigned long HardwareSerial::detectBaudrate(time_t timeoutMillis) +{ + time_t startMillis = millis(); + unsigned long detectedBaudrate; + while ((time_t) millis() - startMillis < timeoutMillis) + { + if ((detectedBaudrate = testBaudrate())) + { + break; + } + yield(); + delay(100); + } + return detectedBaudrate; +} + size_t HardwareSerial::readBytes(char* buffer, size_t size) { - size_t got = 0; - - while (got < size) - { - esp8266::polledTimeout::oneShotFastMs timeOut(_timeout); - size_t avail; - while ((avail = available()) == 0 && !timeOut); - if (avail == 0) - break; - got += read(buffer + got, std::min(size - got, avail)); - } - return got; + size_t got = 0; + + while (got < size) + { + esp8266::polledTimeout::oneShotFastMs timeOut(_timeout); + size_t avail; + while ((avail = available()) == 0 && !timeOut); + if (avail == 0) + { + break; + } + got += read(buffer + got, std::min(size - got, avail)); + } + return got; } - -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) -HardwareSerial Serial(UART0); -#endif -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL1) -HardwareSerial Serial1(UART1); -#endif + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) +HardwareSerial Serial(UART0); +#endif +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL1) +HardwareSerial Serial1(UART1); +#endif diff --git a/cores/esp8266/HardwareSerial.h b/cores/esp8266/HardwareSerial.h index 88130ec7b3..262fc6b6fe 100644 --- a/cores/esp8266/HardwareSerial.h +++ b/cores/esp8266/HardwareSerial.h @@ -1,28 +1,28 @@ /* - HardwareSerial.h - Hardware serial library for Wiring - Copyright (c) 2006 Nicholas Zambetti. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Modified 28 September 2010 by Mark Sproul - Modified 14 August 2012 by Alarus - Modified 3 December 2013 by Matthijs Kooijman - Modified 18 December 2014 by Ivan Grokhotkov (esp8266 platform support) - Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266) - Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266) - */ + HardwareSerial.h - Hardware serial library for Wiring + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 28 September 2010 by Mark Sproul + Modified 14 August 2012 by Alarus + Modified 3 December 2013 by Matthijs Kooijman + Modified 18 December 2014 by Ivan Grokhotkov (esp8266 platform support) + Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266) + Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266) +*/ #ifndef HardwareSerial_h #define HardwareSerial_h @@ -32,7 +32,8 @@ #include "Stream.h" #include "uart.h" -enum SerialConfig { +enum SerialConfig +{ SERIAL_5N1 = UART_5N1, SERIAL_6N1 = UART_6N1, SERIAL_7N1 = UART_7N1, @@ -59,7 +60,8 @@ enum SerialConfig { SERIAL_8O2 = UART_8O2, }; -enum SerialMode { +enum SerialMode +{ SERIAL_FULL = UART_FULL, SERIAL_RX_ONLY = UART_RX_ONLY, SERIAL_TX_ONLY = UART_TX_ONLY @@ -104,18 +106,18 @@ class HardwareSerial: public Stream } /* - * Toggle between use of GPIO1 and GPIO2 as TX on UART 0. - * Note: UART 1 can't be used if GPIO2 is used with UART 0! - */ + Toggle between use of GPIO1 and GPIO2 as TX on UART 0. + Note: UART 1 can't be used if GPIO2 is used with UART 0! + */ void set_tx(uint8_t tx_pin) { uart_set_tx(_uart, tx_pin); } /* - * UART 0 possible options are (1, 3), (2, 3) or (15, 13) - * UART 1 allows only TX on 2 if UART 0 is not (2, 3) - */ + UART 0 possible options are (1, 3), (2, 3) or (15, 13) + UART 1 allows only TX on 2 if UART 0 is not (2, 3) + */ void pins(uint8_t tx, uint8_t rx) { uart_set_pins(_uart, tx, rx); diff --git a/cores/esp8266/IPAddress.cpp b/cores/esp8266/IPAddress.cpp index d121ba6e53..4ba20e5521 100644 --- a/cores/esp8266/IPAddress.cpp +++ b/cores/esp8266/IPAddress.cpp @@ -1,21 +1,21 @@ /* - IPAddress.cpp - Base class that provides IPAddress - Copyright (c) 2011 Adrian McEwen. All right reserved. + IPAddress.cpp - Base class that provides IPAddress + Copyright (c) 2011 Adrian McEwen. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include #include @@ -27,15 +27,18 @@ IPAddress::IPAddress(const IPAddress& from) ip_addr_copy(_ip, from._ip); } -IPAddress::IPAddress() { +IPAddress::IPAddress() +{ _ip = *IP_ANY_TYPE; // lwIP's v4-or-v6 generic address } -bool IPAddress::isSet () const { +bool IPAddress::isSet() const +{ return !ip_addr_isany(&_ip); } -IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) { +IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) +{ setV4(); (*this)[0] = first_octet; (*this)[1] = second_octet; @@ -43,12 +46,14 @@ IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_oc (*this)[3] = fourth_octet; } -void IPAddress::ctor32(uint32_t address) { +void IPAddress::ctor32(uint32_t address) +{ setV4(); v4() = address; } -IPAddress::IPAddress(const uint8_t *address) { +IPAddress::IPAddress(const uint8_t *address) +{ setV4(); (*this)[0] = address[0]; (*this)[1] = address[1]; @@ -56,8 +61,10 @@ IPAddress::IPAddress(const uint8_t *address) { (*this)[3] = address[3]; } -bool IPAddress::fromString(const char *address) { - if (!fromString4(address)) { +bool IPAddress::fromString(const char *address) +{ + if (!fromString4(address)) + { #if LWIP_IPV6 return fromString6(address); #else @@ -67,7 +74,8 @@ bool IPAddress::fromString(const char *address) { return true; } -bool IPAddress::fromString4(const char *address) { +bool IPAddress::fromString4(const char *address) +{ // TODO: (IPv4) add support for "a", "a.b", "a.b.c" formats uint16_t acc = 0; // Accumulator @@ -79,14 +87,16 @@ bool IPAddress::fromString4(const char *address) { if (c >= '0' && c <= '9') { acc = acc * 10 + (c - '0'); - if (acc > 255) { + if (acc > 255) + { // Value out of [0..255] range return false; } } else if (c == '.') { - if (dots == 3) { + if (dots == 3) + { // Too much dots (there must be 3 dots) return false; } @@ -100,7 +110,8 @@ bool IPAddress::fromString4(const char *address) { } } - if (dots != 3) { + if (dots != 3) + { // Too few dots (there must be 3 dots) return false; } @@ -110,51 +121,70 @@ bool IPAddress::fromString4(const char *address) { return true; } -IPAddress& IPAddress::operator=(const uint8_t *address) { +IPAddress& IPAddress::operator=(const uint8_t *address) +{ setV4(); v4() = *reinterpret_cast(address); return *this; } -IPAddress& IPAddress::operator=(uint32_t address) { +IPAddress& IPAddress::operator=(uint32_t address) +{ setV4(); v4() = address; return *this; } -bool IPAddress::operator==(const uint8_t* addr) const { +bool IPAddress::operator==(const uint8_t* addr) const +{ return isV4() && v4() == *reinterpret_cast(addr); } -size_t IPAddress::printTo(Print& p) const { +size_t IPAddress::printTo(Print& p) const +{ size_t n = 0; if (!isSet()) + { return p.print(F("(IP unset)")); + } #if LWIP_IPV6 - if (isV6()) { + if (isV6()) + { int count0 = 0; - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 8; i++) + { uint16_t bit = PP_NTOHS(raw6()[i]); - if (bit || count0 < 0) { + if (bit || count0 < 0) + { n += p.printf("%x", bit); if (count0 > 0) // no more hiding 0 + { count0 = -8; - } else + } + } + else + { count0++; + } if ((i != 7 && count0 < 2) || count0 == 7) + { n += p.print(':'); + } } return n; } #endif - for(int i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) + { n += p.print((*this)[i], DEC); if (i != 3) + { n += p.print('.'); + } } return n; } @@ -164,7 +194,9 @@ String IPAddress::toString() const StreamString sstr; #if LWIP_IPV6 if (isV6()) - sstr.reserve(40); // 8 shorts x 4 chars each + 7 colons + nullterm + { + sstr.reserve(40); // 8 shorts x 4 chars each + 7 colons + nullterm + } else #endif sstr.reserve(16); // 4 bytes with 3 chars max + 3 dots + nullterm, or '(IP unset)' @@ -172,22 +204,25 @@ String IPAddress::toString() const return sstr; } -bool IPAddress::isValid(const String& arg) { - return IPAddress().fromString(arg); +bool IPAddress::isValid(const String& arg) +{ + return IPAddress().fromString(arg); } -bool IPAddress::isValid(const char* arg) { - return IPAddress().fromString(arg); +bool IPAddress::isValid(const char* arg) +{ + return IPAddress().fromString(arg); } CONST IPAddress INADDR_ANY; // generic "0.0.0.0" for IPv4 & IPv6 -const IPAddress INADDR_NONE(255,255,255,255); +const IPAddress INADDR_NONE(255, 255, 255, 255); /**************************************/ #if LWIP_IPV6 -bool IPAddress::fromString6(const char *address) { +bool IPAddress::fromString6(const char *address) +{ // TODO: test test test uint32_t acc = 0; // Accumulator @@ -196,44 +231,64 @@ bool IPAddress::fromString6(const char *address) { while (*address) { char c = tolower(*address++); - if (isalnum(c)) { + if (isalnum(c)) + { if (c >= 'a') + { c -= 'a' - '0' - 10; + } acc = acc * 16 + (c - '0'); if (acc > 0xffff) // Value out of range + { return false; + } } - else if (c == ':') { - if (*address == ':') { + else if (c == ':') + { + if (*address == ':') + { if (doubledots >= 0) // :: allowed once + { return false; + } // remember location doubledots = dots + !!acc; address++; } if (dots == 7) // too many separators + { return false; + } raw6()[dots++] = PP_HTONS(acc); acc = 0; } else // Invalid char + { return false; + } } if (doubledots == -1 && dots != 7) // Too few separators + { return false; + } raw6()[dots++] = PP_HTONS(acc); - if (doubledots != -1) { + if (doubledots != -1) + { for (int i = dots - doubledots - 1; i >= 0; i--) + { raw6()[8 - dots + doubledots + i] = raw6()[doubledots + i]; + } for (int i = doubledots; i < 8 - dots + doubledots; i++) + { raw6()[i] = 0; + } } setV6(); diff --git a/cores/esp8266/IPAddress.h b/cores/esp8266/IPAddress.h index f4ed5a063d..01d1c18e7d 100644 --- a/cores/esp8266/IPAddress.h +++ b/cores/esp8266/IPAddress.h @@ -1,21 +1,21 @@ /* - IPAddress.h - Base class that provides IPAddress - Copyright (c) 2011 Adrian McEwen. All right reserved. + IPAddress.h - Base class that provides IPAddress + Copyright (c) 2011 Adrian McEwen. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef IPAddress_h #define IPAddress_h @@ -54,167 +54,283 @@ struct ip_addr: ipv4_addr { }; // fully backward compatible with legacy IPv4-only Arduino's // with unchanged footprint when IPv6 is disabled -class IPAddress: public Printable { - private: - - ip_addr_t _ip; - - // Access the raw byte array containing the address. Because this returns a pointer - // to the internal structure rather than a copy of the address this function should only - // be used when you know that the usage of the returned uint8_t* will be transient and not - // stored. - uint8_t* raw_address() { - return reinterpret_cast(&v4()); - } - const uint8_t* raw_address() const { - return reinterpret_cast(&v4()); - } - - void ctor32 (uint32_t); - - public: - // Constructors - IPAddress(); - IPAddress(const IPAddress& from); - IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); - IPAddress(uint32_t address) { ctor32(address); } - IPAddress(u32_t address) { ctor32(address); } - IPAddress(int address) { ctor32(address); } - IPAddress(const uint8_t *address); - - bool fromString(const char *address); - bool fromString(const String &address) { return fromString(address.c_str()); } - - // Overloaded cast operator to allow IPAddress objects to be used where a pointer - // to a four-byte uint8_t array is expected - operator uint32_t() const { return isV4()? v4(): (uint32_t)0; } - operator uint32_t() { return isV4()? v4(): (uint32_t)0; } - operator u32_t() const { return isV4()? v4(): (u32_t)0; } - operator u32_t() { return isV4()? v4(): (u32_t)0; } - - bool isSet () const; - operator bool () const { return isSet(); } // <- - operator bool () { return isSet(); } // <- both are needed - - // generic IPv4 wrapper to uint32-view like arduino loves to see it - const u32_t& v4() const { return ip_2_ip4(&_ip)->addr; } // for raw_address(const) - u32_t& v4() { return ip_2_ip4(&_ip)->addr; } - - bool operator==(const IPAddress& addr) const { - return ip_addr_cmp(&_ip, &addr._ip); - } - bool operator!=(const IPAddress& addr) const { - return !ip_addr_cmp(&_ip, &addr._ip); - } - bool operator==(uint32_t addr) const { - return isV4() && v4() == addr; - } - bool operator==(u32_t addr) const { - return isV4() && v4() == addr; - } - bool operator!=(uint32_t addr) const { - return !(isV4() && v4() == addr); - } - bool operator!=(u32_t addr) const { - return !(isV4() && v4() == addr); - } - bool operator==(const uint8_t* addr) const; - - int operator>>(int n) const { - return isV4()? v4() >> n: 0; - } - - // Overloaded index operator to allow getting and setting individual octets of the address - uint8_t operator[](int index) const { - return isV4()? *(raw_address() + index): 0; - } - uint8_t& operator[](int index) { - setV4(); - return *(raw_address() + index); - } - - // Overloaded copy operators to allow initialisation of IPAddress objects from other types - IPAddress& operator=(const uint8_t *address); - IPAddress& operator=(uint32_t address); - - virtual size_t printTo(Print& p) const; - String toString() const; - - /* - check if input string(arg) is a valid IPV4 address or not. - return true on valid. - return false on invalid. - */ - static bool isValid(const String& arg); - static bool isValid(const char* arg); - - friend class EthernetClass; - friend class UDP; - friend class Client; - friend class Server; - friend class DhcpClass; - friend class DNSClient; - - /* - lwIP address compatibility - */ - IPAddress(const ipv4_addr& fw_addr) { setV4(); v4() = fw_addr.addr; } - IPAddress(const ipv4_addr* fw_addr) { setV4(); v4() = fw_addr->addr; } - - IPAddress& operator=(const ipv4_addr& fw_addr) { setV4(); v4() = fw_addr.addr; return *this; } - IPAddress& operator=(const ipv4_addr* fw_addr) { setV4(); v4() = fw_addr->addr; return *this; } - - operator ip_addr_t () const { return _ip; } - operator const ip_addr_t*() const { return &_ip; } - operator ip_addr_t*() { return &_ip; } - - bool isV4() const { return IP_IS_V4_VAL(_ip); } - void setV4() { IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V4); } - - bool isLocal () const { return ip_addr_islinklocal(&_ip); } +class IPAddress: public Printable +{ +private: + + ip_addr_t _ip; + + // Access the raw byte array containing the address. Because this returns a pointer + // to the internal structure rather than a copy of the address this function should only + // be used when you know that the usage of the returned uint8_t* will be transient and not + // stored. + uint8_t* raw_address() + { + return reinterpret_cast(&v4()); + } + const uint8_t* raw_address() const + { + return reinterpret_cast(&v4()); + } + + void ctor32(uint32_t); + +public: + // Constructors + IPAddress(); + IPAddress(const IPAddress& from); + IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); + IPAddress(uint32_t address) + { + ctor32(address); + } + IPAddress(u32_t address) + { + ctor32(address); + } + IPAddress(int address) + { + ctor32(address); + } + IPAddress(const uint8_t *address); + + bool fromString(const char *address); + bool fromString(const String &address) + { + return fromString(address.c_str()); + } + + // Overloaded cast operator to allow IPAddress objects to be used where a pointer + // to a four-byte uint8_t array is expected + operator uint32_t() const + { + return isV4() ? v4() : (uint32_t)0; + } + operator uint32_t() + { + return isV4() ? v4() : (uint32_t)0; + } + operator u32_t() const + { + return isV4() ? v4() : (u32_t)0; + } + operator u32_t() + { + return isV4() ? v4() : (u32_t)0; + } + + bool isSet() const; + operator bool () const + { + return isSet(); // <- + } + operator bool () + { + return isSet(); // <- both are needed + } + + // generic IPv4 wrapper to uint32-view like arduino loves to see it + const u32_t& v4() const + { + return ip_2_ip4(&_ip)->addr; // for raw_address(const) + } + u32_t& v4() + { + return ip_2_ip4(&_ip)->addr; + } + + bool operator==(const IPAddress& addr) const + { + return ip_addr_cmp(&_ip, &addr._ip); + } + bool operator!=(const IPAddress& addr) const + { + return !ip_addr_cmp(&_ip, &addr._ip); + } + bool operator==(uint32_t addr) const + { + return isV4() && v4() == addr; + } + bool operator==(u32_t addr) const + { + return isV4() && v4() == addr; + } + bool operator!=(uint32_t addr) const + { + return !(isV4() && v4() == addr); + } + bool operator!=(u32_t addr) const + { + return !(isV4() && v4() == addr); + } + bool operator==(const uint8_t* addr) const; + + int operator>>(int n) const + { + return isV4() ? v4() >> n : 0; + } + + // Overloaded index operator to allow getting and setting individual octets of the address + uint8_t operator[](int index) const + { + return isV4() ? *(raw_address() + index) : 0; + } + uint8_t& operator[](int index) + { + setV4(); + return *(raw_address() + index); + } + + // Overloaded copy operators to allow initialisation of IPAddress objects from other types + IPAddress& operator=(const uint8_t *address); + IPAddress& operator=(uint32_t address); + + virtual size_t printTo(Print& p) const; + String toString() const; + + /* + check if input string(arg) is a valid IPV4 address or not. + return true on valid. + return false on invalid. + */ + static bool isValid(const String& arg); + static bool isValid(const char* arg); + + friend class EthernetClass; + friend class UDP; + friend class Client; + friend class Server; + friend class DhcpClass; + friend class DNSClient; + + /* + lwIP address compatibility + */ + IPAddress(const ipv4_addr& fw_addr) + { + setV4(); + v4() = fw_addr.addr; + } + IPAddress(const ipv4_addr* fw_addr) + { + setV4(); + v4() = fw_addr->addr; + } + + IPAddress& operator=(const ipv4_addr& fw_addr) + { + setV4(); + v4() = fw_addr.addr; + return *this; + } + IPAddress& operator=(const ipv4_addr* fw_addr) + { + setV4(); + v4() = fw_addr->addr; + return *this; + } + + operator ip_addr_t () const + { + return _ip; + } + operator const ip_addr_t*() const + { + return &_ip; + } + operator ip_addr_t*() + { + return &_ip; + } + + bool isV4() const + { + return IP_IS_V4_VAL(_ip); + } + void setV4() + { + IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V4); + } + + bool isLocal() const + { + return ip_addr_islinklocal(&_ip); + } #if LWIP_IPV6 - IPAddress(const ip_addr_t& lwip_addr) { ip_addr_copy(_ip, lwip_addr); } - IPAddress(const ip_addr_t* lwip_addr) { ip_addr_copy(_ip, *lwip_addr); } - - IPAddress& operator=(const ip_addr_t& lwip_addr) { ip_addr_copy(_ip, lwip_addr); return *this; } - IPAddress& operator=(const ip_addr_t* lwip_addr) { ip_addr_copy(_ip, *lwip_addr); return *this; } - - uint16_t* raw6() - { - setV6(); - return reinterpret_cast(ip_2_ip6(&_ip)); - } - - const uint16_t* raw6() const - { - return isV6()? reinterpret_cast(ip_2_ip6(&_ip)): nullptr; - } - - // when not IPv6, ip_addr_t == ip4_addr_t so this one would be ambiguous - // required otherwise - operator const ip4_addr_t*() const { return isV4()? ip_2_ip4(&_ip): nullptr; } - - bool isV6() const { return IP_IS_V6_VAL(_ip); } - void setV6() { IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V6); } - - protected: - bool fromString6(const char *address); + IPAddress(const ip_addr_t& lwip_addr) + { + ip_addr_copy(_ip, lwip_addr); + } + IPAddress(const ip_addr_t* lwip_addr) + { + ip_addr_copy(_ip, *lwip_addr); + } + + IPAddress& operator=(const ip_addr_t& lwip_addr) + { + ip_addr_copy(_ip, lwip_addr); + return *this; + } + IPAddress& operator=(const ip_addr_t* lwip_addr) + { + ip_addr_copy(_ip, *lwip_addr); + return *this; + } + + uint16_t* raw6() + { + setV6(); + return reinterpret_cast(ip_2_ip6(&_ip)); + } + + const uint16_t* raw6() const + { + return isV6() ? reinterpret_cast(ip_2_ip6(&_ip)) : nullptr; + } + + // when not IPv6, ip_addr_t == ip4_addr_t so this one would be ambiguous + // required otherwise + operator const ip4_addr_t*() const + { + return isV4() ? ip_2_ip4(&_ip) : nullptr; + } + + bool isV6() const + { + return IP_IS_V6_VAL(_ip); + } + void setV6() + { + IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V6); + } + +protected: + bool fromString6(const char *address); #else - // allow portable code when IPv6 is not enabled - - uint16_t* raw6() { return nullptr; } - const uint16_t* raw6() const { return nullptr; } - bool isV6() const { return false; } - void setV6() { } + // allow portable code when IPv6 is not enabled + + uint16_t* raw6() + { + return nullptr; + } + const uint16_t* raw6() const + { + return nullptr; + } + bool isV6() const + { + return false; + } + void setV6() { } #endif - protected: - bool fromString4(const char *address); +protected: + bool fromString4(const char *address); }; diff --git a/cores/esp8266/MD5Builder.cpp b/cores/esp8266/MD5Builder.cpp index b32693ed73..fb033ceadf 100644 --- a/cores/esp8266/MD5Builder.cpp +++ b/cores/esp8266/MD5Builder.cpp @@ -1,60 +1,72 @@ #include #include -uint8_t hex_char_to_byte(uint8_t c){ - return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) : - (c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) : - (c >= '0' && c<= '9') ? (c - (uint8_t)'0') : 0; +uint8_t hex_char_to_byte(uint8_t c) +{ + return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) : + (c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) : + (c >= '0' && c <= '9') ? (c - (uint8_t)'0') : 0; } -void MD5Builder::begin(void){ +void MD5Builder::begin(void) +{ memset(_buf, 0x00, 16); MD5Init(&_ctx); } -void MD5Builder::add(const uint8_t * data, const uint16_t len){ +void MD5Builder::add(const uint8_t * data, const uint16_t len) +{ MD5Update(&_ctx, data, len); } -void MD5Builder::addHexString(const char * data){ +void MD5Builder::addHexString(const char * data) +{ uint16_t i, len = strlen(data); - uint8_t * tmp = (uint8_t*)malloc(len/2); - if(tmp == NULL) { + uint8_t * tmp = (uint8_t*)malloc(len / 2); + if (tmp == NULL) + { return; } - for(i=0; i 0) && (maxLengthLeft > 0)) { + while ((bytesAvailable > 0) && (maxLengthLeft > 0)) + { // determine number of bytes to read int readBytes = bytesAvailable; - if(readBytes > maxLengthLeft) { + if (readBytes > maxLengthLeft) + { readBytes = maxLengthLeft ; // read only until max_len } - if(readBytes > buf_size) { + if (readBytes > buf_size) + { readBytes = buf_size; // not read more the buffer can handle } // read data and check if we got something int numBytesRead = stream.readBytes(buf, readBytes); - if(numBytesRead< 1) { + if (numBytesRead < 1) + { return false; } @@ -71,21 +83,26 @@ bool MD5Builder::addStream(Stream & stream, const size_t maxLen){ return true; } -void MD5Builder::calculate(void){ +void MD5Builder::calculate(void) +{ MD5Final(_buf, &_ctx); } -void MD5Builder::getBytes(uint8_t * output){ +void MD5Builder::getBytes(uint8_t * output) +{ memcpy(output, _buf, 16); } -void MD5Builder::getChars(char * output){ - for(uint8_t i = 0; i < 16; i++) { +void MD5Builder::getChars(char * output) +{ + for (uint8_t i = 0; i < 16; i++) + { sprintf(output + (i * 2), "%02x", _buf[i]); } } -String MD5Builder::toString(void){ +String MD5Builder::toString(void) +{ char out[33]; getChars(out); return String(out); diff --git a/cores/esp8266/MD5Builder.h b/cores/esp8266/MD5Builder.h index 6c6d560a05..0936a90ca6 100644 --- a/cores/esp8266/MD5Builder.h +++ b/cores/esp8266/MD5Builder.h @@ -1,22 +1,22 @@ /* - md5.h - exposed md5 ROM functions for esp8266 + md5.h - exposed md5 ROM functions for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __ESP8266_MD5_BUILDER__ #define __ESP8266_MD5_BUILDER__ @@ -25,19 +25,35 @@ #include #include "md5.h" -class MD5Builder { - private: +class MD5Builder +{ +private: md5_context_t _ctx; uint8_t _buf[16]; - public: +public: void begin(void); void add(const uint8_t * data, const uint16_t len); - void add(const char * data){ add((const uint8_t*)data, strlen(data)); } - void add(char * data){ add((const char*)data); } - void add(const String& data){ add(data.c_str()); } + void add(const char * data) + { + add((const uint8_t*)data, strlen(data)); + } + void add(char * data) + { + add((const char*)data); + } + void add(const String& data) + { + add(data.c_str()); + } void addHexString(const char * data); - void addHexString(char * data){ addHexString((const char*)data); } - void addHexString(const String& data){ addHexString(data.c_str()); } + void addHexString(char * data) + { + addHexString((const char*)data); + } + void addHexString(const String& data) + { + addHexString(data.c_str()); + } bool addStream(Stream & stream, const size_t maxLen); void calculate(void); void getBytes(uint8_t * output); diff --git a/cores/esp8266/PolledTimeout.h b/cores/esp8266/PolledTimeout.h index 95157d3772..7940003098 100644 --- a/cores/esp8266/PolledTimeout.h +++ b/cores/esp8266/PolledTimeout.h @@ -3,25 +3,25 @@ /* - PolledTimeout.h - Encapsulation of a polled Timeout - - Copyright (c) 2018 Daniel Salazar. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + PolledTimeout.h - Encapsulation of a polled Timeout + + Copyright (c) 2018 Daniel Salazar. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include @@ -39,18 +39,24 @@ namespace YieldPolicy struct DoNothing { - static void execute() {} + static void execute() {} }; struct YieldOrSkip { - static void execute() {delay(0);} + static void execute() + { + delay(0); + } }; template struct YieldAndDelayMs { - static void execute() {delay(delayMs);} + static void execute() + { + delay(delayMs); + } }; } //YieldPolicy @@ -60,74 +66,91 @@ namespace TimePolicy struct TimeSourceMillis { - // time policy in milli-seconds based on millis() + // time policy in milli-seconds based on millis() - using timeType = decltype(millis()); - static timeType time() {return millis();} - static constexpr timeType ticksPerSecond = 1000; - static constexpr timeType ticksPerSecondMax = 1000; + using timeType = decltype(millis()); + static timeType time() + { + return millis(); + } + static constexpr timeType ticksPerSecond = 1000; + static constexpr timeType ticksPerSecondMax = 1000; }; struct TimeSourceCycles { - // time policy based on ESP.getCycleCount() - // this particular time measurement is intended to be called very often - // (every loop, every yield) - - using timeType = decltype(ESP.getCycleCount()); - static timeType time() {return ESP.getCycleCount();} - static constexpr timeType ticksPerSecond = F_CPU; // 80'000'000 or 160'000'000 Hz - static constexpr timeType ticksPerSecondMax = 160000000; // 160MHz + // time policy based on ESP.getCycleCount() + // this particular time measurement is intended to be called very often + // (every loop, every yield) + + using timeType = decltype(ESP.getCycleCount()); + static timeType time() + { + return ESP.getCycleCount(); + } + static constexpr timeType ticksPerSecond = F_CPU; // 80'000'000 or 160'000'000 Hz + static constexpr timeType ticksPerSecondMax = 160000000; // 160MHz }; template - // "second_th" units of timeType for one second +// "second_th" units of timeType for one second struct TimeUnit { - using timeType = typename TimeSourceType::timeType; + using timeType = typename TimeSourceType::timeType; #if __GNUC__ < 5 - // gcc-4.8 cannot compile the constexpr-only version of this function - // using #defines instead luckily works - static constexpr timeType computeRangeCompensation () - { - #define number_of_secondTh_in_one_tick ((1.0 * second_th) / ticksPerSecond) - #define fractional (number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick) - - return ({ - fractional == 0? - 1: // no need for compensation - (number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division - }); - - #undef number_of_secondTh_in_one_tick - #undef fractional - } + // gcc-4.8 cannot compile the constexpr-only version of this function + // using #defines instead luckily works + static constexpr timeType computeRangeCompensation() + { +#define number_of_secondTh_in_one_tick ((1.0 * second_th) / ticksPerSecond) +#define fractional (number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick) + + return ( + { + fractional == 0 ? + 1 : // no need for compensation + (number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division + }); + +#undef number_of_secondTh_in_one_tick +#undef fractional + } #else - static constexpr timeType computeRangeCompensation () - { - return ({ - constexpr double number_of_secondTh_in_one_tick = (1.0 * second_th) / ticksPerSecond; - constexpr double fractional = number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick; - fractional == 0? - 1: // no need for compensation - (number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division - }); - } + static constexpr timeType computeRangeCompensation() + { + return ( + { + constexpr double number_of_secondTh_in_one_tick = (1.0 * second_th) / ticksPerSecond; + constexpr double fractional = number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick; + fractional == 0 ? + 1 : // no need for compensation + (number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division + }); + } #endif - static constexpr timeType ticksPerSecond = TimeSourceType::ticksPerSecond; - static constexpr timeType ticksPerSecondMax = TimeSourceType::ticksPerSecondMax; - static constexpr timeType rangeCompensate = computeRangeCompensation(); - static constexpr timeType user2UnitMultiplierMax = (ticksPerSecondMax * rangeCompensate) / second_th; - static constexpr timeType user2UnitMultiplier = (ticksPerSecond * rangeCompensate) / second_th; - static constexpr timeType user2UnitDivider = rangeCompensate; - // std::numeric_limits::max() is reserved - static constexpr timeType timeMax = (std::numeric_limits::max() - 1) / user2UnitMultiplierMax; - - static timeType toTimeTypeUnit (const timeType userUnit) {return (userUnit * user2UnitMultiplier) / user2UnitDivider;} - static timeType toUserUnit (const timeType internalUnit) {return (internalUnit * user2UnitDivider) / user2UnitMultiplier;} - static timeType time () {return TimeSourceType::time();} + static constexpr timeType ticksPerSecond = TimeSourceType::ticksPerSecond; + static constexpr timeType ticksPerSecondMax = TimeSourceType::ticksPerSecondMax; + static constexpr timeType rangeCompensate = computeRangeCompensation(); + static constexpr timeType user2UnitMultiplierMax = (ticksPerSecondMax * rangeCompensate) / second_th; + static constexpr timeType user2UnitMultiplier = (ticksPerSecond * rangeCompensate) / second_th; + static constexpr timeType user2UnitDivider = rangeCompensate; + // std::numeric_limits::max() is reserved + static constexpr timeType timeMax = (std::numeric_limits::max() - 1) / user2UnitMultiplierMax; + + static timeType toTimeTypeUnit(const timeType userUnit) + { + return (userUnit * user2UnitMultiplier) / user2UnitDivider; + } + static timeType toUserUnit(const timeType internalUnit) + { + return (internalUnit * user2UnitDivider) / user2UnitMultiplier; + } + static timeType time() + { + return TimeSourceType::time(); + } }; using TimeMillis = TimeUnit< TimeSourceMillis, 1000 >; @@ -141,109 +164,113 @@ template ::value == true, "timeType must be unsigned"); - - static constexpr timeType alwaysExpired = 0; - static constexpr timeType neverExpires = std::numeric_limits::max(); - static constexpr timeType rangeCompensate = TimePolicyT::rangeCompensate; //debug - - timeoutTemplate(const timeType userTimeout) - { - reset(userTimeout); - } - - ICACHE_RAM_ATTR - bool expired() - { - YieldPolicyT::execute(); //in case of DoNothing: gets optimized away - if(PeriodicT) //in case of false: gets optimized away - return expiredRetrigger(); - return expiredOneShot(); - } - - ICACHE_RAM_ATTR - operator bool() - { - return expired(); - } - - bool canExpire () const - { - return !_neverExpires; - } - - bool canWait () const - { - return _timeout != alwaysExpired; - } - - void reset(const timeType newUserTimeout) - { - reset(); - _timeout = TimePolicyT::toTimeTypeUnit(newUserTimeout); - _neverExpires = (newUserTimeout < 0) || (newUserTimeout > timeMax()); - } - - void reset() - { - _start = TimePolicyT::time(); - } - - void resetToNeverExpires () - { - _timeout = alwaysExpired + 1; // because canWait() has precedence - _neverExpires = true; - } - - timeType getTimeout() const - { - return TimePolicyT::toUserUnit(_timeout); - } - - static constexpr timeType timeMax() - { - return TimePolicyT::timeMax; - } + using timeType = typename TimePolicyT::timeType; + static_assert(std::is_unsigned::value == true, "timeType must be unsigned"); + + static constexpr timeType alwaysExpired = 0; + static constexpr timeType neverExpires = std::numeric_limits::max(); + static constexpr timeType rangeCompensate = TimePolicyT::rangeCompensate; //debug + + timeoutTemplate(const timeType userTimeout) + { + reset(userTimeout); + } + + ICACHE_RAM_ATTR + bool expired() + { + YieldPolicyT::execute(); //in case of DoNothing: gets optimized away + if (PeriodicT) //in case of false: gets optimized away + { + return expiredRetrigger(); + } + return expiredOneShot(); + } + + ICACHE_RAM_ATTR + operator bool() + { + return expired(); + } + + bool canExpire() const + { + return !_neverExpires; + } + + bool canWait() const + { + return _timeout != alwaysExpired; + } + + void reset(const timeType newUserTimeout) + { + reset(); + _timeout = TimePolicyT::toTimeTypeUnit(newUserTimeout); + _neverExpires = (newUserTimeout < 0) || (newUserTimeout > timeMax()); + } + + void reset() + { + _start = TimePolicyT::time(); + } + + void resetToNeverExpires() + { + _timeout = alwaysExpired + 1; // because canWait() has precedence + _neverExpires = true; + } + + timeType getTimeout() const + { + return TimePolicyT::toUserUnit(_timeout); + } + + static constexpr timeType timeMax() + { + return TimePolicyT::timeMax; + } private: - ICACHE_RAM_ATTR - bool checkExpired(const timeType internalUnit) const - { - // canWait() is not checked here - // returns "can expire" and "time expired" - return (!_neverExpires) && ((internalUnit - _start) >= _timeout); - } + ICACHE_RAM_ATTR + bool checkExpired(const timeType internalUnit) const + { + // canWait() is not checked here + // returns "can expire" and "time expired" + return (!_neverExpires) && ((internalUnit - _start) >= _timeout); + } protected: - ICACHE_RAM_ATTR - bool expiredRetrigger() - { - if (!canWait()) - return true; + ICACHE_RAM_ATTR + bool expiredRetrigger() + { + if (!canWait()) + { + return true; + } + + timeType current = TimePolicyT::time(); + if (checkExpired(current)) + { + unsigned long n = (current - _start) / _timeout; //how many _timeouts periods have elapsed, will usually be 1 (current - _start >= _timeout) + _start += n * _timeout; + return true; + } + return false; + } - timeType current = TimePolicyT::time(); - if(checkExpired(current)) + ICACHE_RAM_ATTR + bool expiredOneShot() const { - unsigned long n = (current - _start) / _timeout; //how many _timeouts periods have elapsed, will usually be 1 (current - _start >= _timeout) - _start += n * _timeout; - return true; + // returns "always expired" or "has expired" + return !canWait() || checkExpired(TimePolicyT::time()); } - return false; - } - - ICACHE_RAM_ATTR - bool expiredOneShot() const - { - // returns "always expired" or "has expired" - return !canWait() || checkExpired(TimePolicyT::time()); - } - - timeType _timeout; - timeType _start; - bool _neverExpires; + + timeType _timeout; + timeType _start; + bool _neverExpires; }; // legacy type names, deprecated (unit is milliseconds) @@ -276,11 +303,11 @@ using periodicFastNs = polledTimeout::timeoutTemplate; - * - * Other policies can be implemented by the user, e.g.: simple yield that panics in SYS, and the polledTimeout types built as needed as shown above, without modifying this file. - */ +/* A 1-shot timeout that auto-yields when in CONT can be built as follows: + using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate; + + Other policies can be implemented by the user, e.g.: simple yield that panics in SYS, and the polledTimeout types built as needed as shown above, without modifying this file. +*/ }//esp8266 diff --git a/cores/esp8266/Print.cpp b/cores/esp8266/Print.cpp index 2870f3f87b..2d92a8d7fc 100644 --- a/cores/esp8266/Print.cpp +++ b/cores/esp8266/Print.cpp @@ -1,25 +1,25 @@ /* - Print.cpp - Base class that provides print() and println() - Copyright (c) 2008 David A. Mellis. All right reserved. + Print.cpp - Base class that provides print() and println() + Copyright (c) 2008 David A. Mellis. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified 23 November 2006 by David A. Mellis - Modified December 2014 by Ivan Grokhotkov - Modified May 2015 by Michael C. Miller - esp8266 progmem support - */ + Modified 23 November 2006 by David A. Mellis + Modified December 2014 by Ivan Grokhotkov + Modified May 2015 by Michael C. Miller - esp8266 progmem support +*/ #include #include @@ -32,21 +32,25 @@ // Public Methods ////////////////////////////////////////////////////////////// /* default implementation: may be overridden */ -size_t Print::write(const uint8_t *buffer, size_t size) { +size_t Print::write(const uint8_t *buffer, size_t size) +{ #ifdef DEBUG_ESP_CORE static char not_the_best_way [] PROGMEM STORE_ATTR = "Print::write(data,len) should be overridden for better efficiency\r\n"; static bool once = false; - if (!once) { + if (!once) + { once = true; os_printf_plus(not_the_best_way); } #endif size_t n = 0; - while (size--) { + while (size--) + { size_t ret = write(*buffer++); - if (ret == 0) { + if (ret == 0) + { // Write of last byte didn't complete, abort additional processing break; } @@ -55,16 +59,19 @@ size_t Print::write(const uint8_t *buffer, size_t size) { return n; } -size_t Print::printf(const char *format, ...) { +size_t Print::printf(const char *format, ...) +{ va_list arg; va_start(arg, format); char temp[64]; char* buffer = temp; size_t len = vsnprintf(temp, sizeof(temp), format, arg); va_end(arg); - if (len > sizeof(temp) - 1) { + if (len > sizeof(temp) - 1) + { buffer = new char[len + 1]; - if (!buffer) { + if (!buffer) + { return 0; } va_start(arg, format); @@ -72,22 +79,26 @@ size_t Print::printf(const char *format, ...) { va_end(arg); } len = write((const uint8_t*) buffer, len); - if (buffer != temp) { + if (buffer != temp) + { delete[] buffer; } return len; } -size_t Print::printf_P(PGM_P format, ...) { +size_t Print::printf_P(PGM_P format, ...) +{ va_list arg; va_start(arg, format); char temp[64]; char* buffer = temp; size_t len = vsnprintf_P(temp, sizeof(temp), format, arg); va_end(arg); - if (len > sizeof(temp) - 1) { + if (len > sizeof(temp) - 1) + { buffer = new char[len + 1]; - if (!buffer) { + if (!buffer) + { return 0; } va_start(arg, format); @@ -95,143 +106,181 @@ size_t Print::printf_P(PGM_P format, ...) { va_end(arg); } len = write((const uint8_t*) buffer, len); - if (buffer != temp) { + if (buffer != temp) + { delete[] buffer; } return len; } -size_t Print::print(const __FlashStringHelper *ifsh) { +size_t Print::print(const __FlashStringHelper *ifsh) +{ PGM_P p = reinterpret_cast(ifsh); size_t n = 0; - while (1) { + while (1) + { uint8_t c = pgm_read_byte(p++); - if (c == 0) break; + if (c == 0) + { + break; + } n += write(c); } return n; } -size_t Print::print(const String &s) { +size_t Print::print(const String &s) +{ return write(s.c_str(), s.length()); } -size_t Print::print(const char str[]) { +size_t Print::print(const char str[]) +{ return write(str); } -size_t Print::print(char c) { +size_t Print::print(char c) +{ return write(c); } -size_t Print::print(unsigned char b, int base) { +size_t Print::print(unsigned char b, int base) +{ return print((unsigned long) b, base); } -size_t Print::print(int n, int base) { +size_t Print::print(int n, int base) +{ return print((long) n, base); } -size_t Print::print(unsigned int n, int base) { +size_t Print::print(unsigned int n, int base) +{ return print((unsigned long) n, base); } -size_t Print::print(long n, int base) { - if(base == 0) { +size_t Print::print(long n, int base) +{ + if (base == 0) + { return write(n); - } else if(base == 10) { - if(n < 0) { + } + else if (base == 10) + { + if (n < 0) + { int t = print('-'); n = -n; return printNumber(n, 10) + t; } return printNumber(n, 10); - } else { + } + else + { return printNumber(n, base); } } -size_t Print::print(unsigned long n, int base) { - if(base == 0) +size_t Print::print(unsigned long n, int base) +{ + if (base == 0) + { return write(n); + } else + { return printNumber(n, base); + } } -size_t Print::print(double n, int digits) { +size_t Print::print(double n, int digits) +{ return printFloat(n, digits); } -size_t Print::println(const __FlashStringHelper *ifsh) { +size_t Print::println(const __FlashStringHelper *ifsh) +{ size_t n = print(ifsh); n += println(); return n; } -size_t Print::print(const Printable& x) { +size_t Print::print(const Printable& x) +{ return x.printTo(*this); } -size_t Print::println(void) { +size_t Print::println(void) +{ return print("\r\n"); } -size_t Print::println(const String &s) { +size_t Print::println(const String &s) +{ size_t n = print(s); n += println(); return n; } -size_t Print::println(const char c[]) { +size_t Print::println(const char c[]) +{ size_t n = print(c); n += println(); return n; } -size_t Print::println(char c) { +size_t Print::println(char c) +{ size_t n = print(c); n += println(); return n; } -size_t Print::println(unsigned char b, int base) { +size_t Print::println(unsigned char b, int base) +{ size_t n = print(b, base); n += println(); return n; } -size_t Print::println(int num, int base) { +size_t Print::println(int num, int base) +{ size_t n = print(num, base); n += println(); return n; } -size_t Print::println(unsigned int num, int base) { +size_t Print::println(unsigned int num, int base) +{ size_t n = print(num, base); n += println(); return n; } -size_t Print::println(long num, int base) { +size_t Print::println(long num, int base) +{ size_t n = print(num, base); n += println(); return n; } -size_t Print::println(unsigned long num, int base) { +size_t Print::println(unsigned long num, int base) +{ size_t n = print(num, base); n += println(); return n; } -size_t Print::println(double num, int digits) { +size_t Print::println(double num, int digits) +{ size_t n = print(num, digits); n += println(); return n; } -size_t Print::println(const Printable& x) { +size_t Print::println(const Printable& x) +{ size_t n = print(x); n += println(); return n; @@ -239,48 +288,64 @@ size_t Print::println(const Printable& x) { // Private Methods ///////////////////////////////////////////////////////////// -size_t Print::printNumber(unsigned long n, uint8_t base) { +size_t Print::printNumber(unsigned long n, uint8_t base) +{ char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte. char *str = &buf[sizeof(buf) - 1]; *str = '\0'; // prevent crash if called with base == 1 - if(base < 2) + if (base < 2) + { base = 10; + } - do { + do + { unsigned long m = n; n /= base; char c = m - base * n; *--str = c < 10 ? c + '0' : c + 'A' - 10; - } while(n); + } while (n); return write(str); } -size_t Print::printFloat(double number, uint8_t digits) { +size_t Print::printFloat(double number, uint8_t digits) +{ size_t n = 0; - if(isnan(number)) + if (isnan(number)) + { return print("nan"); - if(isinf(number)) + } + if (isinf(number)) + { return print("inf"); - if(number > 4294967040.0) - return print("ovf"); // constant determined empirically - if(number < -4294967040.0) - return print("ovf"); // constant determined empirically + } + if (number > 4294967040.0) + { + return print("ovf"); // constant determined empirically + } + if (number < -4294967040.0) + { + return print("ovf"); // constant determined empirically + } // Handle negative numbers - if(number < 0.0) { + if (number < 0.0) + { n += print('-'); number = -number; } // Round correctly so that print(1.999, 2) prints as "2.00" double rounding = 0.5; - for(uint8_t i = 0; i < digits; ++i) + for (uint8_t i = 0; i < digits; ++i) + { rounding /= 10.0; + } number += rounding; @@ -290,12 +355,14 @@ size_t Print::printFloat(double number, uint8_t digits) { n += print(int_part); // Print the decimal point, but only if there are digits beyond - if(digits > 0) { + if (digits > 0) + { n += print("."); } // Extract digits from the remainder one at a time - while(digits-- > 0) { + while (digits-- > 0) + { remainder *= 10.0; int toPrint = int(remainder); n += print(toPrint); diff --git a/cores/esp8266/Print.h b/cores/esp8266/Print.h index b92bcc6e74..fe03d1ae07 100644 --- a/cores/esp8266/Print.h +++ b/cores/esp8266/Print.h @@ -1,21 +1,21 @@ /* - Print.h - Base class that provides print() and println() - Copyright (c) 2008 David A. Mellis. All right reserved. + Print.h - Base class that provides print() and println() + Copyright (c) 2008 David A. Mellis. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef Print_h #define Print_h @@ -31,73 +31,100 @@ #define OCT 8 #define BIN 2 -class Print { - private: - int write_error; - size_t printNumber(unsigned long, uint8_t); - size_t printFloat(double, uint8_t); - protected: - void setWriteError(int err = 1) { - write_error = err; - } - public: - Print() : - write_error(0) { - } +class Print +{ +private: + int write_error; + size_t printNumber(unsigned long, uint8_t); + size_t printFloat(double, uint8_t); +protected: + void setWriteError(int err = 1) + { + write_error = err; + } +public: + Print() : + write_error(0) + { + } - int getWriteError() { - return write_error; - } - void clearWriteError() { - setWriteError(0); - } + int getWriteError() + { + return write_error; + } + void clearWriteError() + { + setWriteError(0); + } - virtual size_t write(uint8_t) = 0; - size_t write(const char *str) { - if(str == NULL) - return 0; - return write((const uint8_t *) str, strlen(str)); - } - virtual size_t write(const uint8_t *buffer, size_t size); - size_t write(const char *buffer, size_t size) { - return write((const uint8_t *) buffer, size); + virtual size_t write(uint8_t) = 0; + size_t write(const char *str) + { + if (str == NULL) + { + return 0; } - // These handle ambiguity for write(0) case, because (0) can be a pointer or an integer - size_t write(short t) { return write((uint8_t)t); } - size_t write(unsigned short t) { return write((uint8_t)t); } - size_t write(int t) { return write((uint8_t)t); } - size_t write(unsigned int t) { return write((uint8_t)t); } - size_t write(long t) { return write((uint8_t)t); } - size_t write(unsigned long t) { return write((uint8_t)t); } + return write((const uint8_t *) str, strlen(str)); + } + virtual size_t write(const uint8_t *buffer, size_t size); + size_t write(const char *buffer, size_t size) + { + return write((const uint8_t *) buffer, size); + } + // These handle ambiguity for write(0) case, because (0) can be a pointer or an integer + size_t write(short t) + { + return write((uint8_t)t); + } + size_t write(unsigned short t) + { + return write((uint8_t)t); + } + size_t write(int t) + { + return write((uint8_t)t); + } + size_t write(unsigned int t) + { + return write((uint8_t)t); + } + size_t write(long t) + { + return write((uint8_t)t); + } + size_t write(unsigned long t) + { + return write((uint8_t)t); + } - size_t printf(const char * format, ...) __attribute__ ((format (printf, 2, 3))); - size_t printf_P(PGM_P format, ...) __attribute__((format(printf, 2, 3))); - size_t print(const __FlashStringHelper *); - size_t print(const String &); - size_t print(const char[]); - size_t print(char); - size_t print(unsigned char, int = DEC); - size_t print(int, int = DEC); - size_t print(unsigned int, int = DEC); - size_t print(long, int = DEC); - size_t print(unsigned long, int = DEC); - size_t print(double, int = 2); - size_t print(const Printable&); + size_t printf(const char * format, ...) __attribute__((format(printf, 2, 3))); + size_t printf_P(PGM_P format, ...) __attribute__((format(printf, 2, 3))); + size_t print(const __FlashStringHelper *); + size_t print(const String &); + size_t print(const char[]); + size_t print(char); + size_t print(unsigned char, int = DEC); + size_t print(int, int = DEC); + size_t print(unsigned int, int = DEC); + size_t print(long, int = DEC); + size_t print(unsigned long, int = DEC); + size_t print(double, int = 2); + size_t print(const Printable&); - size_t println(const __FlashStringHelper *); - size_t println(const String &s); - size_t println(const char[]); - size_t println(char); - size_t println(unsigned char, int = DEC); - size_t println(int, int = DEC); - size_t println(unsigned int, int = DEC); - size_t println(long, int = DEC); - size_t println(unsigned long, int = DEC); - size_t println(double, int = 2); - size_t println(const Printable&); - size_t println(void); + size_t println(const __FlashStringHelper *); + size_t println(const String &s); + size_t println(const char[]); + size_t println(char); + size_t println(unsigned char, int = DEC); + size_t println(int, int = DEC); + size_t println(unsigned int, int = DEC); + size_t println(long, int = DEC); + size_t println(unsigned long, int = DEC); + size_t println(double, int = 2); + size_t println(const Printable&); + size_t println(void); - virtual void flush() { /* Empty implementation for backward compatibility */ } + virtual void flush() { /* Empty implementation for backward compatibility */ } }; #endif diff --git a/cores/esp8266/Printable.h b/cores/esp8266/Printable.h index 072b480690..09eb8a1df1 100644 --- a/cores/esp8266/Printable.h +++ b/cores/esp8266/Printable.h @@ -1,21 +1,21 @@ /* - Printable.h - Interface class that allows printing of complex types - Copyright (c) 2011 Adrian McEwen. All right reserved. + Printable.h - Interface class that allows printing of complex types + Copyright (c) 2011 Adrian McEwen. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef Printable_h #define Printable_h @@ -25,14 +25,15 @@ class Print; /** The Printable class provides a way for new classes to allow themselves to be printed. - By deriving from Printable and implementing the printTo method, it will then be possible - for users to print out instances of this class by passing them into the usual - Print::print and Print::println methods. - */ - -class Printable { - public: - virtual size_t printTo(Print& p) const = 0; + By deriving from Printable and implementing the printTo method, it will then be possible + for users to print out instances of this class by passing them into the usual + Print::print and Print::println methods. +*/ + +class Printable +{ +public: + virtual size_t printTo(Print& p) const = 0; }; #endif diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 27b9731954..c8eb5d706c 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -14,18 +14,22 @@ static scheduled_fn_t* sLastUnused = 0; static int sCount = 0; -static scheduled_fn_t* get_fn() { +static scheduled_fn_t* get_fn() +{ scheduled_fn_t* result = NULL; // try to get an item from unused items list - if (sFirstUnused) { + if (sFirstUnused) + { result = sFirstUnused; sFirstUnused = result->mNext; - if (sFirstUnused == NULL) { + if (sFirstUnused == NULL) + { sLastUnused = NULL; } } // if no unused items, and count not too high, allocate a new one - else if (sCount != SCHEDULED_FN_MAX_COUNT) { + else if (sCount != SCHEDULED_FN_MAX_COUNT) + { result = new scheduled_fn_t; result->mNext = NULL; ++sCount; @@ -35,10 +39,12 @@ static scheduled_fn_t* get_fn() { static void recycle_fn(scheduled_fn_t* fn) { - if (!sLastUnused) { + if (!sLastUnused) + { sFirstUnused = fn; } - else { + else + { sLastUnused->mNext = fn; } fn->mNext = NULL; @@ -48,15 +54,18 @@ static void recycle_fn(scheduled_fn_t* fn) bool schedule_function(std::function fn) { scheduled_fn_t* item = get_fn(); - if (!item) { + if (!item) + { return false; } item->mFunc = fn; item->mNext = NULL; - if (!sFirst) { + if (!sFirst) + { sFirst = item; } - else { + else + { sLast->mNext = item; } sLast = item; @@ -65,10 +74,11 @@ bool schedule_function(std::function fn) void run_scheduled_functions() { - scheduled_fn_t* rFirst = sFirst; - sFirst = NULL; - sLast = NULL; - while (rFirst) { + scheduled_fn_t* rFirst = sFirst; + sFirst = NULL; + sLast = NULL; + while (rFirst) + { scheduled_fn_t* item = rFirst; rFirst = item->mNext; item->mFunc(); diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 3399972945..0868f27c8c 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -6,12 +6,12 @@ #define SCHEDULED_FN_MAX_COUNT 32 #define SCHEDULED_FN_INITIAL_COUNT 4 -// Warning -// This API is not considered stable. +// Warning +// This API is not considered stable. // Function signatures will change. // You have been warned. -// Run given function next time `loop` function returns, +// Run given function next time `loop` function returns, // or `run_scheduled_functions` is called. // Use std::bind to pass arguments to a function, or call a class member function. // Note: there is no mechanism for cancelling scheduled functions. @@ -19,7 +19,7 @@ // Returns false if the number of scheduled functions exceeds SCHEDULED_FN_MAX_COUNT. bool schedule_function(std::function fn); -// Run all scheduled functions. +// Run all scheduled functions. // Use this function if your are not using `loop`, or `loop` does not return // on a regular basis. void run_scheduled_functions(); diff --git a/cores/esp8266/ScheduledFunctions.cpp b/cores/esp8266/ScheduledFunctions.cpp index 25bc58db61..ef1c47ad37 100644 --- a/cores/esp8266/ScheduledFunctions.cpp +++ b/cores/esp8266/ScheduledFunctions.cpp @@ -1,117 +1,118 @@ /* - * ScheduledFunctions.cpp - * - * Created on: 27 apr. 2018 - * Author: Herman - */ + ScheduledFunctions.cpp + + Created on: 27 apr. 2018 + Author: Herman +*/ #include "ScheduledFunctions.h" std::list ScheduledFunctions::scheduledFunctions; ScheduledFunctions::ScheduledFunctions() -:ScheduledFunctions(UINT_MAX) + : ScheduledFunctions(UINT_MAX) { } ScheduledFunctions::ScheduledFunctions(unsigned int reqMax) { - maxElements = reqMax; + maxElements = reqMax; } -ScheduledFunctions::~ScheduledFunctions() { +ScheduledFunctions::~ScheduledFunctions() +{ } ScheduledRegistration ScheduledFunctions::insertElement(ScheduledElement se, bool front) { - if (countElements >= maxElements) - { - return nullptr; - } - else - { - countElements++; - if (front) - { - scheduledFunctions.push_front(se); - return scheduledFunctions.begin()->registration; - } - else - { - scheduledFunctions.push_back(se); - return scheduledFunctions.rbegin()->registration; - } - } + if (countElements >= maxElements) + { + return nullptr; + } + else + { + countElements++; + if (front) + { + scheduledFunctions.push_front(se); + return scheduledFunctions.begin()->registration; + } + else + { + scheduledFunctions.push_back(se); + return scheduledFunctions.rbegin()->registration; + } + } } std::list::iterator ScheduledFunctions::eraseElement(std::list::iterator it) { - countElements--; - return scheduledFunctions.erase(it); + countElements--; + return scheduledFunctions.erase(it); } bool ScheduledFunctions::scheduleFunction(ScheduledFunction sf, bool continuous, bool front) { - return (insertElement({this,continuous,nullptr,sf}, front) == nullptr); + return (insertElement({this, continuous, nullptr, sf}, front) == nullptr); } bool ScheduledFunctions::scheduleFunction(ScheduledFunction sf) { - return scheduleFunction(sf, false, false); + return scheduleFunction(sf, false, false); } -ScheduledRegistration ScheduledFunctions::scheduleFunctionReg (ScheduledFunction sf, bool continuous, bool front) +ScheduledRegistration ScheduledFunctions::scheduleFunctionReg(ScheduledFunction sf, bool continuous, bool front) { - return insertElement({this,continuous,std::make_shared(1),sf},front); + return insertElement({this, continuous, std::make_shared(1), sf}, front); } void ScheduledFunctions::runScheduledFunctions() { - auto lastElement = scheduledFunctions.end(); // do not execute elements added during runScheduledFunctions - auto it = scheduledFunctions.begin(); - while (it != lastElement) - { - bool erase = false; - if (it->registration == nullptr) - { - it->function(); - } - else - { - if (it->registration.use_count() > 1) - { - it->function(); - } - else - { - erase = true; - } - } - if ((!it->continuous) || (erase)) - { - it = it->_this->eraseElement(it); - } - else - { - it++; - } - } + auto lastElement = scheduledFunctions.end(); // do not execute elements added during runScheduledFunctions + auto it = scheduledFunctions.begin(); + while (it != lastElement) + { + bool erase = false; + if (it->registration == nullptr) + { + it->function(); + } + else + { + if (it->registration.use_count() > 1) + { + it->function(); + } + else + { + erase = true; + } + } + if ((!it->continuous) || (erase)) + { + it = it->_this->eraseElement(it); + } + else + { + it++; + } + } } void ScheduledFunctions::removeFunction(ScheduledRegistration sr) { - auto it = scheduledFunctions.begin(); - bool removed = false; - while ((!removed) && (it != scheduledFunctions.end())) - { - if (it->registration == sr) - { - it = eraseElement(it); - removed = true; - } - else - { - it++; - } - } + auto it = scheduledFunctions.begin(); + bool removed = false; + while ((!removed) && (it != scheduledFunctions.end())) + { + if (it->registration == sr) + { + it = eraseElement(it); + removed = true; + } + else + { + it++; + } + } } diff --git a/cores/esp8266/ScheduledFunctions.h b/cores/esp8266/ScheduledFunctions.h index 0129635364..48608599be 100644 --- a/cores/esp8266/ScheduledFunctions.h +++ b/cores/esp8266/ScheduledFunctions.h @@ -1,9 +1,9 @@ /* - * ScheduledFunctions.h - * - * Created on: 27 apr. 2018 - * Author: Herman - */ + ScheduledFunctions.h + + Created on: 27 apr. 2018 + Author: Herman +*/ #include "Arduino.h" #include "Schedule.h" @@ -18,33 +18,34 @@ typedef std::function ScheduledFunction; typedef std::shared_ptr ScheduledRegistration; -class ScheduledFunctions { +class ScheduledFunctions +{ public: - ScheduledFunctions(); - ScheduledFunctions(unsigned int reqMax); - virtual ~ScheduledFunctions(); - - struct ScheduledElement - { - ScheduledFunctions* _this; - bool continuous; - ScheduledRegistration registration; - ScheduledFunction function; - }; - - ScheduledRegistration insertElement(ScheduledElement se, bool front); - std::list::iterator eraseElement(std::list::iterator); - bool scheduleFunction(ScheduledFunction sf, bool continuous, bool front); - bool scheduleFunction(ScheduledFunction sf); - ScheduledRegistration scheduleFunctionReg (ScheduledFunction sf, bool continuous, bool front); - static void runScheduledFunctions(); - void removeFunction(ScheduledRegistration sr); - - - static std::list scheduledFunctions; - unsigned int maxElements; - unsigned int countElements = 0; + ScheduledFunctions(); + ScheduledFunctions(unsigned int reqMax); + virtual ~ScheduledFunctions(); + + struct ScheduledElement + { + ScheduledFunctions* _this; + bool continuous; + ScheduledRegistration registration; + ScheduledFunction function; + }; + + ScheduledRegistration insertElement(ScheduledElement se, bool front); + std::list::iterator eraseElement(std::list::iterator); + bool scheduleFunction(ScheduledFunction sf, bool continuous, bool front); + bool scheduleFunction(ScheduledFunction sf); + ScheduledRegistration scheduleFunctionReg(ScheduledFunction sf, bool continuous, bool front); + static void runScheduledFunctions(); + void removeFunction(ScheduledRegistration sr); + + + static std::list scheduledFunctions; + unsigned int maxElements; + unsigned int countElements = 0; }; diff --git a/cores/esp8266/Server.h b/cores/esp8266/Server.h index db5369f433..c9f21ec141 100644 --- a/cores/esp8266/Server.h +++ b/cores/esp8266/Server.h @@ -1,30 +1,31 @@ /* - Server.h - Base class that provides Server - Copyright (c) 2011 Adrian McEwen. All right reserved. + Server.h - Base class that provides Server + Copyright (c) 2011 Adrian McEwen. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef server_h #define server_h #include "Print.h" -class Server: public Print { - public: - virtual void begin() =0; +class Server: public Print +{ +public: + virtual void begin() = 0; }; #endif diff --git a/cores/esp8266/StackThunk.cpp b/cores/esp8266/StackThunk.cpp index 4d86875c97..16ab0af1d6 100644 --- a/cores/esp8266/StackThunk.cpp +++ b/cores/esp8266/StackThunk.cpp @@ -1,27 +1,27 @@ /* - StackThunk.c - Allow use second stack for BearSSL calls + StackThunk.c - Allow use second stack for BearSSL calls - BearSSL uses a significant amount of stack space, much larger than - the default Arduino core stack. These routines handle swapping - between a secondary, user-allocated stack on the heap and the real - stack. + BearSSL uses a significant amount of stack space, much larger than + the default Arduino core stack. These routines handle swapping + between a secondary, user-allocated stack on the heap and the real + stack. - Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. + Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) */ #include @@ -31,97 +31,111 @@ extern "C" { -uint32_t *stack_thunk_ptr = NULL; -uint32_t *stack_thunk_top = NULL; -uint32_t *stack_thunk_save = NULL; /* Saved A1 while in BearSSL */ -uint32_t stack_thunk_refcnt = 0; + uint32_t *stack_thunk_ptr = NULL; + uint32_t *stack_thunk_top = NULL; + uint32_t *stack_thunk_save = NULL; /* Saved A1 while in BearSSL */ + uint32_t stack_thunk_refcnt = 0; #define _stackSize (5600/4) #define _stackPaint 0xdeadbeef -/* Add a reference, and allocate the stack if necessary */ -void stack_thunk_add_ref() -{ - stack_thunk_refcnt++; - if (stack_thunk_refcnt == 1) { - stack_thunk_ptr = (uint32_t *)malloc(_stackSize * sizeof(uint32_t)); - stack_thunk_top = stack_thunk_ptr + _stackSize - 1; - stack_thunk_save = NULL; - stack_thunk_repaint(); - } -} - -/* Drop a reference, and free stack if no more in use */ -void stack_thunk_del_ref() -{ - if (stack_thunk_refcnt == 0) { - /* Error! */ - return; - } - stack_thunk_refcnt--; - if (!stack_thunk_refcnt) { - free(stack_thunk_ptr); - stack_thunk_ptr = NULL; - stack_thunk_top = NULL; - stack_thunk_save = NULL; - } -} - -void stack_thunk_repaint() -{ - for (int i=0; i < _stackSize; i++) { - stack_thunk_ptr[i] = _stackPaint; - } -} - -/* Simple accessor functions used by postmortem */ -uint32_t stack_thunk_get_refcnt() { - return stack_thunk_refcnt; -} - -uint32_t stack_thunk_get_stack_top() { - return (uint32_t)stack_thunk_top; -} - -uint32_t stack_thunk_get_stack_bot() { - return (uint32_t)stack_thunk_ptr; -} - -uint32_t stack_thunk_get_cont_sp() { - return (uint32_t)stack_thunk_save; -} - -/* Return the number of bytes ever used since the stack was created */ -uint32_t stack_thunk_get_max_usage() -{ - uint32_t cnt = 0; - - /* No stack == no usage by definition! */ - if (!stack_thunk_ptr) { - return 0; - } - - for (cnt=0; (cnt < _stackSize) && (stack_thunk_ptr[cnt] == _stackPaint); cnt++) { - /* Noop, all work done in for() */ - } - return 4 * (_stackSize - cnt); -} - -/* Print the stack from the first used 16-byte chunk to the top, decodable by the exception decoder */ -void stack_thunk_dump_stack() -{ - uint32_t *pos = stack_thunk_top; - while (pos < stack_thunk_ptr) { - if ((pos[0] != _stackPaint) || (pos[1] != _stackPaint) || (pos[2] != _stackPaint) || (pos[3] != _stackPaint)) - break; - pos += 4; - } - ets_printf(">>>stack>>>\n"); - while (pos < stack_thunk_ptr) { - ets_printf("%08x: %08x %08x %08x %08x\n", (int32_t)pos, pos[0], pos[1], pos[2], pos[3]); - pos += 4; - } - ets_printf("<<>>stack>>>\n"); + while (pos < stack_thunk_ptr) + { + ets_printf("%08x: %08x %08x %08x %08x\n", (int32_t)pos, pos[0], pos[1], pos[2], pos[3]); + pos += 4; + } + ets_printf("<< #include @@ -26,43 +26,59 @@ #define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field // private method to read stream with timeout -int Stream::timedRead() { +int Stream::timedRead() +{ int c; _startMillis = millis(); - do { + do + { c = read(); - if(c >= 0) + if (c >= 0) + { return c; + } yield(); - } while(millis() - _startMillis < _timeout); + } while (millis() - _startMillis < _timeout); return -1; // -1 indicates timeout } // private method to peek stream with timeout -int Stream::timedPeek() { +int Stream::timedPeek() +{ int c; _startMillis = millis(); - do { + do + { c = peek(); - if(c >= 0) + if (c >= 0) + { return c; + } yield(); - } while(millis() - _startMillis < _timeout); + } while (millis() - _startMillis < _timeout); return -1; // -1 indicates timeout } // returns peek of the next digit in the stream or -1 if timeout // discards non-numeric characters -int Stream::peekNextDigit() { +int Stream::peekNextDigit() +{ int c; - while(1) { + while (1) + { c = timedPeek(); - if(c < 0) - return c; // timeout - if(c == '-') + if (c < 0) + { + return c; // timeout + } + if (c == '-') + { return c; - if(c >= '0' && c <= '9') + } + if (c >= '0' && c <= '9') + { return c; + } read(); // discard non-numeric } } @@ -76,48 +92,65 @@ void Stream::setTimeout(unsigned long timeout) // sets the maximum number of mi } // find returns true if the target string is found -bool Stream::find(const char *target) { +bool Stream::find(const char *target) +{ return findUntil(target, (char*) ""); } // reads data from the stream until the target string of given length is found // returns true if target string is found, false if timed out -bool Stream::find(const char *target, size_t length) { +bool Stream::find(const char *target, size_t length) +{ return findUntil(target, length, NULL, 0); } // as find but search ends if the terminator string is found -bool Stream::findUntil(const char *target, const char *terminator) { +bool Stream::findUntil(const char *target, const char *terminator) +{ return findUntil(target, strlen(target), terminator, strlen(terminator)); } // reads data from the stream until the target string of the given length is found // search terminated if the terminator string is found // returns true if target string is found, false if terminated or timed out -bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen) { +bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen) +{ size_t index = 0; // maximum target string length is 64k bytes! size_t termIndex = 0; int c; - if(*target == 0) - return true; // return true if target is a null string - while((c = timedRead()) > 0) { + if (*target == 0) + { + return true; // return true if target is a null string + } + while ((c = timedRead()) > 0) + { - if(c != target[index]) - index = 0; // reset index if any char does not match + if (c != target[index]) + { + index = 0; // reset index if any char does not match + } - if(c == target[index]) { + if (c == target[index]) + { //////Serial.print("found "); Serial.write(c); Serial.print("index now"); Serial.println(index+1); - if(++index >= targetLen) { // return true if all chars in the target match + if (++index >= targetLen) // return true if all chars in the target match + { return true; } } - if(termLen > 0 && c == terminator[termIndex]) { - if(++termIndex >= termLen) - return false; // return false if terminate string found before target string - } else + if (termLen > 0 && c == terminator[termIndex]) + { + if (++termIndex >= termLen) + { + return false; // return false if terminate string found before target string + } + } + else + { termIndex = 0; + } } return false; } @@ -125,46 +158,59 @@ bool Stream::findUntil(const char *target, size_t targetLen, const char *termina // returns the first valid (long) integer value from the current position. // initial characters that are not digits (or the minus sign) are skipped // function is terminated by the first character that is not a digit. -long Stream::parseInt() { +long Stream::parseInt() +{ return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout) } // as above but a given skipChar is ignored // this allows format characters (typically commas) in values to be ignored -long Stream::parseInt(char skipChar) { +long Stream::parseInt(char skipChar) +{ boolean isNegative = false; long value = 0; int c; c = peekNextDigit(); // ignore non numeric leading characters - if(c < 0) - return 0; // zero returned if timeout + if (c < 0) + { + return 0; // zero returned if timeout + } - do { - if(c == skipChar) + do + { + if (c == skipChar) ; // ignore this charactor - else if(c == '-') + else if (c == '-') + { isNegative = true; - else if(c >= '0' && c <= '9') // is c a digit? + } + else if (c >= '0' && c <= '9') // is c a digit? + { value = value * 10 + c - '0'; + } read(); // consume the character we got with peek c = timedPeek(); - } while((c >= '0' && c <= '9') || c == skipChar); + } while ((c >= '0' && c <= '9') || c == skipChar); - if(isNegative) + if (isNegative) + { value = -value; + } return value; } // as parseInt but returns a floating point value -float Stream::parseFloat() { +float Stream::parseFloat() +{ return parseFloat(NO_SKIP_CHAR); } // as above but the given skipChar is ignored // this allows format characters (typically commas) in values to be ignored -float Stream::parseFloat(char skipChar) { +float Stream::parseFloat(char skipChar) +{ boolean isNegative = false; boolean isFraction = false; long value = 0; @@ -173,31 +219,47 @@ float Stream::parseFloat(char skipChar) { c = peekNextDigit(); // ignore non numeric leading characters - if(c < 0) - return 0; // zero returned if timeout + if (c < 0) + { + return 0; // zero returned if timeout + } - do { - if(c == skipChar) + do + { + if (c == skipChar) ; // ignore - else if(c == '-') + else if (c == '-') + { isNegative = true; - else if(c == '.') + } + else if (c == '.') + { isFraction = true; - else if(c >= '0' && c <= '9') { // is c a digit? + } + else if (c >= '0' && c <= '9') // is c a digit? + { value = value * 10 + c - '0'; - if(isFraction) + if (isFraction) + { fraction *= 0.1; + } } read(); // consume the character we got with peek c = timedPeek(); - } while((c >= '0' && c <= '9') || c == '.' || c == skipChar); + } while ((c >= '0' && c <= '9') || c == '.' || c == skipChar); - if(isNegative) + if (isNegative) + { value = -value; - if(isFraction) + } + if (isFraction) + { return value * fraction; + } else + { return value; + } } // read characters from stream into buffer @@ -205,12 +267,16 @@ float Stream::parseFloat(char skipChar) { // returns the number of characters placed in the buffer // the buffer is NOT null terminated. // -size_t Stream::readBytes(char *buffer, size_t length) { +size_t Stream::readBytes(char *buffer, size_t length) +{ size_t count = 0; - while(count < length) { + while (count < length) + { int c = timedRead(); - if(c < 0) + if (c < 0) + { break; + } *buffer++ = (char) c; count++; } @@ -221,34 +287,44 @@ size_t Stream::readBytes(char *buffer, size_t length) { // terminates if length characters have been read, timeout, or if the terminator character detected // returns the number of characters placed in the buffer (0 means no valid data found) -size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) { - if(length < 1) +size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) +{ + if (length < 1) + { return 0; + } size_t index = 0; - while(index < length) { + while (index < length) + { int c = timedRead(); - if(c < 0 || c == terminator) + if (c < 0 || c == terminator) + { break; + } *buffer++ = (char) c; index++; } return index; // return number of characters, not including null terminator } -String Stream::readString() { +String Stream::readString() +{ String ret; int c = timedRead(); - while(c >= 0) { + while (c >= 0) + { ret += (char) c; c = timedRead(); } return ret; } -String Stream::readStringUntil(char terminator) { +String Stream::readStringUntil(char terminator) +{ String ret; int c = timedRead(); - while(c >= 0 && c != terminator) { + while (c >= 0 && c != terminator) + { ret += (char) c; c = timedRead(); } diff --git a/cores/esp8266/Stream.h b/cores/esp8266/Stream.h index fa786dddc3..4ade72a735 100644 --- a/cores/esp8266/Stream.h +++ b/cores/esp8266/Stream.h @@ -1,23 +1,23 @@ /* - Stream.h - base class for character-based streams. - Copyright (c) 2010 David A. Mellis. All right reserved. + Stream.h - base class for character-based streams. + Copyright (c) 2010 David A. Mellis. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - parsing functions based on TextFinder library by Michael Margolis - */ + parsing functions based on TextFinder library by Michael Margolis +*/ #ifndef Stream_h #define Stream_h @@ -27,89 +27,100 @@ // compatability macros for testing /* - #define getInt() parseInt() - #define getInt(skipChar) parseInt(skipchar) - #define getFloat() parseFloat() - #define getFloat(skipChar) parseFloat(skipChar) - #define getString( pre_string, post_string, buffer, length) - readBytesBetween( pre_string, terminator, buffer, length) - */ - -class Stream: public Print { - protected: - unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read - unsigned long _startMillis; // used for timeout measurement - int timedRead(); // private method to read stream with timeout - int timedPeek(); // private method to peek stream with timeout - int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout - - public: - virtual int available() = 0; - virtual int read() = 0; - virtual int peek() = 0; - - Stream() { - _timeout = 1000; - } - -// parsing methods - - void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second - - bool find(const char *target); // reads data from the stream until the target string is found - bool find(uint8_t *target) { - return find((char *) target); - } - // returns true if target string is found, false if timed out (see setTimeout) - - bool find(const char *target, size_t length); // reads data from the stream until the target string of given length is found - bool find(const uint8_t *target, size_t length) { - return find((char *) target, length); - } - // returns true if target string is found, false if timed out - - bool find(char target) { return find (&target, 1); } - - bool findUntil(const char *target, const char *terminator); // as find but search ends if the terminator string is found - bool findUntil(const uint8_t *target, const char *terminator) { - return findUntil((char *) target, terminator); - } - - bool findUntil(const char *target, size_t targetLen, const char *terminate, size_t termLen); // as above but search ends if the terminate string is found - bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen) { - return findUntil((char *) target, targetLen, terminate, termLen); - } - - long parseInt(); // returns the first valid (long) integer value from the current position. - // initial characters that are not digits (or the minus sign) are skipped - // integer is terminated by the first character that is not a digit. - - float parseFloat(); // float version of parseInt - - virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer - virtual size_t readBytes(uint8_t *buffer, size_t length) { - return readBytes((char *) buffer, length); - } - // terminates if length characters have been read or timeout (see setTimeout) - // returns the number of characters placed in the buffer (0 means no valid data found) - - size_t readBytesUntil(char terminator, char *buffer, size_t length); // as readBytes with terminator character - size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length) { - return readBytesUntil(terminator, (char *) buffer, length); - } - // terminates if length characters have been read, timeout, or if the terminator character detected - // returns the number of characters placed in the buffer (0 means no valid data found) - - // Arduino String functions to be added here - virtual String readString(); - String readStringUntil(char terminator); - - protected: - long parseInt(char skipChar); // as above but the given skipChar is ignored - // as above but the given skipChar is ignored - // this allows format characters (typically commas) in values to be ignored - - float parseFloat(char skipChar); // as above but the given skipChar is ignored + #define getInt() parseInt() + #define getInt(skipChar) parseInt(skipchar) + #define getFloat() parseFloat() + #define getFloat(skipChar) parseFloat(skipChar) + #define getString( pre_string, post_string, buffer, length) + readBytesBetween( pre_string, terminator, buffer, length) +*/ + +class Stream: public Print +{ +protected: + unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read + unsigned long _startMillis; // used for timeout measurement + int timedRead(); // private method to read stream with timeout + int timedPeek(); // private method to peek stream with timeout + int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout + +public: + virtual int available() = 0; + virtual int read() = 0; + virtual int peek() = 0; + + Stream() + { + _timeout = 1000; + } + + // parsing methods + + void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second + + bool find(const char *target); // reads data from the stream until the target string is found + bool find(uint8_t *target) + { + return find((char *) target); + } + // returns true if target string is found, false if timed out (see setTimeout) + + bool find(const char *target, size_t length); // reads data from the stream until the target string of given length is found + bool find(const uint8_t *target, size_t length) + { + return find((char *) target, length); + } + // returns true if target string is found, false if timed out + + bool find(char target) + { + return find(&target, 1); + } + + bool findUntil(const char *target, const char *terminator); // as find but search ends if the terminator string is found + bool findUntil(const uint8_t *target, const char *terminator) + { + return findUntil((char *) target, terminator); + } + + bool findUntil(const char *target, size_t targetLen, const char *terminate, size_t termLen); // as above but search ends if the terminate string is found + bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen) + { + return findUntil((char *) target, targetLen, terminate, termLen); + } + + long parseInt(); // returns the first valid (long) integer value from the current position. + // initial characters that are not digits (or the minus sign) are skipped + // integer is terminated by the first character that is not a digit. + + float parseFloat(); // float version of parseInt + + virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer + virtual size_t readBytes(uint8_t *buffer, size_t length) + { + return readBytes((char *) buffer, length); + } + // terminates if length characters have been read or timeout (see setTimeout) + // returns the number of characters placed in the buffer (0 means no valid data found) + + size_t readBytesUntil(char terminator, char *buffer, size_t length); // as readBytes with terminator character + size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length) + { + return readBytesUntil(terminator, (char *) buffer, length); + } + // terminates if length characters have been read, timeout, or if the terminator character detected + // returns the number of characters placed in the buffer (0 means no valid data found) + + // Arduino String functions to be added here + virtual String readString(); + String readStringUntil(char terminator); + +protected: + long parseInt(char skipChar); // as above but the given skipChar is ignored + // as above but the given skipChar is ignored + // this allows format characters (typically commas) in values to be ignored + + float parseFloat(char skipChar); // as above but the given skipChar is ignored }; #endif diff --git a/cores/esp8266/StreamString.cpp b/cores/esp8266/StreamString.cpp index f50b6825b5..f1b43545f2 100644 --- a/cores/esp8266/StreamString.cpp +++ b/cores/esp8266/StreamString.cpp @@ -1,33 +1,36 @@ /** - StreamString.cpp + StreamString.cpp - Copyright (c) 2015 Markus Sattler. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ +*/ #include #include "StreamString.h" -size_t StreamString::write(const uint8_t *data, size_t size) { - if(size && data) { +size_t StreamString::write(const uint8_t *data, size_t size) +{ + if (size && data) + { const unsigned int newlen = length() + size; - if(reserve(newlen + 1)) { - memcpy((void *) (wbuffer() + len()), (const void *) data, size); + if (reserve(newlen + 1)) + { + memcpy((void *)(wbuffer() + len()), (const void *) data, size); setLen(newlen); *(wbuffer() + newlen) = 0x00; // add null for string end return size; @@ -36,16 +39,20 @@ size_t StreamString::write(const uint8_t *data, size_t size) { return 0; } -size_t StreamString::write(uint8_t data) { +size_t StreamString::write(uint8_t data) +{ return concat((char) data); } -int StreamString::available() { +int StreamString::available() +{ return length(); } -int StreamString::read() { - if(length()) { +int StreamString::read() +{ + if (length()) + { char c = charAt(0); remove(0, 1); return c; @@ -54,14 +61,17 @@ int StreamString::read() { return -1; } -int StreamString::peek() { - if(length()) { +int StreamString::peek() +{ + if (length()) + { char c = charAt(0); return c; } return -1; } -void StreamString::flush() { +void StreamString::flush() +{ } diff --git a/cores/esp8266/StreamString.h b/cores/esp8266/StreamString.h index 1fedc18de0..7e4c365ef9 100644 --- a/cores/esp8266/StreamString.h +++ b/cores/esp8266/StreamString.h @@ -1,39 +1,40 @@ -/** - StreamString.h - - Copyright (c) 2015 Markus Sattler. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#ifndef STREAMSTRING_H_ -#define STREAMSTRING_H_ - - -class StreamString: public Stream, public String { -public: - size_t write(const uint8_t *buffer, size_t size) override; - size_t write(uint8_t data) override; - - int available() override; - int read() override; - int peek() override; - void flush() override; -}; - - -#endif /* STREAMSTRING_H_ */ +/** + StreamString.h + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef STREAMSTRING_H_ +#define STREAMSTRING_H_ + + +class StreamString: public Stream, public String +{ +public: + size_t write(const uint8_t *buffer, size_t size) override; + size_t write(uint8_t data) override; + + int available() override; + int read() override; + int peek() override; + void flush() override; +}; + + +#endif /* STREAMSTRING_H_ */ diff --git a/cores/esp8266/Tone.cpp b/cores/esp8266/Tone.cpp index 35a5a41515..abbb512d75 100644 --- a/cores/esp8266/Tone.cpp +++ b/cores/esp8266/Tone.cpp @@ -1,24 +1,24 @@ /* - Tone.cpp + Tone.cpp - A Tone Generator Library for the ESP8266 + A Tone Generator Library for the ESP8266 - Original Copyright (c) 2016 Ben Pirt. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Original Copyright (c) 2016 Ben Pirt. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Arduino.h" @@ -28,60 +28,74 @@ static uint32_t _toneMap = 0; -static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, unsigned long duration) { - if (_pin > 16) { - return; - } +static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, unsigned long duration) +{ + if (_pin > 16) + { + return; + } - pinMode(_pin, OUTPUT); + pinMode(_pin, OUTPUT); - high = std::max(high, (uint32_t)100); - low = std::max(low, (uint32_t)100); + high = std::max(high, (uint32_t)100); + low = std::max(low, (uint32_t)100); - if (startWaveform(_pin, high, low, (uint32_t) duration * 1000)) { - _toneMap |= 1 << _pin; - } + if (startWaveform(_pin, high, low, (uint32_t) duration * 1000)) + { + _toneMap |= 1 << _pin; + } } -void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) { - if (frequency == 0) { - noTone(_pin); - } else { - uint32_t period = 1000000L / frequency; - uint32_t high = period / 2; - uint32_t low = period - high; - _startTone(_pin, high, low, duration); - } +void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) +{ + if (frequency == 0) + { + noTone(_pin); + } + else + { + uint32_t period = 1000000L / frequency; + uint32_t high = period / 2; + uint32_t low = period - high; + _startTone(_pin, high, low, duration); + } } // Separate tone(float) to hopefully not pull in floating point libs unless // it's called with a float. -void tone(uint8_t _pin, double frequency, unsigned long duration) { - if (frequency < 1.0) { // FP means no exact comparisons - noTone(_pin); - } else { - double period = 1000000.0 / frequency; - uint32_t high = (uint32_t)((period / 2.0) + 0.5); - uint32_t low = (uint32_t)(period + 0.5) - high; - _startTone(_pin, high, low, duration); - } +void tone(uint8_t _pin, double frequency, unsigned long duration) +{ + if (frequency < 1.0) // FP means no exact comparisons + { + noTone(_pin); + } + else + { + double period = 1000000.0 / frequency; + uint32_t high = (uint32_t)((period / 2.0) + 0.5); + uint32_t low = (uint32_t)(period + 0.5) - high; + _startTone(_pin, high, low, duration); + } } // Fix ambiguous tone() binding when adding in a duration -void tone(uint8_t _pin, int frequency, unsigned long duration) { - // Call the unsigned int version of the function explicitly - tone(_pin, (unsigned int)frequency, duration); +void tone(uint8_t _pin, int frequency, unsigned long duration) +{ + // Call the unsigned int version of the function explicitly + tone(_pin, (unsigned int)frequency, duration); } -void noTone(uint8_t _pin) { - if (_pin > 16) { - return; - } - stopWaveform(_pin); - _toneMap &= ~(1 << _pin); - digitalWrite(_pin, 0); +void noTone(uint8_t _pin) +{ + if (_pin > 16) + { + return; + } + stopWaveform(_pin); + _toneMap &= ~(1 << _pin); + digitalWrite(_pin, 0); } diff --git a/cores/esp8266/Udp.h b/cores/esp8266/Udp.h index de5127b18f..17380073d3 100644 --- a/cores/esp8266/Udp.h +++ b/cores/esp8266/Udp.h @@ -1,36 +1,36 @@ /* - * Udp.cpp: Library to send/receive UDP packets. - * - * NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these) - * 1) UDP does not guarantee the order in which assembled UDP packets are received. This - * might not happen often in practice, but in larger network topologies, a UDP - * packet can be received out of sequence. - * 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being - * aware of it. Again, this may not be a concern in practice on small local networks. - * For more information, see http://www.cafeaulait.org/course/week12/35.html - * - * MIT License: - * Copyright (c) 2008 Bjoern Hartmann - * 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. - * - * bjoern@cs.stanford.edu 12/30/2008 - */ + Udp.cpp: Library to send/receive UDP packets. + + NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these) + 1) UDP does not guarantee the order in which assembled UDP packets are received. This + might not happen often in practice, but in larger network topologies, a UDP + packet can be received out of sequence. + 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being + aware of it. Again, this may not be a concern in practice on small local networks. + For more information, see http://www.cafeaulait.org/course/week12/35.html + + MIT License: + Copyright (c) 2008 Bjoern Hartmann + 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. + + bjoern@cs.stanford.edu 12/30/2008 +*/ #ifndef udp_h #define udp_h @@ -38,58 +38,61 @@ #include #include -class UDP: public Stream { +class UDP: public Stream +{ - public: - virtual ~UDP() {}; - virtual uint8_t begin(uint16_t) =0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use - virtual void stop() =0; // Finish with the UDP socket +public: + virtual ~UDP() {}; + virtual uint8_t begin(uint16_t) = 0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual void stop() = 0; // Finish with the UDP socket - // Sending UDP packets + // Sending UDP packets - // Start building up a packet to send to the remote host specific in ip and port - // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port - virtual int beginPacket(IPAddress ip, uint16_t port) =0; - // Start building up a packet to send to the remote host specific in host and port - // Returns 1 if successful, 0 if there was a problem resolving the hostname or port - virtual int beginPacket(const char *host, uint16_t port) =0; - // Finish off this packet and send it - // Returns 1 if the packet was sent successfully, 0 if there was an error - virtual int endPacket() =0; - // Write a single byte into the packet - virtual size_t write(uint8_t) =0; - // Write size bytes from buffer into the packet - virtual size_t write(const uint8_t *buffer, size_t size) =0; + // Start building up a packet to send to the remote host specific in ip and port + // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port + virtual int beginPacket(IPAddress ip, uint16_t port) = 0; + // Start building up a packet to send to the remote host specific in host and port + // Returns 1 if successful, 0 if there was a problem resolving the hostname or port + virtual int beginPacket(const char *host, uint16_t port) = 0; + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + virtual int endPacket() = 0; + // Write a single byte into the packet + virtual size_t write(uint8_t) = 0; + // Write size bytes from buffer into the packet + virtual size_t write(const uint8_t *buffer, size_t size) = 0; - // Start processing the next available incoming packet - // Returns the size of the packet in bytes, or 0 if no packets are available - virtual int parsePacket() =0; - // Number of bytes remaining in the current packet - virtual int available() =0; - // Read a single byte from the current packet - virtual int read() =0; - // Read up to len bytes from the current packet and place them into buffer - // Returns the number of bytes read, or 0 if none are available - virtual int read(unsigned char* buffer, size_t len) =0; - // Read up to len characters from the current packet and place them into buffer - // Returns the number of characters read, or 0 if none are available - virtual int read(char* buffer, size_t len) =0; - // Return the next byte from the current packet without moving on to the next byte - virtual int peek() =0; - virtual void flush() =0; // Finish reading the current packet + // Start processing the next available incoming packet + // Returns the size of the packet in bytes, or 0 if no packets are available + virtual int parsePacket() = 0; + // Number of bytes remaining in the current packet + virtual int available() = 0; + // Read a single byte from the current packet + virtual int read() = 0; + // Read up to len bytes from the current packet and place them into buffer + // Returns the number of bytes read, or 0 if none are available + virtual int read(unsigned char* buffer, size_t len) = 0; + // Read up to len characters from the current packet and place them into buffer + // Returns the number of characters read, or 0 if none are available + virtual int read(char* buffer, size_t len) = 0; + // Return the next byte from the current packet without moving on to the next byte + virtual int peek() = 0; + virtual void flush() = 0; // Finish reading the current packet - // Return the IP address of the host who sent the current incoming packet - virtual IPAddress remoteIP() =0; - // Return the port of the host who sent the current incoming packet - virtual uint16_t remotePort() =0; - protected: - uint8_t* rawIPAddress(IPAddress& addr) { - return addr.raw_address(); - } + // Return the IP address of the host who sent the current incoming packet + virtual IPAddress remoteIP() = 0; + // Return the port of the host who sent the current incoming packet + virtual uint16_t remotePort() = 0; +protected: + uint8_t* rawIPAddress(IPAddress& addr) + { + return addr.raw_address(); + } #if LWIP_VERSION_MAJOR != 1 - const uint8_t* rawIPAddress(const IPAddress& addr) { - return addr.raw_address(); - } + const uint8_t* rawIPAddress(const IPAddress& addr) + { + return addr.raw_address(); + } #endif }; diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 9f68541f44..387fd846e1 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -8,115 +8,127 @@ #include #ifndef ARDUINO_SIGNING - #define ARDUINO_SIGNING 0 +#define ARDUINO_SIGNING 0 #endif #if ARDUINO_SIGNING - #include "../../libraries/ESP8266WiFi/src/BearSSLHelpers.h" - static BearSSL::PublicKey signPubKey(signing_pubkey); - static BearSSL::HashSHA256 hash; - static BearSSL::SigningVerifier sign(&signPubKey); +#include "../../libraries/ESP8266WiFi/src/BearSSLHelpers.h" +static BearSSL::PublicKey signPubKey(signing_pubkey); +static BearSSL::HashSHA256 hash; +static BearSSL::SigningVerifier sign(&signPubKey); #endif extern "C" { - #include "c_types.h" - #include "spi_flash.h" - #include "user_interface.h" +#include "c_types.h" +#include "spi_flash.h" +#include "user_interface.h" } extern "C" uint32_t _SPIFFS_start; UpdaterClass::UpdaterClass() -: _async(false) -, _error(0) -, _buffer(0) -, _bufferLen(0) -, _size(0) -, _startAddress(0) -, _currentAddress(0) -, _command(U_FLASH) -, _hash(nullptr) -, _verify(nullptr) -, _progress_callback(nullptr) + : _async(false) + , _error(0) + , _buffer(0) + , _bufferLen(0) + , _size(0) + , _startAddress(0) + , _currentAddress(0) + , _command(U_FLASH) + , _hash(nullptr) + , _verify(nullptr) + , _progress_callback(nullptr) { #if ARDUINO_SIGNING - installSignature(&hash, &sign); + installSignature(&hash, &sign); #endif } -UpdaterClass& UpdaterClass::onProgress(THandlerFunction_Progress fn) { +UpdaterClass& UpdaterClass::onProgress(THandlerFunction_Progress fn) +{ _progress_callback = fn; return *this; } -void UpdaterClass::_reset() { - if (_buffer) - delete[] _buffer; - _buffer = 0; - _bufferLen = 0; - _startAddress = 0; - _currentAddress = 0; - _size = 0; - _command = U_FLASH; - - if(_ledPin != -1) { - digitalWrite(_ledPin, !_ledOn); // off - } +void UpdaterClass::_reset() +{ + if (_buffer) + { + delete[] _buffer; + } + _buffer = 0; + _bufferLen = 0; + _startAddress = 0; + _currentAddress = 0; + _size = 0; + _command = U_FLASH; + + if (_ledPin != -1) + { + digitalWrite(_ledPin, !_ledOn); // off + } } -bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { - if(_size > 0){ -#ifdef DEBUG_UPDATER - DEBUG_UPDATER.println(F("[begin] already running")); -#endif - return false; - } - - _ledPin = ledPin; - _ledOn = !!ledOn; // 0(LOW) or 1(HIGH) - - /* Check boot mode; if boot mode is 1 (UART download mode), - we will not be able to reset into normal mode once update is done. - Fail early to avoid frustration. - https://github.com/esp8266/Arduino/issues/1017#issuecomment-200605576 - */ - int boot_mode = (GPI >> 16) & 0xf; - if (boot_mode == 1) { - _setError(UPDATE_ERROR_BOOTSTRAP); - return false; - } - +bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) +{ + if (_size > 0) + { #ifdef DEBUG_UPDATER - if (command == U_SPIFFS) { - DEBUG_UPDATER.println(F("[begin] Update SPIFFS.")); - } + DEBUG_UPDATER.println(F("[begin] already running")); #endif + return false; + } - if(size == 0) { - _setError(UPDATE_ERROR_SIZE); - return false; - } + _ledPin = ledPin; + _ledOn = !!ledOn; // 0(LOW) or 1(HIGH) + + /* Check boot mode; if boot mode is 1 (UART download mode), + we will not be able to reset into normal mode once update is done. + Fail early to avoid frustration. + https://github.com/esp8266/Arduino/issues/1017#issuecomment-200605576 + */ + int boot_mode = (GPI >> 16) & 0xf; + if (boot_mode == 1) + { + _setError(UPDATE_ERROR_BOOTSTRAP); + return false; + } - if(!ESP.checkFlashConfig(false)) { - _setError(UPDATE_ERROR_FLASH_CONFIG); - return false; - } +#ifdef DEBUG_UPDATER + if (command == U_SPIFFS) + { + DEBUG_UPDATER.println(F("[begin] Update SPIFFS.")); + } +#endif - _reset(); - clearError(); // _error = 0 + if (size == 0) + { + _setError(UPDATE_ERROR_SIZE); + return false; + } - wifi_set_sleep_type(NONE_SLEEP_T); + if (!ESP.checkFlashConfig(false)) + { + _setError(UPDATE_ERROR_FLASH_CONFIG); + return false; + } - uintptr_t updateStartAddress = 0; - if (command == U_FLASH) { - //size of current sketch rounded to a sector - 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)&_SPIFFS_start - 0x40200000; - //size of the update rounded to a sector - size_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - //address where we will start writing the update - updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0; + _reset(); + clearError(); // _error = 0 + + wifi_set_sleep_type(NONE_SLEEP_T); + + uintptr_t updateStartAddress = 0; + if (command == U_FLASH) + { + //size of current sketch rounded to a sector + 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)&_SPIFFS_start - 0x40200000; + //size of the update rounded to a sector + size_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + //address where we will start writing the update + updateStartAddress = (updateEndAddress > roundedSize) ? (updateEndAddress - roundedSize) : 0; #ifdef DEBUG_UPDATER DEBUG_UPDATER.printf_P(PSTR("[begin] roundedSize: 0x%08zX (%zd)\n"), roundedSize, roundedSize); @@ -124,410 +136,525 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { DEBUG_UPDATER.printf_P(PSTR("[begin] currentSketchSize: 0x%08zX (%zd)\n"), currentSketchSize, currentSketchSize); #endif - //make sure that the size of both sketches is less than the total space (updateEndAddress) - if(updateStartAddress < currentSketchSize) { - _setError(UPDATE_ERROR_SPACE); - return false; - } - } - else if (command == U_SPIFFS) { - updateStartAddress = (uintptr_t)&_SPIFFS_start - 0x40200000; - } - else { - // unknown command + //make sure that the size of both sketches is less than the total space (updateEndAddress) + if (updateStartAddress < currentSketchSize) + { + _setError(UPDATE_ERROR_SPACE); + return false; + } + } + else if (command == U_SPIFFS) + { + updateStartAddress = (uintptr_t)&_SPIFFS_start - 0x40200000; + } + else + { + // unknown command #ifdef DEBUG_UPDATER - DEBUG_UPDATER.println(F("[begin] Unknown update command.")); + DEBUG_UPDATER.println(F("[begin] Unknown update command.")); #endif - return false; - } - - //initialize - _startAddress = updateStartAddress; - _currentAddress = _startAddress; - _size = size; - if (ESP.getFreeHeap() > 2 * FLASH_SECTOR_SIZE) { - _bufferSize = FLASH_SECTOR_SIZE; - } else { - _bufferSize = 256; - } - _buffer = new uint8_t[_bufferSize]; - _command = command; + return false; + } + + //initialize + _startAddress = updateStartAddress; + _currentAddress = _startAddress; + _size = size; + if (ESP.getFreeHeap() > 2 * FLASH_SECTOR_SIZE) + { + _bufferSize = FLASH_SECTOR_SIZE; + } + else + { + _bufferSize = 256; + } + _buffer = new uint8_t[_bufferSize]; + _command = command; #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[begin] _startAddress: 0x%08X (%d)\n"), _startAddress, _startAddress); - DEBUG_UPDATER.printf_P(PSTR("[begin] _currentAddress: 0x%08X (%d)\n"), _currentAddress, _currentAddress); - DEBUG_UPDATER.printf_P(PSTR("[begin] _size: 0x%08zX (%zd)\n"), _size, _size); + DEBUG_UPDATER.printf_P(PSTR("[begin] _startAddress: 0x%08X (%d)\n"), _startAddress, _startAddress); + DEBUG_UPDATER.printf_P(PSTR("[begin] _currentAddress: 0x%08X (%d)\n"), _currentAddress, _currentAddress); + DEBUG_UPDATER.printf_P(PSTR("[begin] _size: 0x%08zX (%zd)\n"), _size, _size); #endif - if (!_verify) { - _md5.begin(); - } - return true; + if (!_verify) + { + _md5.begin(); + } + return true; } -bool UpdaterClass::setMD5(const char * expected_md5){ - if(strlen(expected_md5) != 32) - { - return false; - } - _target_md5 = expected_md5; - return true; +bool UpdaterClass::setMD5(const char * expected_md5) +{ + if (strlen(expected_md5) != 32) + { + return false; + } + _target_md5 = expected_md5; + return true; } -bool UpdaterClass::end(bool evenIfRemaining){ - if(_size == 0){ +bool UpdaterClass::end(bool evenIfRemaining) +{ + if (_size == 0) + { #ifdef DEBUG_UPDATER - DEBUG_UPDATER.println(F("no update")); + DEBUG_UPDATER.println(F("no update")); #endif - return false; - } + return false; + } - if(hasError() || (!isFinished() && !evenIfRemaining)){ + if (hasError() || (!isFinished() && !evenIfRemaining)) + { #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("premature end: res:%u, pos:%zu/%zu\n"), getError(), progress(), _size); + DEBUG_UPDATER.printf_P(PSTR("premature end: res:%u, pos:%zu/%zu\n"), getError(), progress(), _size); #endif - _reset(); - return false; - } + _reset(); + return false; + } - if(evenIfRemaining) { - if(_bufferLen > 0) { - _writeBuffer(); + if (evenIfRemaining) + { + if (_bufferLen > 0) + { + _writeBuffer(); + } + _size = progress(); } - _size = progress(); - } - uint32_t sigLen = 0; - if (_verify) { - ESP.flashRead(_startAddress + _size - sizeof(uint32_t), &sigLen, sizeof(uint32_t)); + uint32_t sigLen = 0; + if (_verify) + { + ESP.flashRead(_startAddress + _size - sizeof(uint32_t), &sigLen, sizeof(uint32_t)); #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen: %d\n"), sigLen); + DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen: %d\n"), sigLen); #endif - if (sigLen != _verify->length()) { - _setError(UPDATE_ERROR_SIGN); - _reset(); - return false; - } + if (sigLen != _verify->length()) + { + _setError(UPDATE_ERROR_SIGN); + _reset(); + return false; + } - int binSize = _size - sigLen - sizeof(uint32_t) /* The siglen word */; - _hash->begin(); + int binSize = _size - sigLen - sizeof(uint32_t) /* The siglen word */; + _hash->begin(); +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted binsize: %d\n"), binSize); +#endif + // Calculate the MD5 and hash using proper size + uint8_t buff[128]; + for (int i = 0; i < binSize; i += sizeof(buff)) + { + ESP.flashRead(_startAddress + i, (uint32_t *)buff, sizeof(buff)); + size_t read = std::min((int)sizeof(buff), binSize - i); + _hash->add(buff, read); + } + _hash->end(); #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted binsize: %d\n"), binSize); + unsigned char *ret = (unsigned char *)_hash->hash(); + DEBUG_UPDATER.printf_P(PSTR("[Updater] Computed Hash:")); + for (int i = 0; i < _hash->len(); i++) + { + DEBUG_UPDATER.printf(" %02x", ret[i]); + } + DEBUG_UPDATER.printf("\n"); #endif - // Calculate the MD5 and hash using proper size - uint8_t buff[128]; - for(int i = 0; i < binSize; i += sizeof(buff)) { - ESP.flashRead(_startAddress + i, (uint32_t *)buff, sizeof(buff)); - size_t read = std::min((int)sizeof(buff), binSize - i); - _hash->add(buff, read); - } - _hash->end(); + uint8_t *sig = (uint8_t*)malloc(sigLen); + if (!sig) + { + _setError(UPDATE_ERROR_SIGN); + _reset(); + return false; + } + ESP.flashRead(_startAddress + binSize, (uint32_t *)sig, sigLen); #ifdef DEBUG_UPDATER - unsigned char *ret = (unsigned char *)_hash->hash(); - DEBUG_UPDATER.printf_P(PSTR("[Updater] Computed Hash:")); - for (int i=0; i<_hash->len(); i++) DEBUG_UPDATER.printf(" %02x", ret[i]); - DEBUG_UPDATER.printf("\n"); + DEBUG_UPDATER.printf_P(PSTR("[Updater] Received Signature:")); + for (size_t i = 0; i < sigLen; i++) + { + DEBUG_UPDATER.printf(" %02x", sig[i]); + } + DEBUG_UPDATER.printf("\n"); #endif - uint8_t *sig = (uint8_t*)malloc(sigLen); - if (!sig) { - _setError(UPDATE_ERROR_SIGN); - _reset(); - return false; - } - ESP.flashRead(_startAddress + binSize, (uint32_t *)sig, sigLen); + if (!_verify->verify(_hash, (void *)sig, sigLen)) + { + _setError(UPDATE_ERROR_SIGN); + _reset(); + return false; + } #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[Updater] Received Signature:")); - for (size_t i=0; iverify(_hash, (void *)sig, sigLen)) { - _setError(UPDATE_ERROR_SIGN); - _reset(); - return false; } + else if (_target_md5.length()) + { + _md5.calculate(); + if (strcasecmp(_target_md5.c_str(), _md5.toString().c_str())) + { + _setError(UPDATE_ERROR_MD5); + _reset(); + return false; + } #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[Updater] Signature matches\n")); + else + { + DEBUG_UPDATER.printf_P(PSTR("MD5 Success: %s\n"), _target_md5.c_str()); + } #endif - } else if (_target_md5.length()) { - _md5.calculate(); - if (strcasecmp(_target_md5.c_str(), _md5.toString().c_str())) { - _setError(UPDATE_ERROR_MD5); - _reset(); - return false; } + + if (!_verifyEnd()) + { + _reset(); + return false; + } + + if (_command == U_FLASH) + { + eboot_command ebcmd; + ebcmd.action = ACTION_COPY_RAW; + ebcmd.args[0] = _startAddress; + ebcmd.args[1] = 0x00000; + ebcmd.args[2] = _size; + eboot_command_write(&ebcmd); + #ifdef DEBUG_UPDATER - else DEBUG_UPDATER.printf_P(PSTR("MD5 Success: %s\n"), _target_md5.c_str()); + DEBUG_UPDATER.printf_P(PSTR("Staged: address:0x%08X, size:0x%08zX\n"), _startAddress, _size); + } + else if (_command == U_SPIFFS) + { + DEBUG_UPDATER.printf_P(PSTR("SPIFFS: address:0x%08X, size:0x%08zX\n"), _startAddress, _size); #endif - } + } - if(!_verifyEnd()) { _reset(); - return false; - } + return true; +} - if (_command == U_FLASH) { - eboot_command ebcmd; - ebcmd.action = ACTION_COPY_RAW; - ebcmd.args[0] = _startAddress; - ebcmd.args[1] = 0x00000; - ebcmd.args[2] = _size; - eboot_command_write(&ebcmd); +bool UpdaterClass::_writeBuffer() +{ +#define FLASH_MODE_PAGE 0 +#define FLASH_MODE_OFFSET 2 + + bool eraseResult = true, writeResult = true; + if (_currentAddress % FLASH_SECTOR_SIZE == 0) + { + if (!_async) + { + yield(); + } + eraseResult = ESP.flashEraseSector(_currentAddress / FLASH_SECTOR_SIZE); + } + // If the flash settings don't match what we already have, modify them. + // But restore them after the modification, so the hash isn't affected. + // This is analogous to what esptool.py does when it receives a --flash_mode argument. + bool modifyFlashMode = false; + FlashMode_t flashMode = FM_QIO; + FlashMode_t bufferFlashMode = FM_QIO; + if (_currentAddress == _startAddress + FLASH_MODE_PAGE) + { + flashMode = ESP.getFlashChipMode(); +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.printf_P(PSTR("Header: 0x%1X %1X %1X %1X\n"), _buffer[0], _buffer[1], _buffer[2], _buffer[3]); +#endif + bufferFlashMode = ESP.magicFlashChipMode(_buffer[FLASH_MODE_OFFSET]); + if (bufferFlashMode != flashMode) + { #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("Staged: address:0x%08X, size:0x%08zX\n"), _startAddress, _size); - } - else if (_command == U_SPIFFS) { - DEBUG_UPDATER.printf_P(PSTR("SPIFFS: address:0x%08X, size:0x%08zX\n"), _startAddress, _size); + DEBUG_UPDATER.printf_P(PSTR("Set flash mode from 0x%1X to 0x%1X\n"), bufferFlashMode, flashMode); #endif - } - _reset(); - return true; -} + _buffer[FLASH_MODE_OFFSET] = flashMode; + modifyFlashMode = true; + } + } -bool UpdaterClass::_writeBuffer(){ - #define FLASH_MODE_PAGE 0 - #define FLASH_MODE_OFFSET 2 - - bool eraseResult = true, writeResult = true; - if (_currentAddress % FLASH_SECTOR_SIZE == 0) { - if(!_async) yield(); - eraseResult = ESP.flashEraseSector(_currentAddress/FLASH_SECTOR_SIZE); - } - - // If the flash settings don't match what we already have, modify them. - // But restore them after the modification, so the hash isn't affected. - // This is analogous to what esptool.py does when it receives a --flash_mode argument. - bool modifyFlashMode = false; - FlashMode_t flashMode = FM_QIO; - FlashMode_t bufferFlashMode = FM_QIO; - if (_currentAddress == _startAddress + FLASH_MODE_PAGE) { - flashMode = ESP.getFlashChipMode(); - #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("Header: 0x%1X %1X %1X %1X\n"), _buffer[0], _buffer[1], _buffer[2], _buffer[3]); - #endif - bufferFlashMode = ESP.magicFlashChipMode(_buffer[FLASH_MODE_OFFSET]); - if (bufferFlashMode != flashMode) { - #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("Set flash mode from 0x%1X to 0x%1X\n"), bufferFlashMode, flashMode); - #endif - - _buffer[FLASH_MODE_OFFSET] = flashMode; - modifyFlashMode = true; - } - } - - if (eraseResult) { - if(!_async) yield(); - writeResult = ESP.flashWrite(_currentAddress, (uint32_t*) _buffer, _bufferLen); - } else { // if erase was unsuccessful - _currentAddress = (_startAddress + _size); - _setError(UPDATE_ERROR_ERASE); - return false; - } + if (eraseResult) + { + if (!_async) + { + yield(); + } + writeResult = ESP.flashWrite(_currentAddress, (uint32_t*) _buffer, _bufferLen); + } + else // if erase was unsuccessful + { + _currentAddress = (_startAddress + _size); + _setError(UPDATE_ERROR_ERASE); + return false; + } - // Restore the old flash mode, if we modified it. - // Ensures that the MD5 hash will still match what was sent. - if (modifyFlashMode) { - _buffer[FLASH_MODE_OFFSET] = bufferFlashMode; - } + // Restore the old flash mode, if we modified it. + // Ensures that the MD5 hash will still match what was sent. + if (modifyFlashMode) + { + _buffer[FLASH_MODE_OFFSET] = bufferFlashMode; + } - if (!writeResult) { - _currentAddress = (_startAddress + _size); - _setError(UPDATE_ERROR_WRITE); - return false; - } - if (!_verify) { - _md5.add(_buffer, _bufferLen); - } - _currentAddress += _bufferLen; - _bufferLen = 0; - return true; + if (!writeResult) + { + _currentAddress = (_startAddress + _size); + _setError(UPDATE_ERROR_WRITE); + return false; + } + if (!_verify) + { + _md5.add(_buffer, _bufferLen); + } + _currentAddress += _bufferLen; + _bufferLen = 0; + return true; } -size_t UpdaterClass::write(uint8_t *data, size_t len) { - if(hasError() || !isRunning()) - return 0; - - if(len > remaining()){ - //len = remaining(); - //fail instead - _setError(UPDATE_ERROR_SPACE); - return 0; - } - - size_t left = len; - - while((_bufferLen + left) > _bufferSize) { - size_t toBuff = _bufferSize - _bufferLen; - memcpy(_buffer + _bufferLen, data + (len - left), toBuff); - _bufferLen += toBuff; - if(!_writeBuffer()){ - return len - left; - } - left -= toBuff; - if(!_async) yield(); - } - //lets see whats left - memcpy(_buffer + _bufferLen, data + (len - left), left); - _bufferLen += left; - if(_bufferLen == remaining()){ - //we are at the end of the update, so should write what's left to flash - if(!_writeBuffer()){ - return len - left; - } - } - return len; +size_t UpdaterClass::write(uint8_t *data, size_t len) +{ + if (hasError() || !isRunning()) + { + return 0; + } + + if (len > remaining()) + { + //len = remaining(); + //fail instead + _setError(UPDATE_ERROR_SPACE); + return 0; + } + + size_t left = len; + + while ((_bufferLen + left) > _bufferSize) + { + size_t toBuff = _bufferSize - _bufferLen; + memcpy(_buffer + _bufferLen, data + (len - left), toBuff); + _bufferLen += toBuff; + if (!_writeBuffer()) + { + return len - left; + } + left -= toBuff; + if (!_async) + { + yield(); + } + } + //lets see whats left + memcpy(_buffer + _bufferLen, data + (len - left), left); + _bufferLen += left; + if (_bufferLen == remaining()) + { + //we are at the end of the update, so should write what's left to flash + if (!_writeBuffer()) + { + return len - left; + } + } + return len; } -bool UpdaterClass::_verifyHeader(uint8_t data) { - if(_command == U_FLASH) { +bool UpdaterClass::_verifyHeader(uint8_t data) +{ + if (_command == U_FLASH) + { // check for valid first magic byte (is always 0xE9) - if(data != 0xE9) { + if (data != 0xE9) + { _currentAddress = (_startAddress + _size); _setError(UPDATE_ERROR_MAGIC_BYTE); return false; } return true; - } else if(_command == U_SPIFFS) { + } + else if (_command == U_SPIFFS) + { // no check of SPIFFS possible with first byte. return true; } return false; } -bool UpdaterClass::_verifyEnd() { - if(_command == U_FLASH) { +bool UpdaterClass::_verifyEnd() +{ + if (_command == U_FLASH) + { uint8_t buf[4]; - if(!ESP.flashRead(_startAddress, (uint32_t *) &buf[0], 4)) { + if (!ESP.flashRead(_startAddress, (uint32_t *) &buf[0], 4)) + { _currentAddress = (_startAddress); - _setError(UPDATE_ERROR_READ); + _setError(UPDATE_ERROR_READ); return false; } // check for valid first magic byte - if(buf[0] != 0xE9) { + if (buf[0] != 0xE9) + { _currentAddress = (_startAddress); - _setError(UPDATE_ERROR_MAGIC_BYTE); + _setError(UPDATE_ERROR_MAGIC_BYTE); return false; } uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4); // check if new bin fits to SPI flash - if(bin_flash_size > ESP.getFlashChipRealSize()) { + if (bin_flash_size > ESP.getFlashChipRealSize()) + { _currentAddress = (_startAddress); - _setError(UPDATE_ERROR_NEW_FLASH_CONFIG); + _setError(UPDATE_ERROR_NEW_FLASH_CONFIG); return false; } return true; - } else if(_command == U_SPIFFS) { + } + else if (_command == U_SPIFFS) + { // SPIFFS is already over written checks make no sense any more. return true; } return false; } -size_t UpdaterClass::writeStream(Stream &data) { +size_t UpdaterClass::writeStream(Stream &data) +{ size_t written = 0; size_t toRead = 0; - if(hasError() || !isRunning()) + if (hasError() || !isRunning()) + { return 0; + } - if(!_verifyHeader(data.peek())) { + if (!_verifyHeader(data.peek())) + { #ifdef DEBUG_UPDATER printError(DEBUG_UPDATER); #endif _reset(); return 0; } - if (_progress_callback) { + if (_progress_callback) + { _progress_callback(0, _size); } - if(_ledPin != -1) { + if (_ledPin != -1) + { pinMode(_ledPin, OUTPUT); } - while(remaining()) { - if(_ledPin != -1) { + while (remaining()) + { + if (_ledPin != -1) + { digitalWrite(_ledPin, _ledOn); // Switch LED on } size_t bytesToRead = _bufferSize - _bufferLen; - if(bytesToRead > remaining()) { + if (bytesToRead > remaining()) + { bytesToRead = remaining(); } toRead = data.readBytes(_buffer + _bufferLen, bytesToRead); - if(toRead == 0) { //Timeout + if (toRead == 0) //Timeout + { delay(100); toRead = data.readBytes(_buffer + _bufferLen, bytesToRead); - if(toRead == 0) { //Timeout + if (toRead == 0) //Timeout + { _currentAddress = (_startAddress + _size); _setError(UPDATE_ERROR_STREAM); _reset(); return written; } } - if(_ledPin != -1) { + if (_ledPin != -1) + { digitalWrite(_ledPin, !_ledOn); // Switch LED off } _bufferLen += toRead; - if((_bufferLen == remaining() || _bufferLen == _bufferSize) && !_writeBuffer()) + if ((_bufferLen == remaining() || _bufferLen == _bufferSize) && !_writeBuffer()) + { return written; + } written += toRead; - if(_progress_callback) { + if (_progress_callback) + { _progress_callback(progress(), _size); } yield(); } - if(_progress_callback) { + if (_progress_callback) + { _progress_callback(progress(), _size); } return written; } -void UpdaterClass::_setError(int error){ - _error = error; +void UpdaterClass::_setError(int error) +{ + _error = error; #ifdef DEBUG_UPDATER - printError(DEBUG_UPDATER); + printError(DEBUG_UPDATER); #endif } -void UpdaterClass::printError(Print &out){ - out.printf_P(PSTR("ERROR[%u]: "), _error); - if(_error == UPDATE_ERROR_OK){ - out.println(F("No Error")); - } else if(_error == UPDATE_ERROR_WRITE){ - out.println(F("Flash Write Failed")); - } else if(_error == UPDATE_ERROR_ERASE){ - out.println(F("Flash Erase Failed")); - } else if(_error == UPDATE_ERROR_READ){ - out.println(F("Flash Read Failed")); - } else if(_error == UPDATE_ERROR_SPACE){ - out.println(F("Not Enough Space")); - } else if(_error == UPDATE_ERROR_SIZE){ - out.println(F("Bad Size Given")); - } else if(_error == UPDATE_ERROR_STREAM){ - out.println(F("Stream Read Timeout")); - } else if(_error == UPDATE_ERROR_MD5){ - out.printf_P(PSTR("MD5 Failed: expected:%s, calculated:%s\n"), _target_md5.c_str(), _md5.toString().c_str()); - } else if(_error == UPDATE_ERROR_SIGN){ - out.println(F("Signature verification failed")); - } else if(_error == UPDATE_ERROR_FLASH_CONFIG){ - out.printf_P(PSTR("Flash config wrong real: %d IDE: %d\n"), ESP.getFlashChipRealSize(), ESP.getFlashChipSize()); - } else if(_error == UPDATE_ERROR_NEW_FLASH_CONFIG){ - out.printf_P(PSTR("new Flash config wrong real: %d\n"), ESP.getFlashChipRealSize()); - } else if(_error == UPDATE_ERROR_MAGIC_BYTE){ - out.println(F("Magic byte is wrong, not 0xE9")); - } else if (_error == UPDATE_ERROR_BOOTSTRAP){ - out.println(F("Invalid bootstrapping state, reset ESP8266 before updating")); - } else { - out.println(F("UNKNOWN")); - } +void UpdaterClass::printError(Print &out) +{ + out.printf_P(PSTR("ERROR[%u]: "), _error); + if (_error == UPDATE_ERROR_OK) + { + out.println(F("No Error")); + } + else if (_error == UPDATE_ERROR_WRITE) + { + out.println(F("Flash Write Failed")); + } + else if (_error == UPDATE_ERROR_ERASE) + { + out.println(F("Flash Erase Failed")); + } + else if (_error == UPDATE_ERROR_READ) + { + out.println(F("Flash Read Failed")); + } + else if (_error == UPDATE_ERROR_SPACE) + { + out.println(F("Not Enough Space")); + } + else if (_error == UPDATE_ERROR_SIZE) + { + out.println(F("Bad Size Given")); + } + else if (_error == UPDATE_ERROR_STREAM) + { + out.println(F("Stream Read Timeout")); + } + else if (_error == UPDATE_ERROR_MD5) + { + out.printf_P(PSTR("MD5 Failed: expected:%s, calculated:%s\n"), _target_md5.c_str(), _md5.toString().c_str()); + } + else if (_error == UPDATE_ERROR_SIGN) + { + out.println(F("Signature verification failed")); + } + else if (_error == UPDATE_ERROR_FLASH_CONFIG) + { + out.printf_P(PSTR("Flash config wrong real: %d IDE: %d\n"), ESP.getFlashChipRealSize(), ESP.getFlashChipSize()); + } + else if (_error == UPDATE_ERROR_NEW_FLASH_CONFIG) + { + out.printf_P(PSTR("new Flash config wrong real: %d\n"), ESP.getFlashChipRealSize()); + } + else if (_error == UPDATE_ERROR_MAGIC_BYTE) + { + out.println(F("Magic byte is wrong, not 0xE9")); + } + else if (_error == UPDATE_ERROR_BOOTSTRAP) + { + out.println(F("Invalid bootstrapping state, reset ESP8266 before updating")); + } + else + { + out.println(F("UNKNOWN")); + } } UpdaterClass Update; diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index 8de16c7ed6..13b0fca1e4 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -31,8 +31,9 @@ #endif // Abstract class to implement whatever signing hash desired -class UpdaterHashClass { - public: +class UpdaterHashClass +{ +public: virtual void begin() = 0; virtual void add(const void *data, uint32_t len) = 0; virtual void end() = 0; @@ -41,144 +42,197 @@ class UpdaterHashClass { }; // Abstract class to implement a signature verifier -class UpdaterVerifyClass { - public: +class UpdaterVerifyClass +{ +public: virtual uint32_t length() = 0; // How many bytes of signature are expected virtual bool verify(UpdaterHashClass *hash, const void *signature, uint32_t signatureLen) = 0; // Verify, return "true" on success }; -class UpdaterClass { - public: +class UpdaterClass +{ +public: typedef std::function THandlerFunction_Progress; - + UpdaterClass(); /* Optionally add a cryptographic signature verification hash and method */ - void installSignature(UpdaterHashClass *hash, UpdaterVerifyClass *verify) { _hash = hash; _verify = verify; } + void installSignature(UpdaterHashClass *hash, UpdaterVerifyClass *verify) + { + _hash = hash; + _verify = verify; + } /* - Call this to check the space needed for the update - Will return false if there is not enough space + Call this to check the space needed for the update + Will return false if there is not enough space */ bool begin(size_t size, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW); /* - Run Updater from asynchronous callbacs + Run Updater from asynchronous callbacs */ - void runAsync(bool async){ _async = async; } + void runAsync(bool async) + { + _async = async; + } /* - Writes a buffer to the flash and increments the address - Returns the amount written + Writes a buffer to the flash and increments the address + Returns the amount written */ size_t write(uint8_t *data, size_t len); /* - Writes the remaining bytes from the Stream to the flash - Uses readBytes() and sets UPDATE_ERROR_STREAM on timeout - Returns the bytes written - Should be equal to the remaining bytes when called - Usable for slow streams like Serial + Writes the remaining bytes from the Stream to the flash + Uses readBytes() and sets UPDATE_ERROR_STREAM on timeout + Returns the bytes written + Should be equal to the remaining bytes when called + Usable for slow streams like Serial */ size_t writeStream(Stream &data); /* - If all bytes are written - this call will write the config to eboot - and return true - If there is already an update running but is not finished and !evenIfRemaining - or there is an error - this will clear everything and return false - the last error is available through getError() - evenIfRemaining is helpful when you update without knowing the final size first + If all bytes are written + this call will write the config to eboot + and return true + If there is already an update running but is not finished and !evenIfRemaining + or there is an error + this will clear everything and return false + the last error is available through getError() + evenIfRemaining is helpful when you update without knowing the final size first */ bool end(bool evenIfRemaining = false); /* - Prints the last error to an output stream + Prints the last error to an output stream */ void printError(Print &out); /* - sets the expected MD5 for the firmware (hexString) + sets the expected MD5 for the firmware (hexString) */ bool setMD5(const char * expected_md5); /* - returns the MD5 String of the sucessfully ended firmware + returns the MD5 String of the sucessfully ended firmware */ - String md5String(void){ return _md5.toString(); } + String md5String(void) + { + return _md5.toString(); + } /* - populated the result with the md5 bytes of the sucessfully ended firmware + populated the result with the md5 bytes of the sucessfully ended firmware */ - void md5(uint8_t * result){ return _md5.getBytes(result); } + void md5(uint8_t * result) + { + return _md5.getBytes(result); + } /* - This callback will be called when Updater is receiving data + This callback will be called when Updater is receiving data */ UpdaterClass& onProgress(THandlerFunction_Progress fn); //Helpers - uint8_t getError(){ return _error; } - void clearError(){ _error = UPDATE_ERROR_OK; } - bool hasError(){ return _error != UPDATE_ERROR_OK; } - bool isRunning(){ return _size > 0; } - bool isFinished(){ return _currentAddress == (_startAddress + _size); } - size_t size(){ return _size; } - size_t progress(){ return _currentAddress - _startAddress; } - size_t remaining(){ return _size - (_currentAddress - _startAddress); } + uint8_t getError() + { + return _error; + } + void clearError() + { + _error = UPDATE_ERROR_OK; + } + bool hasError() + { + return _error != UPDATE_ERROR_OK; + } + bool isRunning() + { + return _size > 0; + } + bool isFinished() + { + return _currentAddress == (_startAddress + _size); + } + size_t size() + { + return _size; + } + size_t progress() + { + return _currentAddress - _startAddress; + } + size_t remaining() + { + return _size - (_currentAddress - _startAddress); + } /* - Template to write from objects that expose - available() and read(uint8_t*, size_t) methods - faster than the writeStream method - writes only what is available + Template to write from objects that expose + available() and read(uint8_t*, size_t) methods + faster than the writeStream method + writes only what is available */ template - size_t write(T &data){ - size_t written = 0; - if (hasError() || !isRunning()) - return 0; - - size_t available = data.available(); - while(available) { - if(_bufferLen + available > remaining()){ - available = remaining() - _bufferLen; + size_t write(T &data) + { + size_t written = 0; + if (hasError() || !isRunning()) + { + return 0; } - if(_bufferLen + available > _bufferSize) { - size_t toBuff = _bufferSize - _bufferLen; - data.read(_buffer + _bufferLen, toBuff); - _bufferLen += toBuff; - if(!_writeBuffer()) - return written; - written += toBuff; - } else { - data.read(_buffer + _bufferLen, available); - _bufferLen += available; - written += available; - if(_bufferLen == remaining()) { - if(!_writeBuffer()) { - return written; + + size_t available = data.available(); + while (available) + { + if (_bufferLen + available > remaining()) + { + available = remaining() - _bufferLen; + } + if (_bufferLen + available > _bufferSize) + { + size_t toBuff = _bufferSize - _bufferLen; + data.read(_buffer + _bufferLen, toBuff); + _bufferLen += toBuff; + if (!_writeBuffer()) + { + return written; + } + written += toBuff; + } + else + { + data.read(_buffer + _bufferLen, available); + _bufferLen += available; + written += available; + if (_bufferLen == remaining()) + { + if (!_writeBuffer()) + { + return written; + } + } + } + if (remaining() == 0) + { + return written; } - } + delay(1); + available = data.available(); } - if(remaining() == 0) - return written; - delay(1); - available = data.available(); - } - return written; + return written; } - private: +private: void _reset(); bool _writeBuffer(); bool _verifyHeader(uint8_t data); bool _verifyEnd(); - void _setError(int error); + void _setError(int error); bool _async; uint8_t _error; diff --git a/cores/esp8266/WCharacter.h b/cores/esp8266/WCharacter.h index 884eed037b..cd294be4f8 100644 --- a/cores/esp8266/WCharacter.h +++ b/cores/esp8266/WCharacter.h @@ -1,21 +1,21 @@ /* - WCharacter.h - Character utility functions for Wiring & Arduino - Copyright (c) 2010 Hernando Barragan. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + WCharacter.h - Character utility functions for Wiring & Arduino + Copyright (c) 2010 Hernando Barragan. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef Character_h #define Character_h @@ -44,96 +44,112 @@ inline int toAscii(int c) __attribute__((always_inline)); inline int toLowerCase(int c) __attribute__((always_inline)); inline int toUpperCase(int c) __attribute__((always_inline)); -// Checks for an alphanumeric character. +// Checks for an alphanumeric character. // It is equivalent to (isalpha(c) || isdigit(c)). -inline boolean isAlphaNumeric(int c) { +inline boolean isAlphaNumeric(int c) +{ return (isalnum(c) == 0 ? false : true); } -// Checks for an alphabetic character. +// Checks for an alphabetic character. // It is equivalent to (isupper(c) || islower(c)). -inline boolean isAlpha(int c) { +inline boolean isAlpha(int c) +{ return (isalpha(c) == 0 ? false : true); } -// Checks whether c is a 7-bit unsigned char value +// Checks whether c is a 7-bit unsigned char value // that fits into the ASCII character set. -inline boolean isAscii(int c) { - return ( isascii (c) == 0 ? false : true); +inline boolean isAscii(int c) +{ + return (isascii(c) == 0 ? false : true); } // Checks for a blank character, that is, a space or a tab. -inline boolean isWhitespace(int c) { +inline boolean isWhitespace(int c) +{ return (isblank(c) == 0 ? false : true); } // Checks for a control character. -inline boolean isControl(int c) { +inline boolean isControl(int c) +{ return (iscntrl(c) == 0 ? false : true); } // Checks for a digit (0 through 9). -inline boolean isDigit(int c) { +inline boolean isDigit(int c) +{ return (isdigit(c) == 0 ? false : true); } // Checks for any printable character except space. -inline boolean isGraph(int c) { +inline boolean isGraph(int c) +{ return (isgraph(c) == 0 ? false : true); } // Checks for a lower-case character. -inline boolean isLowerCase(int c) { +inline boolean isLowerCase(int c) +{ return (islower(c) == 0 ? false : true); } // Checks for any printable character including space. -inline boolean isPrintable(int c) { +inline boolean isPrintable(int c) +{ return (isprint(c) == 0 ? false : true); } -// Checks for any printable character which is not a space +// Checks for any printable character which is not a space // or an alphanumeric character. -inline boolean isPunct(int c) { +inline boolean isPunct(int c) +{ return (ispunct(c) == 0 ? false : true); } -// Checks for white-space characters. For the avr-libc library, -// these are: space, formfeed ('\f'), newline ('\n'), carriage +// Checks for white-space characters. For the avr-libc library, +// these are: space, formfeed ('\f'), newline ('\n'), carriage // return ('\r'), horizontal tab ('\t'), and vertical tab ('\v'). -inline boolean isSpace(int c) { +inline boolean isSpace(int c) +{ return (isspace(c) == 0 ? false : true); } // Checks for an uppercase letter. -inline boolean isUpperCase(int c) { +inline boolean isUpperCase(int c) +{ return (isupper(c) == 0 ? false : true); } -// Checks for a hexadecimal digits, i.e. one of 0 1 2 3 4 5 6 7 +// Checks for a hexadecimal digits, i.e. one of 0 1 2 3 4 5 6 7 // 8 9 a b c d e f A B C D E F. -inline boolean isHexadecimalDigit(int c) { +inline boolean isHexadecimalDigit(int c) +{ return (isxdigit(c) == 0 ? false : true); } -// Converts c to a 7-bit unsigned char value that fits into the +// Converts c to a 7-bit unsigned char value that fits into the // ASCII character set, by clearing the high-order bits. -inline int toAscii(int c) { +inline int toAscii(int c) +{ return toascii(c); } // Warning: -// Many people will be unhappy if you use this function. -// This function will convert accented letters into random +// Many people will be unhappy if you use this function. +// This function will convert accented letters into random // characters. // Converts the letter c to lower case, if possible. -inline int toLowerCase(int c) { +inline int toLowerCase(int c) +{ return tolower(c); } // Converts the letter c to upper case, if possible. -inline int toUpperCase(int c) { +inline int toUpperCase(int c) +{ return toupper(c); } diff --git a/cores/esp8266/WMath.cpp b/cores/esp8266/WMath.cpp index 1f0c8d7dbe..2803efd6e0 100644 --- a/cores/esp8266/WMath.cpp +++ b/cores/esp8266/WMath.cpp @@ -1,27 +1,27 @@ /* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* - Part of the Wiring project - http://wiring.org.co - Copyright (c) 2004-06 Hernando Barragan - Modified 13 August 2006, David A. Mellis for Arduino - http://www.arduino.cc/ - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + Part of the Wiring project - http://wiring.org.co + Copyright (c) 2004-06 Hernando Barragan + Modified 13 August 2006, David A. Mellis for Arduino - http://www.arduino.cc/ - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - $Id$ - */ + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + $Id$ +*/ extern "C" { #include @@ -30,15 +30,19 @@ extern "C" { static bool s_randomSeedCalled = false; -void randomSeed(unsigned long seed) { - if(seed != 0) { +void randomSeed(unsigned long seed) +{ + if (seed != 0) + { srand(seed); s_randomSeedCalled = true; } } -long random(long howbig) { - if(howbig == 0) { +long random(long howbig) +{ + if (howbig == 0) + { return 0; } // if randomSeed was called, fall back to software PRNG @@ -46,41 +50,51 @@ long random(long howbig) { return val % howbig; } -long random(long howsmall, long howbig) { - if(howsmall >= howbig) { +long random(long howsmall, long howbig) +{ + if (howsmall >= howbig) + { return howsmall; } long diff = howbig - howsmall; return random(diff) + howsmall; } -long secureRandom(long howbig) { - if(howbig == 0) { +long secureRandom(long howbig) +{ + if (howbig == 0) + { return 0; } return RANDOM_REG32 % howbig; } -long secureRandom(long howsmall, long howbig) { - if(howsmall >= howbig) { +long secureRandom(long howsmall, long howbig) +{ + if (howsmall >= howbig) + { return howsmall; } long diff = howbig - howsmall; return secureRandom(diff) + howsmall; } -long map(long x, long in_min, long in_max, long out_min, long out_max) { +long map(long x, long in_min, long in_max, long out_min, long out_max) +{ long divisor = (in_max - in_min); - if(divisor == 0){ + if (divisor == 0) + { return -1; //AVR returns -1, SAM returns 0 } return (x - in_min) * (out_max - out_min) / divisor + out_min; } -unsigned int makeWord(unsigned int w) { +unsigned int makeWord(unsigned int w) +{ return w; } -unsigned int makeWord(unsigned char h, unsigned char l) { +unsigned int makeWord(unsigned char h, unsigned char l) +{ return (h << 8) | l; } diff --git a/cores/esp8266/WString.cpp b/cores/esp8266/WString.cpp index 69444696aa..6de68879e1 100644 --- a/cores/esp8266/WString.cpp +++ b/cores/esp8266/WString.cpp @@ -1,25 +1,25 @@ /* - WString.cpp - String library for Wiring & Arduino - ...mostly rewritten by Paul Stoffregen... - Copyright (c) 2009-10 Hernando Barragan. All rights reserved. - Copyright 2011, Paul Stoffregen, paul@pjrc.com - Modified by Ivan Grokhotkov, 2014 - esp8266 support - Modified by Michael C. Miller, 2015 - esp8266 progmem support - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + WString.cpp - String library for Wiring & Arduino + ...mostly rewritten by Paul Stoffregen... + Copyright (c) 2009-10 Hernando Barragan. All rights reserved. + Copyright 2011, Paul Stoffregen, paul@pjrc.com + Modified by Ivan Grokhotkov, 2014 - esp8266 support + Modified by Michael C. Miller, 2015 - esp8266 progmem support + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include #include "WString.h" @@ -29,35 +29,43 @@ /* Constructors */ /*********************************************/ -String::String(const char *cstr) { +String::String(const char *cstr) +{ init(); - if(cstr) + if (cstr) + { copy(cstr, strlen(cstr)); + } } -String::String(const String &value) { +String::String(const String &value) +{ init(); *this = value; } -String::String(const __FlashStringHelper *pstr) { +String::String(const __FlashStringHelper *pstr) +{ init(); *this = pstr; // see operator = } #ifdef __GXX_EXPERIMENTAL_CXX0X__ -String::String(String &&rval) { +String::String(String &&rval) +{ init(); move(rval); } -String::String(StringSumHelper &&rval) { +String::String(StringSumHelper &&rval) +{ init(); move(rval); } #endif -String::String(char c) { +String::String(char c) +{ init(); char buf[2]; buf[0] = c; @@ -65,62 +73,76 @@ String::String(char c) { *this = buf; } -String::String(unsigned char value, unsigned char base) { +String::String(unsigned char value, unsigned char base) +{ init(); char buf[1 + 8 * sizeof(unsigned char)]; utoa(value, buf, base); *this = buf; } -String::String(int value, unsigned char base) { +String::String(int value, unsigned char base) +{ init(); char buf[2 + 8 * sizeof(int)]; - if (base == 10) { + if (base == 10) + { sprintf(buf, "%d", value); - } else { + } + else + { itoa(value, buf, base); } *this = buf; } -String::String(unsigned int value, unsigned char base) { +String::String(unsigned int value, unsigned char base) +{ init(); char buf[1 + 8 * sizeof(unsigned int)]; utoa(value, buf, base); *this = buf; } -String::String(long value, unsigned char base) { +String::String(long value, unsigned char base) +{ init(); char buf[2 + 8 * sizeof(long)]; - if (base==10) { + if (base == 10) + { sprintf(buf, "%ld", value); - } else { + } + else + { ltoa(value, buf, base); } *this = buf; } -String::String(unsigned long value, unsigned char base) { +String::String(unsigned long value, unsigned char base) +{ init(); char buf[1 + 8 * sizeof(unsigned long)]; ultoa(value, buf, base); *this = buf; } -String::String(float value, unsigned char decimalPlaces) { +String::String(float value, unsigned char decimalPlaces) +{ init(); char buf[33]; *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); } -String::String(double value, unsigned char decimalPlaces) { +String::String(double value, unsigned char decimalPlaces) +{ init(); char buf[33]; *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); } -String::~String() { +String::~String() +{ invalidate(); } @@ -128,38 +150,53 @@ String::~String() { // /* Memory Management */ // /*********************************************/ -inline void String::init(void) { +inline void String::init(void) +{ setSSO(false); setCapacity(0); setLen(0); setBuffer(nullptr); } -void String::invalidate(void) { - if(!sso() && wbuffer()) +void String::invalidate(void) +{ + if (!sso() && wbuffer()) + { free(wbuffer()); + } init(); } -unsigned char String::reserve(unsigned int size) { - if(buffer() && capacity() >= size) +unsigned char String::reserve(unsigned int size) +{ + if (buffer() && capacity() >= size) + { return 1; - if(changeBuffer(size)) { - if(len() == 0) + } + if (changeBuffer(size)) + { + if (len() == 0) + { wbuffer()[0] = 0; + } return 1; } return 0; } -unsigned char String::changeBuffer(unsigned int maxStrLen) { +unsigned char String::changeBuffer(unsigned int maxStrLen) +{ // Can we use SSO here to avoid allocation? - if (maxStrLen < sizeof(sso_buf)) { - if (sso() || !buffer()) { + if (maxStrLen < sizeof(sso_buf)) + { + if (sso() || !buffer()) + { // Already using SSO, nothing to do setSSO(true); return 1; - } else { // if bufptr && !sso() + } + else // if bufptr && !sso() + { // Using bufptr, need to shrink into sso_buff char temp[sizeof(sso_buf)]; memcpy(temp, buffer(), maxStrLen); @@ -172,14 +209,17 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) { // Fallthrough to normal allocator size_t newSize = (maxStrLen + 16) & (~0xf); // Make sure we can fit newsize in the buffer - if (newSize > CAPACITY_MAX) { + if (newSize > CAPACITY_MAX) + { return false; } uint16_t oldLen = len(); char *newbuffer = (char *) realloc(sso() ? nullptr : wbuffer(), newSize); - if(newbuffer) { + if (newbuffer) + { size_t oldSize = capacity() + 1; // include NULL. - if (sso()) { + if (sso()) + { // Copy the SSO buffer into allocated space memcpy(newbuffer, sso_buf, sizeof(sso_buf)); } @@ -200,8 +240,10 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) { // /* Copy and Move */ // /*********************************************/ -String & String::copy(const char *cstr, unsigned int length) { - if(!reserve(length)) { +String & String::copy(const char *cstr, unsigned int length) +{ + if (!reserve(length)) + { invalidate(); return *this; } @@ -210,8 +252,10 @@ String & String::copy(const char *cstr, unsigned int length) { return *this; } -String & String::copy(const __FlashStringHelper *pstr, unsigned int length) { - if (!reserve(length)) { +String & String::copy(const __FlashStringHelper *pstr, unsigned int length) +{ + if (!reserve(length)) + { invalidate(); return *this; } @@ -221,24 +265,33 @@ String & String::copy(const __FlashStringHelper *pstr, unsigned int length) { } #ifdef __GXX_EXPERIMENTAL_CXX0X__ -void String::move(String &rhs) { - if(buffer()) { - if(capacity() >= rhs.len()) { +void String::move(String &rhs) +{ + if (buffer()) + { + if (capacity() >= rhs.len()) + { strcpy(wbuffer(), rhs.buffer()); setLen(rhs.len()); - rhs.invalidate(); + rhs.invalidate(); return; - } else { - if (!sso()) { + } + else + { + if (!sso()) + { free(wbuffer()); setBuffer(nullptr); } } } - if (rhs.sso()) { + if (rhs.sso()) + { setSSO(true); memmove(sso_buf, rhs.sso_buf, sizeof(sso_buf)); - } else { + } + else + { setSSO(false); setBuffer(rhs.wbuffer()); } @@ -251,45 +304,69 @@ void String::move(String &rhs) { } #endif -String & String::operator =(const String &rhs) { - if(this == &rhs) +String & String::operator =(const String &rhs) +{ + if (this == &rhs) + { return *this; + } - if(rhs.buffer()) + if (rhs.buffer()) + { copy(rhs.buffer(), rhs.len()); + } else + { invalidate(); + } return *this; } #ifdef __GXX_EXPERIMENTAL_CXX0X__ -String & String::operator =(String &&rval) { - if(this != &rval) +String & String::operator =(String &&rval) +{ + if (this != &rval) + { move(rval); + } return *this; } -String & String::operator =(StringSumHelper &&rval) { - if(this != &rval) +String & String::operator =(StringSumHelper &&rval) +{ + if (this != &rval) + { move(rval); + } return *this; } #endif -String & String::operator =(const char *cstr) { - if(cstr) +String & String::operator =(const char *cstr) +{ + if (cstr) + { copy(cstr, strlen(cstr)); + } else + { invalidate(); + } return *this; } String & String::operator = (const __FlashStringHelper *pstr) { - if (pstr) copy(pstr, strlen_P((PGM_P)pstr)); - else invalidate(); + if (pstr) + { + copy(pstr, strlen_P((PGM_P)pstr)); + } + else + { + invalidate(); + } return *this; } @@ -298,100 +375,138 @@ String & String::operator = (const __FlashStringHelper *pstr) // /* concat */ // /*********************************************/ -unsigned char String::concat(const String &s) { +unsigned char String::concat(const String &s) +{ // Special case if we're concatting ourself (s += s;) since we may end up // realloc'ing the buffer and moving s.buffer in the method called - if (&s == this) { + if (&s == this) + { unsigned int newlen = 2 * len(); if (!s.buffer()) + { return 0; + } if (s.len() == 0) + { return 1; + } if (!reserve(newlen)) + { return 0; + } memcpy(wbuffer() + len(), buffer(), len()); setLen(newlen); wbuffer()[len()] = 0; return 1; - } else { + } + else + { return concat(s.buffer(), s.len()); } } -unsigned char String::concat(const char *cstr, unsigned int length) { +unsigned char String::concat(const char *cstr, unsigned int length) +{ unsigned int newlen = len() + length; - if(!cstr) + if (!cstr) + { return 0; - if(length == 0) + } + if (length == 0) + { return 1; - if(!reserve(newlen)) + } + if (!reserve(newlen)) + { return 0; + } strcpy(wbuffer() + len(), cstr); setLen(newlen); return 1; } -unsigned char String::concat(const char *cstr) { - if(!cstr) +unsigned char String::concat(const char *cstr) +{ + if (!cstr) + { return 0; + } return concat(cstr, strlen(cstr)); } -unsigned char String::concat(char c) { +unsigned char String::concat(char c) +{ char buf[2]; buf[0] = c; buf[1] = 0; return concat(buf, 1); } -unsigned char String::concat(unsigned char num) { +unsigned char String::concat(unsigned char num) +{ char buf[1 + 3 * sizeof(unsigned char)]; sprintf(buf, "%d", num); return concat(buf, strlen(buf)); } -unsigned char String::concat(int num) { +unsigned char String::concat(int num) +{ char buf[2 + 3 * sizeof(int)]; sprintf(buf, "%d", num); return concat(buf, strlen(buf)); } -unsigned char String::concat(unsigned int num) { +unsigned char String::concat(unsigned int num) +{ char buf[1 + 3 * sizeof(unsigned int)]; utoa(num, buf, 10); return concat(buf, strlen(buf)); } -unsigned char String::concat(long num) { +unsigned char String::concat(long num) +{ char buf[2 + 3 * sizeof(long)]; sprintf(buf, "%ld", num); return concat(buf, strlen(buf)); } -unsigned char String::concat(unsigned long num) { +unsigned char String::concat(unsigned long num) +{ char buf[1 + 3 * sizeof(unsigned long)]; ultoa(num, buf, 10); return concat(buf, strlen(buf)); } -unsigned char String::concat(float num) { +unsigned char String::concat(float num) +{ char buf[20]; char* string = dtostrf(num, 4, 2, buf); return concat(string, strlen(string)); } -unsigned char String::concat(double num) { +unsigned char String::concat(double num) +{ char buf[20]; char* string = dtostrf(num, 4, 2, buf); return concat(string, strlen(string)); } -unsigned char String::concat(const __FlashStringHelper * str) { - if (!str) return 0; +unsigned char String::concat(const __FlashStringHelper * str) +{ + if (!str) + { + return 0; + } int length = strlen_P((PGM_P)str); - if (length == 0) return 1; + if (length == 0) + { + return 1; + } unsigned int newlen = len() + length; - if (!reserve(newlen)) return 0; + if (!reserve(newlen)) + { + return 0; + } strcpy_P(wbuffer() + len(), (PGM_P)str); setLen(newlen); return 1; @@ -401,73 +516,103 @@ unsigned char String::concat(const __FlashStringHelper * str) { /* Concatenate */ /*********************************************/ -StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs) { +StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs) +{ StringSumHelper &a = const_cast(lhs); - if(!a.concat(rhs.buffer(), rhs.len())) + if (!a.concat(rhs.buffer(), rhs.len())) + { a.invalidate(); + } return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr) { +StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr) +{ StringSumHelper &a = const_cast(lhs); - if(!cstr || !a.concat(cstr, strlen(cstr))) + if (!cstr || !a.concat(cstr, strlen(cstr))) + { a.invalidate(); + } return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, char c) { +StringSumHelper & operator +(const StringSumHelper &lhs, char c) +{ StringSumHelper &a = const_cast(lhs); - if(!a.concat(c)) + if (!a.concat(c)) + { a.invalidate(); + } return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num) { +StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num) +{ StringSumHelper &a = const_cast(lhs); - if(!a.concat(num)) + if (!a.concat(num)) + { a.invalidate(); + } return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, int num) { +StringSumHelper & operator +(const StringSumHelper &lhs, int num) +{ StringSumHelper &a = const_cast(lhs); - if(!a.concat(num)) + if (!a.concat(num)) + { a.invalidate(); + } return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num) { +StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num) +{ StringSumHelper &a = const_cast(lhs); - if(!a.concat(num)) + if (!a.concat(num)) + { a.invalidate(); + } return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, long num) { +StringSumHelper & operator +(const StringSumHelper &lhs, long num) +{ StringSumHelper &a = const_cast(lhs); - if(!a.concat(num)) + if (!a.concat(num)) + { a.invalidate(); + } return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num) { +StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num) +{ StringSumHelper &a = const_cast(lhs); - if(!a.concat(num)) + if (!a.concat(num)) + { a.invalidate(); + } return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, float num) { +StringSumHelper & operator +(const StringSumHelper &lhs, float num) +{ StringSumHelper &a = const_cast(lhs); - if(!a.concat(num)) + if (!a.concat(num)) + { a.invalidate(); + } return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, double num) { +StringSumHelper & operator +(const StringSumHelper &lhs, double num) +{ StringSumHelper &a = const_cast(lhs); - if(!a.concat(num)) + if (!a.concat(num)) + { a.invalidate(); + } return a; } @@ -475,7 +620,9 @@ StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHel { StringSumHelper &a = const_cast(lhs); if (!a.concat(rhs)) + { a.invalidate(); + } return a; } @@ -483,79 +630,115 @@ StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHel // /* Comparison */ // /*********************************************/ -int String::compareTo(const String &s) const { - if(!buffer() || !s.buffer()) { - if(s.buffer() && s.len() > 0) +int String::compareTo(const String &s) const +{ + if (!buffer() || !s.buffer()) + { + if (s.buffer() && s.len() > 0) + { return 0 - *(unsigned char *) s.buffer(); - if(buffer() && len() > 0) + } + if (buffer() && len() > 0) + { return *(unsigned char *) buffer(); + } return 0; } return strcmp(buffer(), s.buffer()); } -unsigned char String::equals(const String &s2) const { +unsigned char String::equals(const String &s2) const +{ return (len() == s2.len() && compareTo(s2) == 0); } -unsigned char String::equals(const char *cstr) const { - if(len() == 0) +unsigned char String::equals(const char *cstr) const +{ + if (len() == 0) + { return (cstr == NULL || *cstr == 0); - if(cstr == NULL) + } + if (cstr == NULL) + { return buffer()[0] == 0; + } return strcmp(buffer(), cstr) == 0; } -unsigned char String::operator<(const String &rhs) const { +unsigned char String::operator<(const String &rhs) const +{ return compareTo(rhs) < 0; } -unsigned char String::operator>(const String &rhs) const { +unsigned char String::operator>(const String &rhs) const +{ return compareTo(rhs) > 0; } -unsigned char String::operator<=(const String &rhs) const { +unsigned char String::operator<=(const String &rhs) const +{ return compareTo(rhs) <= 0; } -unsigned char String::operator>=(const String &rhs) const { +unsigned char String::operator>=(const String &rhs) const +{ return compareTo(rhs) >= 0; } -unsigned char String::equalsIgnoreCase(const String &s2) const { - if(this == &s2) +unsigned char String::equalsIgnoreCase(const String &s2) const +{ + if (this == &s2) + { return 1; - if(len() != s2.len()) + } + if (len() != s2.len()) + { return 0; - if(len() == 0) + } + if (len() == 0) + { return 1; + } const char *p1 = buffer(); const char *p2 = s2.buffer(); - while(*p1) { - if(tolower(*p1++) != tolower(*p2++)) + while (*p1) + { + if (tolower(*p1++) != tolower(*p2++)) + { return 0; + } } return 1; } -unsigned char String::equalsConstantTime(const String &s2) const { +unsigned char String::equalsConstantTime(const String &s2) const +{ // To avoid possible time-based attacks present function // compares given strings in a constant time. - if(len() != s2.len()) + if (len() != s2.len()) + { return 0; + } //at this point lengths are the same - if(len() == 0) + if (len() == 0) + { return 1; + } //at this point lenghts are the same and non-zero const char *p1 = buffer(); const char *p2 = s2.buffer(); unsigned int equalchars = 0; unsigned int diffchars = 0; - while(*p1) { - if(*p1 == *p2) + while (*p1) + { + if (*p1 == *p2) + { ++equalchars; + } else + { ++diffchars; + } ++p1; ++p2; } @@ -565,21 +748,30 @@ unsigned char String::equalsConstantTime(const String &s2) const { return (equalcond & diffcond); //bitwise AND } -unsigned char String::startsWith(const String &s2) const { - if(len() < s2.len()) +unsigned char String::startsWith(const String &s2) const +{ + if (len() < s2.len()) + { return 0; + } return startsWith(s2, 0); } -unsigned char String::startsWith(const String &s2, unsigned int offset) const { - if(offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer()) +unsigned char String::startsWith(const String &s2, unsigned int offset) const +{ + if (offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer()) + { return 0; + } return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0; } -unsigned char String::endsWith(const String &s2) const { - if(len() < s2.len() || !buffer() || !s2.buffer()) +unsigned char String::endsWith(const String &s2) const +{ + if (len() < s2.len() || !buffer() || !s2.buffer()) + { return 0; + } return strcmp(&buffer()[len() - s2.len()], s2.buffer()) == 0; } @@ -587,40 +779,55 @@ unsigned char String::endsWith(const String &s2) const { // /* Character Access */ // /*********************************************/ -char String::charAt(unsigned int loc) const { +char String::charAt(unsigned int loc) const +{ return operator[](loc); } -void String::setCharAt(unsigned int loc, char c) { - if(loc < len()) +void String::setCharAt(unsigned int loc, char c) +{ + if (loc < len()) + { wbuffer()[loc] = c; + } } -char & String::operator[](unsigned int index) { +char & String::operator[](unsigned int index) +{ static char dummy_writable_char; - if(index >= len() || !buffer()) { + if (index >= len() || !buffer()) + { dummy_writable_char = 0; return dummy_writable_char; } return wbuffer()[index]; } -char String::operator[](unsigned int index) const { - if(index >= len() || !buffer()) +char String::operator[](unsigned int index) const +{ + if (index >= len() || !buffer()) + { return 0; + } return buffer()[index]; } -void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const { - if(!bufsize || !buf) +void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const +{ + if (!bufsize || !buf) + { return; - if(index >= len()) { + } + if (index >= len()) + { buf[0] = 0; return; } unsigned int n = bufsize - 1; - if(n > len() - index) + if (n > len() - index) + { n = len() - index; + } strncpy((char *) buf, buffer() + index, n); buf[n] = 0; } @@ -629,79 +836,114 @@ void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int ind // /* Search */ // /*********************************************/ -int String::indexOf(char c) const { +int String::indexOf(char c) const +{ return indexOf(c, 0); } -int String::indexOf(char ch, unsigned int fromIndex) const { - if(fromIndex >= len()) +int String::indexOf(char ch, unsigned int fromIndex) const +{ + if (fromIndex >= len()) + { return -1; + } const char* temp = strchr(buffer() + fromIndex, ch); - if(temp == NULL) + if (temp == NULL) + { return -1; + } return temp - buffer(); } -int String::indexOf(const String &s2) const { +int String::indexOf(const String &s2) const +{ return indexOf(s2, 0); } -int String::indexOf(const String &s2, unsigned int fromIndex) const { - if(fromIndex >= len()) +int String::indexOf(const String &s2, unsigned int fromIndex) const +{ + if (fromIndex >= len()) + { return -1; + } const char *found = strstr(buffer() + fromIndex, s2.buffer()); - if(found == NULL) + if (found == NULL) + { return -1; + } return found - buffer(); } -int String::lastIndexOf(char theChar) const { +int String::lastIndexOf(char theChar) const +{ return lastIndexOf(theChar, len() - 1); } -int String::lastIndexOf(char ch, unsigned int fromIndex) const { - if(fromIndex >= len()) +int String::lastIndexOf(char ch, unsigned int fromIndex) const +{ + if (fromIndex >= len()) + { return -1; + } char tempchar = buffer()[fromIndex + 1]; wbuffer()[fromIndex + 1] = '\0'; char* temp = strrchr(wbuffer(), ch); wbuffer()[fromIndex + 1] = tempchar; - if(temp == NULL) + if (temp == NULL) + { return -1; + } return temp - buffer(); } -int String::lastIndexOf(const String &s2) const { +int String::lastIndexOf(const String &s2) const +{ return lastIndexOf(s2, len() - s2.len()); } -int String::lastIndexOf(const String &s2, unsigned int fromIndex) const { - if(s2.len() == 0 || len() == 0 || s2.len() > len()) +int String::lastIndexOf(const String &s2, unsigned int fromIndex) const +{ + if (s2.len() == 0 || len() == 0 || s2.len() > len()) + { return -1; - if(fromIndex >= len()) + } + if (fromIndex >= len()) + { fromIndex = len() - 1; + } int found = -1; - for(char *p = wbuffer(); p <= wbuffer() + fromIndex; p++) { + for (char *p = wbuffer(); p <= wbuffer() + fromIndex; p++) + { p = strstr(p, s2.buffer()); - if(!p) + if (!p) + { break; - if((unsigned int) (p - wbuffer()) <= fromIndex) + } + if ((unsigned int)(p - wbuffer()) <= fromIndex) + { found = p - buffer(); + } } return found; } -String String::substring(unsigned int left, unsigned int right) const { - if(left > right) { +String String::substring(unsigned int left, unsigned int right) const +{ + if (left > right) + { unsigned int temp = right; right = left; left = temp; } String out; - if(left >= len()) + if (left >= len()) + { return out; - if(right > len()) + } + if (right > len()) + { right = len(); + } char temp = buffer()[right]; // save the replaced character wbuffer()[right] = '\0'; out = wbuffer() + left; // pointer arithmetic @@ -713,29 +955,43 @@ String String::substring(unsigned int left, unsigned int right) const { // /* Modification */ // /*********************************************/ -void String::replace(char find, char replace) { - if(!buffer()) +void String::replace(char find, char replace) +{ + if (!buffer()) + { return; - for(char *p = wbuffer(); *p; p++) { - if(*p == find) + } + for (char *p = wbuffer(); *p; p++) + { + if (*p == find) + { *p = replace; + } } } -void String::replace(const String& find, const String& replace) { - if(len() == 0 || find.len() == 0) +void String::replace(const String& find, const String& replace) +{ + if (len() == 0 || find.len() == 0) + { return; + } int diff = replace.len() - find.len(); char *readFrom = wbuffer(); char *foundAt; - if(diff == 0) { - while((foundAt = strstr(readFrom, find.buffer())) != NULL) { + if (diff == 0) + { + while ((foundAt = strstr(readFrom, find.buffer())) != NULL) + { memmove(foundAt, replace.buffer(), replace.len()); readFrom = foundAt + replace.len(); } - } else if(diff < 0) { + } + else if (diff < 0) + { char *writeTo = wbuffer(); - while((foundAt = strstr(readFrom, find.buffer())) != NULL) { + while ((foundAt = strstr(readFrom, find.buffer())) != NULL) + { unsigned int n = foundAt - readFrom; memmove(writeTo, readFrom, n); writeTo += n; @@ -744,22 +1000,30 @@ void String::replace(const String& find, const String& replace) { readFrom = foundAt + find.len(); setLen(len() + diff); } - memmove(writeTo, readFrom, strlen(readFrom)+1); - } else { + memmove(writeTo, readFrom, strlen(readFrom) + 1); + } + else + { unsigned int size = len(); // compute size needed for result - while((foundAt = strstr(readFrom, find.buffer())) != NULL) { + while ((foundAt = strstr(readFrom, find.buffer())) != NULL) + { readFrom = foundAt + find.len(); size += diff; } - if(size == len()) + if (size == len()) + { return; - if(size > capacity() && !changeBuffer(size)) - return; // XXX: tell user! + } + if (size > capacity() && !changeBuffer(size)) + { + return; // XXX: tell user! + } int index = len() - 1; - while(index >= 0 && (index = lastIndexOf(find, index)) >= 0) { + while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) + { readFrom = wbuffer() + index + find.len(); memmove(readFrom + diff, readFrom, len() - (readFrom - buffer())); - int newLen = len() + diff; + int newLen = len() + diff; memmove(wbuffer() + index, replace.buffer(), replace.len()); setLen(newLen); wbuffer()[newLen] = 0; @@ -768,21 +1032,26 @@ void String::replace(const String& find, const String& replace) { } } -void String::remove(unsigned int index) { +void String::remove(unsigned int index) +{ // Pass the biggest integer as the count. The remove method // below will take care of truncating it at the end of the // string. remove(index, (unsigned int) -1); } -void String::remove(unsigned int index, unsigned int count) { - if(index >= len()) { +void String::remove(unsigned int index, unsigned int count) +{ + if (index >= len()) + { return; } - if(count <= 0) { + if (count <= 0) + { return; } - if(count > len() - index) { + if (count > len() - index) + { count = len() - index; } char *writeTo = wbuffer() + index; @@ -792,35 +1061,52 @@ void String::remove(unsigned int index, unsigned int count) { wbuffer()[newlen] = 0; } -void String::toLowerCase(void) { - if(!buffer()) +void String::toLowerCase(void) +{ + if (!buffer()) + { return; - for(char *p = wbuffer(); *p; p++) { + } + for (char *p = wbuffer(); *p; p++) + { *p = tolower(*p); } } -void String::toUpperCase(void) { - if(!buffer()) +void String::toUpperCase(void) +{ + if (!buffer()) + { return; - for(char *p = wbuffer(); *p; p++) { + } + for (char *p = wbuffer(); *p; p++) + { *p = toupper(*p); } } -void String::trim(void) { - if(!buffer() || len() == 0) +void String::trim(void) +{ + if (!buffer() || len() == 0) + { return; + } char *begin = wbuffer(); - while(isspace(*begin)) + while (isspace(*begin)) + { begin++; + } char *end = wbuffer() + len() - 1; - while(isspace(*end) && end >= begin) + while (isspace(*end) && end >= begin) + { end--; + } unsigned int newlen = end + 1 - begin; setLen(newlen); - if(begin > buffer()) + if (begin > buffer()) + { memmove(wbuffer(), begin, newlen); + } wbuffer()[newlen] = 0; } @@ -828,22 +1114,30 @@ void String::trim(void) { // /* Parsing / Conversion */ // /*********************************************/ -long String::toInt(void) const { +long String::toInt(void) const +{ if (buffer()) + { return atol(buffer()); + } return 0; } -float String::toFloat(void) const { +float String::toFloat(void) const +{ if (buffer()) + { return atof(buffer()); + } return 0; } double String::toDouble(void) const { if (buffer()) + { return atof(buffer()); + } return 0.0; } diff --git a/cores/esp8266/WString.h b/cores/esp8266/WString.h index 3192a9ee33..3cf85a9db7 100644 --- a/cores/esp8266/WString.h +++ b/cores/esp8266/WString.h @@ -1,23 +1,23 @@ /* - WString.h - String library for Wiring & Arduino - ...mostly rewritten by Paul Stoffregen... - Copyright (c) 2009-10 Hernando Barragan. All right reserved. - Copyright 2011, Paul Stoffregen, paul@pjrc.com + WString.h - String library for Wiring & Arduino + ...mostly rewritten by Paul Stoffregen... + Copyright (c) 2009-10 Hernando Barragan. All right reserved. + Copyright 2011, Paul Stoffregen, paul@pjrc.com - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef String_class_h #define String_class_h @@ -39,285 +39,373 @@ class __FlashStringHelper; #define F(string_literal) (FPSTR(PSTR(string_literal))) // The string class -class String { - // use a function pointer to allow for "if (s)" without the - // complications of an operator bool(). for more information, see: - // http://www.artima.com/cppsource/safebool.html - typedef void (String::*StringIfHelperType)() const; - void StringIfHelper() const { - } +class String +{ + // use a function pointer to allow for "if (s)" without the + // complications of an operator bool(). for more information, see: + // http://www.artima.com/cppsource/safebool.html + typedef void (String::*StringIfHelperType)() const; + void StringIfHelper() const + { + } - public: - // constructors - // creates a copy of the initial value. - // 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 - // be false). - String(const char *cstr = ""); - String(const String &str); - String(const __FlashStringHelper *str); +public: + // constructors + // creates a copy of the initial value. + // 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 + // be false). + String(const char *cstr = ""); + String(const String &str); + String(const __FlashStringHelper *str); #ifdef __GXX_EXPERIMENTAL_CXX0X__ - String(String &&rval); - String(StringSumHelper &&rval); + String(String &&rval); + String(StringSumHelper &&rval); #endif - explicit String(char c); - explicit String(unsigned char, unsigned char base = 10); - explicit String(int, unsigned char base = 10); - explicit String(unsigned int, unsigned char base = 10); - explicit String(long, unsigned char base = 10); - explicit String(unsigned long, unsigned char base = 10); - explicit String(float, unsigned char decimalPlaces = 2); - explicit String(double, unsigned char decimalPlaces = 2); - ~String(void); + explicit String(char c); + explicit String(unsigned char, unsigned char base = 10); + explicit String(int, unsigned char base = 10); + explicit String(unsigned int, unsigned char base = 10); + explicit String(long, unsigned char base = 10); + explicit String(unsigned long, unsigned char base = 10); + explicit String(float, unsigned char decimalPlaces = 2); + explicit String(double, unsigned char decimalPlaces = 2); + ~String(void); - // memory management - // return true on success, false on failure (in which case, the string - // is left unchanged). reserve(0), if successful, will validate an - // invalid string (i.e., "if (s)" will be true afterwards) - unsigned char reserve(unsigned int size); - inline unsigned int length(void) const { - if(buffer()) { - return len(); - } else { - return 0; - } + // memory management + // return true on success, false on failure (in which case, the string + // is left unchanged). reserve(0), if successful, will validate an + // invalid string (i.e., "if (s)" will be true afterwards) + unsigned char reserve(unsigned int size); + inline unsigned int length(void) const + { + if (buffer()) + { + return len(); + } + else + { + return 0; } + } - // creates a copy of the assigned value. if the value is null or - // invalid, or if the memory allocation fails, the string will be - // marked as invalid ("if (s)" will be false). - String & operator =(const String &rhs); - String & operator =(const char *cstr); - String & operator = (const __FlashStringHelper *str); + // creates a copy of the assigned value. if the value is null or + // invalid, or if the memory allocation fails, the string will be + // marked as invalid ("if (s)" will be false). + String & operator =(const String &rhs); + String & operator =(const char *cstr); + String & operator = (const __FlashStringHelper *str); #ifdef __GXX_EXPERIMENTAL_CXX0X__ - String & operator =(String &&rval); - String & operator =(StringSumHelper &&rval); + String & operator =(String &&rval); + String & operator =(StringSumHelper &&rval); #endif - // concatenate (works w/ built-in types) + // concatenate (works w/ built-in types) - // returns true on success, false on failure (in which case, the string - // is left unchanged). if the argument is null or invalid, the - // concatenation is considered unsuccessful. - unsigned char concat(const String &str); - unsigned char concat(const char *cstr); - unsigned char concat(char c); - unsigned char concat(unsigned char c); - unsigned char concat(int num); - unsigned char concat(unsigned int num); - unsigned char concat(long num); - unsigned char concat(unsigned long num); - unsigned char concat(float num); - unsigned char concat(double num); - unsigned char concat(const __FlashStringHelper * str); + // returns true on success, false on failure (in which case, the string + // is left unchanged). if the argument is null or invalid, the + // concatenation is considered unsuccessful. + unsigned char concat(const String &str); + unsigned char concat(const char *cstr); + unsigned char concat(char c); + unsigned char concat(unsigned char c); + unsigned char concat(int num); + unsigned char concat(unsigned int num); + unsigned char concat(long num); + unsigned char concat(unsigned long num); + unsigned char concat(float num); + unsigned char concat(double num); + unsigned char concat(const __FlashStringHelper * str); - // if there's not enough memory for the concatenated value, the string - // will be left unchanged (but this isn't signalled in any way) - String & operator +=(const String &rhs) { - concat(rhs); - return (*this); - } - String & operator +=(const char *cstr) { - concat(cstr); - return (*this); - } - String & operator +=(char c) { - concat(c); - return (*this); - } - String & operator +=(unsigned char num) { - concat(num); - return (*this); - } - String & operator +=(int num) { - concat(num); - return (*this); - } - String & operator +=(unsigned int num) { - concat(num); - return (*this); - } - String & operator +=(long num) { - concat(num); - return (*this); - } - String & operator +=(unsigned long num) { - concat(num); - return (*this); - } - String & operator +=(float num) { - concat(num); - return (*this); - } - String & operator +=(double num) { - concat(num); - return (*this); - } - String & operator += (const __FlashStringHelper *str){ - concat(str); - return (*this); - } + // if there's not enough memory for the concatenated value, the string + // will be left unchanged (but this isn't signalled in any way) + String & operator +=(const String &rhs) + { + concat(rhs); + return (*this); + } + String & operator +=(const char *cstr) + { + concat(cstr); + return (*this); + } + String & operator +=(char c) + { + concat(c); + return (*this); + } + String & operator +=(unsigned char num) + { + concat(num); + return (*this); + } + String & operator +=(int num) + { + concat(num); + return (*this); + } + String & operator +=(unsigned int num) + { + concat(num); + return (*this); + } + String & operator +=(long num) + { + concat(num); + return (*this); + } + String & operator +=(unsigned long num) + { + concat(num); + return (*this); + } + String & operator +=(float num) + { + concat(num); + return (*this); + } + String & operator +=(double num) + { + concat(num); + return (*this); + } + String & operator += (const __FlashStringHelper *str) + { + concat(str); + return (*this); + } - friend StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs); - friend StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr); - friend StringSumHelper & operator +(const StringSumHelper &lhs, char c); - friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num); - friend StringSumHelper & operator +(const StringSumHelper &lhs, int num); - friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num); - friend StringSumHelper & operator +(const StringSumHelper &lhs, long num); - friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num); - friend StringSumHelper & operator +(const StringSumHelper &lhs, float num); - friend StringSumHelper & operator +(const StringSumHelper &lhs, double num); - friend StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs); + friend StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs); + friend StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr); + friend StringSumHelper & operator +(const StringSumHelper &lhs, char c); + friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, int num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, long num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, float num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, double num); + friend StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs); - // comparison (only works w/ Strings and "strings") - operator StringIfHelperType() const { - return buffer() ? &String::StringIfHelper : 0; - } - int compareTo(const String &s) const; - unsigned char equals(const String &s) const; - unsigned char equals(const char *cstr) const; - unsigned char operator ==(const String &rhs) const { - return equals(rhs); - } - unsigned char operator ==(const char *cstr) const { - return equals(cstr); - } - unsigned char operator !=(const String &rhs) const { - return !equals(rhs); - } - unsigned char operator !=(const char *cstr) const { - return !equals(cstr); - } - unsigned char operator <(const String &rhs) const; - unsigned char operator >(const String &rhs) const; - unsigned char operator <=(const String &rhs) const; - unsigned char operator >=(const String &rhs) const; - unsigned char equalsIgnoreCase(const String &s) const; - unsigned char equalsConstantTime(const String &s) const; - unsigned char startsWith(const String &prefix) const; - unsigned char startsWith(const String &prefix, unsigned int offset) const; - unsigned char endsWith(const String &suffix) const; + // comparison (only works w/ Strings and "strings") + operator StringIfHelperType() const + { + return buffer() ? &String::StringIfHelper : 0; + } + int compareTo(const String &s) const; + unsigned char equals(const String &s) const; + unsigned char equals(const char *cstr) const; + unsigned char operator ==(const String &rhs) const + { + return equals(rhs); + } + unsigned char operator ==(const char *cstr) const + { + return equals(cstr); + } + unsigned char operator !=(const String &rhs) const + { + return !equals(rhs); + } + unsigned char operator !=(const char *cstr) const + { + return !equals(cstr); + } + unsigned char operator <(const String &rhs) const; + unsigned char operator >(const String &rhs) const; + unsigned char operator <=(const String &rhs) const; + unsigned char operator >=(const String &rhs) const; + unsigned char equalsIgnoreCase(const String &s) const; + unsigned char equalsConstantTime(const String &s) const; + unsigned char startsWith(const String &prefix) const; + unsigned char startsWith(const String &prefix, unsigned int offset) const; + unsigned char endsWith(const String &suffix) const; - // character access - char charAt(unsigned int index) const; - void setCharAt(unsigned int index, char c); - char operator [](unsigned int index) const; - char& operator [](unsigned int index); - void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const; - void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const { - getBytes((unsigned char *) buf, bufsize, index); - } - const char* c_str() const { return buffer(); } - char* begin() { return wbuffer(); } - char* end() { return wbuffer() + length(); } - const char* begin() const { return c_str(); } - const char* end() const { return c_str() + length(); } + // character access + char charAt(unsigned int index) const; + void setCharAt(unsigned int index, char c); + char operator [](unsigned int index) const; + char& operator [](unsigned int index); + void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const; + void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const + { + getBytes((unsigned char *) buf, bufsize, index); + } + const char* c_str() const + { + return buffer(); + } + char* begin() + { + return wbuffer(); + } + char* end() + { + return wbuffer() + length(); + } + const char* begin() const + { + return c_str(); + } + const char* end() const + { + return c_str() + length(); + } - // search - int indexOf(char ch) const; - int indexOf(char ch, unsigned int fromIndex) const; - int indexOf(const String &str) const; - int indexOf(const String &str, unsigned int fromIndex) const; - int lastIndexOf(char ch) const; - int lastIndexOf(char ch, unsigned int fromIndex) const; - int lastIndexOf(const String &str) const; - int lastIndexOf(const String &str, unsigned int fromIndex) const; - String substring(unsigned int beginIndex) const { - return substring(beginIndex, len()); - } - ; - String substring(unsigned int beginIndex, unsigned int endIndex) const; + // search + int indexOf(char ch) const; + int indexOf(char ch, unsigned int fromIndex) const; + int indexOf(const String &str) const; + int indexOf(const String &str, unsigned int fromIndex) const; + int lastIndexOf(char ch) const; + int lastIndexOf(char ch, unsigned int fromIndex) const; + int lastIndexOf(const String &str) const; + int lastIndexOf(const String &str, unsigned int fromIndex) const; + String substring(unsigned int beginIndex) const + { + return substring(beginIndex, len()); + } + ; + String substring(unsigned int beginIndex, unsigned int endIndex) const; - // modification - void replace(char find, char replace); - void replace(const String& find, const String& replace); - void remove(unsigned int index); - void remove(unsigned int index, unsigned int count); - void toLowerCase(void); - void toUpperCase(void); - void trim(void); + // modification + void replace(char find, char replace); + void replace(const String& find, const String& replace); + void remove(unsigned int index); + void remove(unsigned int index, unsigned int count); + void toLowerCase(void); + void toUpperCase(void); + void trim(void); - // parsing/conversion - long toInt(void) const; - float toFloat(void) const; - double toDouble(void) const; + // parsing/conversion + long toInt(void) const; + float toFloat(void) const; + double toDouble(void) const; - protected: - // Contains the string info when we're not in SSO mode - struct _ptr { - char * buff; - uint16_t cap; - uint16_t len; - }; +protected: + // Contains the string info when we're not in SSO mode + struct _ptr + { + char * buff; + uint16_t cap; + uint16_t len; + }; - // SSO is handled by checking the last byte of sso_buff. - // When not in SSO mode, that byte is set to 0xff, while when in SSO mode it is always 0x00 (so it can serve as the string terminator as well as a flag) - // This allows strings up up to 12 (11 + \0 termination) without any extra space. - enum { SSOSIZE = sizeof(struct _ptr) + 4 }; // Characters to allocate space for SSO, must be 12 or more - enum { CAPACITY_MAX = 65535 }; // If size of capacity changed, be sure to update this enum - union { - struct _ptr ptr; - char sso_buf[SSOSIZE]; - }; - // Accessor functions - inline bool sso() const { return sso_buf[SSOSIZE - 1] == 0; } - inline unsigned int len() const { return sso() ? strlen(sso_buf) : ptr.len; } - inline unsigned int capacity() const { return sso() ? SSOSIZE - 1 : ptr.cap; } - inline void setSSO(bool sso) { sso_buf[SSOSIZE - 1] = sso ? 0x00 : 0xff; } - inline void setLen(int len) { if (!sso()) ptr.len = len; } - inline void setCapacity(int cap) { if (!sso()) ptr.cap = cap; } - inline void setBuffer(char *buff) { if (!sso()) ptr.buff = buff; } - // Buffer accessor functions - inline const char *buffer() const { return (const char *)(sso() ? sso_buf : ptr.buff); } - inline char *wbuffer() const { return sso() ? const_cast(sso_buf) : ptr.buff; } // Writable version of buffer + // SSO is handled by checking the last byte of sso_buff. + // When not in SSO mode, that byte is set to 0xff, while when in SSO mode it is always 0x00 (so it can serve as the string terminator as well as a flag) + // This allows strings up up to 12 (11 + \0 termination) without any extra space. + enum { SSOSIZE = sizeof(struct _ptr) + 4 }; // Characters to allocate space for SSO, must be 12 or more + enum { CAPACITY_MAX = 65535 }; // If size of capacity changed, be sure to update this enum + union + { + struct _ptr ptr; + char sso_buf[SSOSIZE]; + }; + // Accessor functions + inline bool sso() const + { + return sso_buf[SSOSIZE - 1] == 0; + } + inline unsigned int len() const + { + return sso() ? strlen(sso_buf) : ptr.len; + } + inline unsigned int capacity() const + { + return sso() ? SSOSIZE - 1 : ptr.cap; + } + inline void setSSO(bool sso) + { + sso_buf[SSOSIZE - 1] = sso ? 0x00 : 0xff; + } + inline void setLen(int len) + { + if (!sso()) + { + ptr.len = len; + } + } + inline void setCapacity(int cap) + { + if (!sso()) + { + ptr.cap = cap; + } + } + inline void setBuffer(char *buff) + { + if (!sso()) + { + ptr.buff = buff; + } + } + // Buffer accessor functions + inline const char *buffer() const + { + return (const char *)(sso() ? sso_buf : ptr.buff); + } + inline char *wbuffer() const + { + return sso() ? const_cast(sso_buf) : ptr.buff; // Writable version of buffer + } - protected: - void init(void); - void invalidate(void); - unsigned char changeBuffer(unsigned int maxStrLen); - unsigned char concat(const char *cstr, unsigned int length); +protected: + void init(void); + void invalidate(void); + unsigned char changeBuffer(unsigned int maxStrLen); + unsigned char concat(const char *cstr, unsigned int length); - // copy and move - String & copy(const char *cstr, unsigned int length); - String & copy(const __FlashStringHelper *pstr, unsigned int length); + // copy and move + String & copy(const char *cstr, unsigned int length); + String & copy(const __FlashStringHelper *pstr, unsigned int length); #ifdef __GXX_EXPERIMENTAL_CXX0X__ - void move(String &rhs); + void move(String &rhs); #endif }; -class StringSumHelper: public String { - public: - StringSumHelper(const String &s) : - String(s) { - } - StringSumHelper(const char *p) : - String(p) { - } - StringSumHelper(char c) : - String(c) { - } - StringSumHelper(unsigned char num) : - String(num) { - } - StringSumHelper(int num) : - String(num) { - } - StringSumHelper(unsigned int num) : - String(num) { - } - StringSumHelper(long num) : - String(num) { - } - StringSumHelper(unsigned long num) : - String(num) { - } - StringSumHelper(float num) : - String(num) { - } - StringSumHelper(double num) : - String(num) { - } +class StringSumHelper: public String +{ +public: + StringSumHelper(const String &s) : + String(s) + { + } + StringSumHelper(const char *p) : + String(p) + { + } + StringSumHelper(char c) : + String(c) + { + } + StringSumHelper(unsigned char num) : + String(num) + { + } + StringSumHelper(int num) : + String(num) + { + } + StringSumHelper(unsigned int num) : + String(num) + { + } + StringSumHelper(long num) : + String(num) + { + } + StringSumHelper(unsigned long num) : + String(num) + { + } + StringSumHelper(float num) : + String(num) + { + } + StringSumHelper(double num) : + String(num) + { + } }; extern const String emptyString; diff --git a/cores/esp8266/abi.cpp b/cores/esp8266/abi.cpp index 71967e63e5..85cb16912b 100644 --- a/cores/esp8266/abi.cpp +++ b/cores/esp8266/abi.cpp @@ -1,20 +1,20 @@ /* - Copyright (c) 2014 Arduino. All right reserved. + Copyright (c) 2014 Arduino. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include #include @@ -28,8 +28,8 @@ using __cxxabiv1::__guard; extern void *umm_last_fail_alloc_addr; extern int umm_last_fail_alloc_size; -extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__)); -extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__)); +extern "C" void __cxa_pure_virtual(void) __attribute__((__noreturn__)); +extern "C" void __cxa_deleted_virtual(void) __attribute__((__noreturn__)); void __cxa_pure_virtual(void) { @@ -41,7 +41,8 @@ void __cxa_deleted_virtual(void) panic(); } -typedef struct { +typedef struct +{ uint8_t guard; uint8_t ps; } guard_t; @@ -49,7 +50,8 @@ typedef struct { extern "C" int __cxa_guard_acquire(__guard* pg) { uint8_t ps = xt_rsil(15); - if (reinterpret_cast(pg)->guard) { + if (reinterpret_cast(pg)->guard) + { xt_wsr_ps(ps); return 0; } diff --git a/cores/esp8266/base64.cpp b/cores/esp8266/base64.cpp index 3ae51f5a0f..1f3cb0b51b 100644 --- a/cores/esp8266/base64.cpp +++ b/cores/esp8266/base64.cpp @@ -1,71 +1,74 @@ -/** - * base64.cpp - * - * Created on: 09.12.2015 - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the ESP8266 core for Arduino. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "Arduino.h" -extern "C" { -#include "libb64/cdecode.h" -#include "libb64/cencode.h" -} -#include "base64.h" - -/** - * convert input data to base64 - * @param data uint8_t * - * @param length size_t - * @return String - */ -String base64::encode(uint8_t * data, size_t length, bool doNewLines) { - // base64 needs more size then the source data, use cencode.h macros - size_t size = ((doNewLines ? base64_encode_expected_len(length) - : base64_encode_expected_len_nonewlines(length)) + 1); - char * buffer = (char *) malloc(size); - if(buffer) { - base64_encodestate _state; - if(doNewLines) - { - base64_init_encodestate(&_state); - } - else - { - base64_init_encodestate_nonewlines(&_state); - } - int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state); - len = base64_encode_blockend((buffer + len), &_state); - - String base64 = String(buffer); - free(buffer); - return base64; - } - return String("-FAIL-"); -} - -/** - * convert input data to base64 - * @param text String - * @return String - */ -String base64::encode(String text, bool doNewLines) { - return base64::encode((uint8_t *) text.c_str(), text.length(), doNewLines); -} - +/** + base64.cpp + + Created on: 09.12.2015 + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the ESP8266 core for Arduino. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "Arduino.h" +extern "C" { +#include "libb64/cdecode.h" +#include "libb64/cencode.h" +} +#include "base64.h" + +/** + convert input data to base64 + @param data uint8_t + @param length size_t + @return String +*/ +String base64::encode(uint8_t * data, size_t length, bool doNewLines) +{ + // base64 needs more size then the source data, use cencode.h macros + size_t size = ((doNewLines ? base64_encode_expected_len(length) + : base64_encode_expected_len_nonewlines(length)) + 1); + char * buffer = (char *) malloc(size); + if (buffer) + { + base64_encodestate _state; + if (doNewLines) + { + base64_init_encodestate(&_state); + } + else + { + base64_init_encodestate_nonewlines(&_state); + } + int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state); + len = base64_encode_blockend((buffer + len), &_state); + + String base64 = String(buffer); + free(buffer); + return base64; + } + return String("-FAIL-"); +} + +/** + convert input data to base64 + @param text String + @return String +*/ +String base64::encode(String text, bool doNewLines) +{ + return base64::encode((uint8_t *) text.c_str(), text.length(), doNewLines); +} + diff --git a/cores/esp8266/base64.h b/cores/esp8266/base64.h index 67140123ed..7639e7606b 100644 --- a/cores/esp8266/base64.h +++ b/cores/esp8266/base64.h @@ -1,39 +1,40 @@ -/** - * base64.h - * - * Created on: 09.12.2015 - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the ESP8266 core for Arduino. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef CORE_BASE64_H_ -#define CORE_BASE64_H_ - -class base64 { - public: - // NOTE: The default behaviour of backend (lib64) - // is to add a newline every 72 (encoded) characters output. - // This may 'break' longer uris and json variables - static String encode(uint8_t * data, size_t length, bool doNewLines = true); - static String encode(String text, bool doNewLines = true); - private: -}; - - -#endif /* CORE_BASE64_H_ */ +/** + base64.h + + Created on: 09.12.2015 + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the ESP8266 core for Arduino. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef CORE_BASE64_H_ +#define CORE_BASE64_H_ + +class base64 +{ +public: + // NOTE: The default behaviour of backend (lib64) + // is to add a newline every 72 (encoded) characters output. + // This may 'break' longer uris and json variables + static String encode(uint8_t * data, size_t length, bool doNewLines = true); + static String encode(String text, bool doNewLines = true); +private: +}; + + +#endif /* CORE_BASE64_H_ */ diff --git a/cores/esp8266/binary.h b/cores/esp8266/binary.h index c2f189dad1..80ee5242ef 100644 --- a/cores/esp8266/binary.h +++ b/cores/esp8266/binary.h @@ -1,21 +1,21 @@ /* - binary.h - Definitions for binary constants - Copyright (c) 2006 David A. Mellis. All right reserved. + binary.h - Definitions for binary constants + Copyright (c) 2006 David A. Mellis. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef Binary_h #define Binary_h diff --git a/cores/esp8266/cbuf.cpp b/cores/esp8266/cbuf.cpp index e655ca6fc6..69f7d91ef7 100644 --- a/cores/esp8266/cbuf.cpp +++ b/cores/esp8266/cbuf.cpp @@ -1,56 +1,63 @@ -/* - cbuf.cpp - Circular buffer implementation - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ +/* + cbuf.cpp - Circular buffer implementation + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include "cbuf.h" #include "c_types.h" cbuf::cbuf(size_t size) : - next(NULL), _size(size), _buf(new char[size]), _bufend(_buf + size), _begin(_buf), _end(_begin) { + next(NULL), _size(size), _buf(new char[size]), _bufend(_buf + size), _begin(_buf), _end(_begin) +{ } -cbuf::~cbuf() { +cbuf::~cbuf() +{ delete[] _buf; } -size_t cbuf::resizeAdd(size_t addSize) { +size_t cbuf::resizeAdd(size_t addSize) +{ return resize(_size + addSize); } -size_t cbuf::resize(size_t newSize) { +size_t cbuf::resize(size_t newSize) +{ size_t bytes_available = available(); - // not lose any data - // if data can be lost use remove or flush before resize - if((newSize <= bytes_available) || (newSize == _size)) { + // not lose any data + // if data can be lost use remove or flush before resize + if ((newSize <= bytes_available) || (newSize == _size)) + { return _size; } char *newbuf = new char[newSize]; char *oldbuf = _buf; - if(!newbuf) { + if (!newbuf) + { return _size; } - if(_buf) { + if (_buf) + { read(newbuf, bytes_available); memset((newbuf + bytes_available), 0x00, (newSize - bytes_available)); } @@ -66,37 +73,47 @@ size_t cbuf::resize(size_t newSize) { return _size; } -size_t ICACHE_RAM_ATTR cbuf::available() const { - if(_end >= _begin) { +size_t ICACHE_RAM_ATTR cbuf::available() const +{ + if (_end >= _begin) + { return _end - _begin; } return _size - (_begin - _end); } -size_t cbuf::size() { +size_t cbuf::size() +{ return _size; } -size_t cbuf::room() const { - if(_end >= _begin) { +size_t cbuf::room() const +{ + if (_end >= _begin) + { return _size - (_end - _begin) - 1; } return _begin - _end - 1; } -int cbuf::peek() { - if(empty()) +int cbuf::peek() +{ + if (empty()) + { return -1; + } return static_cast(*_begin); } -size_t cbuf::peek(char *dst, size_t size) { +size_t cbuf::peek(char *dst, size_t size) +{ size_t bytes_available = available(); size_t size_to_read = (size < bytes_available) ? size : bytes_available; size_t size_read = size_to_read; char * begin = _begin; - if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) { + if (_end < _begin && size_to_read > (size_t)(_bufend - _begin)) + { size_t top_size = _bufend - _begin; memcpy(dst, _begin, top_size); begin = _buf; @@ -107,20 +124,25 @@ size_t cbuf::peek(char *dst, size_t size) { return size_read; } -int ICACHE_RAM_ATTR cbuf::read() { - if(empty()) +int ICACHE_RAM_ATTR cbuf::read() +{ + if (empty()) + { return -1; + } char result = *_begin; _begin = wrap_if_bufend(_begin + 1); return static_cast(result); } -size_t cbuf::read(char* dst, size_t size) { +size_t cbuf::read(char* dst, size_t size) +{ size_t bytes_available = available(); size_t size_to_read = (size < bytes_available) ? size : bytes_available; size_t size_read = size_to_read; - if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) { + if (_end < _begin && size_to_read > (size_t)(_bufend - _begin)) + { size_t top_size = _bufend - _begin; memcpy(dst, _begin, top_size); _begin = _buf; @@ -132,20 +154,25 @@ size_t cbuf::read(char* dst, size_t size) { return size_read; } -size_t ICACHE_RAM_ATTR cbuf::write(char c) { - if(full()) +size_t ICACHE_RAM_ATTR cbuf::write(char c) +{ + if (full()) + { return 0; + } *_end = c; _end = wrap_if_bufend(_end + 1); return 1; } -size_t cbuf::write(const char* src, size_t size) { +size_t cbuf::write(const char* src, size_t size) +{ size_t bytes_available = room(); size_t size_to_write = (size < bytes_available) ? size : bytes_available; size_t size_written = size_to_write; - if(_end >= _begin && size_to_write > (size_t) (_bufend - _end)) { + if (_end >= _begin && size_to_write > (size_t)(_bufend - _end)) + { size_t top_size = _bufend - _end; memcpy(_end, src, top_size); _end = _buf; @@ -157,19 +184,23 @@ size_t cbuf::write(const char* src, size_t size) { return size_written; } -void cbuf::flush() { +void cbuf::flush() +{ _begin = _buf; _end = _buf; } -size_t cbuf::remove(size_t size) { +size_t cbuf::remove(size_t size) +{ size_t bytes_available = available(); - if(size >= bytes_available) { + if (size >= bytes_available) + { flush(); return 0; } size_t size_to_remove = (size < bytes_available) ? size : bytes_available; - if(_end < _begin && size_to_remove > (size_t) (_bufend - _begin)) { + if (_end < _begin && size_to_remove > (size_t)(_bufend - _begin)) + { size_t top_size = _bufend - _begin; _begin = _buf; size_to_remove -= top_size; diff --git a/cores/esp8266/cbuf.h b/cores/esp8266/cbuf.h index 9c358a70e4..a0a133b7b1 100644 --- a/cores/esp8266/cbuf.h +++ b/cores/esp8266/cbuf.h @@ -1,22 +1,22 @@ -/* - cbuf.h - Circular buffer implementation - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ +/* + cbuf.h - Circular buffer implementation + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef __cbuf_h #define __cbuf_h @@ -25,50 +25,54 @@ #include #include -class cbuf { - public: - cbuf(size_t size); - ~cbuf(); +class cbuf +{ +public: + cbuf(size_t size); + ~cbuf(); - size_t resizeAdd(size_t addSize); - size_t resize(size_t newSize); - size_t available() const; - size_t size(); + size_t resizeAdd(size_t addSize); + size_t resize(size_t newSize); + size_t available() const; + size_t size(); - size_t room() const; + size_t room() const; - inline bool empty() const { - return _begin == _end; - } + inline bool empty() const + { + return _begin == _end; + } - inline bool full() const { - return wrap_if_bufend(_end + 1) == _begin; - } + inline bool full() const + { + return wrap_if_bufend(_end + 1) == _begin; + } - int peek(); - size_t peek(char *dst, size_t size); + int peek(); + size_t peek(char *dst, size_t size); - int read(); - size_t read(char* dst, size_t size); + int read(); + size_t read(char* dst, size_t size); - size_t write(char c); - size_t write(const char* src, size_t size); + size_t write(char c); + size_t write(const char* src, size_t size); - void flush(); - size_t remove(size_t size); + void flush(); + size_t remove(size_t size); - cbuf *next; + cbuf *next; - private: - inline char* wrap_if_bufend(char* ptr) const { - return (ptr == _bufend) ? _buf : ptr; - } +private: + inline char* wrap_if_bufend(char* ptr) const + { + return (ptr == _bufend) ? _buf : ptr; + } - size_t _size; - char* _buf; - const char* _bufend; - char* _begin; - char* _end; + size_t _size; + char* _buf; + const char* _bufend; + char* _begin; + char* _end; }; diff --git a/cores/esp8266/cont.h b/cores/esp8266/cont.h index 21ecad2806..d8cd7572f5 100644 --- a/cores/esp8266/cont.h +++ b/cores/esp8266/cont.h @@ -1,22 +1,22 @@ /* - cont.h - continuations support for Xtensa call0 ABI - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + cont.h - continuations support for Xtensa call0 ABI + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef CONT_H_ #define CONT_H_ @@ -31,22 +31,23 @@ extern "C" { #endif -typedef struct cont_ { - void (*pc_ret)(void); - unsigned* sp_ret; +typedef struct cont_ +{ + void (*pc_ret)(void); + unsigned* sp_ret; - void (*pc_yield)(void); - unsigned* sp_yield; + void (*pc_yield)(void); + unsigned* sp_yield; - unsigned* stack_end; - unsigned unused1; - unsigned unused2; - unsigned stack_guard1; + unsigned* stack_end; + unsigned unused1; + unsigned unused2; + unsigned stack_guard1; - unsigned stack[CONT_STACKSIZE / 4]; + unsigned stack[CONT_STACKSIZE / 4]; - unsigned stack_guard2; - unsigned* struct_start; + unsigned stack_guard2; + unsigned* struct_start; } cont_t; extern cont_t* g_pcont; diff --git a/cores/esp8266/cont_util.cpp b/cores/esp8266/cont_util.cpp index d21a064e35..019330c5ed 100644 --- a/cores/esp8266/cont_util.cpp +++ b/cores/esp8266/cont_util.cpp @@ -1,22 +1,22 @@ /* - cont_util.s - continuations support for Xtensa call0 ABI - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + cont_util.s - continuations support for Xtensa call0 ABI + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include "cont.h" #include @@ -27,57 +27,64 @@ extern "C" { #define CONT_STACKGUARD 0xfeefeffe -void cont_init(cont_t* cont) { - memset(cont, 0, sizeof(cont_t)); - - cont->stack_guard1 = CONT_STACKGUARD; - cont->stack_guard2 = CONT_STACKGUARD; - cont->stack_end = cont->stack + (sizeof(cont->stack) / 4); - cont->struct_start = (unsigned*) cont; - - // fill stack with magic values to check high water mark - for(int pos = 0; pos < (int)(sizeof(cont->stack) / 4); pos++) + void cont_init(cont_t* cont) { - cont->stack[pos] = CONT_STACKGUARD; + memset(cont, 0, sizeof(cont_t)); + + cont->stack_guard1 = CONT_STACKGUARD; + cont->stack_guard2 = CONT_STACKGUARD; + cont->stack_end = cont->stack + (sizeof(cont->stack) / 4); + cont->struct_start = (unsigned*) cont; + + // fill stack with magic values to check high water mark + for (int pos = 0; pos < (int)(sizeof(cont->stack) / 4); pos++) + { + cont->stack[pos] = CONT_STACKGUARD; + } } -} -int ICACHE_RAM_ATTR cont_check(cont_t* cont) { - if(cont->stack_guard1 != CONT_STACKGUARD || cont->stack_guard2 != CONT_STACKGUARD) return 1; + int ICACHE_RAM_ATTR cont_check(cont_t* cont) + { + if (cont->stack_guard1 != CONT_STACKGUARD || cont->stack_guard2 != CONT_STACKGUARD) + { + return 1; + } - return 0; -} + return 0; + } -// No need for this to be in IRAM, not expected to be IRQ called -int cont_get_free_stack(cont_t* cont) { - uint32_t *head = cont->stack; - int freeWords = 0; + // No need for this to be in IRAM, not expected to be IRQ called + int cont_get_free_stack(cont_t* cont) + { + uint32_t *head = cont->stack; + int freeWords = 0; - while(*head == CONT_STACKGUARD) + while (*head == CONT_STACKGUARD) + { + head++; + freeWords++; + } + + return freeWords * 4; + } + + bool ICACHE_RAM_ATTR cont_can_yield(cont_t* cont) { - head++; - freeWords++; + return !ETS_INTR_WITHINISR() && + cont->pc_ret != 0 && cont->pc_yield == 0; } - - return freeWords * 4; -} - -bool ICACHE_RAM_ATTR cont_can_yield(cont_t* cont) { - return !ETS_INTR_WITHINISR() && - cont->pc_ret != 0 && cont->pc_yield == 0; -} - -// No need for this to be in IRAM, not expected to be IRQ called -void cont_repaint_stack(cont_t *cont) -{ - register uint32_t *sp asm("a1"); - // Ensure 64 bytes adjacent to the current SP don't get touched to endure - // we don't accidentally trounce over locals or IRQ temps. - // Fill stack with magic values - for ( uint32_t *pos = sp - 16; pos >= &cont->stack[0]; pos-- ) + + // No need for this to be in IRAM, not expected to be IRQ called + void cont_repaint_stack(cont_t *cont) { - *pos = CONT_STACKGUARD; + register uint32_t *sp asm("a1"); + // Ensure 64 bytes adjacent to the current SP don't get touched to endure + // we don't accidentally trounce over locals or IRQ temps. + // Fill stack with magic values + for (uint32_t *pos = sp - 16; pos >= &cont->stack[0]; pos--) + { + *pos = CONT_STACKGUARD; + } } -} }; diff --git a/cores/esp8266/core_esp8266_app_entry_noextra4k.cpp b/cores/esp8266/core_esp8266_app_entry_noextra4k.cpp index f2f9bffa32..216068db2f 100644 --- a/cores/esp8266/core_esp8266_app_entry_noextra4k.cpp +++ b/cores/esp8266/core_esp8266_app_entry_noextra4k.cpp @@ -1,23 +1,23 @@ /* - * This is the original app_entry() not providing extra 4K heap, but allowing - * the use of WPS. - * - * see comments in core_esp8266_main.cpp's app_entry() - * - */ + This is the original app_entry() not providing extra 4K heap, but allowing + the use of WPS. + + see comments in core_esp8266_main.cpp's app_entry() + +*/ #include #include "cont.h" #include "coredecls.h" -void disable_extra4k_at_link_time (void) +void disable_extra4k_at_link_time(void) { /* - * does nothing - * allows overriding the core_esp8266_main.cpp's app_entry() - * by this one below, at link time - * - */ + does nothing + allows overriding the core_esp8266_main.cpp's app_entry() + by this one below, at link time + + */ } /* the following code is linked only if a call to the above function is made somewhere */ @@ -25,7 +25,7 @@ void disable_extra4k_at_link_time (void) extern "C" void call_user_start(); /* this is the default NONOS-SDK user's heap location */ -static cont_t g_cont __attribute__ ((aligned (16))); +static cont_t g_cont __attribute__((aligned(16))); extern "C" void ICACHE_RAM_ATTR app_entry_redefinable(void) { diff --git a/cores/esp8266/core_esp8266_eboot_command.cpp b/cores/esp8266/core_esp8266_eboot_command.cpp index 44d65a8c3e..c4af9bdefd 100644 --- a/cores/esp8266/core_esp8266_eboot_command.cpp +++ b/cores/esp8266/core_esp8266_eboot_command.cpp @@ -1,23 +1,23 @@ -/* - core_esp8266_eboot_command.c - interface to the eboot bootloader - - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ +/* + core_esp8266_eboot_command.c - interface to the eboot bootloader + + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include #include @@ -25,67 +25,74 @@ extern "C" { -static uint32_t crc_update(uint32_t crc, const uint8_t *data, size_t length) -{ - uint32_t i; - bool bit; - uint8_t c; - - while (length--) { - c = *data++; - for (i = 0x80; i > 0; i >>= 1) { - bit = crc & 0x80000000; - if (c & i) { - bit = !bit; - } - crc <<= 1; - if (bit) { - crc ^= 0x04c11db7; + static uint32_t crc_update(uint32_t crc, const uint8_t *data, size_t length) + { + uint32_t i; + bool bit; + uint8_t c; + + while (length--) + { + c = *data++; + for (i = 0x80; i > 0; i >>= 1) + { + bit = crc & 0x80000000; + if (c & i) + { + bit = !bit; + } + crc <<= 1; + if (bit) + { + crc ^= 0x04c11db7; + } } } - } - return crc; -} - -static uint32_t eboot_command_calculate_crc32(const struct eboot_command* cmd) -{ - return crc_update(0xffffffff, (const uint8_t*) cmd, - offsetof(struct eboot_command, crc32)); -} - -int eboot_command_read(struct eboot_command* cmd) -{ - const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); - uint32_t* dst = (uint32_t *) cmd; - for (uint32_t i = 0; i < dw_count; ++i) { - dst[i] = RTC_MEM[i]; + return crc; } - uint32_t crc32 = eboot_command_calculate_crc32(cmd); - if ((cmd->magic & EBOOT_MAGIC_MASK) != EBOOT_MAGIC || - cmd->crc32 != crc32) { - return 1; + static uint32_t eboot_command_calculate_crc32(const struct eboot_command* cmd) + { + return crc_update(0xffffffff, (const uint8_t*) cmd, + offsetof(struct eboot_command, crc32)); } - return 0; -} + int eboot_command_read(struct eboot_command* cmd) + { + const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); + uint32_t* dst = (uint32_t *) cmd; + for (uint32_t i = 0; i < dw_count; ++i) + { + dst[i] = RTC_MEM[i]; + } -void eboot_command_write(struct eboot_command* cmd) -{ - cmd->magic = EBOOT_MAGIC; - cmd->crc32 = eboot_command_calculate_crc32(cmd); + uint32_t crc32 = eboot_command_calculate_crc32(cmd); + if ((cmd->magic & EBOOT_MAGIC_MASK) != EBOOT_MAGIC || + cmd->crc32 != crc32) + { + return 1; + } - const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); - const uint32_t* src = (const uint32_t *) cmd; - for (uint32_t i = 0; i < dw_count; ++i) { - RTC_MEM[i] = src[i]; + return 0; } -} -void eboot_command_clear() -{ - RTC_MEM[offsetof(struct eboot_command, magic) / sizeof(uint32_t)] = 0; - RTC_MEM[offsetof(struct eboot_command, crc32) / sizeof(uint32_t)] = 0; -} + void eboot_command_write(struct eboot_command* cmd) + { + cmd->magic = EBOOT_MAGIC; + cmd->crc32 = eboot_command_calculate_crc32(cmd); + + const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); + const uint32_t* src = (const uint32_t *) cmd; + for (uint32_t i = 0; i < dw_count; ++i) + { + RTC_MEM[i] = src[i]; + } + } + + void eboot_command_clear() + { + RTC_MEM[offsetof(struct eboot_command, magic) / sizeof(uint32_t)] = 0; + RTC_MEM[offsetof(struct eboot_command, crc32) / sizeof(uint32_t)] = 0; + } }; diff --git a/cores/esp8266/core_esp8266_features.h b/cores/esp8266/core_esp8266_features.h index aff8fa4de9..76b57af62a 100644 --- a/cores/esp8266/core_esp8266_features.h +++ b/cores/esp8266/core_esp8266_features.h @@ -1,39 +1,39 @@ -/* - core_esp8266_features.h - list of features integrated in to ESP8266 core - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - */ - - -#ifndef CORE_ESP8266_FEATURES_H -#define CORE_ESP8266_FEATURES_H - - -#define CORE_HAS_LIBB64 -#define CORE_HAS_BASE64_CLASS -#define CORE_HAS_CXA_GUARD -#define CORE_HAS_UMM - -#define WIFI_HAS_EVENT_CALLBACK - - - - -#endif - +/* + core_esp8266_features.h - list of features integrated in to ESP8266 core + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef CORE_ESP8266_FEATURES_H +#define CORE_ESP8266_FEATURES_H + + +#define CORE_HAS_LIBB64 +#define CORE_HAS_BASE64_CLASS +#define CORE_HAS_CXA_GUARD +#define CORE_HAS_UMM + +#define WIFI_HAS_EVENT_CALLBACK + + + + +#endif + diff --git a/cores/esp8266/core_esp8266_flash_utils.cpp b/cores/esp8266/core_esp8266_flash_utils.cpp index 9abd23c110..9a4136aedd 100644 --- a/cores/esp8266/core_esp8266_flash_utils.cpp +++ b/cores/esp8266/core_esp8266_flash_utils.cpp @@ -1,23 +1,23 @@ -/* - core_esp8266_flash_utils.c - flash and binary image helpers +/* + core_esp8266_flash_utils.c - flash and binary image helpers - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include @@ -27,40 +27,47 @@ extern "C" { -int SPIEraseAreaEx(const uint32_t start, const uint32_t size) -{ - if ((start & (FLASH_SECTOR_SIZE - 1)) != 0) { - return 1; - } + int SPIEraseAreaEx(const uint32_t start, const uint32_t size) + { + if ((start & (FLASH_SECTOR_SIZE - 1)) != 0) + { + return 1; + } - const uint32_t sectors_per_block = FLASH_BLOCK_SIZE / FLASH_SECTOR_SIZE; - uint32_t current_sector = start / FLASH_SECTOR_SIZE; - uint32_t sector_count = (size + FLASH_SECTOR_SIZE - 1) / FLASH_SECTOR_SIZE; - const uint32_t end = current_sector + sector_count; + const uint32_t sectors_per_block = FLASH_BLOCK_SIZE / FLASH_SECTOR_SIZE; + uint32_t current_sector = start / FLASH_SECTOR_SIZE; + uint32_t sector_count = (size + FLASH_SECTOR_SIZE - 1) / FLASH_SECTOR_SIZE; + const uint32_t end = current_sector + sector_count; - for (; current_sector < end && (current_sector & (sectors_per_block-1)); - ++current_sector, --sector_count) { - if (SPIEraseSector(current_sector)) { - return 2; + for (; current_sector < end && (current_sector & (sectors_per_block - 1)); + ++current_sector, --sector_count) + { + if (SPIEraseSector(current_sector)) + { + return 2; + } } - } - for (;current_sector + sectors_per_block <= end; - current_sector += sectors_per_block, - sector_count -= sectors_per_block) { - if (SPIEraseBlock(current_sector / sectors_per_block)) { - return 3; + for (; current_sector + sectors_per_block <= end; + current_sector += sectors_per_block, + sector_count -= sectors_per_block) + { + if (SPIEraseBlock(current_sector / sectors_per_block)) + { + return 3; + } } - } - for (; current_sector < end; - ++current_sector, --sector_count) { - if (SPIEraseSector(current_sector)) { - return 4; + for (; current_sector < end; + ++current_sector, --sector_count) + { + if (SPIEraseSector(current_sector)) + { + return 4; + } } - } - return 0; -} + return 0; + } }; diff --git a/cores/esp8266/core_esp8266_i2s.cpp b/cores/esp8266/core_esp8266_i2s.cpp index 005d1f4652..48ba7c781b 100644 --- a/cores/esp8266/core_esp8266_i2s.cpp +++ b/cores/esp8266/core_esp8266_i2s.cpp @@ -1,24 +1,24 @@ -/* - i2s.c - Software I2S library for esp8266 - - Code taken and reworked from espessif's I2S example - - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +/* + i2s.c - Software I2S library for esp8266 + + Code taken and reworked from espessif's I2S example + + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Arduino.h" #include "osapi.h" @@ -31,51 +31,53 @@ extern "C" { #define SLC_BUF_CNT (8) // Number of buffers in the I2S circular buffer #define SLC_BUF_LEN (64) // Length of one buffer, in 32-bit words. -// We use a queue to keep track of the DMA buffers that are empty. The ISR -// will push buffers to the back of the queue, the I2S transmitter will pull -// them from the front and fill them. For ease, the queue will contain -// *pointers* to the DMA buffers, not the data itself. The queue depth is -// one smaller than the amount of buffers we have, because there's always a -// buffer that is being used by the DMA subsystem *right now* and we don't -// want to be able to write to that simultaneously. - -// For RX, it's a little different. The buffers in i2s_slc_queue are -// placed onto the list when they're filled by DMA - -typedef struct slc_queue_item { - uint32_t blocksize : 12; - uint32_t datalen : 12; - uint32_t unused : 5; - uint32_t sub_sof : 1; - uint32_t eof : 1; - volatile uint32_t owner : 1; // DMA can change this value - uint32_t * buf_ptr; - struct slc_queue_item * next_link_ptr; -} slc_queue_item_t; - -typedef struct i2s_state { - uint32_t * slc_queue[SLC_BUF_CNT]; - volatile uint8_t slc_queue_len; - uint32_t * slc_buf_pntr[SLC_BUF_CNT]; // Pointer to the I2S DMA buffer data - slc_queue_item_t slc_items[SLC_BUF_CNT]; // I2S DMA buffer descriptors - uint32_t * curr_slc_buf; // Current buffer for writing - uint32_t curr_slc_buf_pos; // Position in the current buffer - void (*callback) (void); - // Callback function should be defined as 'void ICACHE_RAM_ATTR function_name()', - // and be placed in IRAM for faster execution. Avoid long computational tasks in this - // function, use it to set flags and process later. -} i2s_state_t; - -// RX = I2S receive (i.e. microphone), TX = I2S transmit (i.e. DAC) -static i2s_state_t *rx = NULL; -static i2s_state_t *tx = NULL; - -// Last I2S sample rate requested -static uint32_t _i2s_sample_rate; - -// IOs used for I2S. Not defined in i2s.h, unfortunately. -// Note these are internal GPIO numbers and not pins on an -// Arduino board. Users need to verify their particular wiring. + // We use a queue to keep track of the DMA buffers that are empty. The ISR + // will push buffers to the back of the queue, the I2S transmitter will pull + // them from the front and fill them. For ease, the queue will contain + // *pointers* to the DMA buffers, not the data itself. The queue depth is + // one smaller than the amount of buffers we have, because there's always a + // buffer that is being used by the DMA subsystem *right now* and we don't + // want to be able to write to that simultaneously. + + // For RX, it's a little different. The buffers in i2s_slc_queue are + // placed onto the list when they're filled by DMA + + typedef struct slc_queue_item + { + uint32_t blocksize : 12; + uint32_t datalen : 12; + uint32_t unused : 5; + uint32_t sub_sof : 1; + uint32_t eof : 1; + volatile uint32_t owner : 1; // DMA can change this value + uint32_t * buf_ptr; + struct slc_queue_item * next_link_ptr; + } slc_queue_item_t; + + typedef struct i2s_state + { + uint32_t * slc_queue[SLC_BUF_CNT]; + volatile uint8_t slc_queue_len; + uint32_t * slc_buf_pntr[SLC_BUF_CNT]; // Pointer to the I2S DMA buffer data + slc_queue_item_t slc_items[SLC_BUF_CNT]; // I2S DMA buffer descriptors + uint32_t * curr_slc_buf; // Current buffer for writing + uint32_t curr_slc_buf_pos; // Position in the current buffer + void (*callback)(void); + // Callback function should be defined as 'void ICACHE_RAM_ATTR function_name()', + // and be placed in IRAM for faster execution. Avoid long computational tasks in this + // function, use it to set flags and process later. + } i2s_state_t; + + // RX = I2S receive (i.e. microphone), TX = I2S transmit (i.e. DAC) + static i2s_state_t *rx = NULL; + static i2s_state_t *tx = NULL; + + // Last I2S sample rate requested + static uint32_t _i2s_sample_rate; + + // IOs used for I2S. Not defined in i2s.h, unfortunately. + // Note these are internal GPIO numbers and not pins on an + // Arduino board. Users need to verify their particular wiring. #define I2SO_DATA 3 #define I2SO_BCK 15 #define I2SO_WS 2 @@ -83,495 +85,608 @@ static uint32_t _i2s_sample_rate; #define I2SI_BCK 13 #define I2SI_WS 14 -static bool _i2s_is_full(const i2s_state_t *ch) { - if (!ch) { - return false; - } - return (ch->curr_slc_buf_pos==SLC_BUF_LEN || ch->curr_slc_buf==NULL) && (ch->slc_queue_len == 0); -} - -bool i2s_is_full() { - return _i2s_is_full( tx ); -} - -bool i2s_rx_is_full() { - return _i2s_is_full( rx ); -} - -static bool _i2s_is_empty(const i2s_state_t *ch) { - if (!ch) { - return false; - } - return (ch->slc_queue_len >= SLC_BUF_CNT-1); -} - -bool i2s_is_empty() { - return _i2s_is_empty( tx ); -} - -bool i2s_rx_is_empty() { - return _i2s_is_empty( rx ); -} - -static uint16_t _i2s_available(const i2s_state_t *ch) { - if (!ch) { - return 0; - } - return (SLC_BUF_CNT - ch->slc_queue_len) * SLC_BUF_LEN; -} - -uint16_t i2s_available(){ - return _i2s_available( tx ); -} - -uint16_t i2s_rx_available(){ - return _i2s_available( rx ); -} - -// Pop the top off of the queue and return it -static uint32_t * ICACHE_RAM_ATTR i2s_slc_queue_next_item(i2s_state_t *ch) { - uint8_t i; - uint32_t *item = ch->slc_queue[0]; - ch->slc_queue_len--; - for ( i = 0; i < ch->slc_queue_len; i++) { - ch->slc_queue[i] = ch->slc_queue[i+1]; - } - return item; -} - -// Append an item to the end of the queue from receive -static void ICACHE_RAM_ATTR i2s_slc_queue_append_item(i2s_state_t *ch, uint32_t *item) { - // Shift everything up, except for the one corresponding to this item - for (int i=0, dest=0; i < ch->slc_queue_len; i++) { - if (ch->slc_queue[i] != item) { - ch->slc_queue[dest++] = ch->slc_queue[i]; - } - } - if (ch->slc_queue_len < SLC_BUF_CNT - 1) { - ch->slc_queue[ch->slc_queue_len++] = item; - } else { - ch->slc_queue[ch->slc_queue_len] = item; - } -} - -static void ICACHE_RAM_ATTR i2s_slc_isr(void) { - ETS_SLC_INTR_DISABLE(); - uint32_t slc_intr_status = SLCIS; - SLCIC = 0xFFFFFFFF; - if (slc_intr_status & SLCIRXEOF) { - slc_queue_item_t *finished_item = (slc_queue_item_t *)SLCRXEDA; - // Zero the buffer so it is mute in case of underflow - ets_memset((void *)finished_item->buf_ptr, 0x00, SLC_BUF_LEN * 4); - if (tx->slc_queue_len >= SLC_BUF_CNT-1) { - // All buffers are empty. This means we have an underflow - i2s_slc_queue_next_item(tx); // Free space for finished_item - } - tx->slc_queue[tx->slc_queue_len++] = finished_item->buf_ptr; - if (tx->callback) { - tx->callback(); - } - } - if (slc_intr_status & SLCITXEOF) { - slc_queue_item_t *finished_item = (slc_queue_item_t *)SLCTXEDA; - // Set owner back to 1 (SW) or else RX stops. TX has no such restriction. - finished_item->owner = 1; - i2s_slc_queue_append_item(rx, finished_item->buf_ptr); - if (rx->callback) { - rx->callback(); - } - } - ETS_SLC_INTR_ENABLE(); -} - -void i2s_set_callback(void (*callback) (void)) { - tx->callback = callback; -} - -void i2s_rx_set_callback(void (*callback) (void)) { - rx->callback = callback; -} - -static bool _alloc_channel(i2s_state_t *ch) { - ch->slc_queue_len = 0; - for (int x=0; xslc_buf_pntr[x] = (uint32_t *)malloc(SLC_BUF_LEN * sizeof(ch->slc_buf_pntr[0][0])); - if (!ch->slc_buf_pntr[x]) { - // OOM, the upper layer will free up any partially allocated channels. - return false; - } - memset(ch->slc_buf_pntr[x], 0, SLC_BUF_LEN * sizeof(ch->slc_buf_pntr[x][0])); - - ch->slc_items[x].unused = 0; - ch->slc_items[x].owner = 1; - ch->slc_items[x].eof = 1; - ch->slc_items[x].sub_sof = 0; - ch->slc_items[x].datalen = SLC_BUF_LEN * 4; - ch->slc_items[x].blocksize = SLC_BUF_LEN * 4; - ch->slc_items[x].buf_ptr = (uint32_t*)&ch->slc_buf_pntr[x][0]; - ch->slc_items[x].next_link_ptr = (x<(SLC_BUF_CNT-1))?(&ch->slc_items[x+1]):(&ch->slc_items[0]); - } - return true; -} - -static bool i2s_slc_begin() { - if (tx) { - if (!_alloc_channel(tx)) { - return false; - } - } - if (rx) { - if (!_alloc_channel(rx)) { - return false; - } - } - - ETS_SLC_INTR_DISABLE(); - SLCC0 |= SLCRXLR | SLCTXLR; - SLCC0 &= ~(SLCRXLR | SLCTXLR); - SLCIC = 0xFFFFFFFF; - - // Configure DMA - SLCC0 &= ~(SLCMM << SLCM); // Clear DMA MODE - SLCC0 |= (1 << SLCM); // Set DMA MODE to 1 - SLCRXDC |= SLCBINR | SLCBTNR; // Enable INFOR_NO_REPLACE and TOKEN_NO_REPLACE - SLCRXDC &= ~(SLCBRXFE | SLCBRXEM | SLCBRXFM); // Disable RX_FILL, RX_EOF_MODE and RX_FILL_MODE - - //Feed DMA the 1st buffer desc addr - //To send data to the I2S subsystem, counter-intuitively we use the RXLINK part, not the TXLINK as you might - //expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw - //an error at us otherwise. Just feed it any random descriptor. - SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address - SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address - if (!rx) { - SLCTXL |= (uint32)&tx->slc_items[1] << SLCTXLA; // Set fake (unused) RX descriptor address - } else { - SLCTXL |= (uint32)&rx->slc_items[0] << SLCTXLA; // Set real RX address - } - if (!tx) { - SLCRXL |= (uint32)&rx->slc_items[1] << SLCRXLA; // Set fake (unused) TX descriptor address - } else { - SLCRXL |= (uint32)&tx->slc_items[0] << SLCRXLA; // Set real TX address - } - - ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL); - SLCIE = (tx?SLCIRXEOF:0) | (rx?SLCITXEOF:0); // Enable appropriate EOF IRQ - - ETS_SLC_INTR_ENABLE(); - - // Start transmission ("TX" DMA always needed to be enabled) - SLCTXL |= SLCTXLS; - if (tx) { - SLCRXL |= SLCRXLS; - } - - return true; -} - -static void i2s_slc_end(){ - ETS_SLC_INTR_DISABLE(); - SLCIC = 0xFFFFFFFF; - SLCIE = 0; - SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address - SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address - - for (int x = 0; xslc_buf_pntr[x]); - tx->slc_buf_pntr[x] = NULL; - } - if (rx) { - free(rx->slc_buf_pntr[x]); - rx->slc_buf_pntr[x] = NULL; - } - } -} - -// These routines push a single, 32-bit sample to the I2S buffers. Call at (on average) -// at least the current sample rate. -static bool _i2s_write_sample(uint32_t sample, bool nb) { - if (!tx) { - return false; - } - - if (tx->curr_slc_buf_pos==SLC_BUF_LEN || tx->curr_slc_buf==NULL) { - if (tx->slc_queue_len == 0) { - if (nb) { - // Don't wait if nonblocking, just notify upper levels - return false; - } - while (1) { - if (tx->slc_queue_len > 0) { - break; - } else { - optimistic_yield(10000); - } - } - } - ETS_SLC_INTR_DISABLE(); - tx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(tx); - ETS_SLC_INTR_ENABLE(); - tx->curr_slc_buf_pos=0; - } - tx->curr_slc_buf[tx->curr_slc_buf_pos++]=sample; - return true; -} - -bool i2s_write_sample(uint32_t sample) { - return _i2s_write_sample(sample, false); -} - -bool i2s_write_sample_nb(uint32_t sample) { - return _i2s_write_sample(sample, true); -} - -bool i2s_write_lr(int16_t left, int16_t right){ - int sample = right & 0xFFFF; - sample = sample << 16; - sample |= left & 0xFFFF; - return i2s_write_sample(sample); -} - -// writes a buffer of frames into the DMA memory, returns the amount of frames written -// A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel. -static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mono, bool nb) { - uint16_t frames_written=0; - - while(frame_count>0) { - - // make sure we have room in the current buffer - if (tx->curr_slc_buf_pos==SLC_BUF_LEN || tx->curr_slc_buf==NULL) { - // no room in the current buffer? if there are no buffers available then exit + static bool _i2s_is_full(const i2s_state_t *ch) + { + if (!ch) + { + return false; + } + return (ch->curr_slc_buf_pos == SLC_BUF_LEN || ch->curr_slc_buf == NULL) && (ch->slc_queue_len == 0); + } + + bool i2s_is_full() + { + return _i2s_is_full(tx); + } + + bool i2s_rx_is_full() + { + return _i2s_is_full(rx); + } + + static bool _i2s_is_empty(const i2s_state_t *ch) + { + if (!ch) + { + return false; + } + return (ch->slc_queue_len >= SLC_BUF_CNT - 1); + } + + bool i2s_is_empty() + { + return _i2s_is_empty(tx); + } + + bool i2s_rx_is_empty() + { + return _i2s_is_empty(rx); + } + + static uint16_t _i2s_available(const i2s_state_t *ch) + { + if (!ch) + { + return 0; + } + return (SLC_BUF_CNT - ch->slc_queue_len) * SLC_BUF_LEN; + } + + uint16_t i2s_available() + { + return _i2s_available(tx); + } + + uint16_t i2s_rx_available() + { + return _i2s_available(rx); + } + + // Pop the top off of the queue and return it + static uint32_t * ICACHE_RAM_ATTR i2s_slc_queue_next_item(i2s_state_t *ch) + { + uint8_t i; + uint32_t *item = ch->slc_queue[0]; + ch->slc_queue_len--; + for (i = 0; i < ch->slc_queue_len; i++) + { + ch->slc_queue[i] = ch->slc_queue[i + 1]; + } + return item; + } + + // Append an item to the end of the queue from receive + static void ICACHE_RAM_ATTR i2s_slc_queue_append_item(i2s_state_t *ch, uint32_t *item) + { + // Shift everything up, except for the one corresponding to this item + for (int i = 0, dest = 0; i < ch->slc_queue_len; i++) + { + if (ch->slc_queue[i] != item) + { + ch->slc_queue[dest++] = ch->slc_queue[i]; + } + } + if (ch->slc_queue_len < SLC_BUF_CNT - 1) + { + ch->slc_queue[ch->slc_queue_len++] = item; + } + else + { + ch->slc_queue[ch->slc_queue_len] = item; + } + } + + static void ICACHE_RAM_ATTR i2s_slc_isr(void) + { + ETS_SLC_INTR_DISABLE(); + uint32_t slc_intr_status = SLCIS; + SLCIC = 0xFFFFFFFF; + if (slc_intr_status & SLCIRXEOF) + { + slc_queue_item_t *finished_item = (slc_queue_item_t *)SLCRXEDA; + // Zero the buffer so it is mute in case of underflow + ets_memset((void *)finished_item->buf_ptr, 0x00, SLC_BUF_LEN * 4); + if (tx->slc_queue_len >= SLC_BUF_CNT - 1) + { + // All buffers are empty. This means we have an underflow + i2s_slc_queue_next_item(tx); // Free space for finished_item + } + tx->slc_queue[tx->slc_queue_len++] = finished_item->buf_ptr; + if (tx->callback) + { + tx->callback(); + } + } + if (slc_intr_status & SLCITXEOF) + { + slc_queue_item_t *finished_item = (slc_queue_item_t *)SLCTXEDA; + // Set owner back to 1 (SW) or else RX stops. TX has no such restriction. + finished_item->owner = 1; + i2s_slc_queue_append_item(rx, finished_item->buf_ptr); + if (rx->callback) + { + rx->callback(); + } + } + ETS_SLC_INTR_ENABLE(); + } + + void i2s_set_callback(void (*callback)(void)) + { + tx->callback = callback; + } + + void i2s_rx_set_callback(void (*callback)(void)) + { + rx->callback = callback; + } + + static bool _alloc_channel(i2s_state_t *ch) + { + ch->slc_queue_len = 0; + for (int x = 0; x < SLC_BUF_CNT; x++) + { + ch->slc_buf_pntr[x] = (uint32_t *)malloc(SLC_BUF_LEN * sizeof(ch->slc_buf_pntr[0][0])); + if (!ch->slc_buf_pntr[x]) + { + // OOM, the upper layer will free up any partially allocated channels. + return false; + } + memset(ch->slc_buf_pntr[x], 0, SLC_BUF_LEN * sizeof(ch->slc_buf_pntr[x][0])); + + ch->slc_items[x].unused = 0; + ch->slc_items[x].owner = 1; + ch->slc_items[x].eof = 1; + ch->slc_items[x].sub_sof = 0; + ch->slc_items[x].datalen = SLC_BUF_LEN * 4; + ch->slc_items[x].blocksize = SLC_BUF_LEN * 4; + ch->slc_items[x].buf_ptr = (uint32_t*)&ch->slc_buf_pntr[x][0]; + ch->slc_items[x].next_link_ptr = (x < (SLC_BUF_CNT - 1)) ? (&ch->slc_items[x + 1]) : (&ch->slc_items[0]); + } + return true; + } + + static bool i2s_slc_begin() + { + if (tx) + { + if (!_alloc_channel(tx)) + { + return false; + } + } + if (rx) + { + if (!_alloc_channel(rx)) + { + return false; + } + } + + ETS_SLC_INTR_DISABLE(); + SLCC0 |= SLCRXLR | SLCTXLR; + SLCC0 &= ~(SLCRXLR | SLCTXLR); + SLCIC = 0xFFFFFFFF; + + // Configure DMA + SLCC0 &= ~(SLCMM << SLCM); // Clear DMA MODE + SLCC0 |= (1 << SLCM); // Set DMA MODE to 1 + SLCRXDC |= SLCBINR | SLCBTNR; // Enable INFOR_NO_REPLACE and TOKEN_NO_REPLACE + SLCRXDC &= ~(SLCBRXFE | SLCBRXEM | SLCBRXFM); // Disable RX_FILL, RX_EOF_MODE and RX_FILL_MODE + + //Feed DMA the 1st buffer desc addr + //To send data to the I2S subsystem, counter-intuitively we use the RXLINK part, not the TXLINK as you might + //expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw + //an error at us otherwise. Just feed it any random descriptor. + SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address + SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address + if (!rx) + { + SLCTXL |= (uint32)&tx->slc_items[1] << SLCTXLA; // Set fake (unused) RX descriptor address + } + else + { + SLCTXL |= (uint32)&rx->slc_items[0] << SLCTXLA; // Set real RX address + } + if (!tx) + { + SLCRXL |= (uint32)&rx->slc_items[1] << SLCRXLA; // Set fake (unused) TX descriptor address + } + else + { + SLCRXL |= (uint32)&tx->slc_items[0] << SLCRXLA; // Set real TX address + } + + ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL); + SLCIE = (tx ? SLCIRXEOF : 0) | (rx ? SLCITXEOF : 0); // Enable appropriate EOF IRQ + + ETS_SLC_INTR_ENABLE(); + + // Start transmission ("TX" DMA always needed to be enabled) + SLCTXL |= SLCTXLS; + if (tx) + { + SLCRXL |= SLCRXLS; + } + + return true; + } + + static void i2s_slc_end() + { + ETS_SLC_INTR_DISABLE(); + SLCIC = 0xFFFFFFFF; + SLCIE = 0; + SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address + SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address + + for (int x = 0; x < SLC_BUF_CNT; x++) + { + if (tx) + { + free(tx->slc_buf_pntr[x]); + tx->slc_buf_pntr[x] = NULL; + } + if (rx) + { + free(rx->slc_buf_pntr[x]); + rx->slc_buf_pntr[x] = NULL; + } + } + } + + // These routines push a single, 32-bit sample to the I2S buffers. Call at (on average) + // at least the current sample rate. + static bool _i2s_write_sample(uint32_t sample, bool nb) + { + if (!tx) + { + return false; + } + + if (tx->curr_slc_buf_pos == SLC_BUF_LEN || tx->curr_slc_buf == NULL) + { if (tx->slc_queue_len == 0) { - if (nb) { - // if nonblocking just return the number of frames written so far - break; + if (nb) + { + // Don't wait if nonblocking, just notify upper levels + return false; } - else { - while (1) { - if (tx->slc_queue_len > 0) { - break; - } else { - optimistic_yield(10000); - } + while (1) + { + if (tx->slc_queue_len > 0) + { + break; + } + else + { + optimistic_yield(10000); } } } - - // get a new buffer ETS_SLC_INTR_DISABLE(); tx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(tx); ETS_SLC_INTR_ENABLE(); - tx->curr_slc_buf_pos=0; - } + tx->curr_slc_buf_pos = 0; + } + tx->curr_slc_buf[tx->curr_slc_buf_pos++] = sample; + return true; + } - //space available in the current buffer - uint16_t available = SLC_BUF_LEN - tx->curr_slc_buf_pos; + bool i2s_write_sample(uint32_t sample) + { + return _i2s_write_sample(sample, false); + } - uint16_t fc = (available < frame_count) ? available : frame_count; + bool i2s_write_sample_nb(uint32_t sample) + { + return _i2s_write_sample(sample, true); + } - if (mono) { - for(uint16_t i=0;icurr_slc_buf[tx->curr_slc_buf_pos++] = (v << 16) | v; - } + bool i2s_write_lr(int16_t left, int16_t right) + { + int sample = right & 0xFFFF; + sample = sample << 16; + sample |= left & 0xFFFF; + return i2s_write_sample(sample); + } + + // writes a buffer of frames into the DMA memory, returns the amount of frames written + // A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel. + static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mono, bool nb) + { + uint16_t frames_written = 0; + + while (frame_count > 0) + { + + // make sure we have room in the current buffer + if (tx->curr_slc_buf_pos == SLC_BUF_LEN || tx->curr_slc_buf == NULL) + { + // no room in the current buffer? if there are no buffers available then exit + if (tx->slc_queue_len == 0) + { + if (nb) + { + // if nonblocking just return the number of frames written so far + break; + } + else + { + while (1) + { + if (tx->slc_queue_len > 0) + { + break; + } + else + { + optimistic_yield(10000); + } + } + } + } + + // get a new buffer + ETS_SLC_INTR_DISABLE(); + tx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(tx); + ETS_SLC_INTR_ENABLE(); + tx->curr_slc_buf_pos = 0; + } + + //space available in the current buffer + uint16_t available = SLC_BUF_LEN - tx->curr_slc_buf_pos; + + uint16_t fc = (available < frame_count) ? available : frame_count; + + if (mono) + { + for (uint16_t i = 0; i < fc; i++) + { + uint16_t v = (uint16_t)(*frames++); + tx->curr_slc_buf[tx->curr_slc_buf_pos++] = (v << 16) | v; + } + } + else + { + for (uint16_t i = 0; i < fc; i++) + { + uint16_t v1 = (uint16_t)(*frames++); + uint16_t v2 = (uint16_t)(*frames++); + tx->curr_slc_buf[tx->curr_slc_buf_pos++] = (v1 << 16) | v2; + } + } + + frame_count -= fc; + frames_written += fc; } - else - { - for(uint16_t i=0;icurr_slc_buf[tx->curr_slc_buf_pos++] = (v1 << 16) | v2; + return frames_written; + } + + uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count) + { + return _i2s_write_buffer(frames, frame_count, true, true); + } + + uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count) + { + return _i2s_write_buffer(frames, frame_count, true, false); + } + + uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count) + { + return _i2s_write_buffer(frames, frame_count, false, true); + } + + uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count) + { + return _i2s_write_buffer(frames, frame_count, false, false); + } + + bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking) + { + if (!rx) + { + return false; + } + if (rx->curr_slc_buf_pos == SLC_BUF_LEN || rx->curr_slc_buf == NULL) + { + if (rx->slc_queue_len == 0) + { + if (!blocking) + { + return false; + } + while (1) + { + if (rx->slc_queue_len > 0) + { + break; + } + else + { + optimistic_yield(10000); + } + } + } + ETS_SLC_INTR_DISABLE(); + rx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(rx); + ETS_SLC_INTR_ENABLE(); + rx->curr_slc_buf_pos = 0; + } + + uint32_t sample = rx->curr_slc_buf[rx->curr_slc_buf_pos++]; + if (left) + { + *left = sample & 0xffff; + } + if (right) + { + *right = sample >> 16; + } + + return true; + } + + + void i2s_set_rate(uint32_t rate) //Rate in HZ + { + if (rate == _i2s_sample_rate) + { + return; + } + _i2s_sample_rate = rate; + + uint32_t scaled_base_freq = I2SBASEFREQ / 32; + float delta_best = scaled_base_freq; + + uint8_t sbd_div_best = 1; + uint8_t scd_div_best = 1; + for (uint8_t i = 1; i < 64; i++) + { + for (uint8_t j = i; j < 64; j++) + { + float new_delta = fabs(((float)scaled_base_freq / i / j) - rate); + if (new_delta < delta_best) + { + delta_best = new_delta; + sbd_div_best = i; + scd_div_best = j; + } + } + } + + i2s_set_dividers(sbd_div_best, scd_div_best); + } + + void i2s_set_dividers(uint8_t div1, uint8_t div2) + { + // Ensure dividers fit in bit fields + div1 &= I2SBDM; + div2 &= I2SCDM; + + // trans master(active low), recv master(active_low), !bits mod(==16 bits/chanel), clear clock dividers + I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD)); + + // I2SRF = Send/recv right channel first (? may be swapped form I2S spec of WS=0 => left) + // I2SMR = MSB recv/xmit first + // I2SRMS, I2STMS = 1-bit delay from WS to MSB (I2S format) + // div1, div2 = Set I2S WS clock frequency. BCLK seems to be generated from 32x this + I2SC |= I2SRF | I2SMR | I2SRMS | I2STMS | (div1 << I2SBD) | (div2 << I2SCD); + } + + float i2s_get_real_rate() + { + return (float)I2SBASEFREQ / 32 / ((I2SC >> I2SBD) & I2SBDM) / ((I2SC >> I2SCD) & I2SCDM); + } + + bool i2s_rxtx_begin(bool enableRx, bool enableTx) + { + if (tx || rx) + { + i2s_end(); // Stop and free any ongoing stuff + } + + if (enableTx) + { + tx = (i2s_state_t*)calloc(1, sizeof(*tx)); + if (!tx) + { + // Nothing to clean up yet + return false; // OOM Error! } - } - - frame_count -= fc; - frames_written += fc; - } - return frames_written; -} - -uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, true); } - -uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, false); } - -uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, true); } - -uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, false); } - -bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking) { - if (!rx) { - return false; - } - if (rx->curr_slc_buf_pos==SLC_BUF_LEN || rx->curr_slc_buf==NULL) { - if (rx->slc_queue_len == 0) { - if (!blocking) { - return false; - } - while (1) { - if (rx->slc_queue_len > 0){ - break; - } else { - optimistic_yield(10000); - } - } - } - ETS_SLC_INTR_DISABLE(); - rx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(rx); - ETS_SLC_INTR_ENABLE(); - rx->curr_slc_buf_pos=0; - } - - uint32_t sample = rx->curr_slc_buf[rx->curr_slc_buf_pos++]; - if (left) { - *left = sample & 0xffff; - } - if (right) { - *right = sample >> 16; - } - - return true; -} - - -void i2s_set_rate(uint32_t rate) { //Rate in HZ - if (rate == _i2s_sample_rate) { - return; - } - _i2s_sample_rate = rate; - - uint32_t scaled_base_freq = I2SBASEFREQ/32; - float delta_best = scaled_base_freq; - - uint8_t sbd_div_best=1; - uint8_t scd_div_best=1; - for (uint8_t i=1; i<64; i++) { - for (uint8_t j=i; j<64; j++) { - float new_delta = fabs(((float)scaled_base_freq/i/j) - rate); - if (new_delta < delta_best){ - delta_best = new_delta; - sbd_div_best = i; - scd_div_best = j; - } - } - } - - i2s_set_dividers( sbd_div_best, scd_div_best ); -} - -void i2s_set_dividers(uint8_t div1, uint8_t div2) { - // Ensure dividers fit in bit fields - div1 &= I2SBDM; - div2 &= I2SCDM; - - // trans master(active low), recv master(active_low), !bits mod(==16 bits/chanel), clear clock dividers - I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD)); - - // I2SRF = Send/recv right channel first (? may be swapped form I2S spec of WS=0 => left) - // I2SMR = MSB recv/xmit first - // I2SRMS, I2STMS = 1-bit delay from WS to MSB (I2S format) - // div1, div2 = Set I2S WS clock frequency. BCLK seems to be generated from 32x this - I2SC |= I2SRF | I2SMR | I2SRMS | I2STMS | (div1 << I2SBD) | (div2 << I2SCD); -} - -float i2s_get_real_rate(){ - return (float)I2SBASEFREQ/32/((I2SC>>I2SBD) & I2SBDM)/((I2SC >> I2SCD) & I2SCDM); -} - -bool i2s_rxtx_begin(bool enableRx, bool enableTx) { - if (tx || rx) { - i2s_end(); // Stop and free any ongoing stuff - } - - if (enableTx) { - tx = (i2s_state_t*)calloc(1, sizeof(*tx)); - if (!tx) { - // Nothing to clean up yet - return false; // OOM Error! - } - pinMode(I2SO_WS, FUNCTION_1); - pinMode(I2SO_DATA, FUNCTION_1); - pinMode(I2SO_BCK, FUNCTION_1); - } - if (enableRx) { - rx = (i2s_state_t*)calloc(1, sizeof(*rx)); - if (!rx) { - i2s_end(); // Clean up any TX or pin changes - return false; // OOM error! - } - pinMode(I2SI_WS, OUTPUT); - pinMode(I2SI_BCK, OUTPUT); - pinMode(I2SI_DATA, INPUT); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_I2SI_DATA); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_I2SI_BCK); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_I2SI_WS); - } - - _i2s_sample_rate = 0; - if (!i2s_slc_begin()) { - // OOM in SLC memory allocations, tear it all down and abort! - i2s_end(); - return false; - } - - I2S_CLK_ENABLE(); - I2SIC = 0x3F; - I2SIE = 0; - - // Reset I2S - I2SC &= ~(I2SRST); - I2SC |= I2SRST; - I2SC &= ~(I2SRST); - - // I2STXFMM, I2SRXFMM=0 => 16-bit, dual channel data shifted in/out - I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); // Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only) - I2SFC |= I2SDE; // Enable DMA - - // I2STXCMM, I2SRXCMM=0 => Dual channel mode - I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM)); // Set RX/TX CHAN_MOD=0 - - i2s_set_rate(44100); - - if (rx) { - // Need to prime the # of samples to receive in the engine - I2SRXEN = SLC_BUF_LEN; - } - - I2SC |= (rx?I2SRXS:0) | (tx?I2STXS:0); // Start transmission/reception - - return true; -} - -void i2s_begin() { - i2s_rxtx_begin(false, true); -} - -void i2s_end() { - // Disable any I2S send or receive - // ? Maybe not needed since we're resetting on the next line... - I2SC &= ~(I2STXS | I2SRXS); - - // Reset I2S - I2SC &= ~(I2SRST); - I2SC |= I2SRST; - I2SC &= ~(I2SRST); - - i2s_slc_end(); - - if (tx) { - pinMode(I2SO_DATA, INPUT); - pinMode(I2SO_BCK, INPUT); - pinMode(I2SO_WS, INPUT); - free(tx); - tx = NULL; - } - if (rx) { - pinMode(I2SI_DATA, INPUT); - pinMode(I2SI_BCK, INPUT); - pinMode(I2SI_WS, INPUT); - free(rx); - rx = NULL; - } -} + pinMode(I2SO_WS, FUNCTION_1); + pinMode(I2SO_DATA, FUNCTION_1); + pinMode(I2SO_BCK, FUNCTION_1); + } + if (enableRx) + { + rx = (i2s_state_t*)calloc(1, sizeof(*rx)); + if (!rx) + { + i2s_end(); // Clean up any TX or pin changes + return false; // OOM error! + } + pinMode(I2SI_WS, OUTPUT); + pinMode(I2SI_BCK, OUTPUT); + pinMode(I2SI_DATA, INPUT); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_I2SI_DATA); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_I2SI_BCK); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_I2SI_WS); + } + + _i2s_sample_rate = 0; + if (!i2s_slc_begin()) + { + // OOM in SLC memory allocations, tear it all down and abort! + i2s_end(); + return false; + } + + I2S_CLK_ENABLE(); + I2SIC = 0x3F; + I2SIE = 0; + + // Reset I2S + I2SC &= ~(I2SRST); + I2SC |= I2SRST; + I2SC &= ~(I2SRST); + + // I2STXFMM, I2SRXFMM=0 => 16-bit, dual channel data shifted in/out + I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); // Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only) + I2SFC |= I2SDE; // Enable DMA + + // I2STXCMM, I2SRXCMM=0 => Dual channel mode + I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM)); // Set RX/TX CHAN_MOD=0 + + i2s_set_rate(44100); + + if (rx) + { + // Need to prime the # of samples to receive in the engine + I2SRXEN = SLC_BUF_LEN; + } + + I2SC |= (rx ? I2SRXS : 0) | (tx ? I2STXS : 0); // Start transmission/reception + + return true; + } + + void i2s_begin() + { + i2s_rxtx_begin(false, true); + } + + void i2s_end() + { + // Disable any I2S send or receive + // ? Maybe not needed since we're resetting on the next line... + I2SC &= ~(I2STXS | I2SRXS); + + // Reset I2S + I2SC &= ~(I2SRST); + I2SC |= I2SRST; + I2SC &= ~(I2SRST); + + i2s_slc_end(); + + if (tx) + { + pinMode(I2SO_DATA, INPUT); + pinMode(I2SO_BCK, INPUT); + pinMode(I2SO_WS, INPUT); + free(tx); + tx = NULL; + } + if (rx) + { + pinMode(I2SI_DATA, INPUT); + pinMode(I2SI_BCK, INPUT); + pinMode(I2SI_WS, INPUT); + free(rx); + rx = NULL; + } + } }; diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 5d0fb54e65..1edf646b2b 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -1,24 +1,24 @@ /* - main.cpp - platform initialization and context switching - emulation + main.cpp - platform initialization and context switching + emulation - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ //This may be used to change user task stack size: //#define CONT_STACKSIZE 4096 @@ -48,10 +48,10 @@ extern void (*__init_array_end)(void); /* Not static, used in Esp.cpp */ struct rst_info resetInfo; -/* Not static, used in core_esp8266_postmortem.c and other places. - * Placed into noinit section because we assign value to this variable - * before .bss is zero-filled, and need to preserve the value. - */ +/* Not static, used in core_esp8266_postmortem.c and other places. + Placed into noinit section because we assign value to this variable + before .bss is zero-filled, and need to preserve the value. +*/ cont_t* g_pcont __attribute__((section(".noinit"))); /* Event queue used by the main (arduino) task */ @@ -62,21 +62,23 @@ static uint32_t s_micros_at_task_start; extern "C" { -extern const uint32_t __attribute__((section(".ver_number"))) core_version = ARDUINO_ESP8266_GIT_VER; -const char* core_release = + extern const uint32_t __attribute__((section(".ver_number"))) core_version = ARDUINO_ESP8266_GIT_VER; + const char* core_release = #ifdef ARDUINO_ESP8266_RELEASE - ARDUINO_ESP8266_RELEASE; + ARDUINO_ESP8266_RELEASE; #else - NULL; + NULL; #endif } // extern "C" void initVariant() __attribute__((weak)); -void initVariant() { +void initVariant() +{ } void preloop_update_frequency() __attribute__((weak)); -void preloop_update_frequency() { +void preloop_update_frequency() +{ #if defined(F_CPU) && (F_CPU == 160000000L) REG_SET_BIT(0x3ff00014, BIT(0)); ets_update_cpu_frequency(160); @@ -84,40 +86,49 @@ void preloop_update_frequency() { } -extern "C" void esp_yield() { - if (cont_can_yield(g_pcont)) { +extern "C" void esp_yield() +{ + if (cont_can_yield(g_pcont)) + { cont_yield(g_pcont); } } -extern "C" void esp_schedule() { +extern "C" void esp_schedule() +{ ets_post(LOOP_TASK_PRIORITY, 0, 0); } -extern "C" void __yield() { - if (cont_can_yield(g_pcont)) { +extern "C" void __yield() +{ + if (cont_can_yield(g_pcont)) + { esp_schedule(); esp_yield(); } - else { + else + { panic(); } } -extern "C" void yield(void) __attribute__ ((weak, alias("__yield"))); +extern "C" void yield(void) __attribute__((weak, alias("__yield"))); -extern "C" void optimistic_yield(uint32_t interval_us) { +extern "C" void optimistic_yield(uint32_t interval_us) +{ if (cont_can_yield(g_pcont) && - (system_get_time() - s_micros_at_task_start) > interval_us) + (system_get_time() - s_micros_at_task_start) > interval_us) { yield(); } } -static void loop_wrapper() { +static void loop_wrapper() +{ static bool setup_done = false; preloop_update_frequency(); - if(!setup_done) { + if (!setup_done) + { setup(); setup_done = true; } @@ -126,56 +137,72 @@ static void loop_wrapper() { esp_schedule(); } -static void loop_task(os_event_t *events) { +static void loop_task(os_event_t *events) +{ (void) events; s_micros_at_task_start = system_get_time(); cont_run(g_pcont, &loop_wrapper); - if (cont_check(g_pcont) != 0) { + if (cont_check(g_pcont) != 0) + { panic(); } } extern "C" { -struct object { long placeholder[ 10 ]; }; -void __register_frame_info (const void *begin, struct object *ob); -extern char __eh_frame[]; + struct object + { + long placeholder[ 10 ]; + }; + void __register_frame_info(const void *begin, struct object *ob); + extern char __eh_frame[]; } -static void do_global_ctors(void) { +static void do_global_ctors(void) +{ static struct object ob; - __register_frame_info( __eh_frame, &ob ); + __register_frame_info(__eh_frame, &ob); void (**p)(void) = &__init_array_end; while (p != &__init_array_start) + { (*--p)(); + } } extern "C" { -extern void __unhandled_exception(const char *str); + extern void __unhandled_exception(const char *str); -static void __unhandled_exception_cpp() -{ + static void __unhandled_exception_cpp() + { #ifndef __EXCEPTIONS - abort(); -#else - static bool terminating; - if (terminating) abort(); - terminating = true; - /* Use a trick from vterminate.cc to get any std::exception what() */ - try { - __throw_exception_again; - } catch (const std::exception& e) { - __unhandled_exception( e.what() ); - } catch (...) { - __unhandled_exception( "" ); - } +#else + static bool terminating; + if (terminating) + { + abort(); + } + terminating = true; + /* Use a trick from vterminate.cc to get any std::exception what() */ + try + { + __throw_exception_again; + } + catch (const std::exception& e) + { + __unhandled_exception(e.what()); + } + catch (...) + { + __unhandled_exception(""); + } #endif -} + } } -void init_done() { +void init_done() +{ system_set_os_print(1); gdb_init(); std::set_terminate(__unhandled_exception_cpp); @@ -183,56 +210,56 @@ void init_done() { esp_schedule(); } -/* This is the entry point of the application. - * It gets called on the default stack, which grows down from the top - * of DRAM area. - * .bss has not been zeroed out yet, but .data and .rodata are in place. - * Cache is not enabled, so only ROM and IRAM functions can be called. - * Peripherals (except for SPI0 and UART0) are not initialized. - * This function does not return. - */ +/* This is the entry point of the application. + It gets called on the default stack, which grows down from the top + of DRAM area. + .bss has not been zeroed out yet, but .data and .rodata are in place. + Cache is not enabled, so only ROM and IRAM functions can be called. + Peripherals (except for SPI0 and UART0) are not initialized. + This function does not return. +*/ /* - A bit of explanation for this entry point: + A bit of explanation for this entry point: - SYS is the SDK task/context used by the upperlying system to run its - administrative tasks (at least WLAN and lwip's receive callbacks and - Ticker). NONOS-SDK is designed to run user's non-threaded code in - another specific task/context with its own stack in BSS. + SYS is the SDK task/context used by the upperlying system to run its + administrative tasks (at least WLAN and lwip's receive callbacks and + Ticker). NONOS-SDK is designed to run user's non-threaded code in + another specific task/context with its own stack in BSS. - Some clever fellows found that the SYS stack was a large and quite unused - piece of ram that we could use for the user's stack instead of using user's - main memory, thus saving around 4KB on ram/heap. + Some clever fellows found that the SYS stack was a large and quite unused + piece of ram that we could use for the user's stack instead of using user's + main memory, thus saving around 4KB on ram/heap. - A problem arose later, which is that this stack can heavily be used by - the SDK for some features. One of these features is WPS. We still don't - know if other features are using this, or if this memory is going to be - used in future SDK releases. + A problem arose later, which is that this stack can heavily be used by + the SDK for some features. One of these features is WPS. We still don't + know if other features are using this, or if this memory is going to be + used in future SDK releases. - WPS beeing flawed by its poor security, or not beeing used by lots of - users, it has been decided that we are still going to use that memory for - user's stack and disable the use of WPS. + WPS beeing flawed by its poor security, or not beeing used by lots of + users, it has been decided that we are still going to use that memory for + user's stack and disable the use of WPS. - app_entry() jumps to app_entry_custom() defined as "weakref" calling - itself a weak customizable function, allowing to use another one when - this is required (see core_esp8266_app_entry_noextra4k.cpp, used by WPS). + app_entry() jumps to app_entry_custom() defined as "weakref" calling + itself a weak customizable function, allowing to use another one when + this is required (see core_esp8266_app_entry_noextra4k.cpp, used by WPS). - (note: setting app_entry() itself as "weak" is not sufficient and always + (note: setting app_entry() itself as "weak" is not sufficient and always ends up with the other "noextra4k" one linked, maybe because it has a default ENTRY(app_entry) value in linker scripts). - References: - https://github.com/esp8266/Arduino/pull/4553 - https://github.com/esp8266/Arduino/pull/4622 - https://github.com/esp8266/Arduino/issues/4779 - https://github.com/esp8266/Arduino/pull/4889 + References: + https://github.com/esp8266/Arduino/pull/4553 + https://github.com/esp8266/Arduino/pull/4622 + https://github.com/esp8266/Arduino/issues/4779 + https://github.com/esp8266/Arduino/pull/4889 */ extern "C" void ICACHE_RAM_ATTR app_entry_redefinable(void) __attribute__((weak)); extern "C" void ICACHE_RAM_ATTR app_entry_redefinable(void) { - /* Allocate continuation context on this SYS stack, - and save pointer to it. */ + /* Allocate continuation context on this SYS stack, + and save pointer to it. */ cont_t s_cont __attribute__((aligned(16))); g_pcont = &s_cont; @@ -240,20 +267,21 @@ extern "C" void ICACHE_RAM_ATTR app_entry_redefinable(void) call_user_start(); } -static void ICACHE_RAM_ATTR app_entry_custom (void) __attribute__((weakref("app_entry_redefinable"))); +static void ICACHE_RAM_ATTR app_entry_custom(void) __attribute__((weakref("app_entry_redefinable"))); -extern "C" void ICACHE_RAM_ATTR app_entry (void) +extern "C" void ICACHE_RAM_ATTR app_entry(void) { return app_entry_custom(); } -extern "C" void preinit (void) __attribute__((weak)); -extern "C" void preinit (void) +extern "C" void preinit(void) __attribute__((weak)); +extern "C" void preinit(void) { /* do nothing by default */ } -extern "C" void user_init(void) { +extern "C" void user_init(void) +{ struct rst_info *rtc_info_ptr = system_get_rst_info(); memcpy((void *) &resetInfo, (void *) rtc_info_ptr, sizeof(resetInfo)); @@ -268,8 +296,8 @@ extern "C" void user_init(void) { preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable. ets_task(loop_task, - LOOP_TASK_PRIORITY, s_loop_queue, - LOOP_QUEUE_SIZE); + LOOP_TASK_PRIORITY, s_loop_queue, + LOOP_QUEUE_SIZE); system_init_done_cb(&init_done); } diff --git a/cores/esp8266/core_esp8266_noniso.cpp b/cores/esp8266/core_esp8266_noniso.cpp index b39edbb288..fb739f7888 100644 --- a/cores/esp8266/core_esp8266_noniso.cpp +++ b/cores/esp8266/core_esp8266_noniso.cpp @@ -1,26 +1,26 @@ /* - core_esp8266_noniso.c - nonstandard (but usefull) conversion functions + core_esp8266_noniso.c - nonstandard (but usefull) conversion functions - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified 03 April 2015 by Markus Sattler + Modified 03 April 2015 by Markus Sattler - */ +*/ #include #include @@ -31,85 +31,104 @@ extern "C" { -char* ltoa(long value, char* result, int base) { - return itoa((int)value, result, base); -} + char* ltoa(long value, char* result, int base) + { + return itoa((int)value, result, base); + } -char* ultoa(unsigned long value, char* result, int base) { - return utoa((unsigned int)value, result, base); -} + char* ultoa(unsigned long value, char* result, int base) + { + return utoa((unsigned int)value, result, base); + } -char * dtostrf(double number, signed char width, unsigned char prec, char *s) { - bool negative = false; + char * dtostrf(double number, signed char width, unsigned char prec, char *s) + { + bool negative = false; - if (isnan(number)) { - strcpy(s, "nan"); - return s; - } - if (isinf(number)) { - strcpy(s, "inf"); - return s; - } + if (isnan(number)) + { + strcpy(s, "nan"); + return s; + } + if (isinf(number)) + { + strcpy(s, "inf"); + return s; + } - char* out = s; + char* out = s; - int fillme = width; // how many cells to fill for the integer part - if (prec > 0) { - fillme -= (prec+1); - } + int fillme = width; // how many cells to fill for the integer part + if (prec > 0) + { + fillme -= (prec + 1); + } - // Handle negative numbers - if (number < 0.0) { - negative = true; - fillme--; - number = -number; - } + // Handle negative numbers + if (number < 0.0) + { + negative = true; + fillme--; + number = -number; + } - // Round correctly so that print(1.999, 2) prints as "2.00" - // I optimized out most of the divisions - double rounding = 2.0; - for (uint8_t i = 0; i < prec; ++i) - rounding *= 10.0; - rounding = 1.0 / rounding; - - number += rounding; - - // Figure out how big our number really is - double tenpow = 1.0; - int digitcount = 1; - while (number >= 10.0 * tenpow) { - tenpow *= 10.0; - digitcount++; - } + // Round correctly so that print(1.999, 2) prints as "2.00" + // I optimized out most of the divisions + double rounding = 2.0; + for (uint8_t i = 0; i < prec; ++i) + { + rounding *= 10.0; + } + rounding = 1.0 / rounding; - number /= tenpow; - fillme -= digitcount; + number += rounding; - // Pad unused cells with spaces - while (fillme-- > 0) { - *out++ = ' '; - } + // Figure out how big our number really is + double tenpow = 1.0; + int digitcount = 1; + while (number >= 10.0 * tenpow) + { + tenpow *= 10.0; + digitcount++; + } + + number /= tenpow; + fillme -= digitcount; - // Handle negative sign - if (negative) *out++ = '-'; - - // Print the digits, and if necessary, the decimal point - digitcount += prec; - int8_t digit = 0; - while (digitcount-- > 0) { - digit = (int8_t)number; - if (digit > 9) digit = 9; // insurance - *out++ = (char)('0' | digit); - if ((digitcount == prec) && (prec > 0)) { - *out++ = '.'; + // Pad unused cells with spaces + while (fillme-- > 0) + { + *out++ = ' '; } - number -= digit; - number *= 10.0; - } - // make sure the string is terminated - *out = 0; - return s; -} + // Handle negative sign + if (negative) + { + *out++ = '-'; + } + + // Print the digits, and if necessary, the decimal point + digitcount += prec; + int8_t digit = 0; + while (digitcount-- > 0) + { + digit = (int8_t)number; + if (digit > 9) + { + digit = 9; // insurance + } + *out++ = (char)('0' | digit); + if ((digitcount == prec) && (prec > 0)) + { + *out++ = '.'; + } + number -= digit; + number *= 10.0; + } + + // make sure the string is terminated + *out = 0; + return s; + } }; diff --git a/cores/esp8266/core_esp8266_phy.cpp b/cores/esp8266/core_esp8266_phy.cpp index 3fb027f4fb..a37934fb21 100644 --- a/cores/esp8266/core_esp8266_phy.cpp +++ b/cores/esp8266/core_esp8266_phy.cpp @@ -1,23 +1,23 @@ /* - phy.c - ESP8266 PHY initialization data + phy.c - ESP8266 PHY initialization data - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include #include @@ -30,330 +30,332 @@ extern "C" { -static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] = -{ - /*[0] =*/ 5, // Reserved, do not change - /*[1] =*/ 8, // Reserved, do not change - /*[2] =*/ 4, // Reserved, do not change - /*[3] =*/ 2, // Reserved, do not change - /*[4] =*/ 5, // Reserved, do not change - /*[5] =*/ 5, // Reserved, do not change - /*[6] =*/ 5, // Reserved, do not change - /*[7] =*/ 2, // Reserved, do not change - /*[8] =*/ 5, // Reserved, do not change - /*[9] =*/ 0, // Reserved, do not change - /*[10] =*/ 4, // Reserved, do not change - /*[11] =*/ 5, // Reserved, do not change - /*[12] =*/ 5, // Reserved, do not change - /*[13] =*/ 4, // Reserved, do not change - /*[14] =*/ 5, // Reserved, do not change - /*[15] =*/ 5, // Reserved, do not change - /*[16] =*/ 4, // Reserved, do not change - /*[17] =*/ (uint8_t)-2, // Reserved, do not change - /*[18] =*/ (uint8_t)-3, // Reserved, do not change - /*[19] =*/ (uint8_t)-1, // Reserved, do not change - /*[20] =*/ (uint8_t)-16, // Reserved, do not change - /*[21] =*/ (uint8_t)-16, // Reserved, do not change - /*[22] =*/ (uint8_t)-16, // Reserved, do not change - /*[23] =*/ (uint8_t)-32, // Reserved, do not change - /*[24] =*/ (uint8_t)-32, // Reserved, do not change - /*[25] =*/ (uint8_t)-32, // Reserved, do not change - - /*[26] =*/ 225, // spur_freq_cfg, spur_freq=spur_freq_cfg/spur_freq_cfg_div - /*[27] =*/ 10, // spur_freq_cfg_div - // each bit for 1 channel, 1 to select the spur_freq if in band, else 40 - /*[28] =*/ 0xff, // spur_freq_en_h - /*[29] =*/ 0xff, // spur_freq_en_l - - /*[30] =*/ 0xf8, // Reserved, do not change - /*[31] =*/ 0, // Reserved, do not change - /*[32] =*/ 0xf8, // Reserved, do not change - /*[33] =*/ 0xf8, // Reserved, do not change - - /*[34] =*/ 78, // target_power_qdb_0, target power is 78/4=19.5dbm - /*[35] =*/ 74, // target_power_qdb_1, target power is 74/4=18.5dbm - /*[36] =*/ 70, // target_power_qdb_2, target power is 70/4=17.5dbm - /*[37] =*/ 64, // target_power_qdb_3, target power is 64/4=16dbm - /*[38] =*/ 60, // target_power_qdb_4, target power is 60/4=15dbm - /*[39] =*/ 56, // target_power_qdb_5, target power is 56/4=14dbm - - /*[40] =*/ 0, // target_power_index_mcs0 - /*[41] =*/ 0, // target_power_index_mcs1 - /*[42] =*/ 1, // target_power_index_mcs2 - /*[43] =*/ 1, // target_power_index_mcs3 - /*[44] =*/ 2, // target_power_index_mcs4 - /*[45] =*/ 3, // target_power_index_mcs5 - /*[46] =*/ 4, // target_power_index_mcs6 - /*[47] =*/ 5, // target_power_index_mcs7 - - // crystal_26m_en - // 0: 40MHz - // 1: 26MHz - // 2: 24MHz - #if F_CRYSTAL == 40000000 - /*[48] =*/ 0, - #else - /*[48] =*/ 1, - #endif - - /*[49] =*/ 0, - - // sdio_configure - // 0: Auto by pin strapping - // 1: SDIO dataoutput is at negative edges (SDIO V1.1) - // 2: SDIO dataoutput is at positive edges (SDIO V2.0) - /*[50] =*/ 0, - - // bt_configure - // 0: None,no bluetooth - // 1: GPIO0 -> WLAN_ACTIVE/ANT_SEL_WIFI - // MTMS -> BT_ACTIVE - // MTCK -> BT_PRIORITY - // U0RXD -> ANT_SEL_BT - // 2: None, have bluetooth - // 3: GPIO0 -> WLAN_ACTIVE/ANT_SEL_WIFI - // MTMS -> BT_PRIORITY - // MTCK -> BT_ACTIVE - // U0RXD -> ANT_SEL_BT - /*[51] =*/ 0, - - // bt_protocol - // 0: WiFi-BT are not enabled. Antenna is for WiFi - // 1: WiFi-BT are not enabled. Antenna is for BT - // 2: WiFi-BT 2-wire are enabled, (only use BT_ACTIVE), independent ant - // 3: WiFi-BT 3-wire are enabled, (when BT_ACTIVE = 0, BT_PRIORITY must be 0), independent ant - // 4: WiFi-BT 2-wire are enabled, (only use BT_ACTIVE), share ant - // 5: WiFi-BT 3-wire are enabled, (when BT_ACTIVE = 0, BT_PRIORITY must be 0), share ant - /*[52] =*/ 0, - - // dual_ant_configure - // 0: None - // 1: dual_ant (antenna diversity for WiFi-only): GPIO0 + U0RXD - // 2: T/R switch for External PA/LNA: GPIO0 is high and U0RXD is low during Tx - // 3: T/R switch for External PA/LNA: GPIO0 is low and U0RXD is high during Tx - /*[53] =*/ 0, - - /*[54] =*/ 2, // Reserved, do not change - - // share_xtal - // This option is to share crystal clock for BT - // The state of Crystal during sleeping - // 0: Off - // 1: Forcely On - // 2: Automatically On according to XPD_DCDC - // 3: Automatically On according to GPIO2 - /*[55] =*/ 0, - - /*[56] =*/ 0, - /*[57] =*/ 0, - /*[58] =*/ 0, - /*[59] =*/ 0, - /*[60] =*/ 0, - /*[61] =*/ 0, - /*[62] =*/ 0, - /*[63] =*/ 0, - - /*[64] =*/ 225, // spur_freq_cfg_2, spur_freq_2=spur_freq_cfg_2/spur_freq_cfg_div_2 - /*[65] =*/ 10, // spur_freq_cfg_div_2 - /*[66] =*/ 0, // spur_freq_en_h_2 - /*[67] =*/ 0, // spur_freq_en_l_2 - /*[68] =*/ 0, // spur_freq_cfg_msb - /*[69] =*/ 0, // spur_freq_cfg_2_msb - /*[70] =*/ 0, // spur_freq_cfg_3_low - /*[71] =*/ 0, // spur_freq_cfg_3_high - /*[72] =*/ 0, // spur_freq_cfg_4_low - /*[73] =*/ 0, // spur_freq_cfg_4_high - - /*[74] =*/ 1, // Reserved, do not change - /*[75] =*/ 0x93, // Reserved, do not change - /*[76] =*/ 0x43, // Reserved, do not change - /*[77] =*/ 0x00, // Reserved, do not change - - /*[78] =*/ 0, - /*[79] =*/ 0, - /*[80] =*/ 0, - /*[81] =*/ 0, - /*[82] =*/ 0, - /*[83] =*/ 0, - /*[84] =*/ 0, - /*[85] =*/ 0, - /*[86] =*/ 0, - /*[87] =*/ 0, - /*[88] =*/ 0, - /*[89] =*/ 0, - /*[90] =*/ 0, - /*[91] =*/ 0, - /*[92] =*/ 0, - - // low_power_en - // 0: disable low power mode - // 1: enable low power mode - /*[93] =*/ 0, - - // lp_rf_stg10 - // the attenuation of RF gain stage 0 and 1, - // 0xf: 0db, - // 0xe: -2.5db, - // 0xd: -6db, - // 0x9: -8.5db, - // 0xc: -11.5db, - // 0x8: -14db, - // 0x4: -17.5, - // 0x0: -23 - /*[94] =*/ 0x00, - - - // lp_bb_att_ext - // the attenuation of BB gain, - // 0: 0db, - // 1: -0.25db, - // 2: -0.5db, - // 3: -0.75db, - // 4: -1db, - // 5: -1.25db, - // 6: -1.5db, - // 7: -1.75db, - // 8: -2db - // max valve is 24(-6db) - /*[95] =*/ 0, - - // pwr_ind_11b_en - // 0: 11b power is same as mcs0 and 6m - // 1: enable 11b power different with ofdm - /*[96] =*/ 0, - - // pwr_ind_11b_0 - // 1m, 2m power index [0~5] - /*[97] =*/ 0, - - // pwr_ind_11b_1 - // 5.5m, 11m power index [0~5] - /*[98] =*/ 0, - - /*[99] =*/ 0, - /*[100] =*/ 0, - /*[101] =*/ 0, - /*[102] =*/ 0, - /*[103] =*/ 0, - /*[104] =*/ 0, - /*[105] =*/ 0, - /*[106] =*/ 0, - - // vdd33_const - // the voltage of PA_VDD - // x=0xff: it can measure VDD33, - // 18<=x<=36: use input voltage, - // the value is voltage*10, 33 is 3.3V, 30 is 3.0V, - // x<18 or x>36: default voltage is 3.3V - // - // the value of this byte depend from the TOUT pin usage (1 or 2): - // 1) - // analogRead function (system_adc_read()): - // is only available when wire TOUT pin17 to external circuitry, Input Voltage Range restricted to 0 ~ 1.0V. - // For this function the vdd33_const must be set as real power voltage of VDD3P3 pin 3 and 4 - // The range of operating voltage of ESP8266 is 1.8V~3.6V,the unit of vdd33_const is 0.1V,so effective value range of vdd33_const is [18,36] - // 2) - // getVcc function (system_get_vdd33): - // is only available when TOUT pin17 is suspended (floating), this function measure the power voltage of VDD3P3 pin 3 and 4 - // For this function the vdd33_const must be set to 255 (0xFF). - /*[107] =*/ 33, - - // disable RF calibration for certain number of times - /*[108] =*/ 0, - - /*[109] =*/ 0, - /*[110] =*/ 0, - /*[111] =*/ 0, - - // freq_correct_en - // bit[0]:0->do not correct frequency offset, 1->correct frequency offset. - // bit[1]:0->bbpll is 168M, it can correct + and - frequency offset, 1->bbpll is 160M, it only can correct + frequency offset - // bit[2]:0->auto measure frequency offset and correct it, 1->use 113 byte force_freq_offset to correct frequency offset. - // 0: do not correct frequency offset. - // 1: auto measure frequency offset and correct it, bbpll is 168M, it can correct + and - frequency offset. - // 3: auto measure frequency offset and correct it, bbpll is 160M, it only can correct + frequency offset. - // 5: use 113 byte force_freq_offset to correct frequency offset, bbpll is 168M, it can correct + and - frequency offset. - // 7: use 113 byte force_freq_offset to correct frequency offset, bbpll is 160M , it only can correct + frequency offset. - /*[112] =*/ 0, - - // force_freq_offset - // signed, unit is 8kHz - /*[113] =*/ 0, - - // rf_cal_use_flash - // 0: RF init no RF CAL, using all RF CAL data in flash, it takes about 2ms for RF init - // 1: RF init only do TX power control CAL, others using RF CAL data in flash, it takes about 20ms for RF init - // 2: RF init no RF CAL, using all RF CAL data in flash, it takes about 2ms for RF init (same as 0?!) - // 3: RF init do all RF CAL, it takes about 200ms for RF init - /*[114] =*/ 1 -}; - - -// These functions will be overriden from C++ code. -// Unfortunately, we can't use extern "C" because Arduino preprocessor -// doesn't generate forward declarations for extern "C" functions correctly, -// so we use mangled names here. + static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] = + { + /*[0] =*/ 5, // Reserved, do not change + /*[1] =*/ 8, // Reserved, do not change + /*[2] =*/ 4, // Reserved, do not change + /*[3] =*/ 2, // Reserved, do not change + /*[4] =*/ 5, // Reserved, do not change + /*[5] =*/ 5, // Reserved, do not change + /*[6] =*/ 5, // Reserved, do not change + /*[7] =*/ 2, // Reserved, do not change + /*[8] =*/ 5, // Reserved, do not change + /*[9] =*/ 0, // Reserved, do not change + /*[10] =*/ 4, // Reserved, do not change + /*[11] =*/ 5, // Reserved, do not change + /*[12] =*/ 5, // Reserved, do not change + /*[13] =*/ 4, // Reserved, do not change + /*[14] =*/ 5, // Reserved, do not change + /*[15] =*/ 5, // Reserved, do not change + /*[16] =*/ 4, // Reserved, do not change + /*[17] =*/ (uint8_t) -2, // Reserved, do not change + /*[18] =*/ (uint8_t) -3, // Reserved, do not change + /*[19] =*/ (uint8_t) -1, // Reserved, do not change + /*[20] =*/ (uint8_t) -16, // Reserved, do not change + /*[21] =*/ (uint8_t) -16, // Reserved, do not change + /*[22] =*/ (uint8_t) -16, // Reserved, do not change + /*[23] =*/ (uint8_t) -32, // Reserved, do not change + /*[24] =*/ (uint8_t) -32, // Reserved, do not change + /*[25] =*/ (uint8_t) -32, // Reserved, do not change + + /*[26] =*/ 225, // spur_freq_cfg, spur_freq=spur_freq_cfg/spur_freq_cfg_div + /*[27] =*/ 10, // spur_freq_cfg_div + // each bit for 1 channel, 1 to select the spur_freq if in band, else 40 + /*[28] =*/ 0xff, // spur_freq_en_h + /*[29] =*/ 0xff, // spur_freq_en_l + + /*[30] =*/ 0xf8, // Reserved, do not change + /*[31] =*/ 0, // Reserved, do not change + /*[32] =*/ 0xf8, // Reserved, do not change + /*[33] =*/ 0xf8, // Reserved, do not change + + /*[34] =*/ 78, // target_power_qdb_0, target power is 78/4=19.5dbm + /*[35] =*/ 74, // target_power_qdb_1, target power is 74/4=18.5dbm + /*[36] =*/ 70, // target_power_qdb_2, target power is 70/4=17.5dbm + /*[37] =*/ 64, // target_power_qdb_3, target power is 64/4=16dbm + /*[38] =*/ 60, // target_power_qdb_4, target power is 60/4=15dbm + /*[39] =*/ 56, // target_power_qdb_5, target power is 56/4=14dbm + + /*[40] =*/ 0, // target_power_index_mcs0 + /*[41] =*/ 0, // target_power_index_mcs1 + /*[42] =*/ 1, // target_power_index_mcs2 + /*[43] =*/ 1, // target_power_index_mcs3 + /*[44] =*/ 2, // target_power_index_mcs4 + /*[45] =*/ 3, // target_power_index_mcs5 + /*[46] =*/ 4, // target_power_index_mcs6 + /*[47] =*/ 5, // target_power_index_mcs7 + + // crystal_26m_en + // 0: 40MHz + // 1: 26MHz + // 2: 24MHz +#if F_CRYSTAL == 40000000 + /*[48] =*/ 0, +#else + /*[48] =*/ 1, +#endif + + /*[49] =*/ 0, + + // sdio_configure + // 0: Auto by pin strapping + // 1: SDIO dataoutput is at negative edges (SDIO V1.1) + // 2: SDIO dataoutput is at positive edges (SDIO V2.0) + /*[50] =*/ 0, + + // bt_configure + // 0: None,no bluetooth + // 1: GPIO0 -> WLAN_ACTIVE/ANT_SEL_WIFI + // MTMS -> BT_ACTIVE + // MTCK -> BT_PRIORITY + // U0RXD -> ANT_SEL_BT + // 2: None, have bluetooth + // 3: GPIO0 -> WLAN_ACTIVE/ANT_SEL_WIFI + // MTMS -> BT_PRIORITY + // MTCK -> BT_ACTIVE + // U0RXD -> ANT_SEL_BT + /*[51] =*/ 0, + + // bt_protocol + // 0: WiFi-BT are not enabled. Antenna is for WiFi + // 1: WiFi-BT are not enabled. Antenna is for BT + // 2: WiFi-BT 2-wire are enabled, (only use BT_ACTIVE), independent ant + // 3: WiFi-BT 3-wire are enabled, (when BT_ACTIVE = 0, BT_PRIORITY must be 0), independent ant + // 4: WiFi-BT 2-wire are enabled, (only use BT_ACTIVE), share ant + // 5: WiFi-BT 3-wire are enabled, (when BT_ACTIVE = 0, BT_PRIORITY must be 0), share ant + /*[52] =*/ 0, + + // dual_ant_configure + // 0: None + // 1: dual_ant (antenna diversity for WiFi-only): GPIO0 + U0RXD + // 2: T/R switch for External PA/LNA: GPIO0 is high and U0RXD is low during Tx + // 3: T/R switch for External PA/LNA: GPIO0 is low and U0RXD is high during Tx + /*[53] =*/ 0, + + /*[54] =*/ 2, // Reserved, do not change + + // share_xtal + // This option is to share crystal clock for BT + // The state of Crystal during sleeping + // 0: Off + // 1: Forcely On + // 2: Automatically On according to XPD_DCDC + // 3: Automatically On according to GPIO2 + /*[55] =*/ 0, + + /*[56] =*/ 0, + /*[57] =*/ 0, + /*[58] =*/ 0, + /*[59] =*/ 0, + /*[60] =*/ 0, + /*[61] =*/ 0, + /*[62] =*/ 0, + /*[63] =*/ 0, + + /*[64] =*/ 225, // spur_freq_cfg_2, spur_freq_2=spur_freq_cfg_2/spur_freq_cfg_div_2 + /*[65] =*/ 10, // spur_freq_cfg_div_2 + /*[66] =*/ 0, // spur_freq_en_h_2 + /*[67] =*/ 0, // spur_freq_en_l_2 + /*[68] =*/ 0, // spur_freq_cfg_msb + /*[69] =*/ 0, // spur_freq_cfg_2_msb + /*[70] =*/ 0, // spur_freq_cfg_3_low + /*[71] =*/ 0, // spur_freq_cfg_3_high + /*[72] =*/ 0, // spur_freq_cfg_4_low + /*[73] =*/ 0, // spur_freq_cfg_4_high + + /*[74] =*/ 1, // Reserved, do not change + /*[75] =*/ 0x93, // Reserved, do not change + /*[76] =*/ 0x43, // Reserved, do not change + /*[77] =*/ 0x00, // Reserved, do not change + + /*[78] =*/ 0, + /*[79] =*/ 0, + /*[80] =*/ 0, + /*[81] =*/ 0, + /*[82] =*/ 0, + /*[83] =*/ 0, + /*[84] =*/ 0, + /*[85] =*/ 0, + /*[86] =*/ 0, + /*[87] =*/ 0, + /*[88] =*/ 0, + /*[89] =*/ 0, + /*[90] =*/ 0, + /*[91] =*/ 0, + /*[92] =*/ 0, + + // low_power_en + // 0: disable low power mode + // 1: enable low power mode + /*[93] =*/ 0, + + // lp_rf_stg10 + // the attenuation of RF gain stage 0 and 1, + // 0xf: 0db, + // 0xe: -2.5db, + // 0xd: -6db, + // 0x9: -8.5db, + // 0xc: -11.5db, + // 0x8: -14db, + // 0x4: -17.5, + // 0x0: -23 + /*[94] =*/ 0x00, + + + // lp_bb_att_ext + // the attenuation of BB gain, + // 0: 0db, + // 1: -0.25db, + // 2: -0.5db, + // 3: -0.75db, + // 4: -1db, + // 5: -1.25db, + // 6: -1.5db, + // 7: -1.75db, + // 8: -2db + // max valve is 24(-6db) + /*[95] =*/ 0, + + // pwr_ind_11b_en + // 0: 11b power is same as mcs0 and 6m + // 1: enable 11b power different with ofdm + /*[96] =*/ 0, + + // pwr_ind_11b_0 + // 1m, 2m power index [0~5] + /*[97] =*/ 0, + + // pwr_ind_11b_1 + // 5.5m, 11m power index [0~5] + /*[98] =*/ 0, + + /*[99] =*/ 0, + /*[100] =*/ 0, + /*[101] =*/ 0, + /*[102] =*/ 0, + /*[103] =*/ 0, + /*[104] =*/ 0, + /*[105] =*/ 0, + /*[106] =*/ 0, + + // vdd33_const + // the voltage of PA_VDD + // x=0xff: it can measure VDD33, + // 18<=x<=36: use input voltage, + // the value is voltage*10, 33 is 3.3V, 30 is 3.0V, + // x<18 or x>36: default voltage is 3.3V + // + // the value of this byte depend from the TOUT pin usage (1 or 2): + // 1) + // analogRead function (system_adc_read()): + // is only available when wire TOUT pin17 to external circuitry, Input Voltage Range restricted to 0 ~ 1.0V. + // For this function the vdd33_const must be set as real power voltage of VDD3P3 pin 3 and 4 + // The range of operating voltage of ESP8266 is 1.8V~3.6V,the unit of vdd33_const is 0.1V,so effective value range of vdd33_const is [18,36] + // 2) + // getVcc function (system_get_vdd33): + // is only available when TOUT pin17 is suspended (floating), this function measure the power voltage of VDD3P3 pin 3 and 4 + // For this function the vdd33_const must be set to 255 (0xFF). + /*[107] =*/ 33, + + // disable RF calibration for certain number of times + /*[108] =*/ 0, + + /*[109] =*/ 0, + /*[110] =*/ 0, + /*[111] =*/ 0, + + // freq_correct_en + // bit[0]:0->do not correct frequency offset, 1->correct frequency offset. + // bit[1]:0->bbpll is 168M, it can correct + and - frequency offset, 1->bbpll is 160M, it only can correct + frequency offset + // bit[2]:0->auto measure frequency offset and correct it, 1->use 113 byte force_freq_offset to correct frequency offset. + // 0: do not correct frequency offset. + // 1: auto measure frequency offset and correct it, bbpll is 168M, it can correct + and - frequency offset. + // 3: auto measure frequency offset and correct it, bbpll is 160M, it only can correct + frequency offset. + // 5: use 113 byte force_freq_offset to correct frequency offset, bbpll is 168M, it can correct + and - frequency offset. + // 7: use 113 byte force_freq_offset to correct frequency offset, bbpll is 160M , it only can correct + frequency offset. + /*[112] =*/ 0, + + // force_freq_offset + // signed, unit is 8kHz + /*[113] =*/ 0, + + // rf_cal_use_flash + // 0: RF init no RF CAL, using all RF CAL data in flash, it takes about 2ms for RF init + // 1: RF init only do TX power control CAL, others using RF CAL data in flash, it takes about 20ms for RF init + // 2: RF init no RF CAL, using all RF CAL data in flash, it takes about 2ms for RF init (same as 0?!) + // 3: RF init do all RF CAL, it takes about 200ms for RF init + /*[114] =*/ 1 + }; + + + // These functions will be overriden from C++ code. + // Unfortunately, we can't use extern "C" because Arduino preprocessor + // doesn't generate forward declarations for extern "C" functions correctly, + // so we use mangled names here. #define __get_adc_mode _Z14__get_adc_modev #define __get_rf_mode _Z13__get_rf_modev #define __run_user_rf_pre_init _Z22__run_user_rf_pre_initv -static bool spoof_init_data = false; + static bool spoof_init_data = false; + + extern int __real_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); + extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); + extern int __get_adc_mode(); + + extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size) + { + if (!spoof_init_data || size != 128) + { + return __real_spi_flash_read(addr, dst, size); + } + + memcpy(dst, phy_init_data, sizeof(phy_init_data)); + ((uint8_t*)dst)[107] = __get_adc_mode(); + return 0; + } + + extern int __get_rf_mode(void) __attribute__((weak)); + extern int __get_rf_mode(void) + { + return -1; // mode not set + } -extern int __real_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); -extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); -extern int __get_adc_mode(); + extern int __get_adc_mode(void) __attribute__((weak)); + extern int __get_adc_mode(void) + { + return 33; // default ADC mode + } + + extern void __run_user_rf_pre_init(void) __attribute__((weak)); + extern void __run_user_rf_pre_init(void) + { + return; // default do noting + } -extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size) -{ - if (!spoof_init_data || size != 128) { - return __real_spi_flash_read(addr, dst, size); + uint32_t user_rf_cal_sector_set(void) + { + spoof_init_data = true; + return flashchip->chip_size / SPI_FLASH_SEC_SIZE - 4; } - memcpy(dst, phy_init_data, sizeof(phy_init_data)); - ((uint8_t*)dst)[107] = __get_adc_mode(); - return 0; -} - -extern int __get_rf_mode(void) __attribute__((weak)); -extern int __get_rf_mode(void) -{ - return -1; // mode not set -} - -extern int __get_adc_mode(void) __attribute__((weak)); -extern int __get_adc_mode(void) -{ - return 33; // default ADC mode -} - -extern void __run_user_rf_pre_init(void) __attribute__((weak)); -extern void __run_user_rf_pre_init(void) -{ - return; // default do noting -} - -uint32_t user_rf_cal_sector_set(void) -{ - spoof_init_data = true; - return flashchip->chip_size/SPI_FLASH_SEC_SIZE - 4; -} - -void user_rf_pre_init() -{ - // *((volatile uint32_t*) 0x60000710) = 0; - spoof_init_data = false; - volatile uint32_t* rtc_reg = (volatile uint32_t*) 0x60001000; - rtc_reg[30] = 0; - - system_set_os_print(0); - int rf_mode = __get_rf_mode(); - if (rf_mode >= 0) { - system_phy_set_rfoption(rf_mode); + void user_rf_pre_init() + { + // *((volatile uint32_t*) 0x60000710) = 0; + spoof_init_data = false; + volatile uint32_t* rtc_reg = (volatile uint32_t*) 0x60001000; + rtc_reg[30] = 0; + + system_set_os_print(0); + int rf_mode = __get_rf_mode(); + if (rf_mode >= 0) + { + system_phy_set_rfoption(rf_mode); + } + __run_user_rf_pre_init(); } - __run_user_rf_pre_init(); -} -void ICACHE_RAM_ATTR user_spi_flash_dio_to_qio_pre_init() {} + void ICACHE_RAM_ATTR user_spi_flash_dio_to_qio_pre_init() {} }; diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index f18dcfe53b..8f663db5e0 100644 --- a/cores/esp8266/core_esp8266_postmortem.cpp +++ b/cores/esp8266/core_esp8266_postmortem.cpp @@ -1,22 +1,22 @@ /* - postmortem.c - output of debug info on sketch crash - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + postmortem.c - output of debug info on sketch crash + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include @@ -35,223 +35,253 @@ extern "C" { -extern void __real_system_restart_local(); - -// These will be pointers to PROGMEM const strings -static const char* s_panic_file = 0; -static int s_panic_line = 0; -static const char* s_panic_func = 0; -static const char* s_panic_what = 0; - -static bool s_abort_called = false; -static const char* s_unhandled_exception = NULL; - -void abort() __attribute__((noreturn)); -static void uart_write_char_d(char c); -static void uart0_write_char_d(char c); -static void uart1_write_char_d(char c); -static void print_stack(uint32_t start, uint32_t end); - -// From UMM, the last caller of a malloc/realloc/calloc which failed: -extern void *umm_last_fail_alloc_addr; -extern int umm_last_fail_alloc_size; - -static void raise_exception() __attribute__((noreturn)); - -extern void __custom_crash_callback( struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) { - (void) rst_info; - (void) stack; - (void) stack_end; -} - -extern void custom_crash_callback( struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) __attribute__ ((weak, alias("__custom_crash_callback"))); - - -// Prints need to use our library function to allow for file and function -// to be safely accessed from flash. This function encapsulates snprintf() -// [which by definition will 0-terminate] and dumping to the UART -static void ets_printf_P(const char *str, ...) { - char destStr[160]; - char *c = destStr; - va_list argPtr; - va_start(argPtr, str); - vsnprintf(destStr, sizeof(destStr), str, argPtr); - va_end(argPtr); - while (*c) { - ets_putc(*(c++)); - } -} - -void __wrap_system_restart_local() { - register uint32_t sp asm("a1"); - uint32_t sp_dump = sp; - - if (gdb_present()) { - /* When GDBStub is present, exceptions are handled by GDBStub, - but Soft WDT will still call this function. - Trigger an exception to break into GDB. - TODO: check why gdb_do_break() or asm("break.n 0") do not - break into GDB here. */ - raise_exception(); - } + extern void __real_system_restart_local(); + + // These will be pointers to PROGMEM const strings + static const char* s_panic_file = 0; + static int s_panic_line = 0; + static const char* s_panic_func = 0; + static const char* s_panic_what = 0; + + static bool s_abort_called = false; + static const char* s_unhandled_exception = NULL; + + void abort() __attribute__((noreturn)); + static void uart_write_char_d(char c); + static void uart0_write_char_d(char c); + static void uart1_write_char_d(char c); + static void print_stack(uint32_t start, uint32_t end); + + // From UMM, the last caller of a malloc/realloc/calloc which failed: + extern void *umm_last_fail_alloc_addr; + extern int umm_last_fail_alloc_size; + + static void raise_exception() __attribute__((noreturn)); - struct rst_info rst_info; - memset(&rst_info, 0, sizeof(rst_info)); - system_rtc_mem_read(0, &rst_info, sizeof(rst_info)); - if (rst_info.reason != REASON_SOFT_WDT_RST && - rst_info.reason != REASON_EXCEPTION_RST && - rst_info.reason != REASON_WDT_RST) + extern void __custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end) { - return; + (void) rst_info; + (void) stack; + (void) stack_end; } - // TODO: ets_install_putc1 definition is wrong in ets_sys.h, need cast - ets_install_putc1((void *)&uart_write_char_d); + extern void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end) __attribute__((weak, alias("__custom_crash_callback"))); - if (s_panic_line) { - ets_printf_P(PSTR("\nPanic %S:%d %S"), s_panic_file, s_panic_line, s_panic_func); - if (s_panic_what) { - ets_printf_P(PSTR(": Assertion '%S' failed."), s_panic_what); + + // Prints need to use our library function to allow for file and function + // to be safely accessed from flash. This function encapsulates snprintf() + // [which by definition will 0-terminate] and dumping to the UART + static void ets_printf_P(const char *str, ...) + { + char destStr[160]; + char *c = destStr; + va_list argPtr; + va_start(argPtr, str); + vsnprintf(destStr, sizeof(destStr), str, argPtr); + va_end(argPtr); + while (*c) + { + ets_putc(*(c++)); } - ets_putc('\n'); - } - else if (s_unhandled_exception) { - ets_printf_P(PSTR("\nUnhandled C++ exception: %S\n"), s_unhandled_exception); - } - else if (s_abort_called) { - ets_printf_P(PSTR("\nAbort called\n")); - } - else if (rst_info.reason == REASON_EXCEPTION_RST) { - ets_printf_P(PSTR("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n"), - rst_info.exccause, rst_info.epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc); - } - else if (rst_info.reason == REASON_SOFT_WDT_RST) { - ets_printf_P(PSTR("\nSoft WDT reset\n")); } - uint32_t cont_stack_start = (uint32_t) &(g_pcont->stack); - uint32_t cont_stack_end = (uint32_t) g_pcont->stack_end; - uint32_t stack_end; + void __wrap_system_restart_local() + { + register uint32_t sp asm("a1"); + uint32_t sp_dump = sp; + + if (gdb_present()) + { + /* When GDBStub is present, exceptions are handled by GDBStub, + but Soft WDT will still call this function. + Trigger an exception to break into GDB. + TODO: check why gdb_do_break() or asm("break.n 0") do not + break into GDB here. */ + raise_exception(); + } + + struct rst_info rst_info; + memset(&rst_info, 0, sizeof(rst_info)); + system_rtc_mem_read(0, &rst_info, sizeof(rst_info)); + if (rst_info.reason != REASON_SOFT_WDT_RST && + rst_info.reason != REASON_EXCEPTION_RST && + rst_info.reason != REASON_WDT_RST) + { + return; + } - // amount of stack taken by interrupt or exception handler - // and everything up to __wrap_system_restart_local - // (determined empirically, might break) - uint32_t offset = 0; - if (rst_info.reason == REASON_SOFT_WDT_RST) { - offset = 0x1b0; - } - else if (rst_info.reason == REASON_EXCEPTION_RST) { - offset = 0x1a0; - } - else if (rst_info.reason == REASON_WDT_RST) { - offset = 0x10; - } + // TODO: ets_install_putc1 definition is wrong in ets_sys.h, need cast + ets_install_putc1((void *)&uart_write_char_d); + + if (s_panic_line) + { + ets_printf_P(PSTR("\nPanic %S:%d %S"), s_panic_file, s_panic_line, s_panic_func); + if (s_panic_what) + { + ets_printf_P(PSTR(": Assertion '%S' failed."), s_panic_what); + } + ets_putc('\n'); + } + else if (s_unhandled_exception) + { + ets_printf_P(PSTR("\nUnhandled C++ exception: %S\n"), s_unhandled_exception); + } + else if (s_abort_called) + { + ets_printf_P(PSTR("\nAbort called\n")); + } + else if (rst_info.reason == REASON_EXCEPTION_RST) + { + ets_printf_P(PSTR("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n"), + rst_info.exccause, rst_info.epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc); + } + else if (rst_info.reason == REASON_SOFT_WDT_RST) + { + ets_printf_P(PSTR("\nSoft WDT reset\n")); + } - ets_printf_P(PSTR("\n>>>stack>>>\n")); + uint32_t cont_stack_start = (uint32_t) & (g_pcont->stack); + uint32_t cont_stack_end = (uint32_t) g_pcont->stack_end; + uint32_t stack_end; + + // amount of stack taken by interrupt or exception handler + // and everything up to __wrap_system_restart_local + // (determined empirically, might break) + uint32_t offset = 0; + if (rst_info.reason == REASON_SOFT_WDT_RST) + { + offset = 0x1b0; + } + else if (rst_info.reason == REASON_EXCEPTION_RST) + { + offset = 0x1a0; + } + else if (rst_info.reason == REASON_WDT_RST) + { + offset = 0x10; + } - if (sp_dump > stack_thunk_get_stack_bot() && sp_dump <= stack_thunk_get_stack_top()) { - // BearSSL we dump the BSSL second stack and then reset SP back to the main cont stack - ets_printf_P(PSTR("\nctx: bearssl\nsp: %08x end: %08x offset: %04x\n"), sp_dump, stack_thunk_get_stack_top(), offset); - print_stack(sp_dump + offset, stack_thunk_get_stack_top()); - offset = 0; // No offset needed anymore, the exception info was stored in the bssl stack - sp_dump = stack_thunk_get_cont_sp(); - } + ets_printf_P(PSTR("\n>>>stack>>>\n")); - if (sp_dump > cont_stack_start && sp_dump < cont_stack_end) { - ets_printf_P(PSTR("\nctx: cont\n")); - stack_end = cont_stack_end; - } - else { - ets_printf_P(PSTR("\nctx: sys\n")); - stack_end = 0x3fffffb0; - // it's actually 0x3ffffff0, but the stuff below ets_run - // is likely not really relevant to the crash - } + if (sp_dump > stack_thunk_get_stack_bot() && sp_dump <= stack_thunk_get_stack_top()) + { + // BearSSL we dump the BSSL second stack and then reset SP back to the main cont stack + ets_printf_P(PSTR("\nctx: bearssl\nsp: %08x end: %08x offset: %04x\n"), sp_dump, stack_thunk_get_stack_top(), offset); + print_stack(sp_dump + offset, stack_thunk_get_stack_top()); + offset = 0; // No offset needed anymore, the exception info was stored in the bssl stack + sp_dump = stack_thunk_get_cont_sp(); + } + + if (sp_dump > cont_stack_start && sp_dump < cont_stack_end) + { + ets_printf_P(PSTR("\nctx: cont\n")); + stack_end = cont_stack_end; + } + else + { + ets_printf_P(PSTR("\nctx: sys\n")); + stack_end = 0x3fffffb0; + // it's actually 0x3ffffff0, but the stuff below ets_run + // is likely not really relevant to the crash + } - ets_printf_P(PSTR("sp: %08x end: %08x offset: %04x\n"), sp_dump, stack_end, offset); + ets_printf_P(PSTR("sp: %08x end: %08x offset: %04x\n"), sp_dump, stack_end, offset); - print_stack(sp_dump + offset, stack_end); + print_stack(sp_dump + offset, stack_end); - ets_printf_P(PSTR("<<> USTXC) & 0xff)) { } -static void print_stack(uint32_t start, uint32_t end) { - for (uint32_t pos = start; pos < end; pos += 0x10) { - uint32_t* values = (uint32_t*)(pos); + if (c == '\n') + { + USF(0) = '\r'; + } + USF(0) = c; + } - // rough indicator: stack frames usually have SP saved as the second word - bool looksLikeStackFrame = (values[2] == pos + 0x10); + static void uart1_write_char_d(char c) + { + while (((USS(1) >> USTXC) & 0xff) >= 0x7e) { } - ets_printf_P(PSTR("%08x: %08x %08x %08x %08x %c\n"), - pos, values[0], values[1], values[2], values[3], (looksLikeStackFrame)?'<':' '); + if (c == '\n') + { + USF(1) = '\r'; + } + USF(1) = c; } -} -static void uart_write_char_d(char c) { - uart0_write_char_d(c); - uart1_write_char_d(c); -} + static void raise_exception() + { + __asm__ __volatile__("syscall"); + while (1); // never reached, needed to satisfy "noreturn" attribute + } -static void uart0_write_char_d(char c) { - while (((USS(0) >> USTXC) & 0xff)) { } + void abort() + { + s_abort_called = true; + raise_exception(); + } - if (c == '\n') { - USF(0) = '\r'; + void __unhandled_exception(const char *str) + { + s_unhandled_exception = str; + raise_exception(); } - USF(0) = c; -} -static void uart1_write_char_d(char c) { - while (((USS(1) >> USTXC) & 0xff) >= 0x7e) { } + void __assert_func(const char *file, int line, const char *func, const char *what) + { + s_panic_file = file; + s_panic_line = line; + s_panic_func = func; + s_panic_what = what; + gdb_do_break(); /* if GDB is not present, this is a no-op */ + raise_exception(); + } - if (c == '\n') { - USF(1) = '\r'; + void __panic_func(const char* file, int line, const char* func) + { + s_panic_file = file; + s_panic_line = line; + s_panic_func = func; + s_panic_what = 0; + gdb_do_break(); /* if GDB is not present, this is a no-op */ + raise_exception(); } - USF(1) = c; -} - -static void raise_exception() { - __asm__ __volatile__ ("syscall"); - while (1); // never reached, needed to satisfy "noreturn" attribute -} - -void abort() { - s_abort_called = true; - raise_exception(); -} - -void __unhandled_exception(const char *str) { - s_unhandled_exception = str; - raise_exception(); -} - -void __assert_func(const char *file, int line, const char *func, const char *what) { - s_panic_file = file; - s_panic_line = line; - s_panic_func = func; - s_panic_what = what; - gdb_do_break(); /* if GDB is not present, this is a no-op */ - raise_exception(); -} - -void __panic_func(const char* file, int line, const char* func) { - s_panic_file = file; - s_panic_line = line; - s_panic_func = func; - s_panic_what = 0; - gdb_do_break(); /* if GDB is not present, this is a no-op */ - raise_exception(); -} }; diff --git a/cores/esp8266/core_esp8266_si2c.cpp b/cores/esp8266/core_esp8266_si2c.cpp index 6f6cd58971..22fa47553c 100644 --- a/cores/esp8266/core_esp8266_si2c.cpp +++ b/cores/esp8266/core_esp8266_si2c.cpp @@ -1,23 +1,23 @@ /* - si2c.c - Software I2C library for esp8266 + si2c.c - Software I2C library for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support */ #include "twi.h" #include "pins_arduino.h" @@ -25,23 +25,23 @@ extern "C" { -unsigned int preferred_si2c_clock = 100000; + unsigned int preferred_si2c_clock = 100000; #include "twi_util.h" #include "ets_sys.h" -unsigned char twi_dcount = 18; -static unsigned char twi_sda, twi_scl; -static uint32_t twi_clockStretchLimit; -static unsigned char twi_addr = 0; + unsigned char twi_dcount = 18; + static unsigned char twi_sda, twi_scl; + static uint32_t twi_clockStretchLimit; + static unsigned char twi_addr = 0; -// modes (private) + // modes (private) #define TWIPM_UNKNOWN 0 #define TWIPM_IDLE 1 #define TWIPM_ADDRESSED 2 #define TWIPM_WAIT 3 -// states (private) + // states (private) #define TWIP_UNKNOWN 0 #define TWIP_IDLE 1 #define TWIP_START 2 @@ -59,37 +59,37 @@ static unsigned char twi_addr = 0; #define TWIP_WRITE 14 #define TWIP_BUS_ERR 15 -static volatile uint8_t twip_mode = TWIPM_IDLE; -static volatile uint8_t twip_state = TWIP_IDLE; -static volatile uint8_t twip_status = TW_NO_INFO; -static volatile uint8_t bitCount = 0; + static volatile uint8_t twip_mode = TWIPM_IDLE; + static volatile uint8_t twip_state = TWIP_IDLE; + static volatile uint8_t twip_status = TW_NO_INFO; + static volatile uint8_t bitCount = 0; #define TWDR twi_data -static volatile uint8_t twi_data = 0x00; -static volatile uint8_t twi_ack = 0; -static volatile uint8_t twi_ack_rec = 0; -static volatile int twi_timeout_ms = 10; + static volatile uint8_t twi_data = 0x00; + static volatile uint8_t twi_ack = 0; + static volatile uint8_t twi_ack_rec = 0; + static volatile int twi_timeout_ms = 10; #define TWI_READY 0 #define TWI_MRX 1 #define TWI_MTX 2 #define TWI_SRX 3 #define TWI_STX 4 -static volatile uint8_t twi_state = TWI_READY; -static volatile uint8_t twi_error = 0xFF; + static volatile uint8_t twi_state = TWI_READY; + static volatile uint8_t twi_error = 0xFF; -static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH]; -static volatile uint8_t twi_txBufferIndex; -static volatile uint8_t twi_txBufferLength; + static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH]; + static volatile uint8_t twi_txBufferIndex; + static volatile uint8_t twi_txBufferLength; -static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; -static volatile uint8_t twi_rxBufferIndex; + static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; + static volatile uint8_t twi_rxBufferIndex; -static void (*twi_onSlaveTransmit)(void); -static void (*twi_onSlaveReceive)(uint8_t*, size_t); + static void (*twi_onSlaveTransmit)(void); + static void (*twi_onSlaveReceive)(uint8_t*, size_t); -static void onSclChange(void); -static void onSdaChange(void); + static void onSclChange(void); + static void onSdaChange(void); #define EVENTTASK_QUEUE_SIZE 1 #define EVENTTASK_QUEUE_PRIO 2 @@ -98,10 +98,10 @@ static void onSdaChange(void); #define TWI_SIG_RX (TWI_SIG_RANGE + 0x01) #define TWI_SIG_TX (TWI_SIG_RANGE + 0x02) -static ETSEvent eventTaskQueue[EVENTTASK_QUEUE_SIZE]; -static void eventTask(ETSEvent *e); -static ETSTimer timer; -static void onTimer(void *unused); + static ETSEvent eventTaskQueue[EVENTTASK_QUEUE_SIZE]; + static void eventTask(ETSEvent *e); + static ETSTimer timer; + static void onTimer(void *unused); #define SDA_LOW() (GPES = (1 << twi_sda)) //Enable SDA (becomes output and since GPO is 0 for the pin, it will pull the line low) #define SDA_HIGH() (GPEC = (1 << twi_sda)) //Disable SDA (becomes input and since it has pullup it will go high) @@ -120,670 +120,867 @@ static void onTimer(void *unused); #define TWI_CLOCK_STRETCH_MULTIPLIER 6 #endif -void twi_setClock(unsigned int freq){ - preferred_si2c_clock = freq; + void twi_setClock(unsigned int freq) + { + preferred_si2c_clock = freq; #if F_CPU == FCPU80 - if(freq <= 50000) twi_dcount = 38;//about 50KHz - else if(freq <= 100000) twi_dcount = 19;//about 100KHz - else if(freq <= 200000) twi_dcount = 8;//about 200KHz - else if(freq <= 300000) twi_dcount = 3;//about 300KHz - else if(freq <= 400000) twi_dcount = 1;//about 400KHz - else twi_dcount = 1;//about 400KHz + if (freq <= 50000) + { + twi_dcount = 38; //about 50KHz + } + else if (freq <= 100000) + { + twi_dcount = 19; //about 100KHz + } + else if (freq <= 200000) + { + twi_dcount = 8; //about 200KHz + } + else if (freq <= 300000) + { + twi_dcount = 3; //about 300KHz + } + else if (freq <= 400000) + { + twi_dcount = 1; //about 400KHz + } + else + { + twi_dcount = 1; //about 400KHz + } #else - if(freq <= 50000) twi_dcount = 64;//about 50KHz - else if(freq <= 100000) twi_dcount = 32;//about 100KHz - else if(freq <= 200000) twi_dcount = 14;//about 200KHz - else if(freq <= 300000) twi_dcount = 8;//about 300KHz - else if(freq <= 400000) twi_dcount = 5;//about 400KHz - else if(freq <= 500000) twi_dcount = 3;//about 500KHz - else if(freq <= 600000) twi_dcount = 2;//about 600KHz - else twi_dcount = 1;//about 700KHz + if (freq <= 50000) + { + twi_dcount = 64; //about 50KHz + } + else if (freq <= 100000) + { + twi_dcount = 32; //about 100KHz + } + else if (freq <= 200000) + { + twi_dcount = 14; //about 200KHz + } + else if (freq <= 300000) + { + twi_dcount = 8; //about 300KHz + } + else if (freq <= 400000) + { + twi_dcount = 5; //about 400KHz + } + else if (freq <= 500000) + { + twi_dcount = 3; //about 500KHz + } + else if (freq <= 600000) + { + twi_dcount = 2; //about 600KHz + } + else + { + twi_dcount = 1; //about 700KHz + } #endif -} - -void twi_setClockStretchLimit(uint32_t limit){ - twi_clockStretchLimit = limit * TWI_CLOCK_STRETCH_MULTIPLIER; -} - -void twi_init(unsigned char sda, unsigned char scl) -{ - // set timer function - ets_timer_setfn(&timer, onTimer, NULL); - - // create event task - ets_task(eventTask, EVENTTASK_QUEUE_PRIO, eventTaskQueue, EVENTTASK_QUEUE_SIZE); - - twi_sda = sda; - twi_scl = scl; - pinMode(twi_sda, INPUT_PULLUP); - pinMode(twi_scl, INPUT_PULLUP); - twi_setClock(preferred_si2c_clock); - twi_setClockStretchLimit(230); // default value is 230 uS - - if (twi_addr != 0) - { - attachInterrupt(scl, onSclChange, CHANGE); - attachInterrupt(sda, onSdaChange, CHANGE); - } -} - -void twi_setAddress(uint8_t address) -{ - // set twi slave address (skip over R/W bit) - twi_addr = address << 1; -} - -static void ICACHE_RAM_ATTR twi_delay(unsigned char v){ - unsigned int i; + } + + void twi_setClockStretchLimit(uint32_t limit) + { + twi_clockStretchLimit = limit * TWI_CLOCK_STRETCH_MULTIPLIER; + } + + void twi_init(unsigned char sda, unsigned char scl) + { + // set timer function + ets_timer_setfn(&timer, onTimer, NULL); + + // create event task + ets_task(eventTask, EVENTTASK_QUEUE_PRIO, eventTaskQueue, EVENTTASK_QUEUE_SIZE); + + twi_sda = sda; + twi_scl = scl; + pinMode(twi_sda, INPUT_PULLUP); + pinMode(twi_scl, INPUT_PULLUP); + twi_setClock(preferred_si2c_clock); + twi_setClockStretchLimit(230); // default value is 230 uS + + if (twi_addr != 0) + { + attachInterrupt(scl, onSclChange, CHANGE); + attachInterrupt(sda, onSdaChange, CHANGE); + } + } + + void twi_setAddress(uint8_t address) + { + // set twi slave address (skip over R/W bit) + twi_addr = address << 1; + } + + static void ICACHE_RAM_ATTR twi_delay(unsigned char v) + { + unsigned int i; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-but-set-variable" - unsigned int reg; - for (i = 0; i < v; i++) { - reg = GPI; - } - (void)reg; + unsigned int reg; + for (i = 0; i < v; i++) + { + reg = GPI; + } + (void)reg; #pragma GCC diagnostic pop -} - -static bool twi_write_start(void) { - SCL_HIGH(); - SDA_HIGH(); - if (SDA_READ() == 0) { - return false; - } - twi_delay(twi_dcount); - SDA_LOW(); - twi_delay(twi_dcount); - return true; -} - -static bool twi_write_stop(void){ - uint32_t i = 0; - SCL_LOW(); - SDA_LOW(); - twi_delay(twi_dcount); - SCL_HIGH(); - while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit); // Clock stretching - twi_delay(twi_dcount); - SDA_HIGH(); - twi_delay(twi_dcount); - return true; -} - -static bool twi_write_bit(bool bit) { - uint32_t i = 0; - SCL_LOW(); - if (bit) SDA_HIGH(); - else SDA_LOW(); - twi_delay(twi_dcount+1); - SCL_HIGH(); - while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit);// Clock stretching - twi_delay(twi_dcount); - return true; -} - -static bool twi_read_bit(void) { - uint32_t i = 0; - SCL_LOW(); - SDA_HIGH(); - twi_delay(twi_dcount+2); - SCL_HIGH(); - while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit);// Clock stretching - bool bit = SDA_READ(); - twi_delay(twi_dcount); - return bit; -} - -static bool twi_write_byte(unsigned char byte) { - unsigned char bit; - for (bit = 0; bit < 8; bit++) { - twi_write_bit(byte & 0x80); - byte <<= 1; - } - return !twi_read_bit();//NACK/ACK -} - -static unsigned char twi_read_byte(bool nack) { - unsigned char byte = 0; - unsigned char bit; - for (bit = 0; bit < 8; bit++) byte = (byte << 1) | twi_read_bit(); - twi_write_bit(nack); - return byte; -} - -unsigned char twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop){ - unsigned int i; - if(!twi_write_start()) return 4;//line busy - if(!twi_write_byte(((address << 1) | 0) & 0xFF)) { - if (sendStop) twi_write_stop(); - return 2; //received NACK on transmit of address - } - for(i=0; i 0) { // if SDA low, read the bits slaves have to sent to a max - twi_read_bit(); - if (SCL_READ() == 0) { - return I2C_SCL_HELD_LOW_AFTER_READ; // I2C bus error. SCL held low beyond slave clock stretch time + + static bool twi_write_start(void) + { + SCL_HIGH(); + SDA_HIGH(); + if (SDA_READ() == 0) + { + return false; + } + twi_delay(twi_dcount); + SDA_LOW(); + twi_delay(twi_dcount); + return true; + } + + static bool twi_write_stop(void) + { + uint32_t i = 0; + SCL_LOW(); + SDA_LOW(); + twi_delay(twi_dcount); + SCL_HIGH(); + while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit); // Clock stretching + twi_delay(twi_dcount); + SDA_HIGH(); + twi_delay(twi_dcount); + return true; + } + + static bool twi_write_bit(bool bit) + { + uint32_t i = 0; + SCL_LOW(); + if (bit) + { + SDA_HIGH(); + } + else + { + SDA_LOW(); + } + twi_delay(twi_dcount + 1); + SCL_HIGH(); + while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit);// Clock stretching + twi_delay(twi_dcount); + return true; + } + + static bool twi_read_bit(void) + { + uint32_t i = 0; + SCL_LOW(); + SDA_HIGH(); + twi_delay(twi_dcount + 2); + SCL_HIGH(); + while (SCL_READ() == 0 && (i++) < twi_clockStretchLimit);// Clock stretching + bool bit = SDA_READ(); + twi_delay(twi_dcount); + return bit; + } + + static bool twi_write_byte(unsigned char byte) + { + unsigned char bit; + for (bit = 0; bit < 8; bit++) + { + twi_write_bit(byte & 0x80); + byte <<= 1; + } + return !twi_read_bit();//NACK/ACK + } + + static unsigned char twi_read_byte(bool nack) + { + unsigned char byte = 0; + unsigned char bit; + for (bit = 0; bit < 8; bit++) + { + byte = (byte << 1) | twi_read_bit(); + } + twi_write_bit(nack); + return byte; + } + + unsigned char twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop) + { + unsigned int i; + if (!twi_write_start()) + { + return 4; //line busy + } + if (!twi_write_byte(((address << 1) | 0) & 0xFF)) + { + if (sendStop) + { + twi_write_stop(); + } + return 2; //received NACK on transmit of address + } + for (i = 0; i < len; i++) + { + if (!twi_write_byte(buf[i])) + { + if (sendStop) + { + twi_write_stop(); + } + return 3;//received NACK on transmit of data + } + } + if (sendStop) + { + twi_write_stop(); + } + i = 0; + while (SDA_READ() == 0 && (i++) < 10) + { + SCL_LOW(); + twi_delay(twi_dcount); + SCL_HIGH(); + unsigned int t = 0; while (SCL_READ() == 0 && (t++) < twi_clockStretchLimit); // twi_clockStretchLimit + twi_delay(twi_dcount); + } + return 0; + } + + unsigned char twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop) + { + unsigned int i; + if (!twi_write_start()) + { + return 4; //line busy + } + if (!twi_write_byte(((address << 1) | 1) & 0xFF)) + { + if (sendStop) + { + twi_write_stop(); + } + return 2;//received NACK on transmit of address + } + for (i = 0; i < (len - 1); i++) + { + buf[i] = twi_read_byte(false); + } + buf[len - 1] = twi_read_byte(true); + if (sendStop) + { + twi_write_stop(); + } + i = 0; + while (SDA_READ() == 0 && (i++) < 10) + { + SCL_LOW(); + twi_delay(twi_dcount); + SCL_HIGH(); + unsigned int t = 0; while (SCL_READ() == 0 && (t++) < twi_clockStretchLimit); // twi_clockStretchLimit + twi_delay(twi_dcount); + } + return 0; + } + + uint8_t twi_status() + { + if (SCL_READ() == 0) + { + return I2C_SCL_HELD_LOW; // SCL held low by another device, no procedure available to recover + } + + int clockCount = 20; + while (SDA_READ() == 0 && clockCount-- > 0) // if SDA low, read the bits slaves have to sent to a max + { + twi_read_bit(); + if (SCL_READ() == 0) + { + return I2C_SCL_HELD_LOW_AFTER_READ; // I2C bus error. SCL held low beyond slave clock stretch time + } + } + if (SDA_READ() == 0) + { + return I2C_SDA_HELD_LOW; // I2C bus error. SDA line held low by slave/another_master after n bits. + } + + if (!twi_write_start()) + { + return I2C_SDA_HELD_LOW_AFTER_INIT; // line busy. SDA again held low by another device. 2nd master? + } + + return I2C_OK; + } + + uint8_t twi_transmit(const uint8_t* data, uint8_t length) + { + uint8_t i; + + // ensure data will fit into buffer + if (length > TWI_BUFFER_LENGTH) + { + return 1; + } + + // ensure we are currently a slave transmitter + if (twi_state != TWI_STX) + { + return 2; + } + + // set length and copy data into tx buffer + twi_txBufferLength = length; + for (i = 0; i < length; ++i) + { + twi_txBuffer[i] = data[i]; + } + + return 0; + } + + void twi_attachSlaveRxEvent(void (*function)(uint8_t*, size_t)) + { + twi_onSlaveReceive = function; + } + + void twi_attachSlaveTxEvent(void (*function)(void)) + { + twi_onSlaveTransmit = function; + } + + void ICACHE_RAM_ATTR twi_reply(uint8_t ack) + { + // transmit master read ready signal, with or without ack + if (ack) + { + //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); + SCL_HIGH(); // _BV(TWINT) + twi_ack = 1; // _BV(TWEA) + } + else + { + //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); + SCL_HIGH(); // _BV(TWINT) + twi_ack = 0; // ~_BV(TWEA) + } + } + + void ICACHE_RAM_ATTR twi_stop(void) + { + // send stop condition + //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); + SCL_HIGH(); // _BV(TWINT) + twi_ack = 1; // _BV(TWEA) + twi_delay(5); // Maybe this should be here + SDA_HIGH(); // _BV(TWSTO) + // update twi state + twi_state = TWI_READY; + } + + void ICACHE_RAM_ATTR twi_releaseBus(void) + { + // release bus + //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); + SCL_HIGH(); // _BV(TWINT) + twi_ack = 1; // _BV(TWEA) + SDA_HIGH(); + + // update twi state + twi_state = TWI_READY; + } + + + void ICACHE_RAM_ATTR twi_onTwipEvent(uint8_t status) + { + switch (status) + { + // Slave Receiver + case TW_SR_SLA_ACK: // addressed, returned ack + case TW_SR_GCALL_ACK: // addressed generally, returned ack + case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack + case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack + // enter slave receiver mode + twi_state = TWI_SRX; + // indicate that rx buffer can be overwritten and ack + twi_rxBufferIndex = 0; + twi_reply(1); + break; + case TW_SR_DATA_ACK: // data received, returned ack + case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack + // if there is still room in the rx buffer + if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) + { + // put byte in buffer and ack + twi_rxBuffer[twi_rxBufferIndex++] = TWDR; + twi_reply(1); + } + else + { + // otherwise nack + twi_reply(0); + } + break; + case TW_SR_STOP: // stop or repeated start condition received + // put a null char after data if there's room + if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) + { + twi_rxBuffer[twi_rxBufferIndex] = '\0'; + } + // callback to user-defined callback over event task to allow for non-RAM-residing code + //twi_rxBufferLock = true; // This may be necessary + ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_RX, twi_rxBufferIndex); + + // since we submit rx buffer to "wire" library, we can reset it + twi_rxBufferIndex = 0; + break; + + case TW_SR_DATA_NACK: // data received, returned nack + case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack + // nack back at master + twi_reply(0); + break; + + // Slave Transmitter + case TW_ST_SLA_ACK: // addressed, returned ack + case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack + // enter slave transmitter mode + twi_state = TWI_STX; + // ready the tx buffer index for iteration + twi_txBufferIndex = 0; + // set tx buffer length to be zero, to verify if user changes it + twi_txBufferLength = 0; + // callback to user-defined callback over event task to allow for non-RAM-residing code + // request for txBuffer to be filled and length to be set + // note: user must call twi_transmit(bytes, length) to do this + ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_TX, 0); + break; + + case TW_ST_DATA_ACK: // byte sent, ack returned + // copy data to output register + TWDR = twi_txBuffer[twi_txBufferIndex++]; + + bitCount = 8; + bitCount--; + (twi_data & 0x80) ? SDA_HIGH() : SDA_LOW(); + twi_data <<= 1; + + // if there is more to send, ack, otherwise nack + if (twi_txBufferIndex < twi_txBufferLength) + { + twi_reply(1); + } + else + { + twi_reply(0); + } + break; + case TW_ST_DATA_NACK: // received nack, we are done + case TW_ST_LAST_DATA: // received ack, but we are done already! + // leave slave receiver state + twi_releaseBus(); + break; + + // All + case TW_NO_INFO: // no state information + break; + case TW_BUS_ERROR: // bus error, illegal stop/start + twi_error = TW_BUS_ERROR; + twi_stop(); + break; + } + } + + void ICACHE_RAM_ATTR onTimer(void *unused) + { + (void)unused; + twi_releaseBus(); + twip_status = TW_BUS_ERROR; + twi_onTwipEvent(twip_status); + twip_mode = TWIPM_WAIT; + twip_state = TWIP_BUS_ERR; + } + + static void eventTask(ETSEvent *e) + { + + if (e == NULL) + { + return; + } + + switch (e->sig) + { + case TWI_SIG_TX: + twi_onSlaveTransmit(); + + // if they didn't change buffer & length, initialize it + if (twi_txBufferLength == 0) + { + twi_txBufferLength = 1; + twi_txBuffer[0] = 0x00; + } + + // Initiate transmission + twi_onTwipEvent(TW_ST_DATA_ACK); + + break; + + case TWI_SIG_RX: + // ack future responses and leave slave receiver state + twi_releaseBus(); + twi_onSlaveReceive(twi_rxBuffer, e->par); + break; + } + } + + void ICACHE_RAM_ATTR onSclChange(void) + { + static uint8_t sda; + static uint8_t scl; + + sda = SDA_READ(); + scl = SCL_READ(); + + twip_status = 0xF8; // reset TWI status + + switch (twip_state) + { + case TWIP_IDLE: + case TWIP_WAIT_STOP: + case TWIP_BUS_ERR: + // ignore + break; + + case TWIP_START: + case TWIP_REP_START: + case TWIP_SLA_W: + case TWIP_READ: + if (!scl) + { + // ignore + } + else + { + bitCount--; + twi_data <<= 1; + twi_data |= sda; + + if (bitCount != 0) + { + // continue + } + else + { + twip_state = TWIP_SEND_ACK; + } + } + break; + + case TWIP_SEND_ACK: + if (scl) + { + // ignore + } + else + { + if (twip_mode == TWIPM_IDLE) + { + if ((twi_data & 0xFE) != twi_addr) + { + // ignore + } + else + { + SDA_LOW(); + } + } + else + { + if (!twi_ack) + { + // ignore + } + else + { + SDA_LOW(); + } + } + twip_state = TWIP_WAIT_ACK; + } + break; + + case TWIP_WAIT_ACK: + if (scl) + { + // ignore + } + else + { + if (twip_mode == TWIPM_IDLE) + { + if ((twi_data & 0xFE) != twi_addr) + { + SDA_HIGH(); + twip_state = TWIP_WAIT_STOP; + } + else + { + SCL_LOW(); // clock stretching + SDA_HIGH(); + twip_mode = TWIPM_ADDRESSED; + if (!(twi_data & 0x01)) + { + twip_status = TW_SR_SLA_ACK; + twi_onTwipEvent(twip_status); + bitCount = 8; + twip_state = TWIP_SLA_W; + } + else + { + twip_status = TW_ST_SLA_ACK; + twi_onTwipEvent(twip_status); + twip_state = TWIP_SLA_R; + } + } + } + else + { + SCL_LOW(); // clock stretching + SDA_HIGH(); + if (!twi_ack) + { + twip_status = TW_SR_DATA_NACK; + twi_onTwipEvent(twip_status); + twip_mode = TWIPM_WAIT; + twip_state = TWIP_WAIT_STOP; + } + else + { + twip_status = TW_SR_DATA_ACK; + twi_onTwipEvent(twip_status); + bitCount = 8; + twip_state = TWIP_READ; + } + } + } + break; + + case TWIP_SLA_R: + case TWIP_WRITE: + if (scl) + { + // ignore + } + else + { + bitCount--; + (twi_data & 0x80) ? SDA_HIGH() : SDA_LOW(); + twi_data <<= 1; + + if (bitCount != 0) + { + // continue + } + else + { + twip_state = TWIP_REC_ACK; + } + } + break; + + case TWIP_REC_ACK: + if (scl) + { + // ignore + } + else + { + SDA_HIGH(); + twip_state = TWIP_READ_ACK; + } + break; + + case TWIP_READ_ACK: + if (!scl) + { + // ignore + } + else + { + twi_ack_rec = !sda; + twip_state = TWIP_RWAIT_ACK; + } + break; + + case TWIP_RWAIT_ACK: + if (scl) + { + // ignore + } + else + { + SCL_LOW(); // clock stretching + if (twi_ack && twi_ack_rec) + { + twip_status = TW_ST_DATA_ACK; + twi_onTwipEvent(twip_status); + twip_state = TWIP_WRITE; + } + else + { + // we have no more data to send and/or the master doesn't want anymore + twip_status = twi_ack_rec ? TW_ST_LAST_DATA : TW_ST_DATA_NACK; + twi_onTwipEvent(twip_status); + twip_mode = TWIPM_WAIT; + twip_state = TWIP_WAIT_STOP; + } + } + break; + + default: + break; + } + } + + void ICACHE_RAM_ATTR onSdaChange(void) + { + static uint8_t sda; + static uint8_t scl; + sda = SDA_READ(); + scl = SCL_READ(); + + switch (twip_state) + { + case TWIP_IDLE: + if (!scl) + { + // DATA - ignore + } + else if (sda) + { + // STOP - ignore + } + else + { + // START + bitCount = 8; + twip_state = TWIP_START; + ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms + } + break; + + case TWIP_START: + case TWIP_REP_START: + case TWIP_SEND_ACK: + case TWIP_WAIT_ACK: + case TWIP_SLA_R: + case TWIP_REC_ACK: + case TWIP_READ_ACK: + case TWIP_RWAIT_ACK: + case TWIP_WRITE: + if (!scl) + { + // DATA - ignore + } + else + { + // START or STOP + SDA_HIGH(); // Should not be necessary + twip_status = TW_BUS_ERROR; + twi_onTwipEvent(twip_status); + twip_mode = TWIPM_WAIT; + twip_state = TWIP_BUS_ERR; + } + break; + + case TWIP_WAIT_STOP: + case TWIP_BUS_ERR: + if (!scl) + { + // DATA - ignore + } + else + { + if (sda) + { + // STOP + SCL_LOW(); // clock stretching + ets_timer_disarm(&timer); + twip_state = TWIP_IDLE; + twip_mode = TWIPM_IDLE; + SCL_HIGH(); + } + else + { + // START + if (twip_state == TWIP_BUS_ERR) + { + // ignore + } + else + { + bitCount = 8; + twip_state = TWIP_REP_START; + ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms + } + } + } + break; + + case TWIP_SLA_W: + case TWIP_READ: + if (!scl) + { + // DATA - ignore + } + else + { + // START or STOP + if (bitCount != 7) + { + // inside byte transfer - error + twip_status = TW_BUS_ERROR; + twi_onTwipEvent(twip_status); + twip_mode = TWIPM_WAIT; + twip_state = TWIP_BUS_ERR; + } + else + { + // during first bit in byte transfer - ok + SCL_LOW(); // clock stretching + twip_status = TW_SR_STOP; + twi_onTwipEvent(twip_status); + if (sda) + { + // STOP + ets_timer_disarm(&timer); + twip_state = TWIP_IDLE; + twip_mode = TWIPM_IDLE; + } + else + { + // START + bitCount = 8; + ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms + twip_state = TWIP_REP_START; + twip_mode = TWIPM_IDLE; + } + } + } + break; + + default: + break; } } - if (SDA_READ() == 0) - return I2C_SDA_HELD_LOW; // I2C bus error. SDA line held low by slave/another_master after n bits. - - if (!twi_write_start()) - return I2C_SDA_HELD_LOW_AFTER_INIT; // line busy. SDA again held low by another device. 2nd master? - - return I2C_OK; -} - -uint8_t twi_transmit(const uint8_t* data, uint8_t length) -{ - uint8_t i; - - // ensure data will fit into buffer - if (length > TWI_BUFFER_LENGTH) { - return 1; - } - - // ensure we are currently a slave transmitter - if (twi_state != TWI_STX) { - return 2; - } - - // set length and copy data into tx buffer - twi_txBufferLength = length; - for (i = 0; i < length; ++i) { - twi_txBuffer[i] = data[i]; - } - - return 0; -} - -void twi_attachSlaveRxEvent( void (*function)(uint8_t*, size_t) ) -{ - twi_onSlaveReceive = function; -} - -void twi_attachSlaveTxEvent( void (*function)(void) ) -{ - twi_onSlaveTransmit = function; -} - -void ICACHE_RAM_ATTR twi_reply(uint8_t ack) -{ - // transmit master read ready signal, with or without ack - if (ack) { - //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); - SCL_HIGH(); // _BV(TWINT) - twi_ack = 1; // _BV(TWEA) - } else { - //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); - SCL_HIGH(); // _BV(TWINT) - twi_ack = 0; // ~_BV(TWEA) - } -} - -void ICACHE_RAM_ATTR twi_stop(void) -{ - // send stop condition - //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); - SCL_HIGH(); // _BV(TWINT) - twi_ack = 1; // _BV(TWEA) - twi_delay(5); // Maybe this should be here - SDA_HIGH(); // _BV(TWSTO) - // update twi state - twi_state = TWI_READY; -} - -void ICACHE_RAM_ATTR twi_releaseBus(void) -{ - // release bus - //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); - SCL_HIGH(); // _BV(TWINT) - twi_ack = 1; // _BV(TWEA) - SDA_HIGH(); - - // update twi state - twi_state = TWI_READY; -} - - -void ICACHE_RAM_ATTR twi_onTwipEvent(uint8_t status) -{ - switch(status) { - // Slave Receiver - case TW_SR_SLA_ACK: // addressed, returned ack - case TW_SR_GCALL_ACK: // addressed generally, returned ack - case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack - case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack - // enter slave receiver mode - twi_state = TWI_SRX; - // indicate that rx buffer can be overwritten and ack - twi_rxBufferIndex = 0; - twi_reply(1); - break; - case TW_SR_DATA_ACK: // data received, returned ack - case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack - // if there is still room in the rx buffer - if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ - // put byte in buffer and ack - twi_rxBuffer[twi_rxBufferIndex++] = TWDR; - twi_reply(1); - }else{ - // otherwise nack - twi_reply(0); - } - break; - case TW_SR_STOP: // stop or repeated start condition received - // put a null char after data if there's room - if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ - twi_rxBuffer[twi_rxBufferIndex] = '\0'; - } - // callback to user-defined callback over event task to allow for non-RAM-residing code - //twi_rxBufferLock = true; // This may be necessary - ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_RX, twi_rxBufferIndex); - - // since we submit rx buffer to "wire" library, we can reset it - twi_rxBufferIndex = 0; - break; - - case TW_SR_DATA_NACK: // data received, returned nack - case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack - // nack back at master - twi_reply(0); - break; - - // Slave Transmitter - case TW_ST_SLA_ACK: // addressed, returned ack - case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack - // enter slave transmitter mode - twi_state = TWI_STX; - // ready the tx buffer index for iteration - twi_txBufferIndex = 0; - // set tx buffer length to be zero, to verify if user changes it - twi_txBufferLength = 0; - // callback to user-defined callback over event task to allow for non-RAM-residing code - // request for txBuffer to be filled and length to be set - // note: user must call twi_transmit(bytes, length) to do this - ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_TX, 0); - break; - - case TW_ST_DATA_ACK: // byte sent, ack returned - // copy data to output register - TWDR = twi_txBuffer[twi_txBufferIndex++]; - - bitCount = 8; - bitCount--; - (twi_data & 0x80) ? SDA_HIGH() : SDA_LOW(); - twi_data <<= 1; - - // if there is more to send, ack, otherwise nack - if(twi_txBufferIndex < twi_txBufferLength){ - twi_reply(1); - }else{ - twi_reply(0); - } - break; - case TW_ST_DATA_NACK: // received nack, we are done - case TW_ST_LAST_DATA: // received ack, but we are done already! - // leave slave receiver state - twi_releaseBus(); - break; - - // All - case TW_NO_INFO: // no state information - break; - case TW_BUS_ERROR: // bus error, illegal stop/start - twi_error = TW_BUS_ERROR; - twi_stop(); - break; - } -} - -void ICACHE_RAM_ATTR onTimer(void *unused) -{ - (void)unused; - twi_releaseBus(); - twip_status = TW_BUS_ERROR; - twi_onTwipEvent(twip_status); - twip_mode = TWIPM_WAIT; - twip_state = TWIP_BUS_ERR; -} - -static void eventTask(ETSEvent *e) -{ - - if (e == NULL) { - return; - } - - switch (e->sig) - { - case TWI_SIG_TX: - twi_onSlaveTransmit(); - - // if they didn't change buffer & length, initialize it - if (twi_txBufferLength == 0) { - twi_txBufferLength = 1; - twi_txBuffer[0] = 0x00; - } - - // Initiate transmission - twi_onTwipEvent(TW_ST_DATA_ACK); - - break; - - case TWI_SIG_RX: - // ack future responses and leave slave receiver state - twi_releaseBus(); - twi_onSlaveReceive(twi_rxBuffer, e->par); - break; - } -} - -void ICACHE_RAM_ATTR onSclChange(void) -{ - static uint8_t sda; - static uint8_t scl; - - sda = SDA_READ(); - scl = SCL_READ(); - - twip_status = 0xF8; // reset TWI status - - switch (twip_state) - { - case TWIP_IDLE: - case TWIP_WAIT_STOP: - case TWIP_BUS_ERR: - // ignore - break; - - case TWIP_START: - case TWIP_REP_START: - case TWIP_SLA_W: - case TWIP_READ: - if (!scl) { - // ignore - } else { - bitCount--; - twi_data <<= 1; - twi_data |= sda; - - if (bitCount != 0) { - // continue - } else { - twip_state = TWIP_SEND_ACK; - } - } - break; - - case TWIP_SEND_ACK: - if (scl) { - // ignore - } else { - if (twip_mode == TWIPM_IDLE) { - if ((twi_data & 0xFE) != twi_addr) { - // ignore - } else { - SDA_LOW(); - } - } else { - if (!twi_ack) { - // ignore - } else { - SDA_LOW(); - } - } - twip_state = TWIP_WAIT_ACK; - } - break; - - case TWIP_WAIT_ACK: - if (scl) { - // ignore - } else { - if (twip_mode == TWIPM_IDLE) { - if ((twi_data & 0xFE) != twi_addr) { - SDA_HIGH(); - twip_state = TWIP_WAIT_STOP; - } else { - SCL_LOW(); // clock stretching - SDA_HIGH(); - twip_mode = TWIPM_ADDRESSED; - if (!(twi_data & 0x01)) { - twip_status = TW_SR_SLA_ACK; - twi_onTwipEvent(twip_status); - bitCount = 8; - twip_state = TWIP_SLA_W; - } else { - twip_status = TW_ST_SLA_ACK; - twi_onTwipEvent(twip_status); - twip_state = TWIP_SLA_R; - } - } - } else { - SCL_LOW(); // clock stretching - SDA_HIGH(); - if (!twi_ack) { - twip_status = TW_SR_DATA_NACK; - twi_onTwipEvent(twip_status); - twip_mode = TWIPM_WAIT; - twip_state = TWIP_WAIT_STOP; - } else { - twip_status = TW_SR_DATA_ACK; - twi_onTwipEvent(twip_status); - bitCount = 8; - twip_state = TWIP_READ; - } - } - } - break; - - case TWIP_SLA_R: - case TWIP_WRITE: - if (scl) { - // ignore - } else { - bitCount--; - (twi_data & 0x80) ? SDA_HIGH() : SDA_LOW(); - twi_data <<= 1; - - if (bitCount != 0) { - // continue - } else { - twip_state = TWIP_REC_ACK; - } - } - break; - - case TWIP_REC_ACK: - if (scl) { - // ignore - } else { - SDA_HIGH(); - twip_state = TWIP_READ_ACK; - } - break; - - case TWIP_READ_ACK: - if (!scl) { - // ignore - } else { - twi_ack_rec = !sda; - twip_state = TWIP_RWAIT_ACK; - } - break; - - case TWIP_RWAIT_ACK: - if (scl) { - // ignore - } else { - SCL_LOW(); // clock stretching - if (twi_ack && twi_ack_rec) { - twip_status = TW_ST_DATA_ACK; - twi_onTwipEvent(twip_status); - twip_state = TWIP_WRITE; - } else { - // we have no more data to send and/or the master doesn't want anymore - twip_status = twi_ack_rec ? TW_ST_LAST_DATA : TW_ST_DATA_NACK; - twi_onTwipEvent(twip_status); - twip_mode = TWIPM_WAIT; - twip_state = TWIP_WAIT_STOP; - } - } - break; - - default: - break; - } -} - -void ICACHE_RAM_ATTR onSdaChange(void) -{ - static uint8_t sda; - static uint8_t scl; - sda = SDA_READ(); - scl = SCL_READ(); - - switch (twip_state) - { - case TWIP_IDLE: - if (!scl) { - // DATA - ignore - } else if (sda) { - // STOP - ignore - } else { - // START - bitCount = 8; - twip_state = TWIP_START; - ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms - } - break; - - case TWIP_START: - case TWIP_REP_START: - case TWIP_SEND_ACK: - case TWIP_WAIT_ACK: - case TWIP_SLA_R: - case TWIP_REC_ACK: - case TWIP_READ_ACK: - case TWIP_RWAIT_ACK: - case TWIP_WRITE: - if (!scl) { - // DATA - ignore - } else { - // START or STOP - SDA_HIGH(); // Should not be necessary - twip_status = TW_BUS_ERROR; - twi_onTwipEvent(twip_status); - twip_mode = TWIPM_WAIT; - twip_state = TWIP_BUS_ERR; - } - break; - - case TWIP_WAIT_STOP: - case TWIP_BUS_ERR: - if (!scl) { - // DATA - ignore - } else { - if (sda) { - // STOP - SCL_LOW(); // clock stretching - ets_timer_disarm(&timer); - twip_state = TWIP_IDLE; - twip_mode = TWIPM_IDLE; - SCL_HIGH(); - } else { - // START - if (twip_state == TWIP_BUS_ERR) { - // ignore - } else { - bitCount = 8; - twip_state = TWIP_REP_START; - ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms - } - } - } - break; - - case TWIP_SLA_W: - case TWIP_READ: - if (!scl) { - // DATA - ignore - } else { - // START or STOP - if (bitCount != 7) { - // inside byte transfer - error - twip_status = TW_BUS_ERROR; - twi_onTwipEvent(twip_status); - twip_mode = TWIPM_WAIT; - twip_state = TWIP_BUS_ERR; - } else { - // during first bit in byte transfer - ok - SCL_LOW(); // clock stretching - twip_status = TW_SR_STOP; - twi_onTwipEvent(twip_status); - if (sda) { - // STOP - ets_timer_disarm(&timer); - twip_state = TWIP_IDLE; - twip_mode = TWIPM_IDLE; - } else { - // START - bitCount = 8; - ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms - twip_state = TWIP_REP_START; - twip_mode = TWIPM_IDLE; - } - } - } - break; - - default: - break; - } -} }; diff --git a/cores/esp8266/core_esp8266_sigma_delta.cpp b/cores/esp8266/core_esp8266_sigma_delta.cpp index 68fc589e24..f8fd0c29ba 100644 --- a/cores/esp8266/core_esp8266_sigma_delta.cpp +++ b/cores/esp8266/core_esp8266_sigma_delta.cpp @@ -1,178 +1,184 @@ /* - core_esp8266_sigma_delta.c - sigma delta library for esp8266 - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + core_esp8266_sigma_delta.c - sigma delta library for esp8266 + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Arduino.h" // using pinMode extern "C" { -// definitions in esp8266_peri.h style + // definitions in esp8266_peri.h style #define GPSD ESP8266_REG(0x368) // GPIO_SIGMA_DELTA register @ 0x600000368 #define GPSDT 0 // target, 8 bits #define GPSDP 8 // prescaler, 8 bits #define GPSDE 16 // enable -void sigmaDeltaSetPrescaler(uint8_t prescaler); // avoids compiler warning - -/****************************************************************************** - * FunctionName : sigmaDeltaEnable - * Description : enable the internal sigma delta source - * Parameters : none - * Returns : none -*******************************************************************************/ -void ICACHE_FLASH_ATTR sigmaDeltaEnable() -{ - GPSD = (0 << GPSDT) | (0 << GPSDP) | (1 << GPSDE); //SIGMA_DELTA_TARGET(0) | SIGMA_DELTA_PRESCALER(0) | SIGMA_DELTA_ENABLE(ENABLED) -} - -/****************************************************************************** - * FunctionName : sigmaDeltaDisable - * Description : stop the internal sigma delta source - * Parameters : none - * Returns : none -*******************************************************************************/ -void ICACHE_FLASH_ATTR sigmaDeltaDisable() -{ - GPSD = (0 << GPSDT) | (0 << GPSDP) | (0 << GPSDE); //SIGMA_DELTA_TARGET(0) | SIGMA_DELTA_PRESCALER(0) | SIGMA_DELTA_ENABLE(DISABLED) -} - -/****************************************************************************** - * FunctionName : sigmaDeltaAttachPin - * Description : connects the sigma delta source to a physical output pin - * Parameters : pin (0..15), channel = unused, for compatibility with ESP32 - * Returns : none -*******************************************************************************/ -void ICACHE_FLASH_ATTR sigmaDeltaAttachPin(uint8_t pin, uint8_t channel) -{ - (void) channel; - // make the chosen pin an output pin - pinMode (pin, OUTPUT); - if (pin < 16) { - // set its source to the sigma delta source - GPC(pin) |= (1 << GPCS); //SOURCE 0:GPIO_DATA,1:SigmaDelta - } -} - -/****************************************************************************** - * FunctionName : sigmaDeltaDetachPin - * Description : disconnects the sigma delta source from a physical output pin - * Parameters : pin (0..16) - * Returns : none -*******************************************************************************/ -void ICACHE_FLASH_ATTR sigmaDeltaDetachPin(uint8_t pin) -{ - if (pin < 16) { - // set its source to the sigma delta source - GPC(pin) &= ~(1 << GPCS); //SOURCE 0:GPIO_DATA,1:SigmaDelta - } -} - -/****************************************************************************** - * FunctionName : sigmaDeltaIsPinAttached - * Description : query if pin is attached - * Parameters : pin (0..16) - * Returns : bool -*******************************************************************************/ -bool ICACHE_FLASH_ATTR sigmaDeltaIsPinAttached(uint8_t pin) -{ - if (pin < 16) { - // set its source to the sigma delta source - return (GPC(pin) & (1 << GPCS)); //SOURCE 0:GPIO_DATA,1:SigmaDelta - } - else - return false; -} - -/****************************************************************************** - * FunctionName : sigmaDeltaSetup - * Description : start the sigma delta generator with the chosen parameters - * Parameters : channel = unused (for compatibility with ESP32), - * freq : 1220-312500 (lowest frequency in the output signal's spectrum) - * Returns : uint32_t the actual frequency, closest to the input parameter -*******************************************************************************/ -uint32_t ICACHE_FLASH_ATTR sigmaDeltaSetup(uint8_t channel, uint32_t freq) -{ - (void) channel; - - uint32_t prescaler = ((uint32_t)10000000/(freq*32)) - 1; - - if(prescaler > 0xFF) { - prescaler = 0xFF; - } - sigmaDeltaEnable(); - sigmaDeltaSetPrescaler ((uint8_t) prescaler); - - return 10000000/((prescaler + 1) * 32); -} - -/****************************************************************************** - * FunctionName : sigmaDeltaWrite - * Description : set the duty cycle for the sigma-delta source - * Parameters : uint8 duty, 0-255, duty cycle = target/256, - * channel = unused, for compatibility with ESP32 - * Returns : none -*******************************************************************************/ -void ICACHE_FLASH_ATTR sigmaDeltaWrite(uint8_t channel, uint8_t duty) -{ - uint32_t reg = GPSD; - (void) channel; - - reg = (reg & ~(0xFF << GPSDT)) | ((duty & 0xFF) << GPSDT); - GPSD = reg; - -} -/****************************************************************************** - * FunctionName : sigmaDeltaRead - * Description : get the duty cycle for the sigma-delta source - * Parameters : channel = unused, for compatibility with ESP32 - * Returns : uint8_t duty cycle value 0..255 -*******************************************************************************/ -uint8_t ICACHE_FLASH_ATTR sigmaDeltaRead(uint8_t channel) -{ - (void) channel; - return (uint8_t)((GPSD >> GPSDT) & 0xFF); -} - -/****************************************************************************** - * FunctionName : sigmaDeltaSetPrescaler - * Description : set the clock divider for the sigma-delta source - * Parameters : uint8 prescaler, 0-255, divides the 80MHz base clock by this amount - * Returns : none -*******************************************************************************/ -void ICACHE_FLASH_ATTR sigmaDeltaSetPrescaler(uint8_t prescaler) -{ - uint32_t reg = GPSD; - - reg = (reg & ~(0xFF << GPSDP)) | ((prescaler & 0xFF) << GPSDP); - GPSD = reg; -} - -/****************************************************************************** - * FunctionName : sigmaDeltaGetPrescaler - * Description : get the prescaler value from the GPIO_SIGMA_DELTA register - * Parameters : none - * Returns : uint8 prescaler, CLK_DIV , 0-255 -*******************************************************************************/ -uint8_t ICACHE_FLASH_ATTR sigmaDeltaGetPrescaler(void) -{ - return (uint8_t)((GPSD >> GPSDP) & 0xFF); -} + void sigmaDeltaSetPrescaler(uint8_t prescaler); // avoids compiler warning + + /****************************************************************************** + FunctionName : sigmaDeltaEnable + Description : enable the internal sigma delta source + Parameters : none + Returns : none + *******************************************************************************/ + void ICACHE_FLASH_ATTR sigmaDeltaEnable() + { + GPSD = (0 << GPSDT) | (0 << GPSDP) | (1 << GPSDE); //SIGMA_DELTA_TARGET(0) | SIGMA_DELTA_PRESCALER(0) | SIGMA_DELTA_ENABLE(ENABLED) + } + + /****************************************************************************** + FunctionName : sigmaDeltaDisable + Description : stop the internal sigma delta source + Parameters : none + Returns : none + *******************************************************************************/ + void ICACHE_FLASH_ATTR sigmaDeltaDisable() + { + GPSD = (0 << GPSDT) | (0 << GPSDP) | (0 << GPSDE); //SIGMA_DELTA_TARGET(0) | SIGMA_DELTA_PRESCALER(0) | SIGMA_DELTA_ENABLE(DISABLED) + } + + /****************************************************************************** + FunctionName : sigmaDeltaAttachPin + Description : connects the sigma delta source to a physical output pin + Parameters : pin (0..15), channel = unused, for compatibility with ESP32 + Returns : none + *******************************************************************************/ + void ICACHE_FLASH_ATTR sigmaDeltaAttachPin(uint8_t pin, uint8_t channel) + { + (void) channel; + // make the chosen pin an output pin + pinMode(pin, OUTPUT); + if (pin < 16) + { + // set its source to the sigma delta source + GPC(pin) |= (1 << GPCS); //SOURCE 0:GPIO_DATA,1:SigmaDelta + } + } + + /****************************************************************************** + FunctionName : sigmaDeltaDetachPin + Description : disconnects the sigma delta source from a physical output pin + Parameters : pin (0..16) + Returns : none + *******************************************************************************/ + void ICACHE_FLASH_ATTR sigmaDeltaDetachPin(uint8_t pin) + { + if (pin < 16) + { + // set its source to the sigma delta source + GPC(pin) &= ~(1 << GPCS); //SOURCE 0:GPIO_DATA,1:SigmaDelta + } + } + + /****************************************************************************** + FunctionName : sigmaDeltaIsPinAttached + Description : query if pin is attached + Parameters : pin (0..16) + Returns : bool + *******************************************************************************/ + bool ICACHE_FLASH_ATTR sigmaDeltaIsPinAttached(uint8_t pin) + { + if (pin < 16) + { + // set its source to the sigma delta source + return (GPC(pin) & (1 << GPCS)); //SOURCE 0:GPIO_DATA,1:SigmaDelta + } + else + { + return false; + } + } + + /****************************************************************************** + FunctionName : sigmaDeltaSetup + Description : start the sigma delta generator with the chosen parameters + Parameters : channel = unused (for compatibility with ESP32), + freq : 1220-312500 (lowest frequency in the output signal's spectrum) + Returns : uint32_t the actual frequency, closest to the input parameter + *******************************************************************************/ + uint32_t ICACHE_FLASH_ATTR sigmaDeltaSetup(uint8_t channel, uint32_t freq) + { + (void) channel; + + uint32_t prescaler = ((uint32_t)10000000 / (freq * 32)) - 1; + + if (prescaler > 0xFF) + { + prescaler = 0xFF; + } + sigmaDeltaEnable(); + sigmaDeltaSetPrescaler((uint8_t) prescaler); + + return 10000000 / ((prescaler + 1) * 32); + } + + /****************************************************************************** + FunctionName : sigmaDeltaWrite + Description : set the duty cycle for the sigma-delta source + Parameters : uint8 duty, 0-255, duty cycle = target/256, + channel = unused, for compatibility with ESP32 + Returns : none + *******************************************************************************/ + void ICACHE_FLASH_ATTR sigmaDeltaWrite(uint8_t channel, uint8_t duty) + { + uint32_t reg = GPSD; + (void) channel; + + reg = (reg & ~(0xFF << GPSDT)) | ((duty & 0xFF) << GPSDT); + GPSD = reg; + + } + /****************************************************************************** + FunctionName : sigmaDeltaRead + Description : get the duty cycle for the sigma-delta source + Parameters : channel = unused, for compatibility with ESP32 + Returns : uint8_t duty cycle value 0..255 + *******************************************************************************/ + uint8_t ICACHE_FLASH_ATTR sigmaDeltaRead(uint8_t channel) + { + (void) channel; + return (uint8_t)((GPSD >> GPSDT) & 0xFF); + } + + /****************************************************************************** + FunctionName : sigmaDeltaSetPrescaler + Description : set the clock divider for the sigma-delta source + Parameters : uint8 prescaler, 0-255, divides the 80MHz base clock by this amount + Returns : none + *******************************************************************************/ + void ICACHE_FLASH_ATTR sigmaDeltaSetPrescaler(uint8_t prescaler) + { + uint32_t reg = GPSD; + + reg = (reg & ~(0xFF << GPSDP)) | ((prescaler & 0xFF) << GPSDP); + GPSD = reg; + } + + /****************************************************************************** + FunctionName : sigmaDeltaGetPrescaler + Description : get the prescaler value from the GPIO_SIGMA_DELTA register + Parameters : none + Returns : uint8 prescaler, CLK_DIV , 0-255 + *******************************************************************************/ + uint8_t ICACHE_FLASH_ATTR sigmaDeltaGetPrescaler(void) + { + return (uint8_t)((GPSD >> GPSDP) & 0xFF); + } }; diff --git a/cores/esp8266/core_esp8266_timer.cpp b/cores/esp8266/core_esp8266_timer.cpp index a2bce55003..c94be2b5a7 100644 --- a/cores/esp8266/core_esp8266_timer.cpp +++ b/cores/esp8266/core_esp8266_timer.cpp @@ -1,22 +1,22 @@ /* - timer.c - Timer1 library for esp8266 + timer.c - Timer1 library for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "wiring_private.h" #include "pins_arduino.h" @@ -26,82 +26,101 @@ extern "C" { -// ------------------------------------------------------------------ - -// timer 1 - -static volatile timercallback timer1_user_cb = NULL; - -void ICACHE_RAM_ATTR timer1_isr_handler(void *para){ - (void) para; - if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) TEIE &= ~TEIE1;//edge int disable - T1I = 0; - if (timer1_user_cb) { - // to make ISR compatible to Arduino AVR model where interrupts are disabled - // we disable them before we call the client ISR - uint32_t savedPS = xt_rsil(15); // stop other interrupts - timer1_user_cb(); - xt_wsr_ps(savedPS); + // ------------------------------------------------------------------ - + // timer 1 + + static volatile timercallback timer1_user_cb = NULL; + + void ICACHE_RAM_ATTR timer1_isr_handler(void *para) + { + (void) para; + if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) + { + TEIE &= ~TEIE1; //edge int disable + } + T1I = 0; + if (timer1_user_cb) + { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + uint32_t savedPS = xt_rsil(15); // stop other interrupts + timer1_user_cb(); + xt_wsr_ps(savedPS); + } } -} - -void ICACHE_RAM_ATTR timer1_isr_init(){ - ETS_FRC_TIMER1_INTR_ATTACH(timer1_isr_handler, NULL); -} - -void timer1_attachInterrupt(timercallback userFunc) { - timer1_user_cb = userFunc; - ETS_FRC1_INTR_ENABLE(); -} - -void ICACHE_RAM_ATTR timer1_detachInterrupt() { - timer1_user_cb = 0; - TEIE &= ~TEIE1;//edge int disable - ETS_FRC1_INTR_DISABLE(); -} - -void timer1_enable(uint8_t divider, uint8_t int_type, uint8_t reload){ - T1C = (1 << TCTE) | ((divider & 3) << TCPD) | ((int_type & 1) << TCIT) | ((reload & 1) << TCAR); - T1I = 0; -} - -void ICACHE_RAM_ATTR timer1_write(uint32_t ticks){ - T1L = ((ticks)& 0x7FFFFF); - if ((T1C & (1 << TCIT)) == 0) TEIE |= TEIE1;//edge int enable -} - -void ICACHE_RAM_ATTR timer1_disable(){ - T1C = 0; - T1I = 0; -} - -//------------------------------------------------------------------- -// timer 0 - -static volatile timercallback timer0_user_cb = NULL; - -void ICACHE_RAM_ATTR timer0_isr_handler(void* para){ - (void) para; - if (timer0_user_cb) { - // to make ISR compatible to Arduino AVR model where interrupts are disabled - // we disable them before we call the client ISR - uint32_t savedPS = xt_rsil(15); // stop other interrupts - timer0_user_cb(); - xt_wsr_ps(savedPS); + + void ICACHE_RAM_ATTR timer1_isr_init() + { + ETS_FRC_TIMER1_INTR_ATTACH(timer1_isr_handler, NULL); + } + + void timer1_attachInterrupt(timercallback userFunc) + { + timer1_user_cb = userFunc; + ETS_FRC1_INTR_ENABLE(); + } + + void ICACHE_RAM_ATTR timer1_detachInterrupt() + { + timer1_user_cb = 0; + TEIE &= ~TEIE1;//edge int disable + ETS_FRC1_INTR_DISABLE(); + } + + void timer1_enable(uint8_t divider, uint8_t int_type, uint8_t reload) + { + T1C = (1 << TCTE) | ((divider & 3) << TCPD) | ((int_type & 1) << TCIT) | ((reload & 1) << TCAR); + T1I = 0; + } + + void ICACHE_RAM_ATTR timer1_write(uint32_t ticks) + { + T1L = ((ticks) & 0x7FFFFF); + if ((T1C & (1 << TCIT)) == 0) + { + TEIE |= TEIE1; //edge int enable + } + } + + void ICACHE_RAM_ATTR timer1_disable() + { + T1C = 0; + T1I = 0; + } + + //------------------------------------------------------------------- + // timer 0 + + static volatile timercallback timer0_user_cb = NULL; + + void ICACHE_RAM_ATTR timer0_isr_handler(void* para) + { + (void) para; + if (timer0_user_cb) + { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + uint32_t savedPS = xt_rsil(15); // stop other interrupts + timer0_user_cb(); + xt_wsr_ps(savedPS); + } } -} -void timer0_isr_init(){ - ETS_CCOMPARE0_INTR_ATTACH(timer0_isr_handler, NULL); -} + void timer0_isr_init() + { + ETS_CCOMPARE0_INTR_ATTACH(timer0_isr_handler, NULL); + } -void timer0_attachInterrupt(timercallback userFunc) { - timer0_user_cb = userFunc; - ETS_CCOMPARE0_ENABLE(); -} + void timer0_attachInterrupt(timercallback userFunc) + { + timer0_user_cb = userFunc; + ETS_CCOMPARE0_ENABLE(); + } -void ICACHE_RAM_ATTR timer0_detachInterrupt() { - timer0_user_cb = NULL; - ETS_CCOMPARE0_DISABLE(); -} + void ICACHE_RAM_ATTR timer0_detachInterrupt() + { + timer0_user_cb = NULL; + ETS_CCOMPARE0_DISABLE(); + } }; diff --git a/cores/esp8266/core_esp8266_version.h b/cores/esp8266/core_esp8266_version.h index 856d9f8f43..b0eca28599 100644 --- a/cores/esp8266/core_esp8266_version.h +++ b/cores/esp8266/core_esp8266_version.h @@ -1,22 +1,22 @@ /* - core_esp8266_version.h - parse "git describe" at compile time - Copyright (c) 2018 david gauchard. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + core_esp8266_version.h - parse "git describe" at compile time + Copyright (c) 2018 david gauchard. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CORE_ESP8266_VERSION_H @@ -33,148 +33,152 @@ extern "C++" { -// Following constexpr functions are compiled and executed -// *after* pre-processing and *during* compilation -// -// Their result is treated like a numeric constant in final binary code. -// git tags must be in the form: -// - .. (2.4.2) (2.5.0) -// - ..-rc (2.5.0-rc1) (2.5.0-rc2) -// -// "git describe" = ARDUINO_ESP8266_GIT_DESC will thus be in the form: -// - (2.4.2) (2.5.0) -// - --g (2.4.2-91-gcb05b86d) (2.5.0-rc3-1-gcb05b86d) -// -// Examples: -// case 2.4.2 (fresh version/tag) -// esp8266CoreVersionSubRevision() is 0 Numeric is: 20402000 -// case 2.4.2-91-gcb05b86d: -// esp8266CoreVersionSubRevision() is -91 Numeric is: 20402091 -// case 2.5.0-rc3-1-gcb05b86d: -// esp8266CoreVersionSubRevision() is 3 Numeric is: 20499903 -// case 2.5.0: -// esp8266CoreVersionSubRevision() is 0 Numeric is: 20500000 -// -// Using esp8266::coreVersionNumeric() in a portable way: -// -// #if HAS_ESP8266_VERSION_NUMERIC -// if (esp8266::coreVersionNumeric() >= 20500042) -// { -// // modern api can be used -// } -// else -// #endif -// { -// // code using older api -// // (will not be compiled in when newer api is usable) -// } - -namespace conststr { - -constexpr -bool isDecimal (const char c) -{ - return c >= '0' && c <= '9'; -} - -template constexpr -bool isMinus (const char (&arr) [N], unsigned i) -{ - return arr[i] == '-' && isDecimal(arr[i+1]); -} - -template constexpr -int atoi (const char (&arr) [N], unsigned i) -{ - return ({ // <= c++11 requires a "return statement" - int ret = 0; - int sign = 1; - if (arr[i] == '-') - { - sign = -1; - i++; - } - while (isDecimal(arr[i])) - ret = 10*ret + arr[i++] - '0'; - ret * sign; - }); -} - -template constexpr -int parseNthInteger (const char (&arr) [N], unsigned f) -{ - return ({ // <= c++11 requires a "return statement" - unsigned i = 0; - while (f && arr[i]) + // Following constexpr functions are compiled and executed + // *after* pre-processing and *during* compilation + // + // Their result is treated like a numeric constant in final binary code. + // git tags must be in the form: + // - .. (2.4.2) (2.5.0) + // - ..-rc (2.5.0-rc1) (2.5.0-rc2) + // + // "git describe" = ARDUINO_ESP8266_GIT_DESC will thus be in the form: + // - (2.4.2) (2.5.0) + // - --g (2.4.2-91-gcb05b86d) (2.5.0-rc3-1-gcb05b86d) + // + // Examples: + // case 2.4.2 (fresh version/tag) + // esp8266CoreVersionSubRevision() is 0 Numeric is: 20402000 + // case 2.4.2-91-gcb05b86d: + // esp8266CoreVersionSubRevision() is -91 Numeric is: 20402091 + // case 2.5.0-rc3-1-gcb05b86d: + // esp8266CoreVersionSubRevision() is 3 Numeric is: 20499903 + // case 2.5.0: + // esp8266CoreVersionSubRevision() is 0 Numeric is: 20500000 + // + // Using esp8266::coreVersionNumeric() in a portable way: + // + // #if HAS_ESP8266_VERSION_NUMERIC + // if (esp8266::coreVersionNumeric() >= 20500042) + // { + // // modern api can be used + // } + // else + // #endif + // { + // // code using older api + // // (will not be compiled in when newer api is usable) + // } + + namespace conststr { + + constexpr + bool isDecimal(const char c) + { + return c >= '0' && c <= '9'; + } + + template constexpr + bool isMinus(const char (&arr) [N], unsigned i) + { + return arr[i] == '-' && isDecimal(arr[i + 1]); + } + + template constexpr + int atoi(const char (&arr) [N], unsigned i) + { + return ( // <= c++11 requires a "return statement" { - if (isMinus(arr, i)) + int ret = 0; + int sign = 1; + if (arr[i] == '-') + { + sign = -1; i++; - for (; isDecimal(arr[i]); i++); - f--; - for (; arr[i] && !isMinus(arr, i) && !isDecimal(arr[i]); i++); - } - atoi(arr, i); - }); -} - -}; // namespace conststr - -namespace esp8266 { - -/* - * version major - */ -constexpr -int coreVersionMajor () -{ - return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 0); -} - -/* - * version minor - */ -constexpr -int coreVersionMinor () -{ - return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 1); -} - -/* - * version revision - */ -constexpr -int coreVersionRevision () -{ - return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 2); -} - -/* - * git commit number since last tag (negative) - * or RC-number (positive) - */ -constexpr -int coreVersionSubRevision () -{ - return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 3); -} - -/* - * unique revision indentifier (never decreases) - */ -constexpr -int coreVersionNumeric () -{ - return coreVersionMajor() * 10000000 - + coreVersionMinor() * 100000 - + coreVersionRevision() * 1000 - + (coreVersionSubRevision() < 0 ? - -coreVersionSubRevision() : - coreVersionSubRevision() ? - coreVersionSubRevision() - 100 : - 0); -} - -}; // namespace esp8266 + } + while (isDecimal(arr[i])) + ret = 10 * ret + arr[i++] - '0'; + ret * sign; + }); + } + + template constexpr + int parseNthInteger(const char (&arr) [N], unsigned f) + { + return ( // <= c++11 requires a "return statement" + { + unsigned i = 0; + while (f && arr[i]) + { + if (isMinus(arr, i)) + { + i++; + } + for (; isDecimal(arr[i]); i++); + f--; + for (; arr[i] && !isMinus(arr, i) && !isDecimal(arr[i]); i++); + } + atoi(arr, i); + }); + } + + }; // namespace conststr + + namespace esp8266 { + + /* + version major + */ + constexpr + int coreVersionMajor() + { + return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 0); + } + + /* + version minor + */ + constexpr + int coreVersionMinor() + { + return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 1); + } + + /* + version revision + */ + constexpr + int coreVersionRevision() + { + return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 2); + } + + /* + git commit number since last tag (negative) + or RC-number (positive) + */ + constexpr + int coreVersionSubRevision() + { + return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 3); + } + + /* + unique revision indentifier (never decreases) + */ + constexpr + int coreVersionNumeric() + { + return coreVersionMajor() * 10000000 + + coreVersionMinor() * 100000 + + coreVersionRevision() * 1000 + + (coreVersionSubRevision() < 0 ? + -coreVersionSubRevision() : + coreVersionSubRevision() ? + coreVersionSubRevision() - 100 : + 0); + } + + }; // namespace esp8266 } // extern "C++" #endif // __cplusplus diff --git a/cores/esp8266/core_esp8266_waveform.cpp b/cores/esp8266/core_esp8266_waveform.cpp index 59fce8695e..08cdf47ed6 100644 --- a/cores/esp8266/core_esp8266_waveform.cpp +++ b/cores/esp8266/core_esp8266_waveform.cpp @@ -1,40 +1,40 @@ /* - esp8266_waveform - General purpose waveform generation and control, + esp8266_waveform - General purpose waveform generation and control, supporting outputs on all pins in parallel. - Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. + Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. - The core idea is to have a programmable waveform generator with a unique - high and low period (defined in microseconds). TIMER1 is set to 1-shot - mode and is always loaded with the time until the next edge of any live - waveforms. + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds). TIMER1 is set to 1-shot + mode and is always loaded with the time until the next edge of any live + waveforms. - Up to one waveform generator per pin supported. + Up to one waveform generator per pin supported. - Each waveform generator is synchronized to the ESP cycle counter, not the - timer. This allows for removing interrupt jitter and delay as the counter - always increments once per 80MHz clock. Changes to a waveform are - contiguous and only take effect on the next waveform transition, - allowing for smooth transitions. + Each waveform generator is synchronized to the ESP cycle counter, not the + timer. This allows for removing interrupt jitter and delay as the counter + always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. - This replaces older tone(), analogWrite(), and the Servo classes. + This replaces older tone(), analogWrite(), and the Servo classes. - Everywhere in the code where "cycles" is used, it means ESP.getCycleTime() - cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz). + Everywhere in the code where "cycles" is used, it means ESP.getCycleTime() + cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz). - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include @@ -43,267 +43,318 @@ extern "C" { -// Maximum delay between IRQs + // Maximum delay between IRQs #define MAXIRQUS (10000) -// Set/clear GPIO 0-15 by bitmask + // Set/clear GPIO 0-15 by bitmask #define SetGPIO(a) do { GPOS = a; } while (0) #define ClearGPIO(a) do { GPOC = a; } while (0) -// Waveform generator can create tones, PWM, and servos -typedef struct { - uint32_t nextServiceCycle; // ESP cycle timer when a transition required - uint32_t expiryCycle; // For time-limited waveform, the cycle when this waveform must stop - uint32_t nextTimeHighCycles; // Copy over low->high to keep smooth waveform - uint32_t nextTimeLowCycles; // Copy over high->low to keep smooth waveform -} Waveform; + // Waveform generator can create tones, PWM, and servos + typedef struct + { + uint32_t nextServiceCycle; // ESP cycle timer when a transition required + uint32_t expiryCycle; // For time-limited waveform, the cycle when this waveform must stop + uint32_t nextTimeHighCycles; // Copy over low->high to keep smooth waveform + uint32_t nextTimeLowCycles; // Copy over high->low to keep smooth waveform + } Waveform; -static Waveform waveform[17]; // State of all possible pins -static volatile uint32_t waveformState = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code -static volatile uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code + static Waveform waveform[17]; // State of all possible pins + static volatile uint32_t waveformState = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code + static volatile uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code -// Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine -static volatile uint32_t waveformToEnable = 0; // Message to the NMI handler to start a waveform on a inactive pin -static volatile uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation + // Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine + static volatile uint32_t waveformToEnable = 0; // Message to the NMI handler to start a waveform on a inactive pin + static volatile uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation -static uint32_t (*timer1CB)() = NULL; + static uint32_t (*timer1CB)() = NULL; -// Non-speed critical bits + // Non-speed critical bits #pragma GCC optimize ("Os") -static inline ICACHE_RAM_ATTR uint32_t GetCycleCount() { - uint32_t ccount; - __asm__ __volatile__("esync; rsr %0,ccount":"=a"(ccount)); - return ccount; -} - -// Interrupt on/off control -static ICACHE_RAM_ATTR void timer1Interrupt(); -static bool timerRunning = false; - -static void initTimer() { - timer1_disable(); - ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); - ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt); - timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); - timerRunning = true; -} - -static void ICACHE_RAM_ATTR deinitTimer() { - ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); - timer1_disable(); - timer1_isr_init(); - timerRunning = false; -} - -// Set a callback. Pass in NULL to stop it -void setTimer1Callback(uint32_t (*fn)()) { - timer1CB = fn; - if (!timerRunning && fn) { - initTimer(); - timer1_write(microsecondsToClockCycles(1)); // Cause an interrupt post-haste - } else if (timerRunning && !fn && !waveformEnabled) { - deinitTimer(); - } -} - -// Start up a waveform on a pin, or change the current one. Will change to the new -// waveform smoothly on next low->high transition. For immediate change, stopWaveform() -// first, then it will immediately begin. -int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) { - if ((pin > 16) || isFlashInterfacePin(pin)) { - return false; - } - Waveform *wave = &waveform[pin]; - // Adjust to shave off some of the IRQ time, approximately - wave->nextTimeHighCycles = microsecondsToClockCycles(timeHighUS); - wave->nextTimeLowCycles = microsecondsToClockCycles(timeLowUS); - wave->expiryCycle = runTimeUS ? GetCycleCount() + microsecondsToClockCycles(runTimeUS) : 0; - if (runTimeUS && !wave->expiryCycle) { - wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it - } - - uint32_t mask = 1<nextServiceCycle = GetCycleCount() + microsecondsToClockCycles(1); - waveformToEnable |= mask; - if (!timerRunning) { - initTimer(); - timer1_write(microsecondsToClockCycles(10)); - } else { - // Ensure timely service.... - if (T1L > microsecondsToClockCycles(10)) { - timer1_write(microsecondsToClockCycles(10)); - } + static inline ICACHE_RAM_ATTR uint32_t GetCycleCount() + { + uint32_t ccount; + __asm__ __volatile__("esync; rsr %0,ccount":"=a"(ccount)); + return ccount; } - while (waveformToEnable) { - delay(0); // Wait for waveform to update + + // Interrupt on/off control + static ICACHE_RAM_ATTR void timer1Interrupt(); + static bool timerRunning = false; + + static void initTimer() + { + timer1_disable(); + ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); + ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt); + timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); + timerRunning = true; + } + + static void ICACHE_RAM_ATTR deinitTimer() + { + ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); + timer1_disable(); + timer1_isr_init(); + timerRunning = false; } - } - return true; -} + // Set a callback. Pass in NULL to stop it + void setTimer1Callback(uint32_t (*fn)()) + { + timer1CB = fn; + if (!timerRunning && fn) + { + initTimer(); + timer1_write(microsecondsToClockCycles(1)); // Cause an interrupt post-haste + } + else if (timerRunning && !fn && !waveformEnabled) + { + deinitTimer(); + } + } + + // Start up a waveform on a pin, or change the current one. Will change to the new + // waveform smoothly on next low->high transition. For immediate change, stopWaveform() + // first, then it will immediately begin. + int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) + { + if ((pin > 16) || isFlashInterfacePin(pin)) + { + return false; + } + Waveform *wave = &waveform[pin]; + // Adjust to shave off some of the IRQ time, approximately + wave->nextTimeHighCycles = microsecondsToClockCycles(timeHighUS); + wave->nextTimeLowCycles = microsecondsToClockCycles(timeLowUS); + wave->expiryCycle = runTimeUS ? GetCycleCount() + microsecondsToClockCycles(runTimeUS) : 0; + if (runTimeUS && !wave->expiryCycle) + { + wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it + } -// Speed critical bits + uint32_t mask = 1 << pin; + if (!(waveformEnabled & mask)) + { + // Actually set the pin high or low in the IRQ service to guarantee times + wave->nextServiceCycle = GetCycleCount() + microsecondsToClockCycles(1); + waveformToEnable |= mask; + if (!timerRunning) + { + initTimer(); + timer1_write(microsecondsToClockCycles(10)); + } + else + { + // Ensure timely service.... + if (T1L > microsecondsToClockCycles(10)) + { + timer1_write(microsecondsToClockCycles(10)); + } + } + while (waveformToEnable) + { + delay(0); // Wait for waveform to update + } + } + + return true; + } + + // Speed critical bits #pragma GCC optimize ("O2") -// Normally would not want two copies like this, but due to different -// optimization levels the inline attribute gets lost if we try the -// other version. - -static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ() { - uint32_t ccount; - __asm__ __volatile__("rsr %0,ccount":"=a"(ccount)); - return ccount; -} - -static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b) { - if (a < b) { - return a; - } - return b; -} - -// Stops a waveform on a pin -int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) { - // Can't possibly need to stop anything if there is no timer active - if (!timerRunning) { - return false; - } - // If user sends in a pin >16 but <32, this will always point to a 0 bit - // If they send >=32, then the shift will result in 0 and it will also return false - uint32_t mask = 1< microsecondsToClockCycles(10)) { - timer1_write(microsecondsToClockCycles(10)); - } - while (waveformToDisable) { - /* no-op */ // Can't delay() since stopWaveform may be called from an IRQ - } - if (!waveformEnabled && !timer1CB) { - deinitTimer(); - } - return true; -} - -// The SDK and hardware take some time to actually get to our NMI code, so -// decrement the next IRQ's timer value by a bit so we can actually catch the -// real CPU cycle counter we want for the waveforms. + // Normally would not want two copies like this, but due to different + // optimization levels the inline attribute gets lost if we try the + // other version. + + static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ() + { + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a"(ccount)); + return ccount; + } + + static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b) + { + if (a < b) + { + return a; + } + return b; + } + + // Stops a waveform on a pin + int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) + { + // Can't possibly need to stop anything if there is no timer active + if (!timerRunning) + { + return false; + } + // If user sends in a pin >16 but <32, this will always point to a 0 bit + // If they send >=32, then the shift will result in 0 and it will also return false + uint32_t mask = 1 << pin; + if (!(waveformEnabled & mask)) + { + return false; // It's not running, nothing to do here + } + waveformToDisable |= mask; + // Ensure timely service.... + if (T1L > microsecondsToClockCycles(10)) + { + timer1_write(microsecondsToClockCycles(10)); + } + while (waveformToDisable) + { + /* no-op */ // Can't delay() since stopWaveform may be called from an IRQ + } + if (!waveformEnabled && !timer1CB) + { + deinitTimer(); + } + return true; + } + + // The SDK and hardware take some time to actually get to our NMI code, so + // decrement the next IRQ's timer value by a bit so we can actually catch the + // real CPU cycle counter we want for the waveforms. #if F_CPU == 80000000 - #define DELTAIRQ (microsecondsToClockCycles(3)) +#define DELTAIRQ (microsecondsToClockCycles(3)) #else - #define DELTAIRQ (microsecondsToClockCycles(2)) +#define DELTAIRQ (microsecondsToClockCycles(2)) #endif -static ICACHE_RAM_ATTR void timer1Interrupt() { - // Optimize the NMI inner loop by keeping track of the min and max GPIO that we - // are generating. In the common case (1 PWM) these may be the same pin and - // we can avoid looking at the other pins. - static int startPin = 0; - static int endPin = 0; - - uint32_t nextEventCycles = microsecondsToClockCycles(MAXIRQUS); - uint32_t timeoutCycle = GetCycleCountIRQ() + microsecondsToClockCycles(14); - - if (waveformToEnable || waveformToDisable) { - // Handle enable/disable requests from main app. - waveformEnabled = (waveformEnabled & ~waveformToDisable) | waveformToEnable; // Set the requested waveforms on/off - waveformState &= ~waveformToEnable; // And clear the state of any just started - waveformToEnable = 0; - waveformToDisable = 0; - // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t) - startPin = __builtin_ffs(waveformEnabled) - 1; - // Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one) - endPin = 32 - __builtin_clz(waveformEnabled); - } - - bool done = false; - if (waveformEnabled) { - do { - nextEventCycles = microsecondsToClockCycles(MAXIRQUS); - for (int i = startPin; i <= endPin; i++) { - uint32_t mask = 1<expiryCycle) { - int32_t expiryToGo = wave->expiryCycle - now; - if (expiryToGo < 0) { - // Done, remove! - waveformEnabled &= ~mask; - if (i == 16) { - GP16O &= ~1; - } else { - ClearGPIO(mask); - } - continue; - } + bool done = false; + if (waveformEnabled) + { + do + { + nextEventCycles = microsecondsToClockCycles(MAXIRQUS); + for (int i = startPin; i <= endPin; i++) + { + uint32_t mask = 1 << i; + + // If it's not on, ignore! + if (!(waveformEnabled & mask)) + { + continue; + } + + Waveform *wave = &waveform[i]; + uint32_t now = GetCycleCountIRQ(); + + // Disable any waveforms that are done + if (wave->expiryCycle) + { + int32_t expiryToGo = wave->expiryCycle - now; + if (expiryToGo < 0) + { + // Done, remove! + waveformEnabled &= ~mask; + if (i == 16) + { + GP16O &= ~1; + } + else + { + ClearGPIO(mask); + } + continue; + } + } + + // Check for toggles + int32_t cyclesToGo = wave->nextServiceCycle - now; + if (cyclesToGo < 0) + { + waveformState ^= mask; + if (waveformState & mask) + { + if (i == 16) + { + GP16O |= 1; // GPIO16 write slow as it's RMW + } + else + { + SetGPIO(mask); + } + wave->nextServiceCycle = now + wave->nextTimeHighCycles; + nextEventCycles = min_u32(nextEventCycles, wave->nextTimeHighCycles); + } + else + { + if (i == 16) + { + GP16O &= ~1; // GPIO16 write slow as it's RMW + } + else + { + ClearGPIO(mask); + } + wave->nextServiceCycle = now + wave->nextTimeLowCycles; + nextEventCycles = min_u32(nextEventCycles, wave->nextTimeLowCycles); + } + } + else + { + uint32_t deltaCycles = wave->nextServiceCycle - now; + nextEventCycles = min_u32(nextEventCycles, deltaCycles); + } + } + + // Exit the loop if we've hit the fixed runtime limit or the next event is known to be after that timeout would occur + uint32_t now = GetCycleCountIRQ(); + int32_t cycleDeltaNextEvent = timeoutCycle - (now + nextEventCycles); + int32_t cyclesLeftTimeout = timeoutCycle - now; + done = (cycleDeltaNextEvent < 0) || (cyclesLeftTimeout < 0); + } while (!done); + } // if (waveformEnabled) + + if (timer1CB) + { + nextEventCycles = min_u32(nextEventCycles, timer1CB()); } - // Check for toggles - int32_t cyclesToGo = wave->nextServiceCycle - now; - if (cyclesToGo < 0) { - waveformState ^= mask; - if (waveformState & mask) { - if (i == 16) { - GP16O |= 1; // GPIO16 write slow as it's RMW - } else { - SetGPIO(mask); - } - wave->nextServiceCycle = now + wave->nextTimeHighCycles; - nextEventCycles = min_u32(nextEventCycles, wave->nextTimeHighCycles); - } else { - if (i == 16) { - GP16O &= ~1; // GPIO16 write slow as it's RMW - } else { - ClearGPIO(mask); - } - wave->nextServiceCycle = now + wave->nextTimeLowCycles; - nextEventCycles = min_u32(nextEventCycles, wave->nextTimeLowCycles); - } - } else { - uint32_t deltaCycles = wave->nextServiceCycle - now; - nextEventCycles = min_u32(nextEventCycles, deltaCycles); + if (nextEventCycles < microsecondsToClockCycles(10)) + { + nextEventCycles = microsecondsToClockCycles(10); } - } - - // Exit the loop if we've hit the fixed runtime limit or the next event is known to be after that timeout would occur - uint32_t now = GetCycleCountIRQ(); - int32_t cycleDeltaNextEvent = timeoutCycle - (now + nextEventCycles); - int32_t cyclesLeftTimeout = timeoutCycle - now; - done = (cycleDeltaNextEvent < 0) || (cyclesLeftTimeout < 0); - } while (!done); - } // if (waveformEnabled) - - if (timer1CB) { - nextEventCycles = min_u32(nextEventCycles, timer1CB()); - } - - if (nextEventCycles < microsecondsToClockCycles(10)) { - nextEventCycles = microsecondsToClockCycles(10); - } - nextEventCycles -= DELTAIRQ; - - // Do it here instead of global function to save time and because we know it's edge-IRQ + nextEventCycles -= DELTAIRQ; + + // Do it here instead of global function to save time and because we know it's edge-IRQ #if F_CPU == 160000000 - T1L = nextEventCycles >> 1; // Already know we're in range by MAXIRQUS + T1L = nextEventCycles >> 1; // Already know we're in range by MAXIRQUS #else - T1L = nextEventCycles; // Already know we're in range by MAXIRQUS + T1L = nextEventCycles; // Already know we're in range by MAXIRQUS #endif - TEIE |= TEIE1; // Edge int enable -} + TEIE |= TEIE1; // Edge int enable + } }; diff --git a/cores/esp8266/core_esp8266_waveform.h b/cores/esp8266/core_esp8266_waveform.h index 24ce91fb36..b783410e9a 100644 --- a/cores/esp8266/core_esp8266_waveform.h +++ b/cores/esp8266/core_esp8266_waveform.h @@ -1,40 +1,40 @@ /* - esp8266_waveform - General purpose waveform generation and control, + esp8266_waveform - General purpose waveform generation and control, supporting outputs on all pins in parallel. - Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. + Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. - The core idea is to have a programmable waveform generator with a unique - high and low period (defined in microseconds). TIMER1 is set to 1-shot - mode and is always loaded with the time until the next edge of any live - waveforms. + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds). TIMER1 is set to 1-shot + mode and is always loaded with the time until the next edge of any live + waveforms. - Up to one waveform generator per pin supported. + Up to one waveform generator per pin supported. - Each waveform generator is synchronized to the ESP cycle counter, not the - timer. This allows for removing interrupt jitter and delay as the counter - always increments once per 80MHz clock. Changes to a waveform are - contiguous and only take effect on the next waveform transition, - allowing for smooth transitions. + Each waveform generator is synchronized to the ESP cycle counter, not the + timer. This allows for removing interrupt jitter and delay as the counter + always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. - This replaces older tone(), analogWrite(), and the Servo classes. + This replaces older tone(), analogWrite(), and the Servo classes. - Everywhere in the code where "cycles" is used, it means ESP.getCycleTime() - cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz). + Everywhere in the code where "cycles" is used, it means ESP.getCycleTime() + cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz). - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include diff --git a/cores/esp8266/core_esp8266_wiring.cpp b/cores/esp8266/core_esp8266_wiring.cpp index 247a02bbda..50befd45ee 100644 --- a/cores/esp8266/core_esp8266_wiring.cpp +++ b/cores/esp8266/core_esp8266_wiring.cpp @@ -1,23 +1,23 @@ -/* - core_esp8266_wiring.c - implementation of Wiring API for esp8266 - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ +/* + core_esp8266_wiring.c - implementation of Wiring API for esp8266 + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include "wiring_private.h" #include "ets_sys.h" @@ -27,191 +27,205 @@ extern "C" { -extern void ets_delay_us(uint32_t us); -extern void esp_schedule(); -extern void esp_yield(); + extern void ets_delay_us(uint32_t us); + extern void esp_schedule(); + extern void esp_yield(); -static os_timer_t delay_timer; -static os_timer_t micros_overflow_timer; -static uint32_t micros_at_last_overflow_tick = 0; -static uint32_t micros_overflow_count = 0; + static os_timer_t delay_timer; + static os_timer_t micros_overflow_timer; + static uint32_t micros_at_last_overflow_tick = 0; + static uint32_t micros_overflow_count = 0; #define ONCE 0 #define REPEAT 1 -void delay_end(void* arg) { - (void) arg; - esp_schedule(); -} - -void delay(unsigned long ms) { - if(ms) { - os_timer_setfn(&delay_timer, (os_timer_func_t*) &delay_end, 0); - os_timer_arm(&delay_timer, ms, ONCE); - } else { + void delay_end(void* arg) + { + (void) arg; esp_schedule(); } - esp_yield(); - if(ms) { - os_timer_disarm(&delay_timer); + + void delay(unsigned long ms) + { + if (ms) + { + os_timer_setfn(&delay_timer, (os_timer_func_t*) &delay_end, 0); + os_timer_arm(&delay_timer, ms, ONCE); + } + else + { + esp_schedule(); + } + esp_yield(); + if (ms) + { + os_timer_disarm(&delay_timer); + } } -} - -void micros_overflow_tick(void* arg) { - (void) arg; - uint32_t m = system_get_time(); - if(m < micros_at_last_overflow_tick) - ++micros_overflow_count; - micros_at_last_overflow_tick = m; -} - -//--------------------------------------------------------------------------- -// millis() 'magic multiplier' approximation -// -// This function corrects the cumlative (296us / usec overflow) drift -// seen in the orignal 'millis()' function. -// -// Input: -// 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF -// 'c' - 32-bit usec overflow counter 0 <= c < 0x00400000 -// Output: -// Returns milliseconds in modulo 0x1,0000,0000 (0 to 0xFFFFFFFF) -// -// Notes: -// -// 1) This routine approximates the 64-bit integer division, -// -// quotient = ( 2^32 c + m ) / 1000, -// -// through the use of 'magic' multipliers. A slow division is replaced by -// a faster multiply using a scaled multiplicative inverse of the divisor: -// -// quotient =~ ( 2^32 c + m ) * k, where k = Ceiling[ 2^n / 1000 ] -// -// The precision difference between multiplier and divisor sets the -// upper-bound of the dividend which can be successfully divided. -// -// For this application, n = 64, and the divisor (1000) has 10-bits of -// precision. This sets the dividend upper-bound to (64 - 10) = 54 bits, -// and that of 'c' to (54 - 32) = 22 bits. This corresponds to a value -// for 'c' = 0x0040,0000 , or +570 years of usec counter overflows. -// -// 2) A distributed multiply with offset-summing is used find k( 2^32 c + m ): -// -// prd = (2^32 kh + kl) * ( 2^32 c + m ) -// = 2^64 kh c + 2^32 kl c + 2^32 kh m + kl m -// (d) (c) (b) (a) -// -// Graphically, the offset-sums align in little endian like this: -// LS -> MS -// 32 64 96 128 -// | a[-1] | a[0] | a[1] | a[2] | -// | m kl | 0 | 0 | a[-1] not needed -// | | m kh | | -// | | c kl | | a[1] holds the result -// | | | c kh | a[2] can be discarded -// -// As only the high-word of 'm kl' and low-word of 'c kh' contribute to the -// overall result, only (2) 32-bit words are needed for the accumulator. -// -// 3) As C++ does not intrinsically test for addition overflows, one must -// code specifically to detect them. This approximation skips these -// overflow checks for speed, hence the sum, -// -// highword( m kl ) + m kh + c kl < (2^64-1), MUST NOT OVERFLOW. -// -// To meet this criteria, not only do we have to pick 'k' to achieve our -// desired precision, we also have to split 'k' appropriately to avoid -// any addition overflows. -// -// 'k' should be also chosen to align the various products on byte -// boundaries to avoid any 64-bit shifts before additions, as they incur -// major time penalties. The 'k' chosen for this specific division by 1000 -// was picked primarily to avoid shifts as well as for precision. -// -// For the reasons list above, this routine is NOT a general one. -// Changing divisors could break the overflow requirement and force -// picking a 'k' split which requires shifts before additions. -// -// ** Test THOROUGHLY after making changes ** -// -// 4) Results of time benchmarks run on an ESP8266 Huzzah feather are: -// -// usec x Orig Comment -// Orig: 3.18 1.00 Original code -// Corr: 13.21 4.15 64-bit reference code -// Test: 4.60 1.45 64-bit magic multiply, 4x32 -// -// The magic multiplier routine runs ~3x faster than the reference. Execution -// times can vary considerably with the numbers being multiplied, so one -// should derate this factor to around 2x, worst case. -// -// Reference function: corrected millis(), 64-bit arithmetic, -// truncated to 32-bits by return -// unsigned long ICACHE_RAM_ATTR millis_corr_DEBUG( void ) -// { -// // Get usec system time, usec overflow conter -// ...... -// return ( (c * 4294967296 + m) / 1000 ); // 64-bit division is SLOW -// } //millis_corr -// -// 5) See this link for a good discussion on magic multipliers: -// http://ridiculousfish.com/blog/posts/labor-of-division-episode-i.html -// + + void micros_overflow_tick(void* arg) + { + (void) arg; + uint32_t m = system_get_time(); + if (m < micros_at_last_overflow_tick) + { + ++micros_overflow_count; + } + micros_at_last_overflow_tick = m; + } + + //--------------------------------------------------------------------------- + // millis() 'magic multiplier' approximation + // + // This function corrects the cumlative (296us / usec overflow) drift + // seen in the orignal 'millis()' function. + // + // Input: + // 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF + // 'c' - 32-bit usec overflow counter 0 <= c < 0x00400000 + // Output: + // Returns milliseconds in modulo 0x1,0000,0000 (0 to 0xFFFFFFFF) + // + // Notes: + // + // 1) This routine approximates the 64-bit integer division, + // + // quotient = ( 2^32 c + m ) / 1000, + // + // through the use of 'magic' multipliers. A slow division is replaced by + // a faster multiply using a scaled multiplicative inverse of the divisor: + // + // quotient =~ ( 2^32 c + m ) * k, where k = Ceiling[ 2^n / 1000 ] + // + // The precision difference between multiplier and divisor sets the + // upper-bound of the dividend which can be successfully divided. + // + // For this application, n = 64, and the divisor (1000) has 10-bits of + // precision. This sets the dividend upper-bound to (64 - 10) = 54 bits, + // and that of 'c' to (54 - 32) = 22 bits. This corresponds to a value + // for 'c' = 0x0040,0000 , or +570 years of usec counter overflows. + // + // 2) A distributed multiply with offset-summing is used find k( 2^32 c + m ): + // + // prd = (2^32 kh + kl) * ( 2^32 c + m ) + // = 2^64 kh c + 2^32 kl c + 2^32 kh m + kl m + // (d) (c) (b) (a) + // + // Graphically, the offset-sums align in little endian like this: + // LS -> MS + // 32 64 96 128 + // | a[-1] | a[0] | a[1] | a[2] | + // | m kl | 0 | 0 | a[-1] not needed + // | | m kh | | + // | | c kl | | a[1] holds the result + // | | | c kh | a[2] can be discarded + // + // As only the high-word of 'm kl' and low-word of 'c kh' contribute to the + // overall result, only (2) 32-bit words are needed for the accumulator. + // + // 3) As C++ does not intrinsically test for addition overflows, one must + // code specifically to detect them. This approximation skips these + // overflow checks for speed, hence the sum, + // + // highword( m kl ) + m kh + c kl < (2^64-1), MUST NOT OVERFLOW. + // + // To meet this criteria, not only do we have to pick 'k' to achieve our + // desired precision, we also have to split 'k' appropriately to avoid + // any addition overflows. + // + // 'k' should be also chosen to align the various products on byte + // boundaries to avoid any 64-bit shifts before additions, as they incur + // major time penalties. The 'k' chosen for this specific division by 1000 + // was picked primarily to avoid shifts as well as for precision. + // + // For the reasons list above, this routine is NOT a general one. + // Changing divisors could break the overflow requirement and force + // picking a 'k' split which requires shifts before additions. + // + // ** Test THOROUGHLY after making changes ** + // + // 4) Results of time benchmarks run on an ESP8266 Huzzah feather are: + // + // usec x Orig Comment + // Orig: 3.18 1.00 Original code + // Corr: 13.21 4.15 64-bit reference code + // Test: 4.60 1.45 64-bit magic multiply, 4x32 + // + // The magic multiplier routine runs ~3x faster than the reference. Execution + // times can vary considerably with the numbers being multiplied, so one + // should derate this factor to around 2x, worst case. + // + // Reference function: corrected millis(), 64-bit arithmetic, + // truncated to 32-bits by return + // unsigned long ICACHE_RAM_ATTR millis_corr_DEBUG( void ) + // { + // // Get usec system time, usec overflow conter + // ...... + // return ( (c * 4294967296 + m) / 1000 ); // 64-bit division is SLOW + // } //millis_corr + // + // 5) See this link for a good discussion on magic multipliers: + // http://ridiculousfish.com/blog/posts/labor-of-division-episode-i.html + // #define MAGIC_1E3_wLO 0x4bc6a7f0 // LS part #define MAGIC_1E3_wHI 0x00418937 // MS part, magic multiplier -unsigned long ICACHE_RAM_ATTR millis() -{ - union { - uint64_t q; // Accumulator, 64-bit, little endian - uint32_t a[2]; // ..........., 32-bit segments - } acc; - acc.a[1] = 0; // Zero high-acc - - // Get usec system time, usec overflow counter - uint32_t m = system_get_time(); - uint32_t c = micros_overflow_count + - ((m < micros_at_last_overflow_tick) ? 1 : 0); - - // (a) Init. low-acc with high-word of 1st product. The right-shift - // falls on a byte boundary, hence is relatively quick. - - acc.q = ( (uint64_t)( m * (uint64_t)MAGIC_1E3_wLO ) >> 32 ); - - // (b) Offset sum, low-acc - acc.q += ( m * (uint64_t)MAGIC_1E3_wHI ); - - // (c) Offset sum, low-acc - acc.q += ( c * (uint64_t)MAGIC_1E3_wLO ); - - // (d) Truncated sum, high-acc - acc.a[1] += (uint32_t)( c * (uint64_t)MAGIC_1E3_wHI ); - - return ( acc.a[1] ); // Extract result, high-acc - -} //millis - -unsigned long ICACHE_RAM_ATTR micros() { - return system_get_time(); -} - -uint64_t ICACHE_RAM_ATTR micros64() { - uint32_t low32_us = system_get_time(); - uint32_t high32_us = micros_overflow_count + ((low32_us < micros_at_last_overflow_tick) ? 1 : 0); - uint64_t duration64_us = (uint64_t)high32_us << 32 | low32_us; - return duration64_us; -} - -void ICACHE_RAM_ATTR delayMicroseconds(unsigned int us) { - os_delay_us(us); -} - -void init() { - initPins(); - timer1_isr_init(); - os_timer_setfn(µs_overflow_timer, (os_timer_func_t*) µs_overflow_tick, 0); - os_timer_arm(µs_overflow_timer, 60000, REPEAT); -} + unsigned long ICACHE_RAM_ATTR millis() + { + union + { + uint64_t q; // Accumulator, 64-bit, little endian + uint32_t a[2]; // ..........., 32-bit segments + } acc; + acc.a[1] = 0; // Zero high-acc + + // Get usec system time, usec overflow counter + uint32_t m = system_get_time(); + uint32_t c = micros_overflow_count + + ((m < micros_at_last_overflow_tick) ? 1 : 0); + + // (a) Init. low-acc with high-word of 1st product. The right-shift + // falls on a byte boundary, hence is relatively quick. + + acc.q = ((uint64_t)(m * (uint64_t)MAGIC_1E3_wLO) >> 32); + + // (b) Offset sum, low-acc + acc.q += (m * (uint64_t)MAGIC_1E3_wHI); + + // (c) Offset sum, low-acc + acc.q += (c * (uint64_t)MAGIC_1E3_wLO); + + // (d) Truncated sum, high-acc + acc.a[1] += (uint32_t)(c * (uint64_t)MAGIC_1E3_wHI); + + return (acc.a[1]); // Extract result, high-acc + + } //millis + + unsigned long ICACHE_RAM_ATTR micros() + { + return system_get_time(); + } + + uint64_t ICACHE_RAM_ATTR micros64() + { + uint32_t low32_us = system_get_time(); + uint32_t high32_us = micros_overflow_count + ((low32_us < micros_at_last_overflow_tick) ? 1 : 0); + uint64_t duration64_us = (uint64_t)high32_us << 32 | low32_us; + return duration64_us; + } + + void ICACHE_RAM_ATTR delayMicroseconds(unsigned int us) + { + os_delay_us(us); + } + + void init() + { + initPins(); + timer1_isr_init(); + os_timer_setfn(µs_overflow_timer, (os_timer_func_t*) µs_overflow_tick, 0); + os_timer_arm(µs_overflow_timer, 60000, REPEAT); + } }; diff --git a/cores/esp8266/core_esp8266_wiring_analog.cpp b/cores/esp8266/core_esp8266_wiring_analog.cpp index 27c98ff3f0..d3e8e72ecd 100644 --- a/cores/esp8266/core_esp8266_wiring_analog.cpp +++ b/cores/esp8266/core_esp8266_wiring_analog.cpp @@ -1,25 +1,25 @@ /* - analog.c - analogRead implementation for esp8266 + analog.c - analogRead implementation for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - 18/06/2015 analogRead bugfix by Testato + 18/06/2015 analogRead bugfix by Testato */ #include "wiring_private.h" @@ -28,15 +28,16 @@ extern "C" { -extern int __analogRead(uint8_t pin) -{ - // accept both A0 constant and ADC channel number - if(pin == 17 || pin == 0) { - return system_adc_read(); + extern int __analogRead(uint8_t pin) + { + // accept both A0 constant and ADC channel number + if (pin == 17 || pin == 0) + { + return system_adc_read(); + } + return digitalRead(pin) * 1023; } - return digitalRead(pin) * 1023; -} -extern int analogRead(uint8_t pin) __attribute__ ((weak, alias("__analogRead"))); + extern int analogRead(uint8_t pin) __attribute__((weak, alias("__analogRead"))); }; diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index 0a8a7252f1..bcac7b7425 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -1,22 +1,22 @@ /* - digital.c - wiring digital implementation for esp8266 + digital.c - wiring digital implementation for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define ARDUINO_MAIN #include "wiring_private.h" @@ -29,220 +29,291 @@ extern "C" { -uint8_t esp8266_gpioToFn[16] = {0x34, 0x18, 0x38, 0x14, 0x3C, 0x40, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x04, 0x08, 0x0C, 0x10}; - -extern void __pinMode(uint8_t pin, uint8_t mode) { - if(pin < 16){ - if(mode == SPECIAL){ - GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) - GPEC = (1 << pin); //Disable - GPF(pin) = GPFFS(GPFFS_BUS(pin));//Set mode to BUS (RX0, TX0, TX1, SPI, HSPI or CLK depending in the pin) - if(pin == 3) GPF(pin) |= (1 << GPFPU);//enable pullup on RX - } else if(mode & FUNCTION_0){ - GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) - GPEC = (1 << pin); //Disable - GPF(pin) = GPFFS((mode >> 4) & 0x07); - if(pin == 13 && mode == FUNCTION_4) GPF(pin) |= (1 << GPFPU);//enable pullup on RX - } else if(mode == OUTPUT || mode == OUTPUT_OPEN_DRAIN){ - GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO - GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) - if(mode == OUTPUT_OPEN_DRAIN) GPC(pin) |= (1 << GPCD); - GPES = (1 << pin); //Enable - } else if(mode == INPUT || mode == INPUT_PULLUP){ - GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO - GPEC = (1 << pin); //Disable - GPC(pin) = (GPC(pin) & (0xF << GPCI)) | (1 << GPCD); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) - if(mode == INPUT_PULLUP) { - GPF(pin) |= (1 << GPFPU); // Enable Pullup - } - } else if(mode == WAKEUP_PULLUP || mode == WAKEUP_PULLDOWN){ - GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO - GPEC = (1 << pin); //Disable - if(mode == WAKEUP_PULLUP) { - GPF(pin) |= (1 << GPFPU); // Enable Pullup - GPC(pin) = (1 << GPCD) | (4 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(LOW) | WAKEUP_ENABLE(ENABLED) - } else { - GPF(pin) |= (1 << GPFPD); // Enable Pulldown - GPC(pin) = (1 << GPCD) | (5 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(HIGH) | WAKEUP_ENABLE(ENABLED) - } + uint8_t esp8266_gpioToFn[16] = {0x34, 0x18, 0x38, 0x14, 0x3C, 0x40, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x04, 0x08, 0x0C, 0x10}; + + extern void __pinMode(uint8_t pin, uint8_t mode) + { + if (pin < 16) + { + if (mode == SPECIAL) + { + GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + GPEC = (1 << pin); //Disable + GPF(pin) = GPFFS(GPFFS_BUS(pin));//Set mode to BUS (RX0, TX0, TX1, SPI, HSPI or CLK depending in the pin) + if (pin == 3) + { + GPF(pin) |= (1 << GPFPU); //enable pullup on RX + } + } + else if (mode & FUNCTION_0) + { + GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + GPEC = (1 << pin); //Disable + GPF(pin) = GPFFS((mode >> 4) & 0x07); + if (pin == 13 && mode == FUNCTION_4) + { + GPF(pin) |= (1 << GPFPU); //enable pullup on RX + } + } + else if (mode == OUTPUT || mode == OUTPUT_OPEN_DRAIN) + { + GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + if (mode == OUTPUT_OPEN_DRAIN) + { + GPC(pin) |= (1 << GPCD); + } + GPES = (1 << pin); //Enable + } + else if (mode == INPUT || mode == INPUT_PULLUP) + { + GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPEC = (1 << pin); //Disable + GPC(pin) = (GPC(pin) & (0xF << GPCI)) | (1 << GPCD); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + if (mode == INPUT_PULLUP) + { + GPF(pin) |= (1 << GPFPU); // Enable Pullup + } + } + else if (mode == WAKEUP_PULLUP || mode == WAKEUP_PULLDOWN) + { + GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPEC = (1 << pin); //Disable + if (mode == WAKEUP_PULLUP) + { + GPF(pin) |= (1 << GPFPU); // Enable Pullup + GPC(pin) = (1 << GPCD) | (4 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(LOW) | WAKEUP_ENABLE(ENABLED) + } + else + { + GPF(pin) |= (1 << GPFPD); // Enable Pulldown + GPC(pin) = (1 << GPCD) | (5 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(HIGH) | WAKEUP_ENABLE(ENABLED) + } + } + } + else if (pin == 16) + { + GPF16 = GP16FFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPC16 = 0; + if (mode == INPUT || mode == INPUT_PULLDOWN_16) + { + if (mode == INPUT_PULLDOWN_16) + { + GPF16 |= (1 << GP16FPD);//Enable Pulldown + } + GP16E &= ~1; + } + else if (mode == OUTPUT) + { + GP16E |= 1; + } + } } - } else if(pin == 16){ - GPF16 = GP16FFS(GPFFS_GPIO(pin));//Set mode to GPIO - GPC16 = 0; - if(mode == INPUT || mode == INPUT_PULLDOWN_16){ - if(mode == INPUT_PULLDOWN_16){ - GPF16 |= (1 << GP16FPD);//Enable Pulldown - } - GP16E &= ~1; - } else if(mode == OUTPUT){ - GP16E |= 1; + + extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) + { + stopWaveform(pin); + if (pin < 16) + { + if (val) + { + GPOS = (1 << pin); + } + else + { + GPOC = (1 << pin); + } + } + else if (pin == 16) + { + if (val) + { + GP16O |= 1; + } + else + { + GP16O &= ~1; + } + } } - } -} - -extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) { - stopWaveform(pin); - if(pin < 16){ - if(val) GPOS = (1 << pin); - else GPOC = (1 << pin); - } else if(pin == 16){ - if(val) GP16O |= 1; - else GP16O &= ~1; - } -} - -extern int ICACHE_RAM_ATTR __digitalRead(uint8_t pin) { - if(pin < 16){ - return GPIP(pin); - } else if(pin == 16){ - return GP16I & 0x01; - } - return 0; -} -/* - GPIO INTERRUPTS -*/ + extern int ICACHE_RAM_ATTR __digitalRead(uint8_t pin) + { + if (pin < 16) + { + return GPIP(pin); + } + else if (pin == 16) + { + return GP16I & 0x01; + } + return 0; + } + + /* + GPIO INTERRUPTS + */ + + typedef void (*voidFuncPtr)(void); + typedef void (*voidFuncPtrArg)(void*); + + typedef struct + { + uint8_t mode; + void (*fn)(void); + void * arg; + } interrupt_handler_t; + + //duplicate from functionalInterrupt.h keep in sync + typedef struct InterruptInfo + { + uint8_t pin; + uint8_t value; + uint32_t micro; + } InterruptInfo; + + typedef struct + { + InterruptInfo* interruptInfo; + void* functionInfo; + } ArgStructure; + + static interrupt_handler_t interrupt_handlers[16]; + static uint32_t interrupt_reg = 0; + + void ICACHE_RAM_ATTR interrupt_handler(void *arg) + { + (void) arg; + uint32_t status = GPIE; + GPIEC = status;//clear them interrupts + uint32_t levels = GPI; + if (status == 0 || interrupt_reg == 0) + { + return; + } + ETS_GPIO_INTR_DISABLE(); + int i = 0; + uint32_t changedbits = status & interrupt_reg; + while (changedbits) + { + while (!(changedbits & (1 << i))) + { + i++; + } + changedbits &= ~(1 << i); + interrupt_handler_t *handler = &interrupt_handlers[i]; + if (handler->fn && + (handler->mode == CHANGE || + (handler->mode & 1) == !!(levels & (1 << i)))) + { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + uint32_t savedPS = xt_rsil(15); // stop other interrupts + ArgStructure* localArg = (ArgStructure*)handler->arg; + if (localArg && localArg->interruptInfo) + { + localArg->interruptInfo->pin = i; + localArg->interruptInfo->value = __digitalRead(i); + localArg->interruptInfo->micro = micros(); + } + if (handler->arg) + { + ((voidFuncPtrArg)handler->fn)(handler->arg); + } + else + { + handler->fn(); + } + xt_wsr_ps(savedPS); + } + } + ETS_GPIO_INTR_ENABLE(); + } + + extern void cleanupFunctional(void* arg); + + extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void *arg, int mode) + { + + // #5780 + // https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map + if ((uint32_t)userFunc >= 0x40200000) + { + // ISR not in IRAM + ::printf((PGM_P)F("ISR not in IRAM!\r\n")); + abort(); + } + + if (pin < 16) + { + ETS_GPIO_INTR_DISABLE(); + interrupt_handler_t *handler = &interrupt_handlers[pin]; + handler->mode = mode; + handler->fn = userFunc; + if (handler->arg) // Clean when new attach without detach + { + cleanupFunctional(handler->arg); + } + handler->arg = arg; + interrupt_reg |= (1 << pin); + GPC(pin) &= ~(0xF << GPCI);//INT mode disabled + GPIEC = (1 << pin); //Clear Interrupt for this pin + GPC(pin) |= ((mode & 0xF) << GPCI);//INT mode "mode" + ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg); + ETS_GPIO_INTR_ENABLE(); + } + } -typedef void (*voidFuncPtr)(void); -typedef void (*voidFuncPtrArg)(void*); - -typedef struct { - uint8_t mode; - void (*fn)(void); - void * arg; -} interrupt_handler_t; - -//duplicate from functionalInterrupt.h keep in sync -typedef struct InterruptInfo { - uint8_t pin; - uint8_t value; - uint32_t micro; -} InterruptInfo; - -typedef struct { - InterruptInfo* interruptInfo; - void* functionInfo; -} ArgStructure; - -static interrupt_handler_t interrupt_handlers[16]; -static uint32_t interrupt_reg = 0; - -void ICACHE_RAM_ATTR interrupt_handler(void *arg) { - (void) arg; - uint32_t status = GPIE; - GPIEC = status;//clear them interrupts - uint32_t levels = GPI; - if(status == 0 || interrupt_reg == 0) return; - ETS_GPIO_INTR_DISABLE(); - int i = 0; - uint32_t changedbits = status & interrupt_reg; - while(changedbits){ - while(!(changedbits & (1 << i))) i++; - changedbits &= ~(1 << i); - interrupt_handler_t *handler = &interrupt_handlers[i]; - if (handler->fn && - (handler->mode == CHANGE || - (handler->mode & 1) == !!(levels & (1 << i)))) { - // to make ISR compatible to Arduino AVR model where interrupts are disabled - // we disable them before we call the client ISR - uint32_t savedPS = xt_rsil(15); // stop other interrupts - ArgStructure* localArg = (ArgStructure*)handler->arg; - if (localArg && localArg->interruptInfo) - { - localArg->interruptInfo->pin = i; - localArg->interruptInfo->value = __digitalRead(i); - localArg->interruptInfo->micro = micros(); - } - if (handler->arg) - { - ((voidFuncPtrArg)handler->fn)(handler->arg); - } - else - { - handler->fn(); - } - xt_wsr_ps(savedPS); + extern void ICACHE_RAM_ATTR __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode) + { + __attachInterruptArg(pin, userFunc, 0, mode); } - } - ETS_GPIO_INTR_ENABLE(); -} - -extern void cleanupFunctional(void* arg); - -extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void *arg, int mode) { - - // #5780 - // https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map - if ((uint32_t)userFunc >= 0x40200000) - { - // ISR not in IRAM - ::printf((PGM_P)F("ISR not in IRAM!\r\n")); - abort(); - } - - if(pin < 16) { - ETS_GPIO_INTR_DISABLE(); - interrupt_handler_t *handler = &interrupt_handlers[pin]; - handler->mode = mode; - handler->fn = userFunc; - if (handler->arg) // Clean when new attach without detach - { - cleanupFunctional(handler->arg); - } - handler->arg = arg; - interrupt_reg |= (1 << pin); - GPC(pin) &= ~(0xF << GPCI);//INT mode disabled - GPIEC = (1 << pin); //Clear Interrupt for this pin - GPC(pin) |= ((mode & 0xF) << GPCI);//INT mode "mode" - ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg); - ETS_GPIO_INTR_ENABLE(); - } -} - -extern void ICACHE_RAM_ATTR __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode ) -{ - __attachInterruptArg (pin, userFunc, 0, mode); -} - -extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) { - if(pin < 16) { - ETS_GPIO_INTR_DISABLE(); - GPC(pin) &= ~(0xF << GPCI);//INT mode disabled - GPIEC = (1 << pin); //Clear Interrupt for this pin - interrupt_reg &= ~(1 << pin); - interrupt_handler_t *handler = &interrupt_handlers[pin]; - handler->mode = 0; - handler->fn = 0; - if (handler->arg) - { - cleanupFunctional(handler->arg); - } - handler->arg = 0; - if (interrupt_reg) - ETS_GPIO_INTR_ENABLE(); - } -} - -void initPins() { - //Disable UART interrupts - system_set_os_print(0); - U0IE = 0; - U1IE = 0; - - for (int i = 0; i <= 5; ++i) { - pinMode(i, INPUT); - } - // pins 6-11 are used for the SPI flash interface - for (int i = 12; i <= 16; ++i) { - pinMode(i, INPUT); - } -} - -extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pinMode"))); -extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite"))); -extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead"))); -extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt"))); -extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt"))); + + extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) + { + if (pin < 16) + { + ETS_GPIO_INTR_DISABLE(); + GPC(pin) &= ~(0xF << GPCI);//INT mode disabled + GPIEC = (1 << pin); //Clear Interrupt for this pin + interrupt_reg &= ~(1 << pin); + interrupt_handler_t *handler = &interrupt_handlers[pin]; + handler->mode = 0; + handler->fn = 0; + if (handler->arg) + { + cleanupFunctional(handler->arg); + } + handler->arg = 0; + if (interrupt_reg) + { + ETS_GPIO_INTR_ENABLE(); + } + } + } + + void initPins() + { + //Disable UART interrupts + system_set_os_print(0); + U0IE = 0; + U1IE = 0; + + for (int i = 0; i <= 5; ++i) + { + pinMode(i, INPUT); + } + // pins 6-11 are used for the SPI flash interface + for (int i = 12; i <= 16; ++i) + { + pinMode(i, INPUT); + } + } + + extern void pinMode(uint8_t pin, uint8_t mode) __attribute__((weak, alias("__pinMode"))); + extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__((weak, alias("__digitalWrite"))); + extern int digitalRead(uint8_t pin) __attribute__((weak, alias("__digitalRead"))); + extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__((weak, alias("__attachInterrupt"))); + extern void detachInterrupt(uint8_t pin) __attribute__((weak, alias("__detachInterrupt"))); }; diff --git a/cores/esp8266/core_esp8266_wiring_pulse.cpp b/cores/esp8266/core_esp8266_wiring_pulse.cpp index 8e124ac312..00d95141d8 100644 --- a/cores/esp8266/core_esp8266_wiring_pulse.cpp +++ b/cores/esp8266/core_esp8266_wiring_pulse.cpp @@ -1,22 +1,22 @@ /* - pulse.c - wiring pulseIn implementation for esp8266 + pulse.c - wiring pulseIn implementation for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "wiring_private.h" @@ -24,7 +24,7 @@ extern "C" { -extern uint32_t xthal_get_ccount(); + extern uint32_t xthal_get_ccount(); #define WAIT_FOR_PIN_STATE(state) \ while (digitalRead(pin) != (state)) { \ @@ -34,25 +34,26 @@ extern uint32_t xthal_get_ccount(); optimistic_yield(5000); \ } -// max timeout is 27 seconds at 160MHz clock and 54 seconds at 80MHz clock -unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) -{ - const uint32_t max_timeout_us = clockCyclesToMicroseconds(UINT_MAX); - if (timeout > max_timeout_us) { - timeout = max_timeout_us; + // max timeout is 27 seconds at 160MHz clock and 54 seconds at 80MHz clock + unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) + { + const uint32_t max_timeout_us = clockCyclesToMicroseconds(UINT_MAX); + if (timeout > max_timeout_us) + { + timeout = max_timeout_us; + } + const uint32_t timeout_cycles = microsecondsToClockCycles(timeout); + const uint32_t start_cycle_count = xthal_get_ccount(); + WAIT_FOR_PIN_STATE(!state); + WAIT_FOR_PIN_STATE(state); + const uint32_t pulse_start_cycle_count = xthal_get_ccount(); + WAIT_FOR_PIN_STATE(!state); + return clockCyclesToMicroseconds(xthal_get_ccount() - pulse_start_cycle_count); + } + + unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout) + { + return pulseIn(pin, state, timeout); } - const uint32_t timeout_cycles = microsecondsToClockCycles(timeout); - const uint32_t start_cycle_count = xthal_get_ccount(); - WAIT_FOR_PIN_STATE(!state); - WAIT_FOR_PIN_STATE(state); - const uint32_t pulse_start_cycle_count = xthal_get_ccount(); - WAIT_FOR_PIN_STATE(!state); - return clockCyclesToMicroseconds(xthal_get_ccount() - pulse_start_cycle_count); -} - -unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout) -{ - return pulseIn(pin, state, timeout); -} }; diff --git a/cores/esp8266/core_esp8266_wiring_pwm.cpp b/cores/esp8266/core_esp8266_wiring_pwm.cpp index 5a3481bbd8..ef7881768e 100644 --- a/cores/esp8266/core_esp8266_wiring_pwm.cpp +++ b/cores/esp8266/core_esp8266_wiring_pwm.cpp @@ -1,24 +1,24 @@ /* - pwm.c - analogWrite implementation for esp8266 + pwm.c - analogWrite implementation for esp8266 - Use the shared TIMER1 utilities to generate PWM signals + Use the shared TIMER1 utilities to generate PWM signals - Original Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Original Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include @@ -26,56 +26,75 @@ extern "C" { -static uint32_t analogMap = 0; -static int32_t analogScale = PWMRANGE; -static uint16_t analogFreq = 1000; + static uint32_t analogMap = 0; + static int32_t analogScale = PWMRANGE; + static uint16_t analogFreq = 1000; -extern void __analogWriteRange(uint32_t range) { - if (range > 0) { - analogScale = range; - } -} + extern void __analogWriteRange(uint32_t range) + { + if (range > 0) + { + analogScale = range; + } + } -extern void __analogWriteFreq(uint32_t freq) { - if (freq < 100) { - analogFreq = 100; - } else if (freq > 40000) { - analogFreq = 40000; - } else { - analogFreq = freq; - } -} + extern void __analogWriteFreq(uint32_t freq) + { + if (freq < 100) + { + analogFreq = 100; + } + else if (freq > 40000) + { + analogFreq = 40000; + } + else + { + analogFreq = freq; + } + } -extern void __analogWrite(uint8_t pin, int val) { - if (pin > 16) { - return; - } - uint32_t analogPeriod = 1000000L / analogFreq; - if (val < 0) { - val = 0; - } else if (val > analogScale) { - val = analogScale; - } + extern void __analogWrite(uint8_t pin, int val) + { + if (pin > 16) + { + return; + } + uint32_t analogPeriod = 1000000L / analogFreq; + if (val < 0) + { + val = 0; + } + else if (val > analogScale) + { + val = analogScale; + } - analogMap &= ~(1 << pin); - uint32_t high = (analogPeriod * val) / analogScale; - uint32_t low = analogPeriod - high; - pinMode(pin, OUTPUT); - if (low == 0) { - stopWaveform(pin); - digitalWrite(pin, HIGH); - } else if (high == 0) { - stopWaveform(pin); - digitalWrite(pin, LOW); - } else { - if (startWaveform(pin, high, low, 0)) { - analogMap |= (1 << pin); + analogMap &= ~(1 << pin); + uint32_t high = (analogPeriod * val) / analogScale; + uint32_t low = analogPeriod - high; + pinMode(pin, OUTPUT); + if (low == 0) + { + stopWaveform(pin); + digitalWrite(pin, HIGH); + } + else if (high == 0) + { + stopWaveform(pin); + digitalWrite(pin, LOW); + } + else + { + if (startWaveform(pin, high, low, 0)) + { + analogMap |= (1 << pin); + } + } } - } -} -extern void analogWrite(uint8_t pin, int val) __attribute__((weak, alias("__analogWrite"))); -extern void analogWriteFreq(uint32_t freq) __attribute__((weak, alias("__analogWriteFreq"))); -extern void analogWriteRange(uint32_t range) __attribute__((weak, alias("__analogWriteRange"))); + extern void analogWrite(uint8_t pin, int val) __attribute__((weak, alias("__analogWrite"))); + extern void analogWriteFreq(uint32_t freq) __attribute__((weak, alias("__analogWriteFreq"))); + extern void analogWriteRange(uint32_t range) __attribute__((weak, alias("__analogWriteRange"))); }; diff --git a/cores/esp8266/core_esp8266_wiring_shift.cpp b/cores/esp8266/core_esp8266_wiring_shift.cpp index a5675c44f4..91722d3d68 100644 --- a/cores/esp8266/core_esp8266_wiring_shift.cpp +++ b/cores/esp8266/core_esp8266_wiring_shift.cpp @@ -1,60 +1,72 @@ /* - wiring_shift.c - shiftOut() function - Part of Arduino - http://www.arduino.cc/ + wiring_shift.c - shiftOut() function + Part of Arduino - http://www.arduino.cc/ - Copyright (c) 2005-2006 David A. Mellis + Copyright (c) 2005-2006 David A. Mellis - Note: file renamed with a core_esp8266_ prefix to simplify linker - script rules for moving code into irom0_text section. + Note: file renamed with a core_esp8266_ prefix to simplify linker + script rules for moving code into irom0_text section. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA - $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $ - */ + $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $ +*/ #include "wiring_private.h" extern "C" { -uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) { - uint8_t value = 0; - uint8_t i; - - for(i = 0; i < 8; ++i) { - digitalWrite(clockPin, HIGH); - if(bitOrder == LSBFIRST) - value |= digitalRead(dataPin) << i; - else - value |= digitalRead(dataPin) << (7 - i); - digitalWrite(clockPin, LOW); + uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) + { + uint8_t value = 0; + uint8_t i; + + for (i = 0; i < 8; ++i) + { + digitalWrite(clockPin, HIGH); + if (bitOrder == LSBFIRST) + { + value |= digitalRead(dataPin) << i; + } + else + { + value |= digitalRead(dataPin) << (7 - i); + } + digitalWrite(clockPin, LOW); + } + return value; } - return value; -} -void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val) { - uint8_t i; + void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val) + { + uint8_t i; - for(i = 0; i < 8; i++) { - if(bitOrder == LSBFIRST) - digitalWrite(dataPin, !!(val & (1 << i))); - else - digitalWrite(dataPin, !!(val & (1 << (7 - i)))); + for (i = 0; i < 8; i++) + { + if (bitOrder == LSBFIRST) + { + digitalWrite(dataPin, !!(val & (1 << i))); + } + else + { + digitalWrite(dataPin, !!(val & (1 << (7 - i)))); + } - digitalWrite(clockPin, HIGH); - digitalWrite(clockPin, LOW); + digitalWrite(clockPin, HIGH); + digitalWrite(clockPin, LOW); + } } -} }; diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index 3ac87eee9f..901f5e0325 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -15,11 +15,11 @@ extern bool timeshift64_is_set; void esp_yield(); void esp_schedule(); -void tune_timeshift64 (uint64_t now_us); -void settimeofday_cb (void (*cb)(void)); -void disable_extra4k_at_link_time (void) __attribute__((noinline)); +void tune_timeshift64(uint64_t now_us); +void settimeofday_cb(void (*cb)(void)); +void disable_extra4k_at_link_time(void) __attribute__((noinline)); -uint32_t sqrt32 (uint32_t n); +uint32_t sqrt32(uint32_t n); #ifdef __cplusplus } diff --git a/cores/esp8266/debug.cpp b/cores/esp8266/debug.cpp index d8bf8bb11d..84abdcfa38 100644 --- a/cores/esp8266/debug.cpp +++ b/cores/esp8266/debug.cpp @@ -1,36 +1,39 @@ -/* - debug.cpp - debug helper functions - Copyright (c) 2015 Markus Sattler. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "Arduino.h" -#include "debug.h" - -void ICACHE_RAM_ATTR hexdump(const void *mem, uint32_t len, uint8_t cols) { - const uint8_t* src = (const uint8_t*) mem; - os_printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); - for(uint32_t i = 0; i < len; i++) { - if(i % cols == 0) { - os_printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); - yield(); - } - os_printf("%02X ", *src); - src++; - } - os_printf("\n"); -} +/* + debug.cpp - debug helper functions + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "Arduino.h" +#include "debug.h" + +void ICACHE_RAM_ATTR hexdump(const void *mem, uint32_t len, uint8_t cols) +{ + const uint8_t* src = (const uint8_t*) mem; + os_printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for (uint32_t i = 0; i < len; i++) + { + if (i % cols == 0) + { + os_printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + yield(); + } + os_printf("%02X ", *src); + src++; + } + os_printf("\n"); +} diff --git a/cores/esp8266/eboot_command.h b/cores/esp8266/eboot_command.h index 3d854afba3..9866944c7a 100644 --- a/cores/esp8266/eboot_command.h +++ b/cores/esp8266/eboot_command.h @@ -9,7 +9,8 @@ extern "C" { #define RTC_MEM ((volatile uint32_t*)0x60001200) -enum action_t { +enum action_t +{ ACTION_COPY_RAW = 0x00000001, ACTION_LOAD_APP = 0xffffffff }; @@ -17,7 +18,8 @@ enum action_t { #define EBOOT_MAGIC 0xeb001000 #define EBOOT_MAGIC_MASK 0xfffff000 -struct eboot_command { +struct eboot_command +{ uint32_t magic; enum action_t action; uint32_t args[29]; diff --git a/cores/esp8266/esp8266_peri.h b/cores/esp8266/esp8266_peri.h index 15a596aa6c..38bfcfb802 100644 --- a/cores/esp8266/esp8266_peri.h +++ b/cores/esp8266/esp8266_peri.h @@ -1,22 +1,22 @@ -/* - esp8266_peri.h - Peripheral registers exposed in more AVR style for esp8266 - - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +/* + esp8266_peri.h - Peripheral registers exposed in more AVR style for esp8266 + + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ESP8266_PERI_H_INCLUDED #define ESP8266_PERI_H_INCLUDED @@ -842,8 +842,8 @@ extern uint8_t esp8266_gpioToFn[16]; #define I2STXCM (0) //I2S_TX_CHAN_MOD_S /** - Random Number Generator 32bit - http://esp8266-re.foogod.com/wiki/Random_Number_Generator + Random Number Generator 32bit + http://esp8266-re.foogod.com/wiki/Random_Number_Generator **/ #define RANDOM_REG32 ESP8266_DREG(0x20E44) diff --git a/cores/esp8266/flash_utils.h b/cores/esp8266/flash_utils.h index eade691a5b..0038d8540e 100644 --- a/cores/esp8266/flash_utils.h +++ b/cores/esp8266/flash_utils.h @@ -1,20 +1,20 @@ /* - flash_utils.h - Flash access function and data structures - Copyright (c) 2015 Ivan Grokhotkov. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + flash_utils.h - Flash access function and data structures + Copyright (c) 2015 Ivan Grokhotkov. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -26,10 +26,10 @@ extern "C" { #endif -/* Definitions are placed in eboot. Include them here rather than duplicate them. - * Also, prefer to have eboot standalone as much as possible and have the core depend on it - * rather than have eboot depend on the core. - */ +/* Definitions are placed in eboot. Include them here rather than duplicate them. + Also, prefer to have eboot standalone as much as possible and have the core depend on it + rather than have eboot depend on the core. +*/ #include <../../bootloaders/eboot/flash.h> diff --git a/cores/esp8266/gdb_hooks.cpp b/cores/esp8266/gdb_hooks.cpp index 3516712c9f..ed46821e3c 100644 --- a/cores/esp8266/gdb_hooks.cpp +++ b/cores/esp8266/gdb_hooks.cpp @@ -1,44 +1,44 @@ /* - gdb_hooks.c - Default (no-op) hooks for GDB Stub library - Copyright (c) 2018 Ivan Grokhotkov. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + gdb_hooks.c - Default (no-op) hooks for GDB Stub library + Copyright (c) 2018 Ivan Grokhotkov. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ets_sys.h" #include "gdb_hooks.h" -/* gdb_init and gdb_do_break do not return anything, but since the return - value is in register, it doesn't hurt to return a bool, so that the - same stub can be used for gdb_present. */ +/* gdb_init and gdb_do_break do not return anything, but since the return + value is in register, it doesn't hurt to return a bool, so that the + same stub can be used for gdb_present. */ extern "C" { -static bool ICACHE_RAM_ATTR __gdb_no_op() -{ - return false; -} - -void gdb_init(void) __attribute__ ((weak, alias("__gdb_no_op"))); -void gdb_do_break(void) __attribute__ ((weak, alias("__gdb_no_op"))); -bool gdb_present(void) __attribute__ ((weak, alias("__gdb_no_op"))); -bool gdbstub_has_putc1_control(void) __attribute__ ((weak, alias("__gdb_no_op"))); -void gdbstub_set_putc1_callback(void (*func)(char)) __attribute__ ((weak, alias("__gdb_no_op"))); -bool gdbstub_has_uart_isr_control(void) __attribute__ ((weak, alias("__gdb_no_op"))); -void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg) __attribute__ ((weak, alias("__gdb_no_op"))); -void gdbstub_write_char(char c) __attribute__ ((weak, alias("__gdb_no_op"))); -void gdbstub_write(const char* buf, size_t size) __attribute__ ((weak, alias("__gdb_no_op"))); + static bool ICACHE_RAM_ATTR __gdb_no_op() + { + return false; + } + + void gdb_init(void) __attribute__((weak, alias("__gdb_no_op"))); + void gdb_do_break(void) __attribute__((weak, alias("__gdb_no_op"))); + bool gdb_present(void) __attribute__((weak, alias("__gdb_no_op"))); + bool gdbstub_has_putc1_control(void) __attribute__((weak, alias("__gdb_no_op"))); + void gdbstub_set_putc1_callback(void (*func)(char)) __attribute__((weak, alias("__gdb_no_op"))); + bool gdbstub_has_uart_isr_control(void) __attribute__((weak, alias("__gdb_no_op"))); + void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg) __attribute__((weak, alias("__gdb_no_op"))); + void gdbstub_write_char(char c) __attribute__((weak, alias("__gdb_no_op"))); + void gdbstub_write(const char* buf, size_t size) __attribute__((weak, alias("__gdb_no_op"))); }; diff --git a/cores/esp8266/gdb_hooks.h b/cores/esp8266/gdb_hooks.h index 4953216a1a..d519180759 100644 --- a/cores/esp8266/gdb_hooks.h +++ b/cores/esp8266/gdb_hooks.h @@ -1,20 +1,20 @@ /* - gdb_hooks.h - Hooks for GDB Stub library - Copyright (c) 2018 Ivan Grokhotkov. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + gdb_hooks.h - Hooks for GDB Stub library + Copyright (c) 2018 Ivan Grokhotkov. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -24,97 +24,97 @@ extern "C" { #endif /** - * @brief Initialize GDB stub, if present - * - * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and does necessary initialization of that library. - * Called early at startup. - */ + @brief Initialize GDB stub, if present + + By default, this function is a no-op. When GDBStub library is linked, + this function is overriden and does necessary initialization of that library. + Called early at startup. +*/ void gdb_init(void); /** - * @brief Break into GDB, if present - * - * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and triggers entry into the debugger, which - * looks like a breakpoint hit. - */ + @brief Break into GDB, if present + + By default, this function is a no-op. When GDBStub library is linked, + this function is overriden and triggers entry into the debugger, which + looks like a breakpoint hit. +*/ void gdb_do_break(void); /** - * @brief Check if GDB stub is present. - * - * By default, this function returns false. When GDBStub library is linked, - * this function is overriden and returns true. Can be used to check whether - * GDB is used. - * - * @return true if GDB stub is present - */ + @brief Check if GDB stub is present. + + By default, this function returns false. When GDBStub library is linked, + this function is overriden and returns true. Can be used to check whether + GDB is used. + + @return true if GDB stub is present +*/ bool gdb_present(void); // If gdbstub has these set true, then we will disable our own // usage of them, but use gdbstub's callbacks for them instead /** - * @brief Check if GDB is installing a putc1 callback. - * - * By default, this function returns false. When GDBStub library is linked, - * this function is overriden and returns true. - * - * @return true if GDB is installing a putc1 callback - */ + @brief Check if GDB is installing a putc1 callback. + + By default, this function returns false. When GDBStub library is linked, + this function is overriden and returns true. + + @return true if GDB is installing a putc1 callback +*/ bool gdbstub_has_putc1_control(void); /** - * @brief Register a putc1 callback with GDB. - * @param func function GDB will proxy putc1 data to - * - * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and sets GDB stub's secondary putc1 callback to - * func. When GDB stub is linked, but a GDB session is not current attached, - * then GDB stub will pass putc1 chars directly to this function. - */ + @brief Register a putc1 callback with GDB. + @param func function GDB will proxy putc1 data to + + By default, this function is a no-op. When GDBStub library is linked, + this function is overriden and sets GDB stub's secondary putc1 callback to + func. When GDB stub is linked, but a GDB session is not current attached, + then GDB stub will pass putc1 chars directly to this function. +*/ void gdbstub_set_putc1_callback(void (*func)(char)); /** - * @brief Check if GDB is installing a uart0 isr callback. - * - * By default, this function returns false. When GDBStub library is linked, - * this function is overriden and returns true. - * - * @return true if GDB is installing a uart0 isr callback - */ + @brief Check if GDB is installing a uart0 isr callback. + + By default, this function returns false. When GDBStub library is linked, + this function is overriden and returns true. + + @return true if GDB is installing a uart0 isr callback +*/ bool gdbstub_has_uart_isr_control(void); /** - * @brief Register a uart0 isr callback with GDB. - * @param func function GDB will proxy uart0 isr data to - * - * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and sets GDB stub's secondary uart0 isr callback - * to func. When GDB stub is linked, but a GDB session is not current attached, - * then GDB stub will pass uart0 isr data back to this function. - */ + @brief Register a uart0 isr callback with GDB. + @param func function GDB will proxy uart0 isr data to + + By default, this function is a no-op. When GDBStub library is linked, + this function is overriden and sets GDB stub's secondary uart0 isr callback + to func. When GDB stub is linked, but a GDB session is not current attached, + then GDB stub will pass uart0 isr data back to this function. +*/ void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg); /** - * @brief Write a character for output to a GDB session on uart0. - * @param c character to write - * - * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and writes a char to either the GDB session on - * uart0 or directly to uart0 if not GDB session is attached. - */ + @brief Write a character for output to a GDB session on uart0. + @param c character to write + + By default, this function is a no-op. When GDBStub library is linked, + this function is overriden and writes a char to either the GDB session on + uart0 or directly to uart0 if not GDB session is attached. +*/ void gdbstub_write_char(char c); /** - * @brief Write a char buffer for output to a GDB session on uart0. - * @param buf buffer of data to write - * @param size length of buffer - * - * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and writes a buffer to either the GDB session on - * uart0 or directly to uart0 if not GDB session is attached. - */ + @brief Write a char buffer for output to a GDB session on uart0. + @param buf buffer of data to write + @param size length of buffer + + By default, this function is a no-op. When GDBStub library is linked, + this function is overriden and writes a buffer to either the GDB session on + uart0 or directly to uart0 if not GDB session is attached. +*/ void gdbstub_write(const char* buf, size_t size); #ifdef __cplusplus diff --git a/cores/esp8266/heap.cpp b/cores/esp8266/heap.cpp index b0bd908614..daa49930bb 100644 --- a/cores/esp8266/heap.cpp +++ b/cores/esp8266/heap.cpp @@ -1,7 +1,7 @@ -/* heap.c - overrides of SDK heap handling functions - * Copyright (c) 2016 Ivan Grokhotkov. All rights reserved. - * This file is distributed under MIT license. - */ +/* heap.c - overrides of SDK heap handling functions + Copyright (c) 2016 Ivan Grokhotkov. All rights reserved. + This file is distributed under MIT license. +*/ #include #include "umm_malloc/umm_malloc.h" @@ -10,186 +10,201 @@ extern "C" { -// Debugging helper, last allocation which returned NULL -void *umm_last_fail_alloc_addr = NULL; -int umm_last_fail_alloc_size = 0; - -void* _malloc_r(struct _reent* unused, size_t size) -{ - (void) unused; - void *ret = malloc(size); - if (0 != size && 0 == ret) { - umm_last_fail_alloc_addr = __builtin_return_address(0); - umm_last_fail_alloc_size = size; - } - return ret; -} - -void _free_r(struct _reent* unused, void* ptr) -{ - (void) unused; - return free(ptr); -} - -void* _realloc_r(struct _reent* unused, void* ptr, size_t size) -{ - (void) unused; - void *ret = realloc(ptr, size); - if (0 != size && 0 == ret) { - umm_last_fail_alloc_addr = __builtin_return_address(0); - umm_last_fail_alloc_size = size; - } - return ret; -} - -void* _calloc_r(struct _reent* unused, size_t count, size_t size) -{ - (void) unused; - void *ret = calloc(count, size); - if (0 != (count * size) && 0 == ret) { - umm_last_fail_alloc_addr = __builtin_return_address(0); - umm_last_fail_alloc_size = count * size; - } - return ret; -} - -void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line) -{ - (void) file; - (void) line; - free(ptr); -} + // Debugging helper, last allocation which returned NULL + void *umm_last_fail_alloc_addr = NULL; + int umm_last_fail_alloc_size = 0; + + void* _malloc_r(struct _reent* unused, size_t size) + { + (void) unused; + void *ret = malloc(size); + if (0 != size && 0 == ret) + { + umm_last_fail_alloc_addr = __builtin_return_address(0); + umm_last_fail_alloc_size = size; + } + return ret; + } + + void _free_r(struct _reent* unused, void* ptr) + { + (void) unused; + return free(ptr); + } + + void* _realloc_r(struct _reent* unused, void* ptr, size_t size) + { + (void) unused; + void *ret = realloc(ptr, size); + if (0 != size && 0 == ret) + { + umm_last_fail_alloc_addr = __builtin_return_address(0); + umm_last_fail_alloc_size = size; + } + return ret; + } + + void* _calloc_r(struct _reent* unused, size_t count, size_t size) + { + (void) unused; + void *ret = calloc(count, size); + if (0 != (count * size) && 0 == ret) + { + umm_last_fail_alloc_addr = __builtin_return_address(0); + umm_last_fail_alloc_size = count * size; + } + return ret; + } + + void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line) + { + (void) file; + (void) line; + free(ptr); + } #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 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 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 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); -} + 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) -{ + 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; -} + } + + 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); -} + 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 xPortGetFreeHeapSize(void) + { + return umm_free_heap_size(); + } -size_t ICACHE_RAM_ATTR xPortWantedSizeAlign(size_t size) -{ - return (size + 3) & ~((size_t) 3); -} + size_t ICACHE_RAM_ATTR xPortWantedSizeAlign(size_t size) + { + return (size + 3) & ~((size_t) 3); + } -void system_show_malloc(void) -{ - umm_info(NULL, 1); -} + void system_show_malloc(void) + { + umm_info(NULL, 1); + } }; diff --git a/cores/esp8266/i2s.h b/cores/esp8266/i2s.h index 70587fd2fb..13f215dfe7 100644 --- a/cores/esp8266/i2s.h +++ b/cores/esp8266/i2s.h @@ -1,39 +1,39 @@ -/* - i2s.h - Software I2S library for esp8266 +/* + i2s.h - Software I2S library for esp8266 + + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef I2S_h #define I2S_h /* -How does this work? Basically, to get sound, you need to: -- Connect an I2S codec to the I2S pins on the ESP. -- Start up a thread that's going to do the sound output -- Call i2s_begin() -- Call i2s_set_rate() with the sample rate you want. -- Generate sound and call i2s_write_sample() with 32-bit samples. -The 32bit samples basically are 2 16-bit signed values (the analog values for -the left and right channel) concatenated as (Rout<<16)+Lout + How does this work? Basically, to get sound, you need to: + - Connect an I2S codec to the I2S pins on the ESP. + - Start up a thread that's going to do the sound output + - Call i2s_begin() + - Call i2s_set_rate() with the sample rate you want. + - Generate sound and call i2s_write_sample() with 32-bit samples. + The 32bit samples basically are 2 16-bit signed values (the analog values for + the left and right channel) concatenated as (Rout<<16)+Lout -i2s_write_sample will block when you're sending data too quickly, so you can just -generate and push data as fast as you can and i2s_write_sample will regulate the -speed. + i2s_write_sample will block when you're sending data too quickly, so you can just + generate and push data as fast as you can and i2s_write_sample will regulate the + speed. */ #ifdef __cplusplus @@ -56,15 +56,15 @@ bool i2s_rx_is_full(); bool i2s_rx_is_empty(); uint16_t i2s_available();// returns the number of samples than can be written before blocking uint16_t i2s_rx_available();// returns the number of samples than can be written before blocking -void i2s_set_callback(void (*callback) (void)); -void i2s_rx_set_callback(void (*callback) (void)); +void i2s_set_callback(void (*callback)(void)); +void i2s_rx_set_callback(void (*callback)(void)); // writes a buffer of frames into the DMA memory, returns the amount of frames written // A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel. uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count); uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count); uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count); -uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count); +uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count); #ifdef __cplusplus } diff --git a/cores/esp8266/interrupts.h b/cores/esp8266/interrupts.h index 868a4a08ec..dfc13524c0 100644 --- a/cores/esp8266/interrupts.h +++ b/cores/esp8266/interrupts.h @@ -9,29 +9,33 @@ extern "C" { } // these auto classes wrap up xt_rsil so your code can be simplier, but can only be -// used in an ino or cpp files. +// used in an ino or cpp files. // InterruptLock is used when you want to completely disable interrupts //{ // { -// InterruptLock lock; +// InterruptLock lock; // // do work within interrupt lock here // } // do work outside of interrupt lock here outside its scope //} // -class InterruptLock { +class InterruptLock +{ public: - InterruptLock() { + InterruptLock() + { _state = xt_rsil(15); } - ~InterruptLock() { + ~InterruptLock() + { xt_wsr_ps(_state); } - uint32_t savedInterruptLevel() const { + uint32_t savedInterruptLevel() const + { return _state & 0x0f; } @@ -57,6 +61,6 @@ public: \ private: \ uint32_t _savedPS; \ }; \ -_AutoDisableIntr _autoDisableIntr +_AutoDisableIntr _autoDisableIntr #endif //INTERRUPTS_H diff --git a/cores/esp8266/libb64/cdecode.cpp b/cores/esp8266/libb64/cdecode.cpp index fcd6f0401a..1f8dde275a 100755 --- a/cores/esp8266/libb64/cdecode.cpp +++ b/cores/esp8266/libb64/cdecode.cpp @@ -1,8 +1,8 @@ /* -cdecoder.c - c source to a base64 decoding algorithm implementation + cdecoder.c - c source to a base64 decoding algorithm implementation -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 + This is part of the libb64 project, and has been placed in the public domain. + For details, see http://sourceforge.net/projects/libb64 */ #include @@ -11,94 +11,117 @@ For details, see http://sourceforge.net/projects/libb64 extern "C" { -static int base64_decode_value_signed(int8_t value_in){ - static const int8_t decoding[] PROGMEM = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; - static const int8_t decoding_size = sizeof(decoding); - value_in -= 43; - if (value_in < 0 || value_in > decoding_size) return -1; - return pgm_read_byte( &decoding[(int)value_in] ); -} + static int base64_decode_value_signed(int8_t value_in) + { + static const int8_t decoding[] PROGMEM = {62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; + static const int8_t decoding_size = sizeof(decoding); + value_in -= 43; + if (value_in < 0 || value_in > decoding_size) + { + return -1; + } + return pgm_read_byte(&decoding[(int)value_in]); + } + + void base64_init_decodestate(base64_decodestate* state_in) + { + state_in->step = step_a; + state_in->plainchar = 0; + } + + static int base64_decode_block_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out, base64_decodestate* state_in) + { + const int8_t* codechar = code_in; + int8_t* plainchar = plaintext_out; + int8_t fragment; -void base64_init_decodestate(base64_decodestate* state_in){ - state_in->step = step_a; - state_in->plainchar = 0; -} + *plainchar = state_in->plainchar; -static int base64_decode_block_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out, base64_decodestate* state_in){ - const int8_t* codechar = code_in; - int8_t* plainchar = plaintext_out; - int8_t fragment; - - *plainchar = state_in->plainchar; - - switch (state_in->step){ - while (1){ - case step_a: - do { - if (codechar == code_in+length_in){ - state_in->step = step_a; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (int8_t)base64_decode_value_signed(*codechar++); - } while (fragment < 0); - *plainchar = (fragment & 0x03f) << 2; - case step_b: - do { - if (codechar == code_in+length_in){ - state_in->step = step_b; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (int8_t)base64_decode_value_signed(*codechar++); - } while (fragment < 0); - *plainchar++ |= (fragment & 0x030) >> 4; - *plainchar = (fragment & 0x00f) << 4; - case step_c: - do { - if (codechar == code_in+length_in){ - state_in->step = step_c; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (int8_t)base64_decode_value_signed(*codechar++); - } while (fragment < 0); - *plainchar++ |= (fragment & 0x03c) >> 2; - *plainchar = (fragment & 0x003) << 6; - case step_d: - do { - if (codechar == code_in+length_in){ - state_in->step = step_d; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (int8_t)base64_decode_value_signed(*codechar++); - } while (fragment < 0); - *plainchar++ |= (fragment & 0x03f); + switch (state_in->step) + { + while (1) + { + case step_a: + do + { + if (codechar == code_in + length_in) + { + state_in->step = step_a; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (int8_t)base64_decode_value_signed(*codechar++); + } while (fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + case step_b: + do + { + if (codechar == code_in + length_in) + { + state_in->step = step_b; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (int8_t)base64_decode_value_signed(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do + { + if (codechar == code_in + length_in) + { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (int8_t)base64_decode_value_signed(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do + { + if (codechar == code_in + length_in) + { + state_in->step = step_d; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (int8_t)base64_decode_value_signed(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return plainchar - plaintext_out; } - } - /* control should not reach here */ - return plainchar - plaintext_out; -} -static int base64_decode_chars_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out){ - base64_decodestate _state; - base64_init_decodestate(&_state); - int len = base64_decode_block_signed(code_in, length_in, plaintext_out, &_state); - if(len > 0) plaintext_out[len] = 0; - return len; -} + static int base64_decode_chars_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out) + { + base64_decodestate _state; + base64_init_decodestate(&_state); + int len = base64_decode_block_signed(code_in, length_in, plaintext_out, &_state); + if (len > 0) + { + plaintext_out[len] = 0; + } + return len; + } -int base64_decode_value(char value_in){ - return base64_decode_value_signed(*((int8_t *) &value_in)); -} + int base64_decode_value(char value_in) + { + return base64_decode_value_signed(*((int8_t *) &value_in)); + } -int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in){ - return base64_decode_block_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out, state_in); -} + int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) + { + return base64_decode_block_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out, state_in); + } -int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out){ - return base64_decode_chars_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out); -} + int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out) + { + return base64_decode_chars_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out); + } }; diff --git a/cores/esp8266/libb64/cdecode.h b/cores/esp8266/libb64/cdecode.h index d75b327a8c..71b308e917 100755 --- a/cores/esp8266/libb64/cdecode.h +++ b/cores/esp8266/libb64/cdecode.h @@ -1,8 +1,8 @@ /* -cdecode.h - c header for a base64 decoding algorithm + cdecode.h - c header for a base64 decoding algorithm -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 + This is part of the libb64 project, and has been placed in the public domain. + For details, see http://sourceforge.net/projects/libb64 */ #ifndef BASE64_CDECODE_H @@ -14,13 +14,15 @@ For details, see http://sourceforge.net/projects/libb64 extern "C" { #endif -typedef enum { - step_a, step_b, step_c, step_d +typedef enum +{ + step_a, step_b, step_c, step_d } base64_decodestep; -typedef struct { - base64_decodestep step; - char plainchar; +typedef struct +{ + base64_decodestep step; + char plainchar; } base64_decodestate; void base64_init_decodestate(base64_decodestate* state_in); diff --git a/cores/esp8266/libb64/cencode.cpp b/cores/esp8266/libb64/cencode.cpp index 69272faeca..a6a9f56c83 100755 --- a/cores/esp8266/libb64/cencode.cpp +++ b/cores/esp8266/libb64/cencode.cpp @@ -1,8 +1,8 @@ /* -cencoder.c - c source to a base64 encoding algorithm implementation + cencoder.c - c source to a base64 encoding algorithm implementation -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 + This is part of the libb64 project, and has been placed in the public domain. + For details, see http://sourceforge.net/projects/libb64 */ #include @@ -10,105 +10,121 @@ For details, see http://sourceforge.net/projects/libb64 extern "C" { -void base64_init_encodestate(base64_encodestate* state_in){ - state_in->step = step_A; - state_in->result = 0; - state_in->stepcount = 0; - state_in->stepsnewline = BASE64_CHARS_PER_LINE; -} - - -void base64_init_encodestate_nonewlines(base64_encodestate* state_in){ - base64_init_encodestate(state_in); - state_in->stepsnewline = -1; -} - -char base64_encode_value(char value_in){ - static const char encoding[] PROGMEM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - if (value_in > 63) return '='; - return pgm_read_byte( &encoding[(int)value_in] ); -} - -int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in){ - const char* plainchar = plaintext_in; - const char* const plaintextend = plaintext_in + length_in; - char* codechar = code_out; - char result; - char fragment; - - result = state_in->result; - - switch (state_in->step){ - while (1){ - case step_A: - if (plainchar == plaintextend){ - state_in->result = result; + void base64_init_encodestate(base64_encodestate* state_in) + { state_in->step = step_A; + state_in->result = 0; + state_in->stepcount = 0; + state_in->stepsnewline = BASE64_CHARS_PER_LINE; + } + + + void base64_init_encodestate_nonewlines(base64_encodestate* state_in) + { + base64_init_encodestate(state_in); + state_in->stepsnewline = -1; + } + + char base64_encode_value(char value_in) + { + static const char encoding[] PROGMEM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) + { + return '='; + } + return pgm_read_byte(&encoding[(int)value_in]); + } + + int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) + { + const char* plainchar = plaintext_in; + const char* const plaintextend = plaintext_in + length_in; + char* codechar = code_out; + char result; + char fragment; + + result = state_in->result; + + switch (state_in->step) + { + while (1) + { + case step_A: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_A; + return codechar - code_out; + } + fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + case step_B: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_B; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + case step_C: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_C; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + + ++(state_in->stepcount); + if ((state_in->stepcount == BASE64_CHARS_PER_LINE / 4) && (state_in->stepsnewline > 0)) + { + *codechar++ = '\n'; + state_in->stepcount = 0; + } + } + } + /* control should not reach here */ return codechar - code_out; - } - fragment = *plainchar++; - result = (fragment & 0x0fc) >> 2; - *codechar++ = base64_encode_value(result); - result = (fragment & 0x003) << 4; - case step_B: - if (plainchar == plaintextend){ - state_in->result = result; - state_in->step = step_B; - return codechar - code_out; - } - fragment = *plainchar++; - result |= (fragment & 0x0f0) >> 4; - *codechar++ = base64_encode_value(result); - result = (fragment & 0x00f) << 2; - case step_C: - if (plainchar == plaintextend){ - state_in->result = result; - state_in->step = step_C; + } + + int base64_encode_blockend(char* code_out, base64_encodestate* state_in) + { + char* codechar = code_out; + + switch (state_in->step) + { + case step_B: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + *codechar++ = '='; + break; + case step_C: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + break; + case step_A: + break; + } + *codechar = 0x00; + return codechar - code_out; - } - fragment = *plainchar++; - result |= (fragment & 0x0c0) >> 6; - *codechar++ = base64_encode_value(result); - result = (fragment & 0x03f) >> 0; - *codechar++ = base64_encode_value(result); - - ++(state_in->stepcount); - if ((state_in->stepcount == BASE64_CHARS_PER_LINE/4) && (state_in->stepsnewline > 0)){ - *codechar++ = '\n'; - state_in->stepcount = 0; - } } - } - /* control should not reach here */ - return codechar - code_out; -} - -int base64_encode_blockend(char* code_out, base64_encodestate* state_in){ - char* codechar = code_out; - - switch (state_in->step){ - case step_B: - *codechar++ = base64_encode_value(state_in->result); - *codechar++ = '='; - *codechar++ = '='; - break; - case step_C: - *codechar++ = base64_encode_value(state_in->result); - *codechar++ = '='; - break; - case step_A: - break; - } - *codechar = 0x00; - - return codechar - code_out; -} - -int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out){ - base64_encodestate _state; - base64_init_encodestate(&_state); - int len = base64_encode_block(plaintext_in, length_in, code_out, &_state); - return len + base64_encode_blockend((code_out + len), &_state); -} + + int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out) + { + base64_encodestate _state; + base64_init_encodestate(&_state); + int len = base64_encode_block(plaintext_in, length_in, code_out, &_state); + return len + base64_encode_blockend((code_out + len), &_state); + } }; diff --git a/cores/esp8266/libb64/cencode.h b/cores/esp8266/libb64/cencode.h index 7c0efc22a0..808d58607e 100755 --- a/cores/esp8266/libb64/cencode.h +++ b/cores/esp8266/libb64/cencode.h @@ -1,8 +1,8 @@ /* -cencode.h - c header for a base64 encoding algorithm + cencode.h - c header for a base64 encoding algorithm -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 + This is part of the libb64 project, and has been placed in the public domain. + For details, see http://sourceforge.net/projects/libb64 */ #ifndef BASE64_CENCODE_H @@ -19,15 +19,17 @@ For details, see http://sourceforge.net/projects/libb64 extern "C" { #endif -typedef enum { - step_A, step_B, step_C +typedef enum +{ + step_A, step_B, step_C } base64_encodestep; -typedef struct { - base64_encodestep step; - char result; - int stepcount; - int stepsnewline; +typedef struct +{ + base64_encodestep step; + char result; + int stepcount; + int stepsnewline; } base64_encodestate; void base64_init_encodestate(base64_encodestate* state_in); diff --git a/cores/esp8266/libc_replacements.cpp b/cores/esp8266/libc_replacements.cpp index 1d591abf0c..569f86ee33 100644 --- a/cores/esp8266/libc_replacements.cpp +++ b/cores/esp8266/libc_replacements.cpp @@ -1,27 +1,27 @@ /* - libc_replacements.c - replaces libc functions with functions - from Espressif SDK + libc_replacements.c - replaces libc functions with functions + from Espressif SDK - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified 03 April 2015 by Markus Sattler + Modified 03 April 2015 by Markus Sattler - */ +*/ #include #include @@ -47,88 +47,103 @@ extern "C" { -int ICACHE_RAM_ATTR _open_r (struct _reent* unused, const char *ptr, int mode) { - (void)unused; - (void)ptr; - (void)mode; - return 0; -} - -int ICACHE_RAM_ATTR _close_r(struct _reent* unused, int file) { - (void)unused; - (void)file; - return 0; -} - -int ICACHE_RAM_ATTR _fstat_r(struct _reent* unused, int file, struct stat *st) { - (void)unused; - (void)file; - st->st_mode = S_IFCHR; - return 0; -} - -int ICACHE_RAM_ATTR _lseek_r(struct _reent* unused, int file, int ptr, int dir) { - (void)unused; - (void)file; - (void)ptr; - (void)dir; - return 0; -} - -int ICACHE_RAM_ATTR _read_r(struct _reent* unused, int file, char *ptr, int len) { - (void)unused; - (void)file; - (void)ptr; - (void)len; - return 0; -} - -int ICACHE_RAM_ATTR _write_r(struct _reent* r, int file, char *ptr, int len) { - (void) r; - int pos = len; - if (file == STDOUT_FILENO) { - while(pos--) { - ets_putc(*ptr); - ++ptr; + int ICACHE_RAM_ATTR _open_r(struct _reent* unused, const char *ptr, int mode) + { + (void)unused; + (void)ptr; + (void)mode; + return 0; + } + + int ICACHE_RAM_ATTR _close_r(struct _reent* unused, int file) + { + (void)unused; + (void)file; + return 0; + } + + int ICACHE_RAM_ATTR _fstat_r(struct _reent* unused, int file, struct stat *st) + { + (void)unused; + (void)file; + st->st_mode = S_IFCHR; + return 0; + } + + int ICACHE_RAM_ATTR _lseek_r(struct _reent* unused, int file, int ptr, int dir) + { + (void)unused; + (void)file; + (void)ptr; + (void)dir; + return 0; + } + + int ICACHE_RAM_ATTR _read_r(struct _reent* unused, int file, char *ptr, int len) + { + (void)unused; + (void)file; + (void)ptr; + (void)len; + return 0; + } + + int ICACHE_RAM_ATTR _write_r(struct _reent* r, int file, char *ptr, int len) + { + (void) r; + int pos = len; + if (file == STDOUT_FILENO) + { + while (pos--) + { + ets_putc(*ptr); + ++ptr; + } } + return len; } - return len; -} -int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) __attribute__((weak)); + int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) __attribute__((weak)); -int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) { - (void) r; - if (file->_file == STDOUT_FILENO) { - return ets_putc(c); + int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) + { + (void) r; + if (file->_file == STDOUT_FILENO) + { + return ets_putc(c); + } + return EOF; } - return EOF; -} -int ICACHE_RAM_ATTR puts(const char * str) { - char c; - while((c = *str) != 0) { - ets_putc(c); - ++str; + int ICACHE_RAM_ATTR puts(const char * str) + { + char c; + while ((c = *str) != 0) + { + ets_putc(c); + ++str; + } + ets_putc('\n'); + return true; } - ets_putc('\n'); - return true; -} #undef putchar -int ICACHE_RAM_ATTR putchar(int c) { - ets_putc(c); - return c; -} - -void _exit(int status) { - (void) status; - abort(); -} - -int atexit(void (*func)()) { - (void) func; - return 0; -} + int ICACHE_RAM_ATTR putchar(int c) + { + ets_putc(c); + return c; + } + + void _exit(int status) + { + (void) status; + abort(); + } + + int atexit(void (*func)()) + { + (void) func; + return 0; + } }; diff --git a/cores/esp8266/md5.h b/cores/esp8266/md5.h index 4efcaa9553..ac6aa93988 100644 --- a/cores/esp8266/md5.h +++ b/cores/esp8266/md5.h @@ -1,24 +1,24 @@ /* - md5.h - exposed md5 ROM functions for esp8266 + md5.h - exposed md5 ROM functions for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - original C source from https://github.com/morrissinger/ESP8266-Websocket/raw/master/MD5.h + original C source from https://github.com/morrissinger/ESP8266-Websocket/raw/master/MD5.h - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __ESP8266_MD5__ #define __ESP8266_MD5__ @@ -27,15 +27,16 @@ extern "C" { #endif -typedef struct { - uint32_t state[4]; - uint32_t count[2]; - uint8_t buffer[64]; +typedef struct +{ + uint32_t state[4]; + uint32_t count[2]; + uint8_t buffer[64]; } md5_context_t; -extern void MD5Init (md5_context_t *); -extern void MD5Update (md5_context_t *, const uint8_t *, const uint16_t); -extern void MD5Final (uint8_t [16], md5_context_t *); +extern void MD5Init(md5_context_t *); +extern void MD5Update(md5_context_t *, const uint8_t *, const uint16_t); +extern void MD5Final(uint8_t [16], md5_context_t *); #ifdef __cplusplus } // extern "C" diff --git a/cores/esp8266/sigma_delta.h b/cores/esp8266/sigma_delta.h index 6792f931cb..ab3bd8fa61 100644 --- a/cores/esp8266/sigma_delta.h +++ b/cores/esp8266/sigma_delta.h @@ -1,45 +1,45 @@ -/* - sigma_delta.h - esp8266 sigma-delta source - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - /******************************************************************************* - * Info Sigma delta module - -This module controls the esp8266 internal sigma delta source -Each pin can be connected to the sigma delta source -The target duty and frequency can be modified via the register GPIO_SIGMA_DELTA - -THE TARGET FREQUENCY IS DEFINED AS: - -FREQ = 80,000,000/prescaler * target /256 HZ, 0tv_usec - * sntp_mktm_r(): review, fix DST handling (this one is currently untouched from lwip-1.4) - * implement adjtime() - */ + sntp-lwip2.c - ESP8266-specific functions for SNTP and lwIP-v2 + Copyright (c) 2015 Espressif (license is tools/sdk/lwip/src/core/sntp.c's) + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH DAMAGE. + + + History: + This code is extracted from lwip1.4-espressif's sntp.c + which is a patched version of the original lwip1's sntp. + (check the mix-up in tools/sdk/lwip/src/core/sntp.c) + It is moved here as-is and cleaned for maintainability and + because it does not belong to lwip. + + TODOs: + settimeofday(): handle tv->tv_usec + sntp_mktm_r(): review, fix DST handling (this one is currently untouched from lwip-1.4) + implement adjtime() +*/ #include #include @@ -45,46 +45,46 @@ extern "C" { -static void (*_settimeofday_cb)(void) = NULL; + static void (*_settimeofday_cb)(void) = NULL; -void settimeofday_cb (void (*cb)(void)) -{ - _settimeofday_cb = cb; -} + void settimeofday_cb(void (*cb)(void)) + { + _settimeofday_cb = cb; + } #if LWIP_VERSION_MAJOR == 1 #include -static const char stod14[] PROGMEM = "settimeofday() can't set time!\n"; -bool sntp_set_timezone(sint8 timezone); -bool sntp_set_timezone_in_seconds(sint32 timezone) -{ - return sntp_set_timezone((sint8)(timezone/(60*60))); //TODO: move this to the same file as sntp_set_timezone() in lwip1.4, and implement correctly over there. -} - -void sntp_set_daylight(int daylight); - -int settimeofday(const struct timeval* tv, const struct timezone* tz) -{ - if (tz) /*before*/ + static const char stod14[] PROGMEM = "settimeofday() can't set time!\n"; + bool sntp_set_timezone(sint8 timezone); + bool sntp_set_timezone_in_seconds(sint32 timezone) { - sntp_set_timezone_in_seconds(tz->tz_minuteswest * 60); - // apparently tz->tz_dsttime is a bitfield and should not be further used (cf man) - sntp_set_daylight(0); + return sntp_set_timezone((sint8)(timezone / (60 * 60))); //TODO: move this to the same file as sntp_set_timezone() in lwip1.4, and implement correctly over there. } - if (tv) /* after*/ - { - // can't call lwip1.4's static sntp_set_system_time() - os_printf(stod14); - // reset time subsystem - timeshift64_is_set = false; - - return -1; + void sntp_set_daylight(int daylight); + + int settimeofday(const struct timeval* tv, const struct timezone* tz) + { + if (tz) /*before*/ + { + sntp_set_timezone_in_seconds(tz->tz_minuteswest * 60); + // apparently tz->tz_dsttime is a bitfield and should not be further used (cf man) + sntp_set_daylight(0); + } + if (tv) /* after*/ + { + // can't call lwip1.4's static sntp_set_system_time() + os_printf(stod14); + + // reset time subsystem + timeshift64_is_set = false; + + return -1; + } + return 0; } - return 0; -} #endif // lwip 1.4 only @@ -92,12 +92,12 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz) #include -static uint32 realtime_stamp = 0; -static uint16 dst = 0; -static sint32 time_zone = 8 * (60 * 60); // espressif HQ's default timezone -LOCAL os_timer_t sntp_timer; + static uint32 realtime_stamp = 0; + static uint16 dst = 0; + static sint32 time_zone = 8 * (60 * 60); // espressif HQ's default timezone + LOCAL os_timer_t sntp_timer; -/*****************************************/ + /*****************************************/ #define SECSPERMIN 60L #define MINSPERHOUR 60L #define HOURSPERDAY 24L @@ -115,373 +115,404 @@ LOCAL os_timer_t sntp_timer; #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) -int __tznorth; -int __tzyear; -char reult[100]; -static const int mon_lengths[2][12] = { - {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -} ; - -static const int year_lengths[2] = { - 365, - 366 -} ; -struct tm -{ - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; -}; + int __tznorth; + int __tzyear; + char reult[100]; + static const int mon_lengths[2][12] = + { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} + } ; -struct tm res_buf; -typedef struct __tzrule_struct -{ - char ch; - int m; - int n; - int d; - int s; - time_t change; - int offset; -} __tzrule_type; - -__tzrule_type sntp__tzrule[2]; -struct tm * -sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime) -{ - long days, rem; - time_t lcltime; - int y; - int yleap; - const int *ip; - - /* base decision about std/dst time on current time */ - lcltime = *tim_p; - - days = ((long)lcltime) / SECSPERDAY; - rem = ((long)lcltime) % SECSPERDAY; - while (rem < 0) + static const int year_lengths[2] = + { + 365, + 366 + } ; + struct tm + { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; + }; + + struct tm res_buf; + typedef struct __tzrule_struct { - rem += SECSPERDAY; - --days; + char ch; + int m; + int n; + int d; + int s; + time_t change; + int offset; + } __tzrule_type; + + __tzrule_type sntp__tzrule[2]; + struct tm * + sntp_mktm_r(const time_t * tim_p, struct tm *res, int is_gmtime) + { + long days, rem; + time_t lcltime; + int y; + int yleap; + const int *ip; + + /* base decision about std/dst time on current time */ + lcltime = *tim_p; + + days = ((long)lcltime) / SECSPERDAY; + rem = ((long)lcltime) % SECSPERDAY; + while (rem < 0) + { + rem += SECSPERDAY; + --days; + } + while (rem >= SECSPERDAY) + { + rem -= SECSPERDAY; + ++days; + } + + /* compute hour, min, and sec */ + res->tm_hour = (int)(rem / SECSPERHOUR); + rem %= SECSPERHOUR; + res->tm_min = (int)(rem / SECSPERMIN); + res->tm_sec = (int)(rem % SECSPERMIN); + + /* compute day of week */ + if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0) + { + res->tm_wday += DAYSPERWEEK; + } + + /* compute year & day of year */ + y = EPOCH_YEAR; + if (days >= 0) + { + for (;;) + { + yleap = isleap(y); + if (days < year_lengths[yleap]) + { + break; + } + y++; + days -= year_lengths[yleap]; + } + } + else + { + do + { + --y; + yleap = isleap(y); + days += year_lengths[yleap]; + } while (days < 0); + } + + res->tm_year = y - YEAR_BASE; + res->tm_yday = days; + ip = mon_lengths[yleap]; + for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon) + { + days -= ip[res->tm_mon]; + } + res->tm_mday = days + 1; + + if (!is_gmtime) + { + int offset; + int hours, mins, secs; + + // TZ_LOCK; + // if (_daylight) + // { + // if (y == __tzyear || __tzcalc_limits (y)) + // res->tm_isdst = (__tznorth + // ? (*tim_p >= __tzrule[0].change && *tim_p < __tzrule[1].change) + // : (*tim_p >= __tzrule[0].change || *tim_p < __tzrule[1].change)); + // else + // res->tm_isdst = -1; + // } + // else + res->tm_isdst = -1; + + offset = (res->tm_isdst == 1 ? sntp__tzrule[1].offset : sntp__tzrule[0].offset); + + hours = offset / SECSPERHOUR; + offset = offset % SECSPERHOUR; + + mins = offset / SECSPERMIN; + secs = offset % SECSPERMIN; + + res->tm_sec -= secs; + res->tm_min -= mins; + res->tm_hour -= hours; + + if (res->tm_sec >= SECSPERMIN) + { + res->tm_min += 1; + res->tm_sec -= SECSPERMIN; + } + else if (res->tm_sec < 0) + { + res->tm_min -= 1; + res->tm_sec += SECSPERMIN; + } + if (res->tm_min >= MINSPERHOUR) + { + res->tm_hour += 1; + res->tm_min -= MINSPERHOUR; + } + else if (res->tm_min < 0) + { + res->tm_hour -= 1; + res->tm_min += MINSPERHOUR; + } + if (res->tm_hour >= HOURSPERDAY) + { + ++res->tm_yday; + ++res->tm_wday; + if (res->tm_wday > 6) + { + res->tm_wday = 0; + } + ++res->tm_mday; + res->tm_hour -= HOURSPERDAY; + if (res->tm_mday > ip[res->tm_mon]) + { + res->tm_mday -= ip[res->tm_mon]; + res->tm_mon += 1; + if (res->tm_mon == 12) + { + res->tm_mon = 0; + res->tm_year += 1; + res->tm_yday = 0; + } + } + } + else if (res->tm_hour < 0) + { + res->tm_yday -= 1; + res->tm_wday -= 1; + if (res->tm_wday < 0) + { + res->tm_wday = 6; + } + res->tm_mday -= 1; + res->tm_hour += 24; + if (res->tm_mday == 0) + { + res->tm_mon -= 1; + if (res->tm_mon < 0) + { + res->tm_mon = 11; + res->tm_year -= 1; + res->tm_yday = 365 + isleap(res->tm_year); + } + res->tm_mday = ip[res->tm_mon]; + } + } + // TZ_UNLOCK; + } + else + { + res->tm_isdst = 0; + } + // os_printf("res %d %d %d %d %d\n",res->tm_year,res->tm_mon,res->tm_mday,res->tm_yday,res->tm_hour); + return (res); } - while (rem >= SECSPERDAY) + struct tm * + sntp_localtime_r(const time_t * tim_p, + struct tm *res) { - rem -= SECSPERDAY; - ++days; + return sntp_mktm_r(tim_p, res, 0); } - /* compute hour, min, and sec */ - res->tm_hour = (int) (rem / SECSPERHOUR); - rem %= SECSPERHOUR; - res->tm_min = (int) (rem / SECSPERMIN); - res->tm_sec = (int) (rem % SECSPERMIN); - - /* compute day of week */ - if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0) - res->tm_wday += DAYSPERWEEK; - - /* compute year & day of year */ - y = EPOCH_YEAR; - if (days >= 0) + struct tm * + sntp_localtime(const time_t * tim_p) { - for (;;) - { - yleap = isleap(y); - if (days < year_lengths[yleap]) - break; - y++; - days -= year_lengths[yleap]; - } + return sntp_localtime_r(tim_p, &res_buf); } - else + + int sntp__tzcalc_limits(int year) { - do - { - --y; - yleap = isleap(y); - days += year_lengths[yleap]; - } while (days < 0); + int days, year_days, years; + int i, j; + + if (year < EPOCH_YEAR) + { + return 0; + } + + __tzyear = year; + + years = (year - EPOCH_YEAR); + + year_days = years * 365 + + (years - 1 + EPOCH_YEARS_SINCE_LEAP) / 4 - (years - 1 + EPOCH_YEARS_SINCE_CENTURY) / 100 + + (years - 1 + EPOCH_YEARS_SINCE_LEAP_CENTURY) / 400; + + for (i = 0; i < 2; ++i) + { + if (sntp__tzrule[i].ch == 'J') + { + days = year_days + sntp__tzrule[i].d + (isleap(year) && sntp__tzrule[i].d >= 60); + } + else if (sntp__tzrule[i].ch == 'D') + { + days = year_days + sntp__tzrule[i].d; + } + else + { + int yleap = isleap(year); + int m_day, m_wday, wday_diff; + const int *ip = mon_lengths[yleap]; + + days = year_days; + + for (j = 1; j < sntp__tzrule[i].m; ++j) + { + days += ip[j - 1]; + } + + m_wday = (EPOCH_WDAY + days) % DAYSPERWEEK; + + wday_diff = sntp__tzrule[i].d - m_wday; + if (wday_diff < 0) + { + wday_diff += DAYSPERWEEK; + } + m_day = (sntp__tzrule[i].n - 1) * DAYSPERWEEK + wday_diff; + + while (m_day >= ip[j - 1]) + { + m_day -= DAYSPERWEEK; + } + + days += m_day; + } + + /* store the change-over time in GMT form by adding offset */ + sntp__tzrule[i].change = days * SECSPERDAY + sntp__tzrule[i].s + sntp__tzrule[i].offset; + } + + __tznorth = (sntp__tzrule[0].change < sntp__tzrule[1].change); + + return 1; } - res->tm_year = y - YEAR_BASE; - res->tm_yday = days; - ip = mon_lengths[yleap]; - for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon) - days -= ip[res->tm_mon]; - res->tm_mday = days + 1; + char* sntp_asctime_r(struct tm *tim_p, char *result) + { + static const char day_name[7][4] = + { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static const char mon_name[12][4] = + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + os_sprintf(result, "%s %s %02d %02d:%02d:%02d %02d\n", + day_name[tim_p->tm_wday], + mon_name[tim_p->tm_mon], + tim_p->tm_mday, tim_p->tm_hour, tim_p->tm_min, + tim_p->tm_sec, 1900 + tim_p->tm_year); + return result; + } - if (!is_gmtime) + char* sntp_asctime(struct tm *tim_p) { - int offset; - int hours, mins, secs; - -// TZ_LOCK; -// if (_daylight) -// { -// if (y == __tzyear || __tzcalc_limits (y)) -// res->tm_isdst = (__tznorth -// ? (*tim_p >= __tzrule[0].change && *tim_p < __tzrule[1].change) -// : (*tim_p >= __tzrule[0].change || *tim_p < __tzrule[1].change)); -// else -// res->tm_isdst = -1; -// } -// else - res->tm_isdst = -1; - - offset = (res->tm_isdst == 1 ? sntp__tzrule[1].offset : sntp__tzrule[0].offset); - - hours = offset / SECSPERHOUR; - offset = offset % SECSPERHOUR; - - mins = offset / SECSPERMIN; - secs = offset % SECSPERMIN; - - res->tm_sec -= secs; - res->tm_min -= mins; - res->tm_hour -= hours; - - if (res->tm_sec >= SECSPERMIN) - { - res->tm_min += 1; - res->tm_sec -= SECSPERMIN; - } - else if (res->tm_sec < 0) - { - res->tm_min -= 1; - res->tm_sec += SECSPERMIN; - } - if (res->tm_min >= MINSPERHOUR) - { - res->tm_hour += 1; - res->tm_min -= MINSPERHOUR; - } - else if (res->tm_min < 0) - { - res->tm_hour -= 1; - res->tm_min += MINSPERHOUR; - } - if (res->tm_hour >= HOURSPERDAY) - { - ++res->tm_yday; - ++res->tm_wday; - if (res->tm_wday > 6) - res->tm_wday = 0; - ++res->tm_mday; - res->tm_hour -= HOURSPERDAY; - if (res->tm_mday > ip[res->tm_mon]) - { - res->tm_mday -= ip[res->tm_mon]; - res->tm_mon += 1; - if (res->tm_mon == 12) - { - res->tm_mon = 0; - res->tm_year += 1; - res->tm_yday = 0; - } - } - } - else if (res->tm_hour < 0) - { - res->tm_yday -= 1; - res->tm_wday -= 1; - if (res->tm_wday < 0) - res->tm_wday = 6; - res->tm_mday -= 1; - res->tm_hour += 24; - if (res->tm_mday == 0) - { - res->tm_mon -= 1; - if (res->tm_mon < 0) - { - res->tm_mon = 11; - res->tm_year -= 1; - res->tm_yday = 365 + isleap(res->tm_year); - } - res->tm_mday = ip[res->tm_mon]; - } - } -// TZ_UNLOCK; + return sntp_asctime_r(tim_p, reult); } - else - res->tm_isdst = 0; -// os_printf("res %d %d %d %d %d\n",res->tm_year,res->tm_mon,res->tm_mday,res->tm_yday,res->tm_hour); - return (res); -} -struct tm * -sntp_localtime_r(const time_t * tim_p , - struct tm *res) -{ - return sntp_mktm_r (tim_p, res, 0); -} - -struct tm * -sntp_localtime(const time_t * tim_p) -{ - return sntp_localtime_r (tim_p, &res_buf); -} - -int sntp__tzcalc_limits(int year) -{ - int days, year_days, years; - int i, j; - - if (year < EPOCH_YEAR) - return 0; - - __tzyear = year; - - years = (year - EPOCH_YEAR); - - year_days = years * 365 + - (years - 1 + EPOCH_YEARS_SINCE_LEAP) / 4 - (years - 1 + EPOCH_YEARS_SINCE_CENTURY) / 100 + - (years - 1 + EPOCH_YEARS_SINCE_LEAP_CENTURY) / 400; - - for (i = 0; i < 2; ++i) + + uint32 ICACHE_RAM_ATTR sntp_get_current_timestamp(void) { - if (sntp__tzrule[i].ch == 'J') - days = year_days + sntp__tzrule[i].d + (isleap(year) && sntp__tzrule[i].d >= 60); - else if (sntp__tzrule[i].ch == 'D') - days = year_days + sntp__tzrule[i].d; - else - { - int yleap = isleap(year); - int m_day, m_wday, wday_diff; - const int *ip = mon_lengths[yleap]; + return realtime_stamp; + } - days = year_days; + char* sntp_get_real_time(time_t t) + { + return sntp_asctime(sntp_localtime(&t)); + } - for (j = 1; j < sntp__tzrule[i].m; ++j) - days += ip[j-1]; + /* Returns the set timezone in seconds. If the timezone was set as seconds, the fractional part is floored. */ + sint32 sntp_get_timezone_in_seconds(void) + { + return time_zone; + } - m_wday = (EPOCH_WDAY + days) % DAYSPERWEEK; + /* Returns the set timezone in hours. If the timezone was set as seconds, the fractional part is floored. */ + sint8 sntp_get_timezone(void) + { + return (sint8)(time_zone / (60 * 60)); + } - wday_diff = sntp__tzrule[i].d - m_wday; - if (wday_diff < 0) - wday_diff += DAYSPERWEEK; - m_day = (sntp__tzrule[i].n - 1) * DAYSPERWEEK + wday_diff; + /* Sets the timezone in hours. Internally, the timezone is converted to seconds. */ + bool sntp_set_timezone_in_seconds(sint32 timezone) + { + if (timezone >= (-11 * (60 * 60)) || timezone <= (13 * (60 * 60))) + { + time_zone = timezone; + return true; + } + return false; + } - while (m_day >= ip[j-1]) - m_day -= DAYSPERWEEK; + /* Sets the timezone in hours. Internally, the timezone is converted to seconds. */ + bool sntp_set_timezone(sint8 timezone) + { + return sntp_set_timezone_in_seconds((sint32)timezone * 60 * 60); + } - days += m_day; - } - /* store the change-over time in GMT form by adding offset */ - sntp__tzrule[i].change = days * SECSPERDAY + sntp__tzrule[i].s + sntp__tzrule[i].offset; + void sntp_set_daylight(int daylight) + { + dst = daylight; } - __tznorth = (sntp__tzrule[0].change < sntp__tzrule[1].change); - - return 1; -} - -char* sntp_asctime_r(struct tm *tim_p ,char *result) -{ - static const char day_name[7][4] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - static const char mon_name[12][4] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - os_sprintf (result, "%s %s %02d %02d:%02d:%02d %02d\n", - day_name[tim_p->tm_wday], - mon_name[tim_p->tm_mon], - tim_p->tm_mday, tim_p->tm_hour, tim_p->tm_min, - tim_p->tm_sec, 1900 + tim_p->tm_year); - return result; -} - -char* sntp_asctime(struct tm *tim_p) -{ - return sntp_asctime_r (tim_p, reult); -} - -uint32 ICACHE_RAM_ATTR sntp_get_current_timestamp(void) -{ - return realtime_stamp; -} - -char* sntp_get_real_time(time_t t) -{ - return sntp_asctime(sntp_localtime (&t)); -} - -/* Returns the set timezone in seconds. If the timezone was set as seconds, the fractional part is floored. */ -sint32 sntp_get_timezone_in_seconds(void) -{ - return time_zone; -} - -/* Returns the set timezone in hours. If the timezone was set as seconds, the fractional part is floored. */ -sint8 sntp_get_timezone(void) -{ - return (sint8)(time_zone / (60 * 60)); -} - -/* Sets the timezone in hours. Internally, the timezone is converted to seconds. */ -bool sntp_set_timezone_in_seconds(sint32 timezone) -{ - if(timezone >= (-11 * (60 * 60)) || timezone <= (13 * (60 * 60))) { - time_zone = timezone; - return true; - } - return false; -} - -/* Sets the timezone in hours. Internally, the timezone is converted to seconds. */ -bool sntp_set_timezone(sint8 timezone) -{ - return sntp_set_timezone_in_seconds((sint32)timezone * 60 * 60); -} - - -void sntp_set_daylight(int daylight) -{ - dst = daylight; -} - -void ICACHE_RAM_ATTR sntp_time_inc (void) -{ - realtime_stamp++; -} - -static void sntp_set_system_time (uint32_t t) -{ - realtime_stamp = t + time_zone + dst; - os_timer_disarm(&sntp_timer); - os_timer_setfn(&sntp_timer, (os_timer_func_t *)sntp_time_inc, NULL); - os_timer_arm(&sntp_timer, 1000, 1); -} - -int settimeofday(const struct timeval* tv, const struct timezone* tz) -{ - if (tz) /*before*/ + void ICACHE_RAM_ATTR sntp_time_inc(void) { - sntp_set_timezone_in_seconds(tz->tz_minuteswest * 60); - // apparently tz->tz_dsttime is a bitfield and should not be further used (cf man) - sntp_set_daylight(0); + realtime_stamp++; } - if (tv) /* after*/ - { - // reset time subsystem - tune_timeshift64(tv->tv_sec * 1000000ULL + tv->tv_usec); - sntp_set_system_time(tv->tv_sec); + static void sntp_set_system_time(uint32_t t) + { + realtime_stamp = t + time_zone + dst; + os_timer_disarm(&sntp_timer); + os_timer_setfn(&sntp_timer, (os_timer_func_t *)sntp_time_inc, NULL); + os_timer_arm(&sntp_timer, 1000, 1); + } - if (_settimeofday_cb) - _settimeofday_cb(); + int settimeofday(const struct timeval* tv, const struct timezone* tz) + { + if (tz) /*before*/ + { + sntp_set_timezone_in_seconds(tz->tz_minuteswest * 60); + // apparently tz->tz_dsttime is a bitfield and should not be further used (cf man) + sntp_set_daylight(0); + } + if (tv) /* after*/ + { + // reset time subsystem + tune_timeshift64(tv->tv_sec * 1000000ULL + tv->tv_usec); + + sntp_set_system_time(tv->tv_sec); + + if (_settimeofday_cb) + { + _settimeofday_cb(); + } + } + return 0; } - return 0; -} #endif // lwip2 only diff --git a/cores/esp8266/sntp-lwip2.h b/cores/esp8266/sntp-lwip2.h index 5ae697754b..cdfb9409f1 100644 --- a/cores/esp8266/sntp-lwip2.h +++ b/cores/esp8266/sntp-lwip2.h @@ -3,7 +3,7 @@ extern "C" { -bool sntp_set_timezone_in_seconds(sint32 timezone); + bool sntp_set_timezone_in_seconds(sint32 timezone); } diff --git a/cores/esp8266/spiffs/spiffs.h b/cores/esp8266/spiffs/spiffs.h index 534c3df8bd..cff6a2c07b 100644 --- a/cores/esp8266/spiffs/spiffs.h +++ b/cores/esp8266/spiffs/spiffs.h @@ -1,9 +1,9 @@ /* - * spiffs.h - * - * Created on: May 26, 2013 - * Author: petera - */ + spiffs.h + + Created on: May 26, 2013 + Author: petera +*/ #ifndef SPIFFS_H_ #define SPIFFS_H_ @@ -99,40 +99,43 @@ typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); #endif // SPIFFS_HAL_CALLBACK_EXTRA /* file system check callback report operation */ -typedef enum { - SPIFFS_CHECK_LOOKUP = 0, - SPIFFS_CHECK_INDEX, - SPIFFS_CHECK_PAGE +typedef enum +{ + SPIFFS_CHECK_LOOKUP = 0, + SPIFFS_CHECK_INDEX, + SPIFFS_CHECK_PAGE } spiffs_check_type; /* file system check callback report type */ -typedef enum { - SPIFFS_CHECK_PROGRESS = 0, - SPIFFS_CHECK_ERROR, - SPIFFS_CHECK_FIX_INDEX, - SPIFFS_CHECK_FIX_LOOKUP, - SPIFFS_CHECK_DELETE_ORPHANED_INDEX, - SPIFFS_CHECK_DELETE_PAGE, - SPIFFS_CHECK_DELETE_BAD_FILE +typedef enum +{ + SPIFFS_CHECK_PROGRESS = 0, + SPIFFS_CHECK_ERROR, + SPIFFS_CHECK_FIX_INDEX, + SPIFFS_CHECK_FIX_LOOKUP, + SPIFFS_CHECK_DELETE_ORPHANED_INDEX, + SPIFFS_CHECK_DELETE_PAGE, + SPIFFS_CHECK_DELETE_BAD_FILE } spiffs_check_report; /* file system check callback function */ #if SPIFFS_HAL_CALLBACK_EXTRA typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report, - u32_t arg1, u32_t arg2); + u32_t arg1, u32_t arg2); #else // SPIFFS_HAL_CALLBACK_EXTRA typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, - u32_t arg1, u32_t arg2); + u32_t arg1, u32_t arg2); #endif // SPIFFS_HAL_CALLBACK_EXTRA /* file system listener callback operation */ -typedef enum { - /* the file has been created */ - SPIFFS_CB_CREATED = 0, - /* the file has been updated or moved to another page */ - SPIFFS_CB_UPDATED, - /* the file has been deleted */ - SPIFFS_CB_DELETED +typedef enum +{ + /* the file has been created */ + SPIFFS_CB_CREATED = 0, + /* the file has been updated or moved to another page */ + SPIFFS_CB_UPDATED, + /* the file has been deleted */ + SPIFFS_CB_DELETED } spiffs_fileop_type; /* file system listener callback function */ @@ -197,142 +200,148 @@ typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, // phys structs // spiffs spi configuration struct -typedef struct { - // physical read function - spiffs_read hal_read_f; - // physical write function - spiffs_write hal_write_f; - // physical erase function - spiffs_erase hal_erase_f; +typedef struct +{ + // physical read function + spiffs_read hal_read_f; + // physical write function + spiffs_write hal_write_f; + // physical erase function + spiffs_erase hal_erase_f; #if SPIFFS_SINGLETON == 0 - // physical size of the spi flash - u32_t phys_size; - // physical offset in spi flash used for spiffs, - // must be on block boundary - u32_t phys_addr; - // physical size when erasing a block - u32_t phys_erase_block; - - // logical size of a block, must be on physical - // block size boundary and must never be less than - // a physical block - u32_t log_block_size; - // logical size of a page, must be at least - // log_block_size / 8 - u32_t log_page_size; + // physical size of the spi flash + u32_t phys_size; + // physical offset in spi flash used for spiffs, + // must be on block boundary + u32_t phys_addr; + // physical size when erasing a block + u32_t phys_erase_block; + + // logical size of a block, must be on physical + // block size boundary and must never be less than + // a physical block + u32_t log_block_size; + // logical size of a page, must be at least + // log_block_size / 8 + u32_t log_page_size; #endif #if SPIFFS_FILEHDL_OFFSET - // an integer offset added to each file handle - u16_t fh_ix_offset; + // an integer offset added to each file handle + u16_t fh_ix_offset; #endif } spiffs_config; -typedef struct spiffs_t { - // file system configuration - spiffs_config cfg; - // number of logical blocks - u32_t block_count; - - // cursor for free blocks, block index - spiffs_block_ix free_cursor_block_ix; - // cursor for free blocks, entry index - int free_cursor_obj_lu_entry; - // cursor when searching, block index - spiffs_block_ix cursor_block_ix; - // cursor when searching, entry index - int cursor_obj_lu_entry; - - // primary work buffer, size of a logical page - u8_t *lu_work; - // secondary work buffer, size of a logical page - u8_t *work; - // file descriptor memory area - u8_t *fd_space; - // available file descriptors - u32_t fd_count; - - // last error - s32_t err_code; - - // current number of free blocks - u32_t free_blocks; - // current number of busy pages - u32_t stats_p_allocated; - // current number of deleted pages - u32_t stats_p_deleted; - // flag indicating that garbage collector is cleaning - u8_t cleaning; - // max erase count amongst all blocks - spiffs_obj_id max_erase_count; +typedef struct spiffs_t +{ + // file system configuration + spiffs_config cfg; + // number of logical blocks + u32_t block_count; + + // cursor for free blocks, block index + spiffs_block_ix free_cursor_block_ix; + // cursor for free blocks, entry index + int free_cursor_obj_lu_entry; + // cursor when searching, block index + spiffs_block_ix cursor_block_ix; + // cursor when searching, entry index + int cursor_obj_lu_entry; + + // primary work buffer, size of a logical page + u8_t *lu_work; + // secondary work buffer, size of a logical page + u8_t *work; + // file descriptor memory area + u8_t *fd_space; + // available file descriptors + u32_t fd_count; + + // last error + s32_t err_code; + + // current number of free blocks + u32_t free_blocks; + // current number of busy pages + u32_t stats_p_allocated; + // current number of deleted pages + u32_t stats_p_deleted; + // flag indicating that garbage collector is cleaning + u8_t cleaning; + // max erase count amongst all blocks + spiffs_obj_id max_erase_count; #if SPIFFS_GC_STATS - u32_t stats_gc_runs; + u32_t stats_gc_runs; #endif #if SPIFFS_CACHE - // cache memory - void *cache; - // cache size - u32_t cache_size; + // cache memory + void *cache; + // cache size + u32_t cache_size; #if SPIFFS_CACHE_STATS - u32_t cache_hits; - u32_t cache_misses; + u32_t cache_hits; + u32_t cache_misses; #endif #endif - // check callback function - spiffs_check_callback check_cb_f; - // file callback function - spiffs_file_callback file_cb_f; - // mounted flag - u8_t mounted; - // user data - void *user_data; - // config magic - u32_t config_magic; + // check callback function + spiffs_check_callback check_cb_f; + // file callback function + spiffs_file_callback file_cb_f; + // mounted flag + u8_t mounted; + // user data + void *user_data; + // config magic + u32_t config_magic; } spiffs; /* spiffs file status struct */ -typedef struct { - spiffs_obj_id obj_id; - u32_t size; - spiffs_obj_type type; - spiffs_page_ix pix; - u8_t name[SPIFFS_OBJ_NAME_LEN]; +typedef struct +{ + spiffs_obj_id obj_id; + u32_t size; + spiffs_obj_type type; + spiffs_page_ix pix; + u8_t name[SPIFFS_OBJ_NAME_LEN]; #if SPIFFS_OBJ_META_LEN - u8_t meta[SPIFFS_OBJ_META_LEN]; + u8_t meta[SPIFFS_OBJ_META_LEN]; #endif } spiffs_stat; -struct spiffs_dirent { - spiffs_obj_id obj_id; - u8_t name[SPIFFS_OBJ_NAME_LEN]; - spiffs_obj_type type; - u32_t size; - spiffs_page_ix pix; +struct spiffs_dirent +{ + spiffs_obj_id obj_id; + u8_t name[SPIFFS_OBJ_NAME_LEN]; + spiffs_obj_type type; + u32_t size; + spiffs_page_ix pix; #if SPIFFS_OBJ_META_LEN - u8_t meta[SPIFFS_OBJ_META_LEN]; + u8_t meta[SPIFFS_OBJ_META_LEN]; #endif }; -typedef struct { - spiffs *fs; - spiffs_block_ix block; - int entry; +typedef struct +{ + spiffs *fs; + spiffs_block_ix block; + int entry; } spiffs_DIR; #if SPIFFS_IX_MAP -typedef struct { - // buffer with looked up data pixes - spiffs_page_ix *map_buf; - // precise file byte offset - u32_t offset; - // start data span index of lookup buffer - spiffs_span_ix start_spix; - // end data span index of lookup buffer - spiffs_span_ix end_spix; +typedef struct +{ + // buffer with looked up data pixes + spiffs_page_ix *map_buf; + // precise file byte offset + u32_t offset; + // start data span index of lookup buffer + spiffs_span_ix start_spix; + // end data span index of lookup buffer + spiffs_span_ix end_spix; } spiffs_ix_map; #endif @@ -341,443 +350,443 @@ typedef struct { #if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 /** - * Special function. This takes a spiffs config struct and returns the number - * of blocks this file system was formatted with. This function relies on - * that following info is set correctly in given config struct: - * - * phys_addr, log_page_size, and log_block_size. - * - * Also, hal_read_f must be set in the config struct. - * - * One must be sure of the correct page size and that the physical address is - * correct in the probed file system when calling this function. It is not - * checked if the phys_addr actually points to the start of the file system, - * so one might get a false positive if entering a phys_addr somewhere in the - * middle of the file system at block boundary. In addition, it is not checked - * if the page size is actually correct. If it is not, weird file system sizes - * will be returned. - * - * If this function detects a file system it returns the assumed file system - * size, which can be used to set the phys_size. - * - * Otherwise, it returns an error indicating why it is not regarded as a file - * system. - * - * Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK - * macros. It returns the error code directly, instead of as read by - * SPIFFS_errno. - * - * @param config essential parts of the physical and logical - * configuration of the file system. - */ + Special function. This takes a spiffs config struct and returns the number + of blocks this file system was formatted with. This function relies on + that following info is set correctly in given config struct: + + phys_addr, log_page_size, and log_block_size. + + Also, hal_read_f must be set in the config struct. + + One must be sure of the correct page size and that the physical address is + correct in the probed file system when calling this function. It is not + checked if the phys_addr actually points to the start of the file system, + so one might get a false positive if entering a phys_addr somewhere in the + middle of the file system at block boundary. In addition, it is not checked + if the page size is actually correct. If it is not, weird file system sizes + will be returned. + + If this function detects a file system it returns the assumed file system + size, which can be used to set the phys_size. + + Otherwise, it returns an error indicating why it is not regarded as a file + system. + + Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK + macros. It returns the error code directly, instead of as read by + SPIFFS_errno. + + @param config essential parts of the physical and logical + configuration of the file system. +*/ s32_t SPIFFS_probe_fs(spiffs_config *config); #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 /** - * Initializes the file system dynamic parameters and mounts the filesystem. - * If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS - * if the flash does not contain a recognizable file system. - * In this case, SPIFFS_format must be called prior to remounting. - * @param fs the file system struct - * @param config the physical and logical configuration of the file system - * @param work a memory work buffer comprising 2*config->log_page_size - * bytes used throughout all file system operations - * @param fd_space memory for file descriptors - * @param fd_space_size memory size of file descriptors - * @param cache memory for cache, may be null - * @param cache_size memory size of cache - * @param check_cb_f callback function for reporting during consistency checks - */ + Initializes the file system dynamic parameters and mounts the filesystem. + If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS + if the flash does not contain a recognizable file system. + In this case, SPIFFS_format must be called prior to remounting. + @param fs the file system struct + @param config the physical and logical configuration of the file system + @param work a memory work buffer comprising 2*config->log_page_size + bytes used throughout all file system operations + @param fd_space memory for file descriptors + @param fd_space_size memory size of file descriptors + @param cache memory for cache, may be null + @param cache_size memory size of cache + @param check_cb_f callback function for reporting during consistency checks +*/ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, - u8_t *fd_space, u32_t fd_space_size, - void *cache, u32_t cache_size, - spiffs_check_callback check_cb_f); + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f); /** - * Unmounts the file system. All file handles will be flushed of any - * cached writes and closed. - * @param fs the file system struct - */ + Unmounts the file system. All file handles will be flushed of any + cached writes and closed. + @param fs the file system struct +*/ void SPIFFS_unmount(spiffs *fs); /** - * Creates a new file. - * @param fs the file system struct - * @param path the path of the new file - * @param mode ignored, for posix compliance - */ + Creates a new file. + @param fs the file system struct + @param path the path of the new file + @param mode ignored, for posix compliance +*/ s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode); /** - * Opens/creates a file. - * @param fs the file system struct - * @param path the path of the new file - * @param flags the flags for the open command, can be combinations of - * SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY, - * SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL - * @param mode ignored, for posix compliance - */ + Opens/creates a file. + @param fs the file system struct + @param path the path of the new file + @param flags the flags for the open command, can be combinations of + SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY, + SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL + @param mode ignored, for posix compliance +*/ spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode); /** - * Opens a file by given dir entry. - * Optimization purposes, when traversing a file system with SPIFFS_readdir - * a normal SPIFFS_open would need to traverse the filesystem again to find - * the file, whilst SPIFFS_open_by_dirent already knows where the file resides. - * @param fs the file system struct - * @param e the dir entry to the file - * @param flags the flags for the open command, can be combinations of - * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, - * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. - * SPIFFS_CREAT will have no effect in this case. - * @param mode ignored, for posix compliance - */ + Opens a file by given dir entry. + Optimization purposes, when traversing a file system with SPIFFS_readdir + a normal SPIFFS_open would need to traverse the filesystem again to find + the file, whilst SPIFFS_open_by_dirent already knows where the file resides. + @param fs the file system struct + @param e the dir entry to the file + @param flags the flags for the open command, can be combinations of + SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, + SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. + SPIFFS_CREAT will have no effect in this case. + @param mode ignored, for posix compliance +*/ spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode); /** - * Opens a file by given page index. - * Optimization purposes, opens a file by directly pointing to the page - * index in the spi flash. - * If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE - * is returned. - * @param fs the file system struct - * @param page_ix the page index - * @param flags the flags for the open command, can be combinations of - * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, - * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. - * SPIFFS_CREAT will have no effect in this case. - * @param mode ignored, for posix compliance - */ + Opens a file by given page index. + Optimization purposes, opens a file by directly pointing to the page + index in the spi flash. + If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE + is returned. + @param fs the file system struct + @param page_ix the page index + @param flags the flags for the open command, can be combinations of + SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, + SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. + SPIFFS_CREAT will have no effect in this case. + @param mode ignored, for posix compliance +*/ spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode); /** - * Reads from given filehandle. - * @param fs the file system struct - * @param fh the filehandle - * @param buf where to put read data - * @param len how much to read - * @returns number of bytes read, or -1 if error - */ + Reads from given filehandle. + @param fs the file system struct + @param fh the filehandle + @param buf where to put read data + @param len how much to read + @returns number of bytes read, or -1 if error +*/ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); /** - * Writes to given filehandle. - * @param fs the file system struct - * @param fh the filehandle - * @param buf the data to write - * @param len how much to write - * @returns number of bytes written, or -1 if error - */ + Writes to given filehandle. + @param fs the file system struct + @param fh the filehandle + @param buf the data to write + @param len how much to write + @returns number of bytes written, or -1 if error +*/ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); /** - * Moves the read/write file offset. Resulting offset is returned or negative if error. - * lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset. - * @param fs the file system struct - * @param fh the filehandle - * @param offs how much/where to move the offset - * @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes - * if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset - * if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative - */ + Moves the read/write file offset. Resulting offset is returned or negative if error. + lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset. + @param fs the file system struct + @param fh the filehandle + @param offs how much/where to move the offset + @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes + if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset + if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative +*/ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); /** - * Removes a file by path - * @param fs the file system struct - * @param path the path of the file to remove - */ + Removes a file by path + @param fs the file system struct + @param path the path of the file to remove +*/ s32_t SPIFFS_remove(spiffs *fs, const char *path); /** - * Removes a file by filehandle - * @param fs the file system struct - * @param fh the filehandle of the file to remove - */ + Removes a file by filehandle + @param fs the file system struct + @param fh the filehandle of the file to remove +*/ s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh); /** - * Gets file status by path - * @param fs the file system struct - * @param path the path of the file to stat - * @param s the stat struct to populate - */ + Gets file status by path + @param fs the file system struct + @param path the path of the file to stat + @param s the stat struct to populate +*/ s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s); /** - * Gets file status by filehandle - * @param fs the file system struct - * @param fh the filehandle of the file to stat - * @param s the stat struct to populate - */ + Gets file status by filehandle + @param fs the file system struct + @param fh the filehandle of the file to stat + @param s the stat struct to populate +*/ s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s); /** - * Flushes all pending write operations from cache for given file - * @param fs the file system struct - * @param fh the filehandle of the file to flush - */ + Flushes all pending write operations from cache for given file + @param fs the file system struct + @param fh the filehandle of the file to flush +*/ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); /** - * Closes a filehandle. If there are pending write operations, these are finalized before closing. - * @param fs the file system struct - * @param fh the filehandle of the file to close - */ + Closes a filehandle. If there are pending write operations, these are finalized before closing. + @param fs the file system struct + @param fh the filehandle of the file to close +*/ s32_t SPIFFS_close(spiffs *fs, spiffs_file fh); /** - * Renames a file - * @param fs the file system struct - * @param old path of file to rename - * @param newPath new path of file - */ + Renames a file + @param fs the file system struct + @param old path of file to rename + @param newPath new path of file +*/ s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath); #if SPIFFS_OBJ_META_LEN /** - * Updates file's metadata - * @param fs the file system struct - * @param path path to the file - * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. - */ + Updates file's metadata + @param fs the file system struct + @param path path to the file + @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. +*/ s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta); /** - * Updates file's metadata - * @param fs the file system struct - * @param fh file handle of the file - * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. - */ + Updates file's metadata + @param fs the file system struct + @param fh file handle of the file + @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. +*/ s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta); #endif /** - * Returns last error of last file operation. - * @param fs the file system struct - */ + Returns last error of last file operation. + @param fs the file system struct +*/ s32_t SPIFFS_errno(spiffs *fs); /** - * Clears last error. - * @param fs the file system struct - */ + Clears last error. + @param fs the file system struct +*/ void SPIFFS_clearerr(spiffs *fs); /** - * Opens a directory stream corresponding to the given name. - * The stream is positioned at the first entry in the directory. - * On hydrogen builds the name argument is ignored as hydrogen builds always correspond - * to a flat file structure - no directories. - * @param fs the file system struct - * @param name the name of the directory - * @param d pointer the directory stream to be populated - */ + Opens a directory stream corresponding to the given name. + The stream is positioned at the first entry in the directory. + On hydrogen builds the name argument is ignored as hydrogen builds always correspond + to a flat file structure - no directories. + @param fs the file system struct + @param name the name of the directory + @param d pointer the directory stream to be populated +*/ spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); /** - * Closes a directory stream - * @param d the directory stream to close - */ + Closes a directory stream + @param d the directory stream to close +*/ s32_t SPIFFS_closedir(spiffs_DIR *d); /** - * Reads a directory into given spifs_dirent struct. - * @param d pointer to the directory stream - * @param e the dirent struct to be populated - * @returns null if error or end of stream, else given dirent is returned - */ + Reads a directory into given spifs_dirent struct. + @param d pointer to the directory stream + @param e the dirent struct to be populated + @returns null if error or end of stream, else given dirent is returned +*/ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); /** - * Runs a consistency check on given filesystem. - * @param fs the file system struct - */ + Runs a consistency check on given filesystem. + @param fs the file system struct +*/ s32_t SPIFFS_check(spiffs *fs); /** - * Returns number of total bytes available and number of used bytes. - * This is an estimation, and depends on if there a many files with little - * data or few files with much data. - * NB: If used number of bytes exceeds total bytes, a SPIFFS_check should - * run. This indicates a power loss in midst of things. In worst case - * (repeated powerlosses in mending or gc) you might have to delete some files. - * - * @param fs the file system struct - * @param total total number of bytes in filesystem - * @param used used number of bytes in filesystem - */ + Returns number of total bytes available and number of used bytes. + This is an estimation, and depends on if there a many files with little + data or few files with much data. + NB: If used number of bytes exceeds total bytes, a SPIFFS_check should + run. This indicates a power loss in midst of things. In worst case + (repeated powerlosses in mending or gc) you might have to delete some files. + + @param fs the file system struct + @param total total number of bytes in filesystem + @param used used number of bytes in filesystem +*/ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used); /** - * Formats the entire file system. All data will be lost. - * The filesystem must not be mounted when calling this. - * - * NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount - * MUST be called prior to formatting in order to configure the filesystem. - * If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling - * SPIFFS_format. - * If SPIFFS_mount fails, SPIFFS_format can be called directly without calling - * SPIFFS_unmount first. - * - * @param fs the file system struct - */ + Formats the entire file system. All data will be lost. + The filesystem must not be mounted when calling this. + + NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount + MUST be called prior to formatting in order to configure the filesystem. + If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling + SPIFFS_format. + If SPIFFS_mount fails, SPIFFS_format can be called directly without calling + SPIFFS_unmount first. + + @param fs the file system struct +*/ s32_t SPIFFS_format(spiffs *fs); /** - * Returns nonzero if spiffs is mounted, or zero if unmounted. - * @param fs the file system struct - */ + Returns nonzero if spiffs is mounted, or zero if unmounted. + @param fs the file system struct +*/ u8_t SPIFFS_mounted(spiffs *fs); /** - * Tries to find a block where most or all pages are deleted, and erase that - * block if found. Does not care for wear levelling. Will not move pages - * around. - * If parameter max_free_pages are set to 0, only blocks with only deleted - * pages will be selected. - * - * NB: the garbage collector is automatically called when spiffs needs free - * pages. The reason for this function is to give possibility to do background - * tidying when user knows the system is idle. - * - * Use with care. - * - * Setting max_free_pages to anything larger than zero will eventually wear - * flash more as a block containing free pages can be erased. - * - * Will set err_no to SPIFFS_OK if a block was found and erased, - * SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found, - * or other error. - * - * @param fs the file system struct - * @param max_free_pages maximum number allowed free pages in block - */ + Tries to find a block where most or all pages are deleted, and erase that + block if found. Does not care for wear levelling. Will not move pages + around. + If parameter max_free_pages are set to 0, only blocks with only deleted + pages will be selected. + + NB: the garbage collector is automatically called when spiffs needs free + pages. The reason for this function is to give possibility to do background + tidying when user knows the system is idle. + + Use with care. + + Setting max_free_pages to anything larger than zero will eventually wear + flash more as a block containing free pages can be erased. + + Will set err_no to SPIFFS_OK if a block was found and erased, + SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found, + or other error. + + @param fs the file system struct + @param max_free_pages maximum number allowed free pages in block +*/ s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages); /** - * Will try to make room for given amount of bytes in the filesystem by moving - * pages and erasing blocks. - * If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If - * there already is this amount (or more) of free space, SPIFFS_gc will - * silently return. It is recommended to call SPIFFS_info before invoking - * this method in order to determine what amount of bytes to give. - * - * NB: the garbage collector is automatically called when spiffs needs free - * pages. The reason for this function is to give possibility to do background - * tidying when user knows the system is idle. - * - * Use with care. - * - * @param fs the file system struct - * @param size amount of bytes that should be freed - */ + Will try to make room for given amount of bytes in the filesystem by moving + pages and erasing blocks. + If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If + there already is this amount (or more) of free space, SPIFFS_gc will + silently return. It is recommended to call SPIFFS_info before invoking + this method in order to determine what amount of bytes to give. + + NB: the garbage collector is automatically called when spiffs needs free + pages. The reason for this function is to give possibility to do background + tidying when user knows the system is idle. + + Use with care. + + @param fs the file system struct + @param size amount of bytes that should be freed +*/ s32_t SPIFFS_gc(spiffs *fs, u32_t size); /** - * Check if EOF reached. - * @param fs the file system struct - * @param fh the filehandle of the file to check - */ + Check if EOF reached. + @param fs the file system struct + @param fh the filehandle of the file to check +*/ s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); /** - * Get position in file. - * @param fs the file system struct - * @param fh the filehandle of the file to check - */ + Get position in file. + @param fs the file system struct + @param fh the filehandle of the file to check +*/ s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); /** - * Registers a callback function that keeps track on operations on file - * headers. Do note, that this callback is called from within internal spiffs - * mechanisms. Any operations on the actual file system being callbacked from - * in this callback will mess things up for sure - do not do this. - * This can be used to track where files are and move around during garbage - * collection, which in turn can be used to build location tables in ram. - * Used in conjuction with SPIFFS_open_by_page this may improve performance - * when opening a lot of files. - * Must be invoked after mount. - * - * @param fs the file system struct - * @param cb_func the callback on file operations - */ + Registers a callback function that keeps track on operations on file + headers. Do note, that this callback is called from within internal spiffs + mechanisms. Any operations on the actual file system being callbacked from + in this callback will mess things up for sure - do not do this. + This can be used to track where files are and move around during garbage + collection, which in turn can be used to build location tables in ram. + Used in conjuction with SPIFFS_open_by_page this may improve performance + when opening a lot of files. + Must be invoked after mount. + + @param fs the file system struct + @param cb_func the callback on file operations +*/ s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func); #if SPIFFS_IX_MAP /** - * Maps the first level index lookup to a given memory map. - * This will make reading big files faster, as the memory map will be used for - * looking up data pages instead of searching for the indices on the physical - * medium. When mapping, all affected indicies are found and the information is - * copied to the array. - * Whole file or only parts of it may be mapped. The index map will cover file - * contents from argument offset until and including arguments (offset+len). - * It is valid to map a longer range than the current file size. The map will - * then be populated when the file grows. - * On garbage collections and file data page movements, the map array will be - * automatically updated. Do not tamper with the map array, as this contains - * the references to the data pages. Modifying it from outside will corrupt any - * future readings using this file descriptor. - * The map will no longer be used when the file descriptor closed or the file - * is unmapped. - * This can be useful to get faster and more deterministic timing when reading - * large files, or when seeking and reading a lot within a file. - * @param fs the file system struct - * @param fh the file handle of the file to map - * @param map a spiffs_ix_map struct, describing the index map - * @param offset absolute file offset where to start the index map - * @param len length of the mapping in actual file bytes - * @param map_buf the array buffer for the look up data - number of required - * elements in the array can be derived from function - * SPIFFS_bytes_to_ix_map_entries given the length - */ + Maps the first level index lookup to a given memory map. + This will make reading big files faster, as the memory map will be used for + looking up data pages instead of searching for the indices on the physical + medium. When mapping, all affected indicies are found and the information is + copied to the array. + Whole file or only parts of it may be mapped. The index map will cover file + contents from argument offset until and including arguments (offset+len). + It is valid to map a longer range than the current file size. The map will + then be populated when the file grows. + On garbage collections and file data page movements, the map array will be + automatically updated. Do not tamper with the map array, as this contains + the references to the data pages. Modifying it from outside will corrupt any + future readings using this file descriptor. + The map will no longer be used when the file descriptor closed or the file + is unmapped. + This can be useful to get faster and more deterministic timing when reading + large files, or when seeking and reading a lot within a file. + @param fs the file system struct + @param fh the file handle of the file to map + @param map a spiffs_ix_map struct, describing the index map + @param offset absolute file offset where to start the index map + @param len length of the mapping in actual file bytes + @param map_buf the array buffer for the look up data - number of required + elements in the array can be derived from function + SPIFFS_bytes_to_ix_map_entries given the length +*/ s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, - u32_t offset, u32_t len, spiffs_page_ix *map_buf); - -/** - * Unmaps the index lookup from this filehandle. All future readings will - * proceed as normal, requiring reading of the first level indices from - * physical media. - * The map and map buffer given in function SPIFFS_ix_map will no longer be - * referenced by spiffs. - * It is not strictly necessary to unmap a file before closing it, as closing - * a file will automatically unmap it. - * @param fs the file system struct - * @param fh the file handle of the file to unmap - */ + u32_t offset, u32_t len, spiffs_page_ix *map_buf); + +/** + Unmaps the index lookup from this filehandle. All future readings will + proceed as normal, requiring reading of the first level indices from + physical media. + The map and map buffer given in function SPIFFS_ix_map will no longer be + referenced by spiffs. + It is not strictly necessary to unmap a file before closing it, as closing + a file will automatically unmap it. + @param fs the file system struct + @param fh the file handle of the file to unmap +*/ s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh); /** - * Moves the offset for the index map given in function SPIFFS_ix_map. Parts or - * all of the map buffer will repopulated. - * @param fs the file system struct - * @param fh the mapped file handle of the file to remap - * @param offset new absolute file offset where to start the index map - */ + Moves the offset for the index map given in function SPIFFS_ix_map. Parts or + all of the map buffer will repopulated. + @param fs the file system struct + @param fh the mapped file handle of the file to remap + @param offset new absolute file offset where to start the index map +*/ s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs); /** - * Utility function to get number of spiffs_page_ix entries a map buffer must - * contain on order to map given amount of file data in bytes. - * See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes. - * @param fs the file system struct - * @param bytes number of file data bytes to map - * @return needed number of elements in a spiffs_page_ix array needed to - * map given amount of bytes in a file - */ + Utility function to get number of spiffs_page_ix entries a map buffer must + contain on order to map given amount of file data in bytes. + See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes. + @param fs the file system struct + @param bytes number of file data bytes to map + @return needed number of elements in a spiffs_page_ix array needed to + map given amount of bytes in a file +*/ s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes); /** - * Utility function to amount of file data bytes that can be mapped when - * mapping a file with buffer having given number of spiffs_page_ix entries. - * See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries. - * @param fs the file system struct - * @param map_page_ix_entries number of entries in a spiffs_page_ix array - * @return amount of file data in bytes that can be mapped given a map - * buffer having given amount of spiffs_page_ix entries - */ + Utility function to amount of file data bytes that can be mapped when + mapping a file with buffer having given number of spiffs_page_ix entries. + See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries. + @param fs the file system struct + @param map_page_ix_entries number of entries in a spiffs_page_ix array + @return amount of file data in bytes that can be mapped given a map + buffer having given amount of spiffs_page_ix entries +*/ s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries); #endif // SPIFFS_IX_MAP @@ -785,24 +794,24 @@ s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries); #if SPIFFS_TEST_VISUALISATION /** - * Prints out a visualization of the filesystem. - * @param fs the file system struct - */ + Prints out a visualization of the filesystem. + @param fs the file system struct +*/ s32_t SPIFFS_vis(spiffs *fs); #endif #if SPIFFS_BUFFER_HELP /** - * Returns number of bytes needed for the filedescriptor buffer given - * amount of file descriptors. - */ + Returns number of bytes needed for the filedescriptor buffer given + amount of file descriptors. +*/ u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs); #if SPIFFS_CACHE /** - * Returns number of bytes needed for the cache buffer given - * amount of cache pages. - */ + Returns number of bytes needed for the cache buffer given + amount of cache pages. +*/ u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages); #endif #endif diff --git a/cores/esp8266/spiffs/spiffs_cache.cpp b/cores/esp8266/spiffs/spiffs_cache.cpp index 3a195b5b09..000446dcb3 100644 --- a/cores/esp8266/spiffs/spiffs_cache.cpp +++ b/cores/esp8266/spiffs/spiffs_cache.cpp @@ -1,9 +1,9 @@ /* - * spiffs_cache.c - * - * Created on: Jun 23, 2013 - * Author: petera - */ + spiffs_cache.c + + Created on: Jun 23, 2013 + Author: petera +*/ #include "spiffs.h" #include "spiffs_nucleus.h" @@ -12,311 +12,372 @@ extern "C" { -// returns cached page for give page index, or null if no such cached page -static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) { - spiffs_cache *cache = spiffs_get_cache(fs); - if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0; - int i; - for (i = 0; i < cache->cpage_count; i++) { - spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); - if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && - cp->pix == pix ) { - //SPIFFS_CACHE_DBG("CACHE_GET: have cache page " _SPIPRIi " for " _SPIPRIpg"\n", i, pix); - cp->last_access = cache->last_access; - return cp; - } - } - //SPIFFS_CACHE_DBG("CACHE_GET: no cache for " _SPIPRIpg"\n", pix); - return 0; -} - -// frees cached page -static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) { - s32_t res = SPIFFS_OK; - spiffs_cache *cache = spiffs_get_cache(fs); - spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix); - if (cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && - (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { - u8_t *mem = spiffs_get_cache_page(fs, cache, ix); - SPIFFS_CACHE_DBG("CACHE_FREE: write cache page " _SPIPRIi " pix " _SPIPRIpg "\n", ix, cp->pix); - res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + // returns cached page for give page index, or null if no such cached page + static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) + { + spiffs_cache *cache = spiffs_get_cache(fs); + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) + { + return 0; + } + int i; + for (i = 0; i < cache->cpage_count; i++) + { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1 << i)) && + (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + cp->pix == pix) + { + //SPIFFS_CACHE_DBG("CACHE_GET: have cache page " _SPIPRIi " for " _SPIPRIpg"\n", i, pix); + cp->last_access = cache->last_access; + return cp; + } + } + //SPIFFS_CACHE_DBG("CACHE_GET: no cache for " _SPIPRIpg"\n", pix); + return 0; } + // frees cached page + static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) + { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix); + if (cache->cpage_use_map & (1 << ix)) + { + if (write_back && + (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) + { + u8_t *mem = spiffs_get_cache_page(fs, cache, ix); + SPIFFS_CACHE_DBG("CACHE_FREE: write cache page " _SPIPRIi " pix " _SPIPRIpg "\n", ix, cp->pix); + res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + } + #if SPIFFS_CACHE_WR - if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) { - SPIFFS_CACHE_DBG("CACHE_FREE: free cache page " _SPIPRIi " objid " _SPIPRIid "\n", ix, cp->obj_id); - } else + if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) + { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page " _SPIPRIi " objid " _SPIPRIid "\n", ix, cp->obj_id); + } + else #endif + { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page " _SPIPRIi " pix " _SPIPRIpg "\n", ix, cp->pix); + } + cache->cpage_use_map &= ~(1 << ix); + cp->flags = 0; + } + + return res; + } + + // removes the oldest accessed cached page + static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) { - SPIFFS_CACHE_DBG("CACHE_FREE: free cache page " _SPIPRIi " pix " _SPIPRIpg "\n", ix, cp->pix); + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) + { + // at least one free cpage + return SPIFFS_OK; + } + + // all busy, scan thru all to find the cpage which has oldest access + int i; + int cand_ix = -1; + u32_t oldest_val = 0; + for (i = 0; i < cache->cpage_count; i++) + { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->last_access - cp->last_access) > oldest_val && + (cp->flags & flag_mask) == flags) + { + oldest_val = cache->last_access - cp->last_access; + cand_ix = i; + } + } + + if (cand_ix >= 0) + { + res = spiffs_cache_page_free(fs, cand_ix, 1); + } + + return res; } - cache->cpage_use_map &= ~(1 << ix); - cp->flags = 0; - } - - return res; -} - -// removes the oldest accessed cached page -static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) { - s32_t res = SPIFFS_OK; - spiffs_cache *cache = spiffs_get_cache(fs); - - if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) { - // at least one free cpage - return SPIFFS_OK; - } - - // all busy, scan thru all to find the cpage which has oldest access - int i; - int cand_ix = -1; - u32_t oldest_val = 0; - for (i = 0; i < cache->cpage_count; i++) { - spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); - if ((cache->last_access - cp->last_access) > oldest_val && - (cp->flags & flag_mask) == flags) { - oldest_val = cache->last_access - cp->last_access; - cand_ix = i; + + // allocates a new cached page and returns it, or null if all cache pages are busy + static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) + { + spiffs_cache *cache = spiffs_get_cache(fs); + if (cache->cpage_use_map == 0xffffffff) + { + // out of cache memory + return 0; + } + int i; + for (i = 0; i < cache->cpage_count; i++) + { + if ((cache->cpage_use_map & (1 << i)) == 0) + { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + cache->cpage_use_map |= (1 << i); + cp->last_access = cache->last_access; + //SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page " _SPIPRIi"\n", i); + return cp; + } + } + // out of cache entries + return 0; } - } - - if (cand_ix >= 0) { - res = spiffs_cache_page_free(fs, cand_ix, 1); - } - - return res; -} - -// allocates a new cached page and returns it, or null if all cache pages are busy -static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) { - spiffs_cache *cache = spiffs_get_cache(fs); - if (cache->cpage_use_map == 0xffffffff) { - // out of cache memory - return 0; - } - int i; - for (i = 0; i < cache->cpage_count; i++) { - if ((cache->cpage_use_map & (1<cpage_use_map |= (1<last_access = cache->last_access; - //SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page " _SPIPRIi"\n", i); - return cp; + + // drops the cache page for give page index + void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) + { + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + if (cp) + { + spiffs_cache_page_free(fs, cp->ix, 0); + } } - } - // out of cache entries - return 0; -} - -// drops the cache page for give page index -void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) { - spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); - if (cp) { - spiffs_cache_page_free(fs, cp->ix, 0); - } -} - -// ------------------------------ - -// reads from spi flash or the cache -s32_t spiffs_phys_rd( - spiffs *fs, - u8_t op, - spiffs_file fh, - u32_t addr, - u32_t len, - u8_t *dst) { - (void)fh; - s32_t res = SPIFFS_OK; - spiffs_cache *cache = spiffs_get_cache(fs); - spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr)); - cache->last_access++; - if (cp) { - // we've already got one, you see + + // ------------------------------ + + // reads from spi flash or the cache + s32_t spiffs_phys_rd( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *dst) + { + (void)fh; + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr)); + cache->last_access++; + if (cp) + { + // we've already got one, you see #if SPIFFS_CACHE_STATS - fs->cache_hits++; + fs->cache_hits++; #endif - cp->last_access = cache->last_access; - u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); - _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); - } else { - if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) { - // for second layer lookup functions, we do not cache in order to prevent shredding - return SPIFFS_HAL_READ(fs, addr, len, dst); - } + cp->last_access = cache->last_access; + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + } + else + { + if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) + { + // for second layer lookup functions, we do not cache in order to prevent shredding + return SPIFFS_HAL_READ(fs, addr, len, dst); + } #if SPIFFS_CACHE_STATS - fs->cache_misses++; + fs->cache_misses++; #endif - // this operation will always free one cache page (unless all already free), - // the result code stems from the write operation of the possibly freed cache page - res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); - - cp = spiffs_cache_page_allocate(fs); - if (cp) { - cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; - cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr); - SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page " _SPIPRIi " for pix " _SPIPRIpg "\n", cp->ix, cp->pix); - - s32_t res2 = SPIFFS_HAL_READ(fs, - addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), - SPIFFS_CFG_LOG_PAGE_SZ(fs), - spiffs_get_cache_page(fs, cache, cp->ix)); - if (res2 != SPIFFS_OK) { - // honor read failure before possible write failure (bad idea?) - res = res2; - } - u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); - _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); - } else { - // this will never happen, last resort for sake of symmetry - s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst); - if (res2 != SPIFFS_OK) { - // honor read failure before possible write failure (bad idea?) - res = res2; - } + // this operation will always free one cache page (unless all already free), + // the result code stems from the write operation of the possibly freed cache page + res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + + cp = spiffs_cache_page_allocate(fs); + if (cp) + { + cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; + cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page " _SPIPRIi " for pix " _SPIPRIpg "\n", cp->ix, cp->pix); + + s32_t res2 = SPIFFS_HAL_READ(fs, + addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + spiffs_get_cache_page(fs, cache, cp->ix)); + if (res2 != SPIFFS_OK) + { + // honor read failure before possible write failure (bad idea?) + res = res2; + } + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + } + else + { + // this will never happen, last resort for sake of symmetry + s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst); + if (res2 != SPIFFS_OK) + { + // honor read failure before possible write failure (bad idea?) + res = res2; + } + } + } + return res; } - } - return res; -} - -// writes to spi flash and/or the cache -s32_t spiffs_phys_wr( - spiffs *fs, - u8_t op, - spiffs_file fh, - u32_t addr, - u32_t len, - u8_t *src) { - (void)fh; - spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr); - spiffs_cache *cache = spiffs_get_cache(fs); - spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); - - if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) { - // have a cache page - // copy in data to cache page - - if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE && - (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) { - // page is being deleted, wipe from cache - unless it is a lookup page - spiffs_cache_page_free(fs, cp->ix, 0); - return SPIFFS_HAL_WRITE(fs, addr, len, src); - } - - u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); - _SPIFFS_MEMCPY(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); - cache->last_access++; - cp->last_access = cache->last_access; - - if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) { - // page is being updated, no write-cache, just pass thru - return SPIFFS_HAL_WRITE(fs, addr, len, src); - } else { - return SPIFFS_OK; + // writes to spi flash and/or the cache + s32_t spiffs_phys_wr( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *src) + { + (void)fh; + spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + + if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) + { + // have a cache page + // copy in data to cache page + + if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE && + (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) + { + // page is being deleted, wipe from cache - unless it is a lookup page + spiffs_cache_page_free(fs, cp->ix, 0); + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } + + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + _SPIFFS_MEMCPY(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); + + cache->last_access++; + cp->last_access = cache->last_access; + + if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) + { + // page is being updated, no write-cache, just pass thru + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } + else + { + return SPIFFS_OK; + } + } + else + { + // no cache page, no write cache - just write thru + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } } - } else { - // no cache page, no write cache - just write thru - return SPIFFS_HAL_WRITE(fs, addr, len, src); - } -} #if SPIFFS_CACHE_WR -// returns the cache page that this fd refers, or null if no cache page -spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) { - spiffs_cache *cache = spiffs_get_cache(fs); - - if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) { - // all cpages free, no cpage cannot be assigned to obj_id - return 0; - } - - int i; - for (i = 0; i < cache->cpage_count; i++) { - spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); - if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) && - cp->obj_id == fd->obj_id) { - return cp; + // returns the cache page that this fd refers, or null if no cache page + spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) + { + spiffs_cache *cache = spiffs_get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) + { + // all cpages free, no cpage cannot be assigned to obj_id + return 0; + } + + int i; + for (i = 0; i < cache->cpage_count; i++) + { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1 << i)) && + (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) && + cp->obj_id == fd->obj_id) + { + return cp; + } + } + + return 0; } - } - - return 0; -} - -// allocates a new cache page and refers this to given fd - flushes an old cache -// page if all cache is busy -spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) { - // before this function is called, it is ensured that there is no already existing - // cache page with same object id - spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); - spiffs_cache_page *cp = spiffs_cache_page_allocate(fs); - if (cp == 0) { - // could not get cache page - return 0; - } - - cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR; - cp->obj_id = fd->obj_id; - fd->cache_page = cp; - SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id); - return cp; -} - -// unrefers all fds that this cache page refers to and releases the cache page -void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) { - if (cp == 0) return; - u32_t i; - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) { - cur_fd->cache_page = 0; + + // allocates a new cache page and refers this to given fd - flushes an old cache + // page if all cache is busy + spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) + { + // before this function is called, it is ensured that there is no already existing + // cache page with same object id + spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + spiffs_cache_page *cp = spiffs_cache_page_allocate(fs); + if (cp == 0) + { + // could not get cache page + return 0; + } + + cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR; + cp->obj_id = fd->obj_id; + fd->cache_page = cp; + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id); + return cp; } - } - spiffs_cache_page_free(fs, cp->ix, 0); - cp->obj_id = 0; -} + // unrefers all fds that this cache page refers to and releases the cache page + void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) + { + if (cp == 0) + { + return; + } + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) + { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) + { + cur_fd->cache_page = 0; + } + } + spiffs_cache_page_free(fs, cp->ix, 0); + + cp->obj_id = 0; + } #endif -// initializes the cache -void spiffs_cache_init(spiffs *fs) { - if (fs->cache == 0) return; - u32_t sz = fs->cache_size; - u32_t cache_mask = 0; - int i; - int cache_entries = - (sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs)); - if (cache_entries <= 0) return; - - for (i = 0; i < cache_entries; i++) { - cache_mask <<= 1; - cache_mask |= 1; - } - - spiffs_cache cache; - memset(&cache, 0, sizeof(spiffs_cache)); - cache.cpage_count = cache_entries; - cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache)); - - cache.cpage_use_map = 0xffffffff; - cache.cpage_use_mask = cache_mask; - _SPIFFS_MEMCPY(fs->cache, &cache, sizeof(spiffs_cache)); - - spiffs_cache *c = spiffs_get_cache(fs); - - memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs)); - - c->cpage_use_map &= ~(c->cpage_use_mask); - for (i = 0; i < cache.cpage_count; i++) { - spiffs_get_cache_page_hdr(fs, c, i)->ix = i; - } -} + // initializes the cache + void spiffs_cache_init(spiffs *fs) + { + if (fs->cache == 0) + { + return; + } + u32_t sz = fs->cache_size; + u32_t cache_mask = 0; + int i; + int cache_entries = + (sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs)); + if (cache_entries <= 0) + { + return; + } + + for (i = 0; i < cache_entries; i++) + { + cache_mask <<= 1; + cache_mask |= 1; + } + + spiffs_cache cache; + memset(&cache, 0, sizeof(spiffs_cache)); + cache.cpage_count = cache_entries; + cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache)); + + cache.cpage_use_map = 0xffffffff; + cache.cpage_use_mask = cache_mask; + _SPIFFS_MEMCPY(fs->cache, &cache, sizeof(spiffs_cache)); + + spiffs_cache *c = spiffs_get_cache(fs); + + memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs)); + + c->cpage_use_map &= ~(c->cpage_use_mask); + for (i = 0; i < cache.cpage_count; i++) + { + spiffs_get_cache_page_hdr(fs, c, i)->ix = i; + } + } }; #endif // SPIFFS_CACHE diff --git a/cores/esp8266/spiffs/spiffs_check.cpp b/cores/esp8266/spiffs/spiffs_check.cpp index 258efb30c7..b56e25b2b0 100644 --- a/cores/esp8266/spiffs/spiffs_check.cpp +++ b/cores/esp8266/spiffs/spiffs_check.cpp @@ -1,23 +1,23 @@ /* - * spiffs_check.c - * - * Contains functionality for checking file system consistency - * and mending problems. - * Three levels of consistency checks are implemented: - * - * Look up consistency - * Checks if indices in lookup pages are coherent with page headers - * Object index consistency - * Checks if there are any orphaned object indices (missing object index headers). - * If an object index is found but not its header, the object index is deleted. - * This is critical for the following page consistency check. - * Page consistency - * Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed - * - * - * Created on: Jul 7, 2013 - * Author: petera - */ + spiffs_check.c + + Contains functionality for checking file system consistency + and mending problems. + Three levels of consistency checks are implemented: + + Look up consistency + Checks if indices in lookup pages are coherent with page headers + Object index consistency + Checks if there are any orphaned object indices (missing object index headers). + If an object index is found but not its header, the object index is deleted. + This is critical for the following page consistency check. + Page consistency + Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed + + + Created on: Jul 7, 2013 + Author: petera +*/ #include "spiffs.h" @@ -39,960 +39,1126 @@ extern "C" { -//--------------------------------------- -// Look up consistency - -// searches in the object indices and returns the referenced page index given -// the object id and the data span index -// destroys fs->lu_work -static s32_t spiffs_object_get_data_page_index_reference( - spiffs *fs, - spiffs_obj_id obj_id, - spiffs_span_ix data_spix, - spiffs_page_ix *pix, - spiffs_page_ix *objix_pix) { - s32_t res; - - // calculate object index span index for given data page span index - spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - - // find obj index for obj id and span index - res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix); - SPIFFS_CHECK_RES(res); - - // load obj index entry - u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix); - if (objix_spix == 0) { - // get referenced page from object index header - addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix); - } else { - // get referenced page from object index - addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix); - } - - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix); - - return res; -} - -// copies page contents to a new page -static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) { - s32_t res; - res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix); - SPIFFS_CHECK_RES(res); - res = spiffs_phys_cpy(fs, 0, - SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header), - SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), - SPIFFS_DATA_PAGE_SIZE(fs)); - SPIFFS_CHECK_RES(res); - return res; -} - -// rewrites the object index for given object id and replaces the -// data page index to a new page index -static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) { - s32_t res; - spiffs_block_ix bix; - int entry; - spiffs_page_ix free_pix; - obj_id |= SPIFFS_OBJ_ID_IX_FLAG; - - // find free entry - res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); - SPIFFS_CHECK_RES(res); - free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - - // calculate object index span index for given data page span index - spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - if (objix_spix == 0) { - // calc index in index header - entry = data_spix; - } else { - // calc entry in index - entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix); - - } - // load index - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - SPIFFS_CHECK_RES(res); - spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; - - // be ultra safe, double check header against provided data - if (objix_p_hdr->obj_id != obj_id) { - spiffs_page_delete(fs, free_pix); - return SPIFFS_ERR_CHECK_OBJ_ID_MISM; - } - if (objix_p_hdr->span_ix != objix_spix) { - spiffs_page_delete(fs, free_pix); - return SPIFFS_ERR_CHECK_SPIX_MISM; - } - if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX | - SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) != - (SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) { - spiffs_page_delete(fs, free_pix); - return SPIFFS_ERR_CHECK_FLAGS_BAD; - } - - // rewrite in mem - if (objix_spix == 0) { - ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; - } else { - ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; - } - - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - SPIFFS_CHECK_RES(res); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, - 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), - sizeof(spiffs_obj_id), - (u8_t *)&obj_id); - SPIFFS_CHECK_RES(res); - res = spiffs_page_delete(fs, objix_pix); - - return res; -} - -// deletes an object just by marking object index header as deleted -static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) { - spiffs_page_ix objix_hdr_pix; - s32_t res; - res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix); - if (res == SPIFFS_ERR_NOT_FOUND) { - return SPIFFS_OK; - } - SPIFFS_CHECK_RES(res); - u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, - 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&flags); - return res; -} - -// validates the given look up entry -static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr, - spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) { - (void)cur_block; - (void)cur_entry; - u8_t delete_page = 0; - s32_t res = SPIFFS_OK; - spiffs_page_ix objix_pix; - spiffs_page_ix ref_pix; - // check validity, take actions - if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) || - ((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) { - // look up entry deleted / free but used in page header - SPIFFS_CHECK_DBG("LU: pix " _SPIPRIpg " deleted/free in lu but not on page\n", cur_pix); - *reload_lu = 1; - delete_page = 1; - if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { - // header says data page - // data page can be removed if not referenced by some object index - res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); - if (res == SPIFFS_ERR_NOT_FOUND) { - // no object with this id, so remove page safely - res = SPIFFS_OK; - } else { + //--------------------------------------- + // Look up consistency + + // searches in the object indices and returns the referenced page index given + // the object id and the data span index + // destroys fs->lu_work + static s32_t spiffs_object_get_data_page_index_reference( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix data_spix, + spiffs_page_ix *pix, + spiffs_page_ix *objix_pix) + { + s32_t res; + + // calculate object index span index for given data page span index + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // find obj index for obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix); SPIFFS_CHECK_RES(res); - if (ref_pix == cur_pix) { - // data page referenced by object index but deleted in lu - // copy page to new place and re-write the object index to new place - spiffs_page_ix new_pix; - res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); - SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting " _SPIPRIpg " to new page " _SPIPRIpg "\n", cur_pix, new_pix); - SPIFFS_CHECK_RES(res); - *reload_lu = 1; - SPIFFS_CHECK_DBG("LU: FIXUP: " _SPIPRIpg " rewritten to " _SPIPRIpg ", affected objix_pix " _SPIPRIpg "\n", cur_pix, new_pix, objix_pix); - res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); - if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { - // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("LU: FIXUP: index bad " _SPIPRIi ", cannot mend!\n", res); - res = spiffs_page_delete(fs, new_pix); - SPIFFS_CHECK_RES(res); - res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); - } else { - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); - } - SPIFFS_CHECK_RES(res); + + // load obj index entry + u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix); + if (objix_spix == 0) + { + // get referenced page from object index header + addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix); } - } - } else { - // header says index page - // index page can be removed if other index with same obj_id and spanix is found - res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0); - if (res == SPIFFS_ERR_NOT_FOUND) { - // no such index page found, check for a data page amongst page headers - // lu cannot be trusted - res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0); - if (res == SPIFFS_OK) { // ignore other errors - // got a data page also, assume lu corruption only, rewrite to new page - spiffs_page_ix new_pix; - res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); - SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting " _SPIPRIpg " to new page " _SPIPRIpg "\n", cur_pix, new_pix); - SPIFFS_CHECK_RES(res); - *reload_lu = 1; - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + else + { + // get referenced page from object index + addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix); } - } else { - SPIFFS_CHECK_RES(res); - } + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix); + + return res; } - } - if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) { - // look up entry used - if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) { - SPIFFS_CHECK_DBG("LU: pix " _SPIPRIpg " differ in obj_id lu:" _SPIPRIid" ph:" _SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id); - delete_page = 1; - if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 || - (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) || - (p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) { - // page deleted or not finalized, just remove it - } else { - if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { - // if data page, check for reference to this page - res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); - if (res == SPIFFS_ERR_NOT_FOUND) { - // no object with this id, so remove page safely - res = SPIFFS_OK; - } else { - SPIFFS_CHECK_RES(res); - // if found, rewrite page with object id, update index, and delete current - if (ref_pix == cur_pix) { - spiffs_page_ix new_pix; - res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); - SPIFFS_CHECK_RES(res); - res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); - if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { - // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("LU: FIXUP: index bad " _SPIPRIi", cannot mend!\n", res); - res = spiffs_page_delete(fs, new_pix); - SPIFFS_CHECK_RES(res); - res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); - *reload_lu = 1; - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); - } - SPIFFS_CHECK_RES(res); - } - } - } else { - // else if index, check for other pages with both obj_id's and spanix - spiffs_page_ix objix_pix_lu, objix_pix_ph; - // see if other object index page exists for lookup obj id and span index - res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu); - if (res == SPIFFS_ERR_NOT_FOUND) { - res = SPIFFS_OK; - objix_pix_lu = 0; - } - SPIFFS_CHECK_RES(res); - // see if other object index exists for page header obj id and span index - res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph); - if (res == SPIFFS_ERR_NOT_FOUND) { - res = SPIFFS_OK; - objix_pix_ph = 0; - } - SPIFFS_CHECK_RES(res); - // if both obj_id's found, just delete current - if (objix_pix_ph == 0 || objix_pix_lu == 0) { - // otherwise try finding first corresponding data pages - spiffs_page_ix data_pix_lu, data_pix_ph; - // see if other data page exists for look up obj id and span index - res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu); - if (res == SPIFFS_ERR_NOT_FOUND) { - res = SPIFFS_OK; - objix_pix_lu = 0; - } - SPIFFS_CHECK_RES(res); - // see if other data page exists for page header obj id and span index - res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph); - if (res == SPIFFS_ERR_NOT_FOUND) { - res = SPIFFS_OK; - objix_pix_ph = 0; - } - SPIFFS_CHECK_RES(res); - spiffs_page_header new_ph; - new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL); - new_ph.span_ix = p_hdr->span_ix; - spiffs_page_ix new_pix; - if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) || - (objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) { - // got a data page for page header obj id - // rewrite as obj_id_ph - new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG; - res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); - SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page " _SPIPRIpg " as " _SPIPRIid" to pix " _SPIPRIpg "\n", cur_pix, new_ph.obj_id, new_pix); - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); - SPIFFS_CHECK_RES(res); - *reload_lu = 1; - } else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) || - (objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) { - // got a data page for look up obj id - // rewrite as obj_id_lu - new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; - SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page " _SPIPRIpg " as " _SPIPRIid"\n", cur_pix, new_ph.obj_id); - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); - res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); - SPIFFS_CHECK_RES(res); - *reload_lu = 1; - } else { - // cannot safely do anything - SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n"); - } - } - } - } - } else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) || - ((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) { - SPIFFS_CHECK_DBG("LU: " _SPIPRIpg " lu/page index marking differ\n", cur_pix); - spiffs_page_ix data_pix, objix_pix_d; - // see if other data page exists for given obj id and span index - res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix); - if (res == SPIFFS_ERR_NOT_FOUND) { - res = SPIFFS_OK; - data_pix = 0; - } - SPIFFS_CHECK_RES(res); - // see if other object index exists for given obj id and span index - res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d); - if (res == SPIFFS_ERR_NOT_FOUND) { - res = SPIFFS_OK; - objix_pix_d = 0; - } - SPIFFS_CHECK_RES(res); - - delete_page = 1; - // if other data page exists and object index exists, just delete page - if (data_pix && objix_pix_d) { - SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n"); - } else - // if only data page exists, make this page index - if (data_pix && objix_pix_d == 0) { - SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n"); - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); - spiffs_page_header new_ph; - spiffs_page_ix new_pix; - new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); - new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; - new_ph.span_ix = p_hdr->span_ix; - res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); - SPIFFS_CHECK_RES(res); - res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), - SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), - SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); - SPIFFS_CHECK_RES(res); - } else - // if only index exists, make data page - if (data_pix == 0 && objix_pix_d) { - SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n"); - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); - spiffs_page_header new_ph; - spiffs_page_ix new_pix; - new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); - new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - new_ph.span_ix = p_hdr->span_ix; - res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + // copies page contents to a new page + static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) + { + s32_t res; + res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0, 0, 0, 0, new_pix); SPIFFS_CHECK_RES(res); - res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), - SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), - SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs)); SPIFFS_CHECK_RES(res); - } else { - // if nothing exists, we cannot safely make a decision - delete - } + return res; } - else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) { - SPIFFS_CHECK_DBG("LU: pix " _SPIPRIpg " busy in lu but deleted on page\n", cur_pix); - delete_page = 1; - } else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) { - SPIFFS_CHECK_DBG("LU: pix " _SPIPRIpg " busy but not final\n", cur_pix); - // page can be removed if not referenced by object index - *reload_lu = 1; - res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); - if (res == SPIFFS_ERR_NOT_FOUND) { - // no object with this id, so remove page safely - res = SPIFFS_OK; - delete_page = 1; - } else { + + // rewrites the object index for given object id and replaces the + // data page index to a new page index + static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) + { + s32_t res; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); SPIFFS_CHECK_RES(res); - if (ref_pix != cur_pix) { - SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n"); - delete_page = 1; - } else { - // page referenced by object index but not final - // just finalize - SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n"); - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); - u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), (u8_t*)&flags); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + // calculate object index span index for given data page span index + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (objix_spix == 0) + { + // calc index in index header + entry = data_spix; } - } - } - } - - if (delete_page) { - SPIFFS_CHECK_DBG("LU: FIXUP: deleting page " _SPIPRIpg "\n", cur_pix); - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); - res = spiffs_page_delete(fs, cur_pix); - SPIFFS_CHECK_RES(res); - } - - return res; -} - -static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry, - const void *user_const_p, void *user_var_p) { - (void)user_const_p; - (void)user_var_p; - s32_t res = SPIFFS_OK; - spiffs_page_header p_hdr; - spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); - - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, - (cur_block * 256)/fs->block_count, 0); - - // load header - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); - SPIFFS_CHECK_RES(res); - - int reload_lu = 0; - - res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu); - SPIFFS_CHECK_RES(res); - - if (res == SPIFFS_OK) { - return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE; - } - return res; -} - - -// Scans all object look up. For each entry, corresponding page header is checked for validity. -// If an object index header page is found, this is also checked -s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { - (void)check_all_objects; - s32_t res = SPIFFS_OK; - - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); - - res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0); - - if (res == SPIFFS_VIS_END) { - res = SPIFFS_OK; - } - - if (res != SPIFFS_OK) { - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); - } - - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); - - return res; -} - -//--------------------------------------- -// Page consistency - -// Scans all pages (except lu pages), reserves 4 bits in working memory for each page -// bit 0: 0 == FREE|DELETED, 1 == USED -// bit 1: 0 == UNREFERENCED, 1 == REFERENCED -// bit 2: 0 == NOT_INDEX, 1 == INDEX -// bit 3: unused -// A consistent file system will have only pages being -// * x000 free, unreferenced, not index -// * x011 used, referenced only once, not index -// * x101 used, unreferenced, index -// The working memory might not fit all pages so several scans might be needed -static s32_t spiffs_page_consistency_check_i(spiffs *fs) { - const u32_t bits = 4; - const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits; - - s32_t res = SPIFFS_OK; - spiffs_page_ix pix_offset = 0; - - // for each range of pages fitting into work memory - while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) { - // set this flag to abort all checks and rescan the page range - u8_t restart = 0; - memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - - spiffs_block_ix cur_block = 0; - // build consistency bitmap for id range traversing all blocks - while (!restart && cur_block < fs->block_count) { - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, - (pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + - ((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), - 0); - // traverse each page except for lookup pages - spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; - while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) { - //if ((cur_pix & 0xff) == 0) - // SPIFFS_CHECK_DBG("PA: processing pix " _SPIPRIpg ", block " _SPIPRIbl" of pix " _SPIPRIpg ", block " _SPIPRIbl"\n", - // cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count); - - // read header - spiffs_page_header p_hdr; + else + { + // calc entry in index + entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix); + + } + // load index res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); SPIFFS_CHECK_RES(res); + spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; - u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan); - const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits); - const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits; - - if (within_range && - (p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) { - // used - fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0)); + // be ultra safe, double check header against provided data + if (objix_p_hdr->obj_id != obj_id) + { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_OBJ_ID_MISM; + } + if (objix_p_hdr->span_ix != objix_spix) + { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_SPIX_MISM; + } + if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX | + SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) != + (SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) + { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_FLAGS_BAD; } - if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) && - (p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) && - (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) { - // found non-deleted index - if (within_range) { - fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2)); - } - - // load non-deleted index - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - SPIFFS_CHECK_RES(res); - - // traverse index for referenced pages - spiffs_page_ix *object_page_index; - spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; - - int entries; - int i; - spiffs_span_ix data_spix_offset; - if (p_hdr.span_ix == 0) { - // object header page index - entries = SPIFFS_OBJ_HDR_IX_LEN(fs); - data_spix_offset = 0; - object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)); - } else { - // object page index - entries = SPIFFS_OBJ_IX_LEN(fs); - data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1); - object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)); - } - - // for all entries in index - for (i = 0; !restart && i < entries; i++) { - spiffs_page_ix rpix = object_page_index[i]; - u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan; - - if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs)) - || (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) { - - // bad reference - SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg"x bad pix / LU referenced from page " _SPIPRIpg "\n", - rpix, cur_pix); - // check for data page elsewhere - spiffs_page_ix data_pix; - res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, - data_spix_offset + i, 0, &data_pix); - if (res == SPIFFS_ERR_NOT_FOUND) { - res = SPIFFS_OK; - data_pix = 0; - } - SPIFFS_CHECK_RES(res); - if (data_pix == 0) { - // if not, allocate free page - spiffs_page_header new_ph; - new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); - new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - new_ph.span_ix = data_spix_offset + i; - res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix); - SPIFFS_CHECK_RES(res); - SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ " _SPIPRIpg "\n", data_pix); - } - // remap index - SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix " _SPIPRIpg "\n", cur_pix); - res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, - data_spix_offset + i, data_pix, cur_pix); - if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { - // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("PA: FIXUP: index bad " _SPIPRIi", cannot mend - delete object\n", res); - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); - // delete file - res = spiffs_page_delete(fs, cur_pix); - } else { - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); - } - SPIFFS_CHECK_RES(res); - restart = 1; - - } else if (rpix_within_range) { - - // valid reference - // read referenced page header - spiffs_page_header rp_hdr; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); - SPIFFS_CHECK_RES(res); - - // cross reference page header check - if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) || - rp_hdr.span_ix != data_spix_offset + i || - (rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) != - (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) { - SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " has inconsistent page header ix id/span:" _SPIPRIid"/" _SPIPRIsp", ref id/span:" _SPIPRIid"/" _SPIPRIsp" flags:" _SPIPRIfl"\n", - rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i, - rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags); - // try finding correct page - spiffs_page_ix data_pix; - res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, - data_spix_offset + i, rpix, &data_pix); - if (res == SPIFFS_ERR_NOT_FOUND) { - res = SPIFFS_OK; - data_pix = 0; - } - SPIFFS_CHECK_RES(res); - if (data_pix == 0) { - // not found, this index is badly borked - SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id " _SPIPRIid"\n", p_hdr.obj_id); - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); - res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); - SPIFFS_CHECK_RES(res); - break; - } else { - // found it, so rewrite index - SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix " _SPIPRIpg ", rewrite ix pix " _SPIPRIpg " id " _SPIPRIid"\n", - data_pix, cur_pix, p_hdr.obj_id); - res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix); - if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { - // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("PA: FIXUP: index bad " _SPIPRIi", cannot mend!\n", res); - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); - res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); - } else { - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); - } - SPIFFS_CHECK_RES(res); - restart = 1; - } - } - else { - // mark rpix as referenced - const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits); - const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits; - if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) { - SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " multiple referenced from page " _SPIPRIpg "\n", - rpix, cur_pix); - // Here, we should have fixed all broken references - getting this means there - // must be multiple files with same object id. Only solution is to delete - // the object which is referring to this page - SPIFFS_CHECK_DBG("PA: FIXUP: removing object " _SPIPRIid" and page " _SPIPRIpg "\n", - p_hdr.obj_id, cur_pix); - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); - res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); - SPIFFS_CHECK_RES(res); - // extra precaution, delete this page also - res = spiffs_page_delete(fs, cur_pix); - SPIFFS_CHECK_RES(res); - restart = 1; - } - fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1)); - } - } - } // for all index entries - } // found index - - // next page - cur_pix++; - } - // next block - cur_block++; - } - // check consistency bitmap - if (!restart) { - spiffs_page_ix objix_pix; - spiffs_page_ix rpix; - - u32_t byte_ix; - u8_t bit_ix; - for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) { - for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) { - u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7; - spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix; - // 000 ok - free, unreferenced, not index + // rewrite in mem + if (objix_spix == 0) + { + ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + } + else + { + ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + } - if (bitmask == 0x1) { + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + res = spiffs_page_delete(fs, objix_pix); - // 001 - SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " USED, UNREFERENCED, not index\n", cur_pix); + return res; + } - u8_t rewrite_ix_to_this = 0; - u8_t delete_page = 0; - // check corresponding object index entry - spiffs_page_header p_hdr; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); - SPIFFS_CHECK_RES(res); + // deletes an object just by marking object index header as deleted + static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) + { + spiffs_page_ix objix_hdr_pix; + s32_t res; + res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix); + if (res == SPIFFS_ERR_NOT_FOUND) + { + return SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); + return res; + } - res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix, - &rpix, &objix_pix); - if (res == SPIFFS_OK) { - if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) { - // pointing to a bad page altogether, rewrite index to this - rewrite_ix_to_this = 1; - SPIFFS_CHECK_DBG("PA: corresponding ref is bad: " _SPIPRIpg ", rewrite to this " _SPIPRIpg "\n", rpix, cur_pix); - } else { - // pointing to something else, check what - spiffs_page_header rp_hdr; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); - SPIFFS_CHECK_RES(res); - if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) && - ((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) == - (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) { - // pointing to something else valid, just delete this page then - SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: " _SPIPRIpg ", delete this " _SPIPRIpg "\n", rpix, cur_pix); - delete_page = 1; - } else { - // pointing to something weird, update index to point to this page instead - if (rpix != cur_pix) { - SPIFFS_CHECK_DBG("PA: corresponding ref is weird: " _SPIPRIpg " %s%s%s%s, rewrite this " _SPIPRIpg "\n", rpix, - (rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ", - (rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ", - (rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "", - (rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "", - cur_pix); - rewrite_ix_to_this = 1; - } else { - // should not happen, destined for fubar - } + // validates the given look up entry + static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr, + spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) + { + (void)cur_block; + (void)cur_entry; + u8_t delete_page = 0; + s32_t res = SPIFFS_OK; + spiffs_page_ix objix_pix; + spiffs_page_ix ref_pix; + // check validity, take actions + if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) || + ((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) + { + // look up entry deleted / free but used in page header + SPIFFS_CHECK_DBG("LU: pix " _SPIPRIpg " deleted/free in lu but not on page\n", cur_pix); + *reload_lu = 1; + delete_page = 1; + if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) + { + // header says data page + // data page can be removed if not referenced by some object index + res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) + { + // no object with this id, so remove page safely + res = SPIFFS_OK; + } + else + { + SPIFFS_CHECK_RES(res); + if (ref_pix == cur_pix) + { + // data page referenced by object index but deleted in lu + // copy page to new place and re-write the object index to new place + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting " _SPIPRIpg " to new page " _SPIPRIpg "\n", cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + SPIFFS_CHECK_DBG("LU: FIXUP: " _SPIPRIpg " rewritten to " _SPIPRIpg ", affected objix_pix " _SPIPRIpg "\n", cur_pix, new_pix, objix_pix); + res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) + { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("LU: FIXUP: index bad " _SPIPRIi ", cannot mend!\n", res); + res = spiffs_page_delete(fs, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + } + else + { + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); + } + SPIFFS_CHECK_RES(res); + } } - } - } else if (res == SPIFFS_ERR_NOT_FOUND) { - SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete " _SPIPRIpg "\n", cur_pix); - delete_page = 1; - res = SPIFFS_OK; } - - if (rewrite_ix_to_this) { - // if pointing to invalid page, redirect index to this page - SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id " _SPIPRIid" data spix " _SPIPRIsp" to point to this pix: " _SPIPRIpg "\n", - p_hdr.obj_id, p_hdr.span_ix, cur_pix); - res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); - if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { - // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("PA: FIXUP: index bad " _SPIPRIi", cannot mend!\n", res); - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); - res = spiffs_page_delete(fs, cur_pix); + else + { + // header says index page + // index page can be removed if other index with same obj_id and spanix is found + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0); + if (res == SPIFFS_ERR_NOT_FOUND) + { + // no such index page found, check for a data page amongst page headers + // lu cannot be trusted + res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0); + if (res == SPIFFS_OK) // ignore other errors + { + // got a data page also, assume lu corruption only, rewrite to new page + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting " _SPIPRIpg " to new page " _SPIPRIpg "\n", cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + } + } + else + { + SPIFFS_CHECK_RES(res); + } + } + } + if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) + { + // look up entry used + if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) + { + SPIFFS_CHECK_DBG("LU: pix " _SPIPRIpg " differ in obj_id lu:" _SPIPRIid" ph:" _SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id); + delete_page = 1; + if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 || + (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) || + (p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) + { + // page deleted or not finalized, just remove it + } + else + { + if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) + { + // if data page, check for reference to this page + res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) + { + // no object with this id, so remove page safely + res = SPIFFS_OK; + } + else + { + SPIFFS_CHECK_RES(res); + // if found, rewrite page with object id, update index, and delete current + if (ref_pix == cur_pix) + { + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) + { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("LU: FIXUP: index bad " _SPIPRIi", cannot mend!\n", res); + res = spiffs_page_delete(fs, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); + *reload_lu = 1; + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + } + SPIFFS_CHECK_RES(res); + } + } + } + else + { + // else if index, check for other pages with both obj_id's and spanix + spiffs_page_ix objix_pix_lu, objix_pix_ph; + // see if other object index page exists for lookup obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu); + if (res == SPIFFS_ERR_NOT_FOUND) + { + res = SPIFFS_OK; + objix_pix_lu = 0; + } + SPIFFS_CHECK_RES(res); + // see if other object index exists for page header obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph); + if (res == SPIFFS_ERR_NOT_FOUND) + { + res = SPIFFS_OK; + objix_pix_ph = 0; + } + SPIFFS_CHECK_RES(res); + // if both obj_id's found, just delete current + if (objix_pix_ph == 0 || objix_pix_lu == 0) + { + // otherwise try finding first corresponding data pages + spiffs_page_ix data_pix_lu, data_pix_ph; + // see if other data page exists for look up obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu); + if (res == SPIFFS_ERR_NOT_FOUND) + { + res = SPIFFS_OK; + objix_pix_lu = 0; + } + SPIFFS_CHECK_RES(res); + // see if other data page exists for page header obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph); + if (res == SPIFFS_ERR_NOT_FOUND) + { + res = SPIFFS_OK; + objix_pix_ph = 0; + } + SPIFFS_CHECK_RES(res); + + spiffs_page_header new_ph; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL); + new_ph.span_ix = p_hdr->span_ix; + spiffs_page_ix new_pix; + if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) || + (objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) + { + // got a data page for page header obj id + // rewrite as obj_id_ph + new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page " _SPIPRIpg " as " _SPIPRIid" to pix " _SPIPRIpg "\n", cur_pix, new_ph.obj_id, new_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + } + else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) || + (objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) + { + // got a data page for look up obj id + // rewrite as obj_id_lu + new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page " _SPIPRIpg " as " _SPIPRIid"\n", cur_pix, new_ph.obj_id); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + } + else + { + // cannot safely do anything + SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n"); + } + } + } + } + } + else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) || + ((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) + { + SPIFFS_CHECK_DBG("LU: " _SPIPRIpg " lu/page index marking differ\n", cur_pix); + spiffs_page_ix data_pix, objix_pix_d; + // see if other data page exists for given obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) + { + res = SPIFFS_OK; + data_pix = 0; + } SPIFFS_CHECK_RES(res); - res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); - } else { - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); - } - SPIFFS_CHECK_RES(res); - restart = 1; - continue; - } else if (delete_page) { - SPIFFS_CHECK_DBG("PA: FIXUP: deleting page " _SPIPRIpg "\n", cur_pix); - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); - res = spiffs_page_delete(fs, cur_pix); + // see if other object index exists for given obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d); + if (res == SPIFFS_ERR_NOT_FOUND) + { + res = SPIFFS_OK; + objix_pix_d = 0; + } + SPIFFS_CHECK_RES(res); + + delete_page = 1; + // if other data page exists and object index exists, just delete page + if (data_pix && objix_pix_d) + { + SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n"); + } + else + // if only data page exists, make this page index + if (data_pix && objix_pix_d == 0) + { + SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n"); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); + spiffs_page_header new_ph; + spiffs_page_ix new_pix; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = p_hdr->span_ix; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + SPIFFS_CHECK_RES(res); + } + else + // if only index exists, make data page + if (data_pix == 0 && objix_pix_d) + { + SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n"); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); + spiffs_page_header new_ph; + spiffs_page_ix new_pix; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); + new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = p_hdr->span_ix; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + SPIFFS_CHECK_RES(res); + } + else + { + // if nothing exists, we cannot safely make a decision - delete + } + } + else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) + { + SPIFFS_CHECK_DBG("LU: pix " _SPIPRIpg " busy in lu but deleted on page\n", cur_pix); + delete_page = 1; + } + else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) + { + SPIFFS_CHECK_DBG("LU: pix " _SPIPRIpg " busy but not final\n", cur_pix); + // page can be removed if not referenced by object index + *reload_lu = 1; + res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) + { + // no object with this id, so remove page safely + res = SPIFFS_OK; + delete_page = 1; + } + else + { + SPIFFS_CHECK_RES(res); + if (ref_pix != cur_pix) + { + SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n"); + delete_page = 1; + } + else + { + // page referenced by object index but not final + // just finalize + SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n"); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), (u8_t*)&flags); + } + } } + } + + if (delete_page) + { + SPIFFS_CHECK_DBG("LU: FIXUP: deleting page " _SPIPRIpg "\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + res = spiffs_page_delete(fs, cur_pix); SPIFFS_CHECK_RES(res); - } - if (bitmask == 0x2) { + } - // 010 - SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " FREE, REFERENCED, not index\n", cur_pix); + return res; + } - // no op, this should be taken care of when checking valid references - } + static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry, + const void *user_const_p, void *user_var_p) + { + (void)user_const_p; + (void)user_var_p; + s32_t res = SPIFFS_OK; + spiffs_page_header p_hdr; + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); - // 011 ok - busy, referenced, not index + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, + (cur_block * 256) / fs->block_count, 0); - if (bitmask == 0x4) { + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); - // 100 - SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " FREE, unreferenced, INDEX\n", cur_pix); + int reload_lu = 0; - // this should never happen, major fubar - } + res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu); + SPIFFS_CHECK_RES(res); + + if (res == SPIFFS_OK) + { + return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE; + } + return res; + } - // 101 ok - busy, unreferenced, index - if (bitmask == 0x6) { + // Scans all object look up. For each entry, corresponding page header is checked for validity. + // If an object index header page is found, this is also checked + s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) + { + (void)check_all_objects; + s32_t res = SPIFFS_OK; - // 110 - SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " FREE, REFERENCED, INDEX\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); - // no op, this should be taken care of when checking valid references - } - if (bitmask == 0x7) { + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0); - // 111 - SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " USED, REFERENCED, INDEX\n", cur_pix); + if (res == SPIFFS_VIS_END) + { + res = SPIFFS_OK; + } - // no op, this should be taken care of when checking valid references - } + if (res != SPIFFS_OK) + { + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); } - } + + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); + + return res; } - SPIFFS_CHECK_DBG("PA: processed " _SPIPRIpg ", restart " _SPIPRIi"\n", pix_offset, restart); - // next page range - if (!restart) { - pix_offset += pages_per_scan; + //--------------------------------------- + // Page consistency + + // Scans all pages (except lu pages), reserves 4 bits in working memory for each page + // bit 0: 0 == FREE|DELETED, 1 == USED + // bit 1: 0 == UNREFERENCED, 1 == REFERENCED + // bit 2: 0 == NOT_INDEX, 1 == INDEX + // bit 3: unused + // A consistent file system will have only pages being + // * x000 free, unreferenced, not index + // * x011 used, referenced only once, not index + // * x101 used, unreferenced, index + // The working memory might not fit all pages so several scans might be needed + static s32_t spiffs_page_consistency_check_i(spiffs *fs) + { + const u32_t bits = 4; + const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits; + + s32_t res = SPIFFS_OK; + spiffs_page_ix pix_offset = 0; + + // for each range of pages fitting into work memory + while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + { + // set this flag to abort all checks and rescan the page range + u8_t restart = 0; + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + spiffs_block_ix cur_block = 0; + // build consistency bitmap for id range traversing all blocks + while (!restart && cur_block < fs->block_count) + { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, + (pix_offset * 256) / (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + + ((((cur_block * pages_per_scan * 256) / (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), + 0); + // traverse each page except for lookup pages + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; + while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block + 1)) + { + //if ((cur_pix & 0xff) == 0) + // SPIFFS_CHECK_DBG("PA: processing pix " _SPIPRIpg ", block " _SPIPRIbl" of pix " _SPIPRIpg ", block " _SPIPRIbl"\n", + // cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count); + + // read header + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan); + const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8 / bits); + const u8_t pix_bit_ix = (cur_pix & ((8 / bits) - 1)) * bits; + + if (within_range && + (p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) + { + // used + fs->work[pix_byte_ix] |= (1 << (pix_bit_ix + 0)); + } + if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) && + (p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) && + (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) + { + // found non-deleted index + if (within_range) + { + fs->work[pix_byte_ix] |= (1 << (pix_bit_ix + 2)); + } + + // load non-deleted index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + + // traverse index for referenced pages + spiffs_page_ix *object_page_index; + spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; + + int entries; + int i; + spiffs_span_ix data_spix_offset; + if (p_hdr.span_ix == 0) + { + // object header page index + entries = SPIFFS_OBJ_HDR_IX_LEN(fs); + data_spix_offset = 0; + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)); + } + else + { + // object page index + entries = SPIFFS_OBJ_IX_LEN(fs); + data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1); + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)); + } + + // for all entries in index + for (i = 0; !restart && i < entries; i++) + { + spiffs_page_ix rpix = object_page_index[i]; + u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan; + + if ((rpix != (spiffs_page_ix) - 1 && rpix > SPIFFS_MAX_PAGES(fs)) + || (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) + { + + // bad reference + SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg"x bad pix / LU referenced from page " _SPIPRIpg "\n", + rpix, cur_pix); + // check for data page elsewhere + spiffs_page_ix data_pix; + res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, 0, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) + { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + if (data_pix == 0) + { + // if not, allocate free page + spiffs_page_header new_ph; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); + new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = data_spix_offset + i; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix); + SPIFFS_CHECK_RES(res); + SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ " _SPIPRIpg "\n", data_pix); + } + // remap index + SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix " _SPIPRIpg "\n", cur_pix); + res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, data_pix, cur_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) + { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad " _SPIPRIi", cannot mend - delete object\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); + // delete file + res = spiffs_page_delete(fs, cur_pix); + } + else + { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + + } + else if (rpix_within_range) + { + + // valid reference + // read referenced page header + spiffs_page_header rp_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); + SPIFFS_CHECK_RES(res); + + // cross reference page header check + if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) || + rp_hdr.span_ix != data_spix_offset + i || + (rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) != + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) + { + SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " has inconsistent page header ix id/span:" _SPIPRIid"/" _SPIPRIsp", ref id/span:" _SPIPRIid"/" _SPIPRIsp" flags:" _SPIPRIfl"\n", + rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i, + rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags); + // try finding correct page + spiffs_page_ix data_pix; + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, rpix, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) + { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + if (data_pix == 0) + { + // not found, this index is badly borked + SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id " _SPIPRIid"\n", p_hdr.obj_id); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + SPIFFS_CHECK_RES(res); + break; + } + else + { + // found it, so rewrite index + SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix " _SPIPRIpg ", rewrite ix pix " _SPIPRIpg " id " _SPIPRIid"\n", + data_pix, cur_pix, p_hdr.obj_id); + res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) + { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad " _SPIPRIi", cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + } + else + { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + } + } + else + { + // mark rpix as referenced + const u32_t rpix_byte_ix = (rpix - pix_offset) / (8 / bits); + const u8_t rpix_bit_ix = (rpix & ((8 / bits) - 1)) * bits; + if (fs->work[rpix_byte_ix] & (1 << (rpix_bit_ix + 1))) + { + SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " multiple referenced from page " _SPIPRIpg "\n", + rpix, cur_pix); + // Here, we should have fixed all broken references - getting this means there + // must be multiple files with same object id. Only solution is to delete + // the object which is referring to this page + SPIFFS_CHECK_DBG("PA: FIXUP: removing object " _SPIPRIid" and page " _SPIPRIpg "\n", + p_hdr.obj_id, cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + SPIFFS_CHECK_RES(res); + // extra precaution, delete this page also + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + restart = 1; + } + fs->work[rpix_byte_ix] |= (1 << (rpix_bit_ix + 1)); + } + } + } // for all index entries + } // found index + + // next page + cur_pix++; + } + // next block + cur_block++; + } + // check consistency bitmap + if (!restart) + { + spiffs_page_ix objix_pix; + spiffs_page_ix rpix; + + u32_t byte_ix; + u8_t bit_ix; + for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) + { + for (bit_ix = 0; !restart && bit_ix < 8 / bits; bit_ix ++) + { + u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7; + spiffs_page_ix cur_pix = pix_offset + byte_ix * (8 / bits) + bit_ix; + + // 000 ok - free, unreferenced, not index + + if (bitmask == 0x1) + { + + // 001 + SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " USED, UNREFERENCED, not index\n", cur_pix); + + u8_t rewrite_ix_to_this = 0; + u8_t delete_page = 0; + // check corresponding object index entry + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix, + &rpix, &objix_pix); + if (res == SPIFFS_OK) + { + if (((rpix == (spiffs_page_ix) - 1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) + { + // pointing to a bad page altogether, rewrite index to this + rewrite_ix_to_this = 1; + SPIFFS_CHECK_DBG("PA: corresponding ref is bad: " _SPIPRIpg ", rewrite to this " _SPIPRIpg "\n", rpix, cur_pix); + } + else + { + // pointing to something else, check what + spiffs_page_header rp_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); + SPIFFS_CHECK_RES(res); + if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) && + ((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) == + (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) + { + // pointing to something else valid, just delete this page then + SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: " _SPIPRIpg ", delete this " _SPIPRIpg "\n", rpix, cur_pix); + delete_page = 1; + } + else + { + // pointing to something weird, update index to point to this page instead + if (rpix != cur_pix) + { + SPIFFS_CHECK_DBG("PA: corresponding ref is weird: " _SPIPRIpg " %s%s%s%s, rewrite this " _SPIPRIpg "\n", rpix, + (rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ", + (rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ", + (rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "", + (rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "", + cur_pix); + rewrite_ix_to_this = 1; + } + else + { + // should not happen, destined for fubar + } + } + } + } + else if (res == SPIFFS_ERR_NOT_FOUND) + { + SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete " _SPIPRIpg "\n", cur_pix); + delete_page = 1; + res = SPIFFS_OK; + } + + if (rewrite_ix_to_this) + { + // if pointing to invalid page, redirect index to this page + SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id " _SPIPRIid" data spix " _SPIPRIsp" to point to this pix: " _SPIPRIpg "\n", + p_hdr.obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) + { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad " _SPIPRIi", cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + } + else + { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + continue; + } + else if (delete_page) + { + SPIFFS_CHECK_DBG("PA: FIXUP: deleting page " _SPIPRIpg "\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + res = spiffs_page_delete(fs, cur_pix); + } + SPIFFS_CHECK_RES(res); + } + if (bitmask == 0x2) + { + + // 010 + SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " FREE, REFERENCED, not index\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + + // 011 ok - busy, referenced, not index + + if (bitmask == 0x4) + { + + // 100 + SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " FREE, unreferenced, INDEX\n", cur_pix); + + // this should never happen, major fubar + } + + // 101 ok - busy, unreferenced, index + + if (bitmask == 0x6) + { + + // 110 + SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " FREE, REFERENCED, INDEX\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + if (bitmask == 0x7) + { + + // 111 + SPIFFS_CHECK_DBG("PA: pix " _SPIPRIpg " USED, REFERENCED, INDEX\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + } + } + } + + SPIFFS_CHECK_DBG("PA: processed " _SPIPRIpg ", restart " _SPIPRIi"\n", pix_offset, restart); + // next page range + if (!restart) + { + pix_offset += pages_per_scan; + } + } // while page range not reached end + return res; } - } // while page range not reached end - return res; -} - -// Checks consistency amongst all pages and fixes irregularities -s32_t spiffs_page_consistency_check(spiffs *fs) { - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); - s32_t res = spiffs_page_consistency_check_i(fs); - if (res != SPIFFS_OK) { - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); - } - CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); - return res; -} - -//--------------------------------------- -// Object index consistency - -// searches for given object id in temporary object id index, -// returns the index or -1 -static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) { - u32_t i; - spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; - obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; - for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) { - if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) { - return i; + + // Checks consistency amongst all pages and fixes irregularities + s32_t spiffs_page_consistency_check(spiffs *fs) + { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); + s32_t res = spiffs_page_consistency_check_i(fs); + if (res != SPIFFS_OK) + { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); + } + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); + return res; } - } - return -1; -} - -static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, - int cur_entry, const void *user_const_p, void *user_var_p) { - (void)user_const_p; - s32_t res_c = SPIFFS_VIS_COUNTINUE; - s32_t res = SPIFFS_OK; - u32_t *log_ix = (u32_t*)user_var_p; - spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; - - CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, - (cur_block * 256)/fs->block_count, 0); - - if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { - spiffs_page_header p_hdr; - spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); - - // load header - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); - SPIFFS_CHECK_RES(res); - - if (p_hdr.span_ix == 0 && - (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == - (SPIFFS_PH_FLAG_DELET)) { - SPIFFS_CHECK_DBG("IX: pix " _SPIPRIpg ", obj id:" _SPIPRIid" spix:" _SPIPRIsp" header not fully deleted - deleting\n", - cur_pix, obj_id, p_hdr.span_ix); - CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); - res = spiffs_page_delete(fs, cur_pix); - SPIFFS_CHECK_RES(res); - return res_c; + + //--------------------------------------- + // Object index consistency + + // searches for given object id in temporary object id index, + // returns the index or -1 + static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) + { + u32_t i; + spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; + obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) + { + if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) + { + return i; + } + } + return -1; } - if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == - (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { - return res_c; + static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, + int cur_entry, const void *user_const_p, void *user_var_p) + { + (void)user_const_p; + s32_t res_c = SPIFFS_VIS_COUNTINUE; + s32_t res = SPIFFS_OK; + u32_t *log_ix = (u32_t*)user_var_p; + spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; + + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, + (cur_block * 256) / fs->block_count, 0); + + if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) + { + spiffs_page_header p_hdr; + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); + + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + if (p_hdr.span_ix == 0 && + (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET)) + { + SPIFFS_CHECK_DBG("IX: pix " _SPIPRIpg ", obj id:" _SPIPRIid" spix:" _SPIPRIsp" header not fully deleted - deleting\n", + cur_pix, obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + return res_c; + } + + if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) + { + return res_c; + } + + if (p_hdr.span_ix == 0) + { + // objix header page, register objid as reachable + int r = spiffs_object_index_search(fs, obj_id); + if (r == -1) + { + // not registered, do it + obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + (*log_ix)++; + if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) + { + *log_ix = 0; + } + } + } + else // span index + { + // objix page, see if header can be found + int r = spiffs_object_index_search(fs, obj_id); + u8_t _delete = 0; + if (r == -1) + { + // not in temporary index, try finding it + spiffs_page_ix objix_hdr_pix; + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix); + res_c = SPIFFS_VIS_COUNTINUE_RELOAD; + if (res == SPIFFS_OK) + { + // found, register as reachable + obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + } + else if (res == SPIFFS_ERR_NOT_FOUND) + { + // not found, register as unreachable + _delete = 1; + obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG; + } + else + { + SPIFFS_CHECK_RES(res); + } + (*log_ix)++; + if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) + { + *log_ix = 0; + } + } + else + { + // in temporary index, check reachable flag + if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) + { + // registered as unreachable + _delete = 1; + } + } + + if (_delete) + { + SPIFFS_CHECK_DBG("IX: FIXUP: pix " _SPIPRIpg ", obj id:" _SPIPRIid" spix:" _SPIPRIsp" is orphan index - deleting\n", + cur_pix, obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + } + } // span index + } // valid object index id + + return res_c; } - if (p_hdr.span_ix == 0) { - // objix header page, register objid as reachable - int r = spiffs_object_index_search(fs, obj_id); - if (r == -1) { - // not registered, do it - obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - (*log_ix)++; - if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { - *log_ix = 0; - } - } - } else { // span index - // objix page, see if header can be found - int r = spiffs_object_index_search(fs, obj_id); - u8_t _delete = 0; - if (r == -1) { - // not in temporary index, try finding it - spiffs_page_ix objix_hdr_pix; - res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix); - res_c = SPIFFS_VIS_COUNTINUE_RELOAD; - if (res == SPIFFS_OK) { - // found, register as reachable - obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - } else if (res == SPIFFS_ERR_NOT_FOUND) { - // not found, register as unreachable - _delete = 1; - obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG; - } else { - SPIFFS_CHECK_RES(res); - } - (*log_ix)++; - if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { - *log_ix = 0; + // Removes orphaned and partially deleted index pages. + // Scans for index pages. When an index page is found, corresponding index header is searched for. + // If no such page exists, the index page cannot be reached as no index header exists and must be + // deleted. + s32_t spiffs_object_index_consistency_check(spiffs *fs) + { + s32_t res = SPIFFS_OK; + // impl note: + // fs->work is used for a temporary object index memory, listing found object ids and + // indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit. + // In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate + // a reachable/unreachable object id. + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + u32_t obj_id_log_ix = 0; + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix, + 0, 0); + if (res == SPIFFS_VIS_END) + { + res = SPIFFS_OK; } - } else { - // in temporary index, check reachable flag - if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) { - // registered as unreachable - _delete = 1; + if (res != SPIFFS_OK) + { + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); } - } - - if (_delete) { - SPIFFS_CHECK_DBG("IX: FIXUP: pix " _SPIPRIpg ", obj id:" _SPIPRIid" spix:" _SPIPRIsp" is orphan index - deleting\n", - cur_pix, obj_id, p_hdr.span_ix); - CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); - res = spiffs_page_delete(fs, cur_pix); - SPIFFS_CHECK_RES(res); - } - } // span index - } // valid object index id - - return res_c; -} - -// Removes orphaned and partially deleted index pages. -// Scans for index pages. When an index page is found, corresponding index header is searched for. -// If no such page exists, the index page cannot be reached as no index header exists and must be -// deleted. -s32_t spiffs_object_index_consistency_check(spiffs *fs) { - s32_t res = SPIFFS_OK; - // impl note: - // fs->work is used for a temporary object index memory, listing found object ids and - // indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit. - // In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate - // a reachable/unreachable object id. - memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - u32_t obj_id_log_ix = 0; - CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); - res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix, - 0, 0); - if (res == SPIFFS_VIS_END) { - res = SPIFFS_OK; - } - if (res != SPIFFS_OK) { - CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); - } - CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); - return res; -} + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); + return res; + } }; diff --git a/cores/esp8266/spiffs/spiffs_config.h b/cores/esp8266/spiffs/spiffs_config.h index c3343fe2fe..b44c865ce3 100644 --- a/cores/esp8266/spiffs/spiffs_config.h +++ b/cores/esp8266/spiffs/spiffs_config.h @@ -1,9 +1,9 @@ /* - * spiffs_config.h - * - * Created on: Jul 3, 2013 - * Author: petera - */ + spiffs_config.h + + Created on: Jul 3, 2013 + Author: petera +*/ #ifndef SPIFFS_CONFIG_H_ #define SPIFFS_CONFIG_H_ diff --git a/cores/esp8266/spiffs/spiffs_gc.cpp b/cores/esp8266/spiffs/spiffs_gc.cpp index fe556d1fc7..0a0faa7279 100644 --- a/cores/esp8266/spiffs/spiffs_gc.cpp +++ b/cores/esp8266/spiffs/spiffs_gc.cpp @@ -5,605 +5,697 @@ extern "C" { -// Erases a logical block and updates the erase counter. -// If cache is enabled, all pages that might be cached in this block -// is dropped. -static s32_t spiffs_gc_erase_block( - spiffs *fs, - spiffs_block_ix bix) { - s32_t res; - - SPIFFS_GC_DBG("gc: erase block " _SPIPRIbl "\n", bix); - res = spiffs_erase_block(fs, bix); - SPIFFS_CHECK_RES(res); + // Erases a logical block and updates the erase counter. + // If cache is enabled, all pages that might be cached in this block + // is dropped. + static s32_t spiffs_gc_erase_block( + spiffs *fs, + spiffs_block_ix bix) + { + s32_t res; + + SPIFFS_GC_DBG("gc: erase block " _SPIPRIbl "\n", bix); + res = spiffs_erase_block(fs, bix); + SPIFFS_CHECK_RES(res); #if SPIFFS_CACHE - { - u32_t i; - for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) { - spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i); - } - } + { + u32_t i; + for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) + { + spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i); + } + } #endif - return res; -} - -// Searches for blocks where all entries are deleted - if one is found, -// the block is erased. Compared to the non-quick gc, the quick one ensures -// that no updates are needed on existing objects on pages that are erased. -s32_t spiffs_gc_quick( - spiffs *fs, u16_t max_free_pages) { - s32_t res = SPIFFS_OK; - u32_t blocks = fs->block_count; - spiffs_block_ix cur_block = 0; - u32_t cur_block_addr = 0; - int cur_entry = 0; - spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; - - SPIFFS_GC_DBG("gc_quick: running\n"); + return res; + } + + // Searches for blocks where all entries are deleted - if one is found, + // the block is erased. Compared to the non-quick gc, the quick one ensures + // that no updates are needed on existing objects on pages that are erased. + s32_t spiffs_gc_quick( + spiffs *fs, u16_t max_free_pages) + { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = 0; + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + + SPIFFS_GC_DBG("gc_quick: running\n"); #if SPIFFS_GC_STATS - fs->stats_gc_runs++; + fs->stats_gc_runs++; #endif - int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); - - // find fully deleted blocks - // check each block - while (res == SPIFFS_OK && blocks--) { - u16_t deleted_pages_in_block = 0; - u16_t free_pages_in_block = 0; - - int obj_lookup_page = 0; - // check each object lookup page - while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { - int entry_offset = obj_lookup_page * entries_per_page; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - // check each entry - while (res == SPIFFS_OK && - cur_entry - entry_offset < entries_per_page && - cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { - spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; - if (obj_id == SPIFFS_OBJ_ID_DELETED) { - deleted_pages_in_block++; - } else if (obj_id == SPIFFS_OBJ_ID_FREE) { - // kill scan, go for next block - free_pages_in_block++; - if (free_pages_in_block > max_free_pages) { - obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); - res = 1; // kill object lu loop - break; - } - } else { - // kill scan, go for next block - obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); - res = 1; // kill object lu loop - break; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // find fully deleted blocks + // check each block + while (res == SPIFFS_OK && blocks--) + { + u16_t deleted_pages_in_block = 0; + u16_t free_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) + { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && + cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) + { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_DELETED) + { + deleted_pages_in_block++; + } + else if (obj_id == SPIFFS_OBJ_ID_FREE) + { + // kill scan, go for next block + free_pages_in_block++; + if (free_pages_in_block > max_free_pages) + { + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + res = 1; // kill object lu loop + break; + } + } + else + { + // kill scan, go for next block + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + res = 1; // kill object lu loop + break; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + if (res == 1) + { + res = SPIFFS_OK; + } + + if (res == SPIFFS_OK && + deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) && + free_pages_in_block <= max_free_pages) + { + // found a fully deleted block + fs->stats_p_deleted -= deleted_pages_in_block; + res = spiffs_gc_erase_block(fs, cur_block); + return res; + } + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + if (res == SPIFFS_OK) + { + res = SPIFFS_ERR_NO_DELETED_BLOCKS; } - cur_entry++; - } // per entry - obj_lookup_page++; - } // per object lookup page - if (res == 1) res = SPIFFS_OK; - - if (res == SPIFFS_OK && - deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) && - free_pages_in_block <= max_free_pages) { - // found a fully deleted block - fs->stats_p_deleted -= deleted_pages_in_block; - res = spiffs_gc_erase_block(fs, cur_block); - return res; + return res; } - cur_entry = 0; - cur_block++; - cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); - } // per block - - if (res == SPIFFS_OK) { - res = SPIFFS_ERR_NO_DELETED_BLOCKS; - } - return res; -} - -// Checks if garbage collecting is necessary. If so a candidate block is found, -// cleansed and erased -s32_t spiffs_gc_check( - spiffs *fs, - u32_t len) { - s32_t res; - s32_t free_pages = - (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2) - - fs->stats_p_allocated - fs->stats_p_deleted; - int tries = 0; - - if (fs->free_blocks > 3 && - (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { - return SPIFFS_OK; - } - - u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); -// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { -// SPIFFS_GC_DBG("gc: full freeblk:" _SPIPRIi " needed:" _SPIPRIi " free:" _SPIPRIi " dele:" _SPIPRIi "\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); -// return SPIFFS_ERR_FULL; -// } - if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) { - SPIFFS_GC_DBG("gc_check: full freeblk:" _SPIPRIi " needed:" _SPIPRIi " free:" _SPIPRIi " dele:" _SPIPRIi "\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); - return SPIFFS_ERR_FULL; - } - - do { - SPIFFS_GC_DBG("\ngc_check #" _SPIPRIi": run gc free_blocks:" _SPIPRIi " pfree:" _SPIPRIi " pallo:" _SPIPRIi " pdele:" _SPIPRIi " [" _SPIPRIi"] len:" _SPIPRIi " of " _SPIPRIi "\n", - tries, - fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted), - len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); - - spiffs_block_ix *cands; - int count; - spiffs_block_ix cand; - s32_t prev_free_pages = free_pages; - // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state - res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0); - SPIFFS_CHECK_RES(res); - if (count == 0) { - SPIFFS_GC_DBG("gc_check: no candidates, return\n"); - return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL; - } + // Checks if garbage collecting is necessary. If so a candidate block is found, + // cleansed and erased + s32_t spiffs_gc_check( + spiffs *fs, + u32_t len) + { + s32_t res; + s32_t free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; + int tries = 0; + + if (fs->free_blocks > 3 && + (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) + { + return SPIFFS_OK; + } + + u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); + // if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { + // SPIFFS_GC_DBG("gc: full freeblk:" _SPIPRIi " needed:" _SPIPRIi " free:" _SPIPRIi " dele:" _SPIPRIi "\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); + // return SPIFFS_ERR_FULL; + // } + if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) + { + SPIFFS_GC_DBG("gc_check: full freeblk:" _SPIPRIi " needed:" _SPIPRIi " free:" _SPIPRIi " dele:" _SPIPRIi "\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); + return SPIFFS_ERR_FULL; + } + + do + { + SPIFFS_GC_DBG("\ngc_check #" _SPIPRIi": run gc free_blocks:" _SPIPRIi " pfree:" _SPIPRIi " pallo:" _SPIPRIi " pdele:" _SPIPRIi " [" _SPIPRIi"] len:" _SPIPRIi " of " _SPIPRIi "\n", + tries, + fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages + fs->stats_p_allocated + fs->stats_p_deleted), + len, (u32_t)(free_pages * SPIFFS_DATA_PAGE_SIZE(fs))); + + spiffs_block_ix *cands; + int count; + spiffs_block_ix cand; + s32_t prev_free_pages = free_pages; + // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state + res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0); + SPIFFS_CHECK_RES(res); + if (count == 0) + { + SPIFFS_GC_DBG("gc_check: no candidates, return\n"); + return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL; + } #if SPIFFS_GC_STATS - fs->stats_gc_runs++; + fs->stats_gc_runs++; #endif - cand = cands[0]; - fs->cleaning = 1; - //SPIFFS_GC_DBG("gcing: cleaning block " _SPIPRIi "\n", cand); - res = spiffs_gc_clean(fs, cand); - fs->cleaning = 0; - if (res < 0) { - SPIFFS_GC_DBG("gc_check: cleaning block " _SPIPRIi ", result " _SPIPRIi "\n", cand, res); - } else { - SPIFFS_GC_DBG("gc_check: cleaning block " _SPIPRIi ", result " _SPIPRIi "\n", cand, res); - } - SPIFFS_CHECK_RES(res); + cand = cands[0]; + fs->cleaning = 1; + //SPIFFS_GC_DBG("gcing: cleaning block " _SPIPRIi "\n", cand); + res = spiffs_gc_clean(fs, cand); + fs->cleaning = 0; + if (res < 0) + { + SPIFFS_GC_DBG("gc_check: cleaning block " _SPIPRIi ", result " _SPIPRIi "\n", cand, res); + } + else + { + SPIFFS_GC_DBG("gc_check: cleaning block " _SPIPRIi ", result " _SPIPRIi "\n", cand, res); + } + SPIFFS_CHECK_RES(res); - res = spiffs_gc_erase_page_stats(fs, cand); - SPIFFS_CHECK_RES(res); + res = spiffs_gc_erase_page_stats(fs, cand); + SPIFFS_CHECK_RES(res); - res = spiffs_gc_erase_block(fs, cand); - SPIFFS_CHECK_RES(res); + res = spiffs_gc_erase_block(fs, cand); + SPIFFS_CHECK_RES(res); - free_pages = - (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) - - fs->stats_p_allocated - fs->stats_p_deleted; + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; - if (prev_free_pages <= 0 && prev_free_pages == free_pages) { - // abort early to reduce wear, at least tried once - SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n"); - break; - } + if (prev_free_pages <= 0 && prev_free_pages == free_pages) + { + // abort early to reduce wear, at least tried once + SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n"); + break; + } - } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || - (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); - - free_pages = - (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) - - fs->stats_p_allocated - fs->stats_p_deleted; - if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { - res = SPIFFS_ERR_FULL; - } - - SPIFFS_GC_DBG("gc_check: finished, " _SPIPRIi " dirty, blocks " _SPIPRIi " free, " _SPIPRIi " pages free, " _SPIPRIi " tries, res " _SPIPRIi "\n", - fs->stats_p_allocated + fs->stats_p_deleted, - fs->free_blocks, free_pages, tries, res); - - return res; -} - -// Updates page statistics for a block that is about to be erased -s32_t spiffs_gc_erase_page_stats( - spiffs *fs, - spiffs_block_ix bix) { - s32_t res = SPIFFS_OK; - int obj_lookup_page = 0; - int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); - spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; - int cur_entry = 0; - u32_t dele = 0; - u32_t allo = 0; - - // check each object lookup page - while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { - int entry_offset = obj_lookup_page * entries_per_page; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - // check each entry - while (res == SPIFFS_OK && - cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { - spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; - if (obj_id == SPIFFS_OBJ_ID_FREE) { - } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { - dele++; - } else { - allo++; - } - cur_entry++; - } // per entry - obj_lookup_page++; - } // per object lookup page - SPIFFS_GC_DBG("gc_check: wipe pallo:" _SPIPRIi " pdele:" _SPIPRIi "\n", allo, dele); - fs->stats_p_allocated -= allo; - fs->stats_p_deleted -= dele; - return res; -} - -// Finds block candidates to erase -s32_t spiffs_gc_find_candidate( - spiffs *fs, - spiffs_block_ix **block_candidates, - int *candidate_count, - char fs_crammed) { - s32_t res = SPIFFS_OK; - u32_t blocks = fs->block_count; - spiffs_block_ix cur_block = 0; - u32_t cur_block_addr = 0; - spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; - int cur_entry = 0; - - // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score - int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t))); - *candidate_count = 0; - memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - - // divide up work area into block indices and scores - spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; - s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); - - // align cand_scores on s32_t boundary - cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1)); - - *block_candidates = cand_blocks; - - int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); - - // check each block - while (res == SPIFFS_OK && blocks--) { - u16_t deleted_pages_in_block = 0; - u16_t used_pages_in_block = 0; - - int obj_lookup_page = 0; - // check each object lookup page - while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { - int entry_offset = obj_lookup_page * entries_per_page; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - // check each entry - while (res == SPIFFS_OK && - cur_entry - entry_offset < entries_per_page && - cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { - spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; - if (obj_id == SPIFFS_OBJ_ID_FREE) { - // when a free entry is encountered, scan logic ensures that all following entries are free also - res = 1; // kill object lu loop - break; - } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { - deleted_pages_in_block++; - } else { - used_pages_in_block++; - } - cur_entry++; - } // per entry - obj_lookup_page++; - } // per object lookup page - if (res == 1) res = SPIFFS_OK; - - // calculate score and insert into candidate table - // stoneage sort, but probably not so many blocks - if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) { - // read erase count - spiffs_obj_id erase_count; - res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, - SPIFFS_ERASE_COUNT_PADDR(fs, cur_block), - sizeof(spiffs_obj_id), (u8_t *)&erase_count); - SPIFFS_CHECK_RES(res); - - spiffs_obj_id erase_age; - if (fs->max_erase_count > erase_count) { - erase_age = fs->max_erase_count - erase_count; - } else { - erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count); - } - - s32_t score = - deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + - used_pages_in_block * SPIFFS_GC_HEUR_W_USED + - erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); - int cand_ix = 0; - SPIFFS_GC_DBG("gc_check: bix:" _SPIPRIbl" del:" _SPIPRIi " use:" _SPIPRIi " score:" _SPIPRIi "\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); - while (cand_ix < max_candidates) { - if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { - cand_blocks[cand_ix] = cur_block; - cand_scores[cand_ix] = score; - break; - } else if (cand_scores[cand_ix] < score) { - int reorder_cand_ix = max_candidates - 2; - while (reorder_cand_ix >= cand_ix) { - cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix]; - cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix]; - reorder_cand_ix--; - } - cand_blocks[cand_ix] = cur_block; - cand_scores[cand_ix] = score; - break; + } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || + (s32_t)len > free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); + + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; + if ((s32_t)len > free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) + { + res = SPIFFS_ERR_FULL; } - cand_ix++; - } - (*candidate_count)++; + + SPIFFS_GC_DBG("gc_check: finished, " _SPIPRIi " dirty, blocks " _SPIPRIi " free, " _SPIPRIi " pages free, " _SPIPRIi " tries, res " _SPIPRIi "\n", + fs->stats_p_allocated + fs->stats_p_deleted, + fs->free_blocks, free_pages, tries, res); + + return res; } - cur_entry = 0; - cur_block++; - cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); - } // per block - - return res; -} - -typedef enum { - FIND_OBJ_DATA, - MOVE_OBJ_DATA, - MOVE_OBJ_IX, - FINISHED -} spiffs_gc_clean_state; - -typedef struct { - spiffs_gc_clean_state state; - spiffs_obj_id cur_obj_id; - spiffs_span_ix cur_objix_spix; - spiffs_page_ix cur_objix_pix; - spiffs_page_ix cur_data_pix; - int stored_scan_entry_index; - u8_t obj_id_found; -} spiffs_gc; - -// Empties given block by moving all data into free pages of another block -// Strategy: -// loop: -// scan object lookup for object data pages -// for first found id, check spix and load corresponding object index page to memory -// push object scan lookup entry index -// rescan object lookup, find data pages with same id and referenced by same object index -// move data page, update object index in memory -// when reached end of lookup, store updated object index -// pop object scan lookup entry index -// repeat loop until end of object lookup -// scan object lookup again for remaining object index pages, move to new page in other block -// -s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { - s32_t res = SPIFFS_OK; - const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); - // this is the global localizer being pushed and popped - int cur_entry = 0; - spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; - spiffs_gc gc; // our stack frame/state - spiffs_page_ix cur_pix = 0; - spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; - spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; - - SPIFFS_GC_DBG("gc_clean: cleaning block " _SPIPRIbl "\n", bix); - - memset(&gc, 0, sizeof(spiffs_gc)); - gc.state = FIND_OBJ_DATA; - - if (fs->free_cursor_block_ix == bix) { - // move free cursor to next block, cannot use free pages from the block we want to clean - fs->free_cursor_block_ix = (bix+1)%fs->block_count; - fs->free_cursor_obj_lu_entry = 0; - SPIFFS_GC_DBG("gc_clean: move free cursor to block " _SPIPRIbl "\n", fs->free_cursor_block_ix); - } - - while (res == SPIFFS_OK && gc.state != FINISHED) { - SPIFFS_GC_DBG("gc_clean: state = " _SPIPRIi " entry:" _SPIPRIi "\n", gc.state, cur_entry); - gc.obj_id_found = 0; // reset (to no found data page) - - // scan through lookup pages - int obj_lookup_page = cur_entry / entries_per_page; - u8_t scan = 1; - // check each object lookup page - while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { - int entry_offset = obj_lookup_page * entries_per_page; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), - SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - // check each object lookup entry - while (scan && res == SPIFFS_OK && - cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { - spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; - cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry); - - // act upon object id depending on gc state - switch (gc.state) { - case FIND_OBJ_DATA: - // find a data page - if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && - ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) { - // found a data page, stop scanning and handle in switch case below - SPIFFS_GC_DBG("gc_clean: FIND_DATA state:" _SPIPRIi " - found obj id " _SPIPRIid"\n", gc.state, obj_id); - gc.obj_id_found = 1; - gc.cur_obj_id = obj_id; - gc.cur_data_pix = cur_pix; - scan = 0; - } - break; - case MOVE_OBJ_DATA: - // evacuate found data pages for corresponding object index we have in memory, - // update memory representation - if (obj_id == gc.cur_obj_id) { - spiffs_page_header p_hdr; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); - SPIFFS_CHECK_RES(res); - SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page " _SPIPRIid":" _SPIPRIsp" @ " _SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); - if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) { - SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n"); - } else { - spiffs_page_ix new_data_pix; - if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { - // move page - res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix); - SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg" to " _SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); - SPIFFS_CHECK_RES(res); - // move wipes obj_lu, reload it + // Updates page statistics for a block that is about to be erased + s32_t spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix) + { + s32_t res = SPIFFS_OK; + int obj_lookup_page = 0; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + u32_t dele = 0; + u32_t allo = 0; + + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) + { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) + { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) + { + } + else if (obj_id == SPIFFS_OBJ_ID_DELETED) + { + dele++; + } + else + { + allo++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + SPIFFS_GC_DBG("gc_check: wipe pallo:" _SPIPRIi " pdele:" _SPIPRIi "\n", allo, dele); + fs->stats_p_allocated -= allo; + fs->stats_p_deleted -= dele; + return res; + } + + // Finds block candidates to erase + s32_t spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidates, + int *candidate_count, + char fs_crammed) + { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + + // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score + int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs) - 8) / (sizeof(spiffs_block_ix) + sizeof(s32_t))); + *candidate_count = 0; + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + // divide up work area into block indices and scores + spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; + s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); + + // align cand_scores on s32_t boundary + cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1)); + + *block_candidates = cand_blocks; + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // check each block + while (res == SPIFFS_OK && blocks--) + { + u16_t deleted_pages_in_block = 0; + u16_t used_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) + { + int entry_offset = obj_lookup_page * entries_per_page; res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), - SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - SPIFFS_CHECK_RES(res); - } else { - // page is deleted but not deleted in lookup, scrap it - - // might seem unnecessary as we will erase this block, but - // we might get aborted - SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); - res = spiffs_page_delete(fs, cur_pix); - SPIFFS_CHECK_RES(res); - new_data_pix = SPIFFS_OBJ_ID_FREE; - } - // update memory representation of object index page with new data page - if (gc.cur_objix_spix == 0) { - // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; - SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page " _SPIPRIpg" to objix_hdr entry " _SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); - } else { - // update object index page - ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; - SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page " _SPIPRIpg" to objix entry " _SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); - } + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && + cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) + { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) + { + // when a free entry is encountered, scan logic ensures that all following entries are free also + res = 1; // kill object lu loop + break; + } + else if (obj_id == SPIFFS_OBJ_ID_DELETED) + { + deleted_pages_in_block++; + } + else + { + used_pages_in_block++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + if (res == 1) + { + res = SPIFFS_OK; } - } - break; - case MOVE_OBJ_IX: - // find and evacuate object index pages - if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && - (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { - // found an index object id - spiffs_page_header p_hdr; - spiffs_page_ix new_pix; - // load header - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); - SPIFFS_CHECK_RES(res); - if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { - // move page - res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix); - SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg" to " _SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr, - SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0); - // move wipes obj_lu, reload it - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), - SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - SPIFFS_CHECK_RES(res); - } else { - // page is deleted but not deleted in lookup, scrap it - - // might seem unnecessary as we will erase this block, but - // we might get aborted - SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); - res = spiffs_page_delete(fs, cur_pix); - if (res == SPIFFS_OK) { - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, - SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); - } + + // calculate score and insert into candidate table + // stoneage sort, but probably not so many blocks + if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) + { + // read erase count + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, cur_block), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + spiffs_obj_id erase_age; + if (fs->max_erase_count > erase_count) + { + erase_age = fs->max_erase_count - erase_count; + } + else + { + erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count); + } + + s32_t score = + deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + + used_pages_in_block * SPIFFS_GC_HEUR_W_USED + + erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); + int cand_ix = 0; + SPIFFS_GC_DBG("gc_check: bix:" _SPIPRIbl" del:" _SPIPRIi " use:" _SPIPRIi " score:" _SPIPRIi "\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); + while (cand_ix < max_candidates) + { + if (cand_blocks[cand_ix] == (spiffs_block_ix) - 1) + { + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } + else if (cand_scores[cand_ix] < score) + { + int reorder_cand_ix = max_candidates - 2; + while (reorder_cand_ix >= cand_ix) + { + cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix]; + cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix]; + reorder_cand_ix--; + } + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } + cand_ix++; + } + (*candidate_count)++; } - SPIFFS_CHECK_RES(res); - } - break; - default: - scan = 0; - break; - } // switch gc state - cur_entry++; - } // per entry - obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop - } // per object lookup page - if (res != SPIFFS_OK) break; - - // state finalization and switch - switch (gc.state) { - case FIND_OBJ_DATA: - if (gc.obj_id_found) { - // handle found data page - - // find out corresponding obj ix page and load it to memory - spiffs_page_header p_hdr; - spiffs_page_ix objix_pix; - gc.stored_scan_entry_index = cur_entry; // push cursor - cur_entry = 0; // restart scan from start - gc.state = MOVE_OBJ_DATA; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); - SPIFFS_CHECK_RES(res); - gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); - SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:" _SPIPRIsp"\n", gc.cur_objix_spix); - res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix); - if (res == SPIFFS_ERR_NOT_FOUND) { - // on borked systems we might get an ERR_NOT_FOUND here - - // this is handled by simply deleting the page as it is not referenced - // from anywhere - SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page " _SPIPRIpg"\n", gc.cur_data_pix); - res = spiffs_page_delete(fs, gc.cur_data_pix); - SPIFFS_CHECK_RES(res); - // then we restore states and continue scanning for data pages - cur_entry = gc.stored_scan_entry_index; // pop cursor - gc.state = FIND_OBJ_DATA; - break; // done + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + return res; + } + + typedef enum + { + FIND_OBJ_DATA, + MOVE_OBJ_DATA, + MOVE_OBJ_IX, + FINISHED + } spiffs_gc_clean_state; + + typedef struct + { + spiffs_gc_clean_state state; + spiffs_obj_id cur_obj_id; + spiffs_span_ix cur_objix_spix; + spiffs_page_ix cur_objix_pix; + spiffs_page_ix cur_data_pix; + int stored_scan_entry_index; + u8_t obj_id_found; + } spiffs_gc; + + // Empties given block by moving all data into free pages of another block + // Strategy: + // loop: + // scan object lookup for object data pages + // for first found id, check spix and load corresponding object index page to memory + // push object scan lookup entry index + // rescan object lookup, find data pages with same id and referenced by same object index + // move data page, update object index in memory + // when reached end of lookup, store updated object index + // pop object scan lookup entry index + // repeat loop until end of object lookup + // scan object lookup again for remaining object index pages, move to new page in other block + // + s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) + { + s32_t res = SPIFFS_OK; + const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + // this is the global localizer being pushed and popped + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_gc gc; // our stack frame/state + spiffs_page_ix cur_pix = 0; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + SPIFFS_GC_DBG("gc_clean: cleaning block " _SPIPRIbl "\n", bix); + + memset(&gc, 0, sizeof(spiffs_gc)); + gc.state = FIND_OBJ_DATA; + + if (fs->free_cursor_block_ix == bix) + { + // move free cursor to next block, cannot use free pages from the block we want to clean + fs->free_cursor_block_ix = (bix + 1) % fs->block_count; + fs->free_cursor_obj_lu_entry = 0; + SPIFFS_GC_DBG("gc_clean: move free cursor to block " _SPIPRIbl "\n", fs->free_cursor_block_ix); } - SPIFFS_CHECK_RES(res); - SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page " _SPIPRIpg"\n", objix_pix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - // cannot allow a gc if the presumed index in fact is no index, a - // check must run or lot of data may be lost - SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix); - gc.cur_objix_pix = objix_pix; - } else { - // no more data pages found, passed thru all block, start evacuating object indices - gc.state = MOVE_OBJ_IX; - cur_entry = 0; // restart entry scan index - } - break; - case MOVE_OBJ_DATA: { - // store modified objix (hdr) page residing in memory now that all - // data pages belonging to this object index and residing in the block - // we want to evacuate - spiffs_page_ix new_objix_pix; - gc.state = FIND_OBJ_DATA; - cur_entry = gc.stored_scan_entry_index; // pop cursor - if (gc.cur_objix_spix == 0) { - // store object index header page - res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix); - SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, " _SPIPRIpg":" _SPIPRIsp"\n", new_objix_pix, 0); - SPIFFS_CHECK_RES(res); - } else { - // store object index page - res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix); - SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, " _SPIPRIpg":" _SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, - SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); - } + + while (res == SPIFFS_OK && gc.state != FINISHED) + { + SPIFFS_GC_DBG("gc_clean: state = " _SPIPRIi " entry:" _SPIPRIi "\n", gc.state, cur_entry); + gc.obj_id_found = 0; // reset (to no found data page) + + // scan through lookup pages + int obj_lookup_page = cur_entry / entries_per_page; + u8_t scan = 1; + // check each object lookup page + while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) + { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each object lookup entry + while (scan && res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) + { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset]; + cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry); + + // act upon object id depending on gc state + switch (gc.state) + { + case FIND_OBJ_DATA: + // find a data page + if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && + ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) + { + // found a data page, stop scanning and handle in switch case below + SPIFFS_GC_DBG("gc_clean: FIND_DATA state:" _SPIPRIi " - found obj id " _SPIPRIid"\n", gc.state, obj_id); + gc.obj_id_found = 1; + gc.cur_obj_id = obj_id; + gc.cur_data_pix = cur_pix; + scan = 0; + } + break; + case MOVE_OBJ_DATA: + // evacuate found data pages for corresponding object index we have in memory, + // update memory representation + if (obj_id == gc.cur_obj_id) + { + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page " _SPIPRIid":" _SPIPRIsp" @ " _SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); + if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) + { + SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n"); + } + else + { + spiffs_page_ix new_data_pix; + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) + { + // move page + res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg" to " _SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); + SPIFFS_CHECK_RES(res); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } + else + { + // page is deleted but not deleted in lookup, scrap it - + // might seem unnecessary as we will erase this block, but + // we might get aborted + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + new_data_pix = SPIFFS_OBJ_ID_FREE; + } + // update memory representation of object index page with new data page + if (gc.cur_objix_spix == 0) + { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page " _SPIPRIpg" to objix_hdr entry " _SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } + else + { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page " _SPIPRIpg" to objix entry " _SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } + } + } + break; + case MOVE_OBJ_IX: + // find and evacuate object index pages + if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && + (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) + { + // found an index object id + spiffs_page_header p_hdr; + spiffs_page_ix new_pix; + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) + { + // move page + res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg" to " _SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr, + SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } + else + { + // page is deleted but not deleted in lookup, scrap it - + // might seem unnecessary as we will erase this block, but + // we might get aborted + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix " _SPIPRIid":" _SPIPRIsp" page " _SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + if (res == SPIFFS_OK) + { + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); + } + } + SPIFFS_CHECK_RES(res); + } + break; + default: + scan = 0; + break; + } // switch gc state + cur_entry++; + } // per entry + obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop + } // per object lookup page + if (res != SPIFFS_OK) + { + break; + } + + // state finalization and switch + switch (gc.state) + { + case FIND_OBJ_DATA: + if (gc.obj_id_found) + { + // handle found data page - + // find out corresponding obj ix page and load it to memory + spiffs_page_header p_hdr; + spiffs_page_ix objix_pix; + gc.stored_scan_entry_index = cur_entry; // push cursor + cur_entry = 0; // restart scan from start + gc.state = MOVE_OBJ_DATA; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); + SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:" _SPIPRIsp"\n", gc.cur_objix_spix); + res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) + { + // on borked systems we might get an ERR_NOT_FOUND here - + // this is handled by simply deleting the page as it is not referenced + // from anywhere + SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page " _SPIPRIpg"\n", gc.cur_data_pix); + res = spiffs_page_delete(fs, gc.cur_data_pix); + SPIFFS_CHECK_RES(res); + // then we restore states and continue scanning for data pages + cur_entry = gc.stored_scan_entry_index; // pop cursor + gc.state = FIND_OBJ_DATA; + break; // done + } + SPIFFS_CHECK_RES(res); + SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page " _SPIPRIpg"\n", objix_pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + // cannot allow a gc if the presumed index in fact is no index, a + // check must run or lot of data may be lost + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix); + gc.cur_objix_pix = objix_pix; + } + else + { + // no more data pages found, passed thru all block, start evacuating object indices + gc.state = MOVE_OBJ_IX; + cur_entry = 0; // restart entry scan index + } + break; + case MOVE_OBJ_DATA: + { + // store modified objix (hdr) page residing in memory now that all + // data pages belonging to this object index and residing in the block + // we want to evacuate + spiffs_page_ix new_objix_pix; + gc.state = FIND_OBJ_DATA; + cur_entry = gc.stored_scan_entry_index; // pop cursor + if (gc.cur_objix_spix == 0) + { + // store object index header page + res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, " _SPIPRIpg":" _SPIPRIsp"\n", new_objix_pix, 0); + SPIFFS_CHECK_RES(res); + } + else + { + // store object index page + res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, " _SPIPRIpg":" _SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + break; + case MOVE_OBJ_IX: + // scanned thru all block, no more object indices found - our work here is done + gc.state = FINISHED; + break; + default: + cur_entry = 0; + break; + } // switch gc.state + SPIFFS_GC_DBG("gc_clean: state-> " _SPIPRIi "\n", gc.state); + } // while state != FINISHED + + + return res; } - break; - case MOVE_OBJ_IX: - // scanned thru all block, no more object indices found - our work here is done - gc.state = FINISHED; - break; - default: - cur_entry = 0; - break; - } // switch gc.state - SPIFFS_GC_DBG("gc_clean: state-> " _SPIPRIi "\n", gc.state); - } // while state != FINISHED - - - return res; -} }; diff --git a/cores/esp8266/spiffs/spiffs_hydrogen.cpp b/cores/esp8266/spiffs/spiffs_hydrogen.cpp index fffc5a83a8..9b83524fde 100644 --- a/cores/esp8266/spiffs/spiffs_hydrogen.cpp +++ b/cores/esp8266/spiffs/spiffs_hydrogen.cpp @@ -1,9 +1,9 @@ /* - * spiffs_hydrogen.c - * - * Created on: Jun 16, 2013 - * Author: petera - */ + spiffs_hydrogen.c + + Created on: Jun 16, 2013 + Author: petera +*/ #include "spiffs.h" #include "spiffs_nucleus.h" @@ -11,1446 +11,1624 @@ extern "C" { #if SPIFFS_CACHE == 1 -static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); + static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); #endif #if SPIFFS_BUFFER_HELP -u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) { - (void)fs; // unused, avoid warning - return num_descs * sizeof(spiffs_fd); -} + u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) + { + (void)fs; // unused, avoid warning + return num_descs * sizeof(spiffs_fd); + } #if SPIFFS_CACHE -u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) { - return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); -} + u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) + { + return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); + } #endif #endif -u8_t SPIFFS_mounted(spiffs *fs) { - return SPIFFS_CHECK_MOUNT(fs); -} + u8_t SPIFFS_mounted(spiffs *fs) + { + return SPIFFS_CHECK_MOUNT(fs); + } -s32_t SPIFFS_format(spiffs *fs) { + s32_t SPIFFS_format(spiffs *fs) + { #if SPIFFS_READ_ONLY - (void)fs; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - if (SPIFFS_CHECK_MOUNT(fs)) { - fs->err_code = SPIFFS_ERR_MOUNTED; - return -1; - } - - s32_t res; - SPIFFS_LOCK(fs); - - spiffs_block_ix bix = 0; - while (bix < fs->block_count) { - fs->max_erase_count = 0; - res = spiffs_erase_block(fs, bix); - if (res != SPIFFS_OK) { - res = SPIFFS_ERR_ERASE_FAIL; - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - bix++; - } + SPIFFS_API_CHECK_CFG(fs); + if (SPIFFS_CHECK_MOUNT(fs)) + { + fs->err_code = SPIFFS_ERR_MOUNTED; + return -1; + } + + s32_t res; + SPIFFS_LOCK(fs); + + spiffs_block_ix bix = 0; + while (bix < fs->block_count) + { + fs->max_erase_count = 0; + res = spiffs_erase_block(fs, bix); + if (res != SPIFFS_OK) + { + res = SPIFFS_ERR_ERASE_FAIL; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + bix++; + } - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return 0; + return 0; #endif // SPIFFS_READ_ONLY -} + } #if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 -s32_t SPIFFS_probe_fs(spiffs_config *config) { - SPIFFS_API_DBG("%s\n", __func__); - s32_t res = spiffs_probe(config); - return res; -} + s32_t SPIFFS_probe_fs(spiffs_config *config) + { + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = spiffs_probe(config); + return res; + } #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 -s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, - u8_t *fd_space, u32_t fd_space_size, - void *cache, u32_t cache_size, - spiffs_check_callback check_cb_f) { - SPIFFS_API_DBG("%s " - " sz:" _SPIPRIi " logpgsz:" _SPIPRIi " logblksz:" _SPIPRIi " perasz:" _SPIPRIi - " addr:" _SPIPRIad - " fdsz:" _SPIPRIi " cachesz:" _SPIPRIi - "\n", - __func__, - SPIFFS_CFG_PHYS_SZ(fs), - SPIFFS_CFG_LOG_PAGE_SZ(fs), - SPIFFS_CFG_LOG_BLOCK_SZ(fs), - SPIFFS_CFG_PHYS_ERASE_SZ(fs), - SPIFFS_CFG_PHYS_ADDR(fs), - fd_space_size, cache_size); - void *user_data; - SPIFFS_LOCK(fs); - user_data = fs->user_data; - memset(fs, 0, sizeof(spiffs)); - _SPIFFS_MEMCPY(&fs->cfg, config, sizeof(spiffs_config)); - fs->user_data = user_data; - fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); - fs->work = &work[0]; - fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; - memset(fd_space, 0, fd_space_size); - // align fd_space pointer to pointer size byte boundary - u8_t ptr_size = sizeof(void*); - u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size-1); - if (addr_lsb) { - fd_space += (ptr_size-addr_lsb); - fd_space_size -= (ptr_size-addr_lsb); - } - fs->fd_space = fd_space; - fs->fd_count = (fd_space_size/sizeof(spiffs_fd)); - - // align cache pointer to 4 byte boundary - addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size-1); - if (addr_lsb) { - u8_t *cache_8 = (u8_t *)cache; - cache_8 += (ptr_size-addr_lsb); - cache = cache_8; - cache_size -= (ptr_size-addr_lsb); - } - if (cache_size & (ptr_size-1)) { - cache_size -= (cache_size & (ptr_size-1)); - } + s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f) + { + SPIFFS_API_DBG("%s " + " sz:" _SPIPRIi " logpgsz:" _SPIPRIi " logblksz:" _SPIPRIi " perasz:" _SPIPRIi + " addr:" _SPIPRIad + " fdsz:" _SPIPRIi " cachesz:" _SPIPRIi + "\n", + __func__, + SPIFFS_CFG_PHYS_SZ(fs), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + SPIFFS_CFG_LOG_BLOCK_SZ(fs), + SPIFFS_CFG_PHYS_ERASE_SZ(fs), + SPIFFS_CFG_PHYS_ADDR(fs), + fd_space_size, cache_size); + void *user_data; + SPIFFS_LOCK(fs); + user_data = fs->user_data; + memset(fs, 0, sizeof(spiffs)); + _SPIFFS_MEMCPY(&fs->cfg, config, sizeof(spiffs_config)); + fs->user_data = user_data; + fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); + fs->work = &work[0]; + fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; + memset(fd_space, 0, fd_space_size); + // align fd_space pointer to pointer size byte boundary + u8_t ptr_size = sizeof(void*); + u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size - 1); + if (addr_lsb) + { + fd_space += (ptr_size - addr_lsb); + fd_space_size -= (ptr_size - addr_lsb); + } + fs->fd_space = fd_space; + fs->fd_count = (fd_space_size / sizeof(spiffs_fd)); + + // align cache pointer to 4 byte boundary + addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size - 1); + if (addr_lsb) + { + u8_t *cache_8 = (u8_t *)cache; + cache_8 += (ptr_size - addr_lsb); + cache = cache_8; + cache_size -= (ptr_size - addr_lsb); + } + if (cache_size & (ptr_size - 1)) + { + cache_size -= (cache_size & (ptr_size - 1)); + } #if SPIFFS_CACHE - fs->cache = cache; - fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs)*32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs)*32 : cache_size; - spiffs_cache_init(fs); + fs->cache = cache; + fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs) * 32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs) * 32 : cache_size; + spiffs_cache_init(fs); #endif - s32_t res; + s32_t res; #if SPIFFS_USE_MAGIC - res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE; - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #endif - fs->config_magic = SPIFFS_CONFIG_MAGIC; + fs->config_magic = SPIFFS_CONFIG_MAGIC; - res = spiffs_obj_lu_scan(fs); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_obj_lu_scan(fs); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_DBG("page index byte len: " _SPIPRIi "\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)); - SPIFFS_DBG("object lookup pages: " _SPIPRIi "\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs)); - SPIFFS_DBG("page pages per block: " _SPIPRIi "\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs)); - SPIFFS_DBG("page header length: " _SPIPRIi "\n", (u32_t)sizeof(spiffs_page_header)); - SPIFFS_DBG("object header index entries: " _SPIPRIi "\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs)); - SPIFFS_DBG("object index entries: " _SPIPRIi "\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs)); - SPIFFS_DBG("available file descriptors: " _SPIPRIi "\n", (u32_t)fs->fd_count); - SPIFFS_DBG("free blocks: " _SPIPRIi "\n", (u32_t)fs->free_blocks); + SPIFFS_DBG("page index byte len: " _SPIPRIi "\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: " _SPIPRIi "\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: " _SPIPRIi "\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: " _SPIPRIi "\n", (u32_t)sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: " _SPIPRIi "\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: " _SPIPRIi "\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: " _SPIPRIi "\n", (u32_t)fs->fd_count); + SPIFFS_DBG("free blocks: " _SPIPRIi "\n", (u32_t)fs->free_blocks); - fs->check_cb_f = check_cb_f; + fs->check_cb_f = check_cb_f; - fs->mounted = 1; + fs->mounted = 1; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return 0; -} + return 0; + } -void SPIFFS_unmount(spiffs *fs) { - SPIFFS_API_DBG("%s\n", __func__); - if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return; - SPIFFS_LOCK(fs); - u32_t i; - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - if (cur_fd->file_nbr != 0) { + void SPIFFS_unmount(spiffs *fs) + { + SPIFFS_API_DBG("%s\n", __func__); + if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) + { + return; + } + SPIFFS_LOCK(fs); + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) + { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0) + { #if SPIFFS_CACHE - (void)spiffs_fflush_cache(fs, cur_fd->file_nbr); + (void)spiffs_fflush_cache(fs, cur_fd->file_nbr); #endif - spiffs_fd_return(fs, cur_fd->file_nbr); - } - } - fs->mounted = 0; + spiffs_fd_return(fs, cur_fd->file_nbr); + } + } + fs->mounted = 0; - SPIFFS_UNLOCK(fs); -} + SPIFFS_UNLOCK(fs); + } -s32_t SPIFFS_errno(spiffs *fs) { - return fs->err_code; -} + s32_t SPIFFS_errno(spiffs *fs) + { + return fs->err_code; + } -void SPIFFS_clearerr(spiffs *fs) { - SPIFFS_API_DBG("%s\n", __func__); - fs->err_code = SPIFFS_OK; -} + void SPIFFS_clearerr(spiffs *fs) + { + SPIFFS_API_DBG("%s\n", __func__); + fs->err_code = SPIFFS_OK; + } -s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) { - SPIFFS_API_DBG("%s '%s'\n", __func__, path); + s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) + { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); #if SPIFFS_READ_ONLY - (void)fs; (void)path; (void)mode; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; (void)path; (void)mode; + return SPIFFS_ERR_RO_NOT_IMPL; #else - (void)mode; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { - SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); - } - SPIFFS_LOCK(fs); - spiffs_obj_id obj_id; - s32_t res; - - res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); - return 0; + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) + { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + spiffs_obj_id obj_id; + s32_t res; + + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; #endif // SPIFFS_READ_ONLY -} + } -spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) { - SPIFFS_API_DBG("%s '%s' " _SPIPRIfl "\n", __func__, path, flags); - (void)mode; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { - SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); - } - SPIFFS_LOCK(fs); + spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) + { + SPIFFS_API_DBG("%s '%s' " _SPIPRIfl "\n", __func__, path, flags); + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) + { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); - spiffs_fd *fd; - spiffs_page_ix pix; + spiffs_fd *fd; + spiffs_page_ix pix; #if SPIFFS_READ_ONLY - // not valid flags in read only mode - flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC); + // not valid flags in read only mode + flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC); #endif // SPIFFS_READ_ONLY - s32_t res = spiffs_fd_find_new(fs, &fd, path); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + s32_t res = spiffs_fd_find_new(fs, &fd, path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if ((flags & SPIFFS_O_CREAT) == 0) + { + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } - res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); - if ((flags & SPIFFS_O_CREAT) == 0) { - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - - if (res == SPIFFS_OK && - (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) { - // creat and excl and file exists - fail - res = SPIFFS_ERR_FILE_EXISTS; - spiffs_fd_return(fs, fd->file_nbr); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - - if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { + if (res == SPIFFS_OK && + (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) + { + // creat and excl and file exists - fail + res = SPIFFS_ERR_FILE_EXISTS; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) + { #if !SPIFFS_READ_ONLY - spiffs_obj_id obj_id; - // no need to enter conflicting name here, already looked for it above - res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, &pix); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - flags &= ~SPIFFS_O_TRUNC; + spiffs_obj_id obj_id; + // no need to enter conflicting name here, already looked for it above + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0); + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, &pix); + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + flags &= ~SPIFFS_O_TRUNC; #endif // !SPIFFS_READ_ONLY - } else { - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + else + { + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if !SPIFFS_READ_ONLY - if (flags & SPIFFS_O_TRUNC) { - res = spiffs_object_truncate(fd, 0, 0); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if (flags & SPIFFS_O_TRUNC) + { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } #endif // !SPIFFS_READ_ONLY - fd->fdoffset = 0; + fd->fdoffset = 0; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return SPIFFS_FH_OFFS(fs, fd->file_nbr); -} + return SPIFFS_FH_OFFS(fs, fd->file_nbr); + } -spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) { - SPIFFS_API_DBG("%s '%s':" _SPIPRIid " " _SPIPRIfl "\n", __func__, e->name, e->obj_id, flags); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) + { + SPIFFS_API_DBG("%s '%s':" _SPIPRIid " " _SPIPRIfl "\n", __func__, e->name, e->obj_id, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; + spiffs_fd *fd; - s32_t res = spiffs_fd_find_new(fs, &fd, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode); + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if !SPIFFS_READ_ONLY - if (flags & SPIFFS_O_TRUNC) { - res = spiffs_object_truncate(fd, 0, 0); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if (flags & SPIFFS_O_TRUNC) + { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } #endif // !SPIFFS_READ_ONLY - fd->fdoffset = 0; + fd->fdoffset = 0; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return SPIFFS_FH_OFFS(fs, fd->file_nbr); -} + return SPIFFS_FH_OFFS(fs, fd->file_nbr); + } -spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) { - SPIFFS_API_DBG("%s " _SPIPRIpg " " _SPIPRIfl "\n", __func__, page_ix, flags); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) + { + SPIFFS_API_DBG("%s " _SPIPRIpg " " _SPIPRIfl "\n", __func__, page_ix, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; + spiffs_fd *fd; - s32_t res = spiffs_fd_find_new(fs, &fd, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) { - res = SPIFFS_ERR_NOT_A_FILE; - spiffs_fd_return(fs, fd->file_nbr); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) + { + res = SPIFFS_ERR_NOT_A_FILE; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } - res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode); - if (res == SPIFFS_ERR_IS_FREE || - res == SPIFFS_ERR_DELETED || - res == SPIFFS_ERR_NOT_FINALIZED || - res == SPIFFS_ERR_NOT_INDEX || - res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) { - res = SPIFFS_ERR_NOT_A_FILE; - } - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode); + if (res == SPIFFS_ERR_IS_FREE || + res == SPIFFS_ERR_DELETED || + res == SPIFFS_ERR_NOT_FINALIZED || + res == SPIFFS_ERR_NOT_INDEX || + res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) + { + res = SPIFFS_ERR_NOT_A_FILE; + } + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if !SPIFFS_READ_ONLY - if (flags & SPIFFS_O_TRUNC) { - res = spiffs_object_truncate(fd, 0, 0); - if (res < SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if (flags & SPIFFS_O_TRUNC) + { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } #endif // !SPIFFS_READ_ONLY - fd->fdoffset = 0; + fd->fdoffset = 0; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return SPIFFS_FH_OFFS(fs, fd->file_nbr); -} + return SPIFFS_FH_OFFS(fs, fd->file_nbr); + } -static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) + { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; - s32_t res; + spiffs_fd *fd; + s32_t res; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if ((fd->flags & SPIFFS_O_RDONLY) == 0) { - res = SPIFFS_ERR_NOT_READABLE; - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if ((fd->flags & SPIFFS_O_RDONLY) == 0) + { + res = SPIFFS_ERR_NOT_READABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } - if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) { - // special case for zero sized files - res = SPIFFS_ERR_END_OF_OBJECT; - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) + { + // special case for zero sized files + res = SPIFFS_ERR_END_OF_OBJECT; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } #if SPIFFS_CACHE_WR - spiffs_fflush_cache(fs, fh); + spiffs_fflush_cache(fs, fh); #endif - if (fd->fdoffset + len >= fd->size) { - // reading beyond file size - s32_t avail = fd->size - fd->fdoffset; - if (avail <= 0) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); - } - res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf); - if (res == SPIFFS_ERR_END_OF_OBJECT) { - fd->fdoffset += avail; - SPIFFS_UNLOCK(fs); - return avail; - } else { - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - len = avail; - } - } else { - // reading within file size - res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } - fd->fdoffset += len; + if (fd->fdoffset + len >= fd->size) + { + // reading beyond file size + s32_t avail = fd->size - fd->fdoffset; + if (avail <= 0) + { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); + } + res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf); + if (res == SPIFFS_ERR_END_OF_OBJECT) + { + fd->fdoffset += avail; + SPIFFS_UNLOCK(fs); + return avail; + } + else + { + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + len = avail; + } + } + else + { + // reading within file size + res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + fd->fdoffset += len; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return len; -} + return len; + } -s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { - SPIFFS_API_DBG("%s " _SPIPRIfd " " _SPIPRIi "\n", __func__, fh, len); - s32_t res = spiffs_hydro_read(fs, fh, buf, len); - if (res == SPIFFS_ERR_END_OF_OBJECT) { - res = 0; - } - return res; -} + s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) + { + SPIFFS_API_DBG("%s " _SPIPRIfd " " _SPIPRIi "\n", __func__, fh, len); + s32_t res = spiffs_hydro_read(fs, fh, buf, len); + if (res == SPIFFS_ERR_END_OF_OBJECT) + { + res = 0; + } + return res; + } #if !SPIFFS_READ_ONLY -static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { - (void)fs; - s32_t res = SPIFFS_OK; - s32_t remaining = len; - if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { - s32_t m_len = MIN((s32_t)(fd->size - offset), len); - res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); - SPIFFS_CHECK_RES(res); - remaining -= m_len; - u8_t *buf_8 = (u8_t *)buf; - buf_8 += m_len; - buf = buf_8; - offset += m_len; - } - if (remaining > 0) { - res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); - SPIFFS_CHECK_RES(res); - } - return len; - -} + static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) + { + (void)fs; + s32_t res = SPIFFS_OK; + s32_t remaining = len; + if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) + { + s32_t m_len = MIN((s32_t)(fd->size - offset), len); + res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); + SPIFFS_CHECK_RES(res); + remaining -= m_len; + u8_t *buf_8 = (u8_t *)buf; + buf_8 += m_len; + buf = buf_8; + offset += m_len; + } + if (remaining > 0) + { + res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); + SPIFFS_CHECK_RES(res); + } + return len; + + } #endif // !SPIFFS_READ_ONLY -s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { - SPIFFS_API_DBG("%s " _SPIPRIfd " " _SPIPRIi "\n", __func__, fh, len); + s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) + { + SPIFFS_API_DBG("%s " _SPIPRIfd " " _SPIPRIi "\n", __func__, fh, len); #if SPIFFS_READ_ONLY - (void)fs; (void)fh; (void)buf; (void)len; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; (void)fh; (void)buf; (void)len; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; - s32_t res; - u32_t offset; + spiffs_fd *fd; + s32_t res; + u32_t offset; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if ((fd->flags & SPIFFS_O_WRONLY) == 0) { - res = SPIFFS_ERR_NOT_WRITABLE; - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if ((fd->flags & SPIFFS_O_WRONLY) == 0) + { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } - if ((fd->flags & SPIFFS_O_APPEND)) { - fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; - } - offset = fd->fdoffset; + if ((fd->flags & SPIFFS_O_APPEND)) + { + fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + } + offset = fd->fdoffset; #if SPIFFS_CACHE_WR - if (fd->cache_page == 0) { - // see if object id is associated with cache already - fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); - } + if (fd->cache_page == 0) + { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } #endif - if (fd->flags & SPIFFS_O_APPEND) { - if (fd->size == SPIFFS_UNDEFINED_LEN) { - offset = 0; - } else { - offset = fd->size; - } + if (fd->flags & SPIFFS_O_APPEND) + { + if (fd->size == SPIFFS_UNDEFINED_LEN) + { + offset = 0; + } + else + { + offset = fd->size; + } #if SPIFFS_CACHE_WR - if (fd->cache_page) { - offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size); - } + if (fd->cache_page) + { + offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size); + } #endif - } + } #if SPIFFS_CACHE_WR - if ((fd->flags & SPIFFS_O_DIRECT) == 0) { - if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { - // small write, try to cache it - u8_t alloc_cpage = 1; - if (fd->cache_page) { - // have a cached page for this fd already, check cache page boundaries - if (offset < fd->cache_page->offset || // writing before cache - offset > fd->cache_page->offset + fd->cache_page->size || // writing after cache - offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page - { - // boundary violation, write back cache first and allocate new - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid ", boundary viol, offs:" _SPIPRIi " size:" _SPIPRIi "\n", - fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); - res = spiffs_hydro_write(fs, fd, - spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), - fd->cache_page->offset, fd->cache_page->size); - spiffs_cache_fd_release(fs, fd->cache_page); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } else { - // writing within cache - alloc_cpage = 0; - } - } - - if (alloc_cpage) { - fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); - if (fd->cache_page) { - fd->cache_page->offset = offset; - fd->cache_page->size = 0; - SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid "\n", - fd->cache_page->ix, fd->file_nbr, fd->obj_id); - } - } - - if (fd->cache_page) { - u32_t offset_in_cpage = offset - fd->cache_page->offset; - SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid ", offs " _SPIPRIi ":" _SPIPRIi " len " _SPIPRIi "\n", - fd->cache_page->ix, fd->file_nbr, fd->obj_id, - offset, offset_in_cpage, len); - spiffs_cache *cache = spiffs_get_cache(fs); - u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); -#ifdef _SPIFFS_TEST + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { - intptr_t __a1 = (u8_t*)&cpage_data[offset_in_cpage]-(u8_t*)cache; - intptr_t __a2 = (u8_t*)&cpage_data[offset_in_cpage]+len-(u8_t*)cache; - intptr_t __b = sizeof(spiffs_cache) + cache->cpage_count * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); - if (__a1 > __b || __a2 > __b) { - printf("FATAL OOB: CACHE_WR: memcpy to cache buffer ixs:%4ld..%4ld of %4ld\n", __a1, __a2, __b); - ERREXIT(); - } + if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) + { + // small write, try to cache it + u8_t alloc_cpage = 1; + if (fd->cache_page) + { + // have a cached page for this fd already, check cache page boundaries + if (offset < fd->cache_page->offset || // writing before cache + offset > fd->cache_page->offset + fd->cache_page->size || // writing after cache + offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page + { + // boundary violation, write back cache first and allocate new + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid ", boundary viol, offs:" _SPIPRIi " size:" _SPIPRIi "\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + else + { + // writing within cache + alloc_cpage = 0; + } + } + + if (alloc_cpage) + { + fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); + if (fd->cache_page) + { + fd->cache_page->offset = offset; + fd->cache_page->size = 0; + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid "\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id); + } + } + + if (fd->cache_page) + { + u32_t offset_in_cpage = offset - fd->cache_page->offset; + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid ", offs " _SPIPRIi ":" _SPIPRIi " len " _SPIPRIi "\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, + offset, offset_in_cpage, len); + spiffs_cache *cache = spiffs_get_cache(fs); + u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); +#ifdef _SPIFFS_TEST + { + intptr_t __a1 = (u8_t*)&cpage_data[offset_in_cpage] - (u8_t*)cache; + intptr_t __a2 = (u8_t*)&cpage_data[offset_in_cpage] + len - (u8_t*)cache; + intptr_t __b = sizeof(spiffs_cache) + cache->cpage_count * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); + if (__a1 > __b || __a2 > __b) + { + printf("FATAL OOB: CACHE_WR: memcpy to cache buffer ixs:%4ld..%4ld of %4ld\n", __a1, __a2, __b); + ERREXIT(); + } + } +#endif + _SPIFFS_MEMCPY(&cpage_data[offset_in_cpage], buf, len); + fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return len; + } + else + { + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; + } + } + else + { + // big write, no need to cache it - but first check if there is a cached write already + if (fd->cache_page) + { + // write back cache first + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid ", big write, offs:" _SPIPRIi " size:" _SPIPRIi "\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + // data written below + } + } } #endif - _SPIFFS_MEMCPY(&cpage_data[offset_in_cpage], buf, len); - fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len); - fd->fdoffset += len; - SPIFFS_UNLOCK(fs); - return len; - } else { + res = spiffs_hydro_write(fs, fd, buf, offset, len); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; - } - } else { - // big write, no need to cache it - but first check if there is a cached write already - if (fd->cache_page) { - // write back cache first - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid ", big write, offs:" _SPIPRIi " size:" _SPIPRIi "\n", - fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); - res = spiffs_hydro_write(fs, fd, - spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), - fd->cache_page->offset, fd->cache_page->size); - spiffs_cache_fd_release(fs, fd->cache_page); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - // data written below - } +#endif // SPIFFS_READ_ONLY } - } -#endif - res = spiffs_hydro_write(fs, fd, buf, offset, len); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - fd->fdoffset += len; + s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) + { + SPIFFS_API_DBG("%s " _SPIPRIfd " " _SPIPRIi " %s\n", __func__, fh, offs, (const char* []) + {"SET", "CUR", "END", "???" + }[MIN(whence, 3)]); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif - return res; -#endif // SPIFFS_READ_ONLY -} + s32_t file_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + + switch (whence) + { + case SPIFFS_SEEK_CUR: + offs = fd->fdoffset + offs; + break; + case SPIFFS_SEEK_END: + offs = file_size + offs; + break; + } + if (offs < 0) + { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_SEEK_BOUNDS); + } + if (offs > file_size) + { + fd->fdoffset = file_size; + res = SPIFFS_ERR_END_OF_OBJECT; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); -s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { - SPIFFS_API_DBG("%s " _SPIPRIfd " " _SPIPRIi " %s\n", __func__, fh, offs, (const char* []){"SET","CUR","END","???"}[MIN(whence,3)]); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + spiffs_span_ix data_spix = (offs > 0 ? (offs - 1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (fd->cursor_objix_spix != objix_spix) + { + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span( + fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->cursor_objix_spix = objix_spix; + fd->cursor_objix_pix = pix; + } + fd->fdoffset = offs; - spiffs_fd *fd; - s32_t res; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); -#if SPIFFS_CACHE_WR - spiffs_fflush_cache(fs, fh); -#endif + return offs; + } - s32_t file_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; - - switch (whence) { - case SPIFFS_SEEK_CUR: - offs = fd->fdoffset+offs; - break; - case SPIFFS_SEEK_END: - offs = file_size + offs; - break; - } - if (offs < 0) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_SEEK_BOUNDS); - } - if (offs > file_size) { - fd->fdoffset = file_size; - res = SPIFFS_ERR_END_OF_OBJECT; - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - spiffs_span_ix data_spix = (offs > 0 ? (offs-1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs); - spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - if (fd->cursor_objix_spix != objix_spix) { - spiffs_page_ix pix; - res = spiffs_obj_lu_find_id_and_span( - fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - fd->cursor_objix_spix = objix_spix; - fd->cursor_objix_pix = pix; - } - fd->fdoffset = offs; - - SPIFFS_UNLOCK(fs); - - return offs; -} - -s32_t SPIFFS_remove(spiffs *fs, const char *path) { - SPIFFS_API_DBG("%s '%s'\n", __func__, path); + s32_t SPIFFS_remove(spiffs *fs, const char *path) + { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); #if SPIFFS_READ_ONLY - (void)fs; (void)path; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; (void)path; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { - SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); - } - SPIFFS_LOCK(fs); - - spiffs_fd *fd; - spiffs_page_ix pix; - s32_t res; - - res = spiffs_fd_find_new(fs, &fd, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - res = spiffs_object_open_by_page(fs, pix, fd, 0,0); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - res = spiffs_object_truncate(fd, 0, 1); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - SPIFFS_UNLOCK(fs); - return 0; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) + { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + s32_t res; + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if (res != SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_truncate(fd, 0, 1); + if (res != SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return 0; #endif // SPIFFS_READ_ONLY -} + } -s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); + s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) + { + SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); #if SPIFFS_READ_ONLY - (void)fs; (void)fh; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; (void)fh; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); - - spiffs_fd *fd; - s32_t res; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if ((fd->flags & SPIFFS_O_WRONLY) == 0) { - res = SPIFFS_ERR_NOT_WRITABLE; - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if ((fd->flags & SPIFFS_O_WRONLY) == 0) + { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } #if SPIFFS_CACHE_WR - spiffs_cache_fd_release(fs, fd->cache_page); + spiffs_cache_fd_release(fs, fd->cache_page); #endif - res = spiffs_object_truncate(fd, 0, 1); + res = spiffs_object_truncate(fd, 0, 1); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return 0; + return 0; #endif // SPIFFS_READ_ONLY -} - -static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { - (void)fh; - spiffs_page_object_ix_header objix_hdr; - spiffs_obj_id obj_id; - s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, - SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); - SPIFFS_API_CHECK_RES(fs, res); - - u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , pix)) + - SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id); - res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh, - obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); - SPIFFS_API_CHECK_RES(fs, res); - - s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - s->type = objix_hdr.type; - s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; - s->pix = pix; - strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); + } + + static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) + { + (void)fh; + spiffs_page_object_ix_header objix_hdr; + spiffs_obj_id obj_id; + s32_t res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_API_CHECK_RES(fs, res); + + u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh, + obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_API_CHECK_RES(fs, res); + + s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + s->type = objix_hdr.type; + s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + s->pix = pix; + strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); #if SPIFFS_OBJ_META_LEN - _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); + _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); #endif - return res; -} + return res; + } -s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) { - SPIFFS_API_DBG("%s '%s'\n", __func__, path); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { - SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); - } - SPIFFS_LOCK(fs); + s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) + { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) + { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); - s32_t res; - spiffs_page_ix pix; + s32_t res; + spiffs_page_ix pix; - res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_stat_pix(fs, pix, 0, s); + res = spiffs_stat_pix(fs, pix, 0, s); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; -} + return res; + } -s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { - SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) + { + SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_fd *fd; - s32_t res; + spiffs_fd *fd; + s32_t res; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if SPIFFS_CACHE_WR - spiffs_fflush_cache(fs, fh); + spiffs_fflush_cache(fs, fh); #endif - res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s); + res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; -} + return res; + } -// Checks if there are any cached writes for the object id associated with -// given filehandle. If so, these writes are flushed. + // Checks if there are any cached writes for the object id associated with + // given filehandle. If so, these writes are flushed. #if SPIFFS_CACHE == 1 -static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { - (void)fs; - (void)fh; - s32_t res = SPIFFS_OK; + static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) + { + (void)fs; + (void)fh; + s32_t res = SPIFFS_OK; #if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES(fs, res); + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); - if ((fd->flags & SPIFFS_O_DIRECT) == 0) { - if (fd->cache_page == 0) { - // see if object id is associated with cache already - fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); - } - if (fd->cache_page) { - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid ", flush, offs:" _SPIPRIi " size:" _SPIPRIi "\n", - fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); - res = spiffs_hydro_write(fs, fd, - spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), - fd->cache_page->offset, fd->cache_page->size); - if (res < SPIFFS_OK) { - fs->err_code = res; - } - spiffs_cache_fd_release(fs, fd->cache_page); - } - } + if ((fd->flags & SPIFFS_O_DIRECT) == 0) + { + if (fd->cache_page == 0) + { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } + if (fd->cache_page) + { + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page " _SPIPRIi " for fd " _SPIPRIfd ":" _SPIPRIid ", flush, offs:" _SPIPRIi " size:" _SPIPRIi "\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + if (res < SPIFFS_OK) + { + fs->err_code = res; + } + spiffs_cache_fd_release(fs, fd->cache_page); + } + } #endif - return res; -} + return res; + } #endif -s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); - (void)fh; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - s32_t res = SPIFFS_OK; + s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) + { + SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); + (void)fh; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + s32_t res = SPIFFS_OK; #if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR - SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fflush_cache(fs, fh); - SPIFFS_API_CHECK_RES_UNLOCK(fs,res); - SPIFFS_UNLOCK(fs); + SPIFFS_LOCK(fs); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); #endif - return res; -} + return res; + } -s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); + s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) + { + SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); - s32_t res = SPIFFS_OK; - SPIFFS_LOCK(fs); + s32_t res = SPIFFS_OK; + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); + fh = SPIFFS_FH_UNOFFS(fs, fh); #if SPIFFS_CACHE - res = spiffs_fflush_cache(fs, fh); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #endif - res = spiffs_fd_return(fs, fh); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fd_return(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; -} + return res; + } -s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) { - SPIFFS_API_DBG("%s %s %s\n", __func__, old_path, new_path); + s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) + { + SPIFFS_API_DBG("%s %s %s\n", __func__, old_path, new_path); #if SPIFFS_READ_ONLY - (void)fs; (void)old_path; (void)new_path; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; (void)old_path; (void)new_path; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 || - strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) { - SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); - } - SPIFFS_LOCK(fs); - - spiffs_page_ix pix_old, pix_dummy; - spiffs_fd *fd; - - s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)old_path, &pix_old); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)new_path, &pix_dummy); - if (res == SPIFFS_ERR_NOT_FOUND) { - res = SPIFFS_OK; - } else if (res == SPIFFS_OK) { - res = SPIFFS_ERR_CONFLICTING_NAME; - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - res = spiffs_fd_find_new(fs, &fd, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path, - 0, 0, &pix_dummy); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 || + strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) + { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_page_ix pix_old, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)old_path, &pix_old); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)new_path, &pix_dummy); + if (res == SPIFFS_ERR_NOT_FOUND) + { + res = SPIFFS_OK; + } + else if (res == SPIFFS_OK) + { + res = SPIFFS_ERR_CONFLICTING_NAME; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0); + if (res != SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path, + 0, 0, &pix_dummy); #if SPIFFS_TEMPORAL_FD_CACHE - if (res == SPIFFS_OK) { - spiffs_fd_temporal_cache_rehash(fs, old_path, new_path); - } + if (res == SPIFFS_OK) + { + spiffs_fd_temporal_cache_rehash(fs, old_path, new_path); + } #endif - spiffs_fd_return(fs, fd->file_nbr); + spiffs_fd_return(fs, fd->file_nbr); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; + return res; #endif // SPIFFS_READ_ONLY -} + } #if SPIFFS_OBJ_META_LEN -s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) { + s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) + { #if SPIFFS_READ_ONLY - (void)fs; (void)name; (void)meta; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; (void)name; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - spiffs_page_ix pix, pix_dummy; - spiffs_fd *fd; + spiffs_page_ix pix, pix_dummy; + spiffs_fd *fd; - s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)name, &pix); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)name, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_fd_find_new(fs, &fd, 0); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, - 0, &pix_dummy); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); - spiffs_fd_return(fs, fd->file_nbr); + spiffs_fd_return(fs, fd->file_nbr); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; + return res; #endif // SPIFFS_READ_ONLY -} + } -s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) { + s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) + { #if SPIFFS_READ_ONLY - (void)fs; (void)fh; (void)meta; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; (void)fh; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; #else - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - s32_t res; - spiffs_fd *fd; - spiffs_page_ix pix_dummy; + s32_t res; + spiffs_fd *fd; + spiffs_page_ix pix_dummy; - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if ((fd->flags & SPIFFS_O_WRONLY) == 0) { - res = SPIFFS_ERR_NOT_WRITABLE; - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } + if ((fd->flags & SPIFFS_O_WRONLY) == 0) + { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, - 0, &pix_dummy); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; + return res; #endif // SPIFFS_READ_ONLY -} + } #endif // SPIFFS_OBJ_META_LEN -spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { - SPIFFS_API_DBG("%s\n", __func__); - (void)name; - - if (!SPIFFS_CHECK_CFG((fs))) { - (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; - return 0; - } - - if (!SPIFFS_CHECK_MOUNT(fs)) { - fs->err_code = SPIFFS_ERR_NOT_MOUNTED; - return 0; - } - - d->fs = fs; - d->block = 0; - d->entry = 0; - return d; -} - -static s32_t spiffs_read_dir_v( - spiffs *fs, - spiffs_obj_id obj_id, - spiffs_block_ix bix, - int ix_entry, - const void *user_const_p, - void *user_var_p) { - (void)user_const_p; - s32_t res; - spiffs_page_object_ix_header objix_hdr; - if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || - (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { - return SPIFFS_VIS_COUNTINUE; - } - - spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); - if (res != SPIFFS_OK) return res; - if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && - objix_hdr.p_hdr.span_ix == 0 && - (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == - (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { - struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p; - e->obj_id = obj_id; - strcpy((char *)e->name, (char *)objix_hdr.name); - e->type = objix_hdr.type; - e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; - e->pix = pix; + spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) + { + SPIFFS_API_DBG("%s\n", __func__); + (void)name; + + if (!SPIFFS_CHECK_CFG((fs))) + { + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; + return 0; + } + + if (!SPIFFS_CHECK_MOUNT(fs)) + { + fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + + d->fs = fs; + d->block = 0; + d->entry = 0; + return d; + } + + static s32_t spiffs_read_dir_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) + { + (void)user_const_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) + { + return SPIFFS_VIS_COUNTINUE; + } + + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res != SPIFFS_OK) + { + return res; + } + if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) + { + struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p; + e->obj_id = obj_id; + strcpy((char *)e->name, (char *)objix_hdr.name); + e->type = objix_hdr.type; + e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + e->pix = pix; #if SPIFFS_OBJ_META_LEN - _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); + _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); #endif - return SPIFFS_OK; - } - return SPIFFS_VIS_COUNTINUE; -} - -struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { - SPIFFS_API_DBG("%s\n", __func__); - if (!SPIFFS_CHECK_MOUNT(d->fs)) { - d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; - return 0; - } - SPIFFS_LOCK(d->fs); - - spiffs_block_ix bix; - int entry; - s32_t res; - struct spiffs_dirent *ret = 0; - - res = spiffs_obj_lu_find_entry_visitor(d->fs, - d->block, - d->entry, - SPIFFS_VIS_NO_WRAP, - 0, - spiffs_read_dir_v, - 0, - e, - &bix, - &entry); - if (res == SPIFFS_OK) { - d->block = bix; - d->entry = entry + 1; - e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; - ret = e; - } else { - d->fs->err_code = res; - } - SPIFFS_UNLOCK(d->fs); - return ret; -} - -s32_t SPIFFS_closedir(spiffs_DIR *d) { - SPIFFS_API_DBG("%s\n", __func__); - SPIFFS_API_CHECK_CFG(d->fs); - SPIFFS_API_CHECK_MOUNT(d->fs); - return 0; -} - -s32_t SPIFFS_check(spiffs *fs) { - SPIFFS_API_DBG("%s\n", __func__); + return SPIFFS_OK; + } + return SPIFFS_VIS_COUNTINUE; + } + + struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) + { + SPIFFS_API_DBG("%s\n", __func__); + if (!SPIFFS_CHECK_MOUNT(d->fs)) + { + d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + SPIFFS_LOCK(d->fs); + + spiffs_block_ix bix; + int entry; + s32_t res; + struct spiffs_dirent *ret = 0; + + res = spiffs_obj_lu_find_entry_visitor(d->fs, + d->block, + d->entry, + SPIFFS_VIS_NO_WRAP, + 0, + spiffs_read_dir_v, + 0, + e, + &bix, + &entry); + if (res == SPIFFS_OK) + { + d->block = bix; + d->entry = entry + 1; + e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + ret = e; + } + else + { + d->fs->err_code = res; + } + SPIFFS_UNLOCK(d->fs); + return ret; + } + + s32_t SPIFFS_closedir(spiffs_DIR *d) + { + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_API_CHECK_CFG(d->fs); + SPIFFS_API_CHECK_MOUNT(d->fs); + return 0; + } + + s32_t SPIFFS_check(spiffs *fs) + { + SPIFFS_API_DBG("%s\n", __func__); #if SPIFFS_READ_ONLY - (void)fs; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; #else - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - res = spiffs_lookup_consistency_check(fs, 0); + res = spiffs_lookup_consistency_check(fs, 0); - res = spiffs_object_index_consistency_check(fs); + res = spiffs_object_index_consistency_check(fs); - res = spiffs_page_consistency_check(fs); + res = spiffs_page_consistency_check(fs); - res = spiffs_obj_lu_scan(fs); + res = spiffs_obj_lu_scan(fs); - SPIFFS_UNLOCK(fs); - return res; + SPIFFS_UNLOCK(fs); + return res; #endif // SPIFFS_READ_ONLY -} - -s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { - SPIFFS_API_DBG("%s\n", __func__); - s32_t res = SPIFFS_OK; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); - - u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs); - u32_t blocks = fs->block_count; - u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs); - u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs); - u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page - - if (total) { - *total = total_data_pages * data_page_size; - } - - if (used) { - *used = fs->stats_p_allocated * data_page_size; - } - - SPIFFS_UNLOCK(fs); - return res; -} - -s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { - SPIFFS_API_DBG("%s " _SPIPRIi "\n", __func__, max_free_pages); + } + + s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) + { + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs); + u32_t blocks = fs->block_count; + u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs); + u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs); + u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page + + if (total) + { + *total = total_data_pages * data_page_size; + } + + if (used) + { + *used = fs->stats_p_allocated * data_page_size; + } + + SPIFFS_UNLOCK(fs); + return res; + } + + s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) + { + SPIFFS_API_DBG("%s " _SPIPRIi "\n", __func__, max_free_pages); #if SPIFFS_READ_ONLY - (void)fs; (void)max_free_pages; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; (void)max_free_pages; + return SPIFFS_ERR_RO_NOT_IMPL; #else - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - res = spiffs_gc_quick(fs, max_free_pages); + res = spiffs_gc_quick(fs, max_free_pages); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); - return 0; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; #endif // SPIFFS_READ_ONLY -} + } -s32_t SPIFFS_gc(spiffs *fs, u32_t size) { - SPIFFS_API_DBG("%s " _SPIPRIi "\n", __func__, size); + s32_t SPIFFS_gc(spiffs *fs, u32_t size) + { + SPIFFS_API_DBG("%s " _SPIPRIi "\n", __func__, size); #if SPIFFS_READ_ONLY - (void)fs; (void)size; - return SPIFFS_ERR_RO_NOT_IMPL; + (void)fs; (void)size; + return SPIFFS_ERR_RO_NOT_IMPL; #else - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - res = spiffs_gc_check(fs, size); + res = spiffs_gc_check(fs, size); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_UNLOCK(fs); - return 0; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; #endif // SPIFFS_READ_ONLY -} + } -s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) + { + SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); + fh = SPIFFS_FH_UNOFFS(fs, fh); - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if SPIFFS_CACHE_WR - res = spiffs_fflush_cache(fs, fh); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #endif - res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size)); + res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size)); - SPIFFS_UNLOCK(fs); - return res; -} + SPIFFS_UNLOCK(fs); + return res; + } -s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); + s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) + { + SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); + fh = SPIFFS_FH_UNOFFS(fs, fh); - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if SPIFFS_CACHE_WR - res = spiffs_fflush_cache(fs, fh); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #endif - res = fd->fdoffset; + res = fd->fdoffset; - SPIFFS_UNLOCK(fs); - return res; -} + SPIFFS_UNLOCK(fs); + return res; + } -s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) { - SPIFFS_API_DBG("%s\n", __func__); - SPIFFS_LOCK(fs); - fs->file_cb_f = cb_func; - SPIFFS_UNLOCK(fs); - return 0; -} + s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) + { + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_LOCK(fs); + fs->file_cb_f = cb_func; + SPIFFS_UNLOCK(fs); + return 0; + } #if SPIFFS_IX_MAP -s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, - u32_t offset, u32_t len, spiffs_page_ix *map_buf) { - SPIFFS_API_DBG("%s " _SPIPRIfd " " _SPIPRIi " " _SPIPRIi "\n", __func__, fh, offset, len); - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); - - fh = SPIFFS_FH_UNOFFS(fs, fh); - - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - if (fd->ix_map) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED); - } - - map->map_buf = map_buf; - map->offset = offset; - // nb: spix range includes last - map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); - map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs); - memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1)); - fd->ix_map = map; - - // scan for pixes - res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - SPIFFS_UNLOCK(fs); - return res; -} - -s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) { - SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); - s32_t res; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); - - fh = SPIFFS_FH_UNOFFS(fs, fh); - - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - if (fd->ix_map == 0) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); - } - - fd->ix_map = 0; - - SPIFFS_UNLOCK(fs); - return res; -} - -s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) { - SPIFFS_API_DBG("%s " _SPIPRIfd " " _SPIPRIi "\n", __func__, fh, offset); - s32_t res = SPIFFS_OK; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); - - fh = SPIFFS_FH_UNOFFS(fs, fh); - - spiffs_fd *fd; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - - if (fd->ix_map == 0) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); - } - - spiffs_ix_map *map = fd->ix_map; - - s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix; - map->offset = offset; - - // move existing pixes if within map offs - if (spix_diff != 0) { - // move vector - int i; - const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last - map->start_spix += spix_diff; - map->end_spix += spix_diff; - if (spix_diff >= vec_len) { - // moving beyond range - memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix)); - // populate_ix_map is inclusive - res = spiffs_populate_ix_map(fs, fd, 0, vec_len-1); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } else if (spix_diff > 0) { - // diff positive - for (i = 0; i < vec_len - spix_diff; i++) { - map->map_buf[i] = map->map_buf[i + spix_diff]; - } - // memset is non-inclusive - memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix)); - // populate_ix_map is inclusive - res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len-1); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - } else { - // diff negative - for (i = vec_len - 1; i >= -spix_diff; i--) { - map->map_buf[i] = map->map_buf[i + spix_diff]; - } - // memset is non-inclusive - memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix)); - // populate_ix_map is inclusive - res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, + u32_t offset, u32_t len, spiffs_page_ix *map_buf) + { + SPIFFS_API_DBG("%s " _SPIPRIfd " " _SPIPRIi " " _SPIPRIi "\n", __func__, fh, offset, len); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map) + { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED); + } + + map->map_buf = map_buf; + map->offset = offset; + // nb: spix range includes last + map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs); + memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1)); + fd->ix_map = map; + + // scan for pixes + res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return res; + } + + s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) + { + SPIFFS_API_DBG("%s " _SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) + { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + fd->ix_map = 0; + + SPIFFS_UNLOCK(fs); + return res; } - } + s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) + { + SPIFFS_API_DBG("%s " _SPIPRIfd " " _SPIPRIi "\n", __func__, fh, offset); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) + { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + spiffs_ix_map *map = fd->ix_map; + + s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix; + map->offset = offset; + + // move existing pixes if within map offs + if (spix_diff != 0) + { + // move vector + int i; + const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last + map->start_spix += spix_diff; + map->end_spix += spix_diff; + if (spix_diff >= vec_len) + { + // moving beyond range + memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, vec_len - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + else if (spix_diff > 0) + { + // diff positive + for (i = 0; i < vec_len - spix_diff; i++) + { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + else + { + // diff negative + for (i = vec_len - 1; i >= -spix_diff; i--) + { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + } - SPIFFS_UNLOCK(fs); - return res; -} + SPIFFS_UNLOCK(fs); + return res; + } -s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) { - SPIFFS_API_CHECK_CFG(fs); - // always add one extra page, the offset might change to the middle of a page - return (bytes + SPIFFS_DATA_PAGE_SIZE(fs) ) / SPIFFS_DATA_PAGE_SIZE(fs); -} + s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) + { + SPIFFS_API_CHECK_CFG(fs); + // always add one extra page, the offset might change to the middle of a page + return (bytes + SPIFFS_DATA_PAGE_SIZE(fs)) / SPIFFS_DATA_PAGE_SIZE(fs); + } -s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) { - SPIFFS_API_CHECK_CFG(fs); - return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs); -} + s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) + { + SPIFFS_API_CHECK_CFG(fs); + return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs); + } #endif // SPIFFS_IX_MAP #if SPIFFS_TEST_VISUALISATION -s32_t SPIFFS_vis(spiffs *fs) { - s32_t res = SPIFFS_OK; - SPIFFS_API_CHECK_CFG(fs); - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); - - int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); - spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; - spiffs_block_ix bix = 0; - - while (bix < fs->block_count) { - // check each object lookup page - int obj_lookup_page = 0; - int cur_entry = 0; - - while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { - int entry_offset = obj_lookup_page * entries_per_page; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - // check each entry - while (res == SPIFFS_OK && - cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { - spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; - if (cur_entry == 0) { - spiffs_printf(_SPIPRIbl" ", bix); - } else if ((cur_entry & 0x3f) == 0) { - spiffs_printf(" "); - } - if (obj_id == SPIFFS_OBJ_ID_FREE) { - spiffs_printf(SPIFFS_TEST_VIS_FREE_STR); - } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { - spiffs_printf(SPIFFS_TEST_VIS_DELE_STR); - } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG){ - spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id)); - } else { - spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id)); - } - cur_entry++; - if ((cur_entry & 0x3f) == 0) { - spiffs_printf("\n"); - } - } // per entry - obj_lookup_page++; - } // per object lookup page - - spiffs_obj_id erase_count; - res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, - SPIFFS_ERASE_COUNT_PADDR(fs, bix), - sizeof(spiffs_obj_id), (u8_t *)&erase_count); - SPIFFS_CHECK_RES(res); - - if (erase_count != (spiffs_obj_id)-1) { - spiffs_printf("\tera_cnt: " _SPIPRIi "\n", erase_count); - } else { - spiffs_printf("\tera_cnt: N/A\n"); + s32_t SPIFFS_vis(spiffs *fs) + { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_block_ix bix = 0; + + while (bix < fs->block_count) + { + // check each object lookup page + int obj_lookup_page = 0; + int cur_entry = 0; + + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) + { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) + { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset]; + if (cur_entry == 0) + { + spiffs_printf(_SPIPRIbl" ", bix); + } + else if ((cur_entry & 0x3f) == 0) + { + spiffs_printf(" "); + } + if (obj_id == SPIFFS_OBJ_ID_FREE) + { + spiffs_printf(SPIFFS_TEST_VIS_FREE_STR); + } + else if (obj_id == SPIFFS_OBJ_ID_DELETED) + { + spiffs_printf(SPIFFS_TEST_VIS_DELE_STR); + } + else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG) + { + spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id)); + } + else + { + spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id)); + } + cur_entry++; + if ((cur_entry & 0x3f) == 0) + { + spiffs_printf("\n"); + } + } // per entry + obj_lookup_page++; + } // per object lookup page + + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + if (erase_count != (spiffs_obj_id) - 1) + { + spiffs_printf("\tera_cnt: " _SPIPRIi "\n", erase_count); + } + else + { + spiffs_printf("\tera_cnt: N/A\n"); + } + + bix++; + } // per block + + spiffs_printf("era_cnt_max: " _SPIPRIi "\n", fs->max_erase_count); + spiffs_printf("last_errno: " _SPIPRIi "\n", fs->err_code); + spiffs_printf("blocks: " _SPIPRIi "\n", fs->block_count); + spiffs_printf("free_blocks: " _SPIPRIi "\n", fs->free_blocks); + spiffs_printf("page_alloc: " _SPIPRIi "\n", fs->stats_p_allocated); + spiffs_printf("page_delet: " _SPIPRIi "\n", fs->stats_p_deleted); + SPIFFS_UNLOCK(fs); + u32_t total, used; + SPIFFS_info(fs, &total, &used); + spiffs_printf("used: " _SPIPRIi " of " _SPIPRIi "\n", used, total); + return res; } - - bix++; - } // per block - - spiffs_printf("era_cnt_max: " _SPIPRIi "\n", fs->max_erase_count); - spiffs_printf("last_errno: " _SPIPRIi "\n", fs->err_code); - spiffs_printf("blocks: " _SPIPRIi "\n", fs->block_count); - spiffs_printf("free_blocks: " _SPIPRIi "\n", fs->free_blocks); - spiffs_printf("page_alloc: " _SPIPRIi "\n", fs->stats_p_allocated); - spiffs_printf("page_delet: " _SPIPRIi "\n", fs->stats_p_deleted); - SPIFFS_UNLOCK(fs); - u32_t total, used; - SPIFFS_info(fs, &total, &used); - spiffs_printf("used: " _SPIPRIi " of " _SPIPRIi "\n", used, total); - return res; -} #endif }; diff --git a/cores/esp8266/spiffs/spiffs_nucleus.cpp b/cores/esp8266/spiffs/spiffs_nucleus.cpp index 35238f351b..3789f320d6 100644 --- a/cores/esp8266/spiffs/spiffs_nucleus.cpp +++ b/cores/esp8266/spiffs/spiffs_nucleus.cpp @@ -3,2361 +3,2766 @@ extern "C" { -static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { - s32_t res = SPIFFS_OK; - if (pix == (spiffs_page_ix)-1) { - // referring to page 0xffff...., bad object index - return SPIFFS_ERR_INDEX_REF_FREE; - } - if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { - // referring to an object lookup page, bad object index - return SPIFFS_ERR_INDEX_REF_LU; - } - if (pix > SPIFFS_MAX_PAGES(fs)) { - // referring to a bad page - return SPIFFS_ERR_INDEX_REF_INVALID; - } + static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) + { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix) - 1) + { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_REF_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) + { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_REF_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) + { + // referring to a bad page + return SPIFFS_ERR_INDEX_REF_INVALID; + } #if SPIFFS_PAGE_CHECK - spiffs_page_header ph; - res = _spiffs_rd( - fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, pix), - sizeof(spiffs_page_header), - (u8_t *)&ph); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix); + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix); #endif - return res; -} + return res; + } #if !SPIFFS_READ_ONLY -static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { - s32_t res = SPIFFS_OK; - if (pix == (spiffs_page_ix)-1) { - // referring to page 0xffff...., bad object index - return SPIFFS_ERR_INDEX_FREE; - } - if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { - // referring to an object lookup page, bad object index - return SPIFFS_ERR_INDEX_LU; - } - if (pix > SPIFFS_MAX_PAGES(fs)) { - // referring to a bad page - return SPIFFS_ERR_INDEX_INVALID; - } + static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) + { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix) - 1) + { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) + { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) + { + // referring to a bad page + return SPIFFS_ERR_INDEX_INVALID; + } #if SPIFFS_PAGE_CHECK - spiffs_page_header ph; - res = _spiffs_rd( - fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, pix), - sizeof(spiffs_page_header), - (u8_t *)&ph); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix); + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix); #endif - return res; -} + return res; + } #endif // !SPIFFS_READ_ONLY #if !SPIFFS_CACHE -s32_t spiffs_phys_rd( - spiffs *fs, - u32_t addr, - u32_t len, - u8_t *dst) { - return SPIFFS_HAL_READ(fs, addr, len, dst); -} - -s32_t spiffs_phys_wr( - spiffs *fs, - u32_t addr, - u32_t len, - u8_t *src) { - return SPIFFS_HAL_WRITE(fs, addr, len, src); -} + s32_t spiffs_phys_rd( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *dst) + { + return SPIFFS_HAL_READ(fs, addr, len, dst); + } + + s32_t spiffs_phys_wr( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *src) + { + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } #endif #if !SPIFFS_READ_ONLY -s32_t spiffs_phys_cpy( - spiffs *fs, - spiffs_file fh, - u32_t dst, - u32_t src, - u32_t len) { - (void)fh; - s32_t res; - u8_t b[SPIFFS_COPY_BUFFER_STACK]; - while (len > 0) { - u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b); - SPIFFS_CHECK_RES(res); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b); - SPIFFS_CHECK_RES(res); - len -= chunk_size; - src += chunk_size; - dst += chunk_size; - } - return SPIFFS_OK; -} + s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len) + { + (void)fh; + s32_t res; + u8_t b[SPIFFS_COPY_BUFFER_STACK]; + while (len > 0) + { + u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b); + SPIFFS_CHECK_RES(res); + len -= chunk_size; + src += chunk_size; + dst += chunk_size; + } + return SPIFFS_OK; + } #endif // !SPIFFS_READ_ONLY -// Find object lookup entry containing given id with visitor. -// Iterate over object lookup pages in each block until a given object id entry is found. -// When found, the visitor function is called with block index, entry index and user data. -// If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be -// ended and visitor's return code is returned to caller. -// If no visitor is given (0) the search returns on first entry with matching object id. -// If no match is found in all look up, SPIFFS_VIS_END is returned. -// @param fs the file system -// @param starting_block the starting block to start search in -// @param starting_lu_entry the look up index entry to start search in -// @param flags ored combination of SPIFFS_VIS_CHECK_ID, SPIFFS_VIS_CHECK_PH, -// SPIFFS_VIS_NO_WRAP -// @param obj_id argument object id -// @param v visitor callback function -// @param user_const_p any const pointer, passed to the callback visitor function -// @param user_var_p any pointer, passed to the callback visitor function -// @param block_ix reported block index where match was found -// @param lu_entry reported look up index where match was found -s32_t spiffs_obj_lu_find_entry_visitor( - spiffs *fs, - spiffs_block_ix starting_block, - int starting_lu_entry, - u8_t flags, - spiffs_obj_id obj_id, - spiffs_visitor_f v, - const void *user_const_p, - void *user_var_p, - spiffs_block_ix *block_ix, - int *lu_entry) { - s32_t res = SPIFFS_OK; - s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs); - spiffs_block_ix cur_block = starting_block; - u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); - - spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; - int cur_entry = starting_lu_entry; - int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); - - // wrap initial - if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { - cur_entry = 0; - cur_block++; - cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); - if (cur_block >= fs->block_count) { - if (flags & SPIFFS_VIS_NO_WRAP) { - return SPIFFS_VIS_END; - } else { - // block wrap - cur_block = 0; - cur_block_addr = 0; - } - } - } - - // check each block - while (res == SPIFFS_OK && entry_count > 0) { - int obj_lookup_page = cur_entry / entries_per_page; - // check each object lookup page - while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { - int entry_offset = obj_lookup_page * entries_per_page; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - // check each entry - while (res == SPIFFS_OK && - cur_entry - entry_offset < entries_per_page && // for non-last obj lookup pages - cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page - { - if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry-entry_offset] == obj_id) { - if (block_ix) *block_ix = cur_block; - if (lu_entry) *lu_entry = cur_entry; - if (v) { - res = v( - fs, - (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry-entry_offset], - cur_block, - cur_entry, - user_const_p, - user_var_p); - if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) { - if (res == SPIFFS_VIS_COUNTINUE_RELOAD) { + // Find object lookup entry containing given id with visitor. + // Iterate over object lookup pages in each block until a given object id entry is found. + // When found, the visitor function is called with block index, entry index and user data. + // If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be + // ended and visitor's return code is returned to caller. + // If no visitor is given (0) the search returns on first entry with matching object id. + // If no match is found in all look up, SPIFFS_VIS_END is returned. + // @param fs the file system + // @param starting_block the starting block to start search in + // @param starting_lu_entry the look up index entry to start search in + // @param flags ored combination of SPIFFS_VIS_CHECK_ID, SPIFFS_VIS_CHECK_PH, + // SPIFFS_VIS_NO_WRAP + // @param obj_id argument object id + // @param v visitor callback function + // @param user_const_p any const pointer, passed to the callback visitor function + // @param user_var_p any pointer, passed to the callback visitor function + // @param block_ix reported block index where match was found + // @param lu_entry reported look up index where match was found + s32_t spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + const void *user_const_p, + void *user_var_p, + spiffs_block_ix *block_ix, + int *lu_entry) + { + s32_t res = SPIFFS_OK; + s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs); + spiffs_block_ix cur_block = starting_block; + u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = starting_lu_entry; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // wrap initial + if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) + { + cur_entry = 0; + cur_block++; + cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) + { + if (flags & SPIFFS_VIS_NO_WRAP) + { + return SPIFFS_VIS_END; + } + else + { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } + + // check each block + while (res == SPIFFS_OK && entry_count > 0) + { + int obj_lookup_page = cur_entry / entries_per_page; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) + { + int entry_offset = obj_lookup_page * entries_per_page; res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - SPIFFS_CHECK_RES(res); - } - res = SPIFFS_OK; - cur_entry++; - entry_count--; - continue; - } else { - return res; + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && // for non-last obj lookup pages + cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page + { + if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry - entry_offset] == obj_id) + { + if (block_ix) + { + *block_ix = cur_block; + } + if (lu_entry) + { + *lu_entry = cur_entry; + } + if (v) + { + res = v( + fs, + (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry - entry_offset], + cur_block, + cur_entry, + user_const_p, + user_var_p); + if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) + { + if (res == SPIFFS_VIS_COUNTINUE_RELOAD) + { + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } + res = SPIFFS_OK; + cur_entry++; + entry_count--; + continue; + } + else + { + return res; + } + } + else + { + return SPIFFS_OK; + } + } + entry_count--; + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) + { + if (flags & SPIFFS_VIS_NO_WRAP) + { + return SPIFFS_VIS_END; + } + else + { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } } - } else { - return SPIFFS_OK; - } - } - entry_count--; - cur_entry++; - } // per entry - obj_lookup_page++; - } // per object lookup page - cur_entry = 0; - cur_block++; - cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); - if (cur_block >= fs->block_count) { - if (flags & SPIFFS_VIS_NO_WRAP) { - return SPIFFS_VIS_END; - } else { - // block wrap - cur_block = 0; - cur_block_addr = 0; - } - } - } // per block + } // per block - SPIFFS_CHECK_RES(res); + SPIFFS_CHECK_RES(res); - return SPIFFS_VIS_END; -} + return SPIFFS_VIS_END; + } #if !SPIFFS_READ_ONLY -s32_t spiffs_erase_block( - spiffs *fs, - spiffs_block_ix bix) { - s32_t res; - u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); - s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); - - // here we ignore res, just try erasing the block - while (size > 0) { - SPIFFS_DBG("erase " _SPIPRIad ":" _SPIPRIi "\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); - SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); - - addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); - size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); - } - fs->free_blocks++; - - // register erase count for this block - res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, - SPIFFS_ERASE_COUNT_PADDR(fs, bix), - sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); - SPIFFS_CHECK_RES(res); + s32_t spiffs_erase_block( + spiffs *fs, + spiffs_block_ix bix) + { + s32_t res; + u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); + s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + // here we ignore res, just try erasing the block + while (size > 0) + { + SPIFFS_DBG("erase " _SPIPRIad ":" _SPIPRIi "\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + + addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); + size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); + } + fs->free_blocks++; + + // register erase count for this block + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); + SPIFFS_CHECK_RES(res); #if SPIFFS_USE_MAGIC - // finally, write magic - spiffs_obj_id magic = SPIFFS_MAGIC(fs, bix); - res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, - SPIFFS_MAGIC_PADDR(fs, bix), - sizeof(spiffs_obj_id), (u8_t *)&magic); - SPIFFS_CHECK_RES(res); + // finally, write magic + spiffs_obj_id magic = SPIFFS_MAGIC(fs, bix); + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_MAGIC_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&magic); + SPIFFS_CHECK_RES(res); #endif - fs->max_erase_count++; - if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) { - fs->max_erase_count = 0; - } + fs->max_erase_count++; + if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) + { + fs->max_erase_count = 0; + } - return res; -} + return res; + } #endif // !SPIFFS_READ_ONLY #if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 -s32_t spiffs_probe( - spiffs_config *cfg) { - s32_t res; - u32_t paddr; - spiffs dummy_fs; // create a dummy fs struct just to be able to use macros - _SPIFFS_MEMCPY(&dummy_fs.cfg, cfg, sizeof(spiffs_config)); - dummy_fs.block_count = 0; - - // Read three magics, as one block may be in an aborted erase state. - // At least two of these must contain magic and be in decreasing order. - spiffs_obj_id magic[3]; - spiffs_obj_id bix_count[3]; - - spiffs_block_ix bix; - for (bix = 0; bix < 3; bix++) { - paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); + s32_t spiffs_probe( + spiffs_config *cfg) + { + s32_t res; + u32_t paddr; + spiffs dummy_fs; // create a dummy fs struct just to be able to use macros + _SPIFFS_MEMCPY(&dummy_fs.cfg, cfg, sizeof(spiffs_config)); + dummy_fs.block_count = 0; + + // Read three magics, as one block may be in an aborted erase state. + // At least two of these must contain magic and be in decreasing order. + spiffs_obj_id magic[3]; + spiffs_obj_id bix_count[3]; + + spiffs_block_ix bix; + for (bix = 0; bix < 3; bix++) + { + paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); #if SPIFFS_HAL_CALLBACK_EXTRA - // not any proper fs to report here, so callback with null - // (cross fingers that no-one gets angry) - res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); + // not any proper fs to report here, so callback with null + // (cross fingers that no-one gets angry) + res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); #else - res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); + res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); #endif - bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0); - SPIFFS_CHECK_RES(res); - } - - // check that we have sane number of blocks - if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; - // check that the order is correct, take aborted erases in calculation - // first block aborted erase - if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { - return (bix_count[1]+1) * cfg->log_block_size; - } - // second block aborted erase - if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { - return bix_count[0] * cfg->log_block_size; - } - // third block aborted erase - if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { - return bix_count[0] * cfg->log_block_size; - } - // no block has aborted erase - if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { - return bix_count[0] * cfg->log_block_size; - } - - return SPIFFS_ERR_PROBE_NOT_A_FS; -} + bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0); + SPIFFS_CHECK_RES(res); + } + + // check that we have sane number of blocks + if (bix_count[0] < 3) + { + return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; + } + // check that the order is correct, take aborted erases in calculation + // first block aborted erase + if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) + { + return (bix_count[1] + 1) * cfg->log_block_size; + } + // second block aborted erase + if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) + { + return bix_count[0] * cfg->log_block_size; + } + // third block aborted erase + if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) + { + return bix_count[0] * cfg->log_block_size; + } + // no block has aborted erase + if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) + { + return bix_count[0] * cfg->log_block_size; + } + + return SPIFFS_ERR_PROBE_NOT_A_FS; + } #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 -static s32_t spiffs_obj_lu_scan_v( - spiffs *fs, - spiffs_obj_id obj_id, - spiffs_block_ix bix, - int ix_entry, - const void *user_const_p, - void *user_var_p) { - (void)bix; - (void)user_const_p; - (void)user_var_p; - if (obj_id == SPIFFS_OBJ_ID_FREE) { - if (ix_entry == 0) { - fs->free_blocks++; - // todo optimize further, return SPIFFS_NEXT_BLOCK + static s32_t spiffs_obj_lu_scan_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) + { + (void)bix; + (void)user_const_p; + (void)user_var_p; + if (obj_id == SPIFFS_OBJ_ID_FREE) + { + if (ix_entry == 0) + { + fs->free_blocks++; + // todo optimize further, return SPIFFS_NEXT_BLOCK + } + } + else if (obj_id == SPIFFS_OBJ_ID_DELETED) + { + fs->stats_p_deleted++; + } + else + { + fs->stats_p_allocated++; + } + + return SPIFFS_VIS_COUNTINUE; } - } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { - fs->stats_p_deleted++; - } else { - fs->stats_p_allocated++; - } - - return SPIFFS_VIS_COUNTINUE; -} - - -// Scans thru all obj lu and counts free, deleted and used pages -// Find the maximum block erase count -// Checks magic if enabled -s32_t spiffs_obj_lu_scan( - spiffs *fs) { - s32_t res; - spiffs_block_ix bix; - int entry; + + + // Scans thru all obj lu and counts free, deleted and used pages + // Find the maximum block erase count + // Checks magic if enabled + s32_t spiffs_obj_lu_scan( + spiffs *fs) + { + s32_t res; + spiffs_block_ix bix; + int entry; #if SPIFFS_USE_MAGIC - spiffs_block_ix unerased_bix = (spiffs_block_ix)-1; + spiffs_block_ix unerased_bix = (spiffs_block_ix) - 1; #endif - // find out erase count - // if enabled, check magic - bix = 0; - spiffs_obj_id erase_count_final; - spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; - spiffs_obj_id erase_count_max = 0; - while (bix < fs->block_count) { + // find out erase count + // if enabled, check magic + bix = 0; + spiffs_obj_id erase_count_final; + spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; + spiffs_obj_id erase_count_max = 0; + while (bix < fs->block_count) + { #if SPIFFS_USE_MAGIC - spiffs_obj_id magic; - res = _spiffs_rd(fs, - SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_MAGIC_PADDR(fs, bix) , - sizeof(spiffs_obj_id), (u8_t *)&magic); - - SPIFFS_CHECK_RES(res); - if (magic != SPIFFS_MAGIC(fs, bix)) { - if (unerased_bix == (spiffs_block_ix)-1) { - // allow one unerased block as it might be powered down during an erase - unerased_bix = bix; - } else { - // more than one unerased block, bail out - SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS); - } - } + spiffs_obj_id magic; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_MAGIC_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&magic); + + SPIFFS_CHECK_RES(res); + if (magic != SPIFFS_MAGIC(fs, bix)) + { + if (unerased_bix == (spiffs_block_ix) - 1) + { + // allow one unerased block as it might be powered down during an erase + unerased_bix = bix; + } + else + { + // more than one unerased block, bail out + SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS); + } + } #endif - spiffs_obj_id erase_count; - res = _spiffs_rd(fs, - SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix) , - sizeof(spiffs_obj_id), (u8_t *)&erase_count); - SPIFFS_CHECK_RES(res); - if (erase_count != SPIFFS_OBJ_ID_FREE) { - erase_count_min = MIN(erase_count_min, erase_count); - erase_count_max = MAX(erase_count_max, erase_count); - } - bix++; - } + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + if (erase_count != SPIFFS_OBJ_ID_FREE) + { + erase_count_min = MIN(erase_count_min, erase_count); + erase_count_max = MAX(erase_count_max, erase_count); + } + bix++; + } - if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) { - // clean system, set counter to zero - erase_count_final = 0; - } else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE)/2) { - // wrap, take min - erase_count_final = erase_count_min+1; - } else { - erase_count_final = erase_count_max+1; - } + if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) + { + // clean system, set counter to zero + erase_count_final = 0; + } + else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE) / 2) + { + // wrap, take min + erase_count_final = erase_count_min + 1; + } + else + { + erase_count_final = erase_count_max + 1; + } - fs->max_erase_count = erase_count_final; + fs->max_erase_count = erase_count_final; #if SPIFFS_USE_MAGIC - if (unerased_bix != (spiffs_block_ix)-1) { - // found one unerased block, remedy - SPIFFS_DBG("mount: erase block " _SPIPRIbl "\n", bix); + if (unerased_bix != (spiffs_block_ix) - 1) + { + // found one unerased block, remedy + SPIFFS_DBG("mount: erase block " _SPIPRIbl "\n", bix); #if SPIFFS_READ_ONLY - res = SPIFFS_ERR_RO_ABORTED_OPERATION; + res = SPIFFS_ERR_RO_ABORTED_OPERATION; #else - res = spiffs_erase_block(fs, unerased_bix); + res = spiffs_erase_block(fs, unerased_bix); #endif // SPIFFS_READ_ONLY - SPIFFS_CHECK_RES(res); - } + SPIFFS_CHECK_RES(res); + } #endif - // count blocks - - fs->free_blocks = 0; - fs->stats_p_allocated = 0; - fs->stats_p_deleted = 0; + // count blocks + + fs->free_blocks = 0; + fs->stats_p_allocated = 0; + fs->stats_p_deleted = 0; + + res = spiffs_obj_lu_find_entry_visitor(fs, + 0, + 0, + 0, + 0, + spiffs_obj_lu_scan_v, + 0, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) + { + res = SPIFFS_OK; + } - res = spiffs_obj_lu_find_entry_visitor(fs, - 0, - 0, - 0, - 0, - spiffs_obj_lu_scan_v, - 0, - 0, - &bix, - &entry); + SPIFFS_CHECK_RES(res); - if (res == SPIFFS_VIS_END) { - res = SPIFFS_OK; - } + return res; + } - SPIFFS_CHECK_RES(res); +#if !SPIFFS_READ_ONLY + // Find free object lookup entry + // Iterate over object lookup pages in each block until a free object id entry is found + s32_t spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_block_ix *block_ix, + int *lu_entry) + { + s32_t res; + if (!fs->cleaning && fs->free_blocks < 2) + { + res = spiffs_gc_quick(fs, 0); + if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) + { + res = SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + if (fs->free_blocks < 2) + { + return SPIFFS_ERR_FULL; + } + } + res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry, + SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); + if (res == SPIFFS_OK) + { + fs->free_cursor_block_ix = *block_ix; + fs->free_cursor_obj_lu_entry = (*lu_entry) + 1; + if (*lu_entry == 0) + { + fs->free_blocks--; + } + } + if (res == SPIFFS_ERR_FULL) + { + SPIFFS_DBG("fs full\n"); + } - return res; -} + return res; + } +#endif // !SPIFFS_READ_ONLY -#if !SPIFFS_READ_ONLY -// Find free object lookup entry -// Iterate over object lookup pages in each block until a free object id entry is found -s32_t spiffs_obj_lu_find_free( - spiffs *fs, - spiffs_block_ix starting_block, - int starting_lu_entry, - spiffs_block_ix *block_ix, - int *lu_entry) { - s32_t res; - if (!fs->cleaning && fs->free_blocks < 2) { - res = spiffs_gc_quick(fs, 0); - if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) { - res = SPIFFS_OK; + // Find object lookup entry containing given id + // Iterate over object lookup pages in each block until a given object id entry is found + s32_t spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *lu_entry) + { + s32_t res = spiffs_obj_lu_find_entry_visitor( + fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry); + if (res == SPIFFS_VIS_END) + { + res = SPIFFS_ERR_NOT_FOUND; + } + return res; } - SPIFFS_CHECK_RES(res); - if (fs->free_blocks < 2) { - return SPIFFS_ERR_FULL; + + + static s32_t spiffs_obj_lu_find_id_and_span_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) + { + s32_t res; + spiffs_page_header ph; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + if (ph.obj_id == obj_id && + ph.span_ix == *((spiffs_span_ix*)user_var_p) && + (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET && + !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) && + (user_const_p == 0 || *((const spiffs_page_ix*)user_const_p) != pix)) + { + return SPIFFS_OK; + } + else + { + return SPIFFS_VIS_COUNTINUE; + } } - } - res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry, - SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); - if (res == SPIFFS_OK) { - fs->free_cursor_block_ix = *block_ix; - fs->free_cursor_obj_lu_entry = (*lu_entry) + 1; - if (*lu_entry == 0) { - fs->free_blocks--; + + // Find object lookup entry containing given id and span index + // Iterate over object lookup pages in each block until a given object id entry is found + s32_t spiffs_obj_lu_find_id_and_span( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix) + { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_ID, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) + { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) + { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; } - } - if (res == SPIFFS_ERR_FULL) { - SPIFFS_DBG("fs full\n"); - } - return res; -} -#endif // !SPIFFS_READ_ONLY + // Find object lookup entry containing given id and span index in page headers only + // Iterate over object lookup pages in each block until a given object id entry is found + s32_t spiffs_obj_lu_find_id_and_span_by_phdr( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix) + { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_PH, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) + { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) + { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } -// Find object lookup entry containing given id -// Iterate over object lookup pages in each block until a given object id entry is found -s32_t spiffs_obj_lu_find_id( - spiffs *fs, - spiffs_block_ix starting_block, - int starting_lu_entry, - spiffs_obj_id obj_id, - spiffs_block_ix *block_ix, - int *lu_entry) { - s32_t res = spiffs_obj_lu_find_entry_visitor( - fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry); - if (res == SPIFFS_VIS_END) { - res = SPIFFS_ERR_NOT_FOUND; - } - return res; -} - - -static s32_t spiffs_obj_lu_find_id_and_span_v( - spiffs *fs, - spiffs_obj_id obj_id, - spiffs_block_ix bix, - int ix_entry, - const void *user_const_p, - void *user_var_p) { - s32_t res; - spiffs_page_header ph; - spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); - res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); - SPIFFS_CHECK_RES(res); - if (ph.obj_id == obj_id && - ph.span_ix == *((spiffs_span_ix*)user_var_p) && - (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET && - !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) && - (user_const_p == 0 || *((const spiffs_page_ix*)user_const_p) != pix)) { - return SPIFFS_OK; - } else { - return SPIFFS_VIS_COUNTINUE; - } -} - -// Find object lookup entry containing given id and span index -// Iterate over object lookup pages in each block until a given object id entry is found -s32_t spiffs_obj_lu_find_id_and_span( - spiffs *fs, - spiffs_obj_id obj_id, - spiffs_span_ix spix, - spiffs_page_ix exclusion_pix, - spiffs_page_ix *pix) { - s32_t res; - spiffs_block_ix bix; - int entry; - - res = spiffs_obj_lu_find_entry_visitor(fs, - fs->cursor_block_ix, - fs->cursor_obj_lu_entry, - SPIFFS_VIS_CHECK_ID, - obj_id, - spiffs_obj_lu_find_id_and_span_v, - exclusion_pix ? &exclusion_pix : 0, - &spix, - &bix, - &entry); - - if (res == SPIFFS_VIS_END) { - res = SPIFFS_ERR_NOT_FOUND; - } - - SPIFFS_CHECK_RES(res); - - if (pix) { - *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } - - fs->cursor_block_ix = bix; - fs->cursor_obj_lu_entry = entry; - - return res; -} - -// Find object lookup entry containing given id and span index in page headers only -// Iterate over object lookup pages in each block until a given object id entry is found -s32_t spiffs_obj_lu_find_id_and_span_by_phdr( - spiffs *fs, - spiffs_obj_id obj_id, - spiffs_span_ix spix, - spiffs_page_ix exclusion_pix, - spiffs_page_ix *pix) { - s32_t res; - spiffs_block_ix bix; - int entry; - - res = spiffs_obj_lu_find_entry_visitor(fs, - fs->cursor_block_ix, - fs->cursor_obj_lu_entry, - SPIFFS_VIS_CHECK_PH, - obj_id, - spiffs_obj_lu_find_id_and_span_v, - exclusion_pix ? &exclusion_pix : 0, - &spix, - &bix, - &entry); - - if (res == SPIFFS_VIS_END) { - res = SPIFFS_ERR_NOT_FOUND; - } - - SPIFFS_CHECK_RES(res); - - if (pix) { - *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } - - fs->cursor_block_ix = bix; - fs->cursor_obj_lu_entry = entry; - - return res; -} + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; + } #if SPIFFS_IX_MAP -// update index map of given fd with given object index data -static void spiffs_update_ix_map(spiffs *fs, - spiffs_fd *fd, spiffs_span_ix objix_spix, spiffs_page_object_ix *objix) { + // update index map of given fd with given object index data + static void spiffs_update_ix_map(spiffs *fs, + spiffs_fd *fd, spiffs_span_ix objix_spix, spiffs_page_object_ix *objix) + { #if SPIFFS_SINGLETON - (void)fs; + (void)fs; #endif - spiffs_ix_map *map = fd->ix_map; - spiffs_span_ix map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix); - spiffs_span_ix map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->end_spix); - - // check if updated ix is within map range - if (objix_spix < map_objix_start_spix || objix_spix > map_objix_end_spix) { - return; - } - - // update memory mapped page index buffer to new pages - - // get range of updated object index map data span indices - spiffs_span_ix objix_data_spix_start = - SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, objix_spix); - spiffs_span_ix objix_data_spix_end = objix_data_spix_start + - (objix_spix == 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs) : SPIFFS_OBJ_IX_LEN(fs)); - - // calc union of object index range and index map range array - spiffs_span_ix map_spix = MAX(map->start_spix, objix_data_spix_start); - spiffs_span_ix map_spix_end = MIN(map->end_spix + 1, objix_data_spix_end); - - while (map_spix < map_spix_end) { - spiffs_page_ix objix_data_pix; - if (objix_spix == 0) { - // get data page from object index header page - objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix]; - } else { - // get data page from object index page - objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)]; + spiffs_ix_map *map = fd->ix_map; + spiffs_span_ix map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix); + spiffs_span_ix map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->end_spix); + + // check if updated ix is within map range + if (objix_spix < map_objix_start_spix || objix_spix > map_objix_end_spix) + { + return; + } + + // update memory mapped page index buffer to new pages + + // get range of updated object index map data span indices + spiffs_span_ix objix_data_spix_start = + SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, objix_spix); + spiffs_span_ix objix_data_spix_end = objix_data_spix_start + + (objix_spix == 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs) : SPIFFS_OBJ_IX_LEN(fs)); + + // calc union of object index range and index map range array + spiffs_span_ix map_spix = MAX(map->start_spix, objix_data_spix_start); + spiffs_span_ix map_spix_end = MIN(map->end_spix + 1, objix_data_spix_end); + + while (map_spix < map_spix_end) + { + spiffs_page_ix objix_data_pix; + if (objix_spix == 0) + { + // get data page from object index header page + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix]; + } + else + { + // get data page from object index page + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)]; + } + + if (objix_data_pix == (spiffs_page_ix) - 1) + { + // reached end of object, abort + break; + } + + map->map_buf[map_spix - map->start_spix] = objix_data_pix; + SPIFFS_DBG("map " _SPIPRIid ":" _SPIPRIsp " (" _SPIPRIsp "--" _SPIPRIsp ") objix.spix:" _SPIPRIsp " to pix " _SPIPRIpg "\n", + fd->obj_id, map_spix - map->start_spix, + map->start_spix, map->end_spix, + objix->p_hdr.span_ix, + objix_data_pix); + + map_spix++; + } } - if (objix_data_pix == (spiffs_page_ix)-1) { - // reached end of object, abort - break; + typedef struct + { + spiffs_fd *fd; + u32_t remaining_objix_pages_to_visit; + spiffs_span_ix map_objix_start_spix; + spiffs_span_ix map_objix_end_spix; + } spiffs_ix_map_populate_state; + + static s32_t spiffs_populate_ix_map_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) + { + (void)user_const_p; + s32_t res; + spiffs_ix_map_populate_state *state = (spiffs_ix_map_populate_state *)user_var_p; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + + // load header to check it + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); + + // check if hdr is ok, and if objix range overlap with ix map range + if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE) && + objix->p_hdr.span_ix >= state->map_objix_start_spix && + objix->p_hdr.span_ix <= state->map_objix_end_spix) + { + // ok, load rest of object index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_object_ix), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix), + (u8_t *)objix + sizeof(spiffs_page_object_ix)); + SPIFFS_CHECK_RES(res); + + spiffs_update_ix_map(fs, state->fd, objix->p_hdr.span_ix, objix); + + state->remaining_objix_pages_to_visit--; + SPIFFS_DBG("map " _SPIPRIid " (" _SPIPRIsp "--" _SPIPRIsp ") remaining objix pages " _SPIPRIi "\n", + state->fd->obj_id, + state->fd->ix_map->start_spix, state->fd->ix_map->end_spix, + state->remaining_objix_pages_to_visit); + } + + if (res == SPIFFS_OK) + { + res = state->remaining_objix_pages_to_visit ? SPIFFS_VIS_COUNTINUE : SPIFFS_VIS_END; + } + return res; } - map->map_buf[map_spix - map->start_spix] = objix_data_pix; - SPIFFS_DBG("map " _SPIPRIid ":" _SPIPRIsp " (" _SPIPRIsp "--" _SPIPRIsp ") objix.spix:" _SPIPRIsp " to pix " _SPIPRIpg "\n", - fd->obj_id, map_spix - map->start_spix, - map->start_spix, map->end_spix, - objix->p_hdr.span_ix, - objix_data_pix); - - map_spix++; - } -} - -typedef struct { - spiffs_fd *fd; - u32_t remaining_objix_pages_to_visit; - spiffs_span_ix map_objix_start_spix; - spiffs_span_ix map_objix_end_spix; -} spiffs_ix_map_populate_state; - -static s32_t spiffs_populate_ix_map_v( - spiffs *fs, - spiffs_obj_id obj_id, - spiffs_block_ix bix, - int ix_entry, - const void *user_const_p, - void *user_var_p) { - (void)user_const_p; - s32_t res; - spiffs_ix_map_populate_state *state = (spiffs_ix_map_populate_state *)user_var_p; - spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); - - // load header to check it - spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); - - // check if hdr is ok, and if objix range overlap with ix map range - if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == - (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE) && - objix->p_hdr.span_ix >= state->map_objix_start_spix && - objix->p_hdr.span_ix <= state->map_objix_end_spix) { - // ok, load rest of object index - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_object_ix), - SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix), - (u8_t *)objix + sizeof(spiffs_page_object_ix)); - SPIFFS_CHECK_RES(res); - - spiffs_update_ix_map(fs, state->fd, objix->p_hdr.span_ix, objix); - - state->remaining_objix_pages_to_visit--; - SPIFFS_DBG("map " _SPIPRIid " (" _SPIPRIsp "--" _SPIPRIsp ") remaining objix pages " _SPIPRIi "\n", - state->fd->obj_id, - state->fd->ix_map->start_spix, state->fd->ix_map->end_spix, - state->remaining_objix_pages_to_visit); - } - - if (res == SPIFFS_OK) { - res = state->remaining_objix_pages_to_visit ? SPIFFS_VIS_COUNTINUE : SPIFFS_VIS_END; - } - return res; -} - -// populates index map, from vector entry start to vector entry end, inclusive -s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u32_t vec_entry_end) { - s32_t res; - spiffs_ix_map *map = fd->ix_map; - spiffs_ix_map_populate_state state; - vec_entry_start = MIN((u32_t)(map->end_spix - map->start_spix), vec_entry_start); - vec_entry_end = MAX((u32_t)(map->end_spix - map->start_spix), vec_entry_end); - if (vec_entry_start > vec_entry_end) { - return SPIFFS_ERR_IX_MAP_BAD_RANGE; - } - state.map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_start); - state.map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_end); - state.remaining_objix_pages_to_visit = - state.map_objix_end_spix - state.map_objix_start_spix + 1; - state.fd = fd; - - res = spiffs_obj_lu_find_entry_visitor( - fs, - SPIFFS_BLOCK_FOR_PAGE(fs, fd->objix_hdr_pix), - SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, fd->objix_hdr_pix), - SPIFFS_VIS_CHECK_ID, - fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, - spiffs_populate_ix_map_v, - 0, - &state, - 0, - 0); - - if (res == SPIFFS_VIS_END) { - res = SPIFFS_OK; - } - - return res; -} + // populates index map, from vector entry start to vector entry end, inclusive + s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u32_t vec_entry_end) + { + s32_t res; + spiffs_ix_map *map = fd->ix_map; + spiffs_ix_map_populate_state state; + vec_entry_start = MIN((u32_t)(map->end_spix - map->start_spix), vec_entry_start); + vec_entry_end = MAX((u32_t)(map->end_spix - map->start_spix), vec_entry_end); + if (vec_entry_start > vec_entry_end) + { + return SPIFFS_ERR_IX_MAP_BAD_RANGE; + } + state.map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_start); + state.map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_end); + state.remaining_objix_pages_to_visit = + state.map_objix_end_spix - state.map_objix_start_spix + 1; + state.fd = fd; + + res = spiffs_obj_lu_find_entry_visitor( + fs, + SPIFFS_BLOCK_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_VIS_CHECK_ID, + fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + spiffs_populate_ix_map_v, + 0, + &state, + 0, + 0); + + if (res == SPIFFS_VIS_END) + { + res = SPIFFS_OK; + } + + return res; + } #endif #if !SPIFFS_READ_ONLY -// Allocates a free defined page with given obj_id -// Occupies object lookup entry and page -// data may be NULL; where only page header is stored, len and page_offs is ignored -s32_t spiffs_page_allocate_data( - spiffs *fs, - spiffs_obj_id obj_id, - spiffs_page_header *ph, - u8_t *data, - u32_t len, - u32_t page_offs, - u8_t finalize, - spiffs_page_ix *pix) { - s32_t res = SPIFFS_OK; - spiffs_block_ix bix; - int entry; - - // find free entry - res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); - SPIFFS_CHECK_RES(res); - - // occupy page in object lookup - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, - 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); - SPIFFS_CHECK_RES(res); - - fs->stats_p_allocated++; - - // write page header - ph->flags &= ~SPIFFS_PH_FLAG_USED; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t*)ph); - SPIFFS_CHECK_RES(res); - - // write page data - if (data) { - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0,SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + sizeof(spiffs_page_header) + page_offs, len, data); - SPIFFS_CHECK_RES(res); - } - - // finalize header if necessary - if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) { - ph->flags &= ~SPIFFS_PH_FLAG_FINAL; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&ph->flags); - SPIFFS_CHECK_RES(res); - } - - // return written page - if (pix) { - *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } - - return res; -} + // Allocates a free defined page with given obj_id + // Occupies object lookup entry and page + // data may be NULL; where only page header is stored, len and page_offs is ignored + s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix) + { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + int entry; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write page header + ph->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t*)ph); + SPIFFS_CHECK_RES(res); + + // write page data + if (data) + { + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + sizeof(spiffs_page_header) + page_offs, len, data); + SPIFFS_CHECK_RES(res); + } + + // finalize header if necessary + if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) + { + ph->flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&ph->flags); + SPIFFS_CHECK_RES(res); + } + + // return written page + if (pix) + { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; + } #endif // !SPIFFS_READ_ONLY #if !SPIFFS_READ_ONLY -// Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page. -// If page data is null, provided header is used for metainfo and page data is physically copied. -s32_t spiffs_page_move( - spiffs *fs, - spiffs_file fh, - u8_t *page_data, - spiffs_obj_id obj_id, - spiffs_page_header *page_hdr, - spiffs_page_ix src_pix, - spiffs_page_ix *dst_pix) { - s32_t res; - u8_t was_final = 0; - spiffs_page_header *p_hdr; - spiffs_block_ix bix; - int entry; - spiffs_page_ix free_pix; - - // find free entry - res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); - SPIFFS_CHECK_RES(res); - free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - - if (dst_pix) *dst_pix = free_pix; - - p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr; - if (page_data) { - // got page data - was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0; - // write unfinalized page - p_hdr->flags |= SPIFFS_PH_FLAG_FINAL; - p_hdr->flags &= ~SPIFFS_PH_FLAG_USED; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), page_data); - } else { - // copy page data - res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs)); - } - SPIFFS_CHECK_RES(res); - - // mark entry in destination object lookup - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, - 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), - sizeof(spiffs_obj_id), - (u8_t *)&obj_id); - SPIFFS_CHECK_RES(res); - - fs->stats_p_allocated++; - - if (was_final) { - // mark finalized in destination page - p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - fh, - SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&p_hdr->flags); - SPIFFS_CHECK_RES(res); - } - // mark source deleted - res = spiffs_page_delete(fs, src_pix); - return res; -} + // Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page. + // If page data is null, provided header is used for metainfo and page data is physically copied. + s32_t spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_obj_id obj_id, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix) + { + s32_t res; + u8_t was_final = 0; + spiffs_page_header *p_hdr; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + if (dst_pix) + { + *dst_pix = free_pix; + } + + p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr; + if (page_data) + { + // got page data + was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0; + // write unfinalized page + p_hdr->flags |= SPIFFS_PH_FLAG_FINAL; + p_hdr->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), page_data); + } + else + { + // copy page data + res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs)); + } + SPIFFS_CHECK_RES(res); + + // mark entry in destination object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + if (was_final) + { + // mark finalized in destination page + p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fh, + SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr->flags); + SPIFFS_CHECK_RES(res); + } + // mark source deleted + res = spiffs_page_delete(fs, src_pix); + return res; + } #endif // !SPIFFS_READ_ONLY #if !SPIFFS_READ_ONLY -// Deletes a page and removes it from object lookup. -s32_t spiffs_page_delete( - spiffs *fs, - spiffs_page_ix pix) { - s32_t res; - spiffs_page_header hdr; - hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); - // mark deleted entry in source object lookup - spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, - 0, - SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix), - sizeof(spiffs_obj_id), - (u8_t *)&d_obj_id); - SPIFFS_CHECK_RES(res); - - fs->stats_p_deleted++; - fs->stats_p_allocated--; - - // mark deleted in source page - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, - 0, - SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&hdr.flags); - - return res; -} + // Deletes a page and removes it from object lookup. + s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix) + { + s32_t res; + spiffs_page_header hdr; + hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); + // mark deleted entry in source object lookup + spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, + 0, + SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&d_obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_deleted++; + fs->stats_p_allocated--; + + // mark deleted in source page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, + 0, + SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&hdr.flags); + + return res; + } #endif // !SPIFFS_READ_ONLY #if !SPIFFS_READ_ONLY -// Create an object index header page with empty index and undefined length -s32_t spiffs_object_create( - spiffs *fs, - spiffs_obj_id obj_id, - const u8_t name[], - const u8_t meta[], - spiffs_obj_type type, - spiffs_page_ix *objix_hdr_pix) { - s32_t res = SPIFFS_OK; - spiffs_block_ix bix; - spiffs_page_object_ix_header oix_hdr; - int entry; - - res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs)); - SPIFFS_CHECK_RES(res); - - obj_id |= SPIFFS_OBJ_ID_IX_FLAG; - - // find free entry - res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); - SPIFFS_CHECK_RES(res); - SPIFFS_DBG("create: found free page @ " _SPIPRIpg " bix:" _SPIPRIbl " entry:" _SPIPRIsp "\n", (spiffs_page_ix)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); - - // occupy page in object lookup - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, - 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); - SPIFFS_CHECK_RES(res); - - fs->stats_p_allocated++; - - // write empty object index page - oix_hdr.p_hdr.obj_id = obj_id; - oix_hdr.p_hdr.span_ix = 0; - oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); - oix_hdr.type = type; - oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page - strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN); + // Create an object index header page with empty index and undefined length + s32_t spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + const u8_t name[], + const u8_t meta[], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix) + { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + spiffs_page_object_ix_header oix_hdr; + int entry; + + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("create: found free page @ " _SPIPRIpg " bix:" _SPIPRIbl " entry:" _SPIPRIsp "\n", (spiffs_page_ix)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write empty object index page + oix_hdr.p_hdr.obj_id = obj_id; + oix_hdr.p_hdr.span_ix = 0; + oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); + oix_hdr.type = type; + oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page + strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN); #if SPIFFS_OBJ_META_LEN - if (meta) { - _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); - } else { - memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); - } + if (meta) + { + _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); + } + else + { + memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); + } #else - (void) meta; + (void) meta; #endif - // update page - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr); + // update page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr, - SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr, + SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); - if (objix_hdr_pix) { - *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } + if (objix_hdr_pix) + { + *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } - return res; -} + return res; + } #endif // !SPIFFS_READ_ONLY #if !SPIFFS_READ_ONLY -// update object index header with any combination of name/size/index -// new_objix_hdr_data may be null, if so the object index header page is loaded -// name may be null, if so name is not changed -// size may be null, if so size is not changed -s32_t spiffs_object_update_index_hdr( - spiffs *fs, - spiffs_fd *fd, - spiffs_obj_id obj_id, - spiffs_page_ix objix_hdr_pix, - u8_t *new_objix_hdr_data, - const u8_t name[], - const u8_t meta[], - u32_t size, - spiffs_page_ix *new_pix) { - s32_t res = SPIFFS_OK; - spiffs_page_object_ix_header *objix_hdr; - spiffs_page_ix new_objix_hdr_pix; - - obj_id |= SPIFFS_OBJ_ID_IX_FLAG; - - if (new_objix_hdr_data) { - // object index header page already given to us, no need to load it - objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data; - } else { - // read object index header page - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - objix_hdr = (spiffs_page_object_ix_header *)fs->work; - } - - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0); - - // change name - if (name) { - strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN); - } + // update object index header with any combination of name/size/index + // new_objix_hdr_data may be null, if so the object index header page is loaded + // name may be null, if so name is not changed + // size may be null, if so size is not changed + s32_t spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_obj_id obj_id, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + const u8_t name[], + const u8_t meta[], + u32_t size, + spiffs_page_ix *new_pix) + { + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header *objix_hdr; + spiffs_page_ix new_objix_hdr_pix; + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + if (new_objix_hdr_data) + { + // object index header page already given to us, no need to load it + objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data; + } + else + { + // read object index header page + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + objix_hdr = (spiffs_page_object_ix_header *)fs->work; + } + + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0); + + // change name + if (name) + { + strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN); + } #if SPIFFS_OBJ_META_LEN - if (meta) { - _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); - } + if (meta) + { + _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); + } #else - (void) meta; + (void) meta; #endif - if (size) { - objix_hdr->size = size; - } + if (size) + { + objix_hdr->size = size; + } - // move and update page - res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t*)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix); + // move and update page + res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t*)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix); - if (res == SPIFFS_OK) { - if (new_pix) { - *new_pix = new_objix_hdr_pix; + if (res == SPIFFS_OK) + { + if (new_pix) + { + *new_pix = new_objix_hdr_pix; + } + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + new_objix_hdr_data ? SPIFFS_EV_IX_UPD : SPIFFS_EV_IX_UPD_HDR, + obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); + if (fd) + { + fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster + } + } + + return res; } - // callback on object index update - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, - new_objix_hdr_data ? SPIFFS_EV_IX_UPD : SPIFFS_EV_IX_UPD_HDR, - obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); - if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster - } - - return res; -} #endif // !SPIFFS_READ_ONLY -void spiffs_cb_object_event( - spiffs *fs, - spiffs_page_object_ix *objix, - int ev, - spiffs_obj_id obj_id_raw, - spiffs_span_ix spix, - spiffs_page_ix new_pix, - u32_t new_size) { + void spiffs_cb_object_event( + spiffs *fs, + spiffs_page_object_ix *objix, + int ev, + spiffs_obj_id obj_id_raw, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size) + { #if SPIFFS_IX_MAP == 0 - (void)objix; + (void)objix; #endif - // update index caches in all file descriptors - spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG; - u32_t i; - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - SPIFFS_DBG(" CALLBACK %s obj_id:" _SPIPRIid " spix:" _SPIPRIsp " npix:" _SPIPRIpg " nsz:" _SPIPRIi "\n", (const char *[]){"UPD", "NEW", "DEL", "MOV", "HUP","???"}[MIN(ev,5)], - obj_id_raw, spix, new_pix, new_size); - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - if ((cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; // fd not related to updated file + // update index caches in all file descriptors + spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + SPIFFS_DBG(" CALLBACK %s obj_id:" _SPIPRIid " spix:" _SPIPRIsp " npix:" _SPIPRIpg " nsz:" _SPIPRIi "\n", (const char *[]) + {"UPD", "NEW", "DEL", "MOV", "HUP", "???" + }[MIN(ev, 5)], + obj_id_raw, spix, new_pix, new_size); + for (i = 0; i < fs->fd_count; i++) + { + spiffs_fd *cur_fd = &fds[i]; + if ((cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) + { + continue; // fd not related to updated file + } #if !SPIFFS_TEMPORAL_FD_CACHE - if (cur_fd->file_nbr == 0) continue; // fd closed + if (cur_fd->file_nbr == 0) + { + continue; // fd closed + } #endif - if (spix == 0) { // object index header update - if (ev != SPIFFS_EV_IX_DEL) { + if (spix == 0) // object index header update + { + if (ev != SPIFFS_EV_IX_DEL) + { #if SPIFFS_TEMPORAL_FD_CACHE - if (cur_fd->score == 0) continue; // never used fd + if (cur_fd->score == 0) + { + continue; // never used fd + } #endif - SPIFFS_DBG(" callback: setting fd " _SPIPRIfd ":" _SPIPRIid "(fdoffs:" _SPIPRIi " offs:" _SPIPRIi ") objix_hdr_pix to " _SPIPRIpg ", size:" _SPIPRIi "\n", - SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, cur_fd->fdoffset, cur_fd->offset, new_pix, new_size); - cur_fd->objix_hdr_pix = new_pix; - if (new_size != 0) { - // update size and offsets for fds to this file - cur_fd->size = new_size; - u32_t act_new_size = new_size == SPIFFS_UNDEFINED_LEN ? 0 : new_size; + SPIFFS_DBG(" callback: setting fd " _SPIPRIfd ":" _SPIPRIid "(fdoffs:" _SPIPRIi " offs:" _SPIPRIi ") objix_hdr_pix to " _SPIPRIpg ", size:" _SPIPRIi "\n", + SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, cur_fd->fdoffset, cur_fd->offset, new_pix, new_size); + cur_fd->objix_hdr_pix = new_pix; + if (new_size != 0) + { + // update size and offsets for fds to this file + cur_fd->size = new_size; + u32_t act_new_size = new_size == SPIFFS_UNDEFINED_LEN ? 0 : new_size; #if SPIFFS_CACHE_WR - if (act_new_size > 0 && cur_fd->cache_page) { - act_new_size = MAX(act_new_size, cur_fd->cache_page->offset + cur_fd->cache_page->size); - } + if (act_new_size > 0 && cur_fd->cache_page) + { + act_new_size = MAX(act_new_size, cur_fd->cache_page->offset + cur_fd->cache_page->size); + } #endif - if (cur_fd->offset > act_new_size) { - cur_fd->offset = act_new_size; - } - if (cur_fd->fdoffset > act_new_size) { - cur_fd->fdoffset = act_new_size; - } + if (cur_fd->offset > act_new_size) + { + cur_fd->offset = act_new_size; + } + if (cur_fd->fdoffset > act_new_size) + { + cur_fd->fdoffset = act_new_size; + } #if SPIFFS_CACHE_WR - if (cur_fd->cache_page && cur_fd->cache_page->offset > act_new_size+1) { - SPIFFS_CACHE_DBG("CACHE_DROP: file trunced, dropping cache page " _SPIPRIi ", no writeback\n", cur_fd->cache_page->ix); - spiffs_cache_fd_release(fs, cur_fd->cache_page); - } + if (cur_fd->cache_page && cur_fd->cache_page->offset > act_new_size + 1) + { + SPIFFS_CACHE_DBG("CACHE_DROP: file trunced, dropping cache page " _SPIPRIi ", no writeback\n", cur_fd->cache_page->ix); + spiffs_cache_fd_release(fs, cur_fd->cache_page); + } #endif - } - } else { - // removing file + } + } + else + { + // removing file #if SPIFFS_CACHE_WR - if (cur_fd->file_nbr && cur_fd->cache_page) { - SPIFFS_CACHE_DBG("CACHE_DROP: file deleted, dropping cache page " _SPIPRIi ", no writeback\n", cur_fd->cache_page->ix); - spiffs_cache_fd_release(fs, cur_fd->cache_page); - } + if (cur_fd->file_nbr && cur_fd->cache_page) + { + SPIFFS_CACHE_DBG("CACHE_DROP: file deleted, dropping cache page " _SPIPRIi ", no writeback\n", cur_fd->cache_page->ix); + spiffs_cache_fd_release(fs, cur_fd->cache_page); + } #endif - SPIFFS_DBG(" callback: release fd " _SPIPRIfd ":" _SPIPRIid " span:" _SPIPRIsp " objix_pix to " _SPIPRIpg "\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); - cur_fd->file_nbr = 0; - cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; - } - } // object index header update - if (cur_fd->cursor_objix_spix == spix) { - if (ev != SPIFFS_EV_IX_DEL) { - SPIFFS_DBG(" callback: setting fd " _SPIPRIfd ":" _SPIPRIid " span:" _SPIPRIsp " objix_pix to " _SPIPRIpg "\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); - cur_fd->cursor_objix_pix = new_pix; - } else { - cur_fd->cursor_objix_pix = 0; - } - } - } // fd update loop + SPIFFS_DBG(" callback: release fd " _SPIPRIfd ":" _SPIPRIid " span:" _SPIPRIsp " objix_pix to " _SPIPRIpg "\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); + cur_fd->file_nbr = 0; + cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; + } + } // object index header update + if (cur_fd->cursor_objix_spix == spix) + { + if (ev != SPIFFS_EV_IX_DEL) + { + SPIFFS_DBG(" callback: setting fd " _SPIPRIfd ":" _SPIPRIid " span:" _SPIPRIsp " objix_pix to " _SPIPRIpg "\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); + cur_fd->cursor_objix_pix = new_pix; + } + else + { + cur_fd->cursor_objix_pix = 0; + } + } + } // fd update loop #if SPIFFS_IX_MAP - // update index maps - if (ev == SPIFFS_EV_IX_UPD || ev == SPIFFS_EV_IX_NEW) { - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - // check fd opened, having ix map, match obj id - if (cur_fd->file_nbr == 0 || - cur_fd->ix_map == 0 || - (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; - SPIFFS_DBG(" callback: map ix update fd " _SPIPRIfd ":" _SPIPRIid " span:" _SPIPRIsp "\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix); - spiffs_update_ix_map(fs, cur_fd, spix, objix); - } - } + // update index maps + if (ev == SPIFFS_EV_IX_UPD || ev == SPIFFS_EV_IX_NEW) + { + for (i = 0; i < fs->fd_count; i++) + { + spiffs_fd *cur_fd = &fds[i]; + // check fd opened, having ix map, match obj id + if (cur_fd->file_nbr == 0 || + cur_fd->ix_map == 0 || + (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) + { + continue; + } + SPIFFS_DBG(" callback: map ix update fd " _SPIPRIfd ":" _SPIPRIid " span:" _SPIPRIsp "\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix); + spiffs_update_ix_map(fs, cur_fd, spix, objix); + } + } #endif - // callback to user if object index header - if (fs->file_cb_f && spix == 0 && (obj_id_raw & SPIFFS_OBJ_ID_IX_FLAG)) { - spiffs_fileop_type op; - if (ev == SPIFFS_EV_IX_NEW) { - op = SPIFFS_CB_CREATED; - } else if (ev == SPIFFS_EV_IX_UPD || - ev == SPIFFS_EV_IX_MOV || - ev == SPIFFS_EV_IX_UPD_HDR) { - op = SPIFFS_CB_UPDATED; - } else if (ev == SPIFFS_EV_IX_DEL) { - op = SPIFFS_CB_DELETED; - } else { - SPIFFS_DBG(" callback: WARNING unknown callback event " _SPIPRIi "\n", ev); - return; // bail out - } - fs->file_cb_f(fs, op, obj_id, new_pix); - } -} - -// Open object by id -s32_t spiffs_object_open_by_id( - spiffs *fs, - spiffs_obj_id obj_id, - spiffs_fd *fd, - spiffs_flags flags, - spiffs_mode mode) { - s32_t res = SPIFFS_OK; - spiffs_page_ix pix; - - res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); - SPIFFS_CHECK_RES(res); - - res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); - - return res; -} - -// Open object by page index -s32_t spiffs_object_open_by_page( - spiffs *fs, - spiffs_page_ix pix, - spiffs_fd *fd, - spiffs_flags flags, - spiffs_mode mode) { - (void)mode; - s32_t res = SPIFFS_OK; - spiffs_page_object_ix_header oix_hdr; - spiffs_obj_id obj_id; - - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); - SPIFFS_CHECK_RES(res); - - spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix); - int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix); - - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, - 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); - - fd->fs = fs; - fd->objix_hdr_pix = pix; - fd->size = oix_hdr.size; - fd->offset = 0; - fd->cursor_objix_pix = pix; - fd->cursor_objix_spix = 0; - fd->obj_id = obj_id; - fd->flags = flags; - - SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); - - SPIFFS_DBG("open: fd " _SPIPRIfd " is obj id " _SPIPRIid "\n", SPIFFS_FH_OFFS(fs, fd->file_nbr), fd->obj_id); - - return res; -} - -#if !SPIFFS_READ_ONLY -// Append to object -// keep current object index (header) page in fs->work buffer -s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { - spiffs *fs = fd->fs; - s32_t res = SPIFFS_OK; - u32_t written = 0; - - SPIFFS_DBG("append: " _SPIPRIi " bytes @ offs " _SPIPRIi " of size " _SPIPRIi "\n", len, offset, fd->size); - - if (offset > fd->size) { - SPIFFS_DBG("append: offset reversed to size\n"); - offset = fd->size; - } - - res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta - if (res != SPIFFS_OK) { - SPIFFS_DBG("append: gc check fail " _SPIPRIi "\n", res); - } - SPIFFS_CHECK_RES(res); - - spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; - spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; - spiffs_page_header p_hdr; - - spiffs_span_ix cur_objix_spix = 0; - spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; - spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; - spiffs_page_ix new_objix_hdr_page; - - spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); - spiffs_page_ix data_page; - u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); - - // write all data - while (res == SPIFFS_OK && written < len) { - // calculate object index page span index - cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - - // handle storing and loading of object indices - if (cur_objix_spix != prev_objix_spix) { - // new object index page - // within this clause we return directly if something fails, object index mess-up - if (written > 0) { - // store previous object index page, unless first pass - SPIFFS_DBG("append: " _SPIPRIid " store objix " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id, - cur_objix_pix, prev_objix_spix, written); - if (prev_objix_spix == 0) { - // this is an update to object index header page - objix_hdr->size = offset+written; - if (offset == 0) { - // was an empty object, update same page (size was 0xffffffff) - res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0); - SPIFFS_CHECK_RES(res); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - } else { - // was a nonempty object, update to new page - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_CHECK_RES(res); - SPIFFS_DBG("append: " _SPIPRIid " store new objix_hdr, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id, - new_objix_hdr_page, 0, written); - } - } else { - // this is an update to an object index page - res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); - SPIFFS_CHECK_RES(res); - - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, - SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); - // update length in object index header page - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_CHECK_RES(res); - SPIFFS_DBG("append: " _SPIPRIid " store new size I " _SPIPRIi " in objix_hdr, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id, - offset+written, new_objix_hdr_page, 0, written); - } - fd->size = offset+written; - fd->offset = offset+written; - } - - // create or load new object index page - if (cur_objix_spix == 0) { - // load object index header page, must always exist - SPIFFS_DBG("append: " _SPIPRIid " load objixhdr page " _SPIPRIpg ":" _SPIPRIsp "\n", fd->obj_id, cur_objix_pix, cur_objix_spix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); - } else { - spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size-1)/SPIFFS_DATA_PAGE_SIZE(fs)); - // on subsequent passes, create a new object index page - if (written > 0 || cur_objix_spix > len_objix_spix) { - p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG; - p_hdr.span_ix = cur_objix_spix; - p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); - res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, - &p_hdr, 0, 0, 0, 1, &cur_objix_pix); - SPIFFS_CHECK_RES(res); - // quick "load" of new object index page - memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - _SPIFFS_MEMCPY(fs->work, &p_hdr, sizeof(spiffs_page_header)); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, - SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); - SPIFFS_DBG("append: " _SPIPRIid " create objix page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id - , cur_objix_pix, cur_objix_spix, written); - } else { - // on first pass, we load existing object index page - spiffs_page_ix pix; - SPIFFS_DBG("append: " _SPIPRIid " find objix span_ix:" _SPIPRIsp "\n", fd->obj_id, cur_objix_spix); - if (fd->cursor_objix_spix == cur_objix_spix) { - pix = fd->cursor_objix_pix; - } else { - res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); - SPIFFS_CHECK_RES(res); - } - SPIFFS_DBG("append: " _SPIPRIid " found object index at page " _SPIPRIpg " [fd size " _SPIPRIi "]\n", fd->obj_id, pix, fd->size); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); - cur_objix_pix = pix; + // callback to user if object index header + if (fs->file_cb_f && spix == 0 && (obj_id_raw & SPIFFS_OBJ_ID_IX_FLAG)) + { + spiffs_fileop_type op; + if (ev == SPIFFS_EV_IX_NEW) + { + op = SPIFFS_CB_CREATED; + } + else if (ev == SPIFFS_EV_IX_UPD || + ev == SPIFFS_EV_IX_MOV || + ev == SPIFFS_EV_IX_UPD_HDR) + { + op = SPIFFS_CB_UPDATED; + } + else if (ev == SPIFFS_EV_IX_DEL) + { + op = SPIFFS_CB_DELETED; + } + else + { + SPIFFS_DBG(" callback: WARNING unknown callback event " _SPIPRIi "\n", ev); + return; // bail out + } + fs->file_cb_f(fs, op, obj_id, new_pix); } - fd->cursor_objix_pix = cur_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - fd->offset = offset+written; - fd->size = offset+written; - } - prev_objix_spix = cur_objix_spix; } - // write data - u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); - if (page_offs == 0) { - // at beginning of a page, allocate and write a new page of data - p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - p_hdr.span_ix = data_spix; - p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately - res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, - &p_hdr, &data[written], to_write, page_offs, 1, &data_page); - SPIFFS_DBG("append: " _SPIPRIid " store new data page, " _SPIPRIpg ":" _SPIPRIsp " offset:" _SPIPRIi ", len " _SPIPRIi ", written " _SPIPRIi "\n", fd->obj_id, - data_page, data_spix, page_offs, to_write, written); - } else { - // append to existing page, fill out free data in existing page - if (cur_objix_spix == 0) { - // get data page from object index header page - data_page = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; - } else { - // get data page from object index page - data_page = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; - } - - res = spiffs_page_data_check(fs, fd, data_page, data_spix); - SPIFFS_CHECK_RES(res); - - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); - SPIFFS_DBG("append: " _SPIPRIid " store to existing data page, " _SPIPRIpg ":" _SPIPRIsp " offset:" _SPIPRIi ", len " _SPIPRIi ", written " _SPIPRIi "\n", fd->obj_id - , data_page, data_spix, page_offs, to_write, written); - } + // Open object by id + s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) + { + s32_t res = SPIFFS_OK; + spiffs_page_ix pix; - if (res != SPIFFS_OK) break; - - // update memory representation of object index page with new data page - if (cur_objix_spix == 0) { - // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; - SPIFFS_DBG("append: " _SPIPRIid " wrote page " _SPIPRIpg " to objix_hdr entry " _SPIPRIsp " in mem\n", fd->obj_id - , data_page, data_spix); - objix_hdr->size = offset+written; - } else { - // update object index page - ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; - SPIFFS_DBG("append: " _SPIPRIid " wrote page " _SPIPRIpg " to objix entry " _SPIPRIsp " in mem\n", fd->obj_id - , data_page, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); - } + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); - // update internals - page_offs = 0; - data_spix++; - written += to_write; - } // while all data - - fd->size = offset+written; - fd->offset = offset+written; - fd->cursor_objix_pix = cur_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - - // finalize updated object indices - s32_t res2 = SPIFFS_OK; - if (cur_objix_spix != 0) { - // wrote beyond object index header page - // write last modified object index page, unless object header index page - SPIFFS_DBG("append: " _SPIPRIid " store objix page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi"\n", fd->obj_id, - cur_objix_pix, cur_objix_spix, written); - - res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); - SPIFFS_CHECK_RES(res2); - - res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res2); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, - SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); - - // update size in object header index page - res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_DBG("append: " _SPIPRIid " store new size II " _SPIPRIi " in objix_hdr, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi ", res " _SPIPRIi "\n", fd->obj_id - , offset+written, new_objix_hdr_page, 0, written, res2); - SPIFFS_CHECK_RES(res2); - } else { - // wrote within object index header page - if (offset == 0) { - // wrote to empty object - simply update size and write whole page - objix_hdr->size = offset+written; - SPIFFS_DBG("append: " _SPIPRIid " store fresh objix_hdr page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id - , cur_objix_pix, cur_objix_spix, written); - - res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); - SPIFFS_CHECK_RES(res2); - - res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res2); - // callback on object index update - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, - SPIFFS_EV_IX_UPD_HDR, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); - } else { - // modifying object index header page, update size and make new copy - res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_DBG("append: " _SPIPRIid " store modified objix_hdr page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id - , new_objix_hdr_page, 0, written); - SPIFFS_CHECK_RES(res2); + return res; } - } - return res; -} // spiffs_object_append -#endif // !SPIFFS_READ_ONLY + // Open object by page index + s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) + { + (void)mode; + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header oix_hdr; + spiffs_obj_id obj_id; -#if !SPIFFS_READ_ONLY -// Modify object -// keep current object index (header) page in fs->work buffer -s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { - spiffs *fs = fd->fs; - s32_t res = SPIFFS_OK; - u32_t written = 0; - - res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); - SPIFFS_CHECK_RES(res); - - spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; - spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; - spiffs_page_header p_hdr; - - spiffs_span_ix cur_objix_spix = 0; - spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; - spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; - spiffs_page_ix new_objix_hdr_pix; - - spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); - spiffs_page_ix data_pix; - u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); - - - // write all data - while (res == SPIFFS_OK && written < len) { - // calculate object index page span index - cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - - // handle storing and loading of object indices - if (cur_objix_spix != prev_objix_spix) { - // new object index page - // within this clause we return directly if something fails, object index mess-up - if (written > 0) { - // store previous object index (header) page, unless first pass - if (prev_objix_spix == 0) { - // store previous object index header page - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); - SPIFFS_DBG("modify: store modified objix_hdr page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", new_objix_hdr_pix, 0, written); - SPIFFS_CHECK_RES(res); - } else { - // store new version of previous object index page - spiffs_page_ix new_objix_pix; - - res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); - SPIFFS_CHECK_RES(res); - - res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); - SPIFFS_DBG("modify: store previous modified objix page, " _SPIPRIid ":" _SPIPRIsp ", written " _SPIPRIi "\n", new_objix_pix, objix->p_hdr.span_ix, written); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, - SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); - } - } - - // load next object index page - if (cur_objix_spix == 0) { - // load object index header page, must exist - SPIFFS_DBG("modify: load objixhdr page " _SPIPRIpg ":" _SPIPRIsp "\n", cur_objix_pix, cur_objix_spix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); - } else { - // load existing object index page on first pass - spiffs_page_ix pix; - SPIFFS_DBG("modify: find objix span_ix:" _SPIPRIsp "\n", cur_objix_spix); - if (fd->cursor_objix_spix == cur_objix_spix) { - pix = fd->cursor_objix_pix; - } else { - res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); - SPIFFS_CHECK_RES(res); - } - SPIFFS_DBG("modify: found object index at page " _SPIPRIpg "\n", pix); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); - cur_objix_pix = pix; - } - fd->cursor_objix_pix = cur_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - fd->offset = offset+written; - prev_objix_spix = cur_objix_spix; - } - // write partial data - u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); - spiffs_page_ix orig_data_pix; - if (cur_objix_spix == 0) { - // get data page from object index header page - orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; - } else { - // get data page from object index page - orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; - } + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix); - p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - p_hdr.span_ix = data_spix; - p_hdr.flags = 0xff; - if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) { - // a full page, allocate and write a new page of data - res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, - &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); - SPIFFS_DBG("modify: store new data page, " _SPIPRIpg ":" _SPIPRIsp " offset:" _SPIPRIi ", len " _SPIPRIi ", written " _SPIPRIi "\n", data_pix, data_spix, page_offs, to_write, written); - } else { - // write to existing page, allocate new and copy unmodified data - - res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix); - SPIFFS_CHECK_RES(res); - - res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, - &p_hdr, 0, 0, 0, 0, &data_pix); - if (res != SPIFFS_OK) break; - - // copy unmodified data - if (page_offs > 0) { - // before modification - res = spiffs_phys_cpy(fs, fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), - SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header), - page_offs); - if (res != SPIFFS_OK) break; - } - if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) { - // after modification - res = spiffs_phys_cpy(fs, fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, - SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, - SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write)); - if (res != SPIFFS_OK) break; - } - - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); - if (res != SPIFFS_OK) break; - p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&p_hdr.flags); - if (res != SPIFFS_OK) break; - - SPIFFS_DBG("modify: store to existing data page, src:" _SPIPRIpg ", dst:" _SPIPRIpg ":" _SPIPRIsp " offset:" _SPIPRIi ", len " _SPIPRIi ", written " _SPIPRIi "\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); - } + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); - // delete original data page - res = spiffs_page_delete(fs, orig_data_pix); - if (res != SPIFFS_OK) break; - // update memory representation of object index page with new data page - if (cur_objix_spix == 0) { - // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; - SPIFFS_DBG("modify: wrote page " _SPIPRIpg " to objix_hdr entry " _SPIPRIsp " in mem\n", data_pix, data_spix); - } else { - // update object index page - ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; - SPIFFS_DBG("modify: wrote page " _SPIPRIpg " to objix entry " _SPIPRIsp " in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); - } + fd->fs = fs; + fd->objix_hdr_pix = pix; + fd->size = oix_hdr.size; + fd->offset = 0; + fd->cursor_objix_pix = pix; + fd->cursor_objix_spix = 0; + fd->obj_id = obj_id; + fd->flags = flags; - // update internals - page_offs = 0; - data_spix++; - written += to_write; - } // while all data - - fd->offset = offset+written; - fd->cursor_objix_pix = cur_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - - // finalize updated object indices - s32_t res2 = SPIFFS_OK; - if (cur_objix_spix != 0) { - // wrote beyond object index header page - // write last modified object index page - // move and update page - spiffs_page_ix new_objix_pix; - - res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); - SPIFFS_CHECK_RES(res2); - - res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); - SPIFFS_DBG("modify: store modified objix page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", new_objix_pix, cur_objix_spix, written); - fd->cursor_objix_pix = new_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - SPIFFS_CHECK_RES(res2); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, - SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); - - } else { - // wrote within object index header page - res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); - SPIFFS_DBG("modify: store modified objix_hdr page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", new_objix_hdr_pix, 0, written); - SPIFFS_CHECK_RES(res2); - } - - return res; -} // spiffs_object_modify -#endif // !SPIFFS_READ_ONLY + SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); -static s32_t spiffs_object_find_object_index_header_by_name_v( - spiffs *fs, - spiffs_obj_id obj_id, - spiffs_block_ix bix, - int ix_entry, - const void *user_const_p, - void *user_var_p) { - (void)user_var_p; - s32_t res; - spiffs_page_object_ix_header objix_hdr; - spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); - if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || - (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { - return SPIFFS_VIS_COUNTINUE; - } - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); - SPIFFS_CHECK_RES(res); - if (objix_hdr.p_hdr.span_ix == 0 && - (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == - (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { - if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { - return SPIFFS_OK; + SPIFFS_DBG("open: fd " _SPIPRIfd " is obj id " _SPIPRIid "\n", SPIFFS_FH_OFFS(fs, fd->file_nbr), fd->obj_id); + + return res; } - } - - return SPIFFS_VIS_COUNTINUE; -} - -// Finds object index header page by name -s32_t spiffs_object_find_object_index_header_by_name( - spiffs *fs, - const u8_t name[SPIFFS_OBJ_NAME_LEN], - spiffs_page_ix *pix) { - s32_t res; - spiffs_block_ix bix; - int entry; - - res = spiffs_obj_lu_find_entry_visitor(fs, - fs->cursor_block_ix, - fs->cursor_obj_lu_entry, - 0, - 0, - spiffs_object_find_object_index_header_by_name_v, - name, - 0, - &bix, - &entry); - - if (res == SPIFFS_VIS_END) { - res = SPIFFS_ERR_NOT_FOUND; - } - SPIFFS_CHECK_RES(res); - - if (pix) { - *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } - - fs->cursor_block_ix = bix; - fs->cursor_obj_lu_entry = entry; - - return res; -} #if !SPIFFS_READ_ONLY -// Truncates object to new size. If new size is null, object may be removed totally -s32_t spiffs_object_truncate( - spiffs_fd *fd, - u32_t new_size, - u8_t remove_full) { - s32_t res = SPIFFS_OK; - spiffs *fs = fd->fs; - - if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove_full) { - // no op - return res; - } - - // need 2 pages if not removing: object index page + possibly chopped data page - if (remove_full == 0) { - res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2); - SPIFFS_CHECK_RES(res); - } - - spiffs_page_ix objix_pix = fd->objix_hdr_pix; - spiffs_span_ix data_spix = (fd->size > 0 ? fd->size-1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); - u32_t cur_size = fd->size == (u32_t)SPIFFS_UNDEFINED_LEN ? 0 : fd->size ; - spiffs_span_ix cur_objix_spix = 0; - spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; - spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; - spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; - spiffs_page_ix data_pix; - spiffs_page_ix new_objix_hdr_pix; - - // before truncating, check if object is to be fully removed and mark this - if (remove_full && new_size == 0) { - u8_t flags = ~( SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE); - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&flags); - SPIFFS_CHECK_RES(res); - } - - // delete from end of object until desired len is reached - while (cur_size > new_size) { - cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - - // put object index for current data span index in work buffer - if (prev_objix_spix != cur_objix_spix) { - if (prev_objix_spix != (spiffs_span_ix)-1) { - // remove previous object index page - SPIFFS_DBG("truncate: delete objix page " _SPIPRIpg ":" _SPIPRIsp "\n", objix_pix, prev_objix_spix); - - res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); + // Append to object + // keep current object index (header) page in fs->work buffer + s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) + { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + SPIFFS_DBG("append: " _SPIPRIi " bytes @ offs " _SPIPRIi " of size " _SPIPRIi "\n", len, offset, fd->size); + + if (offset > fd->size) + { + SPIFFS_DBG("append: offset reversed to size\n"); + offset = fd->size; + } + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta + if (res != SPIFFS_OK) + { + SPIFFS_DBG("append: gc check fail " _SPIPRIi "\n", res); + } SPIFFS_CHECK_RES(res); - res = spiffs_page_delete(fs, objix_pix); + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_page; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_page; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + // write all data + while (res == SPIFFS_OK && written < len) + { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) + { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) + { + // store previous object index page, unless first pass + SPIFFS_DBG("append: " _SPIPRIid " store objix " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id, + cur_objix_pix, prev_objix_spix, written); + if (prev_objix_spix == 0) + { + // this is an update to object index header page + objix_hdr->size = offset + written; + if (offset == 0) + { + // was an empty object, update same page (size was 0xffffffff) + res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + } + else + { + // was a nonempty object, update to new page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: " _SPIPRIid " store new objix_hdr, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id, + new_objix_hdr_page, 0, written); + } + } + else + { + // this is an update to an object index page + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + // update length in object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: " _SPIPRIid " store new size I " _SPIPRIi " in objix_hdr, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id, + offset + written, new_objix_hdr_page, 0, written); + } + fd->size = offset + written; + fd->offset = offset + written; + } + + // create or load new object index page + if (cur_objix_spix == 0) + { + // load object index header page, must always exist + SPIFFS_DBG("append: " _SPIPRIid " load objixhdr page " _SPIPRIpg ":" _SPIPRIsp "\n", fd->obj_id, cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } + else + { + spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size - 1) / SPIFFS_DATA_PAGE_SIZE(fs)); + // on subsequent passes, create a new object index page + if (written > 0 || cur_objix_spix > len_objix_spix) + { + p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = cur_objix_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 1, &cur_objix_pix); + SPIFFS_CHECK_RES(res); + // quick "load" of new object index page + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + _SPIFFS_MEMCPY(fs->work, &p_hdr, sizeof(spiffs_page_header)); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); + SPIFFS_DBG("append: " _SPIPRIid " create objix page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + } + else + { + // on first pass, we load existing object index page + spiffs_page_ix pix; + SPIFFS_DBG("append: " _SPIPRIid " find objix span_ix:" _SPIPRIsp "\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) + { + pix = fd->cursor_objix_pix; + } + else + { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("append: " _SPIPRIid " found object index at page " _SPIPRIpg " [fd size " _SPIPRIi "]\n", fd->obj_id, pix, fd->size); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset + written; + fd->size = offset + written; + } + prev_objix_spix = cur_objix_spix; + } + + // write data + u32_t to_write = MIN(len - written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + if (page_offs == 0) + { + // at beginning of a page, allocate and write a new page of data + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_page); + SPIFFS_DBG("append: " _SPIPRIid " store new data page, " _SPIPRIpg ":" _SPIPRIsp " offset:" _SPIPRIi ", len " _SPIPRIi ", written " _SPIPRIi "\n", fd->obj_id, + data_page, data_spix, page_offs, to_write, written); + } + else + { + // append to existing page, fill out free data in existing page + if (cur_objix_spix == 0) + { + // get data page from object index header page + data_page = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } + else + { + // get data page from object index page + data_page = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + res = spiffs_page_data_check(fs, fd, data_page, data_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + SPIFFS_DBG("append: " _SPIPRIid " store to existing data page, " _SPIPRIpg ":" _SPIPRIsp " offset:" _SPIPRIi ", len " _SPIPRIi ", written " _SPIPRIi "\n", fd->obj_id + , data_page, data_spix, page_offs, to_write, written); + } + + if (res != SPIFFS_OK) + { + break; + } + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) + { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; + SPIFFS_DBG("append: " _SPIPRIid " wrote page " _SPIPRIpg " to objix_hdr entry " _SPIPRIsp " in mem\n", fd->obj_id + , data_page, data_spix); + objix_hdr->size = offset + written; + } + else + { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; + SPIFFS_DBG("append: " _SPIPRIid " wrote page " _SPIPRIpg " to objix entry " _SPIPRIsp " in mem\n", fd->obj_id + , data_page, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->size = offset + written; + fd->offset = offset + written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) + { + // wrote beyond object index header page + // write last modified object index page, unless object header index page + SPIFFS_DBG("append: " _SPIPRIid " store objix page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi"\n", fd->obj_id, + cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + + // update size in object header index page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_DBG("append: " _SPIPRIid " store new size II " _SPIPRIi " in objix_hdr, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi ", res " _SPIPRIi "\n", fd->obj_id + , offset + written, new_objix_hdr_page, 0, written, res2); + SPIFFS_CHECK_RES(res2); + } + else + { + // wrote within object index header page + if (offset == 0) + { + // wrote to empty object - simply update size and write whole page + objix_hdr->size = offset + written; + SPIFFS_DBG("append: " _SPIPRIid " store fresh objix_hdr page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD_HDR, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); + } + else + { + // modifying object index header page, update size and make new copy + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_DBG("append: " _SPIPRIid " store modified objix_hdr page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", fd->obj_id + , new_objix_hdr_page, 0, written); + SPIFFS_CHECK_RES(res2); + } + } + + return res; + } // spiffs_object_append +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY + // Modify object + // keep current object index (header) page in fs->work buffer + s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) + { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, - SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); - if (prev_objix_spix > 0) { - // Update object index header page, unless we totally want to remove the file. - // If fully removing, we're not keeping consistency as good as when storing the header between chunks, - // would we be aborted. But when removing full files, a crammed system may otherwise - // report ERR_FULL a la windows. We cannot have that. - // Hence, take the risk - if aborted, a file check would free the lost pages and mend things - // as the file is marked as fully deleted in the beginning. - if (remove_full == 0) { - SPIFFS_DBG("truncate: update objix hdr page " _SPIPRIpg ":" _SPIPRIsp " to size " _SPIPRIi "\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); - SPIFFS_CHECK_RES(res); - } - fd->size = cur_size; - } - } - // load current object index (header) page - if (cur_objix_spix == 0) { - objix_pix = fd->objix_hdr_pix; - } else { - res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_pix; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_pix; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + + // write all data + while (res == SPIFFS_OK && written < len) + { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) + { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) + { + // store previous object index (header) page, unless first pass + if (prev_objix_spix == 0) + { + // store previous object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res); + } + else + { + // store new version of previous object index page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store previous modified objix page, " _SPIPRIid ":" _SPIPRIsp ", written " _SPIPRIi "\n", new_objix_pix, objix->p_hdr.span_ix, written); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + + // load next object index page + if (cur_objix_spix == 0) + { + // load object index header page, must exist + SPIFFS_DBG("modify: load objixhdr page " _SPIPRIpg ":" _SPIPRIsp "\n", cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } + else + { + // load existing object index page on first pass + spiffs_page_ix pix; + SPIFFS_DBG("modify: find objix span_ix:" _SPIPRIsp "\n", cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) + { + pix = fd->cursor_objix_pix; + } + else + { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("modify: found object index at page " _SPIPRIpg "\n", pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset + written; + prev_objix_spix = cur_objix_spix; + } + + // write partial data + u32_t to_write = MIN(len - written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + spiffs_page_ix orig_data_pix; + if (cur_objix_spix == 0) + { + // get data page from object index header page + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } + else + { + // get data page from object index page + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) + { + // a full page, allocate and write a new page of data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); + SPIFFS_DBG("modify: store new data page, " _SPIPRIpg ":" _SPIPRIsp " offset:" _SPIPRIi ", len " _SPIPRIi ", written " _SPIPRIi "\n", data_pix, data_spix, page_offs, to_write, written); + } + else + { + // write to existing page, allocate new and copy unmodified data + + res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &data_pix); + if (res != SPIFFS_OK) + { + break; + } + + // copy unmodified data + if (page_offs > 0) + { + // before modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header), + page_offs); + if (res != SPIFFS_OK) + { + break; + } + } + if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) + { + // after modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write)); + if (res != SPIFFS_OK) + { + break; + } + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + if (res != SPIFFS_OK) + { + break; + } + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) + { + break; + } + + SPIFFS_DBG("modify: store to existing data page, src:" _SPIPRIpg ", dst:" _SPIPRIpg ":" _SPIPRIsp " offset:" _SPIPRIi ", len " _SPIPRIi ", written " _SPIPRIi "\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); + } + + // delete original data page + res = spiffs_page_delete(fs, orig_data_pix); + if (res != SPIFFS_OK) + { + break; + } + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) + { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; + SPIFFS_DBG("modify: wrote page " _SPIPRIpg " to objix_hdr entry " _SPIPRIsp " in mem\n", data_pix, data_spix); + } + else + { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; + SPIFFS_DBG("modify: wrote page " _SPIPRIpg " to objix entry " _SPIPRIsp " in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->offset = offset + written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) + { + // wrote beyond object index header page + // write last modified object index page + // move and update page + spiffs_page_ix new_objix_pix; + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store modified objix page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", new_objix_pix, cur_objix_spix, written); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + + } + else + { + // wrote within object index header page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, " _SPIPRIpg ":" _SPIPRIsp ", written " _SPIPRIi "\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res2); + } + + return res; + } // spiffs_object_modify +#endif // !SPIFFS_READ_ONLY + + static s32_t spiffs_object_find_object_index_header_by_name_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) + { + (void)user_var_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) + { + return SPIFFS_VIS_COUNTINUE; + } + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); SPIFFS_CHECK_RES(res); - } - - SPIFFS_DBG("truncate: load objix page " _SPIPRIpg ":" _SPIPRIsp " for data spix:" _SPIPRIsp "\n", objix_pix, cur_objix_spix, data_spix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); - fd->cursor_objix_pix = objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - fd->offset = cur_size; - - prev_objix_spix = cur_objix_spix; - } + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) + { + if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) + { + return SPIFFS_OK; + } + } - if (cur_objix_spix == 0) { - // get data page from object index header page - data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; - } else { - // get data page from object index page - data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; - ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; + return SPIFFS_VIS_COUNTINUE; } - SPIFFS_DBG("truncate: got data pix " _SPIPRIpg "\n", data_pix); - - if (new_size == 0 || remove_full || cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs)) { - // delete full data page - res = spiffs_page_data_check(fs, fd, data_pix, data_spix); - if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) { - SPIFFS_DBG("truncate: err validating data pix " _SPIPRIi "\n", res); - break; - } - - if (res == SPIFFS_OK) { - res = spiffs_page_delete(fs, data_pix); - if (res != SPIFFS_OK) { - SPIFFS_DBG("truncate: err deleting data pix " _SPIPRIi "\n", res); - break; - } - } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) { - res = SPIFFS_OK; - } - - // update current size - if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { - cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); - } else { - cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs); - } - fd->size = cur_size; - fd->offset = cur_size; - SPIFFS_DBG("truncate: delete data page " _SPIPRIpg " for data spix:" _SPIPRIsp ", cur_size:" _SPIPRIi "\n", data_pix, data_spix, cur_size); - } else { - // delete last page, partially - spiffs_page_header p_hdr; - spiffs_page_ix new_data_pix; - u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); - SPIFFS_DBG("truncate: delete " _SPIPRIi " bytes from data page " _SPIPRIpg " for data spix:" _SPIPRIsp ", cur_size:" _SPIPRIi "\n", bytes_to_remove, data_pix, data_spix, cur_size); - - res = spiffs_page_data_check(fs, fd, data_pix, data_spix); - if (res != SPIFFS_OK) break; - - p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; - p_hdr.span_ix = data_spix; - p_hdr.flags = 0xff; - // allocate new page and copy unmodified data - res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, - &p_hdr, 0, 0, 0, 0, &new_data_pix); - if (res != SPIFFS_OK) break; - res = spiffs_phys_cpy(fs, 0, - SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header), - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), - SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove); - if (res != SPIFFS_OK) break; - // delete original data page - res = spiffs_page_delete(fs, data_pix); - if (res != SPIFFS_OK) break; - p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; - res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&p_hdr.flags); - if (res != SPIFFS_OK) break; - - // update memory representation of object index page with new data page - if (cur_objix_spix == 0) { - // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; - SPIFFS_DBG("truncate: wrote page " _SPIPRIpg " to objix_hdr entry " _SPIPRIsp " in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); - } else { - // update object index page - ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; - SPIFFS_DBG("truncate: wrote page " _SPIPRIpg " to objix entry " _SPIPRIsp " in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); - } - cur_size = new_size; - fd->size = new_size; - fd->offset = cur_size; - break; - } - data_spix--; - } // while all data - - // update object indices - if (cur_objix_spix == 0) { - // update object index header page - if (cur_size == 0) { - if (remove_full) { - // remove object altogether - SPIFFS_DBG("truncate: remove object index header page " _SPIPRIpg "\n", objix_pix); - - res = spiffs_page_index_check(fs, fd, objix_pix, 0); + // Finds object index header page by name + s32_t spiffs_object_find_object_index_header_by_name( + spiffs *fs, + const u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix) + { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + 0, + 0, + spiffs_object_find_object_index_header_by_name_v, + name, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) + { + res = SPIFFS_ERR_NOT_FOUND; + } SPIFFS_CHECK_RES(res); - res = spiffs_page_delete(fs, objix_pix); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, - SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); - } else { - // make uninitialized object - SPIFFS_DBG("truncate: reset objix_hdr page " _SPIPRIpg "\n", objix_pix); - memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, - SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); - SPIFFS_CHECK_RES(res); - } - } else { - // update object index header page - SPIFFS_DBG("truncate: update object index header page with indices and size\n"); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - objix_pix, fs->work, 0, 0, cur_size, &new_objix_hdr_pix); - SPIFFS_CHECK_RES(res); + if (pix) + { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; } - } else { - // update both current object index page and object index header page - spiffs_page_ix new_objix_pix; - - res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix); - SPIFFS_CHECK_RES(res); - - // move and update object index page - res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix); - SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, - SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); - SPIFFS_DBG("truncate: store modified objix page, " _SPIPRIpg ":" _SPIPRIsp "\n", new_objix_pix, cur_objix_spix); - fd->cursor_objix_pix = new_objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - fd->offset = cur_size; - // update object index header page with new size - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); - SPIFFS_CHECK_RES(res); - } - fd->size = cur_size; - - return res; -} // spiffs_object_truncate -#endif // !SPIFFS_READ_ONLY -s32_t spiffs_object_read( - spiffs_fd *fd, - u32_t offset, - u32_t len, - u8_t *dst) { - s32_t res = SPIFFS_OK; - spiffs *fs = fd->fs; - spiffs_page_ix objix_pix; - spiffs_page_ix data_pix; - spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); - u32_t cur_offset = offset; - spiffs_span_ix cur_objix_spix; - spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; - spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; - spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; - - while (cur_offset < offset + len) { -#if SPIFFS_IX_MAP - // check if we have a memory, index map and if so, if we're within index map's range - // and if so, if the entry is populated - if (fd->ix_map && data_spix >= fd->ix_map->start_spix && data_spix <= fd->ix_map->end_spix - && fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]) { - data_pix = fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]; - } else { -#endif - cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - if (prev_objix_spix != cur_objix_spix) { - // load current object index (header) page - if (cur_objix_spix == 0) { - objix_pix = fd->objix_hdr_pix; - } else { - SPIFFS_DBG("read: find objix " _SPIPRIid ":" _SPIPRIsp "\n", fd->obj_id, cur_objix_spix); - if (fd->cursor_objix_spix == cur_objix_spix) { - objix_pix = fd->cursor_objix_pix; - } else { - res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); +#if !SPIFFS_READ_ONLY + // Truncates object to new size. If new size is null, object may be removed totally + s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_size, + u8_t remove_full) + { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + + if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove_full) + { + // no op + return res; + } + + // need 2 pages if not removing: object index page + possibly chopped data page + if (remove_full == 0) + { + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2); SPIFFS_CHECK_RES(res); - } } - SPIFFS_DBG("read: load objix page " _SPIPRIpg ":" _SPIPRIsp " for data spix:" _SPIPRIsp "\n", objix_pix, cur_objix_spix, data_spix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); - fd->offset = cur_offset; - fd->cursor_objix_pix = objix_pix; - fd->cursor_objix_spix = cur_objix_spix; + spiffs_page_ix objix_pix = fd->objix_hdr_pix; + spiffs_span_ix data_spix = (fd->size > 0 ? fd->size - 1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_size = fd->size == (u32_t)SPIFFS_UNDEFINED_LEN ? 0 : fd->size ; + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_ix data_pix; + spiffs_page_ix new_objix_hdr_pix; + + // before truncating, check if object is to be fully removed and mark this + if (remove_full && new_size == 0) + { + u8_t flags = ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); + SPIFFS_CHECK_RES(res); + } + + // delete from end of object until desired len is reached + while (cur_size > new_size) + { + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // put object index for current data span index in work buffer + if (prev_objix_spix != cur_objix_spix) + { + if (prev_objix_spix != (spiffs_span_ix) - 1) + { + // remove previous object index page + SPIFFS_DBG("truncate: delete objix page " _SPIPRIpg ":" _SPIPRIsp "\n", objix_pix, prev_objix_spix); + + res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); + if (prev_objix_spix > 0) + { + // Update object index header page, unless we totally want to remove the file. + // If fully removing, we're not keeping consistency as good as when storing the header between chunks, + // would we be aborted. But when removing full files, a crammed system may otherwise + // report ERR_FULL a la windows. We cannot have that. + // Hence, take the risk - if aborted, a file check would free the lost pages and mend things + // as the file is marked as fully deleted in the beginning. + if (remove_full == 0) + { + SPIFFS_DBG("truncate: update objix hdr page " _SPIPRIpg ":" _SPIPRIsp " to size " _SPIPRIi "\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + } + } + // load current object index (header) page + if (cur_objix_spix == 0) + { + objix_pix = fd->objix_hdr_pix; + } + else + { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + + SPIFFS_DBG("truncate: load objix page " _SPIPRIpg ":" _SPIPRIsp " for data spix:" _SPIPRIsp "\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) + { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; + } + else + { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; + } - prev_objix_spix = cur_objix_spix; - } + SPIFFS_DBG("truncate: got data pix " _SPIPRIpg "\n", data_pix); + + if (new_size == 0 || remove_full || cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs)) + { + // delete full data page + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) + { + SPIFFS_DBG("truncate: err validating data pix " _SPIPRIi "\n", res); + break; + } + + if (res == SPIFFS_OK) + { + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) + { + SPIFFS_DBG("truncate: err deleting data pix " _SPIPRIi "\n", res); + break; + } + } + else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) + { + res = SPIFFS_OK; + } + + // update current size + if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) + { + cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); + } + else + { + cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs); + } + fd->size = cur_size; + fd->offset = cur_size; + SPIFFS_DBG("truncate: delete data page " _SPIPRIpg " for data spix:" _SPIPRIsp ", cur_size:" _SPIPRIi "\n", data_pix, data_spix, cur_size); + } + else + { + // delete last page, partially + spiffs_page_header p_hdr; + spiffs_page_ix new_data_pix; + u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_DBG("truncate: delete " _SPIPRIi " bytes from data page " _SPIPRIpg " for data spix:" _SPIPRIsp ", cur_size:" _SPIPRIi "\n", bytes_to_remove, data_pix, data_spix, cur_size); + + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_OK) + { + break; + } + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + // allocate new page and copy unmodified data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &new_data_pix); + if (res != SPIFFS_OK) + { + break; + } + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove); + if (res != SPIFFS_OK) + { + break; + } + // delete original data page + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) + { + break; + } + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) + { + break; + } + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) + { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + SPIFFS_DBG("truncate: wrote page " _SPIPRIpg " to objix_hdr entry " _SPIPRIsp " in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + else + { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + SPIFFS_DBG("truncate: wrote page " _SPIPRIpg " to objix entry " _SPIPRIsp " in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + cur_size = new_size; + fd->size = new_size; + fd->offset = cur_size; + break; + } + data_spix--; + } // while all data + + // update object indices + if (cur_objix_spix == 0) + { + // update object index header page + if (cur_size == 0) + { + if (remove_full) + { + // remove object altogether + SPIFFS_DBG("truncate: remove object index header page " _SPIPRIpg "\n", objix_pix); + + res = spiffs_page_index_check(fs, fd, objix_pix, 0); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); + } + else + { + // make uninitialized object + SPIFFS_DBG("truncate: reset objix_hdr page " _SPIPRIpg "\n", objix_pix); + memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } + else + { + // update object index header page + SPIFFS_DBG("truncate: update object index header page with indices and size\n"); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } + else + { + // update both current object index page and object index header page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res); + + // move and update object index page + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + SPIFFS_DBG("truncate: store modified objix page, " _SPIPRIpg ":" _SPIPRIsp "\n", new_objix_pix, cur_objix_spix); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + // update object index header page with new size + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + + return res; + } // spiffs_object_truncate +#endif // !SPIFFS_READ_ONLY - if (cur_objix_spix == 0) { - // get data page from object index header page - data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; - } else { - // get data page from object index page - data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; - } + s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst) + { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + spiffs_page_ix objix_pix; + spiffs_page_ix data_pix; + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_offset = offset; + spiffs_span_ix cur_objix_spix; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + while (cur_offset < offset + len) + { #if SPIFFS_IX_MAP - } + // check if we have a memory, index map and if so, if we're within index map's range + // and if so, if the entry is populated + if (fd->ix_map && data_spix >= fd->ix_map->start_spix && data_spix <= fd->ix_map->end_spix + && fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]) + { + data_pix = fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]; + } + else + { +#endif + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (prev_objix_spix != cur_objix_spix) + { + // load current object index (header) page + if (cur_objix_spix == 0) + { + objix_pix = fd->objix_hdr_pix; + } + else + { + SPIFFS_DBG("read: find objix " _SPIPRIid ":" _SPIPRIsp "\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) + { + objix_pix = fd->cursor_objix_pix; + } + else + { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + } + SPIFFS_DBG("read: load objix page " _SPIPRIpg ":" _SPIPRIsp " for data spix:" _SPIPRIsp "\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); + + fd->offset = cur_offset; + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) + { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } + else + { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } +#if SPIFFS_IX_MAP + } #endif - // all remaining data - u32_t len_to_read = offset + len - cur_offset; - // remaining data in page - len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); - // remaining data in file - len_to_read = MIN(len_to_read, fd->size); - SPIFFS_DBG("read: offset:" _SPIPRIi " rd:" _SPIPRIi " data spix:" _SPIPRIsp " is data_pix:" _SPIPRIpg " addr:" _SPIPRIad "\n", cur_offset, len_to_read, data_spix, data_pix, - (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)))); - if (len_to_read <= 0) { - res = SPIFFS_ERR_END_OF_OBJECT; - break; + // all remaining data + u32_t len_to_read = offset + len - cur_offset; + // remaining data in page + len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + // remaining data in file + len_to_read = MIN(len_to_read, fd->size); + SPIFFS_DBG("read: offset:" _SPIPRIi " rd:" _SPIPRIi " data spix:" _SPIPRIsp " is data_pix:" _SPIPRIpg " addr:" _SPIPRIad "\n", cur_offset, len_to_read, data_spix, data_pix, + (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)))); + if (len_to_read <= 0) + { + res = SPIFFS_ERR_END_OF_OBJECT; + break; + } + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + SPIFFS_CHECK_RES(res); + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)), + len_to_read, + dst); + SPIFFS_CHECK_RES(res); + dst += len_to_read; + cur_offset += len_to_read; + fd->offset = cur_offset; + data_spix++; + } + + return res; } - res = spiffs_page_data_check(fs, fd, data_pix, data_spix); - SPIFFS_CHECK_RES(res); - res = _spiffs_rd( - fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, - fd->file_nbr, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)), - len_to_read, - dst); - SPIFFS_CHECK_RES(res); - dst += len_to_read; - cur_offset += len_to_read; - fd->offset = cur_offset; - data_spix++; - } - - return res; -} #if !SPIFFS_READ_ONLY -typedef struct { - spiffs_obj_id min_obj_id; - spiffs_obj_id max_obj_id; - u32_t compaction; - const u8_t *conflicting_name; -} spiffs_free_obj_id_state; - -static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, - const void *user_const_p, void *user_var_p) { - if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) { - spiffs_obj_id min_obj_id = *((spiffs_obj_id*)user_var_p); - const u8_t *conflicting_name = (const u8_t*)user_const_p; - - // if conflicting name parameter is given, also check if this name is found in object index hdrs - if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) { - spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); - int res; - spiffs_page_object_ix_header objix_hdr; - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); - SPIFFS_CHECK_RES(res); - if (objix_hdr.p_hdr.span_ix == 0 && - (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == - (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { - if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { - return SPIFFS_ERR_CONFLICTING_NAME; - } - } - } + typedef struct + { + spiffs_obj_id min_obj_id; + spiffs_obj_id max_obj_id; + u32_t compaction; + const u8_t *conflicting_name; + } spiffs_free_obj_id_state; + + static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + const void *user_const_p, void *user_var_p) + { + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) + { + spiffs_obj_id min_obj_id = *((spiffs_obj_id*)user_var_p); + const u8_t *conflicting_name = (const u8_t*)user_const_p; + + // if conflicting name parameter is given, also check if this name is found in object index hdrs + if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) + { + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + int res; + spiffs_page_object_ix_header objix_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) + { + if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) + { + return SPIFFS_ERR_CONFLICTING_NAME; + } + } + } - id &= ~SPIFFS_OBJ_ID_IX_FLAG; - u32_t bit_ix = (id-min_obj_id) & 7; - int byte_ix = (id-min_obj_id) >> 3; - if (byte_ix >= 0 && (u32_t)byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { - fs->work[byte_ix] |= (1<conflicting_name && strcmp((const char *)state->conflicting_name, (char *)objix_hdr.name) == 0) { - return SPIFFS_ERR_CONFLICTING_NAME; - } - - id &= ~SPIFFS_OBJ_ID_IX_FLAG; - if (id >= state->min_obj_id && id <= state->max_obj_id) { - u8_t *map = (u8_t *)fs->work; - int ix = (id - state->min_obj_id) / state->compaction; - //SPIFFS_DBG("free_obj_id: add ix " _SPIPRIi " for id " _SPIPRIid " min" _SPIPRIid " max" _SPIPRIid " comp:" _SPIPRIi "\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); - map[ix]++; - } + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t bit_ix = (id - min_obj_id) & 7; + int byte_ix = (id - min_obj_id) >> 3; + if (byte_ix >= 0 && (u32_t)byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) + { + fs->work[byte_ix] |= (1 << bit_ix); + } + } + return SPIFFS_VIS_COUNTINUE; } - } - return SPIFFS_VIS_COUNTINUE; -} - -// Scans thru all object lookup for object index header pages. If total possible number of -// object ids cannot fit into a work buffer, these are grouped. When a group containing free -// object ids is found, the object lu is again scanned for object ids within group and bitmasked. -// Finally, the bitmask is searched for a free id -s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, const u8_t *conflicting_name) { - s32_t res = SPIFFS_OK; - u32_t max_objects = (fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2; - spiffs_free_obj_id_state state; - spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; - state.min_obj_id = 1; - state.max_obj_id = max_objects + 1; - if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) { - state.max_obj_id = ((spiffs_obj_id)-1) & ~SPIFFS_OBJ_ID_IX_FLAG; - } - state.compaction = 0; - state.conflicting_name = conflicting_name; - while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) { - if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8) { - // possible to represent in bitmap - u32_t i, j; - SPIFFS_DBG("free_obj_id: BITM min:" _SPIPRIid " max:" _SPIPRIid "\n", state.min_obj_id, state.max_obj_id); - - memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, - conflicting_name, &state.min_obj_id, 0, 0); - if (res == SPIFFS_VIS_END) res = SPIFFS_OK; - SPIFFS_CHECK_RES(res); - // traverse bitmask until found free obj_id - for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) { - u8_t mask = fs->work[i]; - if (mask == 0xff) { - continue; - } - for (j = 0; j < 8; j++) { - if ((mask & (1<work; - u8_t min_count = 0xff; - - for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(u8_t); i++) { - if (map[i] < min_count) { - min_count = map[i]; - min_i = i; - if (min_count == 0) { - break; + + static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + const void *user_const_p, void *user_var_p) + { + (void)user_var_p; + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED && (id & SPIFFS_OBJ_ID_IX_FLAG)) + { + s32_t res; + const spiffs_free_obj_id_state *state = (const spiffs_free_obj_id_state*)user_const_p; + spiffs_page_object_ix_header objix_hdr; + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, ix_entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&objix_hdr); + if (res == SPIFFS_OK && objix_hdr.p_hdr.span_ix == 0 && + ((objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) == + (SPIFFS_PH_FLAG_DELET))) + { + // ok object look up entry + if (state->conflicting_name && strcmp((const char *)state->conflicting_name, (char *)objix_hdr.name) == 0) + { + return SPIFFS_ERR_CONFLICTING_NAME; + } + + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + if (id >= state->min_obj_id && id <= state->max_obj_id) + { + u8_t *map = (u8_t *)fs->work; + int ix = (id - state->min_obj_id) / state->compaction; + //SPIFFS_DBG("free_obj_id: add ix " _SPIPRIi " for id " _SPIPRIid " min" _SPIPRIid " max" _SPIPRIid " comp:" _SPIPRIi "\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); + map[ix]++; + } } - } - } - - if (min_count == state.compaction) { - // there are no free objids! - SPIFFS_DBG("free_obj_id: compacted table is full\n"); - return SPIFFS_ERR_FULL; - } - - SPIFFS_DBG("free_obj_id: COMP select index:" _SPIPRIi " min_count:" _SPIPRIi " min:" _SPIPRIid " max:" _SPIPRIid " compact:" _SPIPRIi "\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); - - if (min_count == 0) { - // no id in this range, skip compacting and use directly - *obj_id = min_i * state.compaction + state.min_obj_id; - return SPIFFS_OK; - } else { - SPIFFS_DBG("free_obj_id: COMP SEL chunk:" _SPIPRIi " min:" _SPIPRIid " -> " _SPIPRIid "\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); - state.min_obj_id += min_i * state.compaction; - state.max_obj_id = state.min_obj_id + state.compaction; - // decrease compaction - } - if ((state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8)) { - // no need for compacting, use bitmap - continue; - } - } - // in a work memory of log_page_size bytes, we may fit in log_page_size ids - // todo what if compaction is > 255 - then we cannot fit it in a byte - state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); - SPIFFS_DBG("free_obj_id: COMP min:" _SPIPRIid " max:" _SPIPRIid " compact:" _SPIPRIi "\n", state.min_obj_id, state.max_obj_id, state.compaction); - - memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, &state, 0, 0, 0); - if (res == SPIFFS_VIS_END) res = SPIFFS_OK; - SPIFFS_CHECK_RES(res); - state.conflicting_name = 0; // searched for conflicting name once, no need to do it again + } + return SPIFFS_VIS_COUNTINUE; } - } - return res; -} + // Scans thru all object lookup for object index header pages. If total possible number of + // object ids cannot fit into a work buffer, these are grouped. When a group containing free + // object ids is found, the object lu is again scanned for object ids within group and bitmasked. + // Finally, the bitmask is searched for a free id + s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, const u8_t *conflicting_name) + { + s32_t res = SPIFFS_OK; + u32_t max_objects = (fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2; + spiffs_free_obj_id_state state; + spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; + state.min_obj_id = 1; + state.max_obj_id = max_objects + 1; + if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) + { + state.max_obj_id = ((spiffs_obj_id) - 1) & ~SPIFFS_OBJ_ID_IX_FLAG; + } + state.compaction = 0; + state.conflicting_name = conflicting_name; + while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) + { + if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8) + { + // possible to represent in bitmap + u32_t i, j; + SPIFFS_DBG("free_obj_id: BITM min:" _SPIPRIid " max:" _SPIPRIid "\n", state.min_obj_id, state.max_obj_id); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, + conflicting_name, &state.min_obj_id, 0, 0); + if (res == SPIFFS_VIS_END) + { + res = SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + // traverse bitmask until found free obj_id + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) + { + u8_t mask = fs->work[i]; + if (mask == 0xff) + { + continue; + } + for (j = 0; j < 8; j++) + { + if ((mask & (1 << j)) == 0) + { + *obj_id = (i << 3) + j + state.min_obj_id; + return SPIFFS_OK; + } + } + } + return SPIFFS_ERR_FULL; + } + else + { + // not possible to represent all ids in range in a bitmap, compact and count + if (state.compaction != 0) + { + // select element in compacted table, decrease range and recompact + u32_t i, min_i = 0; + u8_t *map = (u8_t *)fs->work; + u8_t min_count = 0xff; + + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t); i++) + { + if (map[i] < min_count) + { + min_count = map[i]; + min_i = i; + if (min_count == 0) + { + break; + } + } + } + + if (min_count == state.compaction) + { + // there are no free objids! + SPIFFS_DBG("free_obj_id: compacted table is full\n"); + return SPIFFS_ERR_FULL; + } + + SPIFFS_DBG("free_obj_id: COMP select index:" _SPIPRIi " min_count:" _SPIPRIi " min:" _SPIPRIid " max:" _SPIPRIid " compact:" _SPIPRIi "\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); + + if (min_count == 0) + { + // no id in this range, skip compacting and use directly + *obj_id = min_i * state.compaction + state.min_obj_id; + return SPIFFS_OK; + } + else + { + SPIFFS_DBG("free_obj_id: COMP SEL chunk:" _SPIPRIi " min:" _SPIPRIid " -> " _SPIPRIid "\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); + state.min_obj_id += min_i * state.compaction; + state.max_obj_id = state.min_obj_id + state.compaction; + // decrease compaction + } + if ((state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8)) + { + // no need for compacting, use bitmap + continue; + } + } + // in a work memory of log_page_size bytes, we may fit in log_page_size ids + // todo what if compaction is > 255 - then we cannot fit it in a byte + state.compaction = (state.max_obj_id - state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); + SPIFFS_DBG("free_obj_id: COMP min:" _SPIPRIid " max:" _SPIPRIid " compact:" _SPIPRIi "\n", state.min_obj_id, state.max_obj_id, state.compaction); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, &state, 0, 0, 0); + if (res == SPIFFS_VIS_END) + { + res = SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + state.conflicting_name = 0; // searched for conflicting name once, no need to do it again + } + } + + return res; + } #endif // !SPIFFS_READ_ONLY #if SPIFFS_TEMPORAL_FD_CACHE -// djb2 hash -static u32_t spiffs_hash(spiffs *fs, const u8_t *name) { - (void)fs; - u32_t hash = 5381; - u8_t c; - int i = 0; - while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) { - hash = (hash * 33) ^ c; - } - return hash; -} + // djb2 hash + static u32_t spiffs_hash(spiffs *fs, const u8_t *name) + { + (void)fs; + u32_t hash = 5381; + u8_t c; + int i = 0; + while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) + { + hash = (hash * 33) ^ c; + } + return hash; + } #endif -s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) { + s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) + { #if SPIFFS_TEMPORAL_FD_CACHE - u32_t i; - u16_t min_score = 0xffff; - u32_t cand_ix = (u32_t)-1; - u32_t name_hash = name ? spiffs_hash(fs, (const u8_t *)name) : 0; - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - - if (name) { - // first, decrease score of all closed descriptors - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - if (cur_fd->file_nbr == 0) { - if (cur_fd->score > 1) { // score == 0 indicates never used fd - cur_fd->score--; - } - } - } - } - - // find the free fd with least score or name match - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - if (cur_fd->file_nbr == 0) { - if (name && cur_fd->name_hash == name_hash) { - cand_ix = i; - break; - } - if (cur_fd->score < min_score) { - min_score = cur_fd->score; - cand_ix = i; - } - } - } - - if (cand_ix != (u32_t)-1) { - spiffs_fd *cur_fd = &fds[cand_ix]; - if (name) { - if (cur_fd->name_hash == name_hash && cur_fd->score > 0) { - // opened an fd with same name hash, assume same file - // set search point to saved obj index page and hope we have a correct match directly - // when start searching - if not, we will just keep searching until it is found - fs->cursor_block_ix = SPIFFS_BLOCK_FOR_PAGE(fs, cur_fd->objix_hdr_pix); - fs->cursor_obj_lu_entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, cur_fd->objix_hdr_pix); - // update score - if (cur_fd->score < 0xffff-SPIFFS_TEMPORAL_CACHE_HIT_SCORE) { - cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE; - } else { - cur_fd->score = 0xffff; - } - } else { - // no hash hit, restore this fd to initial state - cur_fd->score = SPIFFS_TEMPORAL_CACHE_HIT_SCORE; - cur_fd->name_hash = name_hash; - } - } - cur_fd->file_nbr = cand_ix+1; - *fd = cur_fd; - return SPIFFS_OK; - } else { - return SPIFFS_ERR_OUT_OF_FILE_DESCS; - } + u32_t i; + u16_t min_score = 0xffff; + u32_t cand_ix = (u32_t) -1; + u32_t name_hash = name ? spiffs_hash(fs, (const u8_t *)name) : 0; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + + if (name) + { + // first, decrease score of all closed descriptors + for (i = 0; i < fs->fd_count; i++) + { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) + { + if (cur_fd->score > 1) // score == 0 indicates never used fd + { + cur_fd->score--; + } + } + } + } + + // find the free fd with least score or name match + for (i = 0; i < fs->fd_count; i++) + { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) + { + if (name && cur_fd->name_hash == name_hash) + { + cand_ix = i; + break; + } + if (cur_fd->score < min_score) + { + min_score = cur_fd->score; + cand_ix = i; + } + } + } + + if (cand_ix != (u32_t) -1) + { + spiffs_fd *cur_fd = &fds[cand_ix]; + if (name) + { + if (cur_fd->name_hash == name_hash && cur_fd->score > 0) + { + // opened an fd with same name hash, assume same file + // set search point to saved obj index page and hope we have a correct match directly + // when start searching - if not, we will just keep searching until it is found + fs->cursor_block_ix = SPIFFS_BLOCK_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + fs->cursor_obj_lu_entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + // update score + if (cur_fd->score < 0xffff - SPIFFS_TEMPORAL_CACHE_HIT_SCORE) + { + cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + } + else + { + cur_fd->score = 0xffff; + } + } + else + { + // no hash hit, restore this fd to initial state + cur_fd->score = SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + cur_fd->name_hash = name_hash; + } + } + cur_fd->file_nbr = cand_ix + 1; + *fd = cur_fd; + return SPIFFS_OK; + } + else + { + return SPIFFS_ERR_OUT_OF_FILE_DESCS; + } #else - (void)name; - u32_t i; - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - if (cur_fd->file_nbr == 0) { - cur_fd->file_nbr = i+1; - *fd = cur_fd; - return SPIFFS_OK; - } - } - return SPIFFS_ERR_OUT_OF_FILE_DESCS; + (void)name; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) + { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) + { + cur_fd->file_nbr = i + 1; + *fd = cur_fd; + return SPIFFS_OK; + } + } + return SPIFFS_ERR_OUT_OF_FILE_DESCS; #endif -} - -s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) { - if (f <= 0 || f > (s16_t)fs->fd_count) { - return SPIFFS_ERR_BAD_DESCRIPTOR; - } - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - spiffs_fd *fd = &fds[f-1]; - if (fd->file_nbr == 0) { - return SPIFFS_ERR_FILE_CLOSED; - } - fd->file_nbr = 0; + } + + s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) + { + if (f <= 0 || f > (s16_t)fs->fd_count) + { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + spiffs_fd *fd = &fds[f - 1]; + if (fd->file_nbr == 0) + { + return SPIFFS_ERR_FILE_CLOSED; + } + fd->file_nbr = 0; #if SPIFFS_IX_MAP - fd->ix_map = 0; + fd->ix_map = 0; #endif - return SPIFFS_OK; -} - -s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) { - if (f <= 0 || f > (s16_t)fs->fd_count) { - return SPIFFS_ERR_BAD_DESCRIPTOR; - } - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - *fd = &fds[f-1]; - if ((*fd)->file_nbr == 0) { - return SPIFFS_ERR_FILE_CLOSED; - } - return SPIFFS_OK; -} + return SPIFFS_OK; + } + + s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) + { + if (f <= 0 || f > (s16_t)fs->fd_count) + { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + *fd = &fds[f - 1]; + if ((*fd)->file_nbr == 0) + { + return SPIFFS_ERR_FILE_CLOSED; + } + return SPIFFS_OK; + } #if SPIFFS_TEMPORAL_FD_CACHE -void spiffs_fd_temporal_cache_rehash( - spiffs *fs, - const char *old_path, - const char *new_path) { - u32_t i; - u32_t old_hash = spiffs_hash(fs, (const u8_t *)old_path); - u32_t new_hash = spiffs_hash(fs, (const u8_t *)new_path); - spiffs_fd *fds = (spiffs_fd *)fs->fd_space; - for (i = 0; i < fs->fd_count; i++) { - spiffs_fd *cur_fd = &fds[i]; - if (cur_fd->score > 0 && cur_fd->name_hash == old_hash) { - cur_fd->name_hash = new_hash; + void spiffs_fd_temporal_cache_rehash( + spiffs *fs, + const char *old_path, + const char *new_path) + { + u32_t i; + u32_t old_hash = spiffs_hash(fs, (const u8_t *)old_path); + u32_t new_hash = spiffs_hash(fs, (const u8_t *)new_path); + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) + { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->score > 0 && cur_fd->name_hash == old_hash) + { + cur_fd->name_hash = new_hash; + } + } } - } -} #endif }; diff --git a/cores/esp8266/spiffs/spiffs_nucleus.h b/cores/esp8266/spiffs/spiffs_nucleus.h index cc195c7eca..63efc72630 100644 --- a/cores/esp8266/spiffs/spiffs_nucleus.h +++ b/cores/esp8266/spiffs/spiffs_nucleus.h @@ -1,112 +1,112 @@ /* - * spiffs_nucleus.h - * - * Created on: Jun 15, 2013 - * Author: petera - */ - -/* SPIFFS layout - * - * spiffs is designed for following spi flash characteristics: - * - only big areas of data (blocks) can be erased - * - erasing resets all bits in a block to ones - * - writing pulls ones to zeroes - * - zeroes cannot be pulled to ones, without erase - * - wear leveling - * - * spiffs is also meant to be run on embedded, memory constraint devices. - * - * Entire area is divided in blocks. Entire area is also divided in pages. - * Each block contains same number of pages. A page cannot be erased, but a - * block can be erased. - * - * Entire area must be block_size * x - * page_size must be block_size / (2^y) where y > 2 - * - * ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes - * - * BLOCK 0 PAGE 0 object lookup 1 - * PAGE 1 object lookup 2 - * ... - * PAGE n-1 object lookup n - * PAGE n object data 1 - * PAGE n+1 object data 2 - * ... - * PAGE n+m-1 object data m - * - * BLOCK 1 PAGE n+m object lookup 1 - * PAGE n+m+1 object lookup 2 - * ... - * PAGE 2n+m-1 object lookup n - * PAGE 2n+m object data 1 - * PAGE 2n+m object data 2 - * ... - * PAGE 2n+2m-1 object data m - * ... - * - * n is number of object lookup pages, which is number of pages needed to index all pages - * in a block by object id - * : block_size / page_size * sizeof(obj_id) / page_size - * m is number data pages, which is number of pages in block minus number of lookup pages - * : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size - * thus, n+m is total number of pages in a block - * : block_size / page_size - * - * ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256 - * - * Object lookup pages contain object id entries. Each entry represent the corresponding - * data page. - * Assuming a 16 bit object id, an object id being 0xffff represents a free page. - * An object id being 0x0000 represents a deleted page. - * - * ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff .. - * page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff .. - * page 2 : data : data for object id 0008 - * page 3 : data : data for object id 0001 - * page 4 : data : data for object id 0aaa - * ... - * - * - * Object data pages can be either object index pages or object content. - * All object data pages contains a data page header, containing object id and span index. - * The span index denotes the object page ordering amongst data pages with same object id. - * This applies to both object index pages (when index spans more than one page of entries), - * and object data pages. - * An object index page contains page entries pointing to object content page. The entry index - * in a object index page correlates to the span index in the actual object data page. - * The first object index page (span index 0) is called object index header page, and also - * contains object flags (directory/file), size, object name etc. - * - * ex: - * BLOCK 1 - * PAGE 256: objectl lookup page 1 - * [*123] [ 123] [ 123] [ 123] - * [ 123] [*123] [ 123] [ 123] - * [free] [free] [free] [free] ... - * PAGE 257: objectl lookup page 2 - * [free] [free] [free] [free] ... - * PAGE 258: object index page (header) - * obj.id:0123 span.ix:0000 flags:INDEX - * size:1600 name:ex.txt type:file - * [259] [260] [261] [262] - * PAGE 259: object data page - * obj.id:0123 span.ix:0000 flags:DATA - * PAGE 260: object data page - * obj.id:0123 span.ix:0001 flags:DATA - * PAGE 261: object data page - * obj.id:0123 span.ix:0002 flags:DATA - * PAGE 262: object data page - * obj.id:0123 span.ix:0003 flags:DATA - * PAGE 263: object index page - * obj.id:0123 span.ix:0001 flags:INDEX - * [264] [265] [fre] [fre] - * [fre] [fre] [fre] [fre] - * PAGE 264: object data page - * obj.id:0123 span.ix:0004 flags:DATA - * PAGE 265: object data page - * obj.id:0123 span.ix:0005 flags:DATA - * - */ + spiffs_nucleus.h + + Created on: Jun 15, 2013 + Author: petera +*/ + +/* SPIFFS layout + + spiffs is designed for following spi flash characteristics: + - only big areas of data (blocks) can be erased + - erasing resets all bits in a block to ones + - writing pulls ones to zeroes + - zeroes cannot be pulled to ones, without erase + - wear leveling + + spiffs is also meant to be run on embedded, memory constraint devices. + + Entire area is divided in blocks. Entire area is also divided in pages. + Each block contains same number of pages. A page cannot be erased, but a + block can be erased. + + Entire area must be block_size * x + page_size must be block_size / (2^y) where y > 2 + + ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes + + BLOCK 0 PAGE 0 object lookup 1 + PAGE 1 object lookup 2 + ... + PAGE n-1 object lookup n + PAGE n object data 1 + PAGE n+1 object data 2 + ... + PAGE n+m-1 object data m + + BLOCK 1 PAGE n+m object lookup 1 + PAGE n+m+1 object lookup 2 + ... + PAGE 2n+m-1 object lookup n + PAGE 2n+m object data 1 + PAGE 2n+m object data 2 + ... + PAGE 2n+2m-1 object data m + ... + + n is number of object lookup pages, which is number of pages needed to index all pages + in a block by object id + : block_size / page_size * sizeof(obj_id) / page_size + m is number data pages, which is number of pages in block minus number of lookup pages + : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size + thus, n+m is total number of pages in a block + : block_size / page_size + + ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256 + + Object lookup pages contain object id entries. Each entry represent the corresponding + data page. + Assuming a 16 bit object id, an object id being 0xffff represents a free page. + An object id being 0x0000 represents a deleted page. + + ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff .. + page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff .. + page 2 : data : data for object id 0008 + page 3 : data : data for object id 0001 + page 4 : data : data for object id 0aaa + ... + + + Object data pages can be either object index pages or object content. + All object data pages contains a data page header, containing object id and span index. + The span index denotes the object page ordering amongst data pages with same object id. + This applies to both object index pages (when index spans more than one page of entries), + and object data pages. + An object index page contains page entries pointing to object content page. The entry index + in a object index page correlates to the span index in the actual object data page. + The first object index page (span index 0) is called object index header page, and also + contains object flags (directory/file), size, object name etc. + + ex: + BLOCK 1 + PAGE 256: objectl lookup page 1 + [*123] [ 123] [ 123] [ 123] + [ 123] [*123] [ 123] [ 123] + [free] [free] [free] [free] ... + PAGE 257: objectl lookup page 2 + [free] [free] [free] [free] ... + PAGE 258: object index page (header) + obj.id:0123 span.ix:0000 flags:INDEX + size:1600 name:ex.txt type:file + [259] [260] [261] [262] + PAGE 259: object data page + obj.id:0123 span.ix:0000 flags:DATA + PAGE 260: object data page + obj.id:0123 span.ix:0001 flags:DATA + PAGE 261: object data page + obj.id:0123 span.ix:0002 flags:DATA + PAGE 262: object data page + obj.id:0123 span.ix:0003 flags:DATA + PAGE 263: object index page + obj.id:0123 span.ix:0001 flags:INDEX + [264] [265] [fre] [fre] + [fre] [fre] [fre] [fre] + PAGE 264: object data page + obj.id:0123 span.ix:0004 flags:DATA + PAGE 265: object data page + obj.id:0123 span.ix:0005 flags:DATA + +*/ #ifndef SPIFFS_NUCLEUS_H_ #define SPIFFS_NUCLEUS_H_ @@ -148,15 +148,15 @@ extern "C" { #if defined(__GNUC__) || defined(__clang__) - /* For GCC and clang */ +/* For GCC and clang */ #define SPIFFS_PACKED __attribute__((packed)) #elif defined(__ICCARM__) || defined(__CC_ARM) - /* For IAR ARM and Keil MDK-ARM compilers */ -#define SPIFFS_PACKED +/* For IAR ARM and Keil MDK-ARM compilers */ +#define SPIFFS_PACKED #else - /* Unknown compiler */ -#define SPIFFS_PACKED +/* Unknown compiler */ +#define SPIFFS_PACKED #endif @@ -342,7 +342,7 @@ extern "C" { if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \ if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \ if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH; - //if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED; +//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED; #define SPIFFS_VALIDATE_DATA(ph, objid, spix) \ if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ @@ -402,79 +402,85 @@ extern "C" { ((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page)) // cache page struct -typedef struct { - // cache flags - u8_t flags; - // cache page index - u8_t ix; - // last access of this cache page - u32_t last_access; - union { - // type read cache - struct { - // read cache page index - spiffs_page_ix pix; - }; +typedef struct +{ + // cache flags + u8_t flags; + // cache page index + u8_t ix; + // last access of this cache page + u32_t last_access; + union + { + // type read cache + struct + { + // read cache page index + spiffs_page_ix pix; + }; #if SPIFFS_CACHE_WR - // type write cache - struct { - // write cache - spiffs_obj_id obj_id; - // offset in cache page - u32_t offset; - // size of cache page - u16_t size; - }; + // type write cache + struct + { + // write cache + spiffs_obj_id obj_id; + // offset in cache page + u32_t offset; + // size of cache page + u16_t size; + }; #endif - }; + }; } spiffs_cache_page; // cache struct -typedef struct { - u8_t cpage_count; - u32_t last_access; - u32_t cpage_use_map; - u32_t cpage_use_mask; - u8_t *cpages; +typedef struct +{ + u8_t cpage_count; + u32_t last_access; + u32_t cpage_use_map; + u32_t cpage_use_mask; + u8_t *cpages; } spiffs_cache; #endif // spiffs nucleus file descriptor -typedef struct { - // the filesystem of this descriptor - spiffs *fs; - // number of file descriptor - if 0, the file descriptor is closed - spiffs_file file_nbr; - // object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted - spiffs_obj_id obj_id; - // size of the file - u32_t size; - // cached object index header page index - spiffs_page_ix objix_hdr_pix; - // cached offset object index page index - spiffs_page_ix cursor_objix_pix; - // cached offset object index span index - spiffs_span_ix cursor_objix_spix; - // current absolute offset - u32_t offset; - // current file descriptor offset (cached) - u32_t fdoffset; - // fd flags - spiffs_flags flags; +typedef struct +{ + // the filesystem of this descriptor + spiffs *fs; + // number of file descriptor - if 0, the file descriptor is closed + spiffs_file file_nbr; + // object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted + spiffs_obj_id obj_id; + // size of the file + u32_t size; + // cached object index header page index + spiffs_page_ix objix_hdr_pix; + // cached offset object index page index + spiffs_page_ix cursor_objix_pix; + // cached offset object index span index + spiffs_span_ix cursor_objix_spix; + // current absolute offset + u32_t offset; + // current file descriptor offset (cached) + u32_t fdoffset; + // fd flags + spiffs_flags flags; #if SPIFFS_CACHE_WR - spiffs_cache_page *cache_page; + spiffs_cache_page *cache_page; #endif #if SPIFFS_TEMPORAL_FD_CACHE - // djb2 hash of filename - u32_t name_hash; - // hit score (score == 0 indicates never used fd) - u16_t score; + // djb2 hash of filename + u32_t name_hash; + // hit score (score == 0 indicates never used fd) + u16_t score; #endif #if SPIFFS_IX_MAP - // spiffs index map, if 0 it means unmapped - spiffs_ix_map *ix_map; + // spiffs index map, if 0 it means unmapped + spiffs_ix_map *ix_map; #endif } spiffs_fd; @@ -484,46 +490,48 @@ typedef struct { // page header, part of each page except object lookup pages // NB: this is always aligned when the data page is an object index, // as in this case struct spiffs_page_object_ix is used -typedef struct SPIFFS_PACKED { - // object id - spiffs_obj_id obj_id; - // object span index - spiffs_span_ix span_ix; - // flags - u8_t flags; +typedef struct SPIFFS_PACKED +{ + // object id + spiffs_obj_id obj_id; + // object span index + spiffs_span_ix span_ix; + // flags + u8_t flags; } spiffs_page_header; // object index header page header typedef struct SPIFFS_PACKED #if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES - __attribute(( aligned(sizeof(spiffs_page_ix)) )) +__attribute((aligned(sizeof(spiffs_page_ix)))) #endif { - // common page header - spiffs_page_header p_hdr; - // alignment - u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; - // size of object - u32_t size; - // type of object - spiffs_obj_type type; - // name of object - u8_t name[SPIFFS_OBJ_NAME_LEN]; + // common page header + spiffs_page_header p_hdr; + // alignment + u8_t _align[4 - ((sizeof(spiffs_page_header) & 3) == 0 ? 4 : (sizeof(spiffs_page_header) & 3))]; + // size of object + u32_t size; + // type of object + spiffs_obj_type type; + // name of object + u8_t name[SPIFFS_OBJ_NAME_LEN]; #if SPIFFS_OBJ_META_LEN - // metadata. not interpreted by SPIFFS in any way. - u8_t meta[SPIFFS_OBJ_META_LEN]; + // metadata. not interpreted by SPIFFS in any way. + u8_t meta[SPIFFS_OBJ_META_LEN]; #endif } spiffs_page_object_ix_header; // object index page header -typedef struct SPIFFS_PACKED { - spiffs_page_header p_hdr; - u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; +typedef struct SPIFFS_PACKED +{ + spiffs_page_header p_hdr; + u8_t _align[4 - ((sizeof(spiffs_page_header) & 3) == 0 ? 4 : (sizeof(spiffs_page_header) & 3))]; } spiffs_page_object_ix; // callback func for object lookup visitor typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, - const void *user_const_p, void *user_var_p); + const void *user_const_p, void *user_var_p); #if SPIFFS_CACHE diff --git a/cores/esp8266/spiffs_api.cpp b/cores/esp8266/spiffs_api.cpp index f3fcfa2354..9cf768fdb0 100644 --- a/cores/esp8266/spiffs_api.cpp +++ b/cores/esp8266/spiffs_api.cpp @@ -1,50 +1,54 @@ /* - spiffs_api.cpp - file system wrapper for SPIFFS - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + spiffs_api.cpp - file system wrapper for SPIFFS + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This code was influenced by NodeMCU and Sming libraries, and first version of - Arduino wrapper written by Hristo Gochkov. + This code was influenced by NodeMCU and Sming libraries, and first version of + Arduino wrapper written by Hristo Gochkov. - This file is part of the esp8266 core for Arduino environment. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include "spiffs_api.h" using namespace fs; FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode) { - if (!isSpiffsFilenameValid(path)) { + if (!isSpiffsFilenameValid(path)) + { DEBUGV("SPIFFSImpl::open: invalid path=`%s` \r\n", path); return FileImplPtr(); } int mode = getSpiffsMode(openMode, accessMode); int fd = SPIFFS_open(&_fs, path, mode, 0); - if (fd < 0 && _fs.err_code == SPIFFS_ERR_DELETED && (openMode & OM_CREATE)) { + if (fd < 0 && _fs.err_code == SPIFFS_ERR_DELETED && (openMode & OM_CREATE)) + { DEBUGV("SPIFFSImpl::open: fd=%d path=`%s` openMode=%d accessMode=%d err=%d, trying to remove\r\n", fd, path, openMode, accessMode, _fs.err_code); auto rc = SPIFFS_remove(&_fs, path); - if (rc != SPIFFS_OK) { + if (rc != SPIFFS_OK) + { DEBUGV("SPIFFSImpl::open: SPIFFS_ERR_DELETED, but failed to remove path=`%s` openMode=%d accessMode=%d err=%d\r\n", path, openMode, accessMode, _fs.err_code); return FileImplPtr(); } fd = SPIFFS_open(&_fs, path, mode, 0); } - if (fd < 0) { + if (fd < 0) + { DEBUGV("SPIFFSImpl::open: fd=%d path=`%s` openMode=%d accessMode=%d err=%d\r\n", fd, path, openMode, accessMode, _fs.err_code); return FileImplPtr(); @@ -54,7 +58,8 @@ FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode acc bool SPIFFSImpl::exists(const char* path) { - if (!isSpiffsFilenameValid(path)) { + if (!isSpiffsFilenameValid(path)) + { DEBUGV("SPIFFSImpl::exists: invalid path=`%s` \r\n", path); return false; } @@ -63,15 +68,17 @@ bool SPIFFSImpl::exists(const char* path) return rc == SPIFFS_OK; } -DirImplPtr SPIFFSImpl::openDir(const char* path) +DirImplPtr SPIFFSImpl::openDir(const char* path) { - if (strlen(path) > 0 && !isSpiffsFilenameValid(path)) { + if (strlen(path) > 0 && !isSpiffsFilenameValid(path)) + { DEBUGV("SPIFFSImpl::openDir: invalid path=`%s` \r\n", path); return DirImplPtr(); } spiffs_DIR dir; spiffs_DIR* result = SPIFFS_opendir(&_fs, path, &dir); - if (!result) { + if (!result) + { DEBUGV("SPIFFSImpl::openDir: path=`%s` err=%d\r\n", path, _fs.err_code); return DirImplPtr(); } @@ -81,19 +88,24 @@ DirImplPtr SPIFFSImpl::openDir(const char* path) int getSpiffsMode(OpenMode openMode, AccessMode accessMode) { int mode = 0; - if (openMode & OM_CREATE) { + if (openMode & OM_CREATE) + { mode |= SPIFFS_CREAT; } - if (openMode & OM_APPEND) { + if (openMode & OM_APPEND) + { mode |= SPIFFS_APPEND; } - if (openMode & OM_TRUNCATE) { + if (openMode & OM_TRUNCATE) + { mode |= SPIFFS_TRUNC; } - if (accessMode & AM_READ) { + if (accessMode & AM_READ) + { mode |= SPIFFS_RDONLY; } - if (accessMode & AM_WRITE) { + if (accessMode & AM_WRITE) + { mode |= SPIFFS_WRONLY; } return mode; @@ -101,7 +113,8 @@ int getSpiffsMode(OpenMode openMode, AccessMode accessMode) bool isSpiffsFilenameValid(const char* name) { - if (name == nullptr) { + if (name == nullptr) + { return false; } auto len = strlen(name); diff --git a/cores/esp8266/spiffs_api.h b/cores/esp8266/spiffs_api.h index 95600a22ad..126b5fc754 100644 --- a/cores/esp8266/spiffs_api.h +++ b/cores/esp8266/spiffs_api.h @@ -2,36 +2,36 @@ #define spiffs_api_h /* - spiffs_api.h - file system wrapper for SPIFFS - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + spiffs_api.h - file system wrapper for SPIFFS + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This code was influenced by NodeMCU and Sming libraries, and first version of - Arduino wrapper written by Hristo Gochkov. + This code was influenced by NodeMCU and Sming libraries, and first version of + Arduino wrapper written by Hristo Gochkov. - This file is part of the esp8266 core for Arduino environment. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include #include "FS.h" #undef max #undef min #include "FSImpl.h" extern "C" { - #include "spiffs/spiffs.h" - #include "spiffs/spiffs_nucleus.h" +#include "spiffs/spiffs.h" +#include "spiffs/spiffs_nucleus.h" }; #include "debug.h" #include "flash_utils.h" @@ -65,10 +65,10 @@ class SPIFFSImpl : public FSImpl public: SPIFFSImpl(uint32_t start, uint32_t size, uint32_t pageSize, uint32_t blockSize, uint32_t maxOpenFds) : _start(start) - , _size(size) - , _pageSize(pageSize) - , _blockSize(blockSize) - , _maxOpenFds(maxOpenFds) + , _size(size) + , _pageSize(pageSize) + , _blockSize(blockSize) + , _maxOpenFds(maxOpenFds) { memset(&_fs, 0, sizeof(_fs)); } @@ -79,16 +79,19 @@ class SPIFFSImpl : public FSImpl bool rename(const char* pathFrom, const char* pathTo) override { - if (!isSpiffsFilenameValid(pathFrom)) { + if (!isSpiffsFilenameValid(pathFrom)) + { DEBUGV("SPIFFSImpl::rename: invalid pathFrom=`%s`\r\n", pathFrom); return false; } - if (!isSpiffsFilenameValid(pathTo)) { + if (!isSpiffsFilenameValid(pathTo)) + { DEBUGV("SPIFFSImpl::rename: invalid pathTo=`%s` \r\n", pathTo); return false; } auto rc = SPIFFS_rename(&_fs, pathFrom, pathTo); - if (rc != SPIFFS_OK) { + if (rc != SPIFFS_OK) + { DEBUGV("SPIFFS_rename: rc=%d, from=`%s`, to=`%s`\r\n", rc, pathFrom, pathTo); return false; @@ -104,7 +107,8 @@ class SPIFFSImpl : public FSImpl info.maxPathLength = SPIFFS_OBJ_NAME_LEN; uint32_t totalBytes, usedBytes; auto rc = SPIFFS_info(&_fs, &totalBytes, &usedBytes); - if (rc != SPIFFS_OK) { + if (rc != SPIFFS_OK) + { DEBUGV("SPIFFS_info: rc=%d, err=%d\r\n", rc, _fs.err_code); return false; } @@ -115,12 +119,14 @@ class SPIFFSImpl : public FSImpl bool remove(const char* path) override { - if (!isSpiffsFilenameValid(path)) { + if (!isSpiffsFilenameValid(path)) + { DEBUGV("SPIFFSImpl::remove: invalid path=`%s`\r\n", path); return false; } auto rc = SPIFFS_remove(&_fs, path); - if (rc != SPIFFS_OK) { + if (rc != SPIFFS_OK) + { DEBUGV("SPIFFS_remove: rc=%d path=`%s`\r\n", rc, path); return false; } @@ -141,40 +147,49 @@ class SPIFFSImpl : public FSImpl bool setConfig(const FSConfig &cfg) override { - if ((cfg._type != SPIFFSConfig::fsid::FSId) || (SPIFFS_mounted(&_fs) != 0)) { + if ((cfg._type != SPIFFSConfig::fsid::FSId) || (SPIFFS_mounted(&_fs) != 0)) + { return false; } _cfg = *static_cast(&cfg); - return true; + return true; } bool begin() override { - if (SPIFFS_mounted(&_fs) != 0) { + if (SPIFFS_mounted(&_fs) != 0) + { return true; } - if (_size == 0) { + if (_size == 0) + { DEBUGV("SPIFFS size is zero"); return false; } - if (_tryMount()) { + if (_tryMount()) + { return true; } - if (_cfg._autoFormat) { + if (_cfg._autoFormat) + { auto rc = SPIFFS_format(&_fs); - if (rc != SPIFFS_OK) { + if (rc != SPIFFS_OK) + { DEBUGV("SPIFFS_format: rc=%d, err=%d\r\n", rc, _fs.err_code); return false; } return _tryMount(); - } else { + } + else + { return false; } } void end() override { - if (SPIFFS_mounted(&_fs) == 0) { + if (SPIFFS_mounted(&_fs) == 0) + { return; } SPIFFS_unmount(&_fs); @@ -185,23 +200,27 @@ class SPIFFSImpl : public FSImpl bool format() override { - if (_size == 0) { + if (_size == 0) + { DEBUGV("SPIFFS size is zero"); return false; } bool wasMounted = (SPIFFS_mounted(&_fs) != 0); - if (_tryMount()) { + if (_tryMount()) + { SPIFFS_unmount(&_fs); } auto rc = SPIFFS_format(&_fs); - if (rc != SPIFFS_OK) { + if (rc != SPIFFS_OK) + { DEBUGV("SPIFFS_format: rc=%d, err=%d\r\n", rc, _fs.err_code); return false; } - if (wasMounted) { + if (wasMounted) + { return _tryMount(); } @@ -210,7 +229,7 @@ class SPIFFSImpl : public FSImpl bool gc() override { - return SPIFFS_gc_quick( &_fs, 0 ) == SPIFFS_OK; + return SPIFFS_gc_quick(&_fs, 0) == SPIFFS_OK; } protected: @@ -237,22 +256,26 @@ class SPIFFSImpl : public FSImpl config.log_page_size = _pageSize; - if (((uint32_t) std::numeric_limits::max()) < (_size / _blockSize)) { + if (((uint32_t) std::numeric_limits::max()) < (_size / _blockSize)) + { DEBUGV("spiffs_block_ix type too small"); abort(); } - if (((uint32_t) std::numeric_limits::max()) < (_size / _pageSize)) { + if (((uint32_t) std::numeric_limits::max()) < (_size / _pageSize)) + { DEBUGV("spiffs_page_ix type too small"); abort(); } - if (((uint32_t) std::numeric_limits::max()) < (2 + (_size / (2*_pageSize))*2)) { + if (((uint32_t) std::numeric_limits::max()) < (2 + (_size / (2 * _pageSize)) * 2)) + { DEBUGV("spiffs_obj_id type too small"); abort(); } - if (((uint32_t) std::numeric_limits::max()) < (_size / _pageSize - 1)) { + if (((uint32_t) std::numeric_limits::max()) < (_size / _pageSize - 1)) + { DEBUGV("spiffs_span_ix type too small"); abort(); } @@ -267,7 +290,8 @@ class SPIFFSImpl : public FSImpl size_t fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&_fs, _maxOpenFds); size_t cacheBufSize = SPIFFS_buffer_bytes_for_cache(&_fs, _maxOpenFds); - if (!_workBuf) { + if (!_workBuf) + { DEBUGV("SPIFFSImpl: allocating %zd+%zd+%zd=%zd bytes\r\n", workBufSize, fdsBufSize, cacheBufSize, workBufSize + fdsBufSize + cacheBufSize); @@ -324,7 +348,7 @@ class SPIFFSFileImpl : public FileImpl SPIFFSFileImpl(SPIFFSImpl* fs, spiffs_file fd) : _fs(fs) , _fd(fd) - , _written(false) + , _written(false) { memset(&_stat, 0, sizeof(_stat)); _getStat(); @@ -340,7 +364,8 @@ class SPIFFSFileImpl : public FileImpl CHECKFD(); auto result = SPIFFS_write(_fs->getFs(), _fd, (void*) buf, size); - if (result < 0) { + if (result < 0) + { DEBUGV("SPIFFS_write rc=%d\r\n", result); return 0; } @@ -352,7 +377,8 @@ class SPIFFSFileImpl : public FileImpl { CHECKFD(); auto result = SPIFFS_read(_fs->getFs(), _fd, (void*) buf, size); - if (result < 0) { + if (result < 0) + { DEBUGV("SPIFFS_read rc=%d\r\n", result); return 0; } @@ -365,7 +391,8 @@ class SPIFFSFileImpl : public FileImpl CHECKFD(); auto rc = SPIFFS_fflush(_fs->getFs(), _fd); - if (rc < 0) { + if (rc < 0) + { DEBUGV("SPIFFS_fflush rc=%d\r\n", rc); } _written = true; @@ -376,11 +403,13 @@ class SPIFFSFileImpl : public FileImpl CHECKFD(); int32_t offset = static_cast(pos); - if (mode == SeekEnd) { + if (mode == SeekEnd) + { offset = -offset; } auto rc = SPIFFS_lseek(_fs->getFs(), _fd, offset, (int) mode); - if (rc < 0) { + if (rc < 0) + { DEBUGV("SPIFFS_lseek rc=%d\r\n", rc); return false; } @@ -393,7 +422,8 @@ class SPIFFSFileImpl : public FileImpl CHECKFD(); auto result = SPIFFS_lseek(_fs->getFs(), _fd, 0, SPIFFS_SEEK_CUR); - if (result < 0) { + if (result < 0) + { DEBUGV("SPIFFS_tell rc=%d\r\n", result); return 0; } @@ -404,7 +434,8 @@ class SPIFFSFileImpl : public FileImpl size_t size() const override { CHECKFD(); - if (_written) { + if (_written) + { _getStat(); } return _stat.size; @@ -414,10 +445,13 @@ class SPIFFSFileImpl : public FileImpl { CHECKFD(); spiffs_fd *sfd; - if (spiffs_fd_get(_fs->getFs(), _fd, &sfd) == SPIFFS_OK) { + if (spiffs_fd_get(_fs->getFs(), _fd, &sfd) == SPIFFS_OK) + { return SPIFFS_OK == spiffs_object_truncate(sfd, size, 0); - } else { - return false; + } + else + { + return false; } } @@ -458,7 +492,8 @@ class SPIFFSFileImpl : public FileImpl { CHECKFD(); auto rc = SPIFFS_fstat(_fs->getFs(), _fd, &_stat); - if (rc != SPIFFS_OK) { + if (rc != SPIFFS_OK) + { DEBUGV("SPIFFS_fstat rc=%d\r\n", rc); memset(&_stat, 0, sizeof(_stat)); } @@ -490,13 +525,15 @@ class SPIFFSDirImpl : public DirImpl FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) override { - if (!_valid) { + if (!_valid) + { return FileImplPtr(); } int mode = getSpiffsMode(openMode, accessMode); auto fs = _fs->getFs(); spiffs_file fd = SPIFFS_open_by_dirent(fs, &_dirent, mode, 0); - if (fd < 0) { + if (fd < 0) + { DEBUGV("SPIFFSDirImpl::openFile: fd=%d path=`%s` openMode=%d accessMode=%d err=%d\r\n", fd, _dirent.name, openMode, accessMode, fs->err_code); return FileImplPtr(); @@ -506,7 +543,8 @@ class SPIFFSDirImpl : public DirImpl const char* fileName() override { - if (!_valid) { + if (!_valid) + { return nullptr; } @@ -515,7 +553,8 @@ class SPIFFSDirImpl : public DirImpl size_t fileSize() override { - if (!_valid) { + if (!_valid) + { return 0; } @@ -537,10 +576,11 @@ class SPIFFSDirImpl : public DirImpl bool next() override { const int n = _pattern.length(); - do { + do + { spiffs_dirent* result = SPIFFS_readdir(&_dir, &_dirent); _valid = (result != nullptr); - } while(_valid && strncmp((const char*) _dirent.name, _pattern.c_str(), n) != 0); + } while (_valid && strncmp((const char*) _dirent.name, _pattern.c_str(), n) != 0); return _valid; } diff --git a/cores/esp8266/spiffs_hal.cpp b/cores/esp8266/spiffs_hal.cpp index 2d66bd54df..c0d141ad9d 100644 --- a/cores/esp8266/spiffs_hal.cpp +++ b/cores/esp8266/spiffs_hal.cpp @@ -1,22 +1,22 @@ /* - spiffs_hal.cpp - SPI read/write/erase functions for SPIFFS. - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + spiffs_hal.cpp - SPI read/write/erase functions for SPIFFS. + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include #include @@ -29,55 +29,63 @@ extern "C" { #include "spi_flash.h" } /* - spi_flash_read function requires flash address to be aligned on word boundary. - We take care of this by reading first and last words separately and memcpy - relevant bytes into result buffer. - -alignment: 012301230123012301230123 -bytes requested: -------***********------ -read directly: --------xxxxxxxx-------- -read pre: ----aaaa---------------- -read post: ----------------bbbb---- -alignedBegin: ^ -alignedEnd: ^ + spi_flash_read function requires flash address to be aligned on word boundary. + We take care of this by reading first and last words separately and memcpy + relevant bytes into result buffer. + + alignment: 012301230123012301230123 + bytes requested: -------***********------ + read directly: --------xxxxxxxx-------- + read pre: ----aaaa---------------- + read post: ----------------bbbb---- + alignedBegin: ^ + alignedEnd: ^ */ -int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) { +int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) +{ optimistic_yield(10000); uint32_t result = SPIFFS_OK; uint32_t alignedBegin = (addr + 3) & (~3); uint32_t alignedEnd = (addr + size) & (~3); - if (alignedEnd < alignedBegin) { + if (alignedEnd < alignedBegin) + { alignedEnd = alignedBegin; } - if (addr < alignedBegin) { + if (addr < alignedBegin) + { uint32_t nb = alignedBegin - addr; uint32_t tmp; - if (!ESP.flashRead(alignedBegin - 4, &tmp, 4)) { + if (!ESP.flashRead(alignedBegin - 4, &tmp, 4)) + { DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n", - __LINE__, addr, size, alignedBegin, alignedEnd); + __LINE__, addr, size, alignedBegin, alignedEnd); return SPIFFS_ERR_INTERNAL; } memcpy(dst, ((uint8_t*) &tmp) + 4 - nb, nb); } - if (alignedEnd != alignedBegin) { - if (!ESP.flashRead(alignedBegin, (uint32_t*) (dst + alignedBegin - addr), - alignedEnd - alignedBegin)) { + if (alignedEnd != alignedBegin) + { + if (!ESP.flashRead(alignedBegin, (uint32_t*)(dst + alignedBegin - addr), + alignedEnd - alignedBegin)) + { DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n", - __LINE__, addr, size, alignedBegin, alignedEnd); + __LINE__, addr, size, alignedBegin, alignedEnd); return SPIFFS_ERR_INTERNAL; } } - if (addr + size > alignedEnd) { + if (addr + size > alignedEnd) + { uint32_t nb = addr + size - alignedEnd; uint32_t tmp; - if (!ESP.flashRead(alignedEnd, &tmp, 4)) { + if (!ESP.flashRead(alignedEnd, &tmp, 4)) + { DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n", - __LINE__, addr, size, alignedBegin, alignedEnd); + __LINE__, addr, size, alignedBegin, alignedEnd); return SPIFFS_ERR_INTERNAL; } @@ -88,58 +96,68 @@ int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) { } /* - Like spi_flash_read, spi_flash_write has a requirement for flash address to be - aligned. However it also requires RAM address to be aligned as it reads data - in 32-bit words. Flash address (mis-)alignment is handled much the same way - as for reads, but for RAM alignment we have to copy data into a temporary - buffer. The size of this buffer is a tradeoff between number of writes required - and amount of stack required. This is chosen to be 512 bytes here, but might - be adjusted in the future if there are good reasons to do so. + Like spi_flash_read, spi_flash_write has a requirement for flash address to be + aligned. However it also requires RAM address to be aligned as it reads data + in 32-bit words. Flash address (mis-)alignment is handled much the same way + as for reads, but for RAM alignment we have to copy data into a temporary + buffer. The size of this buffer is a tradeoff between number of writes required + and amount of stack required. This is chosen to be 512 bytes here, but might + be adjusted in the future if there are good reasons to do so. */ static const int UNALIGNED_WRITE_BUFFER_SIZE = 512; -int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) { +int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) +{ optimistic_yield(10000); uint32_t alignedBegin = (addr + 3) & (~3); uint32_t alignedEnd = (addr + size) & (~3); - if (alignedEnd < alignedBegin) { + if (alignedEnd < alignedBegin) + { alignedEnd = alignedBegin; } - if (addr < alignedBegin) { + if (addr < alignedBegin) + { uint32_t ofs = alignedBegin - addr; uint32_t nb = (size < ofs) ? size : ofs; uint8_t tmp[4] __attribute__((aligned(4))) = {0xff, 0xff, 0xff, 0xff}; memcpy(tmp + 4 - ofs, src, nb); - if (!ESP.flashWrite(alignedBegin - 4, (uint32_t*) tmp, 4)) { + if (!ESP.flashWrite(alignedBegin - 4, (uint32_t*) tmp, 4)) + { DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n", - __LINE__, addr, size, alignedBegin, alignedEnd); + __LINE__, addr, size, alignedBegin, alignedEnd); return SPIFFS_ERR_INTERNAL; } } - if (alignedEnd != alignedBegin) { - uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr); + if (alignedEnd != alignedBegin) + { + uint32_t* srcLeftover = (uint32_t*)(src + alignedBegin - addr); uint32_t srcAlign = ((uint32_t) srcLeftover) & 3; - if (!srcAlign) { + if (!srcAlign) + { if (!ESP.flashWrite(alignedBegin, (uint32_t*) srcLeftover, - alignedEnd - alignedBegin)) { + alignedEnd - alignedBegin)) + { DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n", - __LINE__, addr, size, alignedBegin, alignedEnd); + __LINE__, addr, size, alignedBegin, alignedEnd); return SPIFFS_ERR_INTERNAL; } } - else { + else + { uint8_t buf[UNALIGNED_WRITE_BUFFER_SIZE]; - for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft; ) { + for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft;) + { size_t willCopy = std::min(sizeLeft, sizeof(buf)); memcpy(buf, srcLeftover, willCopy); - if (!ESP.flashWrite(alignedBegin, (uint32_t*) buf, willCopy)) { + if (!ESP.flashWrite(alignedBegin, (uint32_t*) buf, willCopy)) + { DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n", - __LINE__, addr, size, alignedBegin, alignedEnd); + __LINE__, addr, size, alignedBegin, alignedEnd); return SPIFFS_ERR_INTERNAL; } @@ -150,14 +168,16 @@ int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) { } } - if (addr + size > alignedEnd) { + if (addr + size > alignedEnd) + { uint32_t nb = addr + size - alignedEnd; uint32_t tmp = 0xffffffff; memcpy(&tmp, src + size - nb, nb); - if (!ESP.flashWrite(alignedEnd, &tmp, 4)) { + if (!ESP.flashWrite(alignedEnd, &tmp, 4)) + { DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n", - __LINE__, addr, size, alignedBegin, alignedEnd); + __LINE__, addr, size, alignedBegin, alignedEnd); return SPIFFS_ERR_INTERNAL; } } @@ -165,17 +185,21 @@ int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) { return SPIFFS_OK; } -int32_t spiffs_hal_erase(uint32_t addr, uint32_t size) { +int32_t spiffs_hal_erase(uint32_t addr, uint32_t size) +{ if ((size & (SPI_FLASH_SEC_SIZE - 1)) != 0 || - (addr & (SPI_FLASH_SEC_SIZE - 1)) != 0) { + (addr & (SPI_FLASH_SEC_SIZE - 1)) != 0) + { DEBUGV("_spif_erase called with addr=%x, size=%d\r\n", addr, size); abort(); } const uint32_t sector = addr / SPI_FLASH_SEC_SIZE; const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE; - for (uint32_t i = 0; i < sectorCount; ++i) { + for (uint32_t i = 0; i < sectorCount; ++i) + { optimistic_yield(10000); - if (!ESP.flashEraseSector(sector + i)) { + if (!ESP.flashEraseSector(sector + i)) + { DEBUGV("_spif_erase addr=%x size=%d i=%d\r\n", addr, size, i); return SPIFFS_ERR_INTERNAL; } diff --git a/cores/esp8266/sqrt32.cpp b/cores/esp8266/sqrt32.cpp index 4dbb5d5df6..4be117c615 100644 --- a/cores/esp8266/sqrt32.cpp +++ b/cores/esp8266/sqrt32.cpp @@ -4,57 +4,61 @@ extern "C" { -uint32_t sqrt32 (uint32_t n) -{ - // http://www.codecodex.com/wiki/Calculate_an_integer_square_root#C - // Another very fast algorithm donated by Tristan.Muntsinger@gmail.com - // (note: tested across the full 32 bits range, see comment below) + uint32_t sqrt32(uint32_t n) + { + // http://www.codecodex.com/wiki/Calculate_an_integer_square_root#C + // Another very fast algorithm donated by Tristan.Muntsinger@gmail.com + // (note: tested across the full 32 bits range, see comment below) - // 15 iterations (c=1<<15) + // 15 iterations (c=1<<15) - unsigned int c = 0x8000; - unsigned int g = 0x8000; + unsigned int c = 0x8000; + unsigned int g = 0x8000; - for(;;) - { - if (g*g > n) - g ^= c; - c >>= 1; - if (!c) - return g; - g |= c; + for (;;) + { + if (g * g > n) + { + g ^= c; + } + c >>= 1; + if (!c) + { + return g; + } + g |= c; + } } -} -/* - * tested with: - * + /* + tested with: -#include -#include -#include - -int main (void) -{ - for (uint32_t i = 0; ++i; ) - { - uint32_t sr = sqrt32(i); - uint32_t ifsr = sqrt(i); - if (ifsr != sr) - printf("%d: i%d f%d\n", i, sr, ifsr); + #include + #include + #include - if (!(i & 0xffffff)) + int main (void) { - printf("%i%% (0x%08x)\r", ((i >> 16) * 100) >> 16, i); - fflush(stdout); + for (uint32_t i = 0; ++i; ) + { + uint32_t sr = sqrt32(i); + uint32_t ifsr = sqrt(i); + + if (ifsr != sr) + printf("%d: i%d f%d\n", i, sr, ifsr); + + if (!(i & 0xffffff)) + { + printf("%i%% (0x%08x)\r", ((i >> 16) * 100) >> 16, i); + fflush(stdout); + } + } + + printf("\n"); } - } - printf("\n"); -} - * - */ + */ }; diff --git a/cores/esp8266/stdlib_noniso.h b/cores/esp8266/stdlib_noniso.h index 636cbf437d..6e8f0ebe4c 100644 --- a/cores/esp8266/stdlib_noniso.h +++ b/cores/esp8266/stdlib_noniso.h @@ -1,29 +1,29 @@ -/* - stdlib_noniso.h - nonstandard (but usefull) conversion functions - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +/* + stdlib_noniso.h - nonstandard (but usefull) conversion functions + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef STDLIB_NONISO_H #define STDLIB_NONISO_H #ifdef __cplusplus -extern "C"{ +extern "C" { #endif int atoi(const char *s); @@ -32,15 +32,15 @@ long atol(const char* s); double atof(const char* s); -char* itoa (int val, char *s, int radix); +char* itoa(int val, char *s, int radix); + +char* ltoa(long val, char *s, int radix); -char* ltoa (long val, char *s, int radix); +char* utoa(unsigned int val, char *s, int radix); -char* utoa (unsigned int val, char *s, int radix); +char* ultoa(unsigned long val, char *s, int radix); -char* ultoa (unsigned long val, char *s, int radix); - -char* dtostrf (double val, signed char width, unsigned char prec, char *s); +char* dtostrf(double val, signed char width, unsigned char prec, char *s); void reverse(char* begin, char* end); diff --git a/cores/esp8266/time.cpp b/cores/esp8266/time.cpp index 001b0ec331..3b11de1987 100644 --- a/cores/esp8266/time.cpp +++ b/cores/esp8266/time.cpp @@ -1,20 +1,20 @@ /* - * time.c - ESP8266-specific functions for SNTP - * Copyright (c) 2015 Peter Dobler. All rights reserved. - * This file is part of the esp8266 core for Arduino environment. - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ + time.c - ESP8266-specific functions for SNTP + Copyright (c) 2015 Peter Dobler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ #include #include @@ -28,84 +28,87 @@ extern "C" { #ifndef _TIMEVAL_DEFINED #define _TIMEVAL_DEFINED -struct timeval { - time_t tv_sec; - suseconds_t tv_usec; -}; + struct timeval + { + time_t tv_sec; + suseconds_t tv_usec; + }; #endif -extern char* sntp_asctime(const struct tm *t); -extern struct tm* sntp_localtime(const time_t *clock); -extern uint64_t micros64(); -extern void sntp_set_daylight(int daylight); + extern char* sntp_asctime(const struct tm *t); + extern struct tm* sntp_localtime(const time_t *clock); + extern uint64_t micros64(); + extern void sntp_set_daylight(int daylight); -// time gap in seconds from 01.01.1900 (NTP time) to 01.01.1970 (UNIX time) + // time gap in seconds from 01.01.1900 (NTP time) to 01.01.1970 (UNIX time) #define DIFF1900TO1970 2208988800UL -bool timeshift64_is_set = false; -static uint64_t timeshift64 = 0; + bool timeshift64_is_set = false; + static uint64_t timeshift64 = 0; -void tune_timeshift64 (uint64_t now_us) -{ - timeshift64 = now_us - micros64(); - timeshift64_is_set = true; -} + void tune_timeshift64(uint64_t now_us) + { + timeshift64 = now_us - micros64(); + timeshift64_is_set = true; + } -static void setServer(int id, const char* name_or_ip) -{ - if (name_or_ip) + static void setServer(int id, const char* name_or_ip) { - //TODO: check whether server is given by name or IP - sntp_setservername(id, (char*) name_or_ip); + if (name_or_ip) + { + //TODO: check whether server is given by 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) -{ - (void) unused; - uint64_t m = micros64(); - tp->tv_sec = m / 1000000; - tp->tv_nsec = (m % 1000000) * 1000; - return 0; -} - -time_t time(time_t * t) -{ - time_t seconds = sntp_get_current_timestamp(); - if (t) + + + void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3) { - *t = seconds; + 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(); } - return seconds; -} - -int _gettimeofday_r(struct _reent* unused, struct timeval *tp, void *tzp) -{ - (void) unused; - (void) tzp; - if (tp) + + int clock_gettime(clockid_t unused, struct timespec *tp) + { + (void) unused; + uint64_t m = micros64(); + tp->tv_sec = m / 1000000; + tp->tv_nsec = (m % 1000000) * 1000; + return 0; + } + + time_t time(time_t * t) + { + time_t seconds = sntp_get_current_timestamp(); + if (t) + { + *t = seconds; + } + return seconds; + } + + int _gettimeofday_r(struct _reent* unused, struct timeval *tp, void *tzp) { - if (!timeshift64_is_set) - tune_timeshift64(sntp_get_current_timestamp() * 1000000ULL); - uint64_t currentTime_us = timeshift64 + micros64(); - tp->tv_sec = currentTime_us / 1000000ULL; - tp->tv_usec = currentTime_us % 1000000ULL; + (void) unused; + (void) tzp; + if (tp) + { + if (!timeshift64_is_set) + { + tune_timeshift64(sntp_get_current_timestamp() * 1000000ULL); + } + uint64_t currentTime_us = timeshift64 + micros64(); + tp->tv_sec = currentTime_us / 1000000ULL; + tp->tv_usec = currentTime_us % 1000000ULL; + } + return 0; } - return 0; -} }; diff --git a/cores/esp8266/twi.h b/cores/esp8266/twi.h index 685221820b..660ffd8cc2 100644 --- a/cores/esp8266/twi.h +++ b/cores/esp8266/twi.h @@ -1,23 +1,23 @@ /* - twi.h - Software I2C library for esp8266 + twi.h - Software I2C library for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support */ #ifndef SI2C_h #define SI2C_h @@ -48,8 +48,8 @@ uint8_t twi_status(); uint8_t twi_transmit(const uint8_t*, uint8_t); -void twi_attachSlaveRxEvent( void (*)(uint8_t*, size_t) ); -void twi_attachSlaveTxEvent( void (*)(void) ); +void twi_attachSlaveRxEvent(void (*)(uint8_t*, size_t)); +void twi_attachSlaveTxEvent(void (*)(void)); void twi_reply(uint8_t); //void twi_stop(void); void twi_releaseBus(void); diff --git a/cores/esp8266/twi_util.h b/cores/esp8266/twi_util.h index 60a92fc965..e4e8b10dbb 100644 --- a/cores/esp8266/twi_util.h +++ b/cores/esp8266/twi_util.h @@ -1,35 +1,35 @@ -/* Copyright (c) 2002, Marek Michalkiewicz - Copyright (c) 2005, 2007 Joerg Wunsch - All rights reserved. +/* Copyright (c) 2002, Marek Michalkiewicz + Copyright (c) 2005, 2007 Joerg Wunsch + All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of the copyright holders nor the names of + Neither the name of the copyright holders nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support */ /* $Id$ */ @@ -49,12 +49,12 @@ */ /** \name TWSR values - Mnemonics: -
TW_MT_xxx - master transmitter -
TW_MR_xxx - master receiver -
TW_ST_xxx - slave transmitter -
TW_SR_xxx - slave receiver - */ + Mnemonics: +
TW_MT_xxx - master transmitter +
TW_MR_xxx - master receiver +
TW_ST_xxx - slave transmitter +
TW_SR_xxx - slave receiver +*/ /*@{*/ /* Master */ @@ -206,27 +206,27 @@ #if 0 /** - * \ingroup util_twi - * \def TW_STATUS_MASK - * The lower 3 bits of TWSR are reserved on the ATmega163. - * The 2 LSB carry the prescaler bits on the newer ATmegas. - */ + \ingroup util_twi + \def TW_STATUS_MASK + The lower 3 bits of TWSR are reserved on the ATmega163. + The 2 LSB carry the prescaler bits on the newer ATmegas. +*/ #define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|\ _BV(TWS3)) /** - * \ingroup util_twi - * \def TW_STATUS - * - * TWSR, masked by TW_STATUS_MASK - */ + \ingroup util_twi + \def TW_STATUS + + TWSR, masked by TW_STATUS_MASK +*/ #define TW_STATUS (TWSR & TW_STATUS_MASK) /*@}*/ #endif /** - * \name R/~W bit in SLA+R/W address field. - */ + \name R/~W bit in SLA+R/W address field. +*/ /*@{*/ /** \ingroup util_twi diff --git a/cores/esp8266/uart.cpp b/cores/esp8266/uart.cpp index 3a27ff62f4..bb1f52f6fe 100644 --- a/cores/esp8266/uart.cpp +++ b/cores/esp8266/uart.cpp @@ -1,45 +1,45 @@ /* - uart.cpp - esp8266 UART HAL + uart.cpp - esp8266 UART HAL - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ +*/ /** - * UART GPIOs - * - * UART0 TX: 1 or 2 - * UART0 RX: 3 - * - * UART0 SWAP TX: 15 - * UART0 SWAP RX: 13 - * - * - * UART1 TX: 7 (NC) or 2 - * UART1 RX: 8 (NC) - * - * UART1 SWAP TX: 11 (NC) - * UART1 SWAP RX: 6 (NC) - * - * NC = Not Connected to Module Pads --> No Access - * - */ + UART GPIOs + + UART0 TX: 1 or 2 + UART0 RX: 3 + + UART0 SWAP TX: 15 + UART0 SWAP RX: 13 + + + UART1 TX: 7 (NC) or 2 + UART1 RX: 8 (NC) + + UART1 SWAP TX: 11 (NC) + UART1 SWAP RX: 6 (NC) + + NC = Not Connected to Module Pads --> No Access + +*/ #include "Arduino.h" #include #include "../../libraries/GDBStub/src/GDBStub.h" @@ -49,932 +49,1052 @@ #include "uart_register.h" /* - Some general architecture for GDB integration with the UART to enable - serial debugging. - - UART1 is transmit only and can never be used by GDB. - - When gdbstub_has_uart_isr_control() (only true in the case GDB is enabled), - UART0 needs to be under the control of the GDB stub for enable/disable/irq - (but speed, parity, etc. still alllowable). Basically, GDB needs to make - sure that UART0 is never really disabled. - - GDB sets up UART0 with a fifo and a 2-character timeout during init. This - is required to ensure that GDBStub can check every character coming in, even - if it is not read by the user app or if the commands don't hit the FIFO - interrupt level. It checks every character that comes in, and if GDB isn't - active just passes them to the user RAM FIFO unless it's a Ctrl-C (0x03). - - GDBStub doesn't care about the struct uart_*, and allocating it or freeing - it has no effect (so you can do Serial.end() and free the uart...but as - mentioned above even if you Serial.end, the actual UART0 HW will still be - kept running to enable GDB to do commands. + Some general architecture for GDB integration with the UART to enable + serial debugging. + + UART1 is transmit only and can never be used by GDB. + + When gdbstub_has_uart_isr_control() (only true in the case GDB is enabled), + UART0 needs to be under the control of the GDB stub for enable/disable/irq + (but speed, parity, etc. still alllowable). Basically, GDB needs to make + sure that UART0 is never really disabled. + + GDB sets up UART0 with a fifo and a 2-character timeout during init. This + is required to ensure that GDBStub can check every character coming in, even + if it is not read by the user app or if the commands don't hit the FIFO + interrupt level. It checks every character that comes in, and if GDB isn't + active just passes them to the user RAM FIFO unless it's a Ctrl-C (0x03). + + GDBStub doesn't care about the struct uart_*, and allocating it or freeing + it has no effect (so you can do Serial.end() and free the uart...but as + mentioned above even if you Serial.end, the actual UART0 HW will still be + kept running to enable GDB to do commands. */ extern "C" { -static int s_uart_debug_nr = UART0; + static int s_uart_debug_nr = UART0; -struct uart_rx_buffer_ -{ - size_t size; - size_t rpos; - size_t wpos; - uint8_t * buffer; -}; + struct uart_rx_buffer_ + { + size_t size; + size_t rpos; + size_t wpos; + uint8_t * buffer; + }; -struct uart_ -{ - int uart_nr; - int baud_rate; - bool rx_enabled; - bool tx_enabled; - bool rx_overrun; - bool rx_error; - uint8_t rx_pin; - uint8_t tx_pin; - struct uart_rx_buffer_ * rx_buffer; -}; + struct uart_ + { + int uart_nr; + int baud_rate; + bool rx_enabled; + bool tx_enabled; + bool rx_overrun; + bool rx_error; + uint8_t rx_pin; + uint8_t tx_pin; + struct uart_rx_buffer_ * rx_buffer; + }; + + + /* + In the context of the naming conventions in this file, "_unsafe" means two things: + 1. the input arguments are not checked. It is up to the caller to check argument sanity. + 2. The function body is not interrupt-safe, i.e.: the isr could fire anywhen during the + body execution, leading to corruption of the data shared between the body and the isr + (parts of the rx_buffer). + + The unsafe versions of the functions are private to this TU. There are "safe" versions that + wrap the unsafe ones with disabling/enabling of the uart interrupt for safe public use. + */ + + + + // called by ISR + inline size_t ICACHE_RAM_ATTR + uart_rx_fifo_available(const int uart_nr) + { + return (USS(uart_nr) >> USRXC) & 0xFF; + } -/* - In the context of the naming conventions in this file, "_unsafe" means two things: - 1. the input arguments are not checked. It is up to the caller to check argument sanity. - 2. The function body is not interrupt-safe, i.e.: the isr could fire anywhen during the - body execution, leading to corruption of the data shared between the body and the isr - (parts of the rx_buffer). - - The unsafe versions of the functions are private to this TU. There are "safe" versions that - wrap the unsafe ones with disabling/enabling of the uart interrupt for safe public use. -*/ + /**********************************************************/ + /************ UNSAFE FUNCTIONS ****************************/ + /**********************************************************/ + inline size_t + uart_rx_buffer_available_unsafe(const struct uart_rx_buffer_ * rx_buffer) + { + if (rx_buffer->wpos < rx_buffer->rpos) + { + return (rx_buffer->wpos + rx_buffer->size) - rx_buffer->rpos; + } + return rx_buffer->wpos - rx_buffer->rpos; + } + inline size_t + uart_rx_available_unsafe(uart_t* uart) + { + return uart_rx_buffer_available_unsafe(uart->rx_buffer) + uart_rx_fifo_available(uart->uart_nr); + } -// called by ISR -inline size_t ICACHE_RAM_ATTR -uart_rx_fifo_available(const int uart_nr) -{ - return (USS(uart_nr) >> USRXC) & 0xFF; -} + //#define UART_DISCARD_NEWEST + // Copy all the rx fifo bytes that fit into the rx buffer + // called by ISR + inline void ICACHE_RAM_ATTR + uart_rx_copy_fifo_to_buffer_unsafe(uart_t* uart) + { + struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer; -/**********************************************************/ -/************ UNSAFE FUNCTIONS ****************************/ -/**********************************************************/ -inline size_t -uart_rx_buffer_available_unsafe(const struct uart_rx_buffer_ * rx_buffer) -{ - if(rx_buffer->wpos < rx_buffer->rpos) - return (rx_buffer->wpos + rx_buffer->size) - rx_buffer->rpos; - - return rx_buffer->wpos - rx_buffer->rpos; -} + while (uart_rx_fifo_available(uart->uart_nr)) + { + size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size; + if (nextPos == rx_buffer->rpos) + { + if (!uart->rx_overrun) + { + uart->rx_overrun = true; + //os_printf_plus(overrun_str); + } + + // a choice has to be made here, + // do we discard newest or oldest data? +#ifdef UART_DISCARD_NEWEST + // discard newest data + // Stop copying if rx buffer is full + USF(uart->uart_nr); + break; +#else + // discard oldest data + if (++rx_buffer->rpos == rx_buffer->size) + { + rx_buffer->rpos = 0; + } +#endif + } + uint8_t data = USF(uart->uart_nr); + rx_buffer->buffer[rx_buffer->wpos] = data; + rx_buffer->wpos = nextPos; + } + } -inline size_t -uart_rx_available_unsafe(uart_t* uart) -{ - return uart_rx_buffer_available_unsafe(uart->rx_buffer) + uart_rx_fifo_available(uart->uart_nr); -} + inline int + uart_peek_char_unsafe(uart_t* uart) + { + if (!uart_rx_available_unsafe(uart)) + { + return -1; + } -//#define UART_DISCARD_NEWEST + //without the following if statement and body, there is a good chance of a fifo overrun + if (uart_rx_buffer_available_unsafe(uart->rx_buffer) == 0) + // hw fifo can't be peeked, data need to be copied to sw + { + uart_rx_copy_fifo_to_buffer_unsafe(uart); + } -// Copy all the rx fifo bytes that fit into the rx buffer -// called by ISR -inline void ICACHE_RAM_ATTR -uart_rx_copy_fifo_to_buffer_unsafe(uart_t* uart) -{ - struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer; + return uart->rx_buffer->buffer[uart->rx_buffer->rpos]; + } - while(uart_rx_fifo_available(uart->uart_nr)) + // taking data straight from hw fifo: loopback-test BW jumps by 19% + inline int + uart_read_char_unsafe(uart_t* uart) { - size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size; - if(nextPos == rx_buffer->rpos) + if (uart_rx_buffer_available_unsafe(uart->rx_buffer)) + { + // take oldest sw data + int ret = uart->rx_buffer->buffer[uart->rx_buffer->rpos]; + uart->rx_buffer->rpos = (uart->rx_buffer->rpos + 1) % uart->rx_buffer->size; + return ret; + } + // unavailable + return -1; + } + + size_t + uart_rx_available(uart_t* uart) + { + if (uart == NULL || !uart->rx_enabled) + { + return 0; + } + + ETS_UART_INTR_DISABLE(); + int uartrxbufferavailable = uart_rx_buffer_available_unsafe(uart->rx_buffer); + ETS_UART_INTR_ENABLE(); + + return uartrxbufferavailable + uart_rx_fifo_available(uart->uart_nr); + } + + int + uart_peek_char(uart_t* uart) + { + if (uart == NULL || !uart->rx_enabled) { - if (!uart->rx_overrun) + return -1; + } + + ETS_UART_INTR_DISABLE(); //access to rx_buffer can be interrupted by the isr (similar to a critical section), so disable interrupts here + int ret = uart_peek_char_unsafe(uart); + ETS_UART_INTR_ENABLE(); + return ret; + } + + int + uart_read_char(uart_t* uart) + { + uint8_t ret; + return uart_read(uart, (char*)&ret, 1) ? ret : -1; + } + + // loopback-test BW jumps by 190% + size_t + uart_read(uart_t* uart, char* userbuffer, size_t usersize) + { + if (uart == NULL || !uart->rx_enabled) + { + return 0; + } + + size_t ret = 0; + ETS_UART_INTR_DISABLE(); + + while (ret < usersize && uart_rx_available_unsafe(uart)) + { + if (!uart_rx_buffer_available_unsafe(uart->rx_buffer)) { - uart->rx_overrun = true; - //os_printf_plus(overrun_str); + // no more data in sw buffer, take them from hw fifo + while (ret < usersize && uart_rx_fifo_available(uart->uart_nr)) + { + userbuffer[ret++] = USF(uart->uart_nr); + } + + // no more sw/hw data available + break; } + // pour sw buffer to user's buffer + // get largest linear length from sw buffer + size_t chunk = uart->rx_buffer->rpos < uart->rx_buffer->wpos ? + uart->rx_buffer->wpos - uart->rx_buffer->rpos : + uart->rx_buffer->size - uart->rx_buffer->rpos; + if (ret + chunk > usersize) + { + chunk = usersize - ret; + } + memcpy(userbuffer + ret, uart->rx_buffer->buffer + uart->rx_buffer->rpos, chunk); + uart->rx_buffer->rpos = (uart->rx_buffer->rpos + chunk) % uart->rx_buffer->size; + ret += chunk; + } + + ETS_UART_INTR_ENABLE(); + return ret; + } + + // When GDB is running, this is called one byte at a time to stuff the user FIFO + // instead of the uart_isr...uart_rx_copy_fifo_to_buffer_unsafe() + // Since we've already read the bytes from the FIFO, can't use that + // function directly and need to implement it bytewise here + static void ICACHE_RAM_ATTR uart_isr_handle_data(void* arg, uint8_t data) + { + uart_t* uart = (uart_t*)arg; + if (uart == NULL || !uart->rx_enabled) + { + return; + } + + // Copy all the rx fifo bytes that fit into the rx buffer + // called by ISR + struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer; + + size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size; + if (nextPos == rx_buffer->rpos) + { + uart->rx_overrun = true; + //os_printf_plus(overrun_str); + // a choice has to be made here, // do we discard newest or oldest data? #ifdef UART_DISCARD_NEWEST // discard newest data // Stop copying if rx buffer is full - USF(uart->uart_nr); - break; + return; #else // discard oldest data if (++rx_buffer->rpos == rx_buffer->size) + { rx_buffer->rpos = 0; + } #endif } - uint8_t data = USF(uart->uart_nr); rx_buffer->buffer[rx_buffer->wpos] = data; rx_buffer->wpos = nextPos; - } -} -inline int -uart_peek_char_unsafe(uart_t* uart) -{ - if (!uart_rx_available_unsafe(uart)) - return -1; - - //without the following if statement and body, there is a good chance of a fifo overrun - if (uart_rx_buffer_available_unsafe(uart->rx_buffer) == 0) - // hw fifo can't be peeked, data need to be copied to sw - uart_rx_copy_fifo_to_buffer_unsafe(uart); - - return uart->rx_buffer->buffer[uart->rx_buffer->rpos]; -} - -// taking data straight from hw fifo: loopback-test BW jumps by 19% -inline int -uart_read_char_unsafe(uart_t* uart) -{ - if (uart_rx_buffer_available_unsafe(uart->rx_buffer)) - { - // take oldest sw data - int ret = uart->rx_buffer->buffer[uart->rx_buffer->rpos]; - uart->rx_buffer->rpos = (uart->rx_buffer->rpos + 1) % uart->rx_buffer->size; - return ret; - } - // unavailable - return -1; -} - -size_t -uart_rx_available(uart_t* uart) -{ - if(uart == NULL || !uart->rx_enabled) - return 0; - - ETS_UART_INTR_DISABLE(); - int uartrxbufferavailable = uart_rx_buffer_available_unsafe(uart->rx_buffer); - ETS_UART_INTR_ENABLE(); - - return uartrxbufferavailable + uart_rx_fifo_available(uart->uart_nr); -} - -int -uart_peek_char(uart_t* uart) -{ - if(uart == NULL || !uart->rx_enabled) - return -1; - - ETS_UART_INTR_DISABLE(); //access to rx_buffer can be interrupted by the isr (similar to a critical section), so disable interrupts here - int ret = uart_peek_char_unsafe(uart); - ETS_UART_INTR_ENABLE(); - return ret; -} - -int -uart_read_char(uart_t* uart) -{ - uint8_t ret; - return uart_read(uart, (char*)&ret, 1)? ret: -1; -} - -// loopback-test BW jumps by 190% -size_t -uart_read(uart_t* uart, char* userbuffer, size_t usersize) -{ - if(uart == NULL || !uart->rx_enabled) - return 0; - - size_t ret = 0; - ETS_UART_INTR_DISABLE(); - - while (ret < usersize && uart_rx_available_unsafe(uart)) - { - if (!uart_rx_buffer_available_unsafe(uart->rx_buffer)) - { - // no more data in sw buffer, take them from hw fifo - while (ret < usersize && uart_rx_fifo_available(uart->uart_nr)) - userbuffer[ret++] = USF(uart->uart_nr); - - // no more sw/hw data available - break; + // Check the UART flags and note hardware overflow/etc. + uint32_t usis = USIS(uart->uart_nr); + + if (usis & (1 << UIOF)) + { + uart->rx_overrun = true; } - // pour sw buffer to user's buffer - // get largest linear length from sw buffer - size_t chunk = uart->rx_buffer->rpos < uart->rx_buffer->wpos? - uart->rx_buffer->wpos - uart->rx_buffer->rpos: - uart->rx_buffer->size - uart->rx_buffer->rpos; - if (ret + chunk > usersize) - chunk = usersize - ret; - memcpy(userbuffer + ret, uart->rx_buffer->buffer + uart->rx_buffer->rpos, chunk); - uart->rx_buffer->rpos = (uart->rx_buffer->rpos + chunk) % uart->rx_buffer->size; - ret += chunk; + if (usis & ((1 << UIFR) | (1 << UIPE) | (1 << UITO))) + { + uart->rx_error = true; + } + + USIC(uart->uart_nr) = usis; } - ETS_UART_INTR_ENABLE(); - return ret; -} + size_t + uart_resize_rx_buffer(uart_t* uart, size_t new_size) + { + if (uart == NULL || !uart->rx_enabled) + { + return 0; + } -// When GDB is running, this is called one byte at a time to stuff the user FIFO -// instead of the uart_isr...uart_rx_copy_fifo_to_buffer_unsafe() -// Since we've already read the bytes from the FIFO, can't use that -// function directly and need to implement it bytewise here -static void ICACHE_RAM_ATTR uart_isr_handle_data(void* arg, uint8_t data) -{ - uart_t* uart = (uart_t*)arg; - if(uart == NULL || !uart->rx_enabled) { - return; + if (uart->rx_buffer->size == new_size) + { + return uart->rx_buffer->size; + } + + uint8_t * new_buf = (uint8_t*)malloc(new_size); + if (!new_buf) + { + return uart->rx_buffer->size; + } + + size_t new_wpos = 0; + ETS_UART_INTR_DISABLE(); + while (uart_rx_available_unsafe(uart) && new_wpos < new_size) + { + new_buf[new_wpos++] = uart_read_char_unsafe(uart); //if uart_rx_available_unsafe() returns non-0, uart_read_char_unsafe() can't return -1 + } + if (new_wpos == new_size) + { + new_wpos = 0; + } + + uint8_t * old_buf = uart->rx_buffer->buffer; + uart->rx_buffer->rpos = 0; + uart->rx_buffer->wpos = new_wpos; + uart->rx_buffer->size = new_size; + uart->rx_buffer->buffer = new_buf; + ETS_UART_INTR_ENABLE(); + free(old_buf); + return uart->rx_buffer->size; } -// Copy all the rx fifo bytes that fit into the rx buffer -// called by ISR - struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer; + size_t + uart_get_rx_buffer_size(uart_t* uart) + { + return uart && uart->rx_enabled ? uart->rx_buffer->size : 0; + } - size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size; - if(nextPos == rx_buffer->rpos) + // The default ISR handler called when GDB is not enabled + void ICACHE_RAM_ATTR + uart_isr(void * arg) { - uart->rx_overrun = true; - //os_printf_plus(overrun_str); + uart_t* uart = (uart_t*)arg; + uint32_t usis = USIS(uart->uart_nr); - // a choice has to be made here, - // do we discard newest or oldest data? -#ifdef UART_DISCARD_NEWEST - // discard newest data - // Stop copying if rx buffer is full - return; -#else - // discard oldest data - if (++rx_buffer->rpos == rx_buffer->size) - rx_buffer->rpos = 0; -#endif - } - rx_buffer->buffer[rx_buffer->wpos] = data; - rx_buffer->wpos = nextPos; + if (uart == NULL || !uart->rx_enabled) + { + USIC(uart->uart_nr) = usis; + ETS_UART_INTR_DISABLE(); + return; + } - // Check the UART flags and note hardware overflow/etc. - uint32_t usis = USIS(uart->uart_nr); + if (usis & (1 << UIFF)) + { + uart_rx_copy_fifo_to_buffer_unsafe(uart); + } - if(usis & (1 << UIOF)) - uart->rx_overrun = true; + if (usis & (1 << UIOF)) + { + uart->rx_overrun = true; + //os_printf_plus(overrun_str); + } - if (usis & ((1 << UIFR) | (1 << UIPE) | (1 << UITO))) - uart->rx_error = true; + if (usis & ((1 << UIFR) | (1 << UIPE) | (1 << UITO))) + { + uart->rx_error = true; + } - USIC(uart->uart_nr) = usis; -} + USIC(uart->uart_nr) = usis; + } -size_t -uart_resize_rx_buffer(uart_t* uart, size_t new_size) -{ - if(uart == NULL || !uart->rx_enabled) - return 0; + static void + uart_start_isr(uart_t* uart) + { + if (uart == NULL || !uart->rx_enabled) + { + return; + } - if(uart->rx_buffer->size == new_size) - return uart->rx_buffer->size; + if (gdbstub_has_uart_isr_control()) + { + gdbstub_set_uart_isr_callback(uart_isr_handle_data, (void *)uart); + return; + } - uint8_t * new_buf = (uint8_t*)malloc(new_size); - if(!new_buf) - return uart->rx_buffer->size; - - size_t new_wpos = 0; - ETS_UART_INTR_DISABLE(); - while(uart_rx_available_unsafe(uart) && new_wpos < new_size) - new_buf[new_wpos++] = uart_read_char_unsafe(uart); //if uart_rx_available_unsafe() returns non-0, uart_read_char_unsafe() can't return -1 - if (new_wpos == new_size) - new_wpos = 0; - - uint8_t * old_buf = uart->rx_buffer->buffer; - uart->rx_buffer->rpos = 0; - uart->rx_buffer->wpos = new_wpos; - uart->rx_buffer->size = new_size; - uart->rx_buffer->buffer = new_buf; - ETS_UART_INTR_ENABLE(); - free(old_buf); - return uart->rx_buffer->size; -} - -size_t -uart_get_rx_buffer_size(uart_t* uart) -{ - return uart && uart->rx_enabled? uart->rx_buffer->size: 0; -} - -// The default ISR handler called when GDB is not enabled -void ICACHE_RAM_ATTR -uart_isr(void * arg) -{ - uart_t* uart = (uart_t*)arg; - uint32_t usis = USIS(uart->uart_nr); - - if(uart == NULL || !uart->rx_enabled) + // UCFFT value is when the RX fifo full interrupt triggers. A value of 1 + // triggers the IRS very often. A value of 127 would not leave much time + // for ISR to clear fifo before the next byte is dropped. So pick a value + // in the middle. + // update: loopback test @ 3Mbauds/8n1 (=2343Kibits/s): + // - 4..120 give > 2300Kibits/s + // - 1, 2, 3 are below + // was 100, use 16 to stay away from overrun +#define INTRIGG 16 + + //was:USC1(uart->uart_nr) = (INTRIGG << UCFFT) | (0x02 << UCTOT) | (1 <uart_nr) = (INTRIGG << UCFFT); + USIC(uart->uart_nr) = 0xffff; + //was: USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIFR) | (1 << UITO); + // UIFF: rx fifo full + // UIOF: rx fifo overflow (=overrun) + // UIFR: frame error + // UIPE: parity error + // UITO: rx fifo timeout + USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIOF) | (1 << UIFR) | (1 << UIPE) | (1 << UITO); + ETS_UART_INTR_ATTACH(uart_isr, (void *)uart); + ETS_UART_INTR_ENABLE(); + } + + static void + uart_stop_isr(uart_t* uart) { - USIC(uart->uart_nr) = usis; - ETS_UART_INTR_DISABLE(); - return; - } - - if(usis & (1 << UIFF)) - uart_rx_copy_fifo_to_buffer_unsafe(uart); - - if(usis & (1 << UIOF)) - { - uart->rx_overrun = true; - //os_printf_plus(overrun_str); - } - - if (usis & ((1 << UIFR) | (1 << UIPE) | (1 << UITO))) - uart->rx_error = true; - - USIC(uart->uart_nr) = usis; -} - -static void -uart_start_isr(uart_t* uart) -{ - if(uart == NULL || !uart->rx_enabled) - return; - - if(gdbstub_has_uart_isr_control()) { - gdbstub_set_uart_isr_callback(uart_isr_handle_data, (void *)uart); - return; - } - - // UCFFT value is when the RX fifo full interrupt triggers. A value of 1 - // triggers the IRS very often. A value of 127 would not leave much time - // for ISR to clear fifo before the next byte is dropped. So pick a value - // in the middle. - // update: loopback test @ 3Mbauds/8n1 (=2343Kibits/s): - // - 4..120 give > 2300Kibits/s - // - 1, 2, 3 are below - // was 100, use 16 to stay away from overrun - #define INTRIGG 16 - - //was:USC1(uart->uart_nr) = (INTRIGG << UCFFT) | (0x02 << UCTOT) | (1 <uart_nr) = (INTRIGG << UCFFT); - USIC(uart->uart_nr) = 0xffff; - //was: USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIFR) | (1 << UITO); - // UIFF: rx fifo full - // UIOF: rx fifo overflow (=overrun) - // UIFR: frame error - // UIPE: parity error - // UITO: rx fifo timeout - USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIOF) | (1 << UIFR) | (1 << UIPE) | (1 << UITO); - ETS_UART_INTR_ATTACH(uart_isr, (void *)uart); - ETS_UART_INTR_ENABLE(); -} - -static void -uart_stop_isr(uart_t* uart) -{ - if(uart == NULL || !uart->rx_enabled) - return; - - if(gdbstub_has_uart_isr_control()) { - gdbstub_set_uart_isr_callback(NULL, NULL); - return; - } - - ETS_UART_INTR_DISABLE(); - USC1(uart->uart_nr) = 0; - USIC(uart->uart_nr) = 0xffff; - USIE(uart->uart_nr) = 0; - ETS_UART_INTR_ATTACH(NULL, NULL); -} + if (uart == NULL || !uart->rx_enabled) + { + return; + } -/* - Reference for uart_tx_fifo_available() and uart_tx_fifo_full(): - -Espressif Techinical Reference doc, chapter 11.3.7 - -tools/sdk/uart_register.h - -cores/esp8266/esp8266_peri.h - */ -inline size_t -uart_tx_fifo_available(const int uart_nr) -{ - return (USS(uart_nr) >> USTXC) & 0xff; -} - -inline bool -uart_tx_fifo_full(const int uart_nr) -{ - return uart_tx_fifo_available(uart_nr) >= 0x7f; -} - - -static void -uart_do_write_char(const int uart_nr, char c) -{ - while(uart_tx_fifo_full(uart_nr)); - - USF(uart_nr) = c; -} - -size_t -uart_write_char(uart_t* uart, char c) -{ - if(uart == NULL || !uart->tx_enabled) - return 0; - - if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) { - gdbstub_write_char(c); - return 1; + if (gdbstub_has_uart_isr_control()) + { + gdbstub_set_uart_isr_callback(NULL, NULL); + return; + } + + ETS_UART_INTR_DISABLE(); + USC1(uart->uart_nr) = 0; + USIC(uart->uart_nr) = 0xffff; + USIE(uart->uart_nr) = 0; + ETS_UART_INTR_ATTACH(NULL, NULL); } - uart_do_write_char(uart->uart_nr, c); - return 1; -} -size_t -uart_write(uart_t* uart, const char* buf, size_t size) -{ - if(uart == NULL || !uart->tx_enabled) - return 0; + /* + Reference for uart_tx_fifo_available() and uart_tx_fifo_full(): + -Espressif Techinical Reference doc, chapter 11.3.7 + -tools/sdk/uart_register.h + -cores/esp8266/esp8266_peri.h + */ + inline size_t + uart_tx_fifo_available(const int uart_nr) + { + return (USS(uart_nr) >> USTXC) & 0xff; + } - if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) { - gdbstub_write(buf, size); - return 0; + inline bool + uart_tx_fifo_full(const int uart_nr) + { + return uart_tx_fifo_available(uart_nr) >= 0x7f; } - size_t ret = size; - const int uart_nr = uart->uart_nr; - while (size--) - uart_do_write_char(uart_nr, *buf++); - return ret; -} + static void + uart_do_write_char(const int uart_nr, char c) + { + while (uart_tx_fifo_full(uart_nr)); + + USF(uart_nr) = c; + } + size_t + uart_write_char(uart_t* uart, char c) + { + if (uart == NULL || !uart->tx_enabled) + { + return 0; + } -size_t -uart_tx_free(uart_t* uart) -{ - if(uart == NULL || !uart->tx_enabled) - return 0; + if (gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) + { + gdbstub_write_char(c); + return 1; + } + uart_do_write_char(uart->uart_nr, c); + return 1; + } - return UART_TX_FIFO_SIZE - uart_tx_fifo_available(uart->uart_nr); -} + size_t + uart_write(uart_t* uart, const char* buf, size_t size) + { + if (uart == NULL || !uart->tx_enabled) + { + return 0; + } -void -uart_wait_tx_empty(uart_t* uart) -{ - if(uart == NULL || !uart->tx_enabled) - return; + if (gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) + { + gdbstub_write(buf, size); + return 0; + } - while(uart_tx_fifo_available(uart->uart_nr) > 0) - delay(0); + size_t ret = size; + const int uart_nr = uart->uart_nr; + while (size--) + { + uart_do_write_char(uart_nr, *buf++); + } -} + return ret; + } -void -uart_flush(uart_t* uart) -{ - if(uart == NULL) - return; - uint32_t tmp = 0x00000000; - if(uart->rx_enabled) + size_t + uart_tx_free(uart_t* uart) { - tmp |= (1 << UCRXRST); - ETS_UART_INTR_DISABLE(); - uart->rx_buffer->rpos = 0; - uart->rx_buffer->wpos = 0; - ETS_UART_INTR_ENABLE(); + if (uart == NULL || !uart->tx_enabled) + { + return 0; + } + + return UART_TX_FIFO_SIZE - uart_tx_fifo_available(uart->uart_nr); } - if(uart->tx_enabled) - tmp |= (1 << UCTXRST); + void + uart_wait_tx_empty(uart_t* uart) + { + if (uart == NULL || !uart->tx_enabled) + { + return; + } + + while (uart_tx_fifo_available(uart->uart_nr) > 0) + { + delay(0); + } - if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) { - USC0(uart->uart_nr) |= (tmp); - USC0(uart->uart_nr) &= ~(tmp); } -} -void -uart_set_baudrate(uart_t* uart, int baud_rate) -{ - if(uart == NULL) - return; + void + uart_flush(uart_t* uart) + { + if (uart == NULL) + { + return; + } - uart->baud_rate = baud_rate; - USD(uart->uart_nr) = (ESP8266_CLOCK / uart->baud_rate); -} + uint32_t tmp = 0x00000000; + if (uart->rx_enabled) + { + tmp |= (1 << UCRXRST); + ETS_UART_INTR_DISABLE(); + uart->rx_buffer->rpos = 0; + uart->rx_buffer->wpos = 0; + ETS_UART_INTR_ENABLE(); + } -int -uart_get_baudrate(uart_t* uart) -{ - if(uart == NULL) - return 0; + if (uart->tx_enabled) + { + tmp |= (1 << UCTXRST); + } - return uart->baud_rate; -} + if (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) + { + USC0(uart->uart_nr) |= (tmp); + USC0(uart->uart_nr) &= ~(tmp); + } + } -uart_t* -uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size) -{ - uart_t* uart = (uart_t*) malloc(sizeof(uart_t)); - if(uart == NULL) - return NULL; + void + uart_set_baudrate(uart_t* uart, int baud_rate) + { + if (uart == NULL) + { + return; + } - uart->uart_nr = uart_nr; - uart->rx_overrun = false; - uart->rx_error = false; + uart->baud_rate = baud_rate; + USD(uart->uart_nr) = (ESP8266_CLOCK / uart->baud_rate); + } - switch(uart->uart_nr) + int + uart_get_baudrate(uart_t* uart) { - case UART0: - ETS_UART_INTR_DISABLE(); - if(!gdbstub_has_uart_isr_control()) { - ETS_UART_INTR_ATTACH(NULL, NULL); + if (uart == NULL) + { + return 0; } - uart->rx_enabled = (mode != UART_TX_ONLY); - uart->tx_enabled = (mode != UART_RX_ONLY); - uart->rx_pin = (uart->rx_enabled)?3:255; - if(uart->rx_enabled) + + return uart->baud_rate; + } + + uart_t* + uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size) + { + uart_t* uart = (uart_t*) malloc(sizeof(uart_t)); + if (uart == NULL) { - struct uart_rx_buffer_ * rx_buffer = (struct uart_rx_buffer_ *)malloc(sizeof(struct uart_rx_buffer_)); - if(rx_buffer == NULL) + return NULL; + } + + uart->uart_nr = uart_nr; + uart->rx_overrun = false; + uart->rx_error = false; + + switch (uart->uart_nr) + { + case UART0: + ETS_UART_INTR_DISABLE(); + if (!gdbstub_has_uart_isr_control()) { - free(uart); - return NULL; + ETS_UART_INTR_ATTACH(NULL, NULL); } - rx_buffer->size = rx_size;//var this - rx_buffer->rpos = 0; - rx_buffer->wpos = 0; - rx_buffer->buffer = (uint8_t *)malloc(rx_buffer->size); - if(rx_buffer->buffer == NULL) + uart->rx_enabled = (mode != UART_TX_ONLY); + uart->tx_enabled = (mode != UART_RX_ONLY); + uart->rx_pin = (uart->rx_enabled) ? 3 : 255; + if (uart->rx_enabled) { - free(rx_buffer); - free(uart); - return NULL; + struct uart_rx_buffer_ * rx_buffer = (struct uart_rx_buffer_ *)malloc(sizeof(struct uart_rx_buffer_)); + if (rx_buffer == NULL) + { + free(uart); + return NULL; + } + rx_buffer->size = rx_size;//var this + rx_buffer->rpos = 0; + rx_buffer->wpos = 0; + rx_buffer->buffer = (uint8_t *)malloc(rx_buffer->size); + if (rx_buffer->buffer == NULL) + { + free(rx_buffer); + free(uart); + return NULL; + } + uart->rx_buffer = rx_buffer; + pinMode(uart->rx_pin, SPECIAL); } - uart->rx_buffer = rx_buffer; - pinMode(uart->rx_pin, SPECIAL); - } - if(uart->tx_enabled) - { - if (tx_pin == 2) + if (uart->tx_enabled) { - uart->tx_pin = 2; - pinMode(uart->tx_pin, FUNCTION_4); - } - else + if (tx_pin == 2) + { + uart->tx_pin = 2; + pinMode(uart->tx_pin, FUNCTION_4); + } + else + { + uart->tx_pin = 1; + pinMode(uart->tx_pin, FUNCTION_0); + } + } + else { - uart->tx_pin = 1; - pinMode(uart->tx_pin, FUNCTION_0); + uart->tx_pin = 255; } - } - else - { - uart->tx_pin = 255; - } - IOSWAP &= ~(1 << IOSWAPU0); - break; + IOSWAP &= ~(1 << IOSWAPU0); + break; - case UART1: - // Note: uart_interrupt_handler does not support RX on UART 1. - uart->rx_enabled = false; - uart->tx_enabled = (mode != UART_RX_ONLY); - uart->rx_pin = 255; - uart->tx_pin = (uart->tx_enabled)?2:255; // GPIO7 as TX not possible! See GPIO pins used by UART - if(uart->tx_enabled) - pinMode(uart->tx_pin, SPECIAL); + case UART1: + // Note: uart_interrupt_handler does not support RX on UART 1. + uart->rx_enabled = false; + uart->tx_enabled = (mode != UART_RX_ONLY); + uart->rx_pin = 255; + uart->tx_pin = (uart->tx_enabled) ? 2 : 255; // GPIO7 as TX not possible! See GPIO pins used by UART + if (uart->tx_enabled) + { + pinMode(uart->tx_pin, SPECIAL); + } - break; + break; - case UART_NO: - default: - // big fail! - free(uart); - return NULL; - } + case UART_NO: + default: + // big fail! + free(uart); + return NULL; + } - uart_set_baudrate(uart, baudrate); - USC0(uart->uart_nr) = config; + uart_set_baudrate(uart, baudrate); + USC0(uart->uart_nr) = config; - if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) { - uart_flush(uart); - USC1(uart->uart_nr) = 0; - USIC(uart->uart_nr) = 0xffff; - USIE(uart->uart_nr) = 0; - } - if(uart->uart_nr == UART0) { - if(uart->rx_enabled) { - uart_start_isr(uart); + if (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) + { + uart_flush(uart); + USC1(uart->uart_nr) = 0; + USIC(uart->uart_nr) = 0xffff; + USIE(uart->uart_nr) = 0; } - if(gdbstub_has_uart_isr_control()) { - ETS_UART_INTR_ENABLE(); // Undo the disable in the switch() above + if (uart->uart_nr == UART0) + { + if (uart->rx_enabled) + { + uart_start_isr(uart); + } + if (gdbstub_has_uart_isr_control()) + { + ETS_UART_INTR_ENABLE(); // Undo the disable in the switch() above + } } + + return uart; } - return uart; -} + void + uart_uninit(uart_t* uart) + { + if (uart == NULL) + { + return; + } + + uart_stop_isr(uart); -void -uart_uninit(uart_t* uart) -{ - if(uart == NULL) - return; + if (uart->tx_enabled && (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0)) + { + switch (uart->tx_pin) + { + case 1: + pinMode(1, INPUT); + break; + case 2: + pinMode(2, INPUT); + break; + case 15: + pinMode(15, INPUT); + break; + } + } - uart_stop_isr(uart); + if (uart->rx_enabled) + { + free(uart->rx_buffer->buffer); + free(uart->rx_buffer); + if (!gdbstub_has_uart_isr_control()) + { + switch (uart->rx_pin) + { + case 3: + pinMode(3, INPUT); + break; + case 13: + pinMode(13, INPUT); + break; + } + } + } + free(uart); + } - if(uart->tx_enabled && (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0)) { - switch(uart->tx_pin) + void + uart_swap(uart_t* uart, int tx_pin) + { + if (uart == NULL) { - case 1: - pinMode(1, INPUT); + return; + } + + switch (uart->uart_nr) + { + case UART0: + if (((uart->tx_pin == 1 || uart->tx_pin == 2) && uart->tx_enabled) || (uart->rx_pin == 3 && uart->rx_enabled)) + { + if (uart->tx_enabled) //TX + { + pinMode(uart->tx_pin, INPUT); + uart->tx_pin = 15; + } + if (uart->rx_enabled) //RX + { + pinMode(uart->rx_pin, INPUT); + uart->rx_pin = 13; + } + if (uart->tx_enabled) + { + pinMode(uart->tx_pin, FUNCTION_4); //TX + } + + if (uart->rx_enabled) + { + pinMode(uart->rx_pin, FUNCTION_4); //RX + } + + IOSWAP |= (1 << IOSWAPU0); + } + else + { + if (uart->tx_enabled) //TX + { + pinMode(uart->tx_pin, INPUT); + uart->tx_pin = (tx_pin == 2) ? 2 : 1; + } + if (uart->rx_enabled) //RX + { + pinMode(uart->rx_pin, INPUT); + uart->rx_pin = 3; + } + if (uart->tx_enabled) + { + pinMode(uart->tx_pin, (tx_pin == 2) ? FUNCTION_4 : SPECIAL); //TX + } + + if (uart->rx_enabled) + { + pinMode(3, SPECIAL); //RX + } + + IOSWAP &= ~(1 << IOSWAPU0); + } break; - case 2: - pinMode(2, INPUT); + case UART1: + // Currently no swap possible! See GPIO pins used by UART break; - case 15: - pinMode(15, INPUT); + default: break; } } - if(uart->rx_enabled) { - free(uart->rx_buffer->buffer); - free(uart->rx_buffer); - if(!gdbstub_has_uart_isr_control()) { - switch(uart->rx_pin) + void + uart_set_tx(uart_t* uart, int tx_pin) + { + if (uart == NULL) + { + return; + } + + switch (uart->uart_nr) + { + case UART0: + if (uart->tx_enabled) { - case 3: - pinMode(3, INPUT); - break; - case 13: - pinMode(13, INPUT); - break; + if (uart->tx_pin == 1 && tx_pin == 2) + { + pinMode(uart->tx_pin, INPUT); + uart->tx_pin = 2; + pinMode(uart->tx_pin, FUNCTION_4); + } + else if (uart->tx_pin == 2 && tx_pin != 2) + { + pinMode(uart->tx_pin, INPUT); + uart->tx_pin = 1; + pinMode(uart->tx_pin, SPECIAL); + } } + + break; + case UART1: + // GPIO7 as TX not possible! See GPIO pins used by UART + break; + default: + break; } } - free(uart); -} - -void -uart_swap(uart_t* uart, int tx_pin) -{ - if(uart == NULL) - return; - switch(uart->uart_nr) + void + uart_set_pins(uart_t* uart, int tx, int rx) { - case UART0: - if(((uart->tx_pin == 1 || uart->tx_pin == 2) && uart->tx_enabled) || (uart->rx_pin == 3 && uart->rx_enabled)) + if (uart == NULL) { - if(uart->tx_enabled) //TX - { - pinMode(uart->tx_pin, INPUT); - uart->tx_pin = 15; - } - if(uart->rx_enabled) //RX - { - pinMode(uart->rx_pin, INPUT); - uart->rx_pin = 13; - } - if(uart->tx_enabled) - pinMode(uart->tx_pin, FUNCTION_4); //TX + return; + } - if(uart->rx_enabled) - pinMode(uart->rx_pin, FUNCTION_4); //RX - - IOSWAP |= (1 << IOSWAPU0); - } - else + if (uart->uart_nr == UART0) // Only UART0 allows pin changes { - if(uart->tx_enabled) //TX + if (uart->tx_enabled && uart->tx_pin != tx) { - pinMode(uart->tx_pin, INPUT); - uart->tx_pin = (tx_pin == 2)?2:1; + if (rx == 13 && tx == 15) + { + uart_swap(uart, 15); + } + else if (rx == 3 && (tx == 1 || tx == 2)) + { + if (uart->rx_pin != rx) + { + uart_swap(uart, tx); + } + else + { + uart_set_tx(uart, tx); + } + } } - if(uart->rx_enabled) //RX + if (uart->rx_enabled && uart->rx_pin != rx && rx == 13 && tx == 15) { - pinMode(uart->rx_pin, INPUT); - uart->rx_pin = 3; + uart_swap(uart, 15); } - if(uart->tx_enabled) - pinMode(uart->tx_pin, (tx_pin == 2)?FUNCTION_4:SPECIAL); //TX + } + } - if(uart->rx_enabled) - pinMode(3, SPECIAL); //RX - IOSWAP &= ~(1 << IOSWAPU0); + bool + uart_tx_enabled(uart_t* uart) + { + if (uart == NULL) + { + return false; } - break; - case UART1: - // Currently no swap possible! See GPIO pins used by UART - break; - default: - break; + + return uart->tx_enabled; } -} -void -uart_set_tx(uart_t* uart, int tx_pin) -{ - if(uart == NULL) - return; + bool + uart_rx_enabled(uart_t* uart) + { + if (uart == NULL) + { + return false; + } + + return uart->rx_enabled; + } - switch(uart->uart_nr) + bool + uart_has_overrun(uart_t* uart) { - case UART0: - if(uart->tx_enabled) + if (uart == NULL || !uart->rx_overrun) { - if (uart->tx_pin == 1 && tx_pin == 2) - { - pinMode(uart->tx_pin, INPUT); - uart->tx_pin = 2; - pinMode(uart->tx_pin, FUNCTION_4); - } - else if (uart->tx_pin == 2 && tx_pin != 2) - { - pinMode(uart->tx_pin, INPUT); - uart->tx_pin = 1; - pinMode(uart->tx_pin, SPECIAL); - } + return false; } - break; - case UART1: - // GPIO7 as TX not possible! See GPIO pins used by UART - break; - default: - break; + // clear flag + uart->rx_overrun = false; + return true; } -} -void -uart_set_pins(uart_t* uart, int tx, int rx) -{ - if(uart == NULL) - return; + bool + uart_has_rx_error(uart_t* uart) + { + if (uart == NULL || !uart->rx_error) + { + return false; + } + + // clear flag + uart->rx_error = false; + return true; + } - if(uart->uart_nr == UART0) // Only UART0 allows pin changes + static void + uart_ignore_char(char c) { - if(uart->tx_enabled && uart->tx_pin != tx) + (void) c; + } + + inline void + uart_write_char_delay(const int uart_nr, char c) + { + while (uart_tx_fifo_full(uart_nr)) { - if( rx == 13 && tx == 15) - { - uart_swap(uart, 15); - } - else if (rx == 3 && (tx == 1 || tx == 2)) - { - if (uart->rx_pin != rx) - uart_swap(uart, tx); - else - uart_set_tx(uart, tx); - } + delay(0); } - if(uart->rx_enabled && uart->rx_pin != rx && rx == 13 && tx == 15) - uart_swap(uart, 15); - } -} - - -bool -uart_tx_enabled(uart_t* uart) -{ - if(uart == NULL) - return false; - - return uart->tx_enabled; -} - -bool -uart_rx_enabled(uart_t* uart) -{ - if(uart == NULL) - return false; - - return uart->rx_enabled; -} - -bool -uart_has_overrun (uart_t* uart) -{ - if (uart == NULL || !uart->rx_overrun) - return false; - - // clear flag - uart->rx_overrun = false; - return true; -} - -bool -uart_has_rx_error (uart_t* uart) -{ - if (uart == NULL || !uart->rx_error) - return false; - - // clear flag - uart->rx_error = false; - return true; -} - -static void -uart_ignore_char(char c) -{ - (void) c; -} - -inline void -uart_write_char_delay(const int uart_nr, char c) -{ - while(uart_tx_fifo_full(uart_nr)) - delay(0); - - USF(uart_nr) = c; - -} - -static void -uart0_write_char(char c) -{ - uart_write_char_delay(0, c); -} - -static void -uart1_write_char(char c) -{ - uart_write_char_delay(1, c); -} - -void -uart_set_debug(int uart_nr) -{ - s_uart_debug_nr = uart_nr; - void (*func)(char) = NULL; - switch(s_uart_debug_nr) - { - case UART0: - func = &uart0_write_char; - break; - case UART1: - func = &uart1_write_char; - break; - case UART_NO: - default: - func = &uart_ignore_char; - break; - } - if(gdbstub_has_putc1_control()) { - gdbstub_set_putc1_callback(func); - } else { - if (uart_nr == UART0 || uart_nr == UART1) { - system_set_os_print(1); - } else { - system_set_os_print(0); - } - ets_install_putc1((void *) func); - } -} - -int -uart_get_debug() -{ - return s_uart_debug_nr; -} -/* -To start detection of baud rate with the UART the UART_AUTOBAUD_EN bit needs to be cleared and set. The ROM function uart_baudrate_detect() does this only once, so on a next call the UartDev.rcv_state is not equal to BAUD_RATE_DET. Instead of poking around in the UartDev struct with unknown effect, the UART_AUTOBAUD_EN bit is directly triggered by the function uart_detect_baudrate(). -*/ -void -uart_start_detect_baudrate(int uart_nr) -{ - USA(uart_nr) &= ~(UART_GLITCH_FILT << UART_GLITCH_FILT_S | UART_AUTOBAUD_EN); - USA(uart_nr) = 0x08 << UART_GLITCH_FILT_S | UART_AUTOBAUD_EN; -} + USF(uart_nr) = c; -int -uart_detect_baudrate(int uart_nr) -{ - static bool doTrigger = true; + } + + static void + uart0_write_char(char c) + { + uart_write_char_delay(0, c); + } - if (doTrigger) + static void + uart1_write_char(char c) { - uart_start_detect_baudrate(uart_nr); - doTrigger = false; + uart_write_char_delay(1, c); } - int32_t divisor = uart_baudrate_detect(uart_nr, 1); - if (!divisor) { - return 0; + void + uart_set_debug(int uart_nr) + { + s_uart_debug_nr = uart_nr; + void (*func)(char) = NULL; + switch (s_uart_debug_nr) + { + case UART0: + func = &uart0_write_char; + break; + case UART1: + func = &uart1_write_char; + break; + case UART_NO: + default: + func = &uart_ignore_char; + break; + } + if (gdbstub_has_putc1_control()) + { + gdbstub_set_putc1_callback(func); + } + else + { + if (uart_nr == UART0 || uart_nr == UART1) + { + system_set_os_print(1); + } + else + { + system_set_os_print(0); + } + ets_install_putc1((void *) func); + } } - doTrigger = true; // Initialize for a next round - int32_t baudrate = UART_CLK_FREQ / divisor; + int + uart_get_debug() + { + return s_uart_debug_nr; + } - static const int default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400}; + /* + To start detection of baud rate with the UART the UART_AUTOBAUD_EN bit needs to be cleared and set. The ROM function uart_baudrate_detect() does this only once, so on a next call the UartDev.rcv_state is not equal to BAUD_RATE_DET. Instead of poking around in the UartDev struct with unknown effect, the UART_AUTOBAUD_EN bit is directly triggered by the function uart_detect_baudrate(). + */ + void + uart_start_detect_baudrate(int uart_nr) + { + USA(uart_nr) &= ~(UART_GLITCH_FILT << UART_GLITCH_FILT_S | UART_AUTOBAUD_EN); + USA(uart_nr) = 0x08 << UART_GLITCH_FILT_S | UART_AUTOBAUD_EN; + } - size_t i; - for (i = 1; i < sizeof(default_rates) / sizeof(default_rates[0]) - 1; i++) // find the nearest real baudrate + int + uart_detect_baudrate(int uart_nr) { - if (baudrate <= default_rates[i]) + static bool doTrigger = true; + + if (doTrigger) + { + uart_start_detect_baudrate(uart_nr); + doTrigger = false; + } + + int32_t divisor = uart_baudrate_detect(uart_nr, 1); + if (!divisor) + { + return 0; + } + + doTrigger = true; // Initialize for a next round + int32_t baudrate = UART_CLK_FREQ / divisor; + + static const int default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400}; + + size_t i; + for (i = 1; i < sizeof(default_rates) / sizeof(default_rates[0]) - 1; i++) // find the nearest real baudrate { - if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) { - i--; + if (baudrate <= default_rates[i]) + { + if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) + { + i--; + } + break; } - break; } - } - return default_rates[i]; -} + return default_rates[i]; + } }; diff --git a/cores/esp8266/uart.h b/cores/esp8266/uart.h index 7f9dce0f0a..55c59143c2 100644 --- a/cores/esp8266/uart.h +++ b/cores/esp8266/uart.h @@ -1,22 +1,22 @@ /* - uart.h - UART HAL + uart.h - UART HAL - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ESP_UART_H @@ -77,30 +77,30 @@ extern "C" { #define UART_8O2 ( UART_NB_BIT_8 | UART_PARITY_ODD | UART_NB_STOP_BIT_2 ) /* -#define UART_5N1 0x10 -#define UART_6N1 0x14 -#define UART_7N1 0x18 -#define UART_8N1 0x1c -#define UART_5N2 0x30 -#define UART_6N2 0x34 -#define UART_7N2 0x38 -#define UART_8N2 0x3c -#define UART_5E1 0x12 -#define UART_6E1 0x16 -#define UART_7E1 0x1a -#define UART_8E1 0x1e -#define UART_5E2 0x32 -#define UART_6E2 0x36 -#define UART_7E2 0x3a -#define UART_8E2 0x3e -#define UART_5O1 0x13 -#define UART_6O1 0x17 -#define UART_7O1 0x1b -#define UART_8O1 0x1f -#define UART_5O2 0x33 -#define UART_6O2 0x37 -#define UART_7O2 0x3b -#define UART_8O2 0x3f + #define UART_5N1 0x10 + #define UART_6N1 0x14 + #define UART_7N1 0x18 + #define UART_8N1 0x1c + #define UART_5N2 0x30 + #define UART_6N2 0x34 + #define UART_7N2 0x38 + #define UART_8N2 0x3c + #define UART_5E1 0x12 + #define UART_6E1 0x16 + #define UART_7E1 0x1a + #define UART_8E1 0x1e + #define UART_5E2 0x32 + #define UART_6E2 0x36 + #define UART_7E2 0x3a + #define UART_8E2 0x3e + #define UART_5O1 0x13 + #define UART_6O1 0x17 + #define UART_7O1 0x1b + #define UART_8O1 0x1f + #define UART_5O2 0x33 + #define UART_6O2 0x37 + #define UART_7O2 0x3b + #define UART_8O2 0x3f */ // Options for `mode` argument of uart_init @@ -138,8 +138,8 @@ size_t uart_tx_free(uart_t* uart); void uart_wait_tx_empty(uart_t* uart); void uart_flush(uart_t* uart); -bool uart_has_overrun (uart_t* uart); // returns then clear overrun flag -bool uart_has_rx_error (uart_t* uart); // returns then clear rxerror flag +bool uart_has_overrun(uart_t* uart); // returns then clear overrun flag +bool uart_has_rx_error(uart_t* uart); // returns then clear rxerror flag void uart_set_debug(int uart_nr); int uart_get_debug(); diff --git a/cores/esp8266/umm_malloc/umm_malloc.cpp b/cores/esp8266/umm_malloc/umm_malloc.cpp index 8314c5ec60..a7bc38b28f 100644 --- a/cores/esp8266/umm_malloc/umm_malloc.cpp +++ b/cores/esp8266/umm_malloc/umm_malloc.cpp @@ -1,495 +1,495 @@ -/* ---------------------------------------------------------------------------- - * umm_malloc.c - a memory allocator for embedded systems (microcontrollers) - * - * See copyright notice in LICENSE.TXT - * ---------------------------------------------------------------------------- - * - * R.Hempel 2007-09-22 - Original - * R.Hempel 2008-12-11 - Added MIT License biolerplate - * - realloc() now looks to see if previous block is free - * - made common operations functions - * R.Hempel 2009-03-02 - Added macros to disable tasking - * - Added function to dump heap and check for valid free - * pointer - * R.Hempel 2009-03-09 - Changed name to umm_malloc to avoid conflicts with - * the mm_malloc() library functions - * - Added some test code to assimilate a free block - * with the very block if possible. Complicated and - * not worth the grief. - * D.Frank 2014-04-02 - Fixed heap configuration when UMM_TEST_MAIN is NOT set, - * added user-dependent configuration file umm_malloc_cfg.h - * ---------------------------------------------------------------------------- - * - * Note: when upgrading this file with upstream code, replace all %i with %d in - * printf format strings. ets_printf doesn't handle %i. - * - * ---------------------------------------------------------------------------- - * - * This is a memory management library specifically designed to work with the - * ARM7 embedded processor, but it should work on many other 32 bit processors, - * as well as 16 and 8 bit devices. - * - * ACKNOWLEDGEMENTS - * - * Joerg Wunsch and the avr-libc provided the first malloc() implementation - * that I examined in detail. - * - * http: *www.nongnu.org/avr-libc - * - * Doug Lea's paper on malloc() was another excellent reference and provides - * a lot of detail on advanced memory management techniques such as binning. - * - * http: *g.oswego.edu/dl/html/malloc.html - * - * Bill Dittman provided excellent suggestions, including macros to support - * using these functions in critical sections, and for optimizing realloc() - * 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 - * significantly. - * - * 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 - * allow checking a free pointer to make sure it's valid. - * - * ---------------------------------------------------------------------------- - * - * The memory manager assumes the following things: - * - * 1. The standard POSIX compliant malloc/realloc/free semantics are used - * 2. 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 - * address) is filled in by the linker. - * 3. All memory used by the manager is initialized to 0 as part of the - * runtime startup routine. No other initialization is required. - * - * The fastest linked list implementations use doubly linked lists so that - * 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. - * - * Most memory managers use some kind of list structure made up of pointers - * 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 - * up to 32 bits. - * - * In most embedded systems there is no need for managing large blocks - * of memory dynamically, so a full 32 bit pointer based data structure - * 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! - * - * This memory management library sees the malloc heap as an array of blocks, - * 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 - * bit marks a block as being either free or in use, which will be explained - * later. - * - * The result is that a block of memory on the free list uses just 8 bytes - * instead of 16. - * - * In fact, we go even one step futher when we realize that the free block - * index values are available to store data when the block is allocated. - * - * The overhead of an allocated block is therefore just 4 bytes. - * - * Each memory block holds 8 bytes, and there are up to 32767 blocks - * available, for about 256K of heap space. If that's not enough, you - * can always add more data bytes to the body of the memory block - * at the expense of free block size overhead. - * - * There are a lot of little features and optimizations in this memory - * management system that makes it especially suited to small embedded, but - * the best way to appreciate them is to review the data structures and - * algorithms used, so let's get started. - * - * ---------------------------------------------------------------------------- - * - * We have a general notation for a block that we'll use to describe the - * different scenarios that our memory allocation algorithm must deal with: - * - * +----+----+----+----+ - * c |* n | p | nf | pf | - * +----+----+----+----+ - * - * Where - c is the index of this block +/* ---------------------------------------------------------------------------- + umm_malloc.c - a memory allocator for embedded systems (microcontrollers) + + See copyright notice in LICENSE.TXT + ---------------------------------------------------------------------------- + + R.Hempel 2007-09-22 - Original + R.Hempel 2008-12-11 - Added MIT License biolerplate + - realloc() now looks to see if previous block is free + - made common operations functions + R.Hempel 2009-03-02 - Added macros to disable tasking + - Added function to dump heap and check for valid free + pointer + R.Hempel 2009-03-09 - Changed name to umm_malloc to avoid conflicts with + the mm_malloc() library functions + - Added some test code to assimilate a free block + with the very block if possible. Complicated and + not worth the grief. + D.Frank 2014-04-02 - Fixed heap configuration when UMM_TEST_MAIN is NOT set, + added user-dependent configuration file umm_malloc_cfg.h + ---------------------------------------------------------------------------- + + Note: when upgrading this file with upstream code, replace all %i with %d in + printf format strings. ets_printf doesn't handle %i. + + ---------------------------------------------------------------------------- + + This is a memory management library specifically designed to work with the + ARM7 embedded processor, but it should work on many other 32 bit processors, + as well as 16 and 8 bit devices. + + ACKNOWLEDGEMENTS + + Joerg Wunsch and the avr-libc provided the first malloc() implementation + that I examined in detail. + + http: *www.nongnu.org/avr-libc + + Doug Lea's paper on malloc() was another excellent reference and provides + a lot of detail on advanced memory management techniques such as binning. + + http: *g.oswego.edu/dl/html/malloc.html + + Bill Dittman provided excellent suggestions, including macros to support + using these functions in critical sections, and for optimizing realloc() + 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 + significantly. + + 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 + allow checking a free pointer to make sure it's valid. + + ---------------------------------------------------------------------------- + + The memory manager assumes the following things: + + 1. The standard POSIX compliant malloc/realloc/free semantics are used + 2. 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 + address) is filled in by the linker. + 3. All memory used by the manager is initialized to 0 as part of the + runtime startup routine. No other initialization is required. + + The fastest linked list implementations use doubly linked lists so that + 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. + + Most memory managers use some kind of list structure made up of pointers + 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 + up to 32 bits. + + In most embedded systems there is no need for managing large blocks + of memory dynamically, so a full 32 bit pointer based data structure + 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! + + This memory management library sees the malloc heap as an array of blocks, + 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 + bit marks a block as being either free or in use, which will be explained + later. + + The result is that a block of memory on the free list uses just 8 bytes + instead of 16. + + In fact, we go even one step futher when we realize that the free block + index values are available to store data when the block is allocated. + + The overhead of an allocated block is therefore just 4 bytes. + + Each memory block holds 8 bytes, and there are up to 32767 blocks + available, for about 256K of heap space. If that's not enough, you + can always add more data bytes to the body of the memory block + at the expense of free block size overhead. + + There are a lot of little features and optimizations in this memory + management system that makes it especially suited to small embedded, but + the best way to appreciate them is to review the data structures and + algorithms used, so let's get started. + + ---------------------------------------------------------------------------- + + We have a general notation for a block that we'll use to describe the + different scenarios that our memory allocation algorithm must deal with: + + +----+----+----+----+ + c |* n | p | nf | pf | + +----+----+----+----+ + + Where - c is the index of this block * * is the indicator for a free block - * n is the index of the next block in the heap - * p is the index of the previous block in the heap - * nf is the index of the next block in the free list - * pf is the index of the previous block in the free list - * - * The fact that we have forward and backward links in the block descriptors - * means that malloc() and free() operations can be very fast. It's easy - * to either allocate the whole free item to a new block or to allocate part - * of the free item and leave the rest on the free list without traversing - * the list from front to back first. - * - * The entire block of memory used by the heap is assumed to be initialized - * to 0. The very first block in the heap is special - it't the head of the - * free block list. It is never assimilated with a free block (more on this - * later). - * - * Once a block has been allocated to the application, it looks like this: - * - * +----+----+----+----+ - * c | n | p | ... | - * +----+----+----+----+ - * - * Where - c is the index of this block - * n is the index of the next 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 - * store actual data for the application. It would have been nice to store - * the next and previous free list indexes as well, but that would be a waste - * 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. - * - * The address of the ... area is what is returned to the application - * for data storage. - * - * The following sections describe the scenarios encountered during the - * operation of the library. There are two additional notation conventions: - * - * ?? inside a pointer block means that the data is irrelevant. We don't care - * about it because we don't read or modify it in the scenario being - * described. - * - * ... between memory blocks indicates zero or more additional blocks are - * allocated for use by the upper block. - * - * And 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 - * picture is at a lower address. And the blocks grow downwards their - * block index increases as does their physical address. - * - * Finally, there's one very important characteristic of the individual - * blocks that make up the heap - there can never be two consecutive free - * memory blocks, but there can be consecutive used memory blocks. - * - * The reason is that we always want to have a short free list of the - * largest possible block sizes. By always assimilating a newly freed block - * with adjacent free blocks, we maximize the size of each free memory area. - * - *--------------------------------------------------------------------------- - * - * Operation of malloc right after system startup - * - * As part of the system startup code, all of the heap has been cleared. - * - * During the very first malloc operation, we start traversing the free list - * starting at index 0. The index of the next free block is 0, which means - * we're at the end of the list! - * - * 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 - * list to point at block index 1. - * - * BEFORE AFTER - * - * +----+----+----+----+ +----+----+----+----+ - * 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ - * 1 | 0 | 0 | 0 | 0 | - * +----+----+----+----+ - * - * 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. - * - * 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 - * found. - * - * The current block pointer will be at the end of the free list, and we - * know we're at the end of the list because the nf index is 0, like this: - * - * BEFORE AFTER - * - * +----+----+----+----+ +----+----+----+----+ - * pf |*?? | ?? | cf | ?? | pf |*?? | ?? | lf | ?? | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * p | cf | ?? | ... | p | cf | ?? | ... | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ +----+----+----+----+ - * cf | 0 | p | 0 | pf | c | lf | p | ... | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ - * lf | 0 | cf | 0 | pf | - * +----+----+----+----+ - * - * As we walk the free list looking for a block of size b or larger, we get - * to cf, which is the last item in the free list. We know this because the - * next index is 0. - * - * So we're going to turn cf into the new block of memory, and then create - * a new block that represents the last free entry (lf) and adjust the prev - * index of lf to point at the block we just created. We also need to adjust - * the next index of the new block (c) to point to the last free block. - * - * Note that the next free index of the pf block must point to the new lf - * because cf is no longer a free block! - * - * ---------------------------------------------------------------------------- - * - * Operation of malloc when we have found a block (cf) that will fit the - * current request of b units exactly. - * - * This one is pretty easy, just clear the free list bit in the current - * block and unhook it from the free list. - * - * BEFORE AFTER - * - * +----+----+----+----+ +----+----+----+----+ - * pf |*?? | ?? | cf | ?? | pf |*?? | ?? | nf | ?? | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * p | cf | ?? | ... | p | cf | ?? | ... | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ +----+----+----+----+ Clear the free - * cf |* n | p | nf | pf | cf | n | p | .. | list bit here - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ +----+----+----+----+ - * n | ?? | cf | ... | n | ?? | cf | ... | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * nf |*?? | ?? | ?? | cf | nf | ?? | ?? | ?? | pf | - * +----+----+----+----+ +----+----+----+----+ - * - * Unhooking from the free list is accomplished by adjusting the next and - * prev free list index values in the pf and nf blocks. - * - * ---------------------------------------------------------------------------- - * - * Operation of malloc when we have found a block that will fit the current - * request of b units with some left over. - * - * We'll allocate the new block at the END of the current free block so we - * don't have to change ANY free list pointers. - * - * BEFORE AFTER - * - * +----+----+----+----+ +----+----+----+----+ - * pf |*?? | ?? | cf | ?? | pf |*?? | ?? | cf | ?? | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * p | cf | ?? | ... | p | cf | ?? | ... | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ +----+----+----+----+ - * cf |* n | p | nf | pf | cf |* c | p | nf | pf | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ This is the new - * c | n | cf | .. | block at cf+b - * +----+----+----+----+ - * +----+----+----+----+ +----+----+----+----+ - * n | ?? | cf | ... | n | ?? | c | ... | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * nf |*?? | ?? | ?? | cf | nf | ?? | ?? | ?? | pf | - * +----+----+----+----+ +----+----+----+----+ - * - * This one is prety easy too, except we don't need to mess with the - * free list indexes at all becasue we'll allocate the new block at the - * end of the current free block. We do, however have to adjust the - * indexes in cf, c, and n. - * - * ---------------------------------------------------------------------------- - * - * That covers the initialization and all possible malloc scenarios, so now - * we need to cover the free operation possibilities... - * - * The operation of free depends on the position of the current block being - * freed relative to free list items immediately above or below it. The code - * works like this: - * - * if next block is free - * assimilate with next block already on free list - * if prev block is free - * assimilate with prev block already on free list - * else - * put current block at head of free list - * - * ---------------------------------------------------------------------------- - * - * 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 - * with this one. - * - * Note that c is the block we are freeing up, cf is the free block that - * follows it. - * - * BEFORE AFTER - * - * +----+----+----+----+ +----+----+----+----+ - * pf |*?? | ?? | cf | ?? | pf |*?? | ?? | nf | ?? | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * p | c | ?? | ... | p | c | ?? | ... | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ +----+----+----+----+ This block is - * c | cf | p | ... | c | nn | p | ... | disconnected - * +----+----+----+----+ +----+----+----+----+ from free list, - * +----+----+----+----+ assimilated with - * cf |*nn | c | nf | pf | the next, and - * +----+----+----+----+ ready for step 2 - * +----+----+----+----+ +----+----+----+----+ - * nn | ?? | cf | ?? | ?? | nn | ?? | c | ... | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * nf |*?? | ?? | ?? | cf | nf |*?? | ?? | ?? | pf | - * +----+----+----+----+ +----+----+----+----+ - * - * Take special note that the newly assimilated block (c) is completely - * disconnected from the free list, and it does not have its free list - * bit set. This is important as we move on to step 2 of the procedure... - * - * ---------------------------------------------------------------------------- - * - * Step 2 of the free operation checks if the prev block is free, and if it - * is then assimilate it with this block. - * - * Note that c is the block we are freeing up, pf is the free block that - * precedes it. - * - * BEFORE AFTER - * - * +----+----+----+----+ +----+----+----+----+ This block has - * pf |* c | ?? | nf | ?? | pf |* n | ?? | nf | ?? | assimilated the - * +----+----+----+----+ +----+----+----+----+ current block - * +----+----+----+----+ - * c | n | pf | ... | - * +----+----+----+----+ - * +----+----+----+----+ +----+----+----+----+ - * n | ?? | c | ... | n | ?? | pf | ?? | ?? | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * nf |*?? | ?? | ?? | pf | nf |*?? | ?? | ?? | pf | - * +----+----+----+----+ +----+----+----+----+ - * - * Nothing magic here, except that when we're done, the current block (c) - * is gone since it's been absorbed into the previous free block. Note that - * the previous step guarantees that the next block (n) is not free. - * - * ---------------------------------------------------------------------------- - * - * Step 3 of the free operation only runs if the previous block is not free. - * it just inserts the current block to the head of the free list. - * - * Remember, 0 is always the first block in the memory heap, and it's always - * head of the free list! - * - * BEFORE AFTER - * - * +----+----+----+----+ +----+----+----+----+ - * 0 | ?? | ?? | nf | 0 | 0 | ?? | ?? | c | 0 | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * p | c | ?? | ... | p | c | ?? | ... | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ +----+----+----+----+ - * c | n | p | .. | c |* n | p | nf | 0 | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ +----+----+----+----+ - * n | ?? | c | ... | n | ?? | c | ... | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * nf |*?? | ?? | ?? | 0 | nf |*?? | ?? | ?? | c | - * +----+----+----+----+ +----+----+----+----+ - * - * Again, nothing spectacular here, we're simply adjusting a few pointers - * to make the most recently freed block the first item in the free list. - * - * That's because finding the previous free block would mean a reverse - * traversal of blocks until we found a free one, and it's just easier to - * put it at the head of the list. No traversal is needed. - * - * ---------------------------------------------------------------------------- - * - * Finally, we can cover realloc, which has the following basic operation. - * - * The first thing we do is assimilate up with the next free block of - * memory if possible. This step might help if we're resizing to a bigger - * block of memory. It also helps if we're downsizing and creating a new - * free block with the leftover memory. - * - * First we check to see if the next block is free, and we assimilate it - * to this block if it is. If the previous block is also free, and if - * combining it with the current block would satisfy the request, then we - * assimilate with that block and move the current data down to the new - * location. - * - * Assimilating with the previous free block and moving the data works - * like this: - * - * BEFORE AFTER - * - * +----+----+----+----+ +----+----+----+----+ - * pf |*?? | ?? | cf | ?? | pf |*?? | ?? | nf | ?? | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * cf |* c | ?? | nf | pf | c | n | ?? | ... | The data gets - * +----+----+----+----+ +----+----+----+----+ moved from c to - * +----+----+----+----+ the new data area - * c | n | cf | ... | in cf, then c is - * +----+----+----+----+ adjusted to cf - * +----+----+----+----+ +----+----+----+----+ - * n | ?? | c | ... | n | ?? | c | ?? | ?? | - * +----+----+----+----+ +----+----+----+----+ - * ... ... - * +----+----+----+----+ +----+----+----+----+ - * nf |*?? | ?? | ?? | cf | nf |*?? | ?? | ?? | pf | - * +----+----+----+----+ +----+----+----+----+ - * - * - * Once we're done that, there are three scenarios to consider: - * - * 1. The current block size is exactly the right size, so no more work is - * needed. - * - * 2. The current block is bigger than the new required size, so carve off - * the excess and add it to the free list. - * - * 3. The current block is still smaller than the required size, so malloc - * a new block of the correct size and copy the current data into the new - * block before freeing the current block. - * - * The only one of these scenarios that involves an operation that has not - * yet been described is the second one, and it's shown below: - * - * BEFORE AFTER - * - * +----+----+----+----+ +----+----+----+----+ - * p | c | ?? | ... | p | c | ?? | ... | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ +----+----+----+----+ - * c | n | p | ... | c | s | p | ... | - * +----+----+----+----+ +----+----+----+----+ - * +----+----+----+----+ This is the - * s | n | c | .. | new block at - * +----+----+----+----+ c+blocks - * +----+----+----+----+ +----+----+----+----+ - * n | ?? | c | ... | n | ?? | s | ... | - * +----+----+----+----+ +----+----+----+----+ - * - * Then we call free() with the adress of the data portion of the new - * block (s) which adds it to the free list. - * - * ---------------------------------------------------------------------------- - */ + n is the index of the next block in the heap + p is the index of the previous block in the heap + nf is the index of the next block in the free list + pf is the index of the previous block in the free list + + The fact that we have forward and backward links in the block descriptors + means that malloc() and free() operations can be very fast. It's easy + to either allocate the whole free item to a new block or to allocate part + of the free item and leave the rest on the free list without traversing + the list from front to back first. + + The entire block of memory used by the heap is assumed to be initialized + to 0. The very first block in the heap is special - it't the head of the + free block list. It is never assimilated with a free block (more on this + later). + + Once a block has been allocated to the application, it looks like this: + + +----+----+----+----+ + c | n | p | ... | + +----+----+----+----+ + + Where - c is the index of this block + n is the index of the next 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 + store actual data for the application. It would have been nice to store + the next and previous free list indexes as well, but that would be a waste + 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. + + The address of the ... area is what is returned to the application + for data storage. + + The following sections describe the scenarios encountered during the + operation of the library. There are two additional notation conventions: + + ?? inside a pointer block means that the data is irrelevant. We don't care + about it because we don't read or modify it in the scenario being + described. + + ... between memory blocks indicates zero or more additional blocks are + allocated for use by the upper block. + + And 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 + picture is at a lower address. And the blocks grow downwards their + block index increases as does their physical address. + + Finally, there's one very important characteristic of the individual + blocks that make up the heap - there can never be two consecutive free + memory blocks, but there can be consecutive used memory blocks. + + The reason is that we always want to have a short free list of the + largest possible block sizes. By always assimilating a newly freed block + with adjacent free blocks, we maximize the size of each free memory area. + + --------------------------------------------------------------------------- + + Operation of malloc right after system startup + + As part of the system startup code, all of the heap has been cleared. + + During the very first malloc operation, we start traversing the free list + starting at index 0. The index of the next free block is 0, which means + we're at the end of the list! + + 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 + list to point at block index 1. + + BEFORE AFTER + + +----+----+----+----+ +----+----+----+----+ + 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ + 1 | 0 | 0 | 0 | 0 | + +----+----+----+----+ + + 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. + + 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 + found. + + The current block pointer will be at the end of the free list, and we + know we're at the end of the list because the nf index is 0, like this: + + BEFORE AFTER + + +----+----+----+----+ +----+----+----+----+ + pf |*?? | ?? | cf | ?? | pf |*?? | ?? | lf | ?? | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + p | cf | ?? | ... | p | cf | ?? | ... | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ +----+----+----+----+ + cf | 0 | p | 0 | pf | c | lf | p | ... | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ + lf | 0 | cf | 0 | pf | + +----+----+----+----+ + + As we walk the free list looking for a block of size b or larger, we get + to cf, which is the last item in the free list. We know this because the + next index is 0. + + So we're going to turn cf into the new block of memory, and then create + a new block that represents the last free entry (lf) and adjust the prev + index of lf to point at the block we just created. We also need to adjust + the next index of the new block (c) to point to the last free block. + + Note that the next free index of the pf block must point to the new lf + because cf is no longer a free block! + + ---------------------------------------------------------------------------- + + Operation of malloc when we have found a block (cf) that will fit the + current request of b units exactly. + + This one is pretty easy, just clear the free list bit in the current + block and unhook it from the free list. + + BEFORE AFTER + + +----+----+----+----+ +----+----+----+----+ + pf |*?? | ?? | cf | ?? | pf |*?? | ?? | nf | ?? | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + p | cf | ?? | ... | p | cf | ?? | ... | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ +----+----+----+----+ Clear the free + cf |* n | p | nf | pf | cf | n | p | .. | list bit here + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ +----+----+----+----+ + n | ?? | cf | ... | n | ?? | cf | ... | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + nf |*?? | ?? | ?? | cf | nf | ?? | ?? | ?? | pf | + +----+----+----+----+ +----+----+----+----+ + + Unhooking from the free list is accomplished by adjusting the next and + prev free list index values in the pf and nf blocks. + + ---------------------------------------------------------------------------- + + Operation of malloc when we have found a block that will fit the current + request of b units with some left over. + + We'll allocate the new block at the END of the current free block so we + don't have to change ANY free list pointers. + + BEFORE AFTER + + +----+----+----+----+ +----+----+----+----+ + pf |*?? | ?? | cf | ?? | pf |*?? | ?? | cf | ?? | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + p | cf | ?? | ... | p | cf | ?? | ... | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ +----+----+----+----+ + cf |* n | p | nf | pf | cf |* c | p | nf | pf | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ This is the new + c | n | cf | .. | block at cf+b + +----+----+----+----+ + +----+----+----+----+ +----+----+----+----+ + n | ?? | cf | ... | n | ?? | c | ... | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + nf |*?? | ?? | ?? | cf | nf | ?? | ?? | ?? | pf | + +----+----+----+----+ +----+----+----+----+ + + This one is prety easy too, except we don't need to mess with the + free list indexes at all becasue we'll allocate the new block at the + end of the current free block. We do, however have to adjust the + indexes in cf, c, and n. + + ---------------------------------------------------------------------------- + + That covers the initialization and all possible malloc scenarios, so now + we need to cover the free operation possibilities... + + The operation of free depends on the position of the current block being + freed relative to free list items immediately above or below it. The code + works like this: + + if next block is free + assimilate with next block already on free list + if prev block is free + assimilate with prev block already on free list + else + put current block at head of free list + + ---------------------------------------------------------------------------- + + 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 + with this one. + + Note that c is the block we are freeing up, cf is the free block that + follows it. + + BEFORE AFTER + + +----+----+----+----+ +----+----+----+----+ + pf |*?? | ?? | cf | ?? | pf |*?? | ?? | nf | ?? | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + p | c | ?? | ... | p | c | ?? | ... | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ +----+----+----+----+ This block is + c | cf | p | ... | c | nn | p | ... | disconnected + +----+----+----+----+ +----+----+----+----+ from free list, + +----+----+----+----+ assimilated with + cf |*nn | c | nf | pf | the next, and + +----+----+----+----+ ready for step 2 + +----+----+----+----+ +----+----+----+----+ + nn | ?? | cf | ?? | ?? | nn | ?? | c | ... | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + nf |*?? | ?? | ?? | cf | nf |*?? | ?? | ?? | pf | + +----+----+----+----+ +----+----+----+----+ + + Take special note that the newly assimilated block (c) is completely + disconnected from the free list, and it does not have its free list + bit set. This is important as we move on to step 2 of the procedure... + + ---------------------------------------------------------------------------- + + Step 2 of the free operation checks if the prev block is free, and if it + is then assimilate it with this block. + + Note that c is the block we are freeing up, pf is the free block that + precedes it. + + BEFORE AFTER + + +----+----+----+----+ +----+----+----+----+ This block has + pf |* c | ?? | nf | ?? | pf |* n | ?? | nf | ?? | assimilated the + +----+----+----+----+ +----+----+----+----+ current block + +----+----+----+----+ + c | n | pf | ... | + +----+----+----+----+ + +----+----+----+----+ +----+----+----+----+ + n | ?? | c | ... | n | ?? | pf | ?? | ?? | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + nf |*?? | ?? | ?? | pf | nf |*?? | ?? | ?? | pf | + +----+----+----+----+ +----+----+----+----+ + + Nothing magic here, except that when we're done, the current block (c) + is gone since it's been absorbed into the previous free block. Note that + the previous step guarantees that the next block (n) is not free. + + ---------------------------------------------------------------------------- + + Step 3 of the free operation only runs if the previous block is not free. + it just inserts the current block to the head of the free list. + + Remember, 0 is always the first block in the memory heap, and it's always + head of the free list! + + BEFORE AFTER + + +----+----+----+----+ +----+----+----+----+ + 0 | ?? | ?? | nf | 0 | 0 | ?? | ?? | c | 0 | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + p | c | ?? | ... | p | c | ?? | ... | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ +----+----+----+----+ + c | n | p | .. | c |* n | p | nf | 0 | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ +----+----+----+----+ + n | ?? | c | ... | n | ?? | c | ... | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + nf |*?? | ?? | ?? | 0 | nf |*?? | ?? | ?? | c | + +----+----+----+----+ +----+----+----+----+ + + Again, nothing spectacular here, we're simply adjusting a few pointers + to make the most recently freed block the first item in the free list. + + That's because finding the previous free block would mean a reverse + traversal of blocks until we found a free one, and it's just easier to + put it at the head of the list. No traversal is needed. + + ---------------------------------------------------------------------------- + + Finally, we can cover realloc, which has the following basic operation. + + The first thing we do is assimilate up with the next free block of + memory if possible. This step might help if we're resizing to a bigger + block of memory. It also helps if we're downsizing and creating a new + free block with the leftover memory. + + First we check to see if the next block is free, and we assimilate it + to this block if it is. If the previous block is also free, and if + combining it with the current block would satisfy the request, then we + assimilate with that block and move the current data down to the new + location. + + Assimilating with the previous free block and moving the data works + like this: + + BEFORE AFTER + + +----+----+----+----+ +----+----+----+----+ + pf |*?? | ?? | cf | ?? | pf |*?? | ?? | nf | ?? | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + cf |* c | ?? | nf | pf | c | n | ?? | ... | The data gets + +----+----+----+----+ +----+----+----+----+ moved from c to + +----+----+----+----+ the new data area + c | n | cf | ... | in cf, then c is + +----+----+----+----+ adjusted to cf + +----+----+----+----+ +----+----+----+----+ + n | ?? | c | ... | n | ?? | c | ?? | ?? | + +----+----+----+----+ +----+----+----+----+ + ... ... + +----+----+----+----+ +----+----+----+----+ + nf |*?? | ?? | ?? | cf | nf |*?? | ?? | ?? | pf | + +----+----+----+----+ +----+----+----+----+ + + + Once we're done that, there are three scenarios to consider: + + 1. The current block size is exactly the right size, so no more work is + needed. + + 2. The current block is bigger than the new required size, so carve off + the excess and add it to the free list. + + 3. The current block is still smaller than the required size, so malloc + a new block of the correct size and copy the current data into the new + block before freeing the current block. + + The only one of these scenarios that involves an operation that has not + yet been described is the second one, and it's shown below: + + BEFORE AFTER + + +----+----+----+----+ +----+----+----+----+ + p | c | ?? | ... | p | c | ?? | ... | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ +----+----+----+----+ + c | n | p | ... | c | s | p | ... | + +----+----+----+----+ +----+----+----+----+ + +----+----+----+----+ This is the + s | n | c | .. | new block at + +----+----+----+----+ c+blocks + +----+----+----+----+ +----+----+----+----+ + n | ?? | c | ... | n | ?? | s | ... | + +----+----+----+----+ +----+----+----+----+ + + Then we call free() with the adress of the data portion of the new + block (s) which adds it to the free list. + + ---------------------------------------------------------------------------- +*/ #include #include @@ -501,9 +501,9 @@ extern "C" { -// From UMM, the last caller of a malloc/realloc/calloc which failed: -extern void *umm_last_fail_alloc_addr; -extern int umm_last_fail_alloc_size; + // From UMM, the last caller of a malloc/realloc/calloc which failed: + extern void *umm_last_fail_alloc_addr; + extern int umm_last_fail_alloc_size; #ifndef UMM_FIRST_FIT # ifndef UMM_BEST_FIT @@ -519,37 +519,37 @@ extern int umm_last_fail_alloc_size; # define DBG_LOG_LEVEL DBG_LOG_LEVEL #endif -// Macro to place constant strings into PROGMEM and print them properly + // Macro to place constant strings into PROGMEM and print them properly #define printf(fmt, ...) do { static const char fstr[] PROGMEM = fmt; char rstr[sizeof(fmt)]; for (size_t i=0; i= 6 # define DBG_LOG_TRACE( format, ... ) printf( format, ## __VA_ARGS__ ) @@ -599,30 +599,34 @@ extern int umm_last_fail_alloc_size; #define DBG_LOG_FORCE( force, format, ... ) {if(force) {printf( format, ## __VA_ARGS__ );}} -/* }}} */ + /* }}} */ -/* ------------------------------------------------------------------------- */ + /* ------------------------------------------------------------------------- */ -UMM_H_ATTPACKPRE typedef struct umm_ptr_t { - unsigned short int next; - unsigned short int prev; -} UMM_H_ATTPACKSUF umm_ptr; + UMM_H_ATTPACKPRE typedef struct umm_ptr_t + { + unsigned short int next; + unsigned short int prev; + } UMM_H_ATTPACKSUF umm_ptr; -UMM_H_ATTPACKPRE typedef struct umm_block_t { - union { - umm_ptr used; - } header; - union { - umm_ptr free; - unsigned char data[4]; - } body; -} UMM_H_ATTPACKSUF umm_block; + UMM_H_ATTPACKPRE typedef struct umm_block_t + { + union + { + umm_ptr used; + } header; + union + { + umm_ptr free; + unsigned char data[4]; + } body; + } UMM_H_ATTPACKSUF umm_block; #define UMM_FREELIST_MASK (0x8000) #define UMM_BLOCKNO_MASK (0x7FFF) -/* ------------------------------------------------------------------------- */ + /* ------------------------------------------------------------------------- */ #ifdef UMM_REDEFINE_MEM_FUNCTIONS # define umm_free free @@ -631,12 +635,12 @@ UMM_H_ATTPACKPRE typedef struct umm_block_t { # define umm_realloc realloc #endif -umm_block *umm_heap = NULL; -unsigned short int umm_numblocks = 0; + umm_block *umm_heap = NULL; + unsigned short int umm_numblocks = 0; #define UMM_NUMBLOCKS (umm_numblocks) -/* ------------------------------------------------------------------------ */ + /* ------------------------------------------------------------------------ */ #define UMM_BLOCK(b) (umm_heap[b]) @@ -646,141 +650,152 @@ unsigned short int umm_numblocks = 0; #define UMM_PFREE(b) (UMM_BLOCK(b).body.free.prev) #define UMM_DATA(b) (UMM_BLOCK(b).body.data) -/* integrity check (UMM_INTEGRITY_CHECK) {{{ */ + /* 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. - */ -static int integrity_check(void) { - int ok = 1; - unsigned short int prev; - unsigned short int cur; - - if (umm_heap == NULL) { - umm_init(); - } - - /* Iterate through all free blocks */ - prev = 0; - while(1) { - cur = UMM_NFREE(prev); - - /* Check that next free block number is valid */ - if (cur >= UMM_NUMBLOCKS) { - printf("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) { - printf("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) { - printf("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)) + /* + 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. + */ + static int integrity_check(void) { - printf("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; - } - - /* unmark */ - UMM_PBLOCK(cur) &= UMM_BLOCKNO_MASK; - - /* Check if prev block number matches */ - if (UMM_PBLOCK(cur) != prev) { - printf("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; - } + int ok = 1; + unsigned short int prev; + unsigned short int cur; + + if (umm_heap == NULL) + { + umm_init(); + } + + /* Iterate through all free blocks */ + prev = 0; + while (1) + { + cur = UMM_NFREE(prev); + + /* Check that next free block number is valid */ + if (cur >= UMM_NUMBLOCKS) + { + printf("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) + { + printf("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) + { + printf("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)) + { + printf("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; + } + + /* unmark */ + UMM_PBLOCK(cur) &= UMM_BLOCKNO_MASK; + + /* Check if prev block number matches */ + if (UMM_PBLOCK(cur) != prev) + { + printf("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: - if (!ok){ - UMM_HEAP_CORRUPTION_CB(); - } - return ok; -} + if (!ok) + { + UMM_HEAP_CORRUPTION_CB(); + } + return ok; + } #define INTEGRITY_CHECK() integrity_check() #else -/* - * Integrity check is disabled, so just define stub macro - */ + /* + Integrity check is disabled, so just define stub macro + */ #define INTEGRITY_CHECK() 1 #endif -/* }}} */ + /* }}} */ -/* poisoning (UMM_POISON) {{{ */ + /* poisoning (UMM_POISON) {{{ */ #if defined(UMM_POISON) #define POISON_BYTE (0xa5) -/* - * Yields a size of the poison for the block of size `s`. - * If `s` is 0, returns 0. - */ + /* + Yields a size of the poison for the block of size `s`. + If `s` is 0, returns 0. + */ #define POISON_SIZE(s) ( \ (s) ? \ (UMM_POISON_SIZE_BEFORE + UMM_POISON_SIZE_AFTER + \ @@ -788,996 +803,1090 @@ static int integrity_check(void) { ) : 0 \ ) -/* - * Print memory contents starting from given `ptr` - */ -static void dump_mem ( const unsigned char *ptr, size_t len ) { - while (len--) { - printf(" 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; + /* + Print memory contents starting from given `ptr` + */ + static void dump_mem(const unsigned char *ptr, size_t len) + { + while (len--) + { + printf(" 0x%.2x", (unsigned int)(*ptr++)); + } } - } - - if (!ok) { - printf("there is no poison %s the block. " - "Expected poison address: 0x%lx, actual data:", - where, (unsigned long)ptr); - dump_mem(ptr, poison_size); - printf("\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) { - printf("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")) { - printf("block start: %08x\n", pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE); - UMM_HEAP_CORRUPTION_CB(); - ok = 0; - goto clean; + + /* + 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); } - pc_cur = pc + *((UMM_POISONED_BLOCK_LEN_TYPE *)pc) - UMM_POISON_SIZE_AFTER; - if (!check_poison(pc_cur, UMM_POISON_SIZE_AFTER, "after")) { - printf("block start: %08x\n", pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE); - UMM_HEAP_CORRUPTION_CB(); - ok = 0; - goto clean; + /* + 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) + { + printf("there is no poison %s the block. " + "Expected poison address: 0x%lx, actual data:", + where, (unsigned long)ptr); + dump_mem(ptr, poison_size); + printf("\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) + { + printf("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")) + { + printf("block start: %08x\n", pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE); + UMM_HEAP_CORRUPTION_CB(); + 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")) + { + printf("block start: %08x\n", pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE); + UMM_HEAP_CORRUPTION_CB(); + ok = 0; + goto clean; + } + } clean: - return ok; -} - -/* - * Iterates through all blocks in the heap, and checks poison for all used - * blocks. - */ -static int check_poison_all_blocks(void) { - int ok = 1; - unsigned short int blockNo = 0; - - if (umm_heap == NULL) { - umm_init(); - } - - /* Now iterate through the blocks list */ - blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; - - while( UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK ) { - if ( !(UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK) ) { - /* This is a used block (not free), so, check its poison */ - ok = check_poison_block(&UMM_BLOCK(blockNo)); - if (!ok){ - break; - } + return ok; } - blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; - } - - 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 *vptr, size_t size_w_poison ) { - unsigned char *ptr = (unsigned char *)vptr; - if (size_w_poison != 0 && ptr != NULL) { - - /* Put exact length of the user's chunk of memory */ - memcpy(ptr, &size_w_poison, sizeof(UMM_POISONED_BLOCK_LEN_TYPE)); - - /* 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); - - /* Return pointer at the first non-poisoned byte */ - return ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE; - } else { - return 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 *vptr ) { - unsigned char *ptr = (unsigned char *)vptr; - 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 ptr; -} + /* + Iterates through all blocks in the heap, and checks poison for all used + blocks. + */ + static int check_poison_all_blocks(void) + { + int ok = 1; + unsigned short int blockNo = 0; + + if (umm_heap == NULL) + { + umm_init(); + } + + /* Now iterate through the blocks list */ + blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; + + while (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK) + { + if (!(UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK)) + { + /* This is a used block (not free), so, check its poison */ + ok = check_poison_block(&UMM_BLOCK(blockNo)); + if (!ok) + { + break; + } + } + + blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; + } + + 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 *vptr, size_t size_w_poison) + { + unsigned char *ptr = (unsigned char *)vptr; + if (size_w_poison != 0 && ptr != NULL) + { + + /* Put exact length of the user's chunk of memory */ + memcpy(ptr, &size_w_poison, sizeof(UMM_POISONED_BLOCK_LEN_TYPE)); + + /* 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); + + /* Return pointer at the first non-poisoned byte */ + return ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE; + } + else + { + return 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 *vptr) + { + unsigned char *ptr = (unsigned char *)vptr; + 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 ptr; + } #define CHECK_POISON_ALL_BLOCKS() check_poison_all_blocks() #define GET_POISONED(ptr, size) get_poisoned(ptr, size) #define GET_UNPOISONED(ptr) get_unpoisoned(ptr) #else -/* - * Integrity check is disabled, so just define stub macros - */ + /* + Integrity check is disabled, so just define stub macros + */ #define POISON_SIZE(s) 0 #define CHECK_POISON_ALL_BLOCKS() 1 #define GET_POISONED(ptr, size) (ptr) #define GET_UNPOISONED(ptr) (ptr) #endif -/* }}} */ - -/* ---------------------------------------------------------------------------- - * 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 ICACHE_FLASH_ATTR *umm_info( void *ptr, int force ) { - - unsigned short int blockNo = 0; - - if (umm_heap == NULL) { - umm_init(); - } - - /* Protect the critical section... */ - UMM_CRITICAL_ENTRY(); - - /* - * Clear out all of the entries in the ummHeapInfo structure before doing - * any calculations.. - */ - memset( &ummHeapInfo, 0, sizeof( ummHeapInfo ) ); - - DBG_LOG_FORCE( force, "\n\nDumping the umm_heap...\n" ); - - DBG_LOG_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; - } - - DBG_LOG_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(); + /* }}} */ - return( ptr ); - } - } else { - ++ummHeapInfo.usedEntries; - ummHeapInfo.usedBlocks += curBlocks; - - DBG_LOG_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 ); - } + /* ---------------------------------------------------------------------------- + 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. - blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; - } + 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. - /* - * Update the accounting totals with information from the last block, the - * rest must be free! - */ + The umm_info function does all of that and makes the results available + in the ummHeapInfo structure. + ---------------------------------------------------------------------------- + */ - { - size_t curBlocks = UMM_NUMBLOCKS-blockNo; - ummHeapInfo.freeBlocks += curBlocks; - ummHeapInfo.totalBlocks += curBlocks; + UMM_HEAP_INFO ummHeapInfo; - if (ummHeapInfo.maxFreeContiguousBlocks < curBlocks) { - ummHeapInfo.maxFreeContiguousBlocks = curBlocks; - } - } - - DBG_LOG_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) ); - - DBG_LOG_FORCE( force, "Total Entries %5d Used Entries %5d Free Entries %5d\n", - ummHeapInfo.totalEntries, - ummHeapInfo.usedEntries, - ummHeapInfo.freeEntries ); - - DBG_LOG_FORCE( force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n", - ummHeapInfo.totalBlocks, - ummHeapInfo.usedBlocks, - ummHeapInfo.freeBlocks ); + void ICACHE_FLASH_ATTR *umm_info(void *ptr, int force) + { - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); + unsigned short int blockNo = 0; + + if (umm_heap == NULL) + { + umm_init(); + } + + /* Protect the critical section... */ + UMM_CRITICAL_ENTRY(); + + /* + Clear out all of the entries in the ummHeapInfo structure before doing + any calculations.. + */ + memset(&ummHeapInfo, 0, sizeof(ummHeapInfo)); + + DBG_LOG_FORCE(force, "\n\nDumping the umm_heap...\n"); + + DBG_LOG_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; + } + + DBG_LOG_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(); + + return (ptr); + } + } + else + { + ++ummHeapInfo.usedEntries; + ummHeapInfo.usedBlocks += curBlocks; + + DBG_LOG_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; + } + } + + DBG_LOG_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)); + + DBG_LOG_FORCE(force, "Total Entries %5d Used Entries %5d Free Entries %5d\n", + ummHeapInfo.totalEntries, + ummHeapInfo.usedEntries, + ummHeapInfo.freeEntries); + + DBG_LOG_FORCE(force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n", + ummHeapInfo.totalBlocks, + ummHeapInfo.usedBlocks, + ummHeapInfo.freeBlocks); - return( NULL ); -} + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); -/* ------------------------------------------------------------------------ */ + return (NULL); + } -static unsigned short int umm_blocks( size_t size ) { + /* ------------------------------------------------------------------------ */ - /* - * The calculation of the block size is not too difficult, but there are - * a few little things that we need to be mindful of. - * - * When a block removed from the free list, the space used by the free - * pointers is available for data. That's what the first calculation - * of size is doing. - */ + static unsigned short int umm_blocks(size_t size) + { - if( size <= (sizeof(((umm_block *)0)->body)) ) - return( 1 ); + /* + The calculation of the block size is not too difficult, but there are + a few little things that we need to be mindful of. - /* - * If it's for more than that, then we need to figure out the number of - * additional whole blocks the size of an umm_block are required. - */ + When a block removed from the free list, the space used by the free + pointers is available for data. That's what the first calculation + of size is doing. + */ - size -= ( 1 + (sizeof(((umm_block *)0)->body)) ); + if (size <= (sizeof(((umm_block *)0)->body))) + { + return (1); + } - return( 2 + size/(sizeof(umm_block)) ); -} + /* + If it's for more than that, then we need to figure out the number of + additional whole blocks the size of an umm_block are required. + */ -/* ------------------------------------------------------------------------ */ + size -= (1 + (sizeof(((umm_block *)0)->body))); -/* - * Split the block `c` into two blocks: `c` and `c + blocks`. - * - * - `cur_freemask` should be `0` if `c` used, or `UMM_FREELIST_MASK` - * otherwise. - * - `new_freemask` should be `0` if `c + blocks` used, or `UMM_FREELIST_MASK` - * otherwise. - * - * Note that free pointers are NOT modified by this function. - */ -static void umm_make_new_block( unsigned short int c, - unsigned short int blocks, - unsigned short int cur_freemask, unsigned short int new_freemask ) { - - UMM_NBLOCK(c+blocks) = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) | new_freemask; - UMM_PBLOCK(c+blocks) = c; - - UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) = (c+blocks); - UMM_NBLOCK(c) = (c+blocks) | cur_freemask; -} + return (2 + size / (sizeof(umm_block))); + } -/* ------------------------------------------------------------------------ */ + /* ------------------------------------------------------------------------ */ -static void umm_disconnect_from_free_list( unsigned short int c ) { - /* Disconnect this block from the FREE list */ + /* + Split the block `c` into two blocks: `c` and `c + blocks`. + + - `cur_freemask` should be `0` if `c` used, or `UMM_FREELIST_MASK` + otherwise. + - `new_freemask` should be `0` if `c + blocks` used, or `UMM_FREELIST_MASK` + otherwise. + + Note that free pointers are NOT modified by this function. + */ + static void umm_make_new_block(unsigned short int c, + unsigned short int blocks, + unsigned short int cur_freemask, unsigned short int new_freemask) + { - UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); - UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); + UMM_NBLOCK(c + blocks) = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) | new_freemask; + UMM_PBLOCK(c + blocks) = c; - /* And clear the free block indicator */ + UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) = (c + blocks); + UMM_NBLOCK(c) = (c + blocks) | cur_freemask; + } - UMM_NBLOCK(c) &= (~UMM_FREELIST_MASK); -} + /* ------------------------------------------------------------------------ */ -/* ------------------------------------------------------------------------ */ + static void umm_disconnect_from_free_list(unsigned short int c) + { + /* Disconnect this block from the FREE list */ -static void umm_assimilate_up( unsigned short int c ) { + UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); + UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); - if( UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK ) { - /* - * The next block is a free block, so assimilate up and remove it from - * the free list - */ + /* And clear the free block indicator */ - DBG_LOG_DEBUG( "Assimilate up to next block, which is FREE\n" ); + UMM_NBLOCK(c) &= (~UMM_FREELIST_MASK); + } - /* Disconnect the next block from the FREE list */ + /* ------------------------------------------------------------------------ */ - umm_disconnect_from_free_list( UMM_NBLOCK(c) ); + static void umm_assimilate_up(unsigned short int c) + { - /* Assimilate the next block with this one */ + if (UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK) + { + /* + The next block is a free block, so assimilate up and remove it from + the free list + */ - UMM_PBLOCK(UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) = c; - UMM_NBLOCK(c) = UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK; - } -} + DBG_LOG_DEBUG("Assimilate up to next block, which is FREE\n"); -/* ------------------------------------------------------------------------ */ + /* Disconnect the next block from the FREE list */ -static unsigned short int umm_assimilate_down( unsigned short int c, unsigned short int freemask ) { + umm_disconnect_from_free_list(UMM_NBLOCK(c)); - UMM_NBLOCK(UMM_PBLOCK(c)) = UMM_NBLOCK(c) | freemask; - UMM_PBLOCK(UMM_NBLOCK(c)) = UMM_PBLOCK(c); + /* Assimilate the next block with this one */ - return( UMM_PBLOCK(c) ); -} + UMM_PBLOCK(UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) = c; + UMM_NBLOCK(c) = UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK; + } + } -/* ------------------------------------------------------------------------- */ -/* This function called only one time during OS startup after flash is */ -/* enabled. No need to keep it in IRAM. */ -void ICACHE_FLASH_ATTR umm_init( void ) { - /* init heap pointer and size, and memset it to 0 */ - umm_heap = (umm_block *)UMM_MALLOC_CFG__HEAP_ADDR; - umm_numblocks = (UMM_MALLOC_CFG__HEAP_SIZE / sizeof(umm_block)); - memset(umm_heap, 0x00, UMM_MALLOC_CFG__HEAP_SIZE); + /* ------------------------------------------------------------------------ */ - /* setup initial blank heap structure */ - { - /* index of the 0th `umm_block` */ - const unsigned short int block_0th = 0; - /* index of the 1st `umm_block` */ - const unsigned short int block_1th = 1; - /* index of the latest `umm_block` */ - const unsigned short int block_last = UMM_NUMBLOCKS - 1; + static unsigned short int umm_assimilate_down(unsigned short int c, unsigned short int freemask) + { - /* setup the 0th `umm_block`, which just points to the 1st */ - UMM_NBLOCK(block_0th) = block_1th; - UMM_NFREE(block_0th) = block_1th; + UMM_NBLOCK(UMM_PBLOCK(c)) = UMM_NBLOCK(c) | freemask; + UMM_PBLOCK(UMM_NBLOCK(c)) = UMM_PBLOCK(c); - /* - * Now, we need to set the whole heap space as a huge free block. We should - * not touch the 0th `umm_block`, since it's special: the 0th `umm_block` - * is the head of the free block list. It's a part of the heap invariant. - * - * See the detailed explanation at the beginning of the file. - */ + return (UMM_PBLOCK(c)); + } - /* - * 1th `umm_block` has pointers: - * - * - next `umm_block`: the latest one - * - prev `umm_block`: the 0th - * - * Plus, it's a free `umm_block`, so we need to apply `UMM_FREELIST_MASK` - * - * And it's the last free block, so the next free block is 0. - */ - UMM_NBLOCK(block_1th) = block_last | UMM_FREELIST_MASK; - UMM_NFREE(block_1th) = 0; - UMM_PBLOCK(block_1th) = block_0th; - UMM_PFREE(block_1th) = block_0th; + /* ------------------------------------------------------------------------- */ + /* This function called only one time during OS startup after flash is */ + /* enabled. No need to keep it in IRAM. */ + void ICACHE_FLASH_ATTR umm_init(void) + { + /* init heap pointer and size, and memset it to 0 */ + umm_heap = (umm_block *)UMM_MALLOC_CFG__HEAP_ADDR; + umm_numblocks = (UMM_MALLOC_CFG__HEAP_SIZE / sizeof(umm_block)); + memset(umm_heap, 0x00, UMM_MALLOC_CFG__HEAP_SIZE); + + /* setup initial blank heap structure */ + { + /* index of the 0th `umm_block` */ + const unsigned short int block_0th = 0; + /* index of the 1st `umm_block` */ + const unsigned short int block_1th = 1; + /* index of the latest `umm_block` */ + const unsigned short int block_last = UMM_NUMBLOCKS - 1; + + /* setup the 0th `umm_block`, which just points to the 1st */ + UMM_NBLOCK(block_0th) = block_1th; + UMM_NFREE(block_0th) = block_1th; + + /* + Now, we need to set the whole heap space as a huge free block. We should + not touch the 0th `umm_block`, since it's special: the 0th `umm_block` + is the head of the free block list. It's a part of the heap invariant. + + See the detailed explanation at the beginning of the file. + */ + + /* + 1th `umm_block` has pointers: + + - next `umm_block`: the latest one + - prev `umm_block`: the 0th + + Plus, it's a free `umm_block`, so we need to apply `UMM_FREELIST_MASK` + + And it's the last free block, so the next free block is 0. + */ + UMM_NBLOCK(block_1th) = block_last | UMM_FREELIST_MASK; + UMM_NFREE(block_1th) = 0; + UMM_PBLOCK(block_1th) = block_0th; + UMM_PFREE(block_1th) = block_0th; + + /* + latest `umm_block` has pointers: + + - next `umm_block`: 0 (meaning, there are no more `umm_blocks`) + - prev `umm_block`: the 1st + + It's not a free block, so we don't touch NFREE / PFREE at all. + */ + UMM_NBLOCK(block_last) = 0; + UMM_PBLOCK(block_last) = block_1th; + } + } - /* - * latest `umm_block` has pointers: - * - * - next `umm_block`: 0 (meaning, there are no more `umm_blocks`) - * - prev `umm_block`: the 1st - * - * It's not a free block, so we don't touch NFREE / PFREE at all. - */ - UMM_NBLOCK(block_last) = 0; - UMM_PBLOCK(block_last) = block_1th; - } -} + /* ------------------------------------------------------------------------ */ -/* ------------------------------------------------------------------------ */ + static void _umm_free(void *ptr) + { -static void _umm_free( void *ptr ) { + unsigned short int c; - unsigned short int c; + /* If we're being asked to free a NULL pointer, well that's just silly! */ - /* If we're being asked to free a NULL pointer, well that's just silly! */ + if ((void *)0 == ptr) + { + DBG_LOG_DEBUG("free a null pointer -> do nothing\n"); - if( (void *)0 == ptr ) { - DBG_LOG_DEBUG( "free a null pointer -> do nothing\n" ); + return; + } - return; - } + /* + FIXME: At some point it might be a good idea to add a check to make sure + that the pointer we're being asked to free up is actually within + the umm_heap! - /* - * FIXME: At some point it might be a good idea to add a check to make sure - * that the pointer we're being asked to free up is actually within - * the umm_heap! - * - * NOTE: See the new umm_info() function that you can use to see if a ptr is - * on the free list! - */ + NOTE: See the new umm_info() function that you can use to see if a ptr is + on the free list! + */ - /* Protect the critical section... */ - UMM_CRITICAL_ENTRY(); + /* Protect the critical section... */ + UMM_CRITICAL_ENTRY(); - /* Figure out which block we're in. Note the use of truncated division... */ + /* Figure out which block we're in. Note the use of truncated division... */ - c = (((char *)ptr)-(char *)(&(umm_heap[0])))/sizeof(umm_block); + c = (((char *)ptr) - (char *)(&(umm_heap[0]))) / sizeof(umm_block); - DBG_LOG_DEBUG( "Freeing block %6d\n", c ); + DBG_LOG_DEBUG("Freeing block %6d\n", c); - /* Now let's assimilate this block with the next one if possible. */ + /* Now let's assimilate this block with the next one if possible. */ - umm_assimilate_up( c ); + umm_assimilate_up(c); - /* Then assimilate with the previous block if possible */ + /* Then assimilate with the previous block if possible */ - if( UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK ) { + if (UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK) + { - DBG_LOG_DEBUG( "Assimilate down to next block, which is FREE\n" ); + DBG_LOG_DEBUG("Assimilate down to next block, which is FREE\n"); - c = umm_assimilate_down(c, UMM_FREELIST_MASK); - } else { - /* - * The previous block is not a free block, so add this one to the head - * of the free list - */ + c = umm_assimilate_down(c, UMM_FREELIST_MASK); + } + else + { + /* + The previous block is not a free block, so add this one to the head + of the free list + */ - DBG_LOG_DEBUG( "Just add to head of free list\n" ); + DBG_LOG_DEBUG("Just add to head of free list\n"); - UMM_PFREE(UMM_NFREE(0)) = c; - UMM_NFREE(c) = UMM_NFREE(0); - UMM_PFREE(c) = 0; - UMM_NFREE(0) = c; + UMM_PFREE(UMM_NFREE(0)) = c; + UMM_NFREE(c) = UMM_NFREE(0); + UMM_PFREE(c) = 0; + UMM_NFREE(0) = c; - UMM_NBLOCK(c) |= UMM_FREELIST_MASK; - } + UMM_NBLOCK(c) |= UMM_FREELIST_MASK; + } #if 0 - /* - * The following is experimental code that checks to see if the block we just - * freed can be assimilated with the very last block - it's pretty convoluted in - * terms of block index manipulation, and has absolutely no effect on heap - * fragmentation. I'm not sure that it's worth including but I've left it - * here for posterity. - */ - - if( 0 == UMM_NBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK ) ) { - - if( UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) != UMM_PFREE(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) ) { - UMM_NFREE(UMM_PFREE(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK)) = c; - UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); - UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); - UMM_PFREE(c) = UMM_PFREE(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK); - } - - UMM_NFREE(c) = 0; - UMM_NBLOCK(c) = 0; - } + /* + The following is experimental code that checks to see if the block we just + freed can be assimilated with the very last block - it's pretty convoluted in + terms of block index manipulation, and has absolutely no effect on heap + fragmentation. I'm not sure that it's worth including but I've left it + here for posterity. + */ + + if (0 == UMM_NBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK)) + { + + if (UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) != UMM_PFREE(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK)) + { + UMM_NFREE(UMM_PFREE(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK)) = c; + UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); + UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); + UMM_PFREE(c) = UMM_PFREE(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK); + } + + UMM_NFREE(c) = 0; + UMM_NBLOCK(c) = 0; + } #endif - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); -} + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); + } -/* ------------------------------------------------------------------------ */ + /* ------------------------------------------------------------------------ */ + + static void *_umm_malloc(size_t size) + { + unsigned short int blocks; + unsigned short int blockSize = 0; -static void *_umm_malloc( size_t size ) { - unsigned short int blocks; - unsigned short int blockSize = 0; + unsigned short int bestSize; + unsigned short int bestBlock; - unsigned short int bestSize; - unsigned short int bestBlock; + unsigned short int cf; - unsigned short int cf; + if (umm_heap == NULL) + { + umm_init(); + } - if (umm_heap == NULL) { - umm_init(); - } + /* + the very first thing we do is figure out if we're being asked to allocate + a size of 0 - and if we are we'll simply return a null pointer. if not + then reduce the size by 1 byte so that the subsequent calculations on + the number of blocks to allocate are easier... + */ - /* - * the very first thing we do is figure out if we're being asked to allocate - * a size of 0 - and if we are we'll simply return a null pointer. if not - * then reduce the size by 1 byte so that the subsequent calculations on - * the number of blocks to allocate are easier... - */ + if (0 == size) + { + DBG_LOG_DEBUG("malloc a block of 0 bytes -> do nothing\n"); - if( 0 == size ) { - DBG_LOG_DEBUG( "malloc a block of 0 bytes -> do nothing\n" ); + return ((void *)NULL); + } - return( (void *)NULL ); - } + /* Protect the critical section... */ + UMM_CRITICAL_ENTRY(); - /* Protect the critical section... */ - UMM_CRITICAL_ENTRY(); + blocks = umm_blocks(size); - blocks = umm_blocks( size ); + /* + Now we can scan through the free list until we find a space that's big + enough to hold the number of blocks we need. - /* - * Now we can scan through the free list until we find a space that's big - * enough to hold the number of blocks we need. - * - * This part may be customized to be a best-fit, worst-fit, or first-fit - * algorithm - */ + This part may be customized to be a best-fit, worst-fit, or first-fit + algorithm + */ - cf = UMM_NFREE(0); + cf = UMM_NFREE(0); - bestBlock = UMM_NFREE(0); - bestSize = 0x7FFF; + bestBlock = UMM_NFREE(0); + bestSize = 0x7FFF; - while( cf ) { - blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf; + while (cf) + { + blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf; - DBG_LOG_TRACE( "Looking at block %6d size %6d\n", cf, blockSize ); + DBG_LOG_TRACE("Looking at block %6d size %6d\n", cf, blockSize); #if defined UMM_FIRST_FIT - /* This is the first block that fits! */ - if( (blockSize >= blocks) ) - break; + /* This is the first block that fits! */ + if ((blockSize >= blocks)) + { + break; + } #elif defined UMM_BEST_FIT - if( (blockSize >= blocks) && (blockSize < bestSize) ) { - bestBlock = cf; - bestSize = blockSize; - } + if ((blockSize >= blocks) && (blockSize < bestSize)) + { + bestBlock = cf; + bestSize = blockSize; + } #endif - cf = UMM_NFREE(cf); - } + cf = UMM_NFREE(cf); + } + + if (0x7FFF != bestSize) + { + cf = bestBlock; + blockSize = bestSize; + } + + if (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK && blockSize >= blocks) + { + /* + This is an existing block in the memory heap, we just need to split off + what we need, unlink it from the free list and mark it as in use, and + link the rest of the block back into the freelist as if it was a new + block on the free list... + */ + + if (blockSize == blocks) + { + /* It's an exact fit and we don't neet to split off a block. */ + DBG_LOG_DEBUG("Allocating %6d blocks starting at %6d - exact\n", blocks, cf); + + /* Disconnect this block from the FREE list */ + + umm_disconnect_from_free_list(cf); + + } + else + { + /* It's not an exact fit and we need to split off a block. */ + DBG_LOG_DEBUG("Allocating %6d blocks starting at %6d - existing\n", blocks, cf); + + /* + split current free block `cf` into two blocks. The first one will be + returned to user, so it's not free, and the second one will be free. + */ + umm_make_new_block(cf, blocks, + 0/*`cf` is not free*/, + UMM_FREELIST_MASK/*new block is free*/); + + /* + `umm_make_new_block()` does not update the free pointers (it affects + only free flags), but effectively we've just moved beginning of the + free block from `cf` to `cf + blocks`. So we have to adjust pointers + to and from adjacent free blocks. + */ + + /* previous free block */ + UMM_NFREE(UMM_PFREE(cf)) = cf + blocks; + UMM_PFREE(cf + blocks) = UMM_PFREE(cf); + + /* next free block */ + UMM_PFREE(UMM_NFREE(cf)) = cf + blocks; + UMM_NFREE(cf + blocks) = UMM_NFREE(cf); + } + } + else + { + /* Out of memory */ + + DBG_LOG_DEBUG("Can't allocate %5d blocks\n", blocks); + + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); + + return ((void *)NULL); + } - if( 0x7FFF != bestSize ) { - cf = bestBlock; - blockSize = bestSize; - } + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); - if( UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK && blockSize >= blocks ) { - /* - * This is an existing block in the memory heap, we just need to split off - * what we need, unlink it from the free list and mark it as in use, and - * link the rest of the block back into the freelist as if it was a new - * block on the free list... - */ - - if( blockSize == blocks ) { - /* It's an exact fit and we don't neet to split off a block. */ - DBG_LOG_DEBUG( "Allocating %6d blocks starting at %6d - exact\n", blocks, cf ); - - /* Disconnect this block from the FREE list */ - - umm_disconnect_from_free_list( cf ); - - } else { - /* It's not an exact fit and we need to split off a block. */ - DBG_LOG_DEBUG( "Allocating %6d blocks starting at %6d - existing\n", blocks, cf ); - - /* - * split current free block `cf` into two blocks. The first one will be - * returned to user, so it's not free, and the second one will be free. - */ - umm_make_new_block( cf, blocks, - 0/*`cf` is not free*/, - UMM_FREELIST_MASK/*new block is free*/); - - /* - * `umm_make_new_block()` does not update the free pointers (it affects - * only free flags), but effectively we've just moved beginning of the - * free block from `cf` to `cf + blocks`. So we have to adjust pointers - * to and from adjacent free blocks. - */ - - /* previous free block */ - UMM_NFREE( UMM_PFREE(cf) ) = cf + blocks; - UMM_PFREE( cf + blocks ) = UMM_PFREE(cf); - - /* next free block */ - UMM_PFREE( UMM_NFREE(cf) ) = cf + blocks; - UMM_NFREE( cf + blocks ) = UMM_NFREE(cf); + return ((void *)&UMM_DATA(cf)); } - } else { - /* Out of memory */ - DBG_LOG_DEBUG( "Can't allocate %5d blocks\n", blocks ); + /* ------------------------------------------------------------------------ */ - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); + static void *_umm_realloc(void *ptr, size_t size) + { - return( (void *)NULL ); - } + unsigned short int blocks; + unsigned short int blockSize; - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); + unsigned short int c; - return( (void *)&UMM_DATA(cf) ); -} + size_t curSize; -/* ------------------------------------------------------------------------ */ + if (umm_heap == NULL) + { + umm_init(); + } -static void *_umm_realloc( void *ptr, size_t size ) { + /* + This code looks after the case of a NULL value for ptr. The ANSI C + standard says that if ptr is NULL and size is non-zero, then we've + got to work the same a malloc(). If size is also 0, then our version + of malloc() returns a NULL pointer, which is OK as far as the ANSI C + standard is concerned. + */ - unsigned short int blocks; - unsigned short int blockSize; + if (((void *)NULL == ptr)) + { + DBG_LOG_DEBUG("realloc the NULL pointer - call malloc()\n"); - unsigned short int c; + return (_umm_malloc(size)); + } - size_t curSize; + /* + Now we're sure that we have a non_NULL ptr, but we're not sure what + we should do with it. If the size is 0, then the ANSI C standard says that + we should operate the same as free. + */ - if (umm_heap == NULL) { - umm_init(); - } + if (0 == size) + { + DBG_LOG_DEBUG("realloc to 0 size, just free the block\n"); - /* - * This code looks after the case of a NULL value for ptr. The ANSI C - * standard says that if ptr is NULL and size is non-zero, then we've - * got to work the same a malloc(). If size is also 0, then our version - * of malloc() returns a NULL pointer, which is OK as far as the ANSI C - * standard is concerned. - */ + _umm_free(ptr); - if( ((void *)NULL == ptr) ) { - DBG_LOG_DEBUG( "realloc the NULL pointer - call malloc()\n" ); + return ((void *)NULL); + } - return( _umm_malloc(size) ); - } + /* Protect the critical section... */ + UMM_CRITICAL_ENTRY(); - /* - * Now we're sure that we have a non_NULL ptr, but we're not sure what - * we should do with it. If the size is 0, then the ANSI C standard says that - * we should operate the same as free. - */ + /* + Otherwise we need to actually do a reallocation. A naiive approach + would be to malloc() a new block of the correct size, copy the old data + to the new block, and then free the old block. - if( 0 == size ) { - DBG_LOG_DEBUG( "realloc to 0 size, just free the block\n" ); + While this will work, we end up doing a lot of possibly unnecessary + copying. So first, let's figure out how many blocks we'll need. + */ - _umm_free( ptr ); + blocks = umm_blocks(size); - return( (void *)NULL ); - } + /* Figure out which block we're in. Note the use of truncated division... */ - /* Protect the critical section... */ - UMM_CRITICAL_ENTRY(); + c = (((char *)ptr) - (char *)(&(umm_heap[0]))) / sizeof(umm_block); - /* - * Otherwise we need to actually do a reallocation. A naiive approach - * would be to malloc() a new block of the correct size, copy the old data - * to the new block, and then free the old block. - * - * While this will work, we end up doing a lot of possibly unnecessary - * copying. So first, let's figure out how many blocks we'll need. - */ + /* Figure out how big this block is... */ - blocks = umm_blocks( size ); + blockSize = (UMM_NBLOCK(c) - c); - /* Figure out which block we're in. Note the use of truncated division... */ + /* Figure out how many bytes are in this block */ - c = (((char *)ptr)-(char *)(&(umm_heap[0])))/sizeof(umm_block); + curSize = (blockSize * sizeof(umm_block)) - (sizeof(((umm_block *)0)->header)); - /* Figure out how big this block is... */ + /* + Ok, now that we're here, we know the block number of the original chunk + of memory, and we know how much new memory we want, and we know the original + block size... + */ - blockSize = (UMM_NBLOCK(c) - c); + if (blockSize == blocks) + { + /* This space intentionally left blank - return the original pointer! */ - /* Figure out how many bytes are in this block */ + DBG_LOG_DEBUG("realloc the same size block - %d, do nothing\n", blocks); - curSize = (blockSize*sizeof(umm_block))-(sizeof(((umm_block *)0)->header)); + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); - /* - * Ok, now that we're here, we know the block number of the original chunk - * of memory, and we know how much new memory we want, and we know the original - * block size... - */ + return (ptr); + } - if( blockSize == blocks ) { - /* This space intentionally left blank - return the original pointer! */ + /* + Now we have a block size that could be bigger or smaller. Either + way, try to assimilate up to the next block before doing anything... - DBG_LOG_DEBUG( "realloc the same size block - %d, do nothing\n", blocks ); + If it's still too small, we have to free it anyways and it will save the + assimilation step later in free :-) + */ - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); + umm_assimilate_up(c); - return( ptr ); - } + /* + Now check if it might help to assimilate down, but don't actually + do the downward assimilation unless the resulting block will hold the + new request! If this block of code runs, then the new block will + either fit the request exactly, or be larger than the request. + */ - /* - * Now we have a block size that could be bigger or smaller. Either - * way, try to assimilate up to the next block before doing anything... - * - * If it's still too small, we have to free it anyways and it will save the - * assimilation step later in free :-) - */ + if ((UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK) && + (blocks <= (UMM_NBLOCK(c) - UMM_PBLOCK(c)))) + { - umm_assimilate_up( c ); + /* Check if the resulting block would be big enough... */ - /* - * Now check if it might help to assimilate down, but don't actually - * do the downward assimilation unless the resulting block will hold the - * new request! If this block of code runs, then the new block will - * either fit the request exactly, or be larger than the request. - */ + DBG_LOG_DEBUG("realloc() could assimilate down %d blocks - fits!\n\r", c - UMM_PBLOCK(c)); - if( (UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK) && - (blocks <= (UMM_NBLOCK(c)-UMM_PBLOCK(c))) ) { + /* Disconnect the previous block from the FREE list */ - /* Check if the resulting block would be big enough... */ + umm_disconnect_from_free_list(UMM_PBLOCK(c)); - DBG_LOG_DEBUG( "realloc() could assimilate down %d blocks - fits!\n\r", c-UMM_PBLOCK(c) ); + /* + Connect the previous block to the next block ... and then + realign the current block pointer + */ - /* Disconnect the previous block from the FREE list */ + c = umm_assimilate_down(c, 0); - umm_disconnect_from_free_list( UMM_PBLOCK(c) ); + /* + Move the bytes down to the new block we just created, but be sure to move + only the original bytes. + */ - /* - * Connect the previous block to the next block ... and then - * realign the current block pointer - */ + memmove((void *)&UMM_DATA(c), ptr, curSize); - c = umm_assimilate_down(c, 0); + /* And don't forget to adjust the pointer to the new block location! */ - /* - * Move the bytes down to the new block we just created, but be sure to move - * only the original bytes. - */ + ptr = (void *)&UMM_DATA(c); + } - memmove( (void *)&UMM_DATA(c), ptr, curSize ); + /* Now calculate the block size again...and we'll have three cases */ - /* And don't forget to adjust the pointer to the new block location! */ + blockSize = (UMM_NBLOCK(c) - c); - ptr = (void *)&UMM_DATA(c); - } + if (blockSize == blocks) + { + /* This space intentionally left blank - return the original pointer! */ - /* Now calculate the block size again...and we'll have three cases */ + DBG_LOG_DEBUG("realloc the same size block - %d, do nothing\n", blocks); - blockSize = (UMM_NBLOCK(c) - c); + } + else if (blockSize > blocks) + { + /* + New block is smaller than the old block, so just make a new block + at the end of this one and put it up on the free list... + */ - if( blockSize == blocks ) { - /* This space intentionally left blank - return the original pointer! */ + DBG_LOG_DEBUG("realloc %d to a smaller block %d, shrink and free the leftover bits\n", blockSize, blocks); - DBG_LOG_DEBUG( "realloc the same size block - %d, do nothing\n", blocks ); + umm_make_new_block(c, blocks, 0, 0); + _umm_free((void *)&UMM_DATA(c + blocks)); + } + else + { + /* New block is bigger than the old block... */ - } else if (blockSize > blocks ) { - /* - * New block is smaller than the old block, so just make a new block - * at the end of this one and put it up on the free list... - */ + void *oldptr = ptr; - DBG_LOG_DEBUG( "realloc %d to a smaller block %d, shrink and free the leftover bits\n", blockSize, blocks ); + DBG_LOG_DEBUG("realloc %d to a bigger block %d, make new, copy, and free the old\n", blockSize, blocks); - umm_make_new_block( c, blocks, 0, 0 ); - _umm_free( (void *)&UMM_DATA(c+blocks) ); - } else { - /* New block is bigger than the old block... */ + /* + Now _umm_malloc() a new/ one, copy the old data to the new block, and + free up the old block, but only if the malloc was sucessful! + */ - void *oldptr = ptr; + if ((ptr = _umm_malloc(size))) + { + memcpy(ptr, oldptr, curSize); + _umm_free(oldptr); + } - DBG_LOG_DEBUG( "realloc %d to a bigger block %d, make new, copy, and free the old\n", blockSize, blocks ); + } - /* - * Now _umm_malloc() a new/ one, copy the old data to the new block, and - * free up the old block, but only if the malloc was sucessful! - */ + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); - if( (ptr = _umm_malloc( size )) ) { - memcpy( ptr, oldptr, curSize ); - _umm_free( oldptr ); + return (ptr); } - } - - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); - - return( ptr ); -} - -/* ------------------------------------------------------------------------ */ - -void *umm_malloc( size_t size ) { - void *ret; + /* ------------------------------------------------------------------------ */ - /* check poison of each blocks, if poisoning is enabled */ - if (!CHECK_POISON_ALL_BLOCKS()) { - return NULL; - } - - /* check full integrity of the heap, if this check is enabled */ - if (!INTEGRITY_CHECK()) { - return NULL; - } - - size += POISON_SIZE(size); - - ret = _umm_malloc( size ); - if (0 != size && 0 == ret) { - umm_last_fail_alloc_addr = __builtin_return_address(0); - umm_last_fail_alloc_size = size; - } - - ret = GET_POISONED(ret, size); + void *umm_malloc(size_t size) + { + void *ret; - return ret; -} + /* check poison of each blocks, if poisoning is enabled */ + if (!CHECK_POISON_ALL_BLOCKS()) + { + return NULL; + } -/* ------------------------------------------------------------------------ */ + /* check full integrity of the heap, if this check is enabled */ + if (!INTEGRITY_CHECK()) + { + return NULL; + } -void *umm_calloc( size_t num, size_t item_size ) { - void *ret; - size_t size = item_size * num; + size += POISON_SIZE(size); - /* check poison of each blocks, if poisoning is enabled */ - if (!CHECK_POISON_ALL_BLOCKS()) { - return NULL; - } + ret = _umm_malloc(size); + if (0 != size && 0 == ret) + { + umm_last_fail_alloc_addr = __builtin_return_address(0); + umm_last_fail_alloc_size = size; + } - /* check full integrity of the heap, if this check is enabled */ - if (!INTEGRITY_CHECK()) { - return NULL; - } + ret = GET_POISONED(ret, size); - size += POISON_SIZE(size); - ret = _umm_malloc(size); - if (ret) { - memset(ret, 0x00, size); - } - if (0 != size && 0 == ret) { - umm_last_fail_alloc_addr = __builtin_return_address(0); - umm_last_fail_alloc_size = size; - } + return ret; + } - ret = GET_POISONED(ret, size); + /* ------------------------------------------------------------------------ */ - return ret; -} + void *umm_calloc(size_t num, size_t item_size) + { + void *ret; + size_t size = item_size * num; + + /* check poison of each blocks, if poisoning is enabled */ + if (!CHECK_POISON_ALL_BLOCKS()) + { + return NULL; + } + + /* check full integrity of the heap, if this check is enabled */ + if (!INTEGRITY_CHECK()) + { + return NULL; + } + + size += POISON_SIZE(size); + ret = _umm_malloc(size); + if (ret) + { + memset(ret, 0x00, size); + } + if (0 != size && 0 == ret) + { + umm_last_fail_alloc_addr = __builtin_return_address(0); + umm_last_fail_alloc_size = size; + } + + ret = GET_POISONED(ret, size); + + return ret; + } -/* ------------------------------------------------------------------------ */ + /* ------------------------------------------------------------------------ */ -void *umm_realloc( void *ptr, size_t size ) { - void *ret; + void *umm_realloc(void *ptr, size_t size) + { + void *ret; - ptr = GET_UNPOISONED(ptr); + ptr = GET_UNPOISONED(ptr); - /* check poison of each blocks, if poisoning is enabled */ - if (!CHECK_POISON_ALL_BLOCKS()) { - return NULL; - } + /* check poison of each blocks, if poisoning is enabled */ + if (!CHECK_POISON_ALL_BLOCKS()) + { + return NULL; + } - /* check full integrity of the heap, if this check is enabled */ - if (!INTEGRITY_CHECK()) { - return NULL; - } + /* check full integrity of the heap, if this check is enabled */ + if (!INTEGRITY_CHECK()) + { + return NULL; + } - size += POISON_SIZE(size); - ret = _umm_realloc( ptr, size ); - if (0 != size && 0 == ret) { - umm_last_fail_alloc_addr = __builtin_return_address(0); - umm_last_fail_alloc_size = size; - } + size += POISON_SIZE(size); + ret = _umm_realloc(ptr, size); + if (0 != size && 0 == ret) + { + umm_last_fail_alloc_addr = __builtin_return_address(0); + umm_last_fail_alloc_size = size; + } - ret = GET_POISONED(ret, size); + ret = GET_POISONED(ret, size); - return ret; -} + return ret; + } -/* ------------------------------------------------------------------------ */ + /* ------------------------------------------------------------------------ */ -void umm_free( void *ptr ) { + void umm_free(void *ptr) + { - ptr = GET_UNPOISONED(ptr); + ptr = GET_UNPOISONED(ptr); - /* check poison of each blocks, if poisoning is enabled */ - if (!CHECK_POISON_ALL_BLOCKS()) { - return; - } + /* check poison of each blocks, if poisoning is enabled */ + if (!CHECK_POISON_ALL_BLOCKS()) + { + return; + } - /* check full integrity of the heap, if this check is enabled */ - if (!INTEGRITY_CHECK()) { - return; - } + /* check full integrity of the heap, if this check is enabled */ + if (!INTEGRITY_CHECK()) + { + return; + } - _umm_free( ptr ); -} + _umm_free(ptr); + } -/* ------------------------------------------------------------------------ */ + /* ------------------------------------------------------------------------ */ -size_t ICACHE_FLASH_ATTR umm_free_heap_size( void ) { - umm_info(NULL, 0); - return (size_t)ummHeapInfo.freeBlocks * sizeof(umm_block); -} + size_t ICACHE_FLASH_ATTR umm_free_heap_size(void) + { + umm_info(NULL, 0); + return (size_t)ummHeapInfo.freeBlocks * sizeof(umm_block); + } -size_t ICACHE_FLASH_ATTR umm_max_block_size( void ) { - umm_info(NULL, 0); - return ummHeapInfo.maxFreeContiguousBlocks * sizeof(umm_block); -} + size_t ICACHE_FLASH_ATTR umm_max_block_size(void) + { + umm_info(NULL, 0); + return ummHeapInfo.maxFreeContiguousBlocks * sizeof(umm_block); + } -size_t ICACHE_FLASH_ATTR umm_block_size( void ) { - return sizeof(umm_block); -} + size_t ICACHE_FLASH_ATTR umm_block_size(void) + { + return sizeof(umm_block); + } }; diff --git a/cores/esp8266/umm_malloc/umm_malloc.h b/cores/esp8266/umm_malloc/umm_malloc.h index fcb1ade824..9cd200f4a6 100644 --- a/cores/esp8266/umm_malloc/umm_malloc.h +++ b/cores/esp8266/umm_malloc/umm_malloc.h @@ -1,9 +1,9 @@ -/* ---------------------------------------------------------------------------- - * umm_malloc.h - a memory allocator for embedded systems (microcontrollers) - * - * See copyright notice in LICENSE.TXT - * ---------------------------------------------------------------------------- - */ +/* ---------------------------------------------------------------------------- + umm_malloc.h - a memory allocator for embedded systems (microcontrollers) + + See copyright notice in LICENSE.TXT + ---------------------------------------------------------------------------- +*/ #ifndef UMM_MALLOC_H #define UMM_MALLOC_H @@ -16,35 +16,36 @@ extern "C" { #endif -typedef struct UMM_HEAP_INFO_t { - unsigned short int totalEntries; - unsigned short int usedEntries; - unsigned short int freeEntries; +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 totalBlocks; + unsigned short int usedBlocks; + unsigned short int freeBlocks; - unsigned short int maxFreeContiguousBlocks; + unsigned short int maxFreeContiguousBlocks; - unsigned int freeSize2; + 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_info(void *ptr, int force); -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 ); +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); -size_t umm_free_heap_size( void ); -size_t umm_max_block_size( void ); -size_t umm_block_size( void ); +size_t umm_free_heap_size(void); +size_t umm_max_block_size(void); +size_t umm_block_size(void); #ifdef __cplusplus } diff --git a/cores/esp8266/umm_malloc/umm_malloc_cfg.h b/cores/esp8266/umm_malloc/umm_malloc_cfg.h index e7c573c877..85a25f0512 100644 --- a/cores/esp8266/umm_malloc/umm_malloc_cfg.h +++ b/cores/esp8266/umm_malloc/umm_malloc_cfg.h @@ -1,6 +1,6 @@ /* - * Configuration for umm_malloc - */ + Configuration for umm_malloc +*/ #ifndef _UMM_MALLOC_CFG_H #define _UMM_MALLOC_CFG_H @@ -15,50 +15,50 @@ extern "C" { #include "c_types.h" /* - * There are a number of defines you can set at compile time that affect how - * the memory allocator will operate. - * You can set them in your config file umm_malloc_cfg.h. - * In GNU C, you also can set these compile time defines like this: - * - * -D UMM_TEST_MAIN - * - * Set this if you want to compile in the test suite at the end of this file. - * - * 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) - * - * Set this if you want to use a best-fit algorithm for allocating new - * blocks - * - * -D UMM_FIRST_FIT - * - * Set this if you want to use a first-fit algorithm for allocating new - * blocks - * - * -D UMM_DBG_LOG_LEVEL=n - * - * Set n to a value from 0 to 6 depending on how verbose you want the debug - * log to be - * - * ---------------------------------------------------------------------------- - * - * Support for this library in a multitasking environment is provided when - * you add bodies to the UMM_CRITICAL_ENTRY and UMM_CRITICAL_EXIT macros - * (see below) - * - * ---------------------------------------------------------------------------- - */ + There are a number of defines you can set at compile time that affect how + the memory allocator will operate. + You can set them in your config file umm_malloc_cfg.h. + In GNU C, you also can set these compile time defines like this: + + -D UMM_TEST_MAIN + + Set this if you want to compile in the test suite at the end of this file. + + 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) + + Set this if you want to use a best-fit algorithm for allocating new + blocks + + -D UMM_FIRST_FIT + + Set this if you want to use a first-fit algorithm for allocating new + blocks + + -D UMM_DBG_LOG_LEVEL=n + + Set n to a value from 0 to 6 depending on how verbose you want the debug + log to be + + ---------------------------------------------------------------------------- + + Support for this library in a multitasking environment is provided when + you add bodies to the UMM_CRITICAL_ENTRY and UMM_CRITICAL_EXIT macros + (see below) + + ---------------------------------------------------------------------------- +*/ ///////////////////////////////////////////////// #ifdef DEBUG_ESP_OOM @@ -67,15 +67,15 @@ extern "C" { // 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 ); +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); +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 @@ -83,12 +83,12 @@ void* realloc_loc (void* p, size_t s, const char* file, int line); ///////////////////////////////////////////////// #else // !defined(ESP_DEBUG_OOM) - // umm_*alloc are renamed to *alloc - #define UMM_REDEFINE_MEM_FUNCTIONS +// umm_*alloc are renamed to *alloc +#define UMM_REDEFINE_MEM_FUNCTIONS #endif - #define UMM_BEST_FIT +#define UMM_BEST_FIT /* Start addresses and the size of the heap */ extern char _heap_start; @@ -101,62 +101,62 @@ extern char _heap_start; #define UMM_H_ATTPACKSUF __attribute__((__packed__)) /* - * 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 - * your system uses for this purpose. You can disable interrupts entirely, or - * just disable task switching - it's up to you - * - * NOTE WELL that these macros MUST be allowed to nest, because umm_free() is - * called from within umm_malloc() - */ + 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 + your system uses for this purpose. You can disable interrupts entirely, or + just disable task switching - it's up to you + + NOTE WELL that these macros MUST be allowed to nest, because umm_free() is + called from within umm_malloc() +*/ #define UMM_CRITICAL_ENTRY() ets_intr_lock() #define UMM_CRITICAL_EXIT() ets_intr_unlock() /* - * -D UMM_INTEGRITY_CHECK : - * - * Enables heap integrity check before any heap operation. It affects - * performance, but does NOT consume extra memory. - * - * If integrity violation is detected, the message is printed and user-provided - * callback is called: `UMM_HEAP_CORRUPTION_CB()` - * - * Note that not all buffer overruns are detected: each buffer is aligned by - * 4 bytes, so there might be some trailing "extra" bytes which are not checked - * for corruption. - */ + -D UMM_INTEGRITY_CHECK : + + Enables heap integrity check before any heap operation. It affects + performance, but does NOT consume extra memory. + + If integrity violation is detected, the message is printed and user-provided + callback is called: `UMM_HEAP_CORRUPTION_CB()` + + Note that not all buffer overruns are detected: each buffer is aligned by + 4 bytes, so there might be some trailing "extra" bytes which are not checked + for corruption. +*/ /* -#define UMM_INTEGRITY_CHECK + #define UMM_INTEGRITY_CHECK */ /* - * -D UMM_POISON : - * - * Enables heap poisoning: add predefined value (poison) before and after each - * allocation, and check before each heap operation that no poison is - * corrupted. - * - * Other than the poison itself, we need to store exact user-requested length - * for each buffer, so that overrun by just 1 byte will be always noticed. - * - * Customizations: - * - * UMM_POISON_SIZE_BEFORE: - * Number of poison bytes before each block, e.g. 2 - * UMM_POISON_SIZE_AFTER: - * Number of poison bytes after each block e.g. 2 - * UMM_POISONED_BLOCK_LEN_TYPE - * Type of the exact buffer length, e.g. `short` - * - * NOTE: each allocated buffer is aligned by 4 bytes. But when poisoning is - * enabled, actual pointer returned to user is shifted by - * `(sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE)`. - * It's your responsibility to make resulting pointers aligned appropriately. - * - * If poison corruption is detected, the message is printed and user-provided - * callback is called: `UMM_HEAP_CORRUPTION_CB()` - */ + -D UMM_POISON : + + Enables heap poisoning: add predefined value (poison) before and after each + allocation, and check before each heap operation that no poison is + corrupted. + + Other than the poison itself, we need to store exact user-requested length + for each buffer, so that overrun by just 1 byte will be always noticed. + + Customizations: + + UMM_POISON_SIZE_BEFORE: + Number of poison bytes before each block, e.g. 2 + UMM_POISON_SIZE_AFTER: + Number of poison bytes after each block e.g. 2 + UMM_POISONED_BLOCK_LEN_TYPE + Type of the exact buffer length, e.g. `short` + + NOTE: each allocated buffer is aligned by 4 bytes. But when poisoning is + enabled, actual pointer returned to user is shifted by + `(sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE)`. + It's your responsibility to make resulting pointers aligned appropriately. + + If poison corruption is detected, the message is printed and user-provided + callback is called: `UMM_HEAP_CORRUPTION_CB()` +*/ #if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_CORE) #define UMM_POISON diff --git a/cores/esp8266/wiring_private.h b/cores/esp8266/wiring_private.h index 2c53565a67..4e6d164e02 100644 --- a/cores/esp8266/wiring_private.h +++ b/cores/esp8266/wiring_private.h @@ -1,26 +1,26 @@ /* - wiring_private.h - Internal header file. - Part of Arduino - http://www.arduino.cc/ + wiring_private.h - Internal header file. + Part of Arduino - http://www.arduino.cc/ - Copyright (c) 2005-2006 David A. Mellis + Copyright (c) 2005-2006 David A. Mellis - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA - $Id: wiring.h 239 2007-01-12 17:58:39Z mellis $ - */ + $Id: wiring.h 239 2007-01-12 17:58:39Z mellis $ +*/ #ifndef WiringPrivate_h #define WiringPrivate_h diff --git a/libraries/ArduinoOTA/ArduinoOTA.cpp b/libraries/ArduinoOTA/ArduinoOTA.cpp index ea619d4562..73a65a27d5 100644 --- a/libraries/ArduinoOTA/ArduinoOTA.cpp +++ b/libraries/ArduinoOTA/ArduinoOTA.cpp @@ -8,9 +8,9 @@ #include "StreamString.h" extern "C" { - #include "osapi.h" - #include "ets_sys.h" - #include "user_interface.h" +#include "osapi.h" +#include "ets_sys.h" +#include "user_interface.h" } #include "lwip/opt.h" @@ -31,348 +31,435 @@ extern "C" { #endif ArduinoOTAClass::ArduinoOTAClass() -: _port(0) -, _udp_ota(0) -, _initialized(false) -, _rebootOnSuccess(true) -, _useMDNS(true) -, _state(OTA_IDLE) -, _size(0) -, _cmd(0) -, _ota_port(0) -, _start_callback(NULL) -, _end_callback(NULL) -, _error_callback(NULL) -, _progress_callback(NULL) + : _port(0) + , _udp_ota(0) + , _initialized(false) + , _rebootOnSuccess(true) + , _useMDNS(true) + , _state(OTA_IDLE) + , _size(0) + , _cmd(0) + , _ota_port(0) + , _start_callback(NULL) + , _end_callback(NULL) + , _error_callback(NULL) + , _progress_callback(NULL) { } -ArduinoOTAClass::~ArduinoOTAClass(){ - if(_udp_ota){ - _udp_ota->unref(); - _udp_ota = 0; - } +ArduinoOTAClass::~ArduinoOTAClass() +{ + if (_udp_ota) + { + _udp_ota->unref(); + _udp_ota = 0; + } } -void ArduinoOTAClass::onStart(THandlerFunction fn) { +void ArduinoOTAClass::onStart(THandlerFunction fn) +{ _start_callback = fn; } -void ArduinoOTAClass::onEnd(THandlerFunction fn) { +void ArduinoOTAClass::onEnd(THandlerFunction fn) +{ _end_callback = fn; } -void ArduinoOTAClass::onProgress(THandlerFunction_Progress fn) { +void ArduinoOTAClass::onProgress(THandlerFunction_Progress fn) +{ _progress_callback = fn; } -void ArduinoOTAClass::onError(THandlerFunction_Error fn) { +void ArduinoOTAClass::onError(THandlerFunction_Error fn) +{ _error_callback = fn; } -void ArduinoOTAClass::setPort(uint16_t port) { - if (!_initialized && !_port && port) { - _port = port; - } +void ArduinoOTAClass::setPort(uint16_t port) +{ + if (!_initialized && !_port && port) + { + _port = port; + } } -void ArduinoOTAClass::setHostname(const char * hostname) { - if (!_initialized && !_hostname.length() && hostname) { - _hostname = hostname; - } +void ArduinoOTAClass::setHostname(const char * hostname) +{ + if (!_initialized && !_hostname.length() && hostname) + { + _hostname = hostname; + } } -String ArduinoOTAClass::getHostname() { - return _hostname; +String ArduinoOTAClass::getHostname() +{ + return _hostname; } -void ArduinoOTAClass::setPassword(const char * password) { - if (!_initialized && !_password.length() && password) { - MD5Builder passmd5; - passmd5.begin(); - passmd5.add(password); - passmd5.calculate(); - _password = passmd5.toString(); - } +void ArduinoOTAClass::setPassword(const char * password) +{ + if (!_initialized && !_password.length() && password) + { + MD5Builder passmd5; + passmd5.begin(); + passmd5.add(password); + passmd5.calculate(); + _password = passmd5.toString(); + } } -void ArduinoOTAClass::setPasswordHash(const char * password) { - if (!_initialized && !_password.length() && password) { - _password = password; - } +void ArduinoOTAClass::setPasswordHash(const char * password) +{ + if (!_initialized && !_password.length() && password) + { + _password = password; + } } -void ArduinoOTAClass::setRebootOnSuccess(bool reboot){ - _rebootOnSuccess = reboot; +void ArduinoOTAClass::setRebootOnSuccess(bool reboot) +{ + _rebootOnSuccess = reboot; } -void ArduinoOTAClass::begin(bool useMDNS) { - if (_initialized) - return; - - _useMDNS = useMDNS; - - if (!_hostname.length()) { - char tmp[15]; - sprintf(tmp, "esp8266-%06x", ESP.getChipId()); - _hostname = tmp; - } - if (!_port) { - _port = 8266; - } - - if(_udp_ota){ - _udp_ota->unref(); - _udp_ota = 0; - } - - _udp_ota = new UdpContext; - _udp_ota->ref(); - - if(!_udp_ota->listen(IP_ADDR_ANY, _port)) - return; - _udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this)); - -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) - if(_useMDNS) { - MDNS.begin(_hostname.c_str()); +void ArduinoOTAClass::begin(bool useMDNS) +{ + if (_initialized) + { + return; + } + + _useMDNS = useMDNS; - if (_password.length()) { - MDNS.enableArduino(_port, true); - } else { - MDNS.enableArduino(_port); + if (!_hostname.length()) + { + char tmp[15]; + sprintf(tmp, "esp8266-%06x", ESP.getChipId()); + _hostname = tmp; + } + if (!_port) + { + _port = 8266; + } + + if (_udp_ota) + { + _udp_ota->unref(); + _udp_ota = 0; + } + + _udp_ota = new UdpContext; + _udp_ota->ref(); + + if (!_udp_ota->listen(IP_ADDR_ANY, _port)) + { + return; + } + _udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this)); + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) + if (_useMDNS) + { + MDNS.begin(_hostname.c_str()); + + if (_password.length()) + { + MDNS.enableArduino(_port, true); + } + else + { + MDNS.enableArduino(_port); + } } - } #endif - _initialized = true; - _state = OTA_IDLE; + _initialized = true; + _state = OTA_IDLE; #ifdef OTA_DEBUG - OTA_DEBUG.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port); + OTA_DEBUG.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port); #endif } -int ArduinoOTAClass::parseInt(){ - char data[16]; - uint8_t index; - char value; - while(_udp_ota->peek() == ' ') _udp_ota->read(); - for(index = 0; index < sizeof(data); ++index){ - value = _udp_ota->peek(); - if(value < '0' || value > '9'){ - data[index] = '\0'; - return atoi(data); +int ArduinoOTAClass::parseInt() +{ + char data[16]; + uint8_t index; + char value; + while (_udp_ota->peek() == ' ') + { + _udp_ota->read(); } - data[index] = _udp_ota->read(); - } - return 0; + for (index = 0; index < sizeof(data); ++index) + { + value = _udp_ota->peek(); + if (value < '0' || value > '9') + { + data[index] = '\0'; + return atoi(data); + } + data[index] = _udp_ota->read(); + } + return 0; } -String ArduinoOTAClass::readStringUntil(char end){ - String res = ""; - int value; - while(true){ - value = _udp_ota->read(); - if(value < 0 || value == '\0' || value == end){ - return res; +String ArduinoOTAClass::readStringUntil(char end) +{ + String res = ""; + int value; + while (true) + { + value = _udp_ota->read(); + if (value < 0 || value == '\0' || value == end) + { + return res; + } + res += static_cast(value); } - res += static_cast(value); - } - return res; + return res; } -void ArduinoOTAClass::_onRx(){ - if(!_udp_ota->next()) return; - IPAddress ota_ip; - - if (_state == OTA_IDLE) { - int cmd = parseInt(); - if (cmd != U_FLASH && cmd != U_SPIFFS) - return; - _ota_ip = _udp_ota->getRemoteAddress(); - _cmd = cmd; - _ota_port = parseInt(); - _ota_udp_port = _udp_ota->getRemotePort(); - _size = parseInt(); - _udp_ota->read(); - _md5 = readStringUntil('\n'); - _md5.trim(); - if(_md5.length() != 32) - return; - - ota_ip = _ota_ip; - - if (_password.length()){ - MD5Builder nonce_md5; - nonce_md5.begin(); - nonce_md5.add(String(micros())); - nonce_md5.calculate(); - _nonce = nonce_md5.toString(); - - char auth_req[38]; - sprintf(auth_req, "AUTH %s", _nonce.c_str()); - _udp_ota->append((const char *)auth_req, strlen(auth_req)); - _udp_ota->send(ota_ip, _ota_udp_port); - _state = OTA_WAITAUTH; - return; - } else { - _state = OTA_RUNUPDATE; +void ArduinoOTAClass::_onRx() +{ + if (!_udp_ota->next()) + { + return; } - } else if (_state == OTA_WAITAUTH) { - int cmd = parseInt(); - if (cmd != U_AUTH) { - _state = OTA_IDLE; - return; + IPAddress ota_ip; + + if (_state == OTA_IDLE) + { + int cmd = parseInt(); + if (cmd != U_FLASH && cmd != U_SPIFFS) + { + return; + } + _ota_ip = _udp_ota->getRemoteAddress(); + _cmd = cmd; + _ota_port = parseInt(); + _ota_udp_port = _udp_ota->getRemotePort(); + _size = parseInt(); + _udp_ota->read(); + _md5 = readStringUntil('\n'); + _md5.trim(); + if (_md5.length() != 32) + { + return; + } + + ota_ip = _ota_ip; + + if (_password.length()) + { + MD5Builder nonce_md5; + nonce_md5.begin(); + nonce_md5.add(String(micros())); + nonce_md5.calculate(); + _nonce = nonce_md5.toString(); + + char auth_req[38]; + sprintf(auth_req, "AUTH %s", _nonce.c_str()); + _udp_ota->append((const char *)auth_req, strlen(auth_req)); + _udp_ota->send(ota_ip, _ota_udp_port); + _state = OTA_WAITAUTH; + return; + } + else + { + _state = OTA_RUNUPDATE; + } } - _udp_ota->read(); - String cnonce = readStringUntil(' '); - String response = readStringUntil('\n'); - if (cnonce.length() != 32 || response.length() != 32) { - _state = OTA_IDLE; - return; + else if (_state == OTA_WAITAUTH) + { + int cmd = parseInt(); + if (cmd != U_AUTH) + { + _state = OTA_IDLE; + return; + } + _udp_ota->read(); + String cnonce = readStringUntil(' '); + String response = readStringUntil('\n'); + if (cnonce.length() != 32 || response.length() != 32) + { + _state = OTA_IDLE; + return; + } + + String challenge = _password + ":" + String(_nonce) + ":" + cnonce; + MD5Builder _challengemd5; + _challengemd5.begin(); + _challengemd5.add(challenge); + _challengemd5.calculate(); + String result = _challengemd5.toString(); + + ota_ip = _ota_ip; + if (result.equalsConstantTime(response)) + { + _state = OTA_RUNUPDATE; + } + else + { + _udp_ota->append("Authentication Failed", 21); + _udp_ota->send(ota_ip, _ota_udp_port); + if (_error_callback) + { + _error_callback(OTA_AUTH_ERROR); + } + _state = OTA_IDLE; + } } - String challenge = _password + ":" + String(_nonce) + ":" + cnonce; - MD5Builder _challengemd5; - _challengemd5.begin(); - _challengemd5.add(challenge); - _challengemd5.calculate(); - String result = _challengemd5.toString(); - - ota_ip = _ota_ip; - if(result.equalsConstantTime(response)) { - _state = OTA_RUNUPDATE; - } else { - _udp_ota->append("Authentication Failed", 21); - _udp_ota->send(ota_ip, _ota_udp_port); - if (_error_callback) _error_callback(OTA_AUTH_ERROR); - _state = OTA_IDLE; + while (_udp_ota->next()) + { + _udp_ota->flush(); } - } - - while(_udp_ota->next()) _udp_ota->flush(); } -void ArduinoOTAClass::_runUpdate() { - IPAddress ota_ip = _ota_ip; +void ArduinoOTAClass::_runUpdate() +{ + IPAddress ota_ip = _ota_ip; - if (!Update.begin(_size, _cmd)) { + if (!Update.begin(_size, _cmd)) + { #ifdef OTA_DEBUG - OTA_DEBUG.println("Update Begin Error"); + OTA_DEBUG.println("Update Begin Error"); #endif - if (_error_callback) { - _error_callback(OTA_BEGIN_ERROR); + if (_error_callback) + { + _error_callback(OTA_BEGIN_ERROR); + } + + StreamString ss; + Update.printError(ss); + _udp_ota->append("ERR: ", 5); + _udp_ota->append(ss.c_str(), ss.length()); + _udp_ota->send(ota_ip, _ota_udp_port); + delay(100); + _udp_ota->listen(IP_ADDR_ANY, _port); + _state = OTA_IDLE; + return; } - - StreamString ss; - Update.printError(ss); - _udp_ota->append("ERR: ", 5); - _udp_ota->append(ss.c_str(), ss.length()); + _udp_ota->append("OK", 2); _udp_ota->send(ota_ip, _ota_udp_port); delay(100); - _udp_ota->listen(IP_ADDR_ANY, _port); - _state = OTA_IDLE; - return; - } - _udp_ota->append("OK", 2); - _udp_ota->send(ota_ip, _ota_udp_port); - delay(100); - - Update.setMD5(_md5.c_str()); - WiFiUDP::stopAll(); - WiFiClient::stopAll(); - - if (_start_callback) { - _start_callback(); - } - if (_progress_callback) { - _progress_callback(0, _size); - } - - WiFiClient client; - if (!client.connect(_ota_ip, _ota_port)) { + + Update.setMD5(_md5.c_str()); + WiFiUDP::stopAll(); + WiFiClient::stopAll(); + + if (_start_callback) + { + _start_callback(); + } + if (_progress_callback) + { + _progress_callback(0, _size); + } + + WiFiClient client; + if (!client.connect(_ota_ip, _ota_port)) + { #ifdef OTA_DEBUG - OTA_DEBUG.printf("Connect Failed\n"); + OTA_DEBUG.printf("Connect Failed\n"); #endif - _udp_ota->listen(IP_ADDR_ANY, _port); - if (_error_callback) { - _error_callback(OTA_CONNECT_ERROR); + _udp_ota->listen(IP_ADDR_ANY, _port); + if (_error_callback) + { + _error_callback(OTA_CONNECT_ERROR); + } + _state = OTA_IDLE; } - _state = OTA_IDLE; - } - // OTA sends little packets - client.setNoDelay(true); - - uint32_t written, total = 0; - while (!Update.isFinished() && client.connected()) { - int waited = 1000; - while (!client.available() && waited--) - delay(1); - if (!waited){ + // OTA sends little packets + client.setNoDelay(true); + + uint32_t written, total = 0; + while (!Update.isFinished() && client.connected()) + { + int waited = 1000; + while (!client.available() && waited--) + { + delay(1); + } + if (!waited) + { #ifdef OTA_DEBUG - OTA_DEBUG.printf("Receive Failed\n"); + OTA_DEBUG.printf("Receive Failed\n"); #endif - _udp_ota->listen(IP_ADDR_ANY, _port); - if (_error_callback) { - _error_callback(OTA_RECEIVE_ERROR); - } - _state = OTA_IDLE; - } - written = Update.write(client); - if (written > 0) { - client.print(written, DEC); - total += written; - if(_progress_callback) { - _progress_callback(total, _size); - } + _udp_ota->listen(IP_ADDR_ANY, _port); + if (_error_callback) + { + _error_callback(OTA_RECEIVE_ERROR); + } + _state = OTA_IDLE; + } + written = Update.write(client); + if (written > 0) + { + client.print(written, DEC); + total += written; + if (_progress_callback) + { + _progress_callback(total, _size); + } + } } - } - if (Update.end()) { - client.print("OK"); - client.stop(); - delay(10); + if (Update.end()) + { + client.print("OK"); + client.stop(); + delay(10); #ifdef OTA_DEBUG - OTA_DEBUG.printf("Update Success\n"); + OTA_DEBUG.printf("Update Success\n"); #endif - if (_end_callback) { - _end_callback(); - } - if(_rebootOnSuccess){ + if (_end_callback) + { + _end_callback(); + } + if (_rebootOnSuccess) + { #ifdef OTA_DEBUG - OTA_DEBUG.printf("Rebooting...\n"); + OTA_DEBUG.printf("Rebooting...\n"); #endif - //let serial/network finish tasks that might be given in _end_callback - delay(100); - ESP.restart(); - } - } else { - _udp_ota->listen(IP_ADDR_ANY, _port); - if (_error_callback) { - _error_callback(OTA_END_ERROR); + //let serial/network finish tasks that might be given in _end_callback + delay(100); + ESP.restart(); + } } - Update.printError(client); + else + { + _udp_ota->listen(IP_ADDR_ANY, _port); + if (_error_callback) + { + _error_callback(OTA_END_ERROR); + } + Update.printError(client); #ifdef OTA_DEBUG - Update.printError(OTA_DEBUG); + Update.printError(OTA_DEBUG); #endif - _state = OTA_IDLE; - } + _state = OTA_IDLE; + } } //this needs to be called in the loop() -void ArduinoOTAClass::handle() { - if (_state == OTA_RUNUPDATE) { - _runUpdate(); - _state = OTA_IDLE; - } +void ArduinoOTAClass::handle() +{ + if (_state == OTA_RUNUPDATE) + { + _runUpdate(); + _state = OTA_IDLE; + } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) - if(_useMDNS) - MDNS.update(); //handle MDNS update as well, given that ArduinoOTA relies on it anyways + if (_useMDNS) + { + MDNS.update(); //handle MDNS update as well, given that ArduinoOTA relies on it anyways + } #endif } -int ArduinoOTAClass::getCommand() { - return _cmd; +int ArduinoOTAClass::getCommand() +{ + return _cmd; } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA) diff --git a/libraries/ArduinoOTA/ArduinoOTA.h b/libraries/ArduinoOTA/ArduinoOTA.h index a02dbc485c..c1d2c5bfd3 100644 --- a/libraries/ArduinoOTA/ArduinoOTA.h +++ b/libraries/ArduinoOTA/ArduinoOTA.h @@ -7,26 +7,28 @@ class UdpContext; -typedef enum { - OTA_IDLE, - OTA_WAITAUTH, - OTA_RUNUPDATE +typedef enum +{ + OTA_IDLE, + OTA_WAITAUTH, + OTA_RUNUPDATE } ota_state_t; -typedef enum { - OTA_AUTH_ERROR, - OTA_BEGIN_ERROR, - OTA_CONNECT_ERROR, - OTA_RECEIVE_ERROR, - OTA_END_ERROR +typedef enum +{ + OTA_AUTH_ERROR, + OTA_BEGIN_ERROR, + OTA_CONNECT_ERROR, + OTA_RECEIVE_ERROR, + OTA_END_ERROR } ota_error_t; class ArduinoOTAClass { - public: - typedef std::function THandlerFunction; - typedef std::function THandlerFunction_Error; - typedef std::function THandlerFunction_Progress; +public: + typedef std::function THandlerFunction; + typedef std::function THandlerFunction_Error; + typedef std::function THandlerFunction_Progress; ArduinoOTAClass(); ~ArduinoOTAClass(); @@ -68,7 +70,7 @@ class ArduinoOTAClass //Gets update command type after OTA has started. Either U_FLASH or U_SPIFFS int getCommand(); - private: +private: int _port; String _password; String _hostname; diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index 31d2c90792..6a5aa3e67a 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -13,244 +13,281 @@ DNSServer::DNSServer() { - _ttl = lwip_htonl(60); - _errorReplyCode = DNSReplyCode::NonExistentDomain; + _ttl = lwip_htonl(60); + _errorReplyCode = DNSReplyCode::NonExistentDomain; } bool DNSServer::start(const uint16_t &port, const String &domainName, - const IPAddress &resolvedIP) + const IPAddress &resolvedIP) { - _port = port; - - _domainName = domainName; - _resolvedIP[0] = resolvedIP[0]; - _resolvedIP[1] = resolvedIP[1]; - _resolvedIP[2] = resolvedIP[2]; - _resolvedIP[3] = resolvedIP[3]; - downcaseAndRemoveWwwPrefix(_domainName); - return _udp.begin(_port) == 1; + _port = port; + + _domainName = domainName; + _resolvedIP[0] = resolvedIP[0]; + _resolvedIP[1] = resolvedIP[1]; + _resolvedIP[2] = resolvedIP[2]; + _resolvedIP[3] = resolvedIP[3]; + downcaseAndRemoveWwwPrefix(_domainName); + return _udp.begin(_port) == 1; } void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode) { - _errorReplyCode = replyCode; + _errorReplyCode = replyCode; } void DNSServer::setTTL(const uint32_t &ttl) { - _ttl = lwip_htonl(ttl); + _ttl = lwip_htonl(ttl); } void DNSServer::stop() { - _udp.stop(); + _udp.stop(); } void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName) { - domainName.toLowerCase(); - if (domainName.startsWith("www.")) - domainName.remove(0, 4); + domainName.toLowerCase(); + if (domainName.startsWith("www.")) + { + domainName.remove(0, 4); + } } void DNSServer::respondToRequest(uint8_t *buffer, size_t length) { - DNSHeader *dnsHeader; - uint8_t *query, *start; - const char *matchString; - size_t remaining, labelLength, queryLength; - uint16_t qtype, qclass; - - dnsHeader = (DNSHeader *)buffer; - - // Must be a query for us to do anything with it - if (dnsHeader->QR != DNS_QR_QUERY) - return; - - // If operation is anything other than query, we don't do it - if (dnsHeader->OPCode != DNS_OPCODE_QUERY) - return replyWithError(dnsHeader, DNSReplyCode::NotImplemented); - - // Only support requests containing single queries - everything else - // is badly defined - if (dnsHeader->QDCount != lwip_htons(1)) - return replyWithError(dnsHeader, DNSReplyCode::FormError); - - // We must return a FormError in the case of a non-zero ARCount to - // be minimally compatible with EDNS resolvers - if (dnsHeader->ANCount != 0 || dnsHeader->NSCount != 0 - || dnsHeader->ARCount != 0) - return replyWithError(dnsHeader, DNSReplyCode::FormError); - - // Even if we're not going to use the query, we need to parse it - // so we can check the address type that's being queried - - query = start = buffer + DNS_HEADER_SIZE; - remaining = length - DNS_HEADER_SIZE; - while (remaining != 0 && *start != 0) { - labelLength = *start; - if (labelLength + 1 > remaining) - return replyWithError(dnsHeader, DNSReplyCode::FormError); - remaining -= (labelLength + 1); - start += (labelLength + 1); - } - - // 1 octet labelLength, 2 octet qtype, 2 octet qclass - if (remaining < 5) - return replyWithError(dnsHeader, DNSReplyCode::FormError); - - start += 1; // Skip the 0 length label that we found above - - memcpy(&qtype, start, sizeof(qtype)); - start += 2; - memcpy(&qclass, start, sizeof(qclass)); - start += 2; - - queryLength = start - query; - - if (qclass != lwip_htons(DNS_QCLASS_ANY) - && qclass != lwip_htons(DNS_QCLASS_IN)) - return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, - query, queryLength); - - if (qtype != lwip_htons(DNS_QTYPE_A) - && qtype != lwip_htons(DNS_QTYPE_ANY)) - return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, - query, queryLength); - - // If we have no domain name configured, just return an error - if (_domainName == "") - return replyWithError(dnsHeader, _errorReplyCode, - query, queryLength); - - // If we're running with a wildcard we can just return a result now - if (_domainName == "*") - return replyWithIP(dnsHeader, query, queryLength); - - matchString = _domainName.c_str(); - - start = query; - - // If there's a leading 'www', skip it - if (*start == 3 && strncasecmp("www", (char *) start + 1, 3) == 0) - start += 4; - - while (*start != 0) { - labelLength = *start; - start += 1; - while (labelLength > 0) { - if (tolower(*start) != *matchString) - return replyWithError(dnsHeader, _errorReplyCode, - query, queryLength); - ++start; - ++matchString; - --labelLength; + DNSHeader *dnsHeader; + uint8_t *query, *start; + const char *matchString; + size_t remaining, labelLength, queryLength; + uint16_t qtype, qclass; + + dnsHeader = (DNSHeader *)buffer; + + // Must be a query for us to do anything with it + if (dnsHeader->QR != DNS_QR_QUERY) + { + return; } - if (*start == 0 && *matchString == '\0') - return replyWithIP(dnsHeader, query, queryLength); - if (*matchString != '.') - return replyWithError(dnsHeader, _errorReplyCode, - query, queryLength); - ++matchString; - } + // If operation is anything other than query, we don't do it + if (dnsHeader->OPCode != DNS_OPCODE_QUERY) + { + return replyWithError(dnsHeader, DNSReplyCode::NotImplemented); + } + + // Only support requests containing single queries - everything else + // is badly defined + if (dnsHeader->QDCount != lwip_htons(1)) + { + return replyWithError(dnsHeader, DNSReplyCode::FormError); + } + + // We must return a FormError in the case of a non-zero ARCount to + // be minimally compatible with EDNS resolvers + if (dnsHeader->ANCount != 0 || dnsHeader->NSCount != 0 + || dnsHeader->ARCount != 0) + { + return replyWithError(dnsHeader, DNSReplyCode::FormError); + } + + // Even if we're not going to use the query, we need to parse it + // so we can check the address type that's being queried + + query = start = buffer + DNS_HEADER_SIZE; + remaining = length - DNS_HEADER_SIZE; + while (remaining != 0 && *start != 0) + { + labelLength = *start; + if (labelLength + 1 > remaining) + { + return replyWithError(dnsHeader, DNSReplyCode::FormError); + } + remaining -= (labelLength + 1); + start += (labelLength + 1); + } + + // 1 octet labelLength, 2 octet qtype, 2 octet qclass + if (remaining < 5) + { + return replyWithError(dnsHeader, DNSReplyCode::FormError); + } + + start += 1; // Skip the 0 length label that we found above + + memcpy(&qtype, start, sizeof(qtype)); + start += 2; + memcpy(&qclass, start, sizeof(qclass)); + start += 2; + + queryLength = start - query; + + if (qclass != lwip_htons(DNS_QCLASS_ANY) + && qclass != lwip_htons(DNS_QCLASS_IN)) + return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, + query, queryLength); + + if (qtype != lwip_htons(DNS_QTYPE_A) + && qtype != lwip_htons(DNS_QTYPE_ANY)) + return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, + query, queryLength); + + // If we have no domain name configured, just return an error + if (_domainName == "") + return replyWithError(dnsHeader, _errorReplyCode, + query, queryLength); - return replyWithError(dnsHeader, _errorReplyCode, - query, queryLength); + // If we're running with a wildcard we can just return a result now + if (_domainName == "*") + { + return replyWithIP(dnsHeader, query, queryLength); + } + + matchString = _domainName.c_str(); + + start = query; + + // If there's a leading 'www', skip it + if (*start == 3 && strncasecmp("www", (char *) start + 1, 3) == 0) + { + start += 4; + } + + while (*start != 0) + { + labelLength = *start; + start += 1; + while (labelLength > 0) + { + if (tolower(*start) != *matchString) + return replyWithError(dnsHeader, _errorReplyCode, + query, queryLength); + ++start; + ++matchString; + --labelLength; + } + if (*start == 0 && *matchString == '\0') + { + return replyWithIP(dnsHeader, query, queryLength); + } + + if (*matchString != '.') + return replyWithError(dnsHeader, _errorReplyCode, + query, queryLength); + ++matchString; + } + + return replyWithError(dnsHeader, _errorReplyCode, + query, queryLength); } void DNSServer::processNextRequest() { - size_t currentPacketSize; + size_t currentPacketSize; - currentPacketSize = _udp.parsePacket(); - if (currentPacketSize == 0) - return; + currentPacketSize = _udp.parsePacket(); + if (currentPacketSize == 0) + { + return; + } - // The DNS RFC requires that DNS packets be less than 512 bytes in size, - // so just discard them if they are larger - if (currentPacketSize > MAX_DNS_PACKETSIZE) - return; + // The DNS RFC requires that DNS packets be less than 512 bytes in size, + // so just discard them if they are larger + if (currentPacketSize > MAX_DNS_PACKETSIZE) + { + return; + } - // If the packet size is smaller than the DNS header, then someone is - // messing with us - if (currentPacketSize < DNS_HEADER_SIZE) - return; + // If the packet size is smaller than the DNS header, then someone is + // messing with us + if (currentPacketSize < DNS_HEADER_SIZE) + { + return; + } - std::unique_ptr buffer(new (std::nothrow) uint8_t[currentPacketSize]); + std::unique_ptr buffer(new (std::nothrow) uint8_t[currentPacketSize]); - if (buffer == NULL) - return; + if (buffer == NULL) + { + return; + } - _udp.read(buffer.get(), currentPacketSize); - respondToRequest(buffer.get(), currentPacketSize); + _udp.read(buffer.get(), currentPacketSize); + respondToRequest(buffer.get(), currentPacketSize); } void DNSServer::writeNBOShort(uint16_t value) { - _udp.write((unsigned char *)&value, 2); + _udp.write((unsigned char *)&value, 2); } void DNSServer::replyWithIP(DNSHeader *dnsHeader, - unsigned char * query, - size_t queryLength) + unsigned char * query, + size_t queryLength) { - uint16_t value; + uint16_t value; - dnsHeader->QR = DNS_QR_RESPONSE; - dnsHeader->QDCount = lwip_htons(1); - dnsHeader->ANCount = lwip_htons(1); - dnsHeader->NSCount = 0; - dnsHeader->ARCount = 0; + dnsHeader->QR = DNS_QR_RESPONSE; + dnsHeader->QDCount = lwip_htons(1); + dnsHeader->ANCount = lwip_htons(1); + dnsHeader->NSCount = 0; + dnsHeader->ARCount = 0; - _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); - _udp.write((unsigned char *) dnsHeader, sizeof(DNSHeader)); - _udp.write(query, queryLength); + _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); + _udp.write((unsigned char *) dnsHeader, sizeof(DNSHeader)); + _udp.write(query, queryLength); - // Rather than restate the name here, we use a pointer to the name contained - // in the query section. Pointers have the top two bits set. - value = 0xC000 | DNS_HEADER_SIZE; - writeNBOShort(lwip_htons(value)); + // Rather than restate the name here, we use a pointer to the name contained + // in the query section. Pointers have the top two bits set. + value = 0xC000 | DNS_HEADER_SIZE; + writeNBOShort(lwip_htons(value)); - // Answer is type A (an IPv4 address) - writeNBOShort(lwip_htons(DNS_QTYPE_A)); + // Answer is type A (an IPv4 address) + writeNBOShort(lwip_htons(DNS_QTYPE_A)); - // Answer is in the Internet Class - writeNBOShort(lwip_htons(DNS_QCLASS_IN)); + // Answer is in the Internet Class + writeNBOShort(lwip_htons(DNS_QCLASS_IN)); - // Output TTL (already NBO) - _udp.write((unsigned char*)&_ttl, 4); + // Output TTL (already NBO) + _udp.write((unsigned char*)&_ttl, 4); - // Length of RData is 4 bytes (because, in this case, RData is IPv4) - writeNBOShort(lwip_htons(sizeof(_resolvedIP))); - _udp.write(_resolvedIP, sizeof(_resolvedIP)); - _udp.endPacket(); + // Length of RData is 4 bytes (because, in this case, RData is IPv4) + writeNBOShort(lwip_htons(sizeof(_resolvedIP))); + _udp.write(_resolvedIP, sizeof(_resolvedIP)); + _udp.endPacket(); } void DNSServer::replyWithError(DNSHeader *dnsHeader, - DNSReplyCode rcode, - unsigned char *query, - size_t queryLength) + DNSReplyCode rcode, + unsigned char *query, + size_t queryLength) { - dnsHeader->QR = DNS_QR_RESPONSE; - dnsHeader->RCode = (unsigned char) rcode; - if (query) - dnsHeader->QDCount = lwip_htons(1); - else - dnsHeader->QDCount = 0; - dnsHeader->ANCount = 0; - dnsHeader->NSCount = 0; - dnsHeader->ARCount = 0; - - _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); - _udp.write((unsigned char *)dnsHeader, sizeof(DNSHeader)); - if (query != NULL) - _udp.write(query, queryLength); - _udp.endPacket(); + dnsHeader->QR = DNS_QR_RESPONSE; + dnsHeader->RCode = (unsigned char) rcode; + if (query) + { + dnsHeader->QDCount = lwip_htons(1); + } + else + { + dnsHeader->QDCount = 0; + } + dnsHeader->ANCount = 0; + dnsHeader->NSCount = 0; + dnsHeader->ARCount = 0; + + _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); + _udp.write((unsigned char *)dnsHeader, sizeof(DNSHeader)); + if (query != NULL) + { + _udp.write(query, queryLength); + } + _udp.endPacket(); } void DNSServer::replyWithError(DNSHeader *dnsHeader, - DNSReplyCode rcode) + DNSReplyCode rcode) { - replyWithError(dnsHeader, rcode, NULL, 0); + replyWithError(dnsHeader, rcode, NULL, 0); } diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index 0f3ebd7a34..b858cad11d 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -17,39 +17,40 @@ enum class DNSReplyCode { - NoError = 0, - FormError = 1, - ServerFailure = 2, - NonExistentDomain = 3, - NotImplemented = 4, - Refused = 5, - YXDomain = 6, - YXRRSet = 7, - NXRRSet = 8 + NoError = 0, + FormError = 1, + ServerFailure = 2, + NonExistentDomain = 3, + NotImplemented = 4, + Refused = 5, + YXDomain = 6, + YXRRSet = 7, + NXRRSet = 8 }; struct DNSHeader { - uint16_t ID; // identification number - unsigned char RD : 1; // recursion desired - unsigned char TC : 1; // truncated message - unsigned char AA : 1; // authoritive answer - unsigned char OPCode : 4; // message_type - unsigned char QR : 1; // query/response flag - unsigned char RCode : 4; // response code - unsigned char Z : 3; // its z! reserved - unsigned char RA : 1; // recursion available - uint16_t QDCount; // number of question entries - uint16_t ANCount; // number of answer entries - uint16_t NSCount; // number of authority entries - uint16_t ARCount; // number of resource entries + uint16_t ID; // identification number + unsigned char RD : 1; // recursion desired + unsigned char TC : 1; // truncated message + unsigned char AA : 1; // authoritive answer + unsigned char OPCode : 4; // message_type + unsigned char QR : 1; // query/response flag + unsigned char RCode : 4; // response code + unsigned char Z : 3; // its z! reserved + unsigned char RA : 1; // recursion available + uint16_t QDCount; // number of question entries + uint16_t ANCount; // number of answer entries + uint16_t NSCount; // number of authority entries + uint16_t ARCount; // number of resource entries }; class DNSServer { - public: +public: DNSServer(); - ~DNSServer() { + ~DNSServer() + { stop(); }; void processNextRequest(); @@ -58,12 +59,12 @@ class DNSServer // Returns true if successful, false if there are no sockets available bool start(const uint16_t &port, - const String &domainName, - const IPAddress &resolvedIP); + const String &domainName, + const IPAddress &resolvedIP); // stops the DNS server void stop(); - private: +private: WiFiUDP _udp; uint16_t _port; String _domainName; @@ -73,14 +74,14 @@ class DNSServer void downcaseAndRemoveWwwPrefix(String &domainName); void replyWithIP(DNSHeader *dnsHeader, - unsigned char * query, - size_t queryLength); + unsigned char * query, + size_t queryLength); void replyWithError(DNSHeader *dnsHeader, - DNSReplyCode rcode, - unsigned char *query, - size_t queryLength); + DNSReplyCode rcode, + unsigned char *query, + size_t queryLength); void replyWithError(DNSHeader *dnsHeader, - DNSReplyCode rcode); + DNSReplyCode rcode); void respondToRequest(uint8_t *buffer, size_t length); void writeNBOShort(uint16_t value); }; diff --git a/libraries/EEPROM/EEPROM.cpp b/libraries/EEPROM/EEPROM.cpp index 0ec376abc1..2faab7906d 100644 --- a/libraries/EEPROM/EEPROM.cpp +++ b/libraries/EEPROM/EEPROM.cpp @@ -1,22 +1,22 @@ /* - EEPROM.cpp - esp8266 EEPROM emulation + EEPROM.cpp - esp8266 EEPROM emulation - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Arduino.h" @@ -33,112 +33,145 @@ extern "C" { extern "C" uint32_t _SPIFFS_end; EEPROMClass::EEPROMClass(uint32_t sector) -: _sector(sector) -, _data(0) -, _size(0) -, _dirty(false) + : _sector(sector) + , _data(0) + , _size(0) + , _dirty(false) { } EEPROMClass::EEPROMClass(void) -: _sector((((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE)) -, _data(0) -, _size(0) -, _dirty(false) + : _sector((((uint32_t) & _SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE)) + , _data(0) + , _size(0) + , _dirty(false) { } -void EEPROMClass::begin(size_t size) { - if (size <= 0) - return; - if (size > SPI_FLASH_SEC_SIZE) - size = SPI_FLASH_SEC_SIZE; +void EEPROMClass::begin(size_t size) +{ + if (size <= 0) + { + return; + } + if (size > SPI_FLASH_SEC_SIZE) + { + size = SPI_FLASH_SEC_SIZE; + } - size = (size + 3) & (~3); + size = (size + 3) & (~3); - //In case begin() is called a 2nd+ time, don't reallocate if size is the same - if(_data && size != _size) { - delete[] _data; - _data = new uint8_t[size]; - } else if(!_data) { - _data = new uint8_t[size]; - } + //In case begin() is called a 2nd+ time, don't reallocate if size is the same + if (_data && size != _size) + { + delete[] _data; + _data = new uint8_t[size]; + } + else if (!_data) + { + _data = new uint8_t[size]; + } - _size = size; + _size = size; - noInterrupts(); - spi_flash_read(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(_data), _size); - interrupts(); + noInterrupts(); + spi_flash_read(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(_data), _size); + 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 } -void EEPROMClass::end() { - if (!_size) - return; - - commit(); - if(_data) { - delete[] _data; - } - _data = 0; - _size = 0; - _dirty = false; +void EEPROMClass::end() +{ + if (!_size) + { + return; + } + + commit(); + if (_data) + { + delete[] _data; + } + _data = 0; + _size = 0; + _dirty = false; } -uint8_t EEPROMClass::read(int const address) { - if (address < 0 || (size_t)address >= _size) - return 0; - if(!_data) - return 0; +uint8_t EEPROMClass::read(int const address) +{ + if (address < 0 || (size_t)address >= _size) + { + return 0; + } + if (!_data) + { + return 0; + } - return _data[address]; + return _data[address]; } -void EEPROMClass::write(int const address, uint8_t const value) { - if (address < 0 || (size_t)address >= _size) - return; - if(!_data) - return; - - // Optimise _dirty. Only flagged if data written is different. - uint8_t* pData = &_data[address]; - if (*pData != value) - { - *pData = value; - _dirty = true; - } +void EEPROMClass::write(int const address, uint8_t const value) +{ + if (address < 0 || (size_t)address >= _size) + { + return; + } + if (!_data) + { + return; + } + + // Optimise _dirty. Only flagged if data written is different. + uint8_t* pData = &_data[address]; + if (*pData != value) + { + *pData = value; + _dirty = true; + } } -bool EEPROMClass::commit() { - bool ret = false; - if (!_size) - return false; - if(!_dirty) - return true; - if(!_data) - return false; - - noInterrupts(); - if(spi_flash_erase_sector(_sector) == SPI_FLASH_RESULT_OK) { - if(spi_flash_write(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(_data), _size) == SPI_FLASH_RESULT_OK) { - _dirty = false; - ret = true; +bool EEPROMClass::commit() +{ + bool ret = false; + if (!_size) + { + return false; + } + if (!_dirty) + { + return true; } - } - interrupts(); + if (!_data) + { + return false; + } + + noInterrupts(); + if (spi_flash_erase_sector(_sector) == SPI_FLASH_RESULT_OK) + { + if (spi_flash_write(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(_data), _size) == SPI_FLASH_RESULT_OK) + { + _dirty = false; + ret = true; + } + } + interrupts(); - return ret; + return ret; } -uint8_t * EEPROMClass::getDataPtr() { - _dirty = true; - return &_data[0]; +uint8_t * EEPROMClass::getDataPtr() +{ + _dirty = true; + return &_data[0]; } -uint8_t const * EEPROMClass::getConstDataPtr() const { - return &_data[0]; +uint8_t const * EEPROMClass::getConstDataPtr() const +{ + return &_data[0]; } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_EEPROM) diff --git a/libraries/EEPROM/EEPROM.h b/libraries/EEPROM/EEPROM.h index 54a9c1e336..433451d023 100644 --- a/libraries/EEPROM/EEPROM.h +++ b/libraries/EEPROM/EEPROM.h @@ -1,22 +1,22 @@ -/* - EEPROM.cpp - esp8266 EEPROM emulation - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +/* + EEPROM.cpp - esp8266 EEPROM emulation + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef EEPROM_h @@ -26,51 +26,68 @@ #include #include -class EEPROMClass { +class EEPROMClass +{ public: - EEPROMClass(uint32_t sector); - EEPROMClass(void); - - void begin(size_t size); - uint8_t read(int const address); - void write(int const address, uint8_t const val); - bool commit(); - void end(); - - uint8_t * getDataPtr(); - uint8_t const * getConstDataPtr() const; - - template - T &get(int const address, T &t) { - if (address < 0 || address + sizeof(T) > _size) - return t; - - memcpy((uint8_t*) &t, _data + address, sizeof(T)); - return t; - } - - template - const T &put(int const address, const T &t) { - if (address < 0 || address + sizeof(T) > _size) - return t; - if (memcmp(_data + address, (const uint8_t*)&t, sizeof(T)) != 0) { - _dirty = true; - memcpy(_data + address, (const uint8_t*)&t, sizeof(T)); + EEPROMClass(uint32_t sector); + EEPROMClass(void); + + void begin(size_t size); + uint8_t read(int const address); + void write(int const address, uint8_t const val); + bool commit(); + void end(); + + uint8_t * getDataPtr(); + uint8_t const * getConstDataPtr() const; + + template + T &get(int const address, T &t) + { + if (address < 0 || address + sizeof(T) > _size) + { + return t; + } + + memcpy((uint8_t*) &t, _data + address, sizeof(T)); + return t; } - return t; - } + template + const T &put(int const address, const T &t) + { + if (address < 0 || address + sizeof(T) > _size) + { + return t; + } + if (memcmp(_data + address, (const uint8_t*)&t, sizeof(T)) != 0) + { + _dirty = true; + memcpy(_data + address, (const uint8_t*)&t, sizeof(T)); + } + + return t; + } - size_t length() {return _size;} + size_t length() + { + return _size; + } - uint8_t& operator[](int const address) {return getDataPtr()[address];} - uint8_t const & operator[](int const address) const {return getConstDataPtr()[address];} + uint8_t& operator[](int const address) + { + return getDataPtr()[address]; + } + uint8_t const & operator[](int const address) const + { + return getConstDataPtr()[address]; + } protected: - uint32_t _sector; - uint8_t* _data; - size_t _size; - bool _dirty; + uint32_t _sector; + uint8_t* _data; + size_t _size; + bool _dirty; }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_EEPROM) diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp index 187c917530..3b36c82bc5 100644 --- a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp @@ -1,8 +1,8 @@ /* -AVR In-System Programming over WiFi for ESP8266 -Copyright (c) Kiril Zyapkov + AVR In-System Programming over WiFi for ESP8266 + Copyright (c) Kiril Zyapkov -Original version: + Original version: ArduinoISP version 04m3 Copyright (c) 2008-2011 Randall Bohn If you require a license, see @@ -19,16 +19,16 @@ Original version: #include "command.h" extern "C" { - #include "user_interface.h" - #include "mem.h" +#include "user_interface.h" +#include "mem.h" } #ifdef malloc - #undef malloc +#undef malloc #endif #define malloc os_malloc #ifdef free - #undef free +#undef free #endif #define free os_free @@ -52,126 +52,165 @@ ESP8266AVRISP::ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq setReset(_reset_state); } -void ESP8266AVRISP::begin() { +void ESP8266AVRISP::begin() +{ _server.begin(); } -void ESP8266AVRISP::setSpiFrequency(uint32_t freq) { +void ESP8266AVRISP::setSpiFrequency(uint32_t freq) +{ _spi_freq = freq; - if (_state == AVRISP_STATE_ACTIVE) { + if (_state == AVRISP_STATE_ACTIVE) + { SPI.setFrequency(freq); } } -void ESP8266AVRISP::setReset(bool rst) { +void ESP8266AVRISP::setReset(bool rst) +{ _reset_state = rst; digitalWrite(_reset_pin, _resetLevel(_reset_state)); } -AVRISPState_t ESP8266AVRISP::update() { - switch (_state) { - case AVRISP_STATE_IDLE: { - if (_server.hasClient()) { - _client = _server.available(); - _client.setNoDelay(true); - AVRISP_DEBUG("client connect %s:%d", _client.remoteIP().toString().c_str(), _client.remotePort()); - _client.setTimeout(100); // for getch() - _state = AVRISP_STATE_PENDING; - _reject_incoming(); - } - break; +AVRISPState_t ESP8266AVRISP::update() +{ + switch (_state) + { + case AVRISP_STATE_IDLE: + { + if (_server.hasClient()) + { + _client = _server.available(); + _client.setNoDelay(true); + AVRISP_DEBUG("client connect %s:%d", _client.remoteIP().toString().c_str(), _client.remotePort()); + _client.setTimeout(100); // for getch() + _state = AVRISP_STATE_PENDING; + _reject_incoming(); } - case AVRISP_STATE_PENDING: - case AVRISP_STATE_ACTIVE: { - // handle disconnect - if (!_client.connected()) { - _client.stop(); - AVRISP_DEBUG("client disconnect"); - if (pmode) { - SPI.end(); - pmode = 0; - } - setReset(_reset_state); - _state = AVRISP_STATE_IDLE; - } else { - _reject_incoming(); + break; + } + case AVRISP_STATE_PENDING: + case AVRISP_STATE_ACTIVE: + { + // handle disconnect + if (!_client.connected()) + { + _client.stop(); + AVRISP_DEBUG("client disconnect"); + if (pmode) + { + SPI.end(); + pmode = 0; } - break; + setReset(_reset_state); + _state = AVRISP_STATE_IDLE; + } + else + { + _reject_incoming(); } + break; + } } return _state; } -AVRISPState_t ESP8266AVRISP::serve() { - switch (update()) { - case AVRISP_STATE_IDLE: - // should not be called when idle, error? - break; - case AVRISP_STATE_PENDING: { - _state = AVRISP_STATE_ACTIVE; +AVRISPState_t ESP8266AVRISP::serve() +{ + switch (update()) + { + case AVRISP_STATE_IDLE: + // should not be called when idle, error? + break; + case AVRISP_STATE_PENDING: + { + _state = AVRISP_STATE_ACTIVE; // fallthrough + } + case AVRISP_STATE_ACTIVE: + { + while (_client.available()) + { + avrisp(); } - case AVRISP_STATE_ACTIVE: { - while (_client.available()) { - avrisp(); - } - return update(); - } + return update(); + } } return _state; } -inline void ESP8266AVRISP::_reject_incoming(void) { - while (_server.hasClient()) _server.available().stop(); +inline void ESP8266AVRISP::_reject_incoming(void) +{ + while (_server.hasClient()) + { + _server.available().stop(); + } } -uint8_t ESP8266AVRISP::getch() { - while (!_client.available()) yield(); +uint8_t ESP8266AVRISP::getch() +{ + while (!_client.available()) + { + yield(); + } uint8_t b = (uint8_t)_client.read(); // AVRISP_DEBUG("< %02x", b); return b; } -void ESP8266AVRISP::fill(int n) { +void ESP8266AVRISP::fill(int n) +{ // AVRISP_DEBUG("fill(%u)", n); - for (int x = 0; x < n; x++) { + for (int x = 0; x < n; x++) + { buff[x] = getch(); } } -uint8_t ESP8266AVRISP::spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { +uint8_t ESP8266AVRISP::spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) +{ SPI.transfer(a); SPI.transfer(b); SPI.transfer(c); return SPI.transfer(d); } -void ESP8266AVRISP::empty_reply() { - if (Sync_CRC_EOP == getch()) { +void ESP8266AVRISP::empty_reply() +{ + if (Sync_CRC_EOP == getch()) + { _client.print((char)Resp_STK_INSYNC); _client.print((char)Resp_STK_OK); - } else { + } + else + { error++; _client.print((char)Resp_STK_NOSYNC); } } -void ESP8266AVRISP::breply(uint8_t b) { - if (Sync_CRC_EOP == getch()) { +void ESP8266AVRISP::breply(uint8_t b) +{ + if (Sync_CRC_EOP == getch()) + { uint8_t resp[3]; resp[0] = Resp_STK_INSYNC; resp[1] = b; resp[2] = Resp_STK_OK; _client.write((const uint8_t *)resp, (size_t)3); - } else { + } + else + { error++; _client.print((char)Resp_STK_NOSYNC); } } -void ESP8266AVRISP::get_parameter(uint8_t c) { - switch (c) { +void ESP8266AVRISP::get_parameter(uint8_t c) +{ + switch (c) + { case 0x80: breply(AVRISP_HWVER); break; @@ -189,7 +228,8 @@ void ESP8266AVRISP::get_parameter(uint8_t c) { } } -void ESP8266AVRISP::set_parameters() { +void ESP8266AVRISP::set_parameters() +{ // call this after reading paramter packet into buff[] param.devicecode = buff[0]; param.revision = buff[1]; @@ -208,12 +248,13 @@ void ESP8266AVRISP::set_parameters() { // 32 bits flashsize (big endian) param.flashsize = buff[16] * 0x01000000 - + buff[17] * 0x00010000 - + buff[18] * 0x00000100 - + buff[19]; + + buff[17] * 0x00010000 + + buff[18] * 0x00000100 + + buff[19]; } -void ESP8266AVRISP::start_pmode() { +void ESP8266AVRISP::start_pmode() +{ SPI.begin(); SPI.setFrequency(_spi_freq); SPI.setHwCs(false); @@ -229,13 +270,15 @@ void ESP8266AVRISP::start_pmode() { pmode = 1; } -void ESP8266AVRISP::end_pmode() { +void ESP8266AVRISP::end_pmode() +{ SPI.end(); setReset(_reset_state); pmode = 0; } -void ESP8266AVRISP::universal() { +void ESP8266AVRISP::universal() +{ uint8_t ch; fill(4); @@ -243,47 +286,69 @@ void ESP8266AVRISP::universal() { breply(ch); } -void ESP8266AVRISP::flash(uint8_t hilo, int addr, uint8_t data) { +void ESP8266AVRISP::flash(uint8_t hilo, int addr, uint8_t data) +{ spi_transaction(0x40 + 8 * hilo, addr >> 8 & 0xFF, addr & 0xFF, data); } -void ESP8266AVRISP::commit(int addr) { +void ESP8266AVRISP::commit(int addr) +{ spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0); delay(AVRISP_PTIME); } //#define _addr_page(x) (here & 0xFFFFE0) -int ESP8266AVRISP::addr_page(int addr) { - if (param.pagesize == 32) return addr & 0xFFFFFFF0; - if (param.pagesize == 64) return addr & 0xFFFFFFE0; - if (param.pagesize == 128) return addr & 0xFFFFFFC0; - if (param.pagesize == 256) return addr & 0xFFFFFF80; +int ESP8266AVRISP::addr_page(int addr) +{ + if (param.pagesize == 32) + { + return addr & 0xFFFFFFF0; + } + if (param.pagesize == 64) + { + return addr & 0xFFFFFFE0; + } + if (param.pagesize == 128) + { + return addr & 0xFFFFFFC0; + } + if (param.pagesize == 256) + { + return addr & 0xFFFFFF80; + } AVRISP_DEBUG("unknown page size: %d", param.pagesize); return addr; } -void ESP8266AVRISP::write_flash(int length) { +void ESP8266AVRISP::write_flash(int length) +{ fill(length); - if (Sync_CRC_EOP == getch()) { + if (Sync_CRC_EOP == getch()) + { _client.print((char) Resp_STK_INSYNC); _client.print((char) write_flash_pages(length)); - } else { - error++; - _client.print((char) Resp_STK_NOSYNC); + } + else + { + error++; + _client.print((char) Resp_STK_NOSYNC); } } -uint8_t ESP8266AVRISP::write_flash_pages(int length) { +uint8_t ESP8266AVRISP::write_flash_pages(int length) +{ int x = 0; int page = addr_page(here); - while (x < length) { + while (x < length) + { yield(); - if (page != addr_page(here)) { + if (page != addr_page(here)) + { commit(page); page = addr_page(here); } @@ -295,15 +360,18 @@ uint8_t ESP8266AVRISP::write_flash_pages(int length) { return Resp_STK_OK; } -uint8_t ESP8266AVRISP::write_eeprom(int length) { +uint8_t ESP8266AVRISP::write_eeprom(int length) +{ // here is a word address, get the byte address int start = here * 2; int remaining = length; - if (length > param.eepromsize) { + if (length > param.eepromsize) + { error++; return Resp_STK_FAILED; } - while (remaining > EECHUNK) { + while (remaining > EECHUNK) + { write_eeprom_chunk(start, EECHUNK); start += EECHUNK; remaining -= EECHUNK; @@ -312,12 +380,14 @@ uint8_t ESP8266AVRISP::write_eeprom(int length) { return Resp_STK_OK; } // write (length) bytes, (start) is a byte address -uint8_t ESP8266AVRISP::write_eeprom_chunk(int start, int length) { +uint8_t ESP8266AVRISP::write_eeprom_chunk(int start, int length) +{ // this writes byte-by-byte, // page writing may be faster (4 bytes at a time) fill(length); // prog_lamp(LOW); - for (int x = 0; x < length; x++) { + for (int x = 0; x < length; x++) + { int addr = start + x; spi_transaction(0xC0, (addr >> 8) & 0xFF, addr & 0xFF, buff[x]); delay(45); @@ -326,43 +396,52 @@ uint8_t ESP8266AVRISP::write_eeprom_chunk(int start, int length) { return Resp_STK_OK; } -void ESP8266AVRISP::program_page() { +void ESP8266AVRISP::program_page() +{ char result = (char) Resp_STK_FAILED; int length = 256 * getch(); length += getch(); char memtype = getch(); // flash memory @here, (length) bytes - if (memtype == 'F') { + if (memtype == 'F') + { write_flash(length); return; } - if (memtype == 'E') { + if (memtype == 'E') + { result = (char)write_eeprom(length); - if (Sync_CRC_EOP == getch()) { + if (Sync_CRC_EOP == getch()) + { _client.print((char) Resp_STK_INSYNC); _client.print(result); - } else { + } + else + { error++; _client.print((char) Resp_STK_NOSYNC); } return; } _client.print((char)Resp_STK_FAILED); - return; + return; } -uint8_t ESP8266AVRISP::flash_read(uint8_t hilo, int addr) { +uint8_t ESP8266AVRISP::flash_read(uint8_t hilo, int addr) +{ return spi_transaction(0x20 + hilo * 8, (addr >> 8) & 0xFF, addr & 0xFF, 0); } -void ESP8266AVRISP::flash_read_page(int length) { +void ESP8266AVRISP::flash_read_page(int length) +{ uint8_t *data = (uint8_t *) malloc(length + 1); - for (int x = 0; x < length; x += 2) { + for (int x = 0; x < length; x += 2) + { *(data + x) = flash_read(LOW, here); *(data + x + 1) = flash_read(HIGH, here); here++; @@ -373,11 +452,13 @@ void ESP8266AVRISP::flash_read_page(int length) { return; } -void ESP8266AVRISP::eeprom_read_page(int length) { +void ESP8266AVRISP::eeprom_read_page(int length) +{ // here again we have a word address uint8_t *data = (uint8_t *) malloc(length + 1); int start = here * 2; - for (int x = 0; x < length; x++) { + for (int x = 0; x < length; x++) + { int addr = start + x; uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF); *(data + x) = ee; @@ -388,23 +469,33 @@ void ESP8266AVRISP::eeprom_read_page(int length) { return; } -void ESP8266AVRISP::read_page() { +void ESP8266AVRISP::read_page() +{ int length = 256 * getch(); length += getch(); char memtype = getch(); - if (Sync_CRC_EOP != getch()) { + if (Sync_CRC_EOP != getch()) + { error++; _client.print((char) Resp_STK_NOSYNC); return; } _client.print((char) Resp_STK_INSYNC); - if (memtype == 'F') flash_read_page(length); - if (memtype == 'E') eeprom_read_page(length); + if (memtype == 'F') + { + flash_read_page(length); + } + if (memtype == 'E') + { + eeprom_read_page(length); + } return; } -void ESP8266AVRISP::read_signature() { - if (Sync_CRC_EOP != getch()) { +void ESP8266AVRISP::read_signature() +{ + if (Sync_CRC_EOP != getch()) + { error++; _client.print((char) Resp_STK_NOSYNC); return; @@ -422,7 +513,8 @@ void ESP8266AVRISP::read_signature() { // It seems ArduinoISP is based on the original STK500 (not v2) // but implements only a subset of the commands. -void ESP8266AVRISP::avrisp() { +void ESP8266AVRISP::avrisp() +{ uint8_t data, low, high; uint8_t ch = getch(); // Avoid set but not used warning. Leaving them in as it helps document the code @@ -430,14 +522,16 @@ void ESP8266AVRISP::avrisp() { (void) low; (void) high; // AVRISP_DEBUG("CMD 0x%02x", ch); - switch (ch) { + switch (ch) + { case Cmnd_STK_GET_SYNC: error = 0; empty_reply(); break; case Cmnd_STK_GET_SIGN_ON: - if (getch() == Sync_CRC_EOP) { + if (getch() == Sync_CRC_EOP) + { _client.print((char) Resp_STK_INSYNC); _client.print(F("AVR ISP")); // AVR061 says "AVR STK"? _client.print((char) Resp_STK_OK); @@ -510,21 +604,24 @@ void ESP8266AVRISP::avrisp() { case Cmnd_STK_READ_SIGN: read_signature(); break; - // expecting a command, not Sync_CRC_EOP - // this is how we can get back in sync + // expecting a command, not Sync_CRC_EOP + // this is how we can get back in sync case Sync_CRC_EOP: // 0x20, space error++; _client.print((char) Resp_STK_NOSYNC); break; - // anything else we will return STK_UNKNOWN + // anything else we will return STK_UNKNOWN default: AVRISP_DEBUG("?!?"); error++; - if (Sync_CRC_EOP == getch()) { + if (Sync_CRC_EOP == getch()) + { _client.print((char)Resp_STK_UNKNOWN); - } else { + } + else + { _client.print((char)Resp_STK_NOSYNC); } - } + } } diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h index 492831d3df..497fc2e8d8 100644 --- a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h @@ -1,8 +1,8 @@ /* -AVR In-System Programming over WiFi for ESP8266 -Copyright (c) Kiril Zyapkov + AVR In-System Programming over WiFi for ESP8266 + Copyright (c) Kiril Zyapkov -Original version: + Original version: ArduinoISP version 04m3 Copyright (c) 2008-2011 Randall Bohn If you require a license, see @@ -21,14 +21,16 @@ Original version: #define AVRISP_SPI_FREQ 300e3 // programmer states -typedef enum { +typedef enum +{ AVRISP_STATE_IDLE = 0, // no active TCP session AVRISP_STATE_PENDING, // TCP connected, pending SPI activation AVRISP_STATE_ACTIVE // programmer is active and owns the SPI bus } AVRISPState_t; // stk500 parameters -typedef struct { +typedef struct +{ uint8_t devicecode; uint8_t revision; uint8_t progtype; @@ -45,9 +47,10 @@ typedef struct { } AVRISP_parameter_t; -class ESP8266AVRISP { +class ESP8266AVRISP +{ public: - ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq=AVRISP_SPI_FREQ, bool reset_state=false, bool reset_activehigh=false); + ESP8266AVRISP(uint16_t port, uint8_t reset_pin, uint32_t spi_freq = AVRISP_SPI_FREQ, bool reset_state = false, bool reset_activehigh = false); void begin(); @@ -100,7 +103,10 @@ class ESP8266AVRISP { void start_pmode(void); // enter program mode void end_pmode(void); // exit program mode - inline bool _resetLevel(bool reset_state) { return reset_state == _reset_activehigh; } + inline bool _resetLevel(bool reset_state) + { + return reset_state == _reset_activehigh; + } uint32_t _spi_freq; WiFiServer _server; diff --git a/libraries/ESP8266AVRISP/src/command.h b/libraries/ESP8266AVRISP/src/command.h index 2adc22bd30..4546c076fb 100644 --- a/libraries/ESP8266AVRISP/src/command.h +++ b/libraries/ESP8266AVRISP/src/command.h @@ -1,108 +1,108 @@ -//**** ATMEL AVR - A P P L I C A T I O N N O T E ************************ -//* -//* Title: AVR061 - STK500 Communication Protocol -//* Filename: command.h -//* Version: 1.0 -//* Last updated: 09.09.2002 -//* -//* Support E-mail: avr@atmel.com -//* -//************************************************************************** - -// *****************[ STK Message constants ]*************************** - -#define STK_SIGN_ON_MESSAGE "AVR STK" // Sign on string for Cmnd_STK_GET_SIGN_ON - -// *****************[ STK Response constants ]*************************** - -#define Resp_STK_OK 0x10 // ' ' -#define Resp_STK_FAILED 0x11 // ' ' -#define Resp_STK_UNKNOWN 0x12 // ' ' -#define Resp_STK_NODEVICE 0x13 // ' ' -#define Resp_STK_INSYNC 0x14 // ' ' -#define Resp_STK_NOSYNC 0x15 // ' ' - -#define Resp_ADC_CHANNEL_ERROR 0x16 // ' ' -#define Resp_ADC_MEASURE_OK 0x17 // ' ' -#define Resp_PWM_CHANNEL_ERROR 0x18 // ' ' -#define Resp_PWM_ADJUST_OK 0x19 // ' ' - -// *****************[ STK Special constants ]*************************** - -#define Sync_CRC_EOP 0x20 // 'SPACE' - -// *****************[ STK Command constants ]*************************** - -#define Cmnd_STK_GET_SYNC 0x30 // ' ' -#define Cmnd_STK_GET_SIGN_ON 0x31 // ' ' -#define Cmnd_STK_RESET 0x32 // ' ' -#define Cmnd_STK_SINGLE_CLOCK 0x33 // ' ' -#define Cmnd_STK_STORE_PARAMETERS 0x34 // ' ' - -#define Cmnd_STK_SET_PARAMETER 0x40 // ' ' -#define Cmnd_STK_GET_PARAMETER 0x41 // ' ' -#define Cmnd_STK_SET_DEVICE 0x42 // ' ' -#define Cmnd_STK_GET_DEVICE 0x43 // ' ' -#define Cmnd_STK_GET_STATUS 0x44 // ' ' -#define Cmnd_STK_SET_DEVICE_EXT 0x45 // ' ' - -#define Cmnd_STK_ENTER_PROGMODE 0x50 // ' ' -#define Cmnd_STK_LEAVE_PROGMODE 0x51 // ' ' -#define Cmnd_STK_CHIP_ERASE 0x52 // ' ' -#define Cmnd_STK_CHECK_AUTOINC 0x53 // ' ' -#define Cmnd_STK_CHECK_DEVICE 0x54 // ' ' -#define Cmnd_STK_LOAD_ADDRESS 0x55 // ' ' -#define Cmnd_STK_UNIVERSAL 0x56 // ' ' - -#define Cmnd_STK_PROG_FLASH 0x60 // ' ' -#define Cmnd_STK_PROG_DATA 0x61 // ' ' -#define Cmnd_STK_PROG_FUSE 0x62 // ' ' -#define Cmnd_STK_PROG_LOCK 0x63 // ' ' -#define Cmnd_STK_PROG_PAGE 0x64 // ' ' -#define Cmnd_STK_PROG_FUSE_EXT 0x65 // ' ' - -#define Cmnd_STK_READ_FLASH 0x70 // ' ' -#define Cmnd_STK_READ_DATA 0x71 // ' ' -#define Cmnd_STK_READ_FUSE 0x72 // ' ' -#define Cmnd_STK_READ_LOCK 0x73 // ' ' -#define Cmnd_STK_READ_PAGE 0x74 // ' ' -#define Cmnd_STK_READ_SIGN 0x75 // ' ' -#define Cmnd_STK_READ_OSCCAL 0x76 // ' ' -#define Cmnd_STK_READ_FUSE_EXT 0x77 // ' ' -#define Cmnd_STK_READ_OSCCAL_EXT 0x78 // ' ' - -// *****************[ STK Parameter constants ]*************************** - -#define Parm_STK_HW_VER 0x80 // ' ' - R -#define Parm_STK_SW_MAJOR 0x81 // ' ' - R -#define Parm_STK_SW_MINOR 0x82 // ' ' - R -#define Parm_STK_LEDS 0x83 // ' ' - R/W -#define Parm_STK_VTARGET 0x84 // ' ' - R/W -#define Parm_STK_VADJUST 0x85 // ' ' - R/W -#define Parm_STK_OSC_PSCALE 0x86 // ' ' - R/W -#define Parm_STK_OSC_CMATCH 0x87 // ' ' - R/W -#define Parm_STK_RESET_DURATION 0x88 // ' ' - R/W -#define Parm_STK_SCK_DURATION 0x89 // ' ' - R/W - -#define Parm_STK_BUFSIZEL 0x90 // ' ' - R/W, Range {0..255} -#define Parm_STK_BUFSIZEH 0x91 // ' ' - R/W, Range {0..255} -#define Parm_STK_DEVICE 0x92 // ' ' - R/W, Range {0..255} -#define Parm_STK_PROGMODE 0x93 // ' ' - 'P' or 'S' -#define Parm_STK_PARAMODE 0x94 // ' ' - TRUE or FALSE -#define Parm_STK_POLLING 0x95 // ' ' - TRUE or FALSE -#define Parm_STK_SELFTIMED 0x96 // ' ' - TRUE or FALSE - - -// *****************[ STK status bit definitions ]*************************** - -#define Stat_STK_INSYNC 0x01 // INSYNC status bit, '1' - INSYNC -#define Stat_STK_PROGMODE 0x02 // Programming mode, '1' - PROGMODE -#define Stat_STK_STANDALONE 0x04 // Standalone mode, '1' - SM mode -#define Stat_STK_RESET 0x08 // RESET button, '1' - Pushed -#define Stat_STK_PROGRAM 0x10 // Program button, ' 1' - Pushed -#define Stat_STK_LEDG 0x20 // Green LED status, '1' - Lit -#define Stat_STK_LEDR 0x40 // Red LED status, '1' - Lit -#define Stat_STK_LEDBLINK 0x80 // LED blink ON/OFF, '1' - Blink - - -// *****************************[ End Of COMMAND.H ]************************** +//**** ATMEL AVR - A P P L I C A T I O N N O T E ************************ +//* +//* Title: AVR061 - STK500 Communication Protocol +//* Filename: command.h +//* Version: 1.0 +//* Last updated: 09.09.2002 +//* +//* Support E-mail: avr@atmel.com +//* +//************************************************************************** + +// *****************[ STK Message constants ]*************************** + +#define STK_SIGN_ON_MESSAGE "AVR STK" // Sign on string for Cmnd_STK_GET_SIGN_ON + +// *****************[ STK Response constants ]*************************** + +#define Resp_STK_OK 0x10 // ' ' +#define Resp_STK_FAILED 0x11 // ' ' +#define Resp_STK_UNKNOWN 0x12 // ' ' +#define Resp_STK_NODEVICE 0x13 // ' ' +#define Resp_STK_INSYNC 0x14 // ' ' +#define Resp_STK_NOSYNC 0x15 // ' ' + +#define Resp_ADC_CHANNEL_ERROR 0x16 // ' ' +#define Resp_ADC_MEASURE_OK 0x17 // ' ' +#define Resp_PWM_CHANNEL_ERROR 0x18 // ' ' +#define Resp_PWM_ADJUST_OK 0x19 // ' ' + +// *****************[ STK Special constants ]*************************** + +#define Sync_CRC_EOP 0x20 // 'SPACE' + +// *****************[ STK Command constants ]*************************** + +#define Cmnd_STK_GET_SYNC 0x30 // ' ' +#define Cmnd_STK_GET_SIGN_ON 0x31 // ' ' +#define Cmnd_STK_RESET 0x32 // ' ' +#define Cmnd_STK_SINGLE_CLOCK 0x33 // ' ' +#define Cmnd_STK_STORE_PARAMETERS 0x34 // ' ' + +#define Cmnd_STK_SET_PARAMETER 0x40 // ' ' +#define Cmnd_STK_GET_PARAMETER 0x41 // ' ' +#define Cmnd_STK_SET_DEVICE 0x42 // ' ' +#define Cmnd_STK_GET_DEVICE 0x43 // ' ' +#define Cmnd_STK_GET_STATUS 0x44 // ' ' +#define Cmnd_STK_SET_DEVICE_EXT 0x45 // ' ' + +#define Cmnd_STK_ENTER_PROGMODE 0x50 // ' ' +#define Cmnd_STK_LEAVE_PROGMODE 0x51 // ' ' +#define Cmnd_STK_CHIP_ERASE 0x52 // ' ' +#define Cmnd_STK_CHECK_AUTOINC 0x53 // ' ' +#define Cmnd_STK_CHECK_DEVICE 0x54 // ' ' +#define Cmnd_STK_LOAD_ADDRESS 0x55 // ' ' +#define Cmnd_STK_UNIVERSAL 0x56 // ' ' + +#define Cmnd_STK_PROG_FLASH 0x60 // ' ' +#define Cmnd_STK_PROG_DATA 0x61 // ' ' +#define Cmnd_STK_PROG_FUSE 0x62 // ' ' +#define Cmnd_STK_PROG_LOCK 0x63 // ' ' +#define Cmnd_STK_PROG_PAGE 0x64 // ' ' +#define Cmnd_STK_PROG_FUSE_EXT 0x65 // ' ' + +#define Cmnd_STK_READ_FLASH 0x70 // ' ' +#define Cmnd_STK_READ_DATA 0x71 // ' ' +#define Cmnd_STK_READ_FUSE 0x72 // ' ' +#define Cmnd_STK_READ_LOCK 0x73 // ' ' +#define Cmnd_STK_READ_PAGE 0x74 // ' ' +#define Cmnd_STK_READ_SIGN 0x75 // ' ' +#define Cmnd_STK_READ_OSCCAL 0x76 // ' ' +#define Cmnd_STK_READ_FUSE_EXT 0x77 // ' ' +#define Cmnd_STK_READ_OSCCAL_EXT 0x78 // ' ' + +// *****************[ STK Parameter constants ]*************************** + +#define Parm_STK_HW_VER 0x80 // ' ' - R +#define Parm_STK_SW_MAJOR 0x81 // ' ' - R +#define Parm_STK_SW_MINOR 0x82 // ' ' - R +#define Parm_STK_LEDS 0x83 // ' ' - R/W +#define Parm_STK_VTARGET 0x84 // ' ' - R/W +#define Parm_STK_VADJUST 0x85 // ' ' - R/W +#define Parm_STK_OSC_PSCALE 0x86 // ' ' - R/W +#define Parm_STK_OSC_CMATCH 0x87 // ' ' - R/W +#define Parm_STK_RESET_DURATION 0x88 // ' ' - R/W +#define Parm_STK_SCK_DURATION 0x89 // ' ' - R/W + +#define Parm_STK_BUFSIZEL 0x90 // ' ' - R/W, Range {0..255} +#define Parm_STK_BUFSIZEH 0x91 // ' ' - R/W, Range {0..255} +#define Parm_STK_DEVICE 0x92 // ' ' - R/W, Range {0..255} +#define Parm_STK_PROGMODE 0x93 // ' ' - 'P' or 'S' +#define Parm_STK_PARAMODE 0x94 // ' ' - TRUE or FALSE +#define Parm_STK_POLLING 0x95 // ' ' - TRUE or FALSE +#define Parm_STK_SELFTIMED 0x96 // ' ' - TRUE or FALSE + + +// *****************[ STK status bit definitions ]*************************** + +#define Stat_STK_INSYNC 0x01 // INSYNC status bit, '1' - INSYNC +#define Stat_STK_PROGMODE 0x02 // Programming mode, '1' - PROGMODE +#define Stat_STK_STANDALONE 0x04 // Standalone mode, '1' - SM mode +#define Stat_STK_RESET 0x08 // RESET button, '1' - Pushed +#define Stat_STK_PROGRAM 0x10 // Program button, ' 1' - Pushed +#define Stat_STK_LEDG 0x20 // Green LED status, '1' - Lit +#define Stat_STK_LEDR 0x40 // Red LED status, '1' - Lit +#define Stat_STK_LEDBLINK 0x80 // LED blink ON/OFF, '1' - Blink + + +// *****************************[ End Of COMMAND.H ]************************** diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 8d7880e4ef..5b71c277c3 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -1,26 +1,26 @@ /** - * ESP8266HTTPClient.cpp - * - * Created on: 02.11.2015 - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the ESP8266HTTPClient for Arduino. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ + ESP8266HTTPClient.cpp + + Created on: 02.11.2015 + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the ESP8266HTTPClient for Arduino. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ #include #include "ESP8266HTTPClient.h" @@ -110,8 +110,8 @@ class BearSSLTraits : public TransportTraits #endif // HTTPCLIENT_1_1_COMPATIBLE /** - * constructor - */ + constructor +*/ HTTPClient::HTTPClient() { _client = nullptr; @@ -121,14 +121,16 @@ HTTPClient::HTTPClient() } /** - * destructor - */ + destructor +*/ HTTPClient::~HTTPClient() { - if(_client) { + if (_client) + { _client->stop(); } - if(_currentHeaders) { + if (_currentHeaders) + { delete[] _currentHeaders; } } @@ -144,15 +146,17 @@ void HTTPClient::clear() /** - * parsing the url for all needed parameters - * @param client Client& - * @param url String - * @param https bool - * @return success bool - */ -bool HTTPClient::begin(WiFiClient &client, String url) { + parsing the url for all needed parameters + @param client Client& + @param url String + @param https bool + @return success bool +*/ +bool HTTPClient::begin(WiFiClient &client, String url) +{ #if HTTPCLIENT_1_1_COMPATIBLE - if(_tcpDeprecated) { + if (_tcpDeprecated) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] mix up of new and deprecated api\n"); _canReuse = false; end(); @@ -163,13 +167,15 @@ bool HTTPClient::begin(WiFiClient &client, String url) { // check for : (http: or https:) int index = url.indexOf(':'); - if(index < 0) { + if (index < 0) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] failed to parse protocol\n"); return false; } String protocol = url.substring(0, index); - if(protocol != "http" && protocol != "https") { + if (protocol != "http" && protocol != "https") + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] unknown protocol '%s'\n", protocol.c_str()); return false; } @@ -180,18 +186,19 @@ bool HTTPClient::begin(WiFiClient &client, String url) { /** - * directly supply all needed parameters - * @param client Client& - * @param host String - * @param port uint16_t - * @param uri String - * @param https bool - * @return success bool - */ + directly supply all needed parameters + @param client Client& + @param host String + @param port uint16_t + @param uri String + @param https bool + @return success bool +*/ bool HTTPClient::begin(WiFiClient &client, String host, uint16_t port, String uri, bool https) { #if HTTPCLIENT_1_1_COMPATIBLE - if(_tcpDeprecated) { + if (_tcpDeprecated) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] mix up of new and deprecated api\n"); _canReuse = false; end(); @@ -200,7 +207,7 @@ bool HTTPClient::begin(WiFiClient &client, String host, uint16_t port, String ur _client = &client; - clear(); + clear(); _host = host; _port = port; _uri = uri; @@ -212,16 +219,19 @@ bool HTTPClient::begin(WiFiClient &client, String host, uint16_t port, String ur #if HTTPCLIENT_1_1_COMPATIBLE bool HTTPClient::begin(String url, String httpsFingerprint) { - if(_client && !_tcpDeprecated) { + if (_client && !_tcpDeprecated) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] mix up of new and deprecated api\n"); _canReuse = false; end(); } - if (httpsFingerprint.length() == 0) { + if (httpsFingerprint.length() == 0) + { return false; } - if (!beginInternal(url, "https")) { + if (!beginInternal(url, "https")) + { return false; } _transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint)); @@ -232,18 +242,21 @@ bool HTTPClient::begin(String url, String httpsFingerprint) bool HTTPClient::begin(String url, const uint8_t httpsFingerprint[20]) { - if(_client && !_tcpDeprecated) { + if (_client && !_tcpDeprecated) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] mix up of new and deprecated api\n"); _canReuse = false; end(); } - if (!beginInternal(url, "https")) { + if (!beginInternal(url, "https")) + { return false; } _transportTraits = TransportTraitsPtr(new BearSSLTraits(httpsFingerprint)); DEBUG_HTTPCLIENT("[HTTP-Client][begin] BearSSL-httpsFingerprint:"); - for (size_t i=0; i < 20; i++) { + for (size_t i = 0; i < 20; i++) + { DEBUG_HTTPCLIENT(" %02x", httpsFingerprint[i]); } DEBUG_HTTPCLIENT("\n"); @@ -252,18 +265,20 @@ bool HTTPClient::begin(String url, const uint8_t httpsFingerprint[20]) /** - * parsing the url for all needed parameters - * @param url String - */ + parsing the url for all needed parameters + @param url String +*/ bool HTTPClient::begin(String url) { - if(_client && !_tcpDeprecated) { + if (_client && !_tcpDeprecated) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] mix up of new and deprecated api\n"); _canReuse = false; end(); } - if (!beginInternal(url, "http")) { + if (!beginInternal(url, "http")) + { return false; } _transportTraits = TransportTraitsPtr(new TransportTraits()); @@ -278,7 +293,8 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol) // check for : (http: or https: int index = url.indexOf(':'); - if(index < 0) { + if (index < 0) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] failed to parse protocol\n"); return false; } @@ -286,13 +302,18 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol) _protocol = url.substring(0, index); url.remove(0, (index + 3)); // remove http:// or https:// - if (_protocol == "http") { + if (_protocol == "http") + { // set default port for 'http' _port = 80; - } else if (_protocol == "https") { + } + else if (_protocol == "https") + { // set default port for 'https' _port = 443; - } else { + } + else + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] unsupported protocol: %s\n", _protocol.c_str()); return false; } @@ -303,7 +324,8 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol) // get Authorization index = host.indexOf('@'); - if(index >= 0) { + if (index >= 0) + { // auth info String auth = host.substring(0, index); host.remove(0, index + 1); // remove auth part including @ @@ -312,16 +334,20 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol) // get port index = host.indexOf(':'); - if(index >= 0) { + if (index >= 0) + { _host = host.substring(0, index); // hostname host.remove(0, (index + 1)); // remove hostname + : _port = host.toInt(); // get port - } else { + } + else + { _host = host; } _uri = url; - if ( expectedProtocol != nullptr && _protocol != expectedProtocol) { + if (expectedProtocol != nullptr && _protocol != expectedProtocol) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] unexpected protocol: %s, expected %s\n", _protocol.c_str(), expectedProtocol); return false; } @@ -332,7 +358,8 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol) #if HTTPCLIENT_1_1_COMPATIBLE bool HTTPClient::begin(String host, uint16_t port, String uri) { - if(_client && !_tcpDeprecated) { + if (_client && !_tcpDeprecated) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] mix up of new and deprecated api\n"); _canReuse = false; end(); @@ -351,9 +378,12 @@ bool HTTPClient::begin(String host, uint16_t port, String uri) #pragma GCC diagnostic ignored "-Wdeprecated-declarations" bool HTTPClient::begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) { - if (https) { + if (https) + { return begin(host, port, uri, httpsFingerprint); - } else { + } + else + { return begin(host, port, uri); } } @@ -361,7 +391,8 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, bool https, Strin bool HTTPClient::begin(String host, uint16_t port, String uri, String httpsFingerprint) { - if(_client && !_tcpDeprecated) { + if (_client && !_tcpDeprecated) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] mix up of new and deprecated api\n"); _canReuse = false; end(); @@ -372,7 +403,8 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, String httpsFinge _port = port; _uri = uri; - if (httpsFingerprint.length() == 0) { + if (httpsFingerprint.length() == 0) + { return false; } _transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint)); @@ -382,7 +414,8 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, String httpsFinge bool HTTPClient::begin(String host, uint16_t port, String uri, const uint8_t httpsFingerprint[20]) { - if(_client && !_tcpDeprecated) { + if (_client && !_tcpDeprecated) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] mix up of new and deprecated api\n"); _canReuse = false; end(); @@ -395,7 +428,8 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, const uint8_t htt _transportTraits = TransportTraitsPtr(new BearSSLTraits(httpsFingerprint)); DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s BearSSL-httpsFingerprint:", host.c_str(), port, uri.c_str()); - for (size_t i=0; i < 20; i++) { + for (size_t i = 0; i < 20; i++) + { DEBUG_HTTPCLIENT(" %02x", httpsFingerprint[i]); } DEBUG_HTTPCLIENT("\n"); @@ -404,9 +438,9 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, const uint8_t htt #endif // HTTPCLIENT_1_1_COMPATIBLE /** - * end - * called after the payload is handled - */ + end + called after the payload is handled +*/ void HTTPClient::end(void) { disconnect(); @@ -415,80 +449,93 @@ void HTTPClient::end(void) } /** - * disconnect - * close the TCP socket - */ + disconnect + close the TCP socket +*/ void HTTPClient::disconnect(bool preserveClient) { - if(connected()) { - if(_client->available() > 0) { + if (connected()) + { + if (_client->available() > 0) + { DEBUG_HTTPCLIENT("[HTTP-Client][end] still data in buffer (%d), clean up.\n", _client->available()); - while(_client->available() > 0) { + while (_client->available() > 0) + { _client->read(); } } - if(_reuse && _canReuse) { + if (_reuse && _canReuse) + { DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp keep open for reuse\n"); - } else { + } + else + { DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp stop\n"); - if(_client) { + if (_client) + { _client->stop(); - if (!preserveClient) { + if (!preserveClient) + { _client = nullptr; } } #if HTTPCLIENT_1_1_COMPATIBLE - if(_tcpDeprecated) { + if (_tcpDeprecated) + { _transportTraits.reset(nullptr); _tcpDeprecated.reset(nullptr); } #endif } - } else { + } + else + { DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp is closed\n"); } } /** - * connected - * @return connected status - */ + connected + @return connected status +*/ bool HTTPClient::connected() { - if(_client) { + if (_client) + { return (_client->connected() || (_client->available() > 0)); } return false; } /** - * try to reuse the connection to the server - * keep-alive - * @param reuse bool - */ + try to reuse the connection to the server + keep-alive + @param reuse bool +*/ void HTTPClient::setReuse(bool reuse) { _reuse = reuse; } /** - * set User Agent - * @param userAgent const char * - */ + set User Agent + @param userAgent const char +*/ void HTTPClient::setUserAgent(const String& userAgent) { _userAgent = userAgent; } /** - * set the Authorizatio for the http request - * @param user const char * - * @param password const char * - */ + set the Authorizatio for the http request + @param user const char + @param password const char +*/ void HTTPClient::setAuthorization(const char * user, const char * password) { - if(user && password) { + if (user && password) + { String auth = user; auth += ":"; auth += password; @@ -497,42 +544,46 @@ void HTTPClient::setAuthorization(const char * user, const char * password) } /** - * set the Authorizatio for the http request - * @param auth const char * base64 - */ + set the Authorizatio for the http request + @param auth const char * base64 +*/ void HTTPClient::setAuthorization(const char * auth) { - if(auth) { + if (auth) + { _base64Authorization = auth; } } /** - * set the timeout for the TCP connection - * @param timeout unsigned int - */ + set the timeout for the TCP connection + @param timeout unsigned int +*/ void HTTPClient::setTimeout(uint16_t timeout) { _tcpTimeout = timeout; - if(connected()) { + if (connected()) + { _client->setTimeout(timeout); } } /** - * set the URL to a new value. Handy for following redirects. - * @param url - */ + set the URL to a new value. Handy for following redirects. + @param url +*/ bool HTTPClient::setURL(String url) { // if the new location is only a path then only update the URI - if (_location.startsWith("/")) { + if (_location.startsWith("/")) + { _uri = _location; clear(); return true; } - if (!url.startsWith(_protocol + ":")) { + if (!url.startsWith(_protocol + ":")) + { DEBUG_HTTPCLIENT("[HTTP-Client][setURL] new URL not the same protocol, expected '%s', URL: '%s'\n", _protocol.c_str(), url.c_str()); return false; } @@ -543,9 +594,9 @@ bool HTTPClient::setURL(String url) } /** - * set true to follow redirects. - * @param follow - */ + set true to follow redirects. + @param follow +*/ void HTTPClient::setFollowRedirects(bool follow) { _followRedirects = follow; @@ -557,29 +608,29 @@ void HTTPClient::setRedirectLimit(uint16_t limit) } /** - * use HTTP1.0 - * @param timeout - */ + use HTTP1.0 + @param timeout +*/ void HTTPClient::useHTTP10(bool useHTTP10) { _useHTTP10 = useHTTP10; } /** - * send a GET request - * @return http code - */ + send a GET request + @return http code +*/ int HTTPClient::GET() { return sendRequest("GET"); } /** - * sends a post request to the server - * @param payload uint8_t * - * @param size size_t - * @return http code - */ + sends a post request to the server + @param payload uint8_t + @param size size_t + @return http code +*/ int HTTPClient::POST(uint8_t * payload, size_t size) { return sendRequest("POST", payload, size); @@ -591,59 +642,66 @@ int HTTPClient::POST(String payload) } /** - * sends a put request to the server - * @param payload uint8_t * - * @param size size_t - * @return http code - */ -int HTTPClient::PUT(uint8_t * payload, size_t size) { + sends a put request to the server + @param payload uint8_t + @param size size_t + @return http code +*/ +int HTTPClient::PUT(uint8_t * payload, size_t size) +{ return sendRequest("PUT", payload, size); } -int HTTPClient::PUT(String payload) { +int HTTPClient::PUT(String payload) +{ return PUT((uint8_t *) payload.c_str(), payload.length()); } /** - * sends a patch request to the server - * @param payload uint8_t * - * @param size size_t - * @return http code - */ -int HTTPClient::PATCH(uint8_t * payload, size_t size) { + sends a patch request to the server + @param payload uint8_t + @param size size_t + @return http code +*/ +int HTTPClient::PATCH(uint8_t * payload, size_t size) +{ return sendRequest("PATCH", payload, size); } -int HTTPClient::PATCH(String payload) { +int HTTPClient::PATCH(String payload) +{ return PATCH((uint8_t *) payload.c_str(), payload.length()); } /** - * sendRequest - * @param type const char * "GET", "POST", .... - * @param payload String data for the message body - * @return - */ + sendRequest + @param type const char * "GET", "POST", .... + @param payload String data for the message body + @return +*/ int HTTPClient::sendRequest(const char * type, String payload) { return sendRequest(type, (uint8_t *) payload.c_str(), payload.length()); } /** - * sendRequest - * @param type const char * "GET", "POST", .... - * @param payload uint8_t * data for the message body if null not send - * @param size size_t size for the message body if 0 not send - * @return -1 if no info or > 0 when Content-Length is set by server - */ + sendRequest + @param type const char * "GET", "POST", .... + @param payload uint8_t * data for the message body if null not send + @param size size_t size for the message body if 0 not send + @return -1 if no info or > 0 when Content-Length is set by server +*/ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) { bool redirect = false; int code = 0; - do { + do + { // wipe out any existing headers from previous request - for(size_t i = 0; i < _headerKeysCount; i++) { - if (_currentHeaders[i].value.length() > 0) { + for (size_t i = 0; i < _headerKeysCount; i++) + { + if (_currentHeaders[i].value.length() > 0) + { _currentHeaders[i].value = ""; } } @@ -652,22 +710,27 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] type: '%s' redirCount: %d\n", type, _redirectCount); // connect to server - if(!connect()) { + if (!connect()) + { return returnError(HTTPC_ERROR_CONNECTION_REFUSED); } - if(payload && size > 0) { + if (payload && size > 0) + { addHeader(F("Content-Length"), String(size)); } // send Header - if(!sendHeader(type)) { + if (!sendHeader(type)) + { return returnError(HTTPC_ERROR_SEND_HEADER_FAILED); } // send Payload if needed - if(payload && size > 0) { - if(_client->write(&payload[0], size) != size) { + if (payload && size > 0) + { + if (_client->write(&payload[0], size) != size) + { return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); } } @@ -687,11 +750,13 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) (_location.length() > 0) && (code == 301 || code == 302 || code == 307) && (!strcmp(type, "GET") || !strcmp(type, "HEAD")) - ) { + ) + { _redirectCount += 1; // increment the count for redirect. redirect = true; DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] following redirect:: '%s' redirCount: %d\n", _location.c_str(), _redirectCount); - if (!setURL(_location)) { + if (!setURL(_location)) + { // return the redirect instead of handling on failure of setURL() redirect = false; } @@ -705,9 +770,11 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) (_location.length() > 0) && (code == 303) && strcmp(type, "GET") && strcmp(type, "HEAD") - ) { + ) + { _redirectCount += 1; - if (setURL(_location)) { + if (setURL(_location)) + { code = sendRequest("GET"); } } @@ -717,30 +784,34 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) } /** - * sendRequest - * @param type const char * "GET", "POST", .... - * @param stream Stream * data stream for the message body - * @param size size_t size for the message body if 0 not Content-Length is send - * @return -1 if no info or > 0 when Content-Length is set by server - */ + sendRequest + @param type const char * "GET", "POST", .... + @param stream Stream * data stream for the message body + @param size size_t size for the message body if 0 not Content-Length is send + @return -1 if no info or > 0 when Content-Length is set by server +*/ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) { - if(!stream) { + if (!stream) + { return returnError(HTTPC_ERROR_NO_STREAM); } // connect to server - if(!connect()) { + if (!connect()) + { return returnError(HTTPC_ERROR_CONNECTION_REFUSED); } - if(size > 0) { + if (size > 0) + { addHeader("Content-Length", String(size)); } // send Header - if(!sendHeader(type)) { + if (!sendHeader(type)) + { return returnError(HTTPC_ERROR_SEND_HEADER_FAILED); } @@ -749,36 +820,43 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) int len = size; int bytesWritten = 0; - if(len == 0) { + if (len == 0) + { len = -1; } // if possible create smaller buffer then HTTP_TCP_BUFFER_SIZE - if((len > 0) && (len < HTTP_TCP_BUFFER_SIZE)) { + if ((len > 0) && (len < HTTP_TCP_BUFFER_SIZE)) + { buff_size = len; } // create buffer for read uint8_t * buff = (uint8_t *) malloc(buff_size); - if(buff) { + if (buff) + { // read all data from stream and send it to server - while(connected() && (stream->available() > -1) && (len > 0 || len == -1)) { + while (connected() && (stream->available() > -1) && (len > 0 || len == -1)) + { // get available data size int sizeAvailable = stream->available(); - if(sizeAvailable) { + if (sizeAvailable) + { int readBytes = sizeAvailable; // read only the asked bytes - if(len > 0 && readBytes > len) { + if (len > 0 && readBytes > len) + { readBytes = len; } // not read more the buffer can handle - if(readBytes > buff_size) { + if (readBytes > buff_size) + { readBytes = buff_size; } @@ -790,11 +868,13 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) bytesWritten += bytesWrite; // are all Bytes a writen to stream ? - if(bytesWrite != bytesRead) { + if (bytesWrite != bytesRead) + { DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d retry...\n", bytesRead, bytesWrite); // check for write error - if(_client->getWriteError()) { + if (_client->getWriteError()) + { DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] stream write error %d\n", _client->getWriteError()); //reset write error for retry @@ -807,10 +887,11 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) int leftBytes = (readBytes - bytesWrite); // retry to send the missed bytes - bytesWrite = _client->write((const uint8_t *) (buff + bytesWrite), leftBytes); + bytesWrite = _client->write((const uint8_t *)(buff + bytesWrite), leftBytes); bytesWritten += bytesWrite; - if(bytesWrite != leftBytes) { + if (bytesWrite != leftBytes) + { // failed again DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d failed.\n", leftBytes, bytesWrite); free(buff); @@ -819,34 +900,43 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) } // check for write error - if(_client->getWriteError()) { + if (_client->getWriteError()) + { DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] stream write error %d\n", _client->getWriteError()); free(buff); return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); } // count bytes to read left - if(len > 0) { + if (len > 0) + { len -= readBytes; } delay(0); - } else { + } + else + { delay(1); } } free(buff); - if(size && (int) size != bytesWritten) { + if (size && (int) size != bytesWritten) + { DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload bytesWritten %d and size %zd mismatch!.\n", bytesWritten, size); DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] ERROR SEND PAYLOAD FAILED!"); return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); - } else { + } + else + { DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload written: %d\n", bytesWritten); } - } else { + } + else + { DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] not enough ram! need %d\n", HTTP_TCP_BUFFER_SIZE); return returnError(HTTPC_ERROR_TOO_LESS_RAM); } @@ -856,29 +946,30 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) } /** - * size of message body / payload - * @return -1 if no info or > 0 when Content-Length is set by server - */ + size of message body / payload + @return -1 if no info or > 0 when Content-Length is set by server +*/ int HTTPClient::getSize(void) { return _size; } /** - * Location if redirect - */ + Location if redirect +*/ const String& HTTPClient::getLocation(void) { return _location; } /** - * returns the stream of the tcp connection - * @return WiFiClient - */ + returns the stream of the tcp connection + @return WiFiClient +*/ WiFiClient& HTTPClient::getStream(void) { - if(connected()) { + if (connected()) + { return *_client; } @@ -888,12 +979,13 @@ WiFiClient& HTTPClient::getStream(void) } /** - * returns the stream of the tcp connection - * @return WiFiClient * - */ + returns the stream of the tcp connection + @return WiFiClient +*/ WiFiClient* HTTPClient::getStreamPtr(void) { - if(connected()) { + if (connected()) + { return _client; } @@ -902,18 +994,20 @@ WiFiClient* HTTPClient::getStreamPtr(void) } /** - * write all message body / payload to Stream - * @param stream Stream * - * @return bytes written ( negative values are error codes ) - */ + write all message body / payload to Stream + @param stream Stream + @return bytes written ( negative values are error codes ) +*/ int HTTPClient::writeToStream(Stream * stream) { - if(!stream) { + if (!stream) + { return returnError(HTTPC_ERROR_NO_STREAM); } - if(!connected()) { + if (!connected()) + { return returnError(HTTPC_ERROR_NOT_CONNECTED); } @@ -921,22 +1015,29 @@ int HTTPClient::writeToStream(Stream * stream) int len = _size; int ret = 0; - if(_transferEncoding == HTTPC_TE_IDENTITY) { + if (_transferEncoding == HTTPC_TE_IDENTITY) + { ret = writeToStreamDataBlock(stream, len); // have we an error? - if(ret < 0) { + if (ret < 0) + { return returnError(ret); } - } else if(_transferEncoding == HTTPC_TE_CHUNKED) { + } + else if (_transferEncoding == HTTPC_TE_CHUNKED) + { int size = 0; - while(1) { - if(!connected()) { + while (1) + { + if (!connected()) + { return returnError(HTTPC_ERROR_CONNECTION_LOST); } String chunkHeader = _client->readStringUntil('\n'); - if(chunkHeader.length() <= 0) { + if (chunkHeader.length() <= 0) + { return returnError(HTTPC_ERROR_READ_TIMEOUT); } @@ -948,22 +1049,28 @@ int HTTPClient::writeToStream(Stream * stream) DEBUG_HTTPCLIENT("[HTTP-Client] read chunk len: %d\n", len); // data left? - if(len > 0) { + if (len > 0) + { int r = writeToStreamDataBlock(stream, len); - if(r < 0) { + if (r < 0) + { // error in writeToStreamDataBlock return returnError(r); } ret += r; - } else { + } + else + { // if no length Header use global chunk size - if(_size <= 0) { + if (_size <= 0) + { _size = size; } // check if we have write all data out - if(ret != _size) { + if (ret != _size) + { return returnError(HTTPC_ERROR_STREAM_WRITE); } break; @@ -972,13 +1079,16 @@ int HTTPClient::writeToStream(Stream * stream) // read trailing \r\n at the end of the chunk char buf[2]; auto trailing_seq_len = _client->readBytes((uint8_t*)buf, 2); - if (trailing_seq_len != 2 || buf[0] != '\r' || buf[1] != '\n') { + if (trailing_seq_len != 2 || buf[0] != '\r' || buf[1] != '\n') + { return returnError(HTTPC_ERROR_READ_TIMEOUT); } delay(0); } - } else { + } + else + { return returnError(HTTPC_ERROR_ENCODING); } @@ -987,20 +1097,23 @@ int HTTPClient::writeToStream(Stream * stream) } /** - * return all payload as String (may need lot of ram or trigger out of memory!) - * @return String - */ + return all payload as String (may need lot of ram or trigger out of memory!) + @return String +*/ const String& HTTPClient::getString(void) { - if (_payload) { + if (_payload) + { return *_payload; } _payload.reset(new StreamString()); - if(_size) { + if (_size) + { // try to reserve needed memmory - if(!_payload->reserve((_size + 1))) { + if (!_payload->reserve((_size + 1))) + { DEBUG_HTTPCLIENT("[HTTP-Client][getString] not enough memory to reserve a string! need: %d\n", (_size + 1)); return *_payload; } @@ -1011,13 +1124,14 @@ const String& HTTPClient::getString(void) } /** - * converts error code to String - * @param error int - * @return String - */ + converts error code to String + @param error int + @return String +*/ String HTTPClient::errorToString(int error) { - switch(error) { + switch (error) + { case HTTPC_ERROR_CONNECTION_REFUSED: return F("connection refused"); case HTTPC_ERROR_SEND_HEADER_FAILED: @@ -1046,25 +1160,28 @@ String HTTPClient::errorToString(int error) } /** - * adds Header to the request - * @param name - * @param value - * @param first - */ + adds Header to the request + @param name + @param value + @param first +*/ void HTTPClient::addHeader(const String& name, const String& value, bool first, bool replace) { // not allow set of Header handled by code - if(!name.equalsIgnoreCase(F("Connection")) && - !name.equalsIgnoreCase(F("User-Agent")) && - !name.equalsIgnoreCase(F("Host")) && - !(name.equalsIgnoreCase(F("Authorization")) && _base64Authorization.length())){ + if (!name.equalsIgnoreCase(F("Connection")) && + !name.equalsIgnoreCase(F("User-Agent")) && + !name.equalsIgnoreCase(F("Host")) && + !(name.equalsIgnoreCase(F("Authorization")) && _base64Authorization.length())) + { String headerLine = name; headerLine += ": "; - if (replace) { + if (replace) + { int headerStart = _headers.indexOf(headerLine); - if (headerStart != -1) { + if (headerStart != -1) + { int headerEnd = _headers.indexOf('\n', headerStart); _headers = _headers.substring(0, headerStart) + _headers.substring(headerEnd + 1); } @@ -1072,9 +1189,12 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first, headerLine += value; headerLine += "\r\n"; - if(first) { + if (first) + { _headers = headerLine + _headers; - } else { + } + else + { _headers += headerLine; } } @@ -1084,19 +1204,23 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first, void HTTPClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { _headerKeysCount = headerKeysCount; - if(_currentHeaders) { + if (_currentHeaders) + { delete[] _currentHeaders; } _currentHeaders = new RequestArgument[_headerKeysCount]; - for(size_t i = 0; i < _headerKeysCount; i++) { + for (size_t i = 0; i < _headerKeysCount; i++) + { _currentHeaders[i].key = headerKeys[i]; } } String HTTPClient::header(const char* name) { - for(size_t i = 0; i < _headerKeysCount; ++i) { - if(_currentHeaders[i].key == name) { + for (size_t i = 0; i < _headerKeysCount; ++i) + { + if (_currentHeaders[i].key == name) + { return _currentHeaders[i].value; } } @@ -1105,7 +1229,8 @@ String HTTPClient::header(const char* name) String HTTPClient::header(size_t i) { - if(i < _headerKeysCount) { + if (i < _headerKeysCount) + { return _currentHeaders[i].value; } return String(); @@ -1113,7 +1238,8 @@ String HTTPClient::header(size_t i) String HTTPClient::headerName(size_t i) { - if(i < _headerKeysCount) { + if (i < _headerKeysCount) + { return _currentHeaders[i].key; } return String(); @@ -1126,8 +1252,10 @@ int HTTPClient::headers() bool HTTPClient::hasHeader(const char* name) { - for(size_t i = 0; i < _headerKeysCount; ++i) { - if((_currentHeaders[i].key == name) && (_currentHeaders[i].value.length() > 0)) { + for (size_t i = 0; i < _headerKeysCount; ++i) + { + if ((_currentHeaders[i].key == name) && (_currentHeaders[i].value.length() > 0)) + { return true; } } @@ -1135,34 +1263,39 @@ bool HTTPClient::hasHeader(const char* name) } /** - * init TCP connection and handle ssl verify if needed - * @return true if connection is ok - */ + init TCP connection and handle ssl verify if needed + @return true if connection is ok +*/ bool HTTPClient::connect(void) { - if(connected()) { + if (connected()) + { DEBUG_HTTPCLIENT("[HTTP-Client] connect. already connected, try reuse!\n"); - while(_client->available() > 0) { + while (_client->available() > 0) + { _client->read(); } return true; } #if HTTPCLIENT_1_1_COMPATIBLE - if(!_client && _transportTraits) { + if (!_client && _transportTraits) + { _tcpDeprecated = _transportTraits->create(); _client = _tcpDeprecated.get(); } #endif - if(!_client) { + if (!_client) + { DEBUG_HTTPCLIENT("[HTTP-Client] connect: HTTPClient::begin was not called or returned error\n"); return false; } _client->setTimeout(_tcpTimeout); - if(!_client->connect(_host.c_str(), _port)) { + if (!_client->connect(_host.c_str(), _port)) + { DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u\n", _host.c_str(), _port); return false; } @@ -1170,7 +1303,8 @@ bool HTTPClient::connect(void) DEBUG_HTTPCLIENT("[HTTP-Client] connected to %s:%u\n", _host.c_str(), _port); #if HTTPCLIENT_1_1_COMPATIBLE - if (_tcpDeprecated && !_transportTraits->verify(*_tcpDeprecated, _host.c_str())) { + if (_tcpDeprecated && !_transportTraits->verify(*_tcpDeprecated, _host.c_str())) + { DEBUG_HTTPCLIENT("[HTTP-Client] transport level verify failed\n"); _client->stop(); return false; @@ -1185,21 +1319,25 @@ bool HTTPClient::connect(void) } /** - * sends HTTP request header - * @param type (GET, POST, ...) - * @return status - */ + sends HTTP request header + @param type (GET, POST, ...) + @return status +*/ bool HTTPClient::sendHeader(const char * type) { - if(!connected()) { + if (!connected()) + { return false; } String header = String(type) + " " + (_uri.length() ? _uri : F("/")) + F(" HTTP/1."); - if(_useHTTP10) { + if (_useHTTP10) + { header += "0"; - } else { + } + else + { header += "1"; } @@ -1212,18 +1350,23 @@ bool HTTPClient::sendHeader(const char * type) header += String(F("\r\nUser-Agent: ")) + _userAgent + F("\r\nConnection: "); - if(_reuse) { + if (_reuse) + { header += F("keep-alive"); - } else { + } + else + { header += F("close"); } header += "\r\n"; - if(!_useHTTP10) { + if (!_useHTTP10) + { header += F("Accept-Encoding: identity;q=1,chunked;q=0.1,*;q=0\r\n"); } - if(_base64Authorization.length()) { + if (_base64Authorization.length()) + { _base64Authorization.replace("\n", ""); header += F("Authorization: Basic "); header += _base64Authorization; @@ -1238,13 +1381,14 @@ bool HTTPClient::sendHeader(const char * type) } /** - * reads the response from the server - * @return int http code - */ + reads the response from the server + @return int http code +*/ int HTTPClient::handleHeaderResponse() { - if(!connected()) { + if (!connected()) + { return HTTPC_ERROR_NOT_CONNECTED; } @@ -1254,9 +1398,11 @@ int HTTPClient::handleHeaderResponse() _transferEncoding = HTTPC_TE_IDENTITY; unsigned long lastDataTime = millis(); - while(connected()) { + while (connected()) + { size_t len = _client->available(); - if(len > 0) { + if (len > 0) + { String headerLine = _client->readStringUntil('\n'); headerLine.trim(); // remove \r @@ -1264,35 +1410,47 @@ int HTTPClient::handleHeaderResponse() DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] RX: '%s'\n", headerLine.c_str()); - if(headerLine.startsWith("HTTP/1.")) { + if (headerLine.startsWith("HTTP/1.")) + { _returnCode = headerLine.substring(9, headerLine.indexOf(' ', 9)).toInt(); - } else if(headerLine.indexOf(':')) { + } + else if (headerLine.indexOf(':')) + { String headerName = headerLine.substring(0, headerLine.indexOf(':')); String headerValue = headerLine.substring(headerLine.indexOf(':') + 1); headerValue.trim(); - if(headerName.equalsIgnoreCase("Content-Length")) { + if (headerName.equalsIgnoreCase("Content-Length")) + { _size = headerValue.toInt(); } - if(headerName.equalsIgnoreCase("Connection")) { + if (headerName.equalsIgnoreCase("Connection")) + { _canReuse = headerValue.equalsIgnoreCase("keep-alive"); } - if(headerName.equalsIgnoreCase("Transfer-Encoding")) { + if (headerName.equalsIgnoreCase("Transfer-Encoding")) + { transferEncoding = headerValue; } - if(headerName.equalsIgnoreCase("Location")) { + if (headerName.equalsIgnoreCase("Location")) + { _location = headerValue; } - for(size_t i = 0; i < _headerKeysCount; i++) { - if(_currentHeaders[i].key.equalsIgnoreCase(headerName)) { - if (_currentHeaders[i].value != "") { + for (size_t i = 0; i < _headerKeysCount; i++) + { + if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) + { + if (_currentHeaders[i].value != "") + { // Existing value, append this one with a comma _currentHeaders[i].value += "," + headerValue; - } else { + } + else + { _currentHeaders[i].value = headerValue; } break; // We found a match, stop looking @@ -1300,34 +1458,48 @@ int HTTPClient::handleHeaderResponse() } } - if(headerLine == "") { + if (headerLine == "") + { DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] code: %d\n", _returnCode); - if(_size > 0) { + if (_size > 0) + { DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] size: %d\n", _size); } - if(transferEncoding.length() > 0) { + if (transferEncoding.length() > 0) + { DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] Transfer-Encoding: %s\n", transferEncoding.c_str()); - if(transferEncoding.equalsIgnoreCase("chunked")) { + if (transferEncoding.equalsIgnoreCase("chunked")) + { _transferEncoding = HTTPC_TE_CHUNKED; - } else { + } + else + { return HTTPC_ERROR_ENCODING; } - } else { + } + else + { _transferEncoding = HTTPC_TE_IDENTITY; } - if(_returnCode) { + if (_returnCode) + { return _returnCode; - } else { + } + else + { DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] Remote host is not an HTTP Server!"); return HTTPC_ERROR_NO_HTTP_SERVER; } } - } else { - if((millis() - lastDataTime) > _tcpTimeout) { + } + else + { + if ((millis() - lastDataTime) > _tcpTimeout) + { return HTTPC_ERROR_READ_TIMEOUT; } delay(0); @@ -1338,11 +1510,11 @@ int HTTPClient::handleHeaderResponse() } /** - * write one Data Block to Stream - * @param stream Stream * - * @param size int - * @return < 0 = error >= 0 = size written - */ + write one Data Block to Stream + @param stream Stream + @param size int + @return < 0 = error >= 0 = size written +*/ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) { int buff_size = HTTP_TCP_BUFFER_SIZE; @@ -1350,25 +1522,28 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) int bytesWritten = 0; // if possible create smaller buffer then HTTP_TCP_BUFFER_SIZE - if((len > 0) && (len < HTTP_TCP_BUFFER_SIZE)) { + if ((len > 0) && (len < HTTP_TCP_BUFFER_SIZE)) + { buff_size = len; } // create buffer for read uint8_t * buff = (uint8_t *) malloc(buff_size); - if(!buff) { + if (!buff) + { DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] not enough ram! need %d\n", HTTP_TCP_BUFFER_SIZE); return HTTPC_ERROR_TOO_LESS_RAM; } // read all data from server - while(connected() && (len > 0 || len == -1)) + while (connected() && (len > 0 || len == -1)) { int readBytes = len; // not read more the buffer can handle - if(readBytes > buff_size) { + if (readBytes > buff_size) + { readBytes = buff_size; } @@ -1386,11 +1561,13 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) bytesWritten += bytesWrite; // are all Bytes a writen to stream ? - if(bytesWrite != bytesRead) { + if (bytesWrite != bytesRead) + { DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] short write asked for %d but got %d retry...\n", bytesRead, bytesWrite); // check for write error - if(stream->getWriteError()) { + if (stream->getWriteError()) + { DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] stream write error %d\n", stream->getWriteError()); //reset write error for retry @@ -1406,7 +1583,8 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) bytesWrite = stream->write((buff + bytesWrite), leftBytes); bytesWritten += bytesWrite; - if(bytesWrite != leftBytes) { + if (bytesWrite != leftBytes) + { // failed again DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] short write asked for %d but got %d failed.\n", leftBytes, bytesWrite); free(buff); @@ -1415,14 +1593,16 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) } // check for write error - if(stream->getWriteError()) { + if (stream->getWriteError()) + { DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] stream write error %d\n", stream->getWriteError()); free(buff); return HTTPC_ERROR_STREAM_WRITE; } // count bytes to read left - if(len > 0) { + if (len > 0) + { len -= bytesRead; } @@ -1433,7 +1613,8 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] connection closed or file end (written: %d).\n", bytesWritten); - if((size > 0) && (size != bytesWritten)) { + if ((size > 0) && (size != bytesWritten)) + { DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] bytesWritten %d and size %d mismatch!.\n", bytesWritten, size); return HTTPC_ERROR_STREAM_WRITE; } @@ -1442,15 +1623,17 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) } /** - * called to handle error return, may disconnect the connection if still exists - * @param error - * @return error - */ + called to handle error return, may disconnect the connection if still exists + @param error + @return error +*/ int HTTPClient::returnError(int error) { - if(error < 0) { + if (error < 0) + { DEBUG_HTTPCLIENT("[HTTP-Client][returnError] error(%d): %s\n", error, errorToString(error).c_str()); - if(connected()) { + if (connected()) + { DEBUG_HTTPCLIENT("[HTTP-Client][returnError] tcp stop\n"); _client->stop(); } diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 44335c505c..05c034b702 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -1,27 +1,27 @@ /** - * ESP8266HTTPClient.h - * - * Created on: 02.11.2015 - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the ESP8266HTTPClient for Arduino. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Modified by Jeroen Döll, June 2018 - */ + ESP8266HTTPClient.h + + Created on: 02.11.2015 + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the ESP8266HTTPClient for Arduino. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified by Jeroen Döll, June 2018 +*/ #ifndef ESP8266HTTPClient_H_ #define ESP8266HTTPClient_H_ @@ -64,7 +64,8 @@ #define HTTP_TCP_BUFFER_SIZE (1460) /// HTTP codes see RFC7231 -typedef enum { +typedef enum +{ HTTP_CODE_CONTINUE = 100, HTTP_CODE_SWITCHING_PROTOCOLS = 101, HTTP_CODE_PROCESSING = 102, @@ -125,7 +126,8 @@ typedef enum { HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511 } t_http_codes; -typedef enum { +typedef enum +{ HTTPC_TE_IDENTITY, HTTPC_TE_CHUNKED } transferEncoding_t; @@ -143,25 +145,25 @@ class HTTPClient HTTPClient(); ~HTTPClient(); -/* - * Since both begin() functions take a reference to client as a parameter, you need to - * ensure the client object lives the entire time of the HTTPClient - */ + /* + Since both begin() functions take a reference to client as a parameter, you need to + ensure the client object lives the entire time of the HTTPClient + */ bool begin(WiFiClient &client, String url); bool begin(WiFiClient &client, String host, uint16_t port, String uri = "/", bool https = false); #if HTTPCLIENT_1_1_COMPATIBLE // Plain HTTP connection, unencrypted - bool begin(String url) __attribute__ ((deprecated)); - bool begin(String host, uint16_t port, String uri = "/") __attribute__ ((deprecated)); + bool begin(String url) __attribute__((deprecated)); + bool begin(String host, uint16_t port, String uri = "/") __attribute__((deprecated)); // Use axTLS for secure HTTPS connection - bool begin(String url, String httpsFingerprint) __attribute__ ((deprecated)); - bool begin(String host, uint16_t port, String uri, String httpsFingerprint) __attribute__ ((deprecated)); + bool begin(String url, String httpsFingerprint) __attribute__((deprecated)); + bool begin(String host, uint16_t port, String uri, String httpsFingerprint) __attribute__((deprecated)); // Use BearSSL for secure HTTPS connection - bool begin(String url, const uint8_t httpsFingerprint[20]) __attribute__ ((deprecated)); - bool begin(String host, uint16_t port, String uri, const uint8_t httpsFingerprint[20]) __attribute__ ((deprecated)); + bool begin(String url, const uint8_t httpsFingerprint[20]) __attribute__((deprecated)); + bool begin(String host, uint16_t port, String uri, const uint8_t httpsFingerprint[20]) __attribute__((deprecated)); // deprecated, use the overload above instead - bool begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) __attribute__ ((deprecated)); + bool begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) __attribute__((deprecated)); #endif void end(void); @@ -211,7 +213,8 @@ class HTTPClient static String errorToString(int error); protected: - struct RequestArgument { + struct RequestArgument + { String key; String value; }; diff --git a/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.cpp b/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.cpp index b53f0fa749..733d997af5 100644 --- a/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.cpp +++ b/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.cpp @@ -8,21 +8,21 @@ static const char serverIndex[] PROGMEM = - R"(
+ R"(
)"; -static const char successResponse[] PROGMEM = - "Update Success! Rebooting...\n"; +static const char successResponse[] PROGMEM = + "Update Success! Rebooting...\n"; ESP8266HTTPUpdateServer::ESP8266HTTPUpdateServer(bool serial_debug) { - _serial_output = serial_debug; - _server = NULL; - _username = emptyString; - _password = emptyString; - _authenticated = false; + _serial_output = serial_debug; + _server = NULL; + _username = emptyString; + _password = emptyString; + _authenticated = false; } void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server, const String& path, const String& username, const String& password) @@ -32,73 +32,117 @@ void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server, const String& path _password = password; // handler for the /update form page - _server->on(path.c_str(), HTTP_GET, [&](){ - if(_username != emptyString && _password != emptyString && !_server->authenticate(_username.c_str(), _password.c_str())) - return _server->requestAuthentication(); - _server->send_P(200, PSTR("text/html"), serverIndex); + _server->on(path.c_str(), HTTP_GET, [&]() + { + if (_username != emptyString && _password != emptyString && !_server->authenticate(_username.c_str(), _password.c_str())) + { + return _server->requestAuthentication(); + } + _server->send_P(200, PSTR("text/html"), serverIndex); }); // handler for the /update form POST (once file upload finishes) - _server->on(path.c_str(), HTTP_POST, [&](){ - if(!_authenticated) - return _server->requestAuthentication(); - if (Update.hasError()) { - _server->send(200, F("text/html"), String(F("Update error: ")) + _updaterError); - } else { - _server->client().setNoDelay(true); - _server->send_P(200, PSTR("text/html"), successResponse); - delay(100); - _server->client().stop(); - ESP.restart(); - } - },[&](){ - // handler for the file upload, get's the sketch bytes, and writes - // them through the Update object - HTTPUpload& upload = _server->upload(); + _server->on(path.c_str(), HTTP_POST, [&]() + { + if (!_authenticated) + { + return _server->requestAuthentication(); + } + if (Update.hasError()) + { + _server->send(200, F("text/html"), String(F("Update error: ")) + _updaterError); + } + else + { + _server->client().setNoDelay(true); + _server->send_P(200, PSTR("text/html"), successResponse); + delay(100); + _server->client().stop(); + ESP.restart(); + } + }, [&]() + { + // handler for the file upload, get's the sketch bytes, and writes + // them through the Update object + HTTPUpload& upload = _server->upload(); - if(upload.status == UPLOAD_FILE_START){ - _updaterError = String(); - if (_serial_output) - Serial.setDebugOutput(true); + if (upload.status == UPLOAD_FILE_START) + { + _updaterError = String(); + if (_serial_output) + { + Serial.setDebugOutput(true); + } - _authenticated = (_username == emptyString || _password == emptyString || _server->authenticate(_username.c_str(), _password.c_str())); - if(!_authenticated){ - if (_serial_output) - Serial.printf("Unauthenticated Update\n"); - return; - } + _authenticated = (_username == emptyString || _password == emptyString || _server->authenticate(_username.c_str(), _password.c_str())); + if (!_authenticated) + { + if (_serial_output) + { + Serial.printf("Unauthenticated Update\n"); + } + return; + } - WiFiUDP::stopAll(); - if (_serial_output) - Serial.printf("Update: %s\n", upload.filename.c_str()); - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; - if(!Update.begin(maxSketchSpace)){//start with max available size - _setUpdaterError(); + WiFiUDP::stopAll(); + if (_serial_output) + { + Serial.printf("Update: %s\n", upload.filename.c_str()); + } + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + if (!Update.begin(maxSketchSpace)) //start with max available size + { + _setUpdaterError(); + } + } + else if (_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()) + { + if (_serial_output) + { + Serial.printf("."); + } + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) + { + _setUpdaterError(); + } } - } else if(_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()){ - if (_serial_output) Serial.printf("."); - if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){ - _setUpdaterError(); + else if (_authenticated && upload.status == UPLOAD_FILE_END && !_updaterError.length()) + { + if (Update.end(true)) //true to set the size to the current progress + { + if (_serial_output) + { + Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); + } + } + else + { + _setUpdaterError(); + } + if (_serial_output) + { + Serial.setDebugOutput(false); + } } - } else if(_authenticated && upload.status == UPLOAD_FILE_END && !_updaterError.length()){ - if(Update.end(true)){ //true to set the size to the current progress - if (_serial_output) Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); - } else { - _setUpdaterError(); + else if (_authenticated && upload.status == UPLOAD_FILE_ABORTED) + { + Update.end(); + if (_serial_output) + { + Serial.println("Update was aborted"); + } } - if (_serial_output) Serial.setDebugOutput(false); - } else if(_authenticated && upload.status == UPLOAD_FILE_ABORTED){ - Update.end(); - if (_serial_output) Serial.println("Update was aborted"); - } - delay(0); + delay(0); }); } void ESP8266HTTPUpdateServer::_setUpdaterError() { - if (_serial_output) Update.printError(Serial); - StreamString str; - Update.printError(str); - _updaterError = str.c_str(); + if (_serial_output) + { + Update.printError(Serial); + } + StreamString str; + Update.printError(str); + _updaterError = str.c_str(); } diff --git a/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.h b/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.h index a0faa46758..57b90b6d8b 100644 --- a/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.h +++ b/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.h @@ -5,36 +5,36 @@ class ESP8266WebServer; class ESP8266HTTPUpdateServer { - public: - ESP8266HTTPUpdateServer(bool serial_debug=false); +public: + ESP8266HTTPUpdateServer(bool serial_debug = false); void setup(ESP8266WebServer *server) { - setup(server, emptyString, emptyString); + setup(server, emptyString, emptyString); } void setup(ESP8266WebServer *server, const String& path) { - setup(server, path, emptyString, emptyString); + setup(server, path, emptyString, emptyString); } void setup(ESP8266WebServer *server, const String& username, const String& password) { - setup(server, "/update", username, password); + setup(server, "/update", username, password); } void setup(ESP8266WebServer *server, const String& path, const String& username, const String& password); void updateCredentials(const String& username, const String& password) { - _username = username; - _password = password; + _username = username; + _password = password; } - protected: +protected: void _setUpdaterError(); - private: +private: bool _serial_output; ESP8266WebServer *_server; String _username; diff --git a/libraries/ESP8266LLMNR/ESP8266LLMNR.cpp b/libraries/ESP8266LLMNR/ESP8266LLMNR.cpp index ac8582c579..3f086cb2f6 100644 --- a/libraries/ESP8266LLMNR/ESP8266LLMNR.cpp +++ b/libraries/ESP8266LLMNR/ESP8266LLMNR.cpp @@ -1,39 +1,39 @@ /* - * ESP8266 LLMNR responder - * Copyright (C) 2017 Stephen Warren - * - * Based on: - * ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) - * Version 1.1 - * Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) - * ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) - * MDNS-SD Suport 2015 Hristo Gochkov - * Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) - * - * License (MIT license): - * - * 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. - * - * Reference: - * https://tools.ietf.org/html/rfc4795 (LLMNR) - * https://tools.ietf.org/html/rfc1035 (DNS) - */ + ESP8266 LLMNR responder + Copyright (C) 2017 Stephen Warren + + Based on: + ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) + Version 1.1 + Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) + ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) + MDNS-SD Suport 2015 Hristo Gochkov + Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) + + License (MIT license): + + 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. + + Reference: + https://tools.ietf.org/html/rfc4795 (LLMNR) + https://tools.ietf.org/html/rfc1035 (DNS) +*/ #include #include @@ -72,28 +72,37 @@ static const int LLMNR_MULTICAST_TTL = 1; static const int LLMNR_PORT = 5355; LLMNRResponder::LLMNRResponder() : - _conn(0) { + _conn(0) +{ } -LLMNRResponder::~LLMNRResponder() { +LLMNRResponder::~LLMNRResponder() +{ if (_conn) + { _conn->unref(); + } } -bool LLMNRResponder::begin(const char* hostname) { +bool LLMNRResponder::begin(const char* hostname) +{ // Max length for a single label in DNS if (strlen(hostname) > 63) + { return false; + } _hostname = hostname; _hostname.toLowerCase(); - _sta_got_ip_handler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP& event){ + _sta_got_ip_handler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP & event) + { (void) event; _restart(); }); - _sta_disconnected_handler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected& event) { + _sta_disconnected_handler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected & event) + { (void) event; _restart(); }); @@ -101,12 +110,15 @@ bool LLMNRResponder::begin(const char* hostname) { return _restart(); } -void LLMNRResponder::notify_ap_change() { +void LLMNRResponder::notify_ap_change() +{ _restart(); } -bool LLMNRResponder::_restart() { - if (_conn) { +bool LLMNRResponder::_restart() +{ + if (_conn) + { _conn->unref(); _conn = 0; } @@ -114,13 +126,17 @@ bool LLMNRResponder::_restart() { IPAddress llmnr(LLMNR_MULTICAST_ADDR); if (igmp_joingroup(IP4_ADDR_ANY4, llmnr) != ERR_OK) + { return false; + } _conn = new UdpContext; _conn->ref(); if (!_conn->listen(IP_ADDR_ANY, LLMNR_PORT)) + { return false; + } _conn->setMulticastTTL(LLMNR_MULTICAST_TTL); _conn->onRx(std::bind(&LLMNRResponder::_process_packet, this)); @@ -128,9 +144,12 @@ bool LLMNRResponder::_restart() { return true; } -void LLMNRResponder::_process_packet() { +void LLMNRResponder::_process_packet() +{ if (!_conn || !_conn->next()) + { return; + } #ifdef LLMNR_DEBUG Serial.println("LLMNR: RX'd packet"); @@ -159,21 +178,24 @@ void LLMNRResponder::_process_packet() { #endif #define BAD_FLAGS (FLAGS_QR | (FLAGS_OP_MASK << FLAGS_OP_SHIFT) | FLAGS_C) - if (flags & BAD_FLAGS) { + if (flags & BAD_FLAGS) + { #ifdef LLMNR_DEBUG Serial.println("Bad flags"); #endif return; } - if (qdcount != 1) { + if (qdcount != 1) + { #ifdef LLMNR_DEBUG Serial.println("QDCOUNT != 1"); #endif return; } - if (ancount || nscount || arcount) { + if (ancount || nscount || arcount) + { #ifdef LLMNR_DEBUG Serial.println("AN/NS/AR-COUNT != 0"); #endif @@ -185,7 +207,8 @@ void LLMNRResponder::_process_packet() { Serial.print("QNAME len "); Serial.println(namelen); #endif - if (namelen != _hostname.length()) { + if (namelen != _hostname.length()) + { #ifdef LLMNR_DEBUG Serial.println("QNAME len mismatch"); #endif @@ -201,7 +224,8 @@ void LLMNRResponder::_process_packet() { Serial.println(qname); #endif - if (strcmp(_hostname.c_str(), qname)) { + if (strcmp(_hostname.c_str(), qname)) + { #ifdef LLMNR_DEBUG Serial.println("QNAME mismatch"); #endif @@ -227,26 +251,34 @@ void LLMNRResponder::_process_packet() { #ifdef LLMNR_DEBUG Serial.println("Match; responding"); if (!have_rr) + { Serial.println("(no matching RRs)"); + } #endif IPAddress remote_ip = _conn->getRemoteAddress(); struct ip_info ip_info; bool match_ap = false; - if (wifi_get_opmode() & SOFTAP_MODE) { + if (wifi_get_opmode() & SOFTAP_MODE) + { wifi_get_ip_info(SOFTAP_IF, &ip_info); IPAddress infoIp(ip_info.ip); IPAddress infoMask(ip_info.netmask); if (ip_info.ip.addr && ip_addr_netcmp((const ip_addr_t*)remote_ip, (const ip_addr_t*)infoIp, ip_2_ip4((const ip_addr_t*)infoMask))) + { match_ap = true; + } } if (!match_ap) + { wifi_get_ip_info(STATION_IF, &ip_info); + } uint32_t ip = ip_info.ip.addr; // Header - uint8_t header[] = { + uint8_t header[] = + { (uint8_t)(id >> 8), (uint8_t)(id & 0xff), // ID (uint8_t)(FLAGS_QR >> 8), 0, // FLAGS 0, 1, // QDCOUNT @@ -258,17 +290,20 @@ void LLMNRResponder::_process_packet() { // Question _conn->append(reinterpret_cast(&namelen), 1); _conn->append(qname, namelen); - uint8_t q[] = { + uint8_t q[] = + { 0, // Name terminator 0, 1, // TYPE (A) 0, 1, // CLASS (IN) }; _conn->append(reinterpret_cast(q), sizeof(q)); // Answer, if we have one - if (have_rr) { + if (have_rr) + { _conn->append(reinterpret_cast(&namelen), 1); _conn->append(qname, namelen); - uint8_t rr[] = { + uint8_t rr[] = + { 0, // Name terminator 0, 1, // TYPE (A) 0, 1, // CLASS (IN) diff --git a/libraries/ESP8266LLMNR/ESP8266LLMNR.h b/libraries/ESP8266LLMNR/ESP8266LLMNR.h index 4ad1d4a74d..246aaa6320 100644 --- a/libraries/ESP8266LLMNR/ESP8266LLMNR.h +++ b/libraries/ESP8266LLMNR/ESP8266LLMNR.h @@ -1,35 +1,35 @@ /* - * ESP8266 LLMNR responder - * Copyright (C) 2017 Stephen Warren - * - * Based on: - * ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) - * Version 1.1 - * Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) - * ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) - * MDNS-SD Suport 2015 Hristo Gochkov - * Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) - * - * License (MIT license): - * - * 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. - */ + ESP8266 LLMNR responder + Copyright (C) 2017 Stephen Warren + + Based on: + ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) + Version 1.1 + Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) + ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) + MDNS-SD Suport 2015 Hristo Gochkov + Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) + + License (MIT license): + + 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. +*/ #ifndef ESP8266LLMNR_H #define ESP8266LLMNR_H @@ -38,7 +38,8 @@ class UdpContext; -class LLMNRResponder { +class LLMNRResponder +{ public: LLMNRResponder(); ~LLMNRResponder(); diff --git a/libraries/ESP8266NetBIOS/ESP8266NetBIOS.cpp b/libraries/ESP8266NetBIOS/ESP8266NetBIOS.cpp index aedcfcdc7c..4a82c15efb 100644 --- a/libraries/ESP8266NetBIOS/ESP8266NetBIOS.cpp +++ b/libraries/ESP8266NetBIOS/ESP8266NetBIOS.cpp @@ -1,279 +1,302 @@ -/* Klient sluzby NBNS - */ - -#include "ESP8266NetBIOS.h" - -#include - -extern "C" { -#include "osapi.h" -#include "ets_sys.h" -#include "user_interface.h" -} - -#include "lwip/opt.h" -#include "lwip/inet.h" -#include "lwip/udp.h" - -#define NBNSQ_TYPE_NB (0x0020) -#define NBNSQ_CLASS_IN (0x0001) -#ifndef LWIP_PLATFORM_HTONS -#define LWIP_PLATFORM_HTONS(_n) ((u16_t)((((_n) & 0xff) << 8) | (((_n) >> 8) & 0xff))) -#endif -#ifndef LWIP_PLATFORM_HTONL -#define LWIP_PLATFORM_HTONL(_n) ((u32_t)( (((_n) & 0xff) << 24) | (((_n) & 0xff00) << 8) | (((_n) >> 8) & 0xff00) | (((_n) >> 24) & 0xff) )) -#endif - -// Definice struktury NBNS dotazu (alespon veci, ktere jsem vypozoroval): -struct NBNSQUESTION { - uint16_t NBNSQ_ID; // ID dotazu - uint8_t NBNSQ_FLAGS1; - uint8_t NBNSQ_FLAGS2; - uint16_t NBNSQ_QUESTIONCOUNT; - uint16_t NBNSQ_ANSWERCOUNT; - uint16_t NBNSQ_AUTHORITYCOUNT; - uint16_t NBNSQ_ADDITIONALRECORDCOUNT; - uint8_t NBNSQ_NAMESIZE; // delka nasledujiciho retezce - char NBNSQ_NAME[32+1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha - uint16_t NBNSQ_TYPE; - uint16_t NBNSQ_CLASS; -} __attribute__((packed)); - -// Definice struktury NBNS odpovedi (stejne jako u dotazu) -struct NBNSANSWER { - uint16_t NBNSA_ID; // ID dotazu - uint8_t NBNSA_FLAGS1; - uint8_t NBNSA_FLAGS2; - uint16_t NBNSA_QUESTIONCOUNT; - uint16_t NBNSA_ANSWERCOUNT; - uint16_t NBNSA_AUTHORITYCOUNT; - uint16_t NBNSA_ADDITIONALRECORDCOUNT; - uint8_t NBNSA_NAMESIZE; // delka nasledujiciho retezce - char NBNSA_NAME[32 + 1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha - uint16_t NBNSA_TYPE; - uint16_t NBNSA_CLASS; - uint32_t NBNSA_TIMETOLIVE; - uint16_t NBNSA_LENGTH; - uint16_t NBNSA_NODEFLAGS; // POZOR!!! tady si nejsem moc jisty - uint32_t NBNSA_NODEADDRESS; -} __attribute__((packed)); - -// Definice struktury NBNS odpovedi na dotaz na jmeno -struct NBNSANSWERN { - uint16_t NBNSAN_ID; // ID dotazu - uint8_t NBNSAN_FLAGS1; - uint8_t NBNSAN_FLAGS2; - uint16_t NBNSAN_QUESTIONCOUNT; - uint16_t NBNSAN_ANSWERCOUNT; - uint16_t NBNSAN_AUTHORITYCOUNT; - uint16_t NBNSAN_ADDITIONALRECORDCOUNT; - uint8_t NBNSAN_NAMESIZE; // delka nasledujiciho retezce - char NBNSAN_NAME[32 + 1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha - uint16_t NBNSAN_TYPE; - uint16_t NBNSAN_CLASS; - uint32_t NBNSAN_TIMETOLIVE; - uint16_t NBNSAN_LENGTH; - uint8_t NBNSAN_NUMBER; // number of names - char NBNSAN_NNAME[15]; // jmeno nodu - uint8_t NBNSAN_NTYPE; // typ jmena - uint16_t NBNSAN_NFLAGS; // node flags -} __attribute__((packed)); - -/** Metoda pro ziskani jmena z kodovani NETBIOS. - * \param nbname Ukazatel na jmeno v NETBIOS kodovani. - * \param name Ukazatel na misto, kam prevadime jmeno. - * \param maxlen Maximalni pocet znaku v nbname. - */ -void ESP8266NetBIOS::_getnbname(char *nbname, char *name, uint8_t maxlen) -{ - uint8_t b; - uint8_t c = 0; - - while ((*nbname != 0x0) && (c < maxlen)) { - b = (*nbname++ - 'A') << 4; // opravime nibble a prevedeme ho do vyssich bitu - c++; // pocitame pocet odebranych bytu - if (*nbname != 0x0) { - b |= *nbname++ - 'A'; // pridame nizsi nibble - c++; // opet spocitame pocet odebranych znaku - } - *name++ = b; // ulozime znak do vysledku a posuneme ukazatel - } - *name = 0x0; // ulozime ukoncovaci 0 -} - -/** Prevod zadaneho textu do NETBIOS kodovani - * \param name Ukazatel na prevadene jmeno. - * \param nbname Ukazatel na misto, kam vytvarime jmeno. - * \param outlen Pocet vystupnich znaku (mimo ukoncovaci 0) musi byt delitelne 2 - */ -void ESP8266NetBIOS::_makenbname(char *name, char *nbname, uint8_t outlen) -{ - uint8_t b; - uint8_t c = 0; - - while (c < (outlen - 2)) { - b = *name; // prevadeny znak - if (b) { - name++; // zatim se posunujeme - } else { - b = 0x20; // konec retezce je nahrazeny mezerou - } - *nbname++ = (b >> 4) + 'A'; // jeden nibble ze znaku - *nbname++ = (b & 0xf) + 'A'; // druhy nibble ze znaku - c += 2; // pocet prevedenych znaku - } - *nbname++ = 'A'; - *nbname++ = 'A'; // ulozime ukoncovaci 0 v NBNS kodovani - *nbname = 0; // ulozime ukoncovaci 0 retezce -} - -ESP8266NetBIOS::ESP8266NetBIOS():_pcb(NULL) -{ - -} -ESP8266NetBIOS::~ESP8266NetBIOS() -{ - end(); -} - -// Vytvoreni a otevreni UDP soketu, pokud jeste neni... -bool ESP8266NetBIOS::begin(const char *name) -{ - size_t n = strlen(name); - if (n > sizeof(_name)) { - // prilis dlouhe jmeno - return false; - } - - // presuneme jmeno zarizeni se soucasnou upravou na UPPER case - for (size_t i = 0; i < n; ++i) { - _name[i] = toupper(name[i]); - } - _name[n] = '\0'; - - if(_pcb != NULL) { - return true; - } - _pcb = udp_new(); - udp_recv(_pcb, &_s_recv, (void *) this); - err_t err = udp_bind(_pcb, INADDR_ANY, NBNS_PORT); - if(err != ERR_OK) { - end(); - return false; - } - return true; -} - -void ESP8266NetBIOS::end() -{ - if(_pcb != NULL) { - udp_remove(_pcb); - _pcb = NULL; - } -} - -void ESP8266NetBIOS::_recv(udp_pcb *upcb, pbuf *pb, CONST ip_addr_t *addr, uint16_t port) -{ - (void)upcb; - (void)addr; - (void)port; - while(pb != NULL) { - uint8_t * data = (uint8_t*)((pb)->payload); - size_t len = pb->len; +/* Klient sluzby NBNS +*/ + +#include "ESP8266NetBIOS.h" + +#include + +extern "C" { +#include "osapi.h" +#include "ets_sys.h" +#include "user_interface.h" +} + +#include "lwip/opt.h" +#include "lwip/inet.h" +#include "lwip/udp.h" + +#define NBNSQ_TYPE_NB (0x0020) +#define NBNSQ_CLASS_IN (0x0001) +#ifndef LWIP_PLATFORM_HTONS +#define LWIP_PLATFORM_HTONS(_n) ((u16_t)((((_n) & 0xff) << 8) | (((_n) >> 8) & 0xff))) +#endif +#ifndef LWIP_PLATFORM_HTONL +#define LWIP_PLATFORM_HTONL(_n) ((u32_t)( (((_n) & 0xff) << 24) | (((_n) & 0xff00) << 8) | (((_n) >> 8) & 0xff00) | (((_n) >> 24) & 0xff) )) +#endif + +// Definice struktury NBNS dotazu (alespon veci, ktere jsem vypozoroval): +struct NBNSQUESTION +{ + uint16_t NBNSQ_ID; // ID dotazu + uint8_t NBNSQ_FLAGS1; + uint8_t NBNSQ_FLAGS2; + uint16_t NBNSQ_QUESTIONCOUNT; + uint16_t NBNSQ_ANSWERCOUNT; + uint16_t NBNSQ_AUTHORITYCOUNT; + uint16_t NBNSQ_ADDITIONALRECORDCOUNT; + uint8_t NBNSQ_NAMESIZE; // delka nasledujiciho retezce + char NBNSQ_NAME[32 + 1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha + uint16_t NBNSQ_TYPE; + uint16_t NBNSQ_CLASS; +} __attribute__((packed)); + +// Definice struktury NBNS odpovedi (stejne jako u dotazu) +struct NBNSANSWER +{ + uint16_t NBNSA_ID; // ID dotazu + uint8_t NBNSA_FLAGS1; + uint8_t NBNSA_FLAGS2; + uint16_t NBNSA_QUESTIONCOUNT; + uint16_t NBNSA_ANSWERCOUNT; + uint16_t NBNSA_AUTHORITYCOUNT; + uint16_t NBNSA_ADDITIONALRECORDCOUNT; + uint8_t NBNSA_NAMESIZE; // delka nasledujiciho retezce + char NBNSA_NAME[32 + 1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha + uint16_t NBNSA_TYPE; + uint16_t NBNSA_CLASS; + uint32_t NBNSA_TIMETOLIVE; + uint16_t NBNSA_LENGTH; + uint16_t NBNSA_NODEFLAGS; // POZOR!!! tady si nejsem moc jisty + uint32_t NBNSA_NODEADDRESS; +} __attribute__((packed)); + +// Definice struktury NBNS odpovedi na dotaz na jmeno +struct NBNSANSWERN +{ + uint16_t NBNSAN_ID; // ID dotazu + uint8_t NBNSAN_FLAGS1; + uint8_t NBNSAN_FLAGS2; + uint16_t NBNSAN_QUESTIONCOUNT; + uint16_t NBNSAN_ANSWERCOUNT; + uint16_t NBNSAN_AUTHORITYCOUNT; + uint16_t NBNSAN_ADDITIONALRECORDCOUNT; + uint8_t NBNSAN_NAMESIZE; // delka nasledujiciho retezce + char NBNSAN_NAME[32 + 1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha + uint16_t NBNSAN_TYPE; + uint16_t NBNSAN_CLASS; + uint32_t NBNSAN_TIMETOLIVE; + uint16_t NBNSAN_LENGTH; + uint8_t NBNSAN_NUMBER; // number of names + char NBNSAN_NNAME[15]; // jmeno nodu + uint8_t NBNSAN_NTYPE; // typ jmena + uint16_t NBNSAN_NFLAGS; // node flags +} __attribute__((packed)); + +/** Metoda pro ziskani jmena z kodovani NETBIOS. + \param nbname Ukazatel na jmeno v NETBIOS kodovani. + \param name Ukazatel na misto, kam prevadime jmeno. + \param maxlen Maximalni pocet znaku v nbname. +*/ +void ESP8266NetBIOS::_getnbname(char *nbname, char *name, uint8_t maxlen) +{ + uint8_t b; + uint8_t c = 0; + + while ((*nbname != 0x0) && (c < maxlen)) + { + b = (*nbname++ - 'A') << 4; // opravime nibble a prevedeme ho do vyssich bitu + c++; // pocitame pocet odebranych bytu + if (*nbname != 0x0) + { + b |= *nbname++ - 'A'; // pridame nizsi nibble + c++; // opet spocitame pocet odebranych znaku + } + *name++ = b; // ulozime znak do vysledku a posuneme ukazatel + } + *name = 0x0; // ulozime ukoncovaci 0 +} + +/** Prevod zadaneho textu do NETBIOS kodovani + \param name Ukazatel na prevadene jmeno. + \param nbname Ukazatel na misto, kam vytvarime jmeno. + \param outlen Pocet vystupnich znaku (mimo ukoncovaci 0) musi byt delitelne 2 +*/ +void ESP8266NetBIOS::_makenbname(char *name, char *nbname, uint8_t outlen) +{ + uint8_t b; + uint8_t c = 0; + + while (c < (outlen - 2)) + { + b = *name; // prevadeny znak + if (b) + { + name++; // zatim se posunujeme + } + else + { + b = 0x20; // konec retezce je nahrazeny mezerou + } + *nbname++ = (b >> 4) + 'A'; // jeden nibble ze znaku + *nbname++ = (b & 0xf) + 'A'; // druhy nibble ze znaku + c += 2; // pocet prevedenych znaku + } + *nbname++ = 'A'; + *nbname++ = 'A'; // ulozime ukoncovaci 0 v NBNS kodovani + *nbname = 0; // ulozime ukoncovaci 0 retezce +} + +ESP8266NetBIOS::ESP8266NetBIOS(): _pcb(NULL) +{ + +} +ESP8266NetBIOS::~ESP8266NetBIOS() +{ + end(); +} + +// Vytvoreni a otevreni UDP soketu, pokud jeste neni... +bool ESP8266NetBIOS::begin(const char *name) +{ + size_t n = strlen(name); + if (n > sizeof(_name)) + { + // prilis dlouhe jmeno + return false; + } + + // presuneme jmeno zarizeni se soucasnou upravou na UPPER case + for (size_t i = 0; i < n; ++i) + { + _name[i] = toupper(name[i]); + } + _name[n] = '\0'; + + if (_pcb != NULL) + { + return true; + } + _pcb = udp_new(); + udp_recv(_pcb, &_s_recv, (void *) this); + err_t err = udp_bind(_pcb, INADDR_ANY, NBNS_PORT); + if (err != ERR_OK) + { + end(); + return false; + } + return true; +} + +void ESP8266NetBIOS::end() +{ + if (_pcb != NULL) + { + udp_remove(_pcb); + _pcb = NULL; + } +} + +void ESP8266NetBIOS::_recv(udp_pcb *upcb, pbuf *pb, CONST ip_addr_t *addr, uint16_t port) +{ + (void)upcb; + (void)addr; + (void)port; + while (pb != NULL) + { + uint8_t * data = (uint8_t*)((pb)->payload); + size_t len = pb->len; #if LWIP_VERSION_MAJOR == 1 - // check UdpContext.h + // check UdpContext.h ip_addr_t* saddr = ¤t_iphdr_src; #else - // check UdpContext.h + // check UdpContext.h const ip_addr_t* saddr = &ip_data.current_iphdr_src; #endif - - if (len >= sizeof(struct NBNSQUESTION)) { - struct NBNSQUESTION * question = (struct NBNSQUESTION *)data; - if (0 == (question->NBNSQ_FLAGS1 & 0x80)) { - char name[ NBNS_MAX_HOSTNAME_LEN + 1 ]; // dekodovane dotazovane jmeno - char *str; // pomocna promenna, pouze pro praci s retezcem - - _getnbname(&question->NBNSQ_NAME[0], (char *)&name, question->NBNSQ_NAMESIZE); // prevedeme dotazovane jmeno - if ((str = strchr(name, ' ')) != NULL) { // jmeno hledaneho zarizeni v tomto pripade ukoncuje i mezera - *str = '\0'; // ukoncime retezec na vyskytu prvni mezery - } - - if (0 == strcmp(name, _name)) { - // dotaz primo na nas - struct NBNSANSWER nbnsa; // buffer, do ktereho je sestavena odpoved na dotaz - - nbnsa.NBNSA_ID = question->NBNSQ_ID;// ID dotazu kopirujeme do ID odpovedi - nbnsa.NBNSA_FLAGS1 = 0x85; // priznak odpovedi - nbnsa.NBNSA_FLAGS2 = 0; // vlajky 2 a response code - nbnsa.NBNSA_QUESTIONCOUNT = LWIP_PLATFORM_HTONS(0); - nbnsa.NBNSA_ANSWERCOUNT = LWIP_PLATFORM_HTONS(1);// poradove cislo odpovedi - nbnsa.NBNSA_AUTHORITYCOUNT = LWIP_PLATFORM_HTONS(0); - nbnsa.NBNSA_ADDITIONALRECORDCOUNT = LWIP_PLATFORM_HTONS(0); - nbnsa.NBNSA_NAMESIZE = sizeof(nbnsa.NBNSA_NAME) - 1; // prekopirujeme delku jmena stanice - _makenbname(_name, &nbnsa.NBNSA_NAME[0], sizeof(nbnsa.NBNSA_NAME) - 1); // prevedeme jmeno - nbnsa.NBNSA_TYPE = LWIP_PLATFORM_HTONS(0x20); // NetBIOS name - nbnsa.NBNSA_CLASS = LWIP_PLATFORM_HTONS(1); // Internet name - nbnsa.NBNSA_TIMETOLIVE = LWIP_PLATFORM_HTONL(300000UL);// Time to live (30000 sekund) - nbnsa.NBNSA_LENGTH = LWIP_PLATFORM_HTONS(6); - nbnsa.NBNSA_NODEFLAGS = LWIP_PLATFORM_HTONS(0); - nbnsa.NBNSA_NODEADDRESS = WiFi.localIP(); // ulozime nasi IP adresu - - pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, sizeof(nbnsa), PBUF_RAM); - if(pbt != NULL) { - uint8_t* dst = reinterpret_cast(pbt->payload); - memcpy(dst, (uint8_t *)&nbnsa, sizeof(nbnsa)); - udp_sendto(_pcb, pbt, saddr, NBNS_PORT); - pbuf_free(pbt); - } - } else if (0 == strcmp(name, "*")) { - // obecny dotaz - mireny nejspis na nasi IP adresu - struct NBNSANSWERN nbnsan; // buffer, do ktereho je sestavena odpoved na dotaz - - nbnsan.NBNSAN_ID = question->NBNSQ_ID;// ID dotazu kopirujeme do ID odpovedi - nbnsan.NBNSAN_FLAGS1 = 0x84; // priznak odpovedi - nbnsan.NBNSAN_FLAGS2 = 0; // vlajky 2 a response code - nbnsan.NBNSAN_QUESTIONCOUNT = LWIP_PLATFORM_HTONS(0); - nbnsan.NBNSAN_ANSWERCOUNT = LWIP_PLATFORM_HTONS(1);// poradove cislo odpovedi - nbnsan.NBNSAN_AUTHORITYCOUNT = LWIP_PLATFORM_HTONS(0); - nbnsan.NBNSAN_ADDITIONALRECORDCOUNT = LWIP_PLATFORM_HTONS(0); - nbnsan.NBNSAN_NAMESIZE = question->NBNSQ_NAMESIZE; // prekopirujeme delku jmena stanice - memcpy(nbnsan.NBNSAN_NAME, question->NBNSQ_NAME, sizeof(nbnsan.NBNSAN_NAME)); // prekopirujeme dotazovane jmeno - nbnsan.NBNSAN_TYPE = LWIP_PLATFORM_HTONS(0x21); // NBSTAT - nbnsan.NBNSAN_CLASS = LWIP_PLATFORM_HTONS(1); // Internet name - nbnsan.NBNSAN_TIMETOLIVE = LWIP_PLATFORM_HTONL(0); - nbnsan.NBNSAN_LENGTH = LWIP_PLATFORM_HTONS(4 + sizeof(nbnsan.NBNSAN_NNAME)); - nbnsan.NBNSAN_NUMBER = 1; // Number of names - memset(nbnsan.NBNSAN_NNAME, 0x20, sizeof(nbnsan.NBNSAN_NNAME)); - memcpy(nbnsan.NBNSAN_NNAME, _name, strlen(_name)); - nbnsan.NBNSAN_NTYPE = 0; // Workstation/Redirector - nbnsan.NBNSAN_NFLAGS = LWIP_PLATFORM_HTONS(0x400); // b-node, unique, active - - pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, sizeof(nbnsan), PBUF_RAM); - if(pbt != NULL) { - uint8_t* dst = reinterpret_cast(pbt->payload); - memcpy(dst, (uint8_t *)&nbnsan, sizeof(nbnsan)); - udp_sendto(_pcb, pbt, saddr, NBNS_PORT); - pbuf_free(pbt); - } - } - } - } - - pbuf * this_pb = pb; - pb = pb->next; - this_pb->next = NULL; - pbuf_free(this_pb); - } -} - -void ESP8266NetBIOS::_s_recv(void *arg, udp_pcb *upcb, pbuf *p, CONST ip_addr_t *addr, uint16_t port) -{ - reinterpret_cast(arg)->_recv(upcb, p, addr, port); -} - -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_NETBIOS) -ESP8266NetBIOS NBNS; -#endif - -// EOF + + if (len >= sizeof(struct NBNSQUESTION)) + { + struct NBNSQUESTION * question = (struct NBNSQUESTION *)data; + if (0 == (question->NBNSQ_FLAGS1 & 0x80)) + { + char name[ NBNS_MAX_HOSTNAME_LEN + 1 ]; // dekodovane dotazovane jmeno + char *str; // pomocna promenna, pouze pro praci s retezcem + + _getnbname(&question->NBNSQ_NAME[0], (char *)&name, question->NBNSQ_NAMESIZE); // prevedeme dotazovane jmeno + if ((str = strchr(name, ' ')) != NULL) // jmeno hledaneho zarizeni v tomto pripade ukoncuje i mezera + { + *str = '\0'; // ukoncime retezec na vyskytu prvni mezery + } + + if (0 == strcmp(name, _name)) + { + // dotaz primo na nas + struct NBNSANSWER nbnsa; // buffer, do ktereho je sestavena odpoved na dotaz + + nbnsa.NBNSA_ID = question->NBNSQ_ID;// ID dotazu kopirujeme do ID odpovedi + nbnsa.NBNSA_FLAGS1 = 0x85; // priznak odpovedi + nbnsa.NBNSA_FLAGS2 = 0; // vlajky 2 a response code + nbnsa.NBNSA_QUESTIONCOUNT = LWIP_PLATFORM_HTONS(0); + nbnsa.NBNSA_ANSWERCOUNT = LWIP_PLATFORM_HTONS(1);// poradove cislo odpovedi + nbnsa.NBNSA_AUTHORITYCOUNT = LWIP_PLATFORM_HTONS(0); + nbnsa.NBNSA_ADDITIONALRECORDCOUNT = LWIP_PLATFORM_HTONS(0); + nbnsa.NBNSA_NAMESIZE = sizeof(nbnsa.NBNSA_NAME) - 1; // prekopirujeme delku jmena stanice + _makenbname(_name, &nbnsa.NBNSA_NAME[0], sizeof(nbnsa.NBNSA_NAME) - 1); // prevedeme jmeno + nbnsa.NBNSA_TYPE = LWIP_PLATFORM_HTONS(0x20); // NetBIOS name + nbnsa.NBNSA_CLASS = LWIP_PLATFORM_HTONS(1); // Internet name + nbnsa.NBNSA_TIMETOLIVE = LWIP_PLATFORM_HTONL(300000UL);// Time to live (30000 sekund) + nbnsa.NBNSA_LENGTH = LWIP_PLATFORM_HTONS(6); + nbnsa.NBNSA_NODEFLAGS = LWIP_PLATFORM_HTONS(0); + nbnsa.NBNSA_NODEADDRESS = WiFi.localIP(); // ulozime nasi IP adresu + + pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, sizeof(nbnsa), PBUF_RAM); + if (pbt != NULL) + { + uint8_t* dst = reinterpret_cast(pbt->payload); + memcpy(dst, (uint8_t *)&nbnsa, sizeof(nbnsa)); + udp_sendto(_pcb, pbt, saddr, NBNS_PORT); + pbuf_free(pbt); + } + } + else if (0 == strcmp(name, "*")) + { + // obecny dotaz - mireny nejspis na nasi IP adresu + struct NBNSANSWERN nbnsan; // buffer, do ktereho je sestavena odpoved na dotaz + + nbnsan.NBNSAN_ID = question->NBNSQ_ID;// ID dotazu kopirujeme do ID odpovedi + nbnsan.NBNSAN_FLAGS1 = 0x84; // priznak odpovedi + nbnsan.NBNSAN_FLAGS2 = 0; // vlajky 2 a response code + nbnsan.NBNSAN_QUESTIONCOUNT = LWIP_PLATFORM_HTONS(0); + nbnsan.NBNSAN_ANSWERCOUNT = LWIP_PLATFORM_HTONS(1);// poradove cislo odpovedi + nbnsan.NBNSAN_AUTHORITYCOUNT = LWIP_PLATFORM_HTONS(0); + nbnsan.NBNSAN_ADDITIONALRECORDCOUNT = LWIP_PLATFORM_HTONS(0); + nbnsan.NBNSAN_NAMESIZE = question->NBNSQ_NAMESIZE; // prekopirujeme delku jmena stanice + memcpy(nbnsan.NBNSAN_NAME, question->NBNSQ_NAME, sizeof(nbnsan.NBNSAN_NAME)); // prekopirujeme dotazovane jmeno + nbnsan.NBNSAN_TYPE = LWIP_PLATFORM_HTONS(0x21); // NBSTAT + nbnsan.NBNSAN_CLASS = LWIP_PLATFORM_HTONS(1); // Internet name + nbnsan.NBNSAN_TIMETOLIVE = LWIP_PLATFORM_HTONL(0); + nbnsan.NBNSAN_LENGTH = LWIP_PLATFORM_HTONS(4 + sizeof(nbnsan.NBNSAN_NNAME)); + nbnsan.NBNSAN_NUMBER = 1; // Number of names + memset(nbnsan.NBNSAN_NNAME, 0x20, sizeof(nbnsan.NBNSAN_NNAME)); + memcpy(nbnsan.NBNSAN_NNAME, _name, strlen(_name)); + nbnsan.NBNSAN_NTYPE = 0; // Workstation/Redirector + nbnsan.NBNSAN_NFLAGS = LWIP_PLATFORM_HTONS(0x400); // b-node, unique, active + + pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, sizeof(nbnsan), PBUF_RAM); + if (pbt != NULL) + { + uint8_t* dst = reinterpret_cast(pbt->payload); + memcpy(dst, (uint8_t *)&nbnsan, sizeof(nbnsan)); + udp_sendto(_pcb, pbt, saddr, NBNS_PORT); + pbuf_free(pbt); + } + } + } + } + + pbuf * this_pb = pb; + pb = pb->next; + this_pb->next = NULL; + pbuf_free(this_pb); + } +} + +void ESP8266NetBIOS::_s_recv(void *arg, udp_pcb *upcb, pbuf *p, CONST ip_addr_t *addr, uint16_t port) +{ + reinterpret_cast(arg)->_recv(upcb, p, addr, port); +} + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_NETBIOS) +ESP8266NetBIOS NBNS; +#endif + +// EOF diff --git a/libraries/ESP8266NetBIOS/ESP8266NetBIOS.h b/libraries/ESP8266NetBIOS/ESP8266NetBIOS.h index 1ce8f79ccb..55c2b62ab5 100644 --- a/libraries/ESP8266NetBIOS/ESP8266NetBIOS.h +++ b/libraries/ESP8266NetBIOS/ESP8266NetBIOS.h @@ -1,45 +1,45 @@ -// -#ifndef __ESPNBNS_h__ -#define __ESPNBNS_h__ - -extern "C" { -#include "lwip/init.h" // LWIP_VERSION_ -#include -} -#include - -#define NBNS_PORT 137 -/** -* @def NBNS_MAX_HOSTNAME_LEN -* @brief maximalni delka NBNS jmena zarizeni -* @remarks -* Jmeno zarizeni musi byt uvedeno VELKYMI pismenami a nesmi obsahovat mezery (whitespaces). -*/ -#define NBNS_MAX_HOSTNAME_LEN 16 - -struct udp_pcb; -struct pbuf; - -class ESP8266NetBIOS -{ -protected: - udp_pcb* _pcb; - char _name[NBNS_MAX_HOSTNAME_LEN + 1]; - void _getnbname(char *nbname, char *name, uint8_t maxlen); - void _makenbname(char *name, char *nbname, uint8_t outlen); - - void _recv(udp_pcb *upcb, pbuf *pb, CONST ip_addr_t *addr, uint16_t port); - static void _s_recv(void *arg, udp_pcb *upcb, pbuf *p, CONST ip_addr_t *addr, uint16_t port); - -public: - ESP8266NetBIOS(); - ~ESP8266NetBIOS(); - bool begin(const char *name); - void end(); -}; - -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_NETBIOS) -extern ESP8266NetBIOS NBNS; -#endif - -#endif +// +#ifndef __ESPNBNS_h__ +#define __ESPNBNS_h__ + +extern "C" { +#include "lwip/init.h" // LWIP_VERSION_ +#include +} +#include + +#define NBNS_PORT 137 +/** + @def NBNS_MAX_HOSTNAME_LEN + @brief maximalni delka NBNS jmena zarizeni + @remarks + Jmeno zarizeni musi byt uvedeno VELKYMI pismenami a nesmi obsahovat mezery (whitespaces). +*/ +#define NBNS_MAX_HOSTNAME_LEN 16 + +struct udp_pcb; +struct pbuf; + +class ESP8266NetBIOS +{ +protected: + udp_pcb* _pcb; + char _name[NBNS_MAX_HOSTNAME_LEN + 1]; + void _getnbname(char *nbname, char *name, uint8_t maxlen); + void _makenbname(char *name, char *nbname, uint8_t outlen); + + void _recv(udp_pcb *upcb, pbuf *pb, CONST ip_addr_t *addr, uint16_t port); + static void _s_recv(void *arg, udp_pcb *upcb, pbuf *p, CONST ip_addr_t *addr, uint16_t port); + +public: + ESP8266NetBIOS(); + ~ESP8266NetBIOS(); + bool begin(const char *name); + void end(); +}; + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_NETBIOS) +extern ESP8266NetBIOS NBNS; +#endif + +#endif diff --git a/libraries/ESP8266SSDP/ESP8266SSDP.cpp b/libraries/ESP8266SSDP/ESP8266SSDP.cpp index 900c2059fe..7f2fb8a820 100644 --- a/libraries/ESP8266SSDP/ESP8266SSDP.cpp +++ b/libraries/ESP8266SSDP/ESP8266SSDP.cpp @@ -1,28 +1,28 @@ /* -ESP8266 Simple Service Discovery -Copyright (c) 2015 Hristo Gochkov - -Original (Arduino) version by Filippo Sallemi, July 23, 2014. -Can be found at: https://github.com/nomadnt/uSSDP - -License (MIT license): - 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. + ESP8266 Simple Service Discovery + Copyright (c) 2015 Hristo Gochkov + + Original (Arduino) version by Filippo Sallemi, July 23, 2014. + Can be found at: https://github.com/nomadnt/uSSDP + + License (MIT license): + 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. */ #ifndef LWIP_OPEN_SRC @@ -59,467 +59,558 @@ extern "C" { #define SSDP_MULTICAST_ADDR 239, 255, 255, 250 static const char _ssdp_response_template[] PROGMEM = - "HTTP/1.1 200 OK\r\n" - "EXT:\r\n"; + "HTTP/1.1 200 OK\r\n" + "EXT:\r\n"; static const char _ssdp_notify_template[] PROGMEM = - "NOTIFY * HTTP/1.1\r\n" - "HOST: 239.255.255.250:1900\r\n" - "NTS: ssdp:alive\r\n"; + "NOTIFY * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "NTS: ssdp:alive\r\n"; static const char _ssdp_packet_template[] PROGMEM = - "%s" // _ssdp_response_template / _ssdp_notify_template - "CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL - "SERVER: Arduino/1.0 UPNP/1.1 %s/%s\r\n" // _modelName, _modelNumber - "USN: %s\r\n" // _uuid - "%s: %s\r\n" // "NT" or "ST", _deviceType - "LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL - "\r\n"; + "%s" // _ssdp_response_template / _ssdp_notify_template + "CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL + "SERVER: Arduino/1.0 UPNP/1.1 %s/%s\r\n" // _modelName, _modelNumber + "USN: %s\r\n" // _uuid + "%s: %s\r\n" // "NT" or "ST", _deviceType + "LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL + "\r\n"; static const char _ssdp_schema_template[] PROGMEM = - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/xml\r\n" - "Connection: close\r\n" - "Access-Control-Allow-Origin: *\r\n" - "\r\n" - "" - "" - "" - "1" - "0" - "" - "http://%u.%u.%u.%u:%u/" // WiFi.localIP(), _port - "" - "%s" - "%s" - "%s" - "%s" - "%s" - "%s" - "%s" - "%s" - "%s" - "%s" - "" - //"" - //"" - //"image/png" - //"48" - //"48" - //"24" - //"icon48.png" - //"" - //"" - //"image/png" - //"120" - //"120" - //"24" - //"icon120.png" - //"" - //"" - "\r\n" - "\r\n"; - - -struct SSDPTimer { - ETSTimer timer; + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/xml\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "\r\n" + "" + "" + "" + "1" + "0" + "" + "http://%u.%u.%u.%u:%u/" // WiFi.localIP(), _port + "" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "" + //"" + //"" + //"image/png" + //"48" + //"48" + //"24" + //"icon48.png" + //"" + //"" + //"image/png" + //"120" + //"120" + //"24" + //"icon120.png" + //"" + //"" + "\r\n" + "\r\n"; + + +struct SSDPTimer +{ + ETSTimer timer; }; SSDPClass::SSDPClass() : - _server(0), - _timer(0), - _port(80), - _ttl(SSDP_MULTICAST_TTL), - _respondToAddr(0,0,0,0), - _respondToPort(0), - _pending(false), - _st_is_uuid(false), - _delay(0), - _process_time(0), - _notify_time(0) + _server(0), + _timer(0), + _port(80), + _ttl(SSDP_MULTICAST_TTL), + _respondToAddr(0, 0, 0, 0), + _respondToPort(0), + _pending(false), + _st_is_uuid(false), + _delay(0), + _process_time(0), + _notify_time(0) { - _uuid[0] = '\0'; - _modelNumber[0] = '\0'; - sprintf(_deviceType, "urn:schemas-upnp-org:device:Basic:1"); - _friendlyName[0] = '\0'; - _presentationURL[0] = '\0'; - _serialNumber[0] = '\0'; - _modelName[0] = '\0'; - _modelURL[0] = '\0'; - _manufacturer[0] = '\0'; - _manufacturerURL[0] = '\0'; - sprintf(_schemaURL, "ssdp/schema.xml"); + _uuid[0] = '\0'; + _modelNumber[0] = '\0'; + sprintf(_deviceType, "urn:schemas-upnp-org:device:Basic:1"); + _friendlyName[0] = '\0'; + _presentationURL[0] = '\0'; + _serialNumber[0] = '\0'; + _modelName[0] = '\0'; + _modelURL[0] = '\0'; + _manufacturer[0] = '\0'; + _manufacturerURL[0] = '\0'; + sprintf(_schemaURL, "ssdp/schema.xml"); } -SSDPClass::~SSDPClass() { - end(); +SSDPClass::~SSDPClass() +{ + end(); } -bool SSDPClass::begin() { - end(); - - _pending = false; - _st_is_uuid = false; - if (strcmp(_uuid,"") == 0) { - uint32_t chipId = ESP.getChipId(); - sprintf_P(_uuid, PSTR("uuid:38323636-4558-4dda-9188-cda0e6%02x%02x%02x"), - (uint16_t) ((chipId >> 16) & 0xff), - (uint16_t) ((chipId >> 8) & 0xff), - (uint16_t) chipId & 0xff); - } - +bool SSDPClass::begin() +{ + end(); + + _pending = false; + _st_is_uuid = false; + if (strcmp(_uuid, "") == 0) + { + uint32_t chipId = ESP.getChipId(); + sprintf_P(_uuid, PSTR("uuid:38323636-4558-4dda-9188-cda0e6%02x%02x%02x"), + (uint16_t)((chipId >> 16) & 0xff), + (uint16_t)((chipId >> 8) & 0xff), + (uint16_t) chipId & 0xff); + } + #ifdef DEBUG_SSDP - DEBUG_SSDP.printf("SSDP UUID: %s\n", (char *)_uuid); + DEBUG_SSDP.printf("SSDP UUID: %s\n", (char *)_uuid); #endif - assert(NULL == _server); + assert(NULL == _server); - _server = new UdpContext; - _server->ref(); + _server = new UdpContext; + _server->ref(); - IPAddress local = WiFi.localIP(); - IPAddress mcast(SSDP_MULTICAST_ADDR); + IPAddress local = WiFi.localIP(); + IPAddress mcast(SSDP_MULTICAST_ADDR); - if (igmp_joingroup(local, mcast) != ERR_OK ) { + if (igmp_joingroup(local, mcast) != ERR_OK) + { #ifdef DEBUG_SSDP - DEBUG_SSDP.printf_P(PSTR("SSDP failed to join igmp group\n")); + DEBUG_SSDP.printf_P(PSTR("SSDP failed to join igmp group\n")); #endif - return false; - } + return false; + } - if (!_server->listen(IP_ADDR_ANY, SSDP_PORT)) { - return false; - } + if (!_server->listen(IP_ADDR_ANY, SSDP_PORT)) + { + return false; + } - _server->setMulticastInterface(local); - _server->setMulticastTTL(_ttl); - _server->onRx(std::bind(&SSDPClass::_update, this)); - if (!_server->connect(mcast, SSDP_PORT)) { - return false; - } + _server->setMulticastInterface(local); + _server->setMulticastTTL(_ttl); + _server->onRx(std::bind(&SSDPClass::_update, this)); + if (!_server->connect(mcast, SSDP_PORT)) + { + return false; + } - _startTimer(); + _startTimer(); - return true; + return true; } -void SSDPClass::end() { - if(!_server) - return; // object is zeroed already, nothing to do +void SSDPClass::end() +{ + if (!_server) + { + return; // object is zeroed already, nothing to do + } #ifdef DEBUG_SSDP DEBUG_SSDP.printf_P(PSTR("SSDP end ... ")); #endif - // undo all initializations done in begin(), in reverse order - _stopTimer(); + // undo all initializations done in begin(), in reverse order + _stopTimer(); - _server->disconnect(); + _server->disconnect(); - IPAddress local = WiFi.localIP(); - IPAddress mcast(SSDP_MULTICAST_ADDR); + IPAddress local = WiFi.localIP(); + IPAddress mcast(SSDP_MULTICAST_ADDR); - if (igmp_leavegroup(local, mcast) != ERR_OK ) { + if (igmp_leavegroup(local, mcast) != ERR_OK) + { #ifdef DEBUG_SSDP - DEBUG_SSDP.printf_P(PSTR("SSDP failed to leave igmp group\n")); + DEBUG_SSDP.printf_P(PSTR("SSDP failed to leave igmp group\n")); #endif - } + } - _server->unref(); - _server = 0; + _server->unref(); + _server = 0; #ifdef DEBUG_SSDP DEBUG_SSDP.printf_P(PSTR("ok\n")); #endif } -void SSDPClass::_send(ssdp_method_t method) { - char buffer[1460]; - IPAddress ip = WiFi.localIP(); - - char valueBuffer[strlen_P(_ssdp_notify_template) + 1]; - strcpy_P(valueBuffer, (method == NONE) ? _ssdp_response_template : _ssdp_notify_template); - - int len = snprintf_P(buffer, sizeof(buffer), - _ssdp_packet_template, - valueBuffer, - SSDP_INTERVAL, - _modelName, - _modelNumber, - _uuid, - (method == NONE) ? "ST" : "NT", - (_st_is_uuid) ? _uuid : _deviceType, - ip[0], ip[1], ip[2], ip[3], _port, _schemaURL - ); - - _server->append(buffer, len); - - IPAddress remoteAddr; - uint16_t remotePort; - if (method == NONE) { - remoteAddr = _respondToAddr; - remotePort = _respondToPort; +void SSDPClass::_send(ssdp_method_t method) +{ + char buffer[1460]; + IPAddress ip = WiFi.localIP(); + + char valueBuffer[strlen_P(_ssdp_notify_template) + 1]; + strcpy_P(valueBuffer, (method == NONE) ? _ssdp_response_template : _ssdp_notify_template); + + int len = snprintf_P(buffer, sizeof(buffer), + _ssdp_packet_template, + valueBuffer, + SSDP_INTERVAL, + _modelName, + _modelNumber, + _uuid, + (method == NONE) ? "ST" : "NT", + (_st_is_uuid) ? _uuid : _deviceType, + ip[0], ip[1], ip[2], ip[3], _port, _schemaURL + ); + + _server->append(buffer, len); + + IPAddress remoteAddr; + uint16_t remotePort; + if (method == NONE) + { + remoteAddr = _respondToAddr; + remotePort = _respondToPort; #ifdef DEBUG_SSDP - DEBUG_SSDP.print("Sending Response to "); + DEBUG_SSDP.print("Sending Response to "); #endif - } else { - remoteAddr = IPAddress(SSDP_MULTICAST_ADDR); - remotePort = SSDP_PORT; + } + else + { + remoteAddr = IPAddress(SSDP_MULTICAST_ADDR); + remotePort = SSDP_PORT; #ifdef DEBUG_SSDP - DEBUG_SSDP.println("Sending Notify to "); + DEBUG_SSDP.println("Sending Notify to "); #endif - } + } #ifdef DEBUG_SSDP - DEBUG_SSDP.print(IPAddress(remoteAddr)); - DEBUG_SSDP.print(":"); - DEBUG_SSDP.println(remotePort); + DEBUG_SSDP.print(IPAddress(remoteAddr)); + DEBUG_SSDP.print(":"); + DEBUG_SSDP.println(remotePort); #endif - _server->send(remoteAddr, remotePort); + _server->send(remoteAddr, remotePort); } -void SSDPClass::schema(WiFiClient client) { - IPAddress ip = WiFi.localIP(); - char buffer[strlen_P(_ssdp_schema_template) + 1]; - strcpy_P(buffer, _ssdp_schema_template); - client.printf(buffer, - ip[0], ip[1], ip[2], ip[3], _port, - _deviceType, - _friendlyName, - _presentationURL, - _serialNumber, - _modelName, - _modelNumber, - _modelURL, - _manufacturer, - _manufacturerURL, - _uuid - ); +void SSDPClass::schema(WiFiClient client) +{ + IPAddress ip = WiFi.localIP(); + char buffer[strlen_P(_ssdp_schema_template) + 1]; + strcpy_P(buffer, _ssdp_schema_template); + client.printf(buffer, + ip[0], ip[1], ip[2], ip[3], _port, + _deviceType, + _friendlyName, + _presentationURL, + _serialNumber, + _modelName, + _modelNumber, + _modelURL, + _manufacturer, + _manufacturerURL, + _uuid + ); } -void SSDPClass::_update() { - if (!_pending && _server->next()) { - ssdp_method_t method = NONE; - - _respondToAddr = _server->getRemoteAddress(); - _respondToPort = _server->getRemotePort(); - - typedef enum {METHOD, URI, PROTO, KEY, VALUE, ABORT} states; - states state = METHOD; - - typedef enum {START, MAN, ST, MX} headers; - headers header = START; - - uint8_t cursor = 0; - uint8_t cr = 0; - - char buffer[SSDP_BUFFER_SIZE] = {0}; - - while (_server->getSize() > 0) { - char c = _server->read(); - - (c == '\r' || c == '\n') ? cr++ : cr = 0; - - switch (state) { - case METHOD: - if (c == ' ') { - if (strcmp(buffer, "M-SEARCH") == 0) method = SEARCH; - - if (method == NONE) state = ABORT; - else state = URI; - cursor = 0; - - } else if (cursor < SSDP_METHOD_SIZE - 1) { - buffer[cursor++] = c; - buffer[cursor] = '\0'; - } - break; - case URI: - if (c == ' ') { - if (strcmp(buffer, "*")) state = ABORT; - else state = PROTO; - cursor = 0; - } else if (cursor < SSDP_URI_SIZE - 1) { - buffer[cursor++] = c; - buffer[cursor] = '\0'; - } - break; - case PROTO: - if (cr == 2) { - state = KEY; - cursor = 0; - } - break; - case KEY: - if (cr == 4) { - _pending = true; - _process_time = millis(); - } - else if (c == ' ') { - cursor = 0; - state = VALUE; - } - else if (c != '\r' && c != '\n' && c != ':' && cursor < SSDP_BUFFER_SIZE - 1) { - buffer[cursor++] = c; - buffer[cursor] = '\0'; - } - break; - case VALUE: - if (cr == 2) { - switch (header) { - case START: +void SSDPClass::_update() +{ + if (!_pending && _server->next()) + { + ssdp_method_t method = NONE; + + _respondToAddr = _server->getRemoteAddress(); + _respondToPort = _server->getRemotePort(); + + typedef enum {METHOD, URI, PROTO, KEY, VALUE, ABORT} states; + states state = METHOD; + + typedef enum {START, MAN, ST, MX} headers; + headers header = START; + + uint8_t cursor = 0; + uint8_t cr = 0; + + char buffer[SSDP_BUFFER_SIZE] = {0}; + + while (_server->getSize() > 0) + { + char c = _server->read(); + + (c == '\r' || c == '\n') ? cr++ : cr = 0; + + switch (state) + { + case METHOD: + if (c == ' ') + { + if (strcmp(buffer, "M-SEARCH") == 0) + { + method = SEARCH; + } + + if (method == NONE) + { + state = ABORT; + } + else + { + state = URI; + } + cursor = 0; + + } + else if (cursor < SSDP_METHOD_SIZE - 1) + { + buffer[cursor++] = c; + buffer[cursor] = '\0'; + } + break; + case URI: + if (c == ' ') + { + if (strcmp(buffer, "*")) + { + state = ABORT; + } + else + { + state = PROTO; + } + cursor = 0; + } + else if (cursor < SSDP_URI_SIZE - 1) + { + buffer[cursor++] = c; + buffer[cursor] = '\0'; + } + break; + case PROTO: + if (cr == 2) + { + state = KEY; + cursor = 0; + } + break; + case KEY: + if (cr == 4) + { + _pending = true; + _process_time = millis(); + } + else if (c == ' ') + { + cursor = 0; + state = VALUE; + } + else if (c != '\r' && c != '\n' && c != ':' && cursor < SSDP_BUFFER_SIZE - 1) + { + buffer[cursor++] = c; + buffer[cursor] = '\0'; + } break; - case MAN: + case VALUE: + if (cr == 2) + { + switch (header) + { + case START: + break; + case MAN: #ifdef DEBUG_SSDP - DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer); + DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer); #endif - break; - case ST: - if (strcmp(buffer, "ssdp:all")) { - state = ABORT; + break; + case ST: + if (strcmp(buffer, "ssdp:all")) + { + state = ABORT; #ifdef DEBUG_SSDP - DEBUG_SSDP.printf("REJECT: %s\n", (char *)buffer); + DEBUG_SSDP.printf("REJECT: %s\n", (char *)buffer); #endif - }else{ - _st_is_uuid = false; - } - // if the search type matches our type, we should respond instead of ABORT - if (strcasecmp(buffer, _deviceType) == 0) { - _pending = true; - _st_is_uuid = false; - _process_time = millis(); - state = KEY; + } + else + { + _st_is_uuid = false; + } + // if the search type matches our type, we should respond instead of ABORT + if (strcasecmp(buffer, _deviceType) == 0) + { + _pending = true; + _st_is_uuid = false; + _process_time = millis(); + state = KEY; + } + if (strcasecmp(buffer, _uuid) == 0) + { + _pending = true; + _st_is_uuid = true; + _process_time = millis(); + state = KEY; + } + break; + case MX: + _delay = random(0, atoi(buffer)) * 1000L; + break; + } + + if (state != ABORT) + { + state = KEY; + header = START; + cursor = 0; + } } - if (strcasecmp(buffer, _uuid) == 0) { - _pending = true; - _st_is_uuid = true; - _process_time = millis(); - state = KEY; + else if (c != '\r' && c != '\n') + { + if (header == START) + { + if (strncmp(buffer, "MA", 2) == 0) + { + header = MAN; + } + else if (strcmp(buffer, "ST") == 0) + { + header = ST; + } + else if (strcmp(buffer, "MX") == 0) + { + header = MX; + } + } + + if (cursor < SSDP_BUFFER_SIZE - 1) + { + buffer[cursor++] = c; + buffer[cursor] = '\0'; + } } break; - case MX: - _delay = random(0, atoi(buffer)) * 1000L; + case ABORT: + _pending = false; _delay = 0; break; } - - if (state != ABORT) { - state = KEY; - header = START; - cursor = 0; - } - } else if (c != '\r' && c != '\n') { - if (header == START) { - if (strncmp(buffer, "MA", 2) == 0) header = MAN; - else if (strcmp(buffer, "ST") == 0) header = ST; - else if (strcmp(buffer, "MX") == 0) header = MX; - } - - if (cursor < SSDP_BUFFER_SIZE - 1) { - buffer[cursor++] = c; - buffer[cursor] = '\0'; - } - } - break; - case ABORT: - _pending = false; _delay = 0; - break; - } + } } - } - if (_pending && (millis() - _process_time) > _delay) { - _pending = false; _delay = 0; - _send(NONE); - } else if(_notify_time == 0 || (millis() - _notify_time) > (SSDP_INTERVAL * 1000L)){ - _notify_time = millis(); - _st_is_uuid = false; - _send(NOTIFY); - } + if (_pending && (millis() - _process_time) > _delay) + { + _pending = false; _delay = 0; + _send(NONE); + } + else if (_notify_time == 0 || (millis() - _notify_time) > (SSDP_INTERVAL * 1000L)) + { + _notify_time = millis(); + _st_is_uuid = false; + _send(NOTIFY); + } - if (_pending) { - while (_server->next()) - _server->flush(); - } + if (_pending) + { + while (_server->next()) + { + _server->flush(); + } + } } -void SSDPClass::setSchemaURL(const char *url) { - strlcpy(_schemaURL, url, sizeof(_schemaURL)); +void SSDPClass::setSchemaURL(const char *url) +{ + strlcpy(_schemaURL, url, sizeof(_schemaURL)); } -void SSDPClass::setHTTPPort(uint16_t port) { - _port = port; +void SSDPClass::setHTTPPort(uint16_t port) +{ + _port = port; } -void SSDPClass::setDeviceType(const char *deviceType) { - strlcpy(_deviceType, deviceType, sizeof(_deviceType)); +void SSDPClass::setDeviceType(const char *deviceType) +{ + strlcpy(_deviceType, deviceType, sizeof(_deviceType)); } -void SSDPClass::setUUID(const char *uuid) { - snprintf_P(_uuid, sizeof(_uuid), PSTR("uuid:%s"), uuid); +void SSDPClass::setUUID(const char *uuid) +{ + snprintf_P(_uuid, sizeof(_uuid), PSTR("uuid:%s"), uuid); } -void SSDPClass::setName(const char *name) { - strlcpy(_friendlyName, name, sizeof(_friendlyName)); +void SSDPClass::setName(const char *name) +{ + strlcpy(_friendlyName, name, sizeof(_friendlyName)); } -void SSDPClass::setURL(const char *url) { - strlcpy(_presentationURL, url, sizeof(_presentationURL)); +void SSDPClass::setURL(const char *url) +{ + strlcpy(_presentationURL, url, sizeof(_presentationURL)); } -void SSDPClass::setSerialNumber(const char *serialNumber) { - strlcpy(_serialNumber, serialNumber, sizeof(_serialNumber)); +void SSDPClass::setSerialNumber(const char *serialNumber) +{ + strlcpy(_serialNumber, serialNumber, sizeof(_serialNumber)); } -void SSDPClass::setSerialNumber(const uint32_t serialNumber) { - snprintf(_serialNumber, sizeof(uint32_t) * 2 + 1, "%08X", serialNumber); +void SSDPClass::setSerialNumber(const uint32_t serialNumber) +{ + snprintf(_serialNumber, sizeof(uint32_t) * 2 + 1, "%08X", serialNumber); } -void SSDPClass::setModelName(const char *name) { - strlcpy(_modelName, name, sizeof(_modelName)); +void SSDPClass::setModelName(const char *name) +{ + strlcpy(_modelName, name, sizeof(_modelName)); } -void SSDPClass::setModelNumber(const char *num) { - strlcpy(_modelNumber, num, sizeof(_modelNumber)); +void SSDPClass::setModelNumber(const char *num) +{ + strlcpy(_modelNumber, num, sizeof(_modelNumber)); } -void SSDPClass::setModelURL(const char *url) { - strlcpy(_modelURL, url, sizeof(_modelURL)); +void SSDPClass::setModelURL(const char *url) +{ + strlcpy(_modelURL, url, sizeof(_modelURL)); } -void SSDPClass::setManufacturer(const char *name) { - strlcpy(_manufacturer, name, sizeof(_manufacturer)); +void SSDPClass::setManufacturer(const char *name) +{ + strlcpy(_manufacturer, name, sizeof(_manufacturer)); } -void SSDPClass::setManufacturerURL(const char *url) { - strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL)); +void SSDPClass::setManufacturerURL(const char *url) +{ + strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL)); } -void SSDPClass::setTTL(const uint8_t ttl) { - _ttl = ttl; +void SSDPClass::setTTL(const uint8_t ttl) +{ + _ttl = ttl; } -void SSDPClass::_onTimerStatic(SSDPClass* self) { - self->_update(); +void SSDPClass::_onTimerStatic(SSDPClass* self) +{ + self->_update(); } -void SSDPClass::_startTimer() { - _stopTimer(); - _timer = new SSDPTimer(); - ETSTimer* tm = &(_timer->timer); - const int interval = 1000; - os_timer_disarm(tm); - os_timer_setfn(tm, reinterpret_cast(&SSDPClass::_onTimerStatic), reinterpret_cast(this)); - os_timer_arm(tm, interval, 1 /* repeat */); +void SSDPClass::_startTimer() +{ + _stopTimer(); + _timer = new SSDPTimer(); + ETSTimer* tm = &(_timer->timer); + const int interval = 1000; + os_timer_disarm(tm); + os_timer_setfn(tm, reinterpret_cast(&SSDPClass::_onTimerStatic), reinterpret_cast(this)); + os_timer_arm(tm, interval, 1 /* repeat */); } -void SSDPClass::_stopTimer() { - if(!_timer) - return; +void SSDPClass::_stopTimer() +{ + if (!_timer) + { + return; + } - ETSTimer* tm = &(_timer->timer); - os_timer_disarm(tm); - delete _timer; - _timer = NULL; + ETSTimer* tm = &(_timer->timer); + os_timer_disarm(tm); + delete _timer; + _timer = NULL; } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP) diff --git a/libraries/ESP8266SSDP/ESP8266SSDP.h b/libraries/ESP8266SSDP/ESP8266SSDP.h index 5fbbf59364..f4ce5d1975 100644 --- a/libraries/ESP8266SSDP/ESP8266SSDP.h +++ b/libraries/ESP8266SSDP/ESP8266SSDP.h @@ -1,28 +1,28 @@ /* -ESP8266 Simple Service Discovery -Copyright (c) 2015 Hristo Gochkov - -Original (Arduino) version by Filippo Sallemi, July 23, 2014. -Can be found at: https://github.com/nomadnt/uSSDP - -License (MIT license): - 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. + ESP8266 Simple Service Discovery + Copyright (c) 2015 Hristo Gochkov + + Original (Arduino) version by Filippo Sallemi, July 23, 2014. + Can be found at: https://github.com/nomadnt/uSSDP + + License (MIT license): + 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. */ @@ -47,52 +47,87 @@ class UdpContext; #define SSDP_MANUFACTURER_SIZE 64 #define SSDP_MANUFACTURER_URL_SIZE 128 -typedef enum { - NONE, - SEARCH, - NOTIFY +typedef enum +{ + NONE, + SEARCH, + NOTIFY } ssdp_method_t; struct SSDPTimer; -class SSDPClass{ - public: +class SSDPClass +{ +public: SSDPClass(); ~SSDPClass(); bool begin(); void end(); void schema(WiFiClient client); - void setDeviceType(const String& deviceType) { setDeviceType(deviceType.c_str()); } + void setDeviceType(const String& deviceType) + { + setDeviceType(deviceType.c_str()); + } void setDeviceType(const char *deviceType); - + /*To define a custom UUID, you must call the method before begin(). Otherwise an automatic UUID based on CHIPID will be generated.*/ - void setUUID(const String& uuid) { setUUID(uuid.c_str()); } + void setUUID(const String& uuid) + { + setUUID(uuid.c_str()); + } void setUUID(const char *uuid); - - void setName(const String& name) { setName(name.c_str()); } + + void setName(const String& name) + { + setName(name.c_str()); + } void setName(const char *name); - void setURL(const String& url) { setURL(url.c_str()); } + void setURL(const String& url) + { + setURL(url.c_str()); + } void setURL(const char *url); - void setSchemaURL(const String& url) { setSchemaURL(url.c_str()); } + void setSchemaURL(const String& url) + { + setSchemaURL(url.c_str()); + } void setSchemaURL(const char *url); - void setSerialNumber(const String& serialNumber) { setSerialNumber(serialNumber.c_str()); } + void setSerialNumber(const String& serialNumber) + { + setSerialNumber(serialNumber.c_str()); + } void setSerialNumber(const char *serialNumber); void setSerialNumber(const uint32_t serialNumber); - void setModelName(const String& name) { setModelName(name.c_str()); } + void setModelName(const String& name) + { + setModelName(name.c_str()); + } void setModelName(const char *name); - void setModelNumber(const String& num) { setModelNumber(num.c_str()); } + void setModelNumber(const String& num) + { + setModelNumber(num.c_str()); + } void setModelNumber(const char *num); - void setModelURL(const String& url) { setModelURL(url.c_str()); } + void setModelURL(const String& url) + { + setModelURL(url.c_str()); + } void setModelURL(const char *url); - void setManufacturer(const String& name) { setManufacturer(name.c_str()); } + void setManufacturer(const String& name) + { + setManufacturer(name.c_str()); + } void setManufacturer(const char *name); - void setManufacturerURL(const String& url) { setManufacturerURL(url.c_str()); } + void setManufacturerURL(const String& url) + { + setManufacturerURL(url.c_str()); + } void setManufacturerURL(const char *url); void setHTTPPort(uint16_t port); void setTTL(uint8_t ttl); - protected: +protected: void _send(ssdp_method_t method); void _update(); void _startTimer(); diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index 2aac608083..5e16b22a1e 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -1,23 +1,23 @@ /* - ESP8266WebServer.cpp - Dead simple web-server. - Supports only one simultaneous client, knows how to handle GET and POST. + ESP8266WebServer.cpp - Dead simple web-server. + Supports only one simultaneous client, knows how to handle GET and POST. - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) */ @@ -44,330 +44,404 @@ static const char Content_Length[] PROGMEM = "Content-Length"; ESP8266WebServer::ESP8266WebServer(IPAddress addr, int port) -: _server(addr, port) -, _currentMethod(HTTP_ANY) -, _currentVersion(0) -, _currentStatus(HC_NONE) -, _statusChange(0) -, _currentHandler(nullptr) -, _firstHandler(nullptr) -, _lastHandler(nullptr) -, _currentArgCount(0) -, _currentArgs(nullptr) -, _postArgsLen(0) -, _postArgs(nullptr) -, _headerKeysCount(0) -, _currentHeaders(nullptr) -, _contentLength(0) -, _chunked(false) + : _server(addr, port) + , _currentMethod(HTTP_ANY) + , _currentVersion(0) + , _currentStatus(HC_NONE) + , _statusChange(0) + , _currentHandler(nullptr) + , _firstHandler(nullptr) + , _lastHandler(nullptr) + , _currentArgCount(0) + , _currentArgs(nullptr) + , _postArgsLen(0) + , _postArgs(nullptr) + , _headerKeysCount(0) + , _currentHeaders(nullptr) + , _contentLength(0) + , _chunked(false) { } ESP8266WebServer::ESP8266WebServer(int port) -: _server(port) -, _currentMethod(HTTP_ANY) -, _currentVersion(0) -, _currentStatus(HC_NONE) -, _statusChange(0) -, _currentHandler(nullptr) -, _firstHandler(nullptr) -, _lastHandler(nullptr) -, _currentArgCount(0) -, _currentArgs(nullptr) -, _postArgsLen(0) -, _postArgs(nullptr) -, _headerKeysCount(0) -, _currentHeaders(nullptr) -, _contentLength(0) -, _chunked(false) -{ -} - -ESP8266WebServer::~ESP8266WebServer() { - _server.close(); - if (_currentHeaders) - delete[]_currentHeaders; - RequestHandler* handler = _firstHandler; - while (handler) { - RequestHandler* next = handler->next(); - delete handler; - handler = next; - } -} - -void ESP8266WebServer::begin() { - close(); - _server.begin(); -} - -void ESP8266WebServer::begin(uint16_t port) { - close(); - _server.begin(port); -} - -String ESP8266WebServer::_extractParam(String& authReq,const String& param,const char delimit) const { - int _begin = authReq.indexOf(param); - if (_begin == -1) - return emptyString; - return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length())); -} - -bool ESP8266WebServer::authenticate(const char * username, const char * password){ - if(hasHeader(FPSTR(AUTHORIZATION_HEADER))) { - String authReq = header(FPSTR(AUTHORIZATION_HEADER)); - if(authReq.startsWith(F("Basic"))){ - authReq = authReq.substring(6); - authReq.trim(); - char toencodeLen = strlen(username)+strlen(password)+1; - char *toencode = new char[toencodeLen + 1]; - if(toencode == NULL){ - authReq = ""; - return false; - } - char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; - if(encoded == NULL){ - authReq = ""; - delete[] toencode; - return false; - } - sprintf(toencode, "%s:%s", username, password); - if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) { - authReq = ""; - delete[] toencode; - delete[] encoded; - return true; - } - delete[] toencode; - delete[] encoded; - } else if(authReq.startsWith(F("Digest"))) { - authReq = authReq.substring(7); - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println(authReq); - #endif - String _username = _extractParam(authReq,F("username=\"")); - if(!_username.length() || _username != String(username)) { - authReq = ""; - return false; - } - // extracting required parameters for RFC 2069 simpler Digest - String _realm = _extractParam(authReq, F("realm=\"")); - String _nonce = _extractParam(authReq, F("nonce=\"")); - String _uri = _extractParam(authReq, F("uri=\"")); - String _response = _extractParam(authReq, F("response=\"")); - String _opaque = _extractParam(authReq, F("opaque=\"")); - - if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) { - authReq = ""; - return false; - } - if((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) { - authReq = ""; - return false; - } - // parameters for the RFC 2617 newer Digest - String _nc,_cnonce; - if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) { - _nc = _extractParam(authReq, F("nc="), ','); - _cnonce = _extractParam(authReq, F("cnonce=\"")); - } - MD5Builder md5; - md5.begin(); - md5.add(String(username) + ':' + _realm + ':' + String(password)); // md5 of the user:realm:user - md5.calculate(); - String _H1 = md5.toString(); - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Hash of user:realm:pass=" + _H1); - #endif - md5.begin(); - if(_currentMethod == HTTP_GET){ - md5.add(String(F("GET:")) + _uri); - }else if(_currentMethod == HTTP_POST){ - md5.add(String(F("POST:")) + _uri); - }else if(_currentMethod == HTTP_PUT){ - md5.add(String(F("PUT:")) + _uri); - }else if(_currentMethod == HTTP_DELETE){ - md5.add(String(F("DELETE:")) + _uri); - }else{ - md5.add(String(F("GET:")) + _uri); - } - md5.calculate(); - String _H2 = md5.toString(); - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Hash of GET:uri=" + _H2); - #endif - md5.begin(); - if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) { - md5.add(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); - } else { - md5.add(_H1 + ':' + _nonce + ':' + _H2); - } - md5.calculate(); - String _responsecheck = md5.toString(); - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("The Proper response=" +_responsecheck); - #endif - if(_response == _responsecheck){ + : _server(port) + , _currentMethod(HTTP_ANY) + , _currentVersion(0) + , _currentStatus(HC_NONE) + , _statusChange(0) + , _currentHandler(nullptr) + , _firstHandler(nullptr) + , _lastHandler(nullptr) + , _currentArgCount(0) + , _currentArgs(nullptr) + , _postArgsLen(0) + , _postArgs(nullptr) + , _headerKeysCount(0) + , _currentHeaders(nullptr) + , _contentLength(0) + , _chunked(false) +{ +} + +ESP8266WebServer::~ESP8266WebServer() +{ + _server.close(); + if (_currentHeaders) + { + delete[]_currentHeaders; + } + RequestHandler* handler = _firstHandler; + while (handler) + { + RequestHandler* next = handler->next(); + delete handler; + handler = next; + } +} + +void ESP8266WebServer::begin() +{ + close(); + _server.begin(); +} + +void ESP8266WebServer::begin(uint16_t port) +{ + close(); + _server.begin(port); +} + +String ESP8266WebServer::_extractParam(String& authReq, const String& param, const char delimit) const +{ + int _begin = authReq.indexOf(param); + if (_begin == -1) + { + return emptyString; + } + return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length())); +} + +bool ESP8266WebServer::authenticate(const char * username, const char * password) +{ + if (hasHeader(FPSTR(AUTHORIZATION_HEADER))) + { + String authReq = header(FPSTR(AUTHORIZATION_HEADER)); + if (authReq.startsWith(F("Basic"))) + { + authReq = authReq.substring(6); + authReq.trim(); + char toencodeLen = strlen(username) + strlen(password) + 1; + char *toencode = new char[toencodeLen + 1]; + if (toencode == NULL) + { + authReq = ""; + return false; + } + char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; + if (encoded == NULL) + { + authReq = ""; + delete[] toencode; + return false; + } + sprintf(toencode, "%s:%s", username, password); + if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) + { + authReq = ""; + delete[] toencode; + delete[] encoded; + return true; + } + delete[] toencode; + delete[] encoded; + } + else if (authReq.startsWith(F("Digest"))) + { + authReq = authReq.substring(7); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println(authReq); +#endif + String _username = _extractParam(authReq, F("username=\"")); + if (!_username.length() || _username != String(username)) + { + authReq = ""; + return false; + } + // extracting required parameters for RFC 2069 simpler Digest + String _realm = _extractParam(authReq, F("realm=\"")); + String _nonce = _extractParam(authReq, F("nonce=\"")); + String _uri = _extractParam(authReq, F("uri=\"")); + String _response = _extractParam(authReq, F("response=\"")); + String _opaque = _extractParam(authReq, F("opaque=\"")); + + if ((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) + { + authReq = ""; + return false; + } + if ((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) + { + authReq = ""; + return false; + } + // parameters for the RFC 2617 newer Digest + String _nc, _cnonce; + if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) + { + _nc = _extractParam(authReq, F("nc="), ','); + _cnonce = _extractParam(authReq, F("cnonce=\"")); + } + MD5Builder md5; + md5.begin(); + md5.add(String(username) + ':' + _realm + ':' + String(password)); // md5 of the user:realm:user + md5.calculate(); + String _H1 = md5.toString(); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("Hash of user:realm:pass=" + _H1); +#endif + md5.begin(); + if (_currentMethod == HTTP_GET) + { + md5.add(String(F("GET:")) + _uri); + } + else if (_currentMethod == HTTP_POST) + { + md5.add(String(F("POST:")) + _uri); + } + else if (_currentMethod == HTTP_PUT) + { + md5.add(String(F("PUT:")) + _uri); + } + else if (_currentMethod == HTTP_DELETE) + { + md5.add(String(F("DELETE:")) + _uri); + } + else + { + md5.add(String(F("GET:")) + _uri); + } + md5.calculate(); + String _H2 = md5.toString(); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("Hash of GET:uri=" + _H2); +#endif + md5.begin(); + if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) + { + md5.add(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); + } + else + { + md5.add(_H1 + ':' + _nonce + ':' + _H2); + } + md5.calculate(); + String _responsecheck = md5.toString(); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("The Proper response=" + _responsecheck); +#endif + if (_response == _responsecheck) + { + authReq = ""; + return true; + } + } authReq = ""; - return true; - } } - authReq = ""; - } - return false; + return false; } -String ESP8266WebServer::_getRandomHexString() { - char buffer[33]; // buffer to hold 32 Hex Digit + /0 - int i; - for(i = 0; i < 4; i++) { - sprintf (buffer + (i*8), "%08x", RANDOM_REG32); - } - return String(buffer); +String ESP8266WebServer::_getRandomHexString() +{ + char buffer[33]; // buffer to hold 32 Hex Digit + /0 + int i; + for (i = 0; i < 4; i++) + { + sprintf(buffer + (i * 8), "%08x", RANDOM_REG32); + } + return String(buffer); } -void ESP8266WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, const String& authFailMsg) { - if(realm == NULL) { - _srealm = String(F("Login Required")); - } else { - _srealm = String(realm); - } - if(mode == BASIC_AUTH) { - sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\""))); - } else { - _snonce=_getRandomHexString(); - _sopaque=_getRandomHexString(); - sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\""))); - } - using namespace mime; - send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); +void ESP8266WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, const String& authFailMsg) +{ + if (realm == NULL) + { + _srealm = String(F("Login Required")); + } + else + { + _srealm = String(realm); + } + if (mode == BASIC_AUTH) + { + sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\""))); + } + else + { + _snonce = _getRandomHexString(); + _sopaque = _getRandomHexString(); + sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) + _srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\""))); + } + using namespace mime; + send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); } -void ESP8266WebServer::on(const String &uri, ESP8266WebServer::THandlerFunction handler) { - on(uri, HTTP_ANY, handler); +void ESP8266WebServer::on(const String &uri, ESP8266WebServer::THandlerFunction handler) +{ + on(uri, HTTP_ANY, handler); } -void ESP8266WebServer::on(const String &uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) { - on(uri, method, fn, _fileUploadHandler); +void ESP8266WebServer::on(const String &uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) +{ + on(uri, method, fn, _fileUploadHandler); } -void ESP8266WebServer::on(const String &uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn, ESP8266WebServer::THandlerFunction ufn) { - _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); +void ESP8266WebServer::on(const String &uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn, ESP8266WebServer::THandlerFunction ufn) +{ + _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); } -void ESP8266WebServer::addHandler(RequestHandler* handler) { +void ESP8266WebServer::addHandler(RequestHandler* handler) +{ _addRequestHandler(handler); } -void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) { - if (!_lastHandler) { - _firstHandler = handler; - _lastHandler = handler; +void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) +{ + if (!_lastHandler) + { + _firstHandler = handler; + _lastHandler = handler; } - else { - _lastHandler->next(handler); - _lastHandler = handler; + else + { + _lastHandler->next(handler); + _lastHandler = handler; } } -void ESP8266WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) { +void ESP8266WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) +{ _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); } -void ESP8266WebServer::handleClient() { - if (_currentStatus == HC_NONE) { - WiFiClient client = _server.available(); - if (!client) { - return; - } +void ESP8266WebServer::handleClient() +{ + if (_currentStatus == HC_NONE) + { + WiFiClient client = _server.available(); + if (!client) + { + return; + } #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("New client"); + DEBUG_OUTPUT.println("New client"); #endif - _currentClient = client; - _currentStatus = HC_WAIT_READ; - _statusChange = millis(); - } - - bool keepCurrentClient = false; - bool callYield = false; - - if (_currentClient.connected()) { - switch (_currentStatus) { - case HC_NONE: - // No-op to avoid C++ compiler warning - break; - case HC_WAIT_READ: - // Wait for data from client to become available - if (_currentClient.available()) { - if (_parseRequest(_currentClient)) { - _currentClient.setTimeout(HTTP_MAX_SEND_WAIT); - _contentLength = CONTENT_LENGTH_NOT_SET; - _handleRequest(); - - if (_currentClient.connected()) { - _currentStatus = HC_WAIT_CLOSE; - _statusChange = millis(); - keepCurrentClient = true; - } - } - } else { // !_currentClient.available() - if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { - keepCurrentClient = true; + _currentClient = client; + _currentStatus = HC_WAIT_READ; + _statusChange = millis(); + } + + bool keepCurrentClient = false; + bool callYield = false; + + if (_currentClient.connected()) + { + switch (_currentStatus) + { + case HC_NONE: + // No-op to avoid C++ compiler warning + break; + case HC_WAIT_READ: + // Wait for data from client to become available + if (_currentClient.available()) + { + if (_parseRequest(_currentClient)) + { + _currentClient.setTimeout(HTTP_MAX_SEND_WAIT); + _contentLength = CONTENT_LENGTH_NOT_SET; + _handleRequest(); + + if (_currentClient.connected()) + { + _currentStatus = HC_WAIT_CLOSE; + _statusChange = millis(); + keepCurrentClient = true; + } + } + } + else // !_currentClient.available() + { + if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) + { + keepCurrentClient = true; + } + callYield = true; + } + break; + case HC_WAIT_CLOSE: + // Wait for client to close the connection + if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) + { + keepCurrentClient = true; + callYield = true; + } } - callYield = true; - } - break; - case HC_WAIT_CLOSE: - // Wait for client to close the connection - if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) { - keepCurrentClient = true; - callYield = true; - } - } - } - - if (!keepCurrentClient) { - _currentClient = WiFiClient(); - _currentStatus = HC_NONE; - _currentUpload.reset(); - } + } - if (callYield) { - yield(); - } -} + if (!keepCurrentClient) + { + _currentClient = WiFiClient(); + _currentStatus = HC_NONE; + _currentUpload.reset(); + } -void ESP8266WebServer::close() { - _server.close(); - _currentStatus = HC_NONE; - if(!_headerKeysCount) - collectHeaders(0, 0); + if (callYield) + { + yield(); + } } -void ESP8266WebServer::stop() { - close(); +void ESP8266WebServer::close() +{ + _server.close(); + _currentStatus = HC_NONE; + if (!_headerKeysCount) + { + collectHeaders(0, 0); + } } -void ESP8266WebServer::sendHeader(const String& name, const String& value, bool first) { - String headerLine = name; - headerLine += F(": "); - headerLine += value; - headerLine += "\r\n"; +void ESP8266WebServer::stop() +{ + close(); +} - if (first) { - _responseHeaders = headerLine + _responseHeaders; - } - else { - _responseHeaders += headerLine; - } +void ESP8266WebServer::sendHeader(const String& name, const String& value, bool first) +{ + String headerLine = name; + headerLine += F(": "); + headerLine += value; + headerLine += "\r\n"; + + if (first) + { + _responseHeaders = headerLine + _responseHeaders; + } + else + { + _responseHeaders += headerLine; + } } -void ESP8266WebServer::setContentLength(const size_t contentLength) { +void ESP8266WebServer::setContentLength(const size_t contentLength) +{ _contentLength = contentLength; } -void ESP8266WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) { +void ESP8266WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) +{ response = String(F("HTTP/1.")) + String(_currentVersion) + ' '; response += String(code); response += ' '; @@ -376,18 +450,25 @@ void ESP8266WebServer::_prepareHeader(String& response, int code, const char* co using namespace mime; if (!content_type) + { content_type = mimeTable[html].mimeType; + } sendHeader(String(F("Content-Type")), String(FPSTR(content_type)), true); - if (_contentLength == CONTENT_LENGTH_NOT_SET) { + if (_contentLength == CONTENT_LENGTH_NOT_SET) + { sendHeader(String(FPSTR(Content_Length)), String(contentLength)); - } else if (_contentLength != CONTENT_LENGTH_UNKNOWN) { + } + else if (_contentLength != CONTENT_LENGTH_UNKNOWN) + { sendHeader(String(FPSTR(Content_Length)), String(_contentLength)); - } else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client - //let's do chunked - _chunked = true; - sendHeader(String(F("Accept-Ranges")),String(F("none"))); - sendHeader(String(F("Transfer-Encoding")),String(F("chunked"))); + } + else if (_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion) //HTTP/1.1 or above client + { + //let's do chunked + _chunked = true; + sendHeader(String(F("Accept-Ranges")), String(F("none"))); + sendHeader(String(F("Transfer-Encoding")), String(F("chunked"))); } sendHeader(String(F("Connection")), String(F("close"))); @@ -396,237 +477,310 @@ void ESP8266WebServer::_prepareHeader(String& response, int code, const char* co _responseHeaders = ""; } -void ESP8266WebServer::send(int code, const char* content_type, const String& content) { +void ESP8266WebServer::send(int code, const char* content_type, const String& content) +{ String header; // Can we asume the following? //if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET) // _contentLength = CONTENT_LENGTH_UNKNOWN; _prepareHeader(header, code, content_type, content.length()); _currentClientWrite(header.c_str(), header.length()); - if(content.length()) - sendContent(content); + if (content.length()) + { + sendContent(content); + } } -void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content) { +void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content) +{ size_t contentLength = 0; - if (content != NULL) { + if (content != NULL) + { contentLength = strlen_P(content); } String header; char type[64]; memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); - _prepareHeader(header, code, (const char* )type, contentLength); + _prepareHeader(header, code, (const char*)type, contentLength); _currentClientWrite(header.c_str(), header.length()); - if (contentLength) { + if (contentLength) + { sendContent_P(content); } } -void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) { +void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) +{ String header; char type[64]; memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); - _prepareHeader(header, code, (const char* )type, contentLength); + _prepareHeader(header, code, (const char*)type, contentLength); sendContent(header); sendContent_P(content, contentLength); } -void ESP8266WebServer::send(int code, char* content_type, const String& content) { - send(code, (const char*)content_type, content); +void ESP8266WebServer::send(int code, char* content_type, const String& content) +{ + send(code, (const char*)content_type, content); } -void ESP8266WebServer::send(int code, const String& content_type, const String& content) { - send(code, (const char*)content_type.c_str(), content); +void ESP8266WebServer::send(int code, const String& content_type, const String& content) +{ + send(code, (const char*)content_type.c_str(), content); } -void ESP8266WebServer::sendContent(const String& content) { - const char * footer = "\r\n"; - size_t len = content.length(); - if(_chunked) { - char chunkSize[11]; - sprintf(chunkSize, "%zx\r\n", len); - _currentClientWrite(chunkSize, strlen(chunkSize)); - } - _currentClientWrite(content.c_str(), len); - if(_chunked){ - _currentClient.write(footer, 2); - if (len == 0) { - _chunked = false; +void ESP8266WebServer::sendContent(const String& content) +{ + const char * footer = "\r\n"; + size_t len = content.length(); + if (_chunked) + { + char chunkSize[11]; + sprintf(chunkSize, "%zx\r\n", len); + _currentClientWrite(chunkSize, strlen(chunkSize)); + } + _currentClientWrite(content.c_str(), len); + if (_chunked) + { + _currentClient.write(footer, 2); + if (len == 0) + { + _chunked = false; + } } - } } -void ESP8266WebServer::sendContent_P(PGM_P content) { - sendContent_P(content, strlen_P(content)); +void ESP8266WebServer::sendContent_P(PGM_P content) +{ + sendContent_P(content, strlen_P(content)); } -void ESP8266WebServer::sendContent_P(PGM_P content, size_t size) { - const char * footer = "\r\n"; - if(_chunked) { - char chunkSize[11]; - sprintf(chunkSize, "%zx\r\n", size); - _currentClientWrite(chunkSize, strlen(chunkSize)); - } - _currentClientWrite_P(content, size); - if(_chunked){ - _currentClient.write(footer, 2); - if (size == 0) { - _chunked = false; +void ESP8266WebServer::sendContent_P(PGM_P content, size_t size) +{ + const char * footer = "\r\n"; + if (_chunked) + { + char chunkSize[11]; + sprintf(chunkSize, "%zx\r\n", size); + _currentClientWrite(chunkSize, strlen(chunkSize)); + } + _currentClientWrite_P(content, size); + if (_chunked) + { + _currentClient.write(footer, 2); + if (size == 0) + { + _chunked = false; + } } - } } void ESP8266WebServer::_streamFileCore(const size_t fileSize, const String & fileName, const String & contentType) { - using namespace mime; - setContentLength(fileSize); - if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) && - contentType != String(FPSTR(mimeTable[gz].mimeType)) && - contentType != String(FPSTR(mimeTable[none].mimeType))) { - sendHeader(F("Content-Encoding"), F("gzip")); - } - send(200, contentType, emptyString); + using namespace mime; + setContentLength(fileSize); + if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) && + contentType != String(FPSTR(mimeTable[gz].mimeType)) && + contentType != String(FPSTR(mimeTable[none].mimeType))) + { + sendHeader(F("Content-Encoding"), F("gzip")); + } + send(200, contentType, emptyString); } -const String& ESP8266WebServer::arg(String name) const { - for (int j = 0; j < _postArgsLen; ++j) { - if ( _postArgs[j].key == name ) - return _postArgs[j].value; - } - for (int i = 0; i < _currentArgCount; ++i) { - if ( _currentArgs[i].key == name ) - return _currentArgs[i].value; - } - return emptyString; +const String& ESP8266WebServer::arg(String name) const +{ + for (int j = 0; j < _postArgsLen; ++j) + { + if (_postArgs[j].key == name) + { + return _postArgs[j].value; + } + } + for (int i = 0; i < _currentArgCount; ++i) + { + if (_currentArgs[i].key == name) + { + return _currentArgs[i].value; + } + } + return emptyString; } -const String& ESP8266WebServer::arg(int i) const { - if (i >= 0 && i < _currentArgCount) - return _currentArgs[i].value; - return emptyString; +const String& ESP8266WebServer::arg(int i) const +{ + if (i >= 0 && i < _currentArgCount) + { + return _currentArgs[i].value; + } + return emptyString; } -const String& ESP8266WebServer::argName(int i) const { - if (i >= 0 && i < _currentArgCount) - return _currentArgs[i].key; - return emptyString; +const String& ESP8266WebServer::argName(int i) const +{ + if (i >= 0 && i < _currentArgCount) + { + return _currentArgs[i].key; + } + return emptyString; } -int ESP8266WebServer::args() const { - return _currentArgCount; +int ESP8266WebServer::args() const +{ + return _currentArgCount; } -bool ESP8266WebServer::hasArg(const String& name) const { - for (int j = 0; j < _postArgsLen; ++j) { - if (_postArgs[j].key == name) - return true; - } - for (int i = 0; i < _currentArgCount; ++i) { - if (_currentArgs[i].key == name) - return true; - } - return false; +bool ESP8266WebServer::hasArg(const String& name) const +{ + for (int j = 0; j < _postArgsLen; ++j) + { + if (_postArgs[j].key == name) + { + return true; + } + } + for (int i = 0; i < _currentArgCount; ++i) + { + if (_currentArgs[i].key == name) + { + return true; + } + } + return false; } -const String& ESP8266WebServer::header(String name) const { - for (int i = 0; i < _headerKeysCount; ++i) { - if (_currentHeaders[i].key.equalsIgnoreCase(name)) - return _currentHeaders[i].value; - } - return emptyString; +const String& ESP8266WebServer::header(String name) const +{ + for (int i = 0; i < _headerKeysCount; ++i) + { + if (_currentHeaders[i].key.equalsIgnoreCase(name)) + { + return _currentHeaders[i].value; + } + } + return emptyString; } -void ESP8266WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { - _headerKeysCount = headerKeysCount + 1; - if (_currentHeaders) - delete[]_currentHeaders; - _currentHeaders = new RequestArgument[_headerKeysCount]; - _currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER); - for (int i = 1; i < _headerKeysCount; i++){ - _currentHeaders[i].key = headerKeys[i-1]; - } +void ESP8266WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) +{ + _headerKeysCount = headerKeysCount + 1; + if (_currentHeaders) + { + delete[]_currentHeaders; + } + _currentHeaders = new RequestArgument[_headerKeysCount]; + _currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER); + for (int i = 1; i < _headerKeysCount; i++) + { + _currentHeaders[i].key = headerKeys[i - 1]; + } } -const String& ESP8266WebServer::header(int i) const { - if (i < _headerKeysCount) - return _currentHeaders[i].value; - return emptyString; +const String& ESP8266WebServer::header(int i) const +{ + if (i < _headerKeysCount) + { + return _currentHeaders[i].value; + } + return emptyString; } -const String& ESP8266WebServer::headerName(int i) const { - if (i < _headerKeysCount) - return _currentHeaders[i].key; - return emptyString; +const String& ESP8266WebServer::headerName(int i) const +{ + if (i < _headerKeysCount) + { + return _currentHeaders[i].key; + } + return emptyString; } -int ESP8266WebServer::headers() const { - return _headerKeysCount; +int ESP8266WebServer::headers() const +{ + return _headerKeysCount; } -bool ESP8266WebServer::hasHeader(String name) const { - for (int i = 0; i < _headerKeysCount; ++i) { - if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) - return true; - } - return false; +bool ESP8266WebServer::hasHeader(String name) const +{ + for (int i = 0; i < _headerKeysCount; ++i) + { + if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) + { + return true; + } + } + return false; } -const String& ESP8266WebServer::hostHeader() const { - return _hostHeader; +const String& ESP8266WebServer::hostHeader() const +{ + return _hostHeader; } -void ESP8266WebServer::onFileUpload(THandlerFunction fn) { - _fileUploadHandler = fn; +void ESP8266WebServer::onFileUpload(THandlerFunction fn) +{ + _fileUploadHandler = fn; } -void ESP8266WebServer::onNotFound(THandlerFunction fn) { - _notFoundHandler = fn; +void ESP8266WebServer::onNotFound(THandlerFunction fn) +{ + _notFoundHandler = fn; } -void ESP8266WebServer::_handleRequest() { - bool handled = false; - if (!_currentHandler){ +void ESP8266WebServer::_handleRequest() +{ + bool handled = false; + if (!_currentHandler) + { #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("request handler not found"); + DEBUG_OUTPUT.println("request handler not found"); #endif - } - else { - handled = _currentHandler->handle(*this, _currentMethod, _currentUri); -#ifdef DEBUG_ESP_HTTP_SERVER - if (!handled) { - DEBUG_OUTPUT.println("request handler failed to handle request"); } + else + { + handled = _currentHandler->handle(*this, _currentMethod, _currentUri); +#ifdef DEBUG_ESP_HTTP_SERVER + if (!handled) + { + DEBUG_OUTPUT.println("request handler failed to handle request"); + } #endif - } - if (!handled && _notFoundHandler) { - _notFoundHandler(); - handled = true; - } - if (!handled) { - using namespace mime; - send(404, String(FPSTR(mimeTable[html].mimeType)), String(F("Not found: ")) + _currentUri); - handled = true; - } - if (handled) { - _finalizeResponse(); - } - _currentUri = ""; + } + if (!handled && _notFoundHandler) + { + _notFoundHandler(); + handled = true; + } + if (!handled) + { + using namespace mime; + send(404, String(FPSTR(mimeTable[html].mimeType)), String(F("Not found: ")) + _currentUri); + handled = true; + } + if (handled) + { + _finalizeResponse(); + } + _currentUri = ""; } -void ESP8266WebServer::_finalizeResponse() { - if (_chunked) { - sendContent(emptyString); - } +void ESP8266WebServer::_finalizeResponse() +{ + if (_chunked) + { + sendContent(emptyString); + } } -const String ESP8266WebServer::responseCodeToString(const int code) { - switch (code) { +const String ESP8266WebServer::responseCodeToString(const int code) +{ + switch (code) + { case 100: return F("Continue"); case 101: return F("Switching Protocols"); case 200: return F("OK"); @@ -668,5 +822,5 @@ const String ESP8266WebServer::responseCodeToString(const int code) { case 504: return F("Gateway Time-out"); case 505: return F("HTTP Version not supported"); default: return F(""); - } + } } diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 10e9a5666f..9b261e20c6 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -1,23 +1,23 @@ /* - ESP8266WebServer.h - Dead simple web-server. - Supports only one simultaneous client, knows how to handle GET and POST. + ESP8266WebServer.h - Dead simple web-server. + Supports only one simultaneous client, knows how to handle GET and POST. - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) */ @@ -30,7 +30,8 @@ enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS }; enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, - UPLOAD_FILE_ABORTED }; + UPLOAD_FILE_ABORTED + }; enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH }; @@ -50,153 +51,175 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH }; class ESP8266WebServer; -typedef struct { - HTTPUploadStatus status; - String filename; - String name; - String type; - size_t totalSize; // total size of uploaded file so far - size_t currentSize; // size of data currently in buf - size_t contentLength; // size of entire post request, file size + headers and other request data. - uint8_t buf[HTTP_UPLOAD_BUFLEN]; +typedef struct +{ + HTTPUploadStatus status; + String filename; + String name; + String type; + size_t totalSize; // total size of uploaded file so far + size_t currentSize; // size of data currently in buf + size_t contentLength; // size of entire post request, file size + headers and other request data. + uint8_t buf[HTTP_UPLOAD_BUFLEN]; } HTTPUpload; #include "detail/RequestHandler.h" -namespace fs { +namespace fs +{ class FS; } class ESP8266WebServer { public: - ESP8266WebServer(IPAddress addr, int port = 80); - ESP8266WebServer(int port = 80); - virtual ~ESP8266WebServer(); - - virtual void begin(); - virtual void begin(uint16_t port); - virtual void handleClient(); - - virtual void close(); - void stop(); - - bool authenticate(const char * username, const char * password); - void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") ); - - typedef std::function THandlerFunction; - void on(const String &uri, THandlerFunction handler); - void on(const String &uri, HTTPMethod method, THandlerFunction fn); - void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); - void addHandler(RequestHandler* handler); - void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); - void onNotFound(THandlerFunction fn); //called when handler is not assigned - void onFileUpload(THandlerFunction fn); //handle file uploads - - const String& uri() const { return _currentUri; } - HTTPMethod method() const { return _currentMethod; } - virtual WiFiClient client() { return _currentClient; } - HTTPUpload& upload() { return *_currentUpload; } - - const String& arg(String name) const; // get request argument value by name - const String& arg(int i) const; // get request argument value by number - const String& argName(int i) const; // get request argument name by number - int args() const; // get arguments count - bool hasArg(const String& name) const; // check if argument exists - void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect - const String& header(String name) const; // get request header value by name - const String& header(int i) const; // get request header value by number - const String& headerName(int i) const; // get request header name by number - int headers() const; // get header count - bool hasHeader(String name) const; // check if header exists - const String& hostHeader() const; // get request host header if available or empty String if not - - // send response to the client - // code - HTTP response code, can be 200 or 404 - // content_type - HTTP content type, like "text/plain" or "image/png" - // content - actual content body - void send(int code, const char* content_type = NULL, const String& content = String("")); - void send(int code, char* content_type, const String& content); - void send(int code, const String& content_type, const String& content); - void send_P(int code, PGM_P content_type, PGM_P content); - void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); - - void setContentLength(const size_t contentLength); - void sendHeader(const String& name, const String& value, bool first = false); - void sendContent(const String& content); - void sendContent_P(PGM_P content); - void sendContent_P(PGM_P content, size_t size); - - static String urlDecode(const String& text); - - template - size_t streamFile(T &file, const String& contentType) { - _streamFileCore(file.size(), file.name(), contentType); - return _currentClient.write(file); - } - - static const String responseCodeToString(const int code); + ESP8266WebServer(IPAddress addr, int port = 80); + ESP8266WebServer(int port = 80); + virtual ~ESP8266WebServer(); + + virtual void begin(); + virtual void begin(uint16_t port); + virtual void handleClient(); + + virtual void close(); + void stop(); + + bool authenticate(const char * username, const char * password); + void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("")); + + typedef std::function THandlerFunction; + void on(const String &uri, THandlerFunction handler); + void on(const String &uri, HTTPMethod method, THandlerFunction fn); + void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); + void addHandler(RequestHandler* handler); + void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL); + void onNotFound(THandlerFunction fn); //called when handler is not assigned + void onFileUpload(THandlerFunction fn); //handle file uploads + + const String& uri() const + { + return _currentUri; + } + HTTPMethod method() const + { + return _currentMethod; + } + virtual WiFiClient client() + { + return _currentClient; + } + HTTPUpload& upload() + { + return *_currentUpload; + } + + const String& arg(String name) const; // get request argument value by name + const String& arg(int i) const; // get request argument value by number + const String& argName(int i) const; // get request argument name by number + int args() const; // get arguments count + bool hasArg(const String& name) const; // check if argument exists + void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect + const String& header(String name) const; // get request header value by name + const String& header(int i) const; // get request header value by number + const String& headerName(int i) const; // get request header name by number + int headers() const; // get header count + bool hasHeader(String name) const; // check if header exists + const String& hostHeader() const; // get request host header if available or empty String if not + + // send response to the client + // code - HTTP response code, can be 200 or 404 + // content_type - HTTP content type, like "text/plain" or "image/png" + // content - actual content body + void send(int code, const char* content_type = NULL, const String& content = String("")); + void send(int code, char* content_type, const String& content); + void send(int code, const String& content_type, const String& content); + void send_P(int code, PGM_P content_type, PGM_P content); + void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); + + void setContentLength(const size_t contentLength); + void sendHeader(const String& name, const String& value, bool first = false); + void sendContent(const String& content); + void sendContent_P(PGM_P content); + void sendContent_P(PGM_P content, size_t size); + + static String urlDecode(const String& text); + + template + size_t streamFile(T &file, const String& contentType) + { + _streamFileCore(file.size(), file.name(), contentType); + return _currentClient.write(file); + } + + static const String responseCodeToString(const int code); protected: - virtual size_t _currentClientWrite(const char* b, size_t l) { return _currentClient.write( b, l ); } - virtual size_t _currentClientWrite_P(PGM_P b, size_t l) { return _currentClient.write_P( b, l ); } - void _addRequestHandler(RequestHandler* handler); - void _handleRequest(); - void _finalizeResponse(); - bool _parseRequest(WiFiClient& client); - void _parseArguments(const String& data); - int _parseArgumentsPrivate(const String& data, std::function handler); - bool _parseForm(WiFiClient& client, const String& boundary, uint32_t len); - bool _parseFormUploadAborted(); - void _uploadWriteByte(uint8_t b); - uint8_t _uploadReadByte(WiFiClient& client); - void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); - bool _collectHeader(const char* headerName, const char* headerValue); - - void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType); - - static String _getRandomHexString(); - // for extracting Auth parameters - String _extractParam(String& authReq,const String& param,const char delimit = '"') const; - - struct RequestArgument { - String key; - String value; - }; - - WiFiServer _server; - - WiFiClient _currentClient; - HTTPMethod _currentMethod; - String _currentUri; - uint8_t _currentVersion; - HTTPClientStatus _currentStatus; - unsigned long _statusChange; - - RequestHandler* _currentHandler; - RequestHandler* _firstHandler; - RequestHandler* _lastHandler; - THandlerFunction _notFoundHandler; - THandlerFunction _fileUploadHandler; - - int _currentArgCount; - RequestArgument* _currentArgs; - std::unique_ptr _currentUpload; - int _postArgsLen; - RequestArgument* _postArgs; - - int _headerKeysCount; - RequestArgument* _currentHeaders; - - size_t _contentLength; - String _responseHeaders; - - String _hostHeader; - bool _chunked; - - String _snonce; // Store noance and opaque for future comparison - String _sopaque; - String _srealm; // Store the Auth realm between Calls + virtual size_t _currentClientWrite(const char* b, size_t l) + { + return _currentClient.write(b, l); + } + virtual size_t _currentClientWrite_P(PGM_P b, size_t l) + { + return _currentClient.write_P(b, l); + } + void _addRequestHandler(RequestHandler* handler); + void _handleRequest(); + void _finalizeResponse(); + bool _parseRequest(WiFiClient& client); + void _parseArguments(const String& data); + int _parseArgumentsPrivate(const String& data, std::function handler); + bool _parseForm(WiFiClient& client, const String& boundary, uint32_t len); + bool _parseFormUploadAborted(); + void _uploadWriteByte(uint8_t b); + uint8_t _uploadReadByte(WiFiClient& client); + void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); + bool _collectHeader(const char* headerName, const char* headerValue); + + void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType); + + static String _getRandomHexString(); + // for extracting Auth parameters + String _extractParam(String& authReq, const String& param, const char delimit = '"') const; + + struct RequestArgument + { + String key; + String value; + }; + + WiFiServer _server; + + WiFiClient _currentClient; + HTTPMethod _currentMethod; + String _currentUri; + uint8_t _currentVersion; + HTTPClientStatus _currentStatus; + unsigned long _statusChange; + + RequestHandler* _currentHandler; + RequestHandler* _firstHandler; + RequestHandler* _lastHandler; + THandlerFunction _notFoundHandler; + THandlerFunction _fileUploadHandler; + + int _currentArgCount; + RequestArgument* _currentArgs; + std::unique_ptr _currentUpload; + int _postArgsLen; + RequestArgument* _postArgs; + + int _headerKeysCount; + RequestArgument* _currentHeaders; + + size_t _contentLength; + String _responseHeaders; + + String _hostHeader; + bool _chunked; + + String _snonce; // Store noance and opaque for future comparison + String _sopaque; + String _srealm; // Store the Auth realm between Calls }; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServerSecure.h b/libraries/ESP8266WebServer/src/ESP8266WebServerSecure.h index 5258c6cc89..4b1d5ef345 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServerSecure.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServerSecure.h @@ -1,22 +1,22 @@ /* - ESP8266WebServerSecure.h - Dead simple HTTPS web-server. - Supports only one simultaneous client, knows how to handle GET and POST. + ESP8266WebServerSecure.h - Dead simple HTTPS web-server. + Supports only one simultaneous client, knows how to handle GET and POST. - Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. + Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServerSecureAxTLS.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServerSecureAxTLS.cpp index 09ba1ba5c9..d4ddba8e9c 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServerSecureAxTLS.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServerSecureAxTLS.cpp @@ -1,23 +1,23 @@ /* - ESP8266WebServerSecure.cpp - Dead simple HTTPS web-server. - Supports only one simultaneous client, knows how to handle GET and POST. + ESP8266WebServerSecure.cpp - Dead simple HTTPS web-server. + Supports only one simultaneous client, knows how to handle GET and POST. - Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. + Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) */ @@ -34,18 +34,19 @@ #define DEBUG_OUTPUT Serial #endif -namespace axTLS { +namespace axTLS +{ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -ESP8266WebServerSecure::ESP8266WebServerSecure(IPAddress addr, int port) -: _serverSecure(addr, port) +ESP8266WebServerSecure::ESP8266WebServerSecure(IPAddress addr, int port) + : _serverSecure(addr, port) { } ESP8266WebServerSecure::ESP8266WebServerSecure(int port) -: _serverSecure(port) + : _serverSecure(port) { } @@ -61,97 +62,116 @@ void ESP8266WebServerSecure::setServerKeyAndCert(const uint8_t *key, int keyLen, _serverSecure.setServerKeyAndCert(key, keyLen, cert, certLen); } -ESP8266WebServerSecure::~ESP8266WebServerSecure() { - // Nothing to do here. - // Base class's destructor will be called to clean up itself +ESP8266WebServerSecure::~ESP8266WebServerSecure() +{ + // Nothing to do here. + // Base class's destructor will be called to clean up itself } // We need to basically cut-n-paste these from WebServer because of the problem // of object slicing. The class uses assignment operators like "WiFiClient x=y;" -// When this happens, even if "y" is a WiFiClientSecure, the main class is +// When this happens, even if "y" is a WiFiClientSecure, the main class is // already compiled down into code which will only copy the WiFiClient superclass // and not the extra bits for our own class (since when it was compiled it needed // to know the size of memory to allocate on the stack for this local variable // there's not realy anything else it could do). -void ESP8266WebServerSecure::begin() { - _currentStatus = HC_NONE; - _serverSecure.begin(); - if(!_headerKeysCount) - collectHeaders(0, 0); +void ESP8266WebServerSecure::begin() +{ + _currentStatus = HC_NONE; + _serverSecure.begin(); + if (!_headerKeysCount) + { + collectHeaders(0, 0); + } } -void ESP8266WebServerSecure::handleClient() { - if (_currentStatus == HC_NONE) { - WiFiClientSecure client = _serverSecure.available(); - if (!client) { - return; - } +void ESP8266WebServerSecure::handleClient() +{ + if (_currentStatus == HC_NONE) + { + WiFiClientSecure client = _serverSecure.available(); + if (!client) + { + return; + } #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("New secure client"); + DEBUG_OUTPUT.println("New secure client"); #endif - _currentClientSecure = client; - _currentStatus = HC_WAIT_READ; - _statusChange = millis(); - } - - bool keepCurrentClient = false; - bool callYield = false; - - if (_currentClientSecure.connected()) { - switch (_currentStatus) { - case HC_NONE: - // No-op to avoid C++ compiler warning - break; - case HC_WAIT_READ: - // Wait for data from client to become available - if (_currentClientSecure.available()) { - if (_parseRequest(_currentClientSecure)) { - _currentClientSecure.setTimeout(HTTP_MAX_SEND_WAIT); - _contentLength = CONTENT_LENGTH_NOT_SET; - _handleRequest(); - - if (_currentClientSecure.connected()) { - _currentStatus = HC_WAIT_CLOSE; - _statusChange = millis(); - keepCurrentClient = true; - } - } - } else { // !_currentClient.available() - if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { - keepCurrentClient = true; + _currentClientSecure = client; + _currentStatus = HC_WAIT_READ; + _statusChange = millis(); + } + + bool keepCurrentClient = false; + bool callYield = false; + + if (_currentClientSecure.connected()) + { + switch (_currentStatus) + { + case HC_NONE: + // No-op to avoid C++ compiler warning + break; + case HC_WAIT_READ: + // Wait for data from client to become available + if (_currentClientSecure.available()) + { + if (_parseRequest(_currentClientSecure)) + { + _currentClientSecure.setTimeout(HTTP_MAX_SEND_WAIT); + _contentLength = CONTENT_LENGTH_NOT_SET; + _handleRequest(); + + if (_currentClientSecure.connected()) + { + _currentStatus = HC_WAIT_CLOSE; + _statusChange = millis(); + keepCurrentClient = true; + } + } + } + else // !_currentClient.available() + { + if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) + { + keepCurrentClient = true; + } + callYield = true; + } + break; + case HC_WAIT_CLOSE: + // Wait for client to close the connection + if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) + { + keepCurrentClient = true; + callYield = true; + } } - callYield = true; - } - break; - case HC_WAIT_CLOSE: - // Wait for client to close the connection - if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) { - keepCurrentClient = true; - callYield = true; - } } - } - if (!keepCurrentClient) { + if (!keepCurrentClient) + { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - _currentClientSecure = WiFiClientSecure(); + _currentClientSecure = WiFiClientSecure(); #pragma GCC diagnostic pop - _currentStatus = HC_NONE; - _currentUpload.reset(); - } + _currentStatus = HC_NONE; + _currentUpload.reset(); + } - if (callYield) { - yield(); - } + if (callYield) + { + yield(); + } } -void ESP8266WebServerSecure::close() { - _currentClientSecure.stop(); - _serverSecure.close(); +void ESP8266WebServerSecure::close() +{ + _currentClientSecure.stop(); + _serverSecure.close(); } }; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServerSecureAxTLS.h b/libraries/ESP8266WebServer/src/ESP8266WebServerSecureAxTLS.h index a53d6fa834..5775624317 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServerSecureAxTLS.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServerSecureAxTLS.h @@ -1,22 +1,22 @@ /* - ESP8266WebServerSecure.h - Dead simple HTTPS web-server. - Supports only one simultaneous client, knows how to handle GET and POST. + ESP8266WebServerSecure.h - Dead simple HTTPS web-server. + Supports only one simultaneous client, knows how to handle GET and POST. - Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. + Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,37 +27,48 @@ #include #include -namespace axTLS { +namespace axTLS +{ class ESP8266WebServerSecure : public ESP8266WebServer { public: - ESP8266WebServerSecure(IPAddress addr, int port = 443); - ESP8266WebServerSecure(int port = 443); - virtual ~ESP8266WebServerSecure(); + ESP8266WebServerSecure(IPAddress addr, int port = 443); + ESP8266WebServerSecure(int port = 443); + virtual ~ESP8266WebServerSecure(); - void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); - void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); + void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); + void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); - WiFiClient client() override { return _currentClientSecure; } + WiFiClient client() override + { + return _currentClientSecure; + } - void begin() override; - void handleClient() override; - void close() override; + void begin() override; + void handleClient() override; + void close() override; - template - size_t streamFile(T &file, const String& contentType) { - _streamFileCore(file.size(), file.name(), contentType); - return _currentClientSecure.write(file); - } + template + size_t streamFile(T &file, const String& contentType) + { + _streamFileCore(file.size(), file.name(), contentType); + return _currentClientSecure.write(file); + } private: - size_t _currentClientWrite (const char *bytes, size_t len) override { return _currentClientSecure.write((const uint8_t *)bytes, len); } - size_t _currentClientWrite_P (PGM_P bytes, size_t len) override { return _currentClientSecure.write_P(bytes, len); } + size_t _currentClientWrite(const char *bytes, size_t len) override + { + return _currentClientSecure.write((const uint8_t *)bytes, len); + } + size_t _currentClientWrite_P(PGM_P bytes, size_t len) override + { + return _currentClientSecure.write_P(bytes, len); + } protected: - WiFiServerSecure _serverSecure; - WiFiClientSecure _currentClientSecure; + WiFiServerSecure _serverSecure; + WiFiClientSecure _currentClientSecure; }; }; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServerSecureBearSSL.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServerSecureBearSSL.cpp index 83428ff1d7..984a74de0b 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServerSecureBearSSL.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServerSecureBearSSL.cpp @@ -1,23 +1,23 @@ /* - ESP8266WebServerSecure.cpp - Dead simple HTTPS web-server. - Supports only one simultaneous client, knows how to handle GET and POST. + ESP8266WebServerSecure.cpp - Dead simple HTTPS web-server. + Supports only one simultaneous client, knows how to handle GET and POST. - Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. + Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) */ @@ -34,132 +34,153 @@ #define DEBUG_OUTPUT Serial #endif -namespace BearSSL { +namespace BearSSL +{ -ESP8266WebServerSecure::ESP8266WebServerSecure(IPAddress addr, int port) -: _serverSecure(addr, port) +ESP8266WebServerSecure::ESP8266WebServerSecure(IPAddress addr, int port) + : _serverSecure(addr, port) { } ESP8266WebServerSecure::ESP8266WebServerSecure(int port) -: _serverSecure(port) + : _serverSecure(port) { } void ESP8266WebServerSecure::setRSACert(const X509List *chain, const PrivateKey *sk) { - _serverSecure.setRSACert(chain, sk); + _serverSecure.setRSACert(chain, sk); } void ESP8266WebServerSecure::setECCert(const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk) { - _serverSecure.setECCert(chain, cert_issuer_key_type, sk); + _serverSecure.setECCert(chain, cert_issuer_key_type, sk); } void ESP8266WebServerSecure::setBufferSizes(int recv, int xmit) { - _serverSecure.setBufferSizes(recv, xmit); + _serverSecure.setBufferSizes(recv, xmit); } -ESP8266WebServerSecure::~ESP8266WebServerSecure() { - // Nothing to do here. - // Base class's destructor will be called to clean up itself +ESP8266WebServerSecure::~ESP8266WebServerSecure() +{ + // Nothing to do here. + // Base class's destructor will be called to clean up itself } // We need to basically cut-n-paste these from WebServer because of the problem // of object slicing. The class uses assignment operators like "WiFiClient x=y;" -// When this happens, even if "y" is a WiFiClientSecure, the main class is +// When this happens, even if "y" is a WiFiClientSecure, the main class is // already compiled down into code which will only copy the WiFiClient superclass // and not the extra bits for our own class (since when it was compiled it needed // to know the size of memory to allocate on the stack for this local variable // there's not realy anything else it could do). -void ESP8266WebServerSecure::begin() { - _currentStatus = HC_NONE; - _serverSecure.begin(); - if(!_headerKeysCount) - collectHeaders(0, 0); +void ESP8266WebServerSecure::begin() +{ + _currentStatus = HC_NONE; + _serverSecure.begin(); + if (!_headerKeysCount) + { + collectHeaders(0, 0); + } } -void ESP8266WebServerSecure::handleClient() { - if (_currentStatus == HC_NONE) { - WiFiClientSecure client = _serverSecure.available(); - if (!client) { - return; - } +void ESP8266WebServerSecure::handleClient() +{ + if (_currentStatus == HC_NONE) + { + WiFiClientSecure client = _serverSecure.available(); + if (!client) + { + return; + } #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("New secure client"); + DEBUG_OUTPUT.println("New secure client"); #endif - _currentClientSecure = client; - _currentStatus = HC_WAIT_READ; - _statusChange = millis(); - } - - bool keepCurrentClient = false; - bool callYield = false; - - if (_currentClientSecure.connected()) { - switch (_currentStatus) { - case HC_NONE: - // No-op to avoid C++ compiler warning - break; - case HC_WAIT_READ: - // Wait for data from client to become available - if (_currentClientSecure.available()) { - if (_parseRequest(_currentClientSecure)) { - _currentClientSecure.setTimeout(HTTP_MAX_SEND_WAIT); - _contentLength = CONTENT_LENGTH_NOT_SET; - _handleRequest(); - - if (_currentClientSecure.connected()) { - _currentStatus = HC_WAIT_CLOSE; - _statusChange = millis(); - keepCurrentClient = true; - } - } - } else { // !_currentClient.available() - if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { - keepCurrentClient = true; + _currentClientSecure = client; + _currentStatus = HC_WAIT_READ; + _statusChange = millis(); + } + + bool keepCurrentClient = false; + bool callYield = false; + + if (_currentClientSecure.connected()) + { + switch (_currentStatus) + { + case HC_NONE: + // No-op to avoid C++ compiler warning + break; + case HC_WAIT_READ: + // Wait for data from client to become available + if (_currentClientSecure.available()) + { + if (_parseRequest(_currentClientSecure)) + { + _currentClientSecure.setTimeout(HTTP_MAX_SEND_WAIT); + _contentLength = CONTENT_LENGTH_NOT_SET; + _handleRequest(); + + if (_currentClientSecure.connected()) + { + _currentStatus = HC_WAIT_CLOSE; + _statusChange = millis(); + keepCurrentClient = true; + } + } + } + else // !_currentClient.available() + { + if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) + { + keepCurrentClient = true; + } + callYield = true; + } + break; + case HC_WAIT_CLOSE: + // Wait for client to close the connection + if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) + { + keepCurrentClient = true; + callYield = true; + } } - callYield = true; - } - break; - case HC_WAIT_CLOSE: - // Wait for client to close the connection - if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) { - keepCurrentClient = true; - callYield = true; - } } - } - if (!keepCurrentClient) { - _currentClientSecure = WiFiClientSecure(); - _currentStatus = HC_NONE; - _currentUpload.reset(); - } + if (!keepCurrentClient) + { + _currentClientSecure = WiFiClientSecure(); + _currentStatus = HC_NONE; + _currentUpload.reset(); + } - if (callYield) { - yield(); - } + if (callYield) + { + yield(); + } } -void ESP8266WebServerSecure::close() { - _currentClientSecure.flush(); - _currentClientSecure.stop(); - _serverSecure.close(); +void ESP8266WebServerSecure::close() +{ + _currentClientSecure.flush(); + _currentClientSecure.stop(); + _serverSecure.close(); } -void ESP8266WebServerSecure::setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) { - _serverSecure.setServerKeyAndCert_P(key, keyLen, cert, certLen); +void ESP8266WebServerSecure::setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) +{ + _serverSecure.setServerKeyAndCert_P(key, keyLen, cert, certLen); } void ESP8266WebServerSecure::setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) { - _serverSecure.setServerKeyAndCert(key, keyLen, cert, certLen); + _serverSecure.setServerKeyAndCert(key, keyLen, cert, certLen); } }; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServerSecureBearSSL.h b/libraries/ESP8266WebServer/src/ESP8266WebServerSecureBearSSL.h index d7a69a973a..d1911026c2 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServerSecureBearSSL.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServerSecureBearSSL.h @@ -1,22 +1,22 @@ /* - ESP8266WebServerSecure.h - Dead simple HTTPS web-server. - Supports only one simultaneous client, knows how to handle GET and POST. + ESP8266WebServerSecure.h - Dead simple HTTPS web-server. + Supports only one simultaneous client, knows how to handle GET and POST. - Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. + Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -27,42 +27,53 @@ #include #include -namespace BearSSL { +namespace BearSSL +{ class ESP8266WebServerSecure : public ESP8266WebServer { public: - ESP8266WebServerSecure(IPAddress addr, int port = 443); - ESP8266WebServerSecure(int port = 443); - virtual ~ESP8266WebServerSecure(); - - void setBufferSizes(int recv, int xmit); - void setRSACert(const X509List *chain, const PrivateKey *sk); - void setECCert(const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk); - - WiFiClient client() override { return _currentClientSecure; } - - void begin() override; - void handleClient() override; - void close() override; - - template - size_t streamFile(T &file, const String& contentType) { - _streamFileCore(file.size(), file.name(), contentType); - return _currentClientSecure.write(file); - } - - // AXTLS Compatibility - void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); - void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); + ESP8266WebServerSecure(IPAddress addr, int port = 443); + ESP8266WebServerSecure(int port = 443); + virtual ~ESP8266WebServerSecure(); + + void setBufferSizes(int recv, int xmit); + void setRSACert(const X509List *chain, const PrivateKey *sk); + void setECCert(const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk); + + WiFiClient client() override + { + return _currentClientSecure; + } + + void begin() override; + void handleClient() override; + void close() override; + + template + size_t streamFile(T &file, const String& contentType) + { + _streamFileCore(file.size(), file.name(), contentType); + return _currentClientSecure.write(file); + } + + // AXTLS Compatibility + void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); + void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); private: - size_t _currentClientWrite (const char *bytes, size_t len) override { return _currentClientSecure.write((const uint8_t *)bytes, len); } - size_t _currentClientWrite_P (PGM_P bytes, size_t len) override { return _currentClientSecure.write_P(bytes, len); } + size_t _currentClientWrite(const char *bytes, size_t len) override + { + return _currentClientSecure.write((const uint8_t *)bytes, len); + } + size_t _currentClientWrite_P(PGM_P bytes, size_t len) override + { + return _currentClientSecure.write_P(bytes, len); + } protected: - WiFiServerSecure _serverSecure; - WiFiClientSecure _currentClientSecure; + WiFiServerSecure _serverSecure; + WiFiClientSecure _currentClientSecure; }; }; diff --git a/libraries/ESP8266WebServer/src/Parsing.cpp b/libraries/ESP8266WebServer/src/Parsing.cpp index 9d5ecc012d..48bf606a69 100644 --- a/libraries/ESP8266WebServer/src/Parsing.cpp +++ b/libraries/ESP8266WebServer/src/Parsing.cpp @@ -1,22 +1,22 @@ /* - Parsing.cpp - HTTP request parsing. + Parsing.cpp - HTTP request parsing. - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) */ #include @@ -41,585 +41,742 @@ static const char filename[] PROGMEM = "filename"; static bool readBytesWithTimeout(WiFiClient& client, size_t maxLength, String& data, int timeout_ms) { - if (!data.reserve(maxLength + 1)) - return false; - data[0] = 0; // data.clear()?? - while (data.length() < maxLength) { - int tries = timeout_ms; - size_t avail; - while (!(avail = client.available()) && tries--) - delay(1); - if (!avail) - break; - if (data.length() + avail > maxLength) - avail = maxLength - data.length(); - while (avail--) - data += (char)client.read(); - } - return data.length() == maxLength; + if (!data.reserve(maxLength + 1)) + { + return false; + } + data[0] = 0; // data.clear()?? + while (data.length() < maxLength) + { + int tries = timeout_ms; + size_t avail; + while (!(avail = client.available()) && tries--) + { + delay(1); + } + if (!avail) + { + break; + } + if (data.length() + avail > maxLength) + { + avail = maxLength - data.length(); + } + while (avail--) + { + data += (char)client.read(); + } + } + return data.length() == maxLength; } -bool ESP8266WebServer::_parseRequest(WiFiClient& client) { - // Read the first line of HTTP request - String req = client.readStringUntil('\r'); +bool ESP8266WebServer::_parseRequest(WiFiClient& client) +{ + // Read the first line of HTTP request + String req = client.readStringUntil('\r'); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("request: "); DEBUG_OUTPUT.println(req); #endif - client.readStringUntil('\n'); - //reset header value - for (int i = 0; i < _headerKeysCount; ++i) { - _currentHeaders[i].value =String(); - } - - // First line of HTTP request looks like "GET /path HTTP/1.1" - // Retrieve the "/path" part by finding the spaces - int addr_start = req.indexOf(' '); - int addr_end = req.indexOf(' ', addr_start + 1); - if (addr_start == -1 || addr_end == -1) { + client.readStringUntil('\n'); + //reset header value + for (int i = 0; i < _headerKeysCount; ++i) + { + _currentHeaders[i].value = String(); + } + + // First line of HTTP request looks like "GET /path HTTP/1.1" + // Retrieve the "/path" part by finding the spaces + int addr_start = req.indexOf(' '); + int addr_end = req.indexOf(' ', addr_start + 1); + if (addr_start == -1 || addr_end == -1) + { #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Invalid request"); + DEBUG_OUTPUT.println("Invalid request"); #endif - return false; - } - - String methodStr = req.substring(0, addr_start); - String url = req.substring(addr_start + 1, addr_end); - String versionEnd = req.substring(addr_end + 8); - _currentVersion = atoi(versionEnd.c_str()); - String searchStr = ""; - int hasSearch = url.indexOf('?'); - if (hasSearch != -1){ - searchStr = url.substring(hasSearch + 1); - url = url.substring(0, hasSearch); - } - _currentUri = url; - _chunked = false; - - HTTPMethod method = HTTP_GET; - if (methodStr == F("POST")) { - method = HTTP_POST; - } else if (methodStr == F("DELETE")) { - method = HTTP_DELETE; - } else if (methodStr == F("OPTIONS")) { - method = HTTP_OPTIONS; - } else if (methodStr == F("PUT")) { - method = HTTP_PUT; - } else if (methodStr == F("PATCH")) { - method = HTTP_PATCH; - } - _currentMethod = method; + return false; + } + + String methodStr = req.substring(0, addr_start); + String url = req.substring(addr_start + 1, addr_end); + String versionEnd = req.substring(addr_end + 8); + _currentVersion = atoi(versionEnd.c_str()); + String searchStr = ""; + int hasSearch = url.indexOf('?'); + if (hasSearch != -1) + { + searchStr = url.substring(hasSearch + 1); + url = url.substring(0, hasSearch); + } + _currentUri = url; + _chunked = false; + + HTTPMethod method = HTTP_GET; + if (methodStr == F("POST")) + { + method = HTTP_POST; + } + else if (methodStr == F("DELETE")) + { + method = HTTP_DELETE; + } + else if (methodStr == F("OPTIONS")) + { + method = HTTP_OPTIONS; + } + else if (methodStr == F("PUT")) + { + method = HTTP_PUT; + } + else if (methodStr == F("PATCH")) + { + method = HTTP_PATCH; + } + _currentMethod = method; #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("method: "); - DEBUG_OUTPUT.print(methodStr); - DEBUG_OUTPUT.print(" url: "); - DEBUG_OUTPUT.print(url); - DEBUG_OUTPUT.print(" search: "); - DEBUG_OUTPUT.println(searchStr); + DEBUG_OUTPUT.print("method: "); + DEBUG_OUTPUT.print(methodStr); + DEBUG_OUTPUT.print(" url: "); + DEBUG_OUTPUT.print(url); + DEBUG_OUTPUT.print(" search: "); + DEBUG_OUTPUT.println(searchStr); #endif - //attach handler - RequestHandler* handler; - for (handler = _firstHandler; handler; handler = handler->next()) { - if (handler->canHandle(_currentMethod, _currentUri)) - break; - } - _currentHandler = handler; - - String formData; - // below is needed only when POST type request - if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){ - String boundaryStr; - String headerName; - String headerValue; - bool isForm = false; - bool isEncoded = false; - uint32_t contentLength = 0; - //parse headers - while(1){ - req = client.readStringUntil('\r'); - client.readStringUntil('\n'); - if (req == "") break;//no moar headers - int headerDiv = req.indexOf(':'); - if (headerDiv == -1){ - break; - } - headerName = req.substring(0, headerDiv); - headerValue = req.substring(headerDiv + 1); - headerValue.trim(); - _collectHeader(headerName.c_str(),headerValue.c_str()); - - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("headerName: "); - DEBUG_OUTPUT.println(headerName); - DEBUG_OUTPUT.print("headerValue: "); - DEBUG_OUTPUT.println(headerValue); - #endif - - if (headerName.equalsIgnoreCase(FPSTR(Content_Type))){ - using namespace mime; - if (headerValue.startsWith(FPSTR(mimeTable[txt].mimeType))){ - isForm = false; - } else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))){ - isForm = false; - isEncoded = true; - } else if (headerValue.startsWith(F("multipart/"))){ - boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1); - boundaryStr.replace("\"",""); - isForm = true; + //attach handler + RequestHandler* handler; + for (handler = _firstHandler; handler; handler = handler->next()) + { + if (handler->canHandle(_currentMethod, _currentUri)) + { + break; } - } else if (headerName.equalsIgnoreCase(F("Content-Length"))){ - contentLength = headerValue.toInt(); - } else if (headerName.equalsIgnoreCase(F("Host"))){ - _hostHeader = headerValue; - } } + _currentHandler = handler; - String plainBuf; - if ( !isForm - && // read content into plainBuf - ( !readBytesWithTimeout(client, contentLength, plainBuf, HTTP_MAX_POST_WAIT) - || (plainBuf.length() < contentLength) - ) - ) + String formData; + // below is needed only when POST type request + if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE) { - return false; - } + String boundaryStr; + String headerName; + String headerValue; + bool isForm = false; + bool isEncoded = false; + uint32_t contentLength = 0; + //parse headers + while (1) + { + req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (req == "") + { + break; //no moar headers + } + int headerDiv = req.indexOf(':'); + if (headerDiv == -1) + { + break; + } + headerName = req.substring(0, headerDiv); + headerValue = req.substring(headerDiv + 1); + headerValue.trim(); + _collectHeader(headerName.c_str(), headerValue.c_str()); - if (isEncoded) { - // isEncoded => !isForm => plainBuf is not empty - // add plainBuf in search str - if (searchStr.length()) - searchStr += '&'; - searchStr += plainBuf; - } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("headerName: "); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print("headerValue: "); + DEBUG_OUTPUT.println(headerValue); +#endif - // parse searchStr for key/value pairs - _parseArguments(searchStr); - - if (!isForm) { - if (contentLength) { - // add key=value: plain={body} (post json or other data) - RequestArgument& arg = _currentArgs[_currentArgCount++]; - arg.key = F("plain"); - arg.value = plainBuf; - } - } else { // isForm is true - // here: content is not yet read (plainBuf is still empty) - if (!_parseForm(client, boundaryStr, contentLength)) { - return false; - } + if (headerName.equalsIgnoreCase(FPSTR(Content_Type))) + { + using namespace mime; + if (headerValue.startsWith(FPSTR(mimeTable[txt].mimeType))) + { + isForm = false; + } + else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))) + { + isForm = false; + isEncoded = true; + } + else if (headerValue.startsWith(F("multipart/"))) + { + boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1); + boundaryStr.replace("\"", ""); + isForm = true; + } + } + else if (headerName.equalsIgnoreCase(F("Content-Length"))) + { + contentLength = headerValue.toInt(); + } + else if (headerName.equalsIgnoreCase(F("Host"))) + { + _hostHeader = headerValue; + } + } + + String plainBuf; + if (!isForm + && // read content into plainBuf + (!readBytesWithTimeout(client, contentLength, plainBuf, HTTP_MAX_POST_WAIT) + || (plainBuf.length() < contentLength) + ) + ) + { + return false; + } + + if (isEncoded) + { + // isEncoded => !isForm => plainBuf is not empty + // add plainBuf in search str + if (searchStr.length()) + { + searchStr += '&'; + } + searchStr += plainBuf; + } + + // parse searchStr for key/value pairs + _parseArguments(searchStr); + + if (!isForm) + { + if (contentLength) + { + // add key=value: plain={body} (post json or other data) + RequestArgument& arg = _currentArgs[_currentArgCount++]; + arg.key = F("plain"); + arg.value = plainBuf; + } + } + else // isForm is true + { + // here: content is not yet read (plainBuf is still empty) + if (!_parseForm(client, boundaryStr, contentLength)) + { + return false; + } + } } - } else { - String headerName; - String headerValue; - //parse headers - while(1){ - req = client.readStringUntil('\r'); - client.readStringUntil('\n'); - if (req == "") break;//no moar headers - int headerDiv = req.indexOf(':'); - if (headerDiv == -1){ - break; - } - headerName = req.substring(0, headerDiv); - headerValue = req.substring(headerDiv + 2); - _collectHeader(headerName.c_str(),headerValue.c_str()); + else + { + String headerName; + String headerValue; + //parse headers + while (1) + { + req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (req == "") + { + break; //no moar headers + } + int headerDiv = req.indexOf(':'); + if (headerDiv == -1) + { + break; + } + headerName = req.substring(0, headerDiv); + headerValue = req.substring(headerDiv + 2); + _collectHeader(headerName.c_str(), headerValue.c_str()); #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print(F("headerName: ")); - DEBUG_OUTPUT.println(headerName); - DEBUG_OUTPUT.print(F("headerValue: ")); - DEBUG_OUTPUT.println(headerValue); + DEBUG_OUTPUT.print(F("headerName: ")); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print(F("headerValue: ")); + DEBUG_OUTPUT.println(headerValue); #endif - if (headerName.equalsIgnoreCase("Host")){ - _hostHeader = headerValue; - } + if (headerName.equalsIgnoreCase("Host")) + { + _hostHeader = headerValue; + } + } + _parseArguments(searchStr); } - _parseArguments(searchStr); - } - client.flush(); + client.flush(); #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print(F("Request: ")); - DEBUG_OUTPUT.println(url); - DEBUG_OUTPUT.print(F("Arguments: ")); - DEBUG_OUTPUT.println(searchStr); - - DEBUG_OUTPUT.println(F("final list of key/value pairs:")); - for (int i = 0; i < _currentArgCount; i++) - DEBUG_OUTPUT.printf(" key:'%s' value:'%s'\r\n", - _currentArgs[i].key.c_str(), - _currentArgs[i].value.c_str()); + DEBUG_OUTPUT.print(F("Request: ")); + DEBUG_OUTPUT.println(url); + DEBUG_OUTPUT.print(F("Arguments: ")); + DEBUG_OUTPUT.println(searchStr); + + DEBUG_OUTPUT.println(F("final list of key/value pairs:")); + for (int i = 0; i < _currentArgCount; i++) + DEBUG_OUTPUT.printf(" key:'%s' value:'%s'\r\n", + _currentArgs[i].key.c_str(), + _currentArgs[i].value.c_str()); #endif - return true; + return true; } -bool ESP8266WebServer::_collectHeader(const char* headerName, const char* headerValue) { - for (int i = 0; i < _headerKeysCount; i++) { - if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { - _currentHeaders[i].value=headerValue; +bool ESP8266WebServer::_collectHeader(const char* headerName, const char* headerValue) +{ + for (int i = 0; i < _headerKeysCount; i++) + { + if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) + { + _currentHeaders[i].value = headerValue; return true; } - } - return false; + } + return false; } struct storeArgHandler { - void operator() (String& key, String& value, const String& data, int equal_index, int pos, int key_end_pos, int next_index) - { - key = ESP8266WebServer::urlDecode(data.substring(pos, key_end_pos)); - if ((equal_index != -1) && ((equal_index < next_index - 1) || (next_index == -1))) - value = ESP8266WebServer::urlDecode(data.substring(equal_index + 1, next_index)); - } + void operator()(String& key, String& value, const String& data, int equal_index, int pos, int key_end_pos, int next_index) + { + key = ESP8266WebServer::urlDecode(data.substring(pos, key_end_pos)); + if ((equal_index != -1) && ((equal_index < next_index - 1) || (next_index == -1))) + { + value = ESP8266WebServer::urlDecode(data.substring(equal_index + 1, next_index)); + } + } }; struct nullArgHandler { - void operator() (String& key, String& value, const String& data, int equal_index, int pos, int key_end_pos, int next_index) { - (void)key; (void)value; (void)data; (void)equal_index; (void)pos; (void)key_end_pos; (void)next_index; - // do nothing - } + void operator()(String& key, String& value, const String& data, int equal_index, int pos, int key_end_pos, int next_index) + { + (void)key; (void)value; (void)data; (void)equal_index; (void)pos; (void)key_end_pos; (void)next_index; + // do nothing + } }; -void ESP8266WebServer::_parseArguments(const String& data) { - if (_currentArgs) - delete[] _currentArgs; +void ESP8266WebServer::_parseArguments(const String& data) +{ + if (_currentArgs) + { + delete[] _currentArgs; + } - _currentArgCount = _parseArgumentsPrivate(data, nullArgHandler()); + _currentArgCount = _parseArgumentsPrivate(data, nullArgHandler()); - // allocate one more, this is needed because {"plain": plainBuf} is always added - _currentArgs = new RequestArgument[_currentArgCount + 1]; + // allocate one more, this is needed because {"plain": plainBuf} is always added + _currentArgs = new RequestArgument[_currentArgCount + 1]; - (void)_parseArgumentsPrivate(data, storeArgHandler()); + (void)_parseArgumentsPrivate(data, storeArgHandler()); } -int ESP8266WebServer::_parseArgumentsPrivate(const String& data, std::function handler) { +int ESP8266WebServer::_parseArgumentsPrivate(const String& data, std::function handler) +{ #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("args: "); - DEBUG_OUTPUT.println(data); + DEBUG_OUTPUT.print("args: "); + DEBUG_OUTPUT.println(data); #endif - size_t pos = 0; - int arg_total = 0; + size_t pos = 0; + int arg_total = 0; + + while (true) + { - while (true) { + // skip empty expression + while (data[pos] == '&' || data[pos] == ';') + if (++pos >= data.length()) + { + break; + } - // skip empty expression - while (data[pos] == '&' || data[pos] == ';') - if (++pos >= data.length()) - break; + // locate separators + int equal_index = data.indexOf('=', pos); + int key_end_pos = equal_index; + int next_index = data.indexOf('&', pos); + int next_index2 = data.indexOf(';', pos); + if ((next_index == -1) || (next_index2 != -1 && next_index2 < next_index)) + { + next_index = next_index2; + } + if ((key_end_pos == -1) || ((key_end_pos > next_index) && (next_index != -1))) + { + key_end_pos = next_index; + } + if (key_end_pos == -1) + { + key_end_pos = data.length(); + } - // locate separators - int equal_index = data.indexOf('=', pos); - int key_end_pos = equal_index; - int next_index = data.indexOf('&', pos); - int next_index2 = data.indexOf(';', pos); - if ((next_index == -1) || (next_index2 != -1 && next_index2 < next_index)) - next_index = next_index2; - if ((key_end_pos == -1) || ((key_end_pos > next_index) && (next_index != -1))) - key_end_pos = next_index; - if (key_end_pos == -1) - key_end_pos = data.length(); + // handle key/value + if ((int)pos < key_end_pos) + { - // handle key/value - if ((int)pos < key_end_pos) { + RequestArgument& arg = _currentArgs[arg_total]; + handler(arg.key, arg.value, data, equal_index, pos, key_end_pos, next_index); - RequestArgument& arg = _currentArgs[arg_total]; - handler(arg.key, arg.value, data, equal_index, pos, key_end_pos, next_index); + ++arg_total; + pos = next_index + 1; + } - ++arg_total; - pos = next_index + 1; + if (next_index == -1) + { + break; + } } - if (next_index == -1) - break; - } - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("args count: "); - DEBUG_OUTPUT.println(arg_total); + DEBUG_OUTPUT.print("args count: "); + DEBUG_OUTPUT.println(arg_total); #endif - return arg_total; + return arg_total; } -void ESP8266WebServer::_uploadWriteByte(uint8_t b){ - if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN){ - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, *_currentUpload); - _currentUpload->totalSize += _currentUpload->currentSize; - _currentUpload->currentSize = 0; - } - _currentUpload->buf[_currentUpload->currentSize++] = b; +void ESP8266WebServer::_uploadWriteByte(uint8_t b) +{ + if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN) + { + if (_currentHandler && _currentHandler->canUpload(_currentUri)) + { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } + _currentUpload->totalSize += _currentUpload->currentSize; + _currentUpload->currentSize = 0; + } + _currentUpload->buf[_currentUpload->currentSize++] = b; } -uint8_t ESP8266WebServer::_uploadReadByte(WiFiClient& client){ - int res = client.read(); - if(res == -1){ - while(!client.available() && client.connected()) - yield(); - res = client.read(); - } - return (uint8_t)res; +uint8_t ESP8266WebServer::_uploadReadByte(WiFiClient& client) +{ + int res = client.read(); + if (res == -1) + { + while (!client.available() && client.connected()) + { + yield(); + } + res = client.read(); + } + return (uint8_t)res; } -bool ESP8266WebServer::_parseForm(WiFiClient& client, const String& boundary, uint32_t len){ - (void) len; +bool ESP8266WebServer::_parseForm(WiFiClient& client, const String& boundary, uint32_t len) +{ + (void) len; #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Parse Form: Boundary: "); - DEBUG_OUTPUT.print(boundary); - DEBUG_OUTPUT.print(" Length: "); - DEBUG_OUTPUT.println(len); + DEBUG_OUTPUT.print("Parse Form: Boundary: "); + DEBUG_OUTPUT.print(boundary); + DEBUG_OUTPUT.print(" Length: "); + DEBUG_OUTPUT.println(len); #endif - String line; - int retry = 0; - do { - line = client.readStringUntil('\r'); - ++retry; - } while (line.length() == 0 && retry < 3); - - client.readStringUntil('\n'); - //start reading the form - if (line == ("--"+boundary)){ - if(_postArgs) delete[] _postArgs; - _postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS]; - _postArgsLen = 0; - while(1){ - String argName; - String argValue; - String argType; - String argFilename; - bool argIsFile = false; - - line = client.readStringUntil('\r'); - client.readStringUntil('\n'); - if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){ - int nameStart = line.indexOf('='); - if (nameStart != -1){ - argName = line.substring(nameStart+2); - nameStart = argName.indexOf('='); - if (nameStart == -1){ - argName = argName.substring(0, argName.length() - 1); - } else { - argFilename = argName.substring(nameStart+2, argName.length() - 1); - argName = argName.substring(0, argName.indexOf('"')); - argIsFile = true; + String line; + int retry = 0; + do + { + line = client.readStringUntil('\r'); + ++retry; + } while (line.length() == 0 && retry < 3); + + client.readStringUntil('\n'); + //start reading the form + if (line == ("--" + boundary)) + { + if (_postArgs) + { + delete[] _postArgs; + } + _postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS]; + _postArgsLen = 0; + while (1) + { + String argName; + String argValue; + String argType; + String argFilename; + bool argIsFile = false; + + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) + { + int nameStart = line.indexOf('='); + if (nameStart != -1) + { + argName = line.substring(nameStart + 2); + nameStart = argName.indexOf('='); + if (nameStart == -1) + { + argName = argName.substring(0, argName.length() - 1); + } + else + { + argFilename = argName.substring(nameStart + 2, argName.length() - 1); + argName = argName.substring(0, argName.indexOf('"')); + argIsFile = true; #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg FileName: "); - DEBUG_OUTPUT.println(argFilename); + DEBUG_OUTPUT.print("PostArg FileName: "); + DEBUG_OUTPUT.println(argFilename); #endif - //use GET to set the filename if uploading using blob - if (argFilename == F("blob") && hasArg(FPSTR(filename))) - argFilename = arg(FPSTR(filename)); - } + //use GET to set the filename if uploading using blob + if (argFilename == F("blob") && hasArg(FPSTR(filename))) + { + argFilename = arg(FPSTR(filename)); + } + } #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg Name: "); - DEBUG_OUTPUT.println(argName); + DEBUG_OUTPUT.print("PostArg Name: "); + DEBUG_OUTPUT.println(argName); #endif - using namespace mime; - argType = FPSTR(mimeTable[txt].mimeType); - line = client.readStringUntil('\r'); - client.readStringUntil('\n'); - if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))){ - argType = line.substring(line.indexOf(':')+2); - //skip next line - client.readStringUntil('\r'); - client.readStringUntil('\n'); - } + using namespace mime; + argType = FPSTR(mimeTable[txt].mimeType); + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))) + { + argType = line.substring(line.indexOf(':') + 2); + //skip next line + client.readStringUntil('\r'); + client.readStringUntil('\n'); + } #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg Type: "); - DEBUG_OUTPUT.println(argType); + DEBUG_OUTPUT.print("PostArg Type: "); + DEBUG_OUTPUT.println(argType); #endif - if (!argIsFile){ - while(1){ - line = client.readStringUntil('\r'); - client.readStringUntil('\n'); - if (line.startsWith("--"+boundary)) break; - if (argValue.length() > 0) argValue += "\n"; - argValue += line; - } + if (!argIsFile) + { + while (1) + { + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.startsWith("--" + boundary)) + { + break; + } + if (argValue.length() > 0) + { + argValue += "\n"; + } + argValue += line; + } #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg Value: "); - DEBUG_OUTPUT.println(argValue); - DEBUG_OUTPUT.println(); + DEBUG_OUTPUT.print("PostArg Value: "); + DEBUG_OUTPUT.println(argValue); + DEBUG_OUTPUT.println(); #endif - RequestArgument& arg = _postArgs[_postArgsLen++]; - arg.key = argName; - arg.value = argValue; + RequestArgument& arg = _postArgs[_postArgsLen++]; + arg.key = argName; + arg.value = argValue; - if (line == ("--"+boundary+"--")){ + if (line == ("--" + boundary + "--")) + { #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Done Parsing POST"); + DEBUG_OUTPUT.println("Done Parsing POST"); #endif - break; - } - } else { - _currentUpload.reset(new HTTPUpload()); - _currentUpload->status = UPLOAD_FILE_START; - _currentUpload->name = argName; - _currentUpload->filename = argFilename; - _currentUpload->type = argType; - _currentUpload->totalSize = 0; - _currentUpload->currentSize = 0; - _currentUpload->contentLength = len; + break; + } + } + else + { + _currentUpload.reset(new HTTPUpload()); + _currentUpload->status = UPLOAD_FILE_START; + _currentUpload->name = argName; + _currentUpload->filename = argFilename; + _currentUpload->type = argType; + _currentUpload->totalSize = 0; + _currentUpload->currentSize = 0; + _currentUpload->contentLength = len; #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Start File: "); - DEBUG_OUTPUT.print(_currentUpload->filename); - DEBUG_OUTPUT.print(" Type: "); - DEBUG_OUTPUT.println(_currentUpload->type); + DEBUG_OUTPUT.print("Start File: "); + DEBUG_OUTPUT.print(_currentUpload->filename); + DEBUG_OUTPUT.print(" Type: "); + DEBUG_OUTPUT.println(_currentUpload->type); #endif - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, *_currentUpload); - _currentUpload->status = UPLOAD_FILE_WRITE; - uint8_t argByte = _uploadReadByte(client); + if (_currentHandler && _currentHandler->canUpload(_currentUri)) + { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } + _currentUpload->status = UPLOAD_FILE_WRITE; + uint8_t argByte = _uploadReadByte(client); readfile: - while(argByte != 0x0D){ - if (!client.connected()) return _parseFormUploadAborted(); - _uploadWriteByte(argByte); - argByte = _uploadReadByte(client); - } - - argByte = _uploadReadByte(client); - if (!client.connected()) return _parseFormUploadAborted(); - if (argByte == 0x0A){ - argByte = _uploadReadByte(client); - if (!client.connected()) return _parseFormUploadAborted(); - if ((char)argByte != '-'){ - //continue reading the file - _uploadWriteByte(0x0D); - _uploadWriteByte(0x0A); - goto readfile; - } else { - argByte = _uploadReadByte(client); - if (!client.connected()) return _parseFormUploadAborted(); - if ((char)argByte != '-'){ - //continue reading the file - _uploadWriteByte(0x0D); - _uploadWriteByte(0x0A); - _uploadWriteByte((uint8_t)('-')); - goto readfile; - } - } - - uint8_t endBuf[boundary.length()]; - client.readBytes(endBuf, boundary.length()); - - if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, *_currentUpload); - _currentUpload->totalSize += _currentUpload->currentSize; - _currentUpload->status = UPLOAD_FILE_END; - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, *_currentUpload); + while (argByte != 0x0D) + { + if (!client.connected()) + { + return _parseFormUploadAborted(); + } + _uploadWriteByte(argByte); + argByte = _uploadReadByte(client); + } + + argByte = _uploadReadByte(client); + if (!client.connected()) + { + return _parseFormUploadAborted(); + } + if (argByte == 0x0A) + { + argByte = _uploadReadByte(client); + if (!client.connected()) + { + return _parseFormUploadAborted(); + } + if ((char)argByte != '-') + { + //continue reading the file + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + goto readfile; + } + else + { + argByte = _uploadReadByte(client); + if (!client.connected()) + { + return _parseFormUploadAborted(); + } + if ((char)argByte != '-') + { + //continue reading the file + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + _uploadWriteByte((uint8_t)('-')); + goto readfile; + } + } + + uint8_t endBuf[boundary.length()]; + client.readBytes(endBuf, boundary.length()); + + if (strstr((const char*)endBuf, boundary.c_str()) != NULL) + { + if (_currentHandler && _currentHandler->canUpload(_currentUri)) + { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } + _currentUpload->totalSize += _currentUpload->currentSize; + _currentUpload->status = UPLOAD_FILE_END; + if (_currentHandler && _currentHandler->canUpload(_currentUri)) + { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("End File: "); - DEBUG_OUTPUT.print(_currentUpload->filename); - DEBUG_OUTPUT.print(" Type: "); - DEBUG_OUTPUT.print(_currentUpload->type); - DEBUG_OUTPUT.print(" Size: "); - DEBUG_OUTPUT.println(_currentUpload->totalSize); + DEBUG_OUTPUT.print("End File: "); + DEBUG_OUTPUT.print(_currentUpload->filename); + DEBUG_OUTPUT.print(" Type: "); + DEBUG_OUTPUT.print(_currentUpload->type); + DEBUG_OUTPUT.print(" Size: "); + DEBUG_OUTPUT.println(_currentUpload->totalSize); #endif - line = client.readStringUntil(0x0D); - client.readStringUntil(0x0A); - if (line == "--"){ + line = client.readStringUntil(0x0D); + client.readStringUntil(0x0A); + if (line == "--") + { #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Done Parsing POST"); + DEBUG_OUTPUT.println("Done Parsing POST"); #endif - break; - } - continue; - } else { - _uploadWriteByte(0x0D); - _uploadWriteByte(0x0A); - _uploadWriteByte((uint8_t)('-')); - _uploadWriteByte((uint8_t)('-')); - uint32_t i = 0; - while(i < boundary.length()){ - _uploadWriteByte(endBuf[i++]); + break; + } + continue; + } + else + { + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + _uploadWriteByte((uint8_t)('-')); + _uploadWriteByte((uint8_t)('-')); + uint32_t i = 0; + while (i < boundary.length()) + { + _uploadWriteByte(endBuf[i++]); + } + argByte = _uploadReadByte(client); + goto readfile; + } + } + else + { + _uploadWriteByte(0x0D); + goto readfile; + } + break; + } } - argByte = _uploadReadByte(client); - goto readfile; - } - } else { - _uploadWriteByte(0x0D); - goto readfile; } - break; - } } - } - } - int iarg; - int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount)?(WEBSERVER_MAX_POST_ARGS - _postArgsLen):_currentArgCount; - for (iarg = 0; iarg < totalArgs; iarg++){ - RequestArgument& arg = _postArgs[_postArgsLen++]; - arg.key = _currentArgs[iarg].key; - arg.value = _currentArgs[iarg].value; - } - if (_currentArgs) delete[] _currentArgs; - _currentArgs = new RequestArgument[_postArgsLen]; - for (iarg = 0; iarg < _postArgsLen; iarg++){ - RequestArgument& arg = _currentArgs[iarg]; - arg.key = _postArgs[iarg].key; - arg.value = _postArgs[iarg].value; - } - _currentArgCount = iarg; - if (_postArgs) { - delete[] _postArgs; - _postArgs = nullptr; - _postArgsLen = 0; + int iarg; + int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount) ? (WEBSERVER_MAX_POST_ARGS - _postArgsLen) : _currentArgCount; + for (iarg = 0; iarg < totalArgs; iarg++) + { + RequestArgument& arg = _postArgs[_postArgsLen++]; + arg.key = _currentArgs[iarg].key; + arg.value = _currentArgs[iarg].value; + } + if (_currentArgs) + { + delete[] _currentArgs; + } + _currentArgs = new RequestArgument[_postArgsLen]; + for (iarg = 0; iarg < _postArgsLen; iarg++) + { + RequestArgument& arg = _currentArgs[iarg]; + arg.key = _postArgs[iarg].key; + arg.value = _postArgs[iarg].value; + } + _currentArgCount = iarg; + if (_postArgs) + { + delete[] _postArgs; + _postArgs = nullptr; + _postArgsLen = 0; + } + return true; } - return true; - } #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Error: line: "); - DEBUG_OUTPUT.println(line); + DEBUG_OUTPUT.print("Error: line: "); + DEBUG_OUTPUT.println(line); #endif - return false; + return false; } String ESP8266WebServer::urlDecode(const String& text) { - String decoded = ""; - char temp[] = "0x00"; - unsigned int len = text.length(); - unsigned int i = 0; - while (i < len) - { - char decodedChar; - char encodedChar = text.charAt(i++); - if ((encodedChar == '%') && (i + 1 < len)) + String decoded = ""; + char temp[] = "0x00"; + unsigned int len = text.length(); + unsigned int i = 0; + while (i < len) { - temp[2] = text.charAt(i++); - temp[3] = text.charAt(i++); - - decodedChar = strtol(temp, NULL, 16); - } - else { - if (encodedChar == '+') - { - decodedChar = ' '; - } - else { - decodedChar = encodedChar; // normal ascii char - } + char decodedChar; + char encodedChar = text.charAt(i++); + if ((encodedChar == '%') && (i + 1 < len)) + { + temp[2] = text.charAt(i++); + temp[3] = text.charAt(i++); + + decodedChar = strtol(temp, NULL, 16); + } + else + { + if (encodedChar == '+') + { + decodedChar = ' '; + } + else + { + decodedChar = encodedChar; // normal ascii char + } + } + decoded += decodedChar; } - decoded += decodedChar; - } - return decoded; + return decoded; } -bool ESP8266WebServer::_parseFormUploadAborted(){ - _currentUpload->status = UPLOAD_FILE_ABORTED; - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, *_currentUpload); - return false; +bool ESP8266WebServer::_parseFormUploadAborted() +{ + _currentUpload->status = UPLOAD_FILE_ABORTED; + if (_currentHandler && _currentHandler->canUpload(_currentUri)) + { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } + return false; } diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandler.h b/libraries/ESP8266WebServer/src/detail/RequestHandler.h index fc5d0371d2..fdbd843fb6 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandler.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandler.h @@ -1,16 +1,43 @@ #ifndef REQUESTHANDLER_H #define REQUESTHANDLER_H -class RequestHandler { +class RequestHandler +{ public: virtual ~RequestHandler() { } - virtual bool canHandle(HTTPMethod method, String uri) { (void) method; (void) uri; return false; } - virtual bool canUpload(String uri) { (void) uri; return false; } - virtual bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; } - virtual void upload(ESP8266WebServer& server, String requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; } + virtual bool canHandle(HTTPMethod method, String uri) + { + (void) method; + (void) uri; + return false; + } + virtual bool canUpload(String uri) + { + (void) uri; + return false; + } + virtual bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) + { + (void) server; + (void) requestMethod; + (void) requestUri; + return false; + } + virtual void upload(ESP8266WebServer& server, String requestUri, HTTPUpload& upload) + { + (void) server; + (void) requestUri; + (void) upload; + } - RequestHandler* next() { return _next; } - void next(RequestHandler* r) { _next = r; } + RequestHandler* next() + { + return _next; + } + void next(RequestHandler* r) + { + _next = r; + } private: RequestHandler* _next = nullptr; diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h index feada8c13b..bdf6039ae4 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h @@ -7,47 +7,62 @@ using namespace mime; -class FunctionRequestHandler : public RequestHandler { +class FunctionRequestHandler : public RequestHandler +{ public: FunctionRequestHandler(ESP8266WebServer::THandlerFunction fn, ESP8266WebServer::THandlerFunction ufn, const String &uri, HTTPMethod method) - : _fn(fn) - , _ufn(ufn) - , _uri(uri) - , _method(method) + : _fn(fn) + , _ufn(ufn) + , _uri(uri) + , _method(method) { } - bool canHandle(HTTPMethod requestMethod, String requestUri) override { + bool canHandle(HTTPMethod requestMethod, String requestUri) override + { if (_method != HTTP_ANY && _method != requestMethod) + { return false; + } if (requestUri != _uri) + { return false; + } return true; } - bool canUpload(String requestUri) override { + bool canUpload(String requestUri) override + { if (!_ufn || !canHandle(HTTP_POST, requestUri)) + { return false; + } return true; } - bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { + bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override + { (void) server; if (!canHandle(requestMethod, requestUri)) + { return false; + } _fn(); return true; } - void upload(ESP8266WebServer& server, String requestUri, HTTPUpload& upload) override { + void upload(ESP8266WebServer& server, String requestUri, HTTPUpload& upload) override + { (void) server; (void) upload; if (canUpload(requestUri)) + { _ufn(); + } } protected: @@ -57,42 +72,54 @@ class FunctionRequestHandler : public RequestHandler { HTTPMethod _method; }; -class StaticRequestHandler : public RequestHandler { +class StaticRequestHandler : public RequestHandler +{ public: StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header) - : _fs(fs) - , _uri(uri) - , _path(path) - , _cache_header(cache_header) + : _fs(fs) + , _uri(uri) + , _path(path) + , _cache_header(cache_header) { _isFile = fs.exists(path); DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header); _baseUriLength = _uri.length(); } - bool canHandle(HTTPMethod requestMethod, String requestUri) override { + bool canHandle(HTTPMethod requestMethod, String requestUri) override + { if (requestMethod != HTTP_GET) + { return false; + } if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri)) + { return false; + } return true; } - bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { + bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override + { if (!canHandle(requestMethod, requestUri)) + { return false; + } DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str()); String path(_path); - if (!_isFile) { + if (!_isFile) + { // Base URI doesn't point to a file. // If a directory is requested, look for index file. - if (requestUri.endsWith("/")) - requestUri += "index.htm"; + if (requestUri.endsWith("/")) + { + requestUri += "index.htm"; + } // Append whatever follows this URI in request to get the file path. path += requestUri.substring(_baseUriLength); @@ -103,35 +130,45 @@ class StaticRequestHandler : public RequestHandler { // look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for // if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc... - if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !_fs.exists(path)) { + if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !_fs.exists(path)) + { String pathWithGz = path + FPSTR(mimeTable[gz].endsWith); - if(_fs.exists(pathWithGz)) + if (_fs.exists(pathWithGz)) + { path += FPSTR(mimeTable[gz].endsWith); + } } File f = _fs.open(path, "r"); if (!f) + { return false; + } if (_cache_header.length() != 0) + { server.sendHeader("Cache-Control", _cache_header); + } server.streamFile(f, contentType); return true; } - static String getContentType(const String& path) { + static String getContentType(const String& path) + { char buff[sizeof(mimeTable[0].mimeType)]; // Check all entries but last one for match, return if found - for (size_t i=0; i < sizeof(mimeTable)/sizeof(mimeTable[0])-1; i++) { + for (size_t i = 0; i < sizeof(mimeTable) / sizeof(mimeTable[0]) - 1; i++) + { strcpy_P(buff, mimeTable[i].endsWith); - if (path.endsWith(buff)) { + if (path.endsWith(buff)) + { strcpy_P(buff, mimeTable[i].mimeType); return String(buff); } } // Fall-through and just return default type - strcpy_P(buff, mimeTable[sizeof(mimeTable)/sizeof(mimeTable[0])-1].mimeType); + strcpy_P(buff, mimeTable[sizeof(mimeTable) / sizeof(mimeTable[0]) - 1].mimeType); return String(buff); } diff --git a/libraries/ESP8266WebServer/src/detail/mimetable.cpp b/libraries/ESP8266WebServer/src/detail/mimetable.cpp index d646857a8a..25eceeff71 100644 --- a/libraries/ESP8266WebServer/src/detail/mimetable.cpp +++ b/libraries/ESP8266WebServer/src/detail/mimetable.cpp @@ -5,7 +5,7 @@ namespace mime { // Table of extension->MIME strings stored in PROGMEM, needs to be global due to GCC section typing rules -const Entry mimeTable[maxType] PROGMEM = +const Entry mimeTable[maxType] PROGMEM = { { ".html", "text/html" }, { ".htm", "text/html" }, @@ -29,7 +29,7 @@ const Entry mimeTable[maxType] PROGMEM = { ".zip", "application/zip" }, { ".gz", "application/x-gzip" }, { ".appcache", "text/cache-manifest" }, - { "", "application/octet-stream" } + { "", "application/octet-stream" } }; } diff --git a/libraries/ESP8266WebServer/src/detail/mimetable.h b/libraries/ESP8266WebServer/src/detail/mimetable.h index 191356c489..d5723eaad4 100644 --- a/libraries/ESP8266WebServer/src/detail/mimetable.h +++ b/libraries/ESP8266WebServer/src/detail/mimetable.h @@ -7,36 +7,36 @@ namespace mime enum type { - html, - htm, - css, - txt, - js, - json, - png, - gif, - jpg, - ico, - svg, - ttf, - otf, - woff, - woff2, - eot, - sfnt, - xml, - pdf, - zip, - gz, - appcache, - none, - maxType + html, + htm, + css, + txt, + js, + json, + png, + gif, + jpg, + ico, + svg, + ttf, + otf, + woff, + woff2, + eot, + sfnt, + xml, + pdf, + zip, + gz, + appcache, + none, + maxType }; struct Entry { - const char endsWith[16]; - const char mimeType[32]; + const char endsWith[16]; + const char mimeType[32]; }; diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp b/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp index 8698df5972..fca036b33a 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp @@ -1,23 +1,23 @@ /* - WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries - - Mostly compatible with Arduino WiFi shield library and standard + WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries + - Mostly compatible with Arduino WiFi shield library and standard WiFiClient/ServerSecure (except for certificate handling). - Copyright (c) 2018 Earle F. Philhower, III + Copyright (c) 2018 Earle F. Philhower, III - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include @@ -30,81 +30,90 @@ #include #include "BearSSLHelpers.h" -namespace brssl { - // Code here is pulled from brssl sources, with the copyright and license - // shown below. I've rewritten things using C++ semantics and removed - // custom VEC_* calls (std::vector to the rescue) and adjusted things to - // allow for long-running operation (i.e. some memory issues when DERs - // passed into the decoders). Bugs are most likely my fault. - - // Original (c) message follows: - /* - Copyright (c) 2016 Thomas Pornin - - 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. - */ - - class private_key { - public: +namespace brssl +{ +// Code here is pulled from brssl sources, with the copyright and license +// shown below. I've rewritten things using C++ semantics and removed +// custom VEC_* calls (std::vector to the rescue) and adjusted things to +// allow for long-running operation (i.e. some memory issues when DERs +// passed into the decoders). Bugs are most likely my fault. + +// Original (c) message follows: +/* + Copyright (c) 2016 Thomas Pornin + + 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. +*/ + +class private_key +{ +public: int key_type; /* BR_KEYTYPE_RSA or BR_KEYTYPE_EC */ - union { - br_rsa_private_key rsa; - br_ec_private_key ec; + union + { + br_rsa_private_key rsa; + br_ec_private_key ec; } key; - }; +}; - class public_key { - public: +class public_key +{ +public: int key_type; /* BR_KEYTYPE_RSA or BR_KEYTYPE_EC */ - union { - br_rsa_public_key rsa; - br_ec_public_key ec; + union + { + br_rsa_public_key rsa; + br_ec_public_key ec; } key; - }; +}; - class pem_object { - public: +class pem_object +{ +public: char *name; unsigned char *data; size_t data_len; - }; - - // Forward definitions - void free_ta_contents(br_x509_trust_anchor *ta); - void free_public_key(public_key *pk); - void free_private_key(private_key *sk); - bool looks_like_DER(const unsigned char *buf, size_t len); - pem_object *decode_pem(const void *src, size_t len, size_t *num); - void free_pem_object_contents(pem_object *po); - - // Used as callback multiple places to append a string to a vector - static void byte_vector_append(void *ctx, const void *buff, size_t len) { +}; + +// Forward definitions +void free_ta_contents(br_x509_trust_anchor *ta); +void free_public_key(public_key *pk); +void free_private_key(private_key *sk); +bool looks_like_DER(const unsigned char *buf, size_t len); +pem_object *decode_pem(const void *src, size_t len, size_t *num); +void free_pem_object_contents(pem_object *po); + +// Used as callback multiple places to append a string to a vector +static void byte_vector_append(void *ctx, const void *buff, size_t len) +{ std::vector *vec = static_cast*>(ctx); vec->reserve(vec->size() + len); // Allocate extra space all at once - for (size_t i = 0; i < len; i++) { - vec->push_back(((uint8_t*)buff)[i]); + for (size_t i = 0; i < len; i++) + { + vec->push_back(((uint8_t*)buff)[i]); } - } +} - static bool certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta, const br_x509_certificate *xc) { +static bool certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta, const br_x509_certificate *xc) +{ std::unique_ptr dc(new br_x509_decoder_context); // auto-delete on exit std::vector vdn; br_x509_pkey *pk; @@ -115,132 +124,161 @@ namespace brssl { br_x509_decoder_init(dc.get(), byte_vector_append, (void*)&vdn, 0, 0); br_x509_decoder_push(dc.get(), xc->data, xc->data_len); pk = br_x509_decoder_get_pkey(dc.get()); - if (pk == nullptr) { - return false; // No key present, something broken in the cert! + if (pk == nullptr) + { + return false; // No key present, something broken in the cert! } // Copy the raw certificate data ta->dn.data = (uint8_t*)malloc(vdn.size()); - if (!ta->dn.data) { - return false; // OOM, but nothing yet allocated + if (!ta->dn.data) + { + return false; // OOM, but nothing yet allocated } memcpy(ta->dn.data, &vdn[0], vdn.size()); ta->dn.len = vdn.size(); ta->flags = 0; - if (br_x509_decoder_isCA(dc.get())) { - ta->flags |= BR_X509_TA_CA; + if (br_x509_decoder_isCA(dc.get())) + { + ta->flags |= BR_X509_TA_CA; } // Extract the public key - switch (pk->key_type) { - case BR_KEYTYPE_RSA: + switch (pk->key_type) + { + case BR_KEYTYPE_RSA: ta->pkey.key_type = BR_KEYTYPE_RSA; ta->pkey.key.rsa.n = (uint8_t*)malloc(pk->key.rsa.nlen); ta->pkey.key.rsa.e = (uint8_t*)malloc(pk->key.rsa.elen); - if ((ta->pkey.key.rsa.n == nullptr) || (ta->pkey.key.rsa.e == nullptr)) { - free_ta_contents(ta); // OOM, so clean up - return false; + if ((ta->pkey.key.rsa.n == nullptr) || (ta->pkey.key.rsa.e == nullptr)) + { + free_ta_contents(ta); // OOM, so clean up + return false; } memcpy(ta->pkey.key.rsa.n, pk->key.rsa.n, pk->key.rsa.nlen); ta->pkey.key.rsa.nlen = pk->key.rsa.nlen; memcpy(ta->pkey.key.rsa.e, pk->key.rsa.e, pk->key.rsa.elen); ta->pkey.key.rsa.elen = pk->key.rsa.elen; return true; - case BR_KEYTYPE_EC: + case BR_KEYTYPE_EC: ta->pkey.key_type = BR_KEYTYPE_EC; ta->pkey.key.ec.curve = pk->key.ec.curve; ta->pkey.key.ec.q = (uint8_t*)malloc(pk->key.ec.qlen); - if (ta->pkey.key.ec.q == nullptr) { - free_ta_contents(ta); // OOM, so clean up - return false; + if (ta->pkey.key.ec.q == nullptr) + { + free_ta_contents(ta); // OOM, so clean up + return false; } memcpy(ta->pkey.key.ec.q, pk->key.ec.q, pk->key.ec.qlen); ta->pkey.key.ec.qlen = pk->key.ec.qlen; return true; - default: + default: free_ta_contents(ta); // Unknown key type return false; } // Should never get here, if so there was an unknown error return false; - } +} - br_x509_trust_anchor *certificate_to_trust_anchor(const br_x509_certificate *xc) { +br_x509_trust_anchor *certificate_to_trust_anchor(const br_x509_certificate *xc) +{ br_x509_trust_anchor *ta = (br_x509_trust_anchor*)malloc(sizeof(br_x509_trust_anchor)); - if (!ta) { - return nullptr; + if (!ta) + { + return nullptr; } - if (!certificate_to_trust_anchor_inner(ta, xc)) { - free(ta); - return nullptr; + if (!certificate_to_trust_anchor_inner(ta, xc)) + { + free(ta); + return nullptr; } return ta; - } - - void free_ta_contents(br_x509_trust_anchor *ta) { - if (ta) { - free(ta->dn.data); - if (ta->pkey.key_type == BR_KEYTYPE_RSA) { - free(ta->pkey.key.rsa.n); - free(ta->pkey.key.rsa.e); - } else if (ta->pkey.key_type == BR_KEYTYPE_EC) { - free(ta->pkey.key.ec.q); - } - memset(ta, 0, sizeof(*ta)); - } - } - - // Checks if a bitstream looks like a valid DER(binary) encoding. - // Basically tries to verify the length of all included segments - // matches the length of the input buffer. Does not actually - // validate any contents. - bool looks_like_DER(const unsigned char *buff, size_t len) { - if (len < 2) { - return false; - } - if (pgm_read_byte(buff++) != 0x30) { - return false; +} + +void free_ta_contents(br_x509_trust_anchor *ta) +{ + if (ta) + { + free(ta->dn.data); + if (ta->pkey.key_type == BR_KEYTYPE_RSA) + { + free(ta->pkey.key.rsa.n); + free(ta->pkey.key.rsa.e); + } + else if (ta->pkey.key_type == BR_KEYTYPE_EC) + { + free(ta->pkey.key.ec.q); + } + memset(ta, 0, sizeof(*ta)); + } +} + +// Checks if a bitstream looks like a valid DER(binary) encoding. +// Basically tries to verify the length of all included segments +// matches the length of the input buffer. Does not actually +// validate any contents. +bool looks_like_DER(const unsigned char *buff, size_t len) +{ + if (len < 2) + { + return false; + } + if (pgm_read_byte(buff++) != 0x30) + { + return false; } int fb = pgm_read_byte(buff++); len -= 2; - if (fb < 0x80) { - return (size_t)fb == len; - } else if (fb == 0x80) { - return false; - } else { - fb -= 0x80; - if (len < (size_t)fb + 2) { + if (fb < 0x80) + { + return (size_t)fb == len; + } + else if (fb == 0x80) + { return false; - } - len -= (size_t)fb; - size_t dlen = 0; - while (fb -- > 0) { - if (dlen > (len >> 8)) { - return false; + } + else + { + fb -= 0x80; + if (len < (size_t)fb + 2) + { + return false; + } + len -= (size_t)fb; + size_t dlen = 0; + while (fb -- > 0) + { + if (dlen > (len >> 8)) + { + return false; + } + dlen = (dlen << 8) + (size_t)pgm_read_byte(buff++); } - dlen = (dlen << 8) + (size_t)pgm_read_byte(buff++); - } - return dlen == len; + return dlen == len; } - } +} - void free_pem_object_contents(pem_object *po) { - if (po) { - free(po->name); - free(po->data); +void free_pem_object_contents(pem_object *po) +{ + if (po) + { + free(po->name); + free(po->data); } - } +} - // Converts a PEM (~=base64) source into a set of DER-encoded binary blobs. - // Each blob is named by the ---- BEGIN xxx ---- field, and multiple - // blobs may be returned. - pem_object *decode_pem(const void *src, size_t len, size_t *num) { +// Converts a PEM (~=base64) source into a set of DER-encoded binary blobs. +// Each blob is named by the ---- BEGIN xxx ---- field, and multiple +// blobs may be returned. +pem_object *decode_pem(const void *src, size_t len, size_t *num) +{ std::vector pem_list; std::unique_ptr pc(new br_pem_decoder_context); // auto-delete on exit - if (!pc.get()) { - return nullptr; + if (!pc.get()) + { + return nullptr; } pem_object po, *pos; const unsigned char *buff; @@ -255,76 +293,86 @@ namespace brssl { bool inobj = false; bool extra_nl = true; - while (len > 0) { - size_t tlen; + while (len > 0) + { + size_t tlen; - tlen = br_pem_decoder_push(pc.get(), buff, len); - buff += tlen; - len -= tlen; - switch (br_pem_decoder_event(pc.get())) { + tlen = br_pem_decoder_push(pc.get(), buff, len); + buff += tlen; + len -= tlen; + switch (br_pem_decoder_event(pc.get())) + { case BR_PEM_BEGIN_OBJ: - po.name = strdup(br_pem_decoder_name(pc.get())); - br_pem_decoder_setdest(pc.get(), byte_vector_append, &bv); - inobj = true; - break; + po.name = strdup(br_pem_decoder_name(pc.get())); + br_pem_decoder_setdest(pc.get(), byte_vector_append, &bv); + inobj = true; + break; case BR_PEM_END_OBJ: - if (inobj) { - // Stick data into the vector - po.data = (uint8_t*)malloc(bv.size()); - if (po.data) { - memcpy(po.data, &bv[0], bv.size()); - po.data_len = bv.size(); - pem_list.push_back(po); + if (inobj) + { + // Stick data into the vector + po.data = (uint8_t*)malloc(bv.size()); + if (po.data) + { + memcpy(po.data, &bv[0], bv.size()); + po.data_len = bv.size(); + pem_list.push_back(po); + } + // Clean up state for next blob processing + bv.clear(); + po.name = nullptr; + po.data = nullptr; + po.data_len = 0; + inobj = false; } - // Clean up state for next blob processing - bv.clear(); - po.name = nullptr; - po.data = nullptr; - po.data_len = 0; - inobj = false; - } - break; + break; case BR_PEM_ERROR: - free(po.name); - for (size_t i = 0; i < pem_list.size(); i++) { - free_pem_object_contents(&pem_list[i]); - } - return nullptr; + free(po.name); + for (size_t i = 0; i < pem_list.size(); i++) + { + free_pem_object_contents(&pem_list[i]); + } + return nullptr; default: - // Do nothing here, the parser is still working on things - break; - } + // Do nothing here, the parser is still working on things + break; + } - if (len == 0 && extra_nl) { - extra_nl = false; - buff = (const unsigned char *)"\n"; - len = 1; - } + if (len == 0 && extra_nl) + { + extra_nl = false; + buff = (const unsigned char *)"\n"; + len = 1; + } } - if (inobj) { - free(po.name); - for (size_t i = 0; i < pem_list.size(); i++) { - free_pem_object_contents(&pem_list[i]); - } - return nullptr; + if (inobj) + { + free(po.name); + for (size_t i = 0; i < pem_list.size(); i++) + { + free_pem_object_contents(&pem_list[i]); + } + return nullptr; } pos = (pem_object*)malloc((1 + pem_list.size()) * sizeof(*pos)); - if (pos) { - *num = pem_list.size(); - pem_list.push_back(po); // Null-terminate list - memcpy(pos, &pem_list[0], pem_list.size() * sizeof(*pos)); + if (pos) + { + *num = pem_list.size(); + pem_list.push_back(po); // Null-terminate list + memcpy(pos, &pem_list[0], pem_list.size() * sizeof(*pos)); } return pos; - } +} - // Parse out DER or PEM encoded certificates from a binary buffer, - // potentially stored in PROGMEM. - br_x509_certificate *read_certificates(const char *buff, size_t len, size_t *num) { +// Parse out DER or PEM encoded certificates from a binary buffer, +// potentially stored in PROGMEM. +br_x509_certificate *read_certificates(const char *buff, size_t len, size_t *num) +{ std::vector cert_list; pem_object *pos; size_t u, num_pos; @@ -333,75 +381,90 @@ namespace brssl { *num = 0; - if (looks_like_DER((const unsigned char *)buff, len)) { - xcs = (br_x509_certificate*)malloc(2 * sizeof(*xcs)); - if (!xcs) { - return nullptr; - } - xcs[0].data = (uint8_t*)malloc(len); - if (!xcs[0].data) { - free(xcs); - return nullptr; - } - memcpy_P(xcs[0].data, buff, len); - xcs[0].data_len = len; - xcs[1].data = nullptr; - xcs[1].data_len = 0; - *num = 1; - return xcs; + if (looks_like_DER((const unsigned char *)buff, len)) + { + xcs = (br_x509_certificate*)malloc(2 * sizeof(*xcs)); + if (!xcs) + { + return nullptr; + } + xcs[0].data = (uint8_t*)malloc(len); + if (!xcs[0].data) + { + free(xcs); + return nullptr; + } + memcpy_P(xcs[0].data, buff, len); + xcs[0].data_len = len; + xcs[1].data = nullptr; + xcs[1].data_len = 0; + *num = 1; + return xcs; } pos = decode_pem(buff, len, &num_pos); - if (!pos) { - return nullptr; + if (!pos) + { + return nullptr; } - for (u = 0; u < num_pos; u ++) { - if (!strcmp_P(pos[u].name, PSTR("CERTIFICATE")) || !strcmp_P(pos[u].name, PSTR("X509 CERTIFICATE"))) { - br_x509_certificate xc; - xc.data = pos[u].data; - xc.data_len = pos[u].data_len; - pos[u].data = nullptr; // Don't free the data we moved to the xc vector! - cert_list.push_back(xc); - } + for (u = 0; u < num_pos; u ++) + { + if (!strcmp_P(pos[u].name, PSTR("CERTIFICATE")) || !strcmp_P(pos[u].name, PSTR("X509 CERTIFICATE"))) + { + br_x509_certificate xc; + xc.data = pos[u].data; + xc.data_len = pos[u].data_len; + pos[u].data = nullptr; // Don't free the data we moved to the xc vector! + cert_list.push_back(xc); + } } - for (u = 0; u < num_pos; u ++) { - free_pem_object_contents(&pos[u]); + for (u = 0; u < num_pos; u ++) + { + free_pem_object_contents(&pos[u]); } free(pos); - if (cert_list.size() == 0) { - return nullptr; + if (cert_list.size() == 0) + { + return nullptr; } *num = cert_list.size(); dummy.data = nullptr; dummy.data_len = 0; cert_list.push_back(dummy); xcs = (br_x509_certificate*)malloc(cert_list.size() * sizeof(*xcs)); - if (!xcs) { - for (size_t i = 0; i < cert_list.size(); i++) { - free(cert_list[i].data); // Clean up any captured data blobs - } - return nullptr; + if (!xcs) + { + for (size_t i = 0; i < cert_list.size(); i++) + { + free(cert_list[i].data); // Clean up any captured data blobs + } + return nullptr; } memcpy(xcs, &cert_list[0], cert_list.size() * sizeof(br_x509_certificate)); // XCS now has [].data pointing to the previously allocated blobs, so don't // want to free anything in cert_list[]. return xcs; - } +} - void free_certificates(br_x509_certificate *certs, size_t num) { - if (certs) { - for (size_t u = 0; u < num; u ++) { - free(certs[u].data); - } - free(certs); +void free_certificates(br_x509_certificate *certs, size_t num) +{ + if (certs) + { + for (size_t u = 0; u < num; u ++) + { + free(certs[u].data); + } + free(certs); } - } +} - static public_key *decode_public_key(const unsigned char *buff, size_t len) { +static public_key *decode_public_key(const unsigned char *buff, size_t len) +{ std::unique_ptr dc(new br_pkey_decoder_context); // auto-delete on exit - if (!dc.get()) { - return nullptr; + if (!dc.get()) + { + return nullptr; } public_key *pk = nullptr; @@ -409,27 +472,31 @@ namespace brssl { br_pkey_decoder_init(dc.get()); br_pkey_decoder_push(dc.get(), buff, len); int err = br_pkey_decoder_last_error(dc.get()); - if (err != 0) { - return nullptr; + if (err != 0) + { + return nullptr; } const br_rsa_public_key *rk = nullptr; const br_ec_public_key *ek = nullptr; - switch (br_pkey_decoder_key_type(dc.get())) { - case BR_KEYTYPE_RSA: + switch (br_pkey_decoder_key_type(dc.get())) + { + case BR_KEYTYPE_RSA: rk = br_pkey_decoder_get_rsa(dc.get()); pk = (public_key*)malloc(sizeof * pk); - if (!pk) { - return nullptr; + if (!pk) + { + return nullptr; } pk->key_type = BR_KEYTYPE_RSA; pk->key.rsa.n = (uint8_t*)malloc(rk->nlen); pk->key.rsa.e = (uint8_t*)malloc(rk->elen); - if (!pk->key.rsa.n || !pk->key.rsa.e) { - free(pk->key.rsa.n); - free(pk->key.rsa.e); - free(pk); - return nullptr; + if (!pk->key.rsa.n || !pk->key.rsa.e) + { + free(pk->key.rsa.n); + free(pk->key.rsa.e); + free(pk); + return nullptr; } memcpy(pk->key.rsa.n, rk->n, rk->nlen); pk->key.rsa.nlen = rk->nlen; @@ -437,43 +504,52 @@ namespace brssl { pk->key.rsa.elen = rk->elen; return pk; - case BR_KEYTYPE_EC: + case BR_KEYTYPE_EC: ek = br_pkey_decoder_get_ec(dc.get()); pk = (public_key*)malloc(sizeof * pk); - if (!pk) { - return nullptr; + if (!pk) + { + return nullptr; } pk->key_type = BR_KEYTYPE_EC; pk->key.ec.q = (uint8_t*)malloc(ek->qlen); - if (!pk->key.ec.q) { - free(pk); - return nullptr; + if (!pk->key.ec.q) + { + free(pk); + return nullptr; } memcpy(pk->key.ec.q, ek->q, ek->qlen); pk->key.ec.qlen = ek->qlen; return pk; - default: + default: return nullptr; } - } +} - void free_public_key(public_key *pk) { - if (pk) { - if (pk->key_type == BR_KEYTYPE_RSA) { - free(pk->key.rsa.n); - free(pk->key.rsa.e); - } else if (pk->key_type == BR_KEYTYPE_EC) { - free(pk->key.ec.q); - } - free(pk); +void free_public_key(public_key *pk) +{ + if (pk) + { + if (pk->key_type == BR_KEYTYPE_RSA) + { + free(pk->key.rsa.n); + free(pk->key.rsa.e); + } + else if (pk->key_type == BR_KEYTYPE_EC) + { + free(pk->key.ec.q); + } + free(pk); } - } +} - static private_key *decode_private_key(const unsigned char *buff, size_t len) { +static private_key *decode_private_key(const unsigned char *buff, size_t len) +{ std::unique_ptr dc(new br_skey_decoder_context); // auto-delete on exit - if (!dc.get()) { - return nullptr; + if (!dc.get()) + { + return nullptr; } private_key *sk = nullptr; @@ -481,18 +557,21 @@ namespace brssl { br_skey_decoder_init(dc.get()); br_skey_decoder_push(dc.get(), buff, len); int err = br_skey_decoder_last_error(dc.get()); - if (err != 0) { - return nullptr; + if (err != 0) + { + return nullptr; } const br_rsa_private_key *rk = nullptr; const br_ec_private_key *ek = nullptr; - switch (br_skey_decoder_key_type(dc.get())) { - case BR_KEYTYPE_RSA: + switch (br_skey_decoder_key_type(dc.get())) + { + case BR_KEYTYPE_RSA: rk = br_skey_decoder_get_rsa(dc.get()); sk = (private_key*)malloc(sizeof * sk); - if (!sk) { - return nullptr; + if (!sk) + { + return nullptr; } sk->key_type = BR_KEYTYPE_RSA; sk->key.rsa.p = (uint8_t*)malloc(rk->plen); @@ -500,9 +579,10 @@ namespace brssl { sk->key.rsa.dp = (uint8_t*)malloc(rk->dplen); sk->key.rsa.dq = (uint8_t*)malloc(rk->dqlen); sk->key.rsa.iq = (uint8_t*)malloc(rk->iqlen); - if (!sk->key.rsa.p || !sk->key.rsa.q || !sk->key.rsa.dp || !sk->key.rsa.dq || !sk->key.rsa.iq) { - free_private_key(sk); - return nullptr; + if (!sk->key.rsa.p || !sk->key.rsa.q || !sk->key.rsa.dp || !sk->key.rsa.dq || !sk->key.rsa.iq) + { + free_private_key(sk); + return nullptr; } sk->key.rsa.n_bitlen = rk->n_bitlen; memcpy(sk->key.rsa.p, rk->p, rk->plen); @@ -517,369 +597,455 @@ namespace brssl { sk->key.rsa.iqlen = rk->iqlen; return sk; - case BR_KEYTYPE_EC: + case BR_KEYTYPE_EC: ek = br_skey_decoder_get_ec(dc.get()); sk = (private_key*)malloc(sizeof * sk); sk->key_type = BR_KEYTYPE_EC; sk->key.ec.curve = ek->curve; sk->key.ec.x = (uint8_t*)malloc(ek->xlen); - if (!sk->key.ec.x) { - free_private_key(sk); - return nullptr; + if (!sk->key.ec.x) + { + free_private_key(sk); + return nullptr; } memcpy(sk->key.ec.x, ek->x, ek->xlen); sk->key.ec.xlen = ek->xlen; return sk; - default: + default: return nullptr; } - } +} - void free_private_key(private_key *sk) { - if (sk) { - switch (sk->key_type) { +void free_private_key(private_key *sk) +{ + if (sk) + { + switch (sk->key_type) + { case BR_KEYTYPE_RSA: - free(sk->key.rsa.p); - free(sk->key.rsa.q); - free(sk->key.rsa.dp); - free(sk->key.rsa.dq); - free(sk->key.rsa.iq); - break; + free(sk->key.rsa.p); + free(sk->key.rsa.q); + free(sk->key.rsa.dp); + free(sk->key.rsa.dq); + free(sk->key.rsa.iq); + break; case BR_KEYTYPE_EC: - free(sk->key.ec.x); - break; + free(sk->key.ec.x); + break; default: - // Could be an uninitted key, no sub elements to free - break; - } - free(sk); + // Could be an uninitted key, no sub elements to free + break; + } + free(sk); } - } +} - void free_pem_object(pem_object *pos) { - if (pos != nullptr) { - for (size_t u = 0; pos[u].name; u ++) { - free_pem_object_contents(&pos[u]); - } - free(pos); +void free_pem_object(pem_object *pos) +{ + if (pos != nullptr) + { + for (size_t u = 0; pos[u].name; u ++) + { + free_pem_object_contents(&pos[u]); + } + free(pos); } - } +} - private_key *read_private_key(const char *buff, size_t len) { +private_key *read_private_key(const char *buff, size_t len) +{ private_key *sk = nullptr; pem_object *pos = nullptr; - if (looks_like_DER((const unsigned char*)buff, len)) { - sk = decode_private_key((const unsigned char*)buff, len); - return sk; + if (looks_like_DER((const unsigned char*)buff, len)) + { + sk = decode_private_key((const unsigned char*)buff, len); + return sk; } size_t num; pos = decode_pem(buff, len, &num); - if (pos == nullptr) { - return nullptr; // PEM decode error - } - for (size_t u = 0; pos[u].name; u ++) { - const char *name = pos[u].name; - if (!strcmp_P(name, PSTR("RSA PRIVATE KEY")) || !strcmp_P(name, PSTR("EC PRIVATE KEY")) || !strcmp_P(name, PSTR("PRIVATE KEY"))) { - sk = decode_private_key(pos[u].data, pos[u].data_len); - free_pem_object(pos); - return sk; - } + if (pos == nullptr) + { + return nullptr; // PEM decode error + } + for (size_t u = 0; pos[u].name; u ++) + { + const char *name = pos[u].name; + if (!strcmp_P(name, PSTR("RSA PRIVATE KEY")) || !strcmp_P(name, PSTR("EC PRIVATE KEY")) || !strcmp_P(name, PSTR("PRIVATE KEY"))) + { + sk = decode_private_key(pos[u].data, pos[u].data_len); + free_pem_object(pos); + return sk; + } } // If we hit here, no match free_pem_object(pos); return nullptr; - } +} - public_key *read_public_key(const char *buff, size_t len) { +public_key *read_public_key(const char *buff, size_t len) +{ public_key *pk = nullptr; pem_object *pos = nullptr; - if (looks_like_DER((const unsigned char*)buff, len)) { - pk = decode_public_key((const unsigned char*)buff, len); - return pk; + if (looks_like_DER((const unsigned char*)buff, len)) + { + pk = decode_public_key((const unsigned char*)buff, len); + return pk; } size_t num; pos = decode_pem(buff, len, &num); - if (pos == nullptr) { - return nullptr; // PEM decode error - } - for (size_t u = 0; pos[u].name; u ++) { - const char *name = pos[u].name; - if (!strcmp_P(name, PSTR("RSA PUBLIC KEY")) || !strcmp_P(name, PSTR("EC PUBLIC KEY")) || !strcmp_P(name, PSTR("PUBLIC KEY"))) { - pk = decode_public_key(pos[u].data, pos[u].data_len); - free_pem_object(pos); - return pk; - } + if (pos == nullptr) + { + return nullptr; // PEM decode error + } + for (size_t u = 0; pos[u].name; u ++) + { + const char *name = pos[u].name; + if (!strcmp_P(name, PSTR("RSA PUBLIC KEY")) || !strcmp_P(name, PSTR("EC PUBLIC KEY")) || !strcmp_P(name, PSTR("PUBLIC KEY"))) + { + pk = decode_public_key(pos[u].data, pos[u].data_len); + free_pem_object(pos); + return pk; + } } // We hit here == no key found free_pem_object(pos); return pk; - } +} }; -namespace BearSSL { +namespace BearSSL +{ // ----- Public Key ----- -PublicKey::PublicKey() { - _key = nullptr; +PublicKey::PublicKey() +{ + _key = nullptr; } -PublicKey::PublicKey(const char *pemKey) { - _key = nullptr; - parse(pemKey); +PublicKey::PublicKey(const char *pemKey) +{ + _key = nullptr; + parse(pemKey); } -PublicKey::PublicKey(const uint8_t *derKey, size_t derLen) { - _key = nullptr; - parse(derKey, derLen); +PublicKey::PublicKey(const uint8_t *derKey, size_t derLen) +{ + _key = nullptr; + parse(derKey, derLen); } -PublicKey::~PublicKey() { - if (_key) { - brssl::free_public_key(_key); - } +PublicKey::~PublicKey() +{ + if (_key) + { + brssl::free_public_key(_key); + } } -bool PublicKey::parse(const char *pemKey) { - return parse((const uint8_t *)pemKey, strlen_P(pemKey)); +bool PublicKey::parse(const char *pemKey) +{ + return parse((const uint8_t *)pemKey, strlen_P(pemKey)); } -bool PublicKey::parse(const uint8_t *derKey, size_t derLen) { - if (_key) { - brssl::free_public_key(_key); - _key = nullptr; - } - _key = brssl::read_public_key((const char *)derKey, derLen); - return _key ? true : false; +bool PublicKey::parse(const uint8_t *derKey, size_t derLen) +{ + if (_key) + { + brssl::free_public_key(_key); + _key = nullptr; + } + _key = brssl::read_public_key((const char *)derKey, derLen); + return _key ? true : false; } -bool PublicKey::isRSA() const { - if (!_key || _key->key_type != BR_KEYTYPE_RSA) { - return false; - } - return true; +bool PublicKey::isRSA() const +{ + if (!_key || _key->key_type != BR_KEYTYPE_RSA) + { + return false; + } + return true; } -bool PublicKey::isEC() const { - if (!_key || _key->key_type != BR_KEYTYPE_EC) { - return false; - } - return true; +bool PublicKey::isEC() const +{ + if (!_key || _key->key_type != BR_KEYTYPE_EC) + { + return false; + } + return true; } -const br_rsa_public_key *PublicKey::getRSA() const { - if (!_key || _key->key_type != BR_KEYTYPE_RSA) { - return nullptr; - } - return &_key->key.rsa; +const br_rsa_public_key *PublicKey::getRSA() const +{ + if (!_key || _key->key_type != BR_KEYTYPE_RSA) + { + return nullptr; + } + return &_key->key.rsa; } -const br_ec_public_key *PublicKey::getEC() const { - if (!_key || _key->key_type != BR_KEYTYPE_EC) { - return nullptr; - } - return &_key->key.ec; +const br_ec_public_key *PublicKey::getEC() const +{ + if (!_key || _key->key_type != BR_KEYTYPE_EC) + { + return nullptr; + } + return &_key->key.ec; } // ----- Private Key ----- -PrivateKey::PrivateKey() { - _key = nullptr; +PrivateKey::PrivateKey() +{ + _key = nullptr; } -PrivateKey::PrivateKey(const char *pemKey) { - _key = nullptr; - parse(pemKey); +PrivateKey::PrivateKey(const char *pemKey) +{ + _key = nullptr; + parse(pemKey); } -PrivateKey::PrivateKey(const uint8_t *derKey, size_t derLen) { - _key = nullptr; - parse(derKey, derLen); +PrivateKey::PrivateKey(const uint8_t *derKey, size_t derLen) +{ + _key = nullptr; + parse(derKey, derLen); } -PrivateKey::~PrivateKey() { - if (_key) { - brssl::free_private_key(_key); - } +PrivateKey::~PrivateKey() +{ + if (_key) + { + brssl::free_private_key(_key); + } } -bool PrivateKey::parse(const char *pemKey) { - return parse((const uint8_t *)pemKey, strlen_P(pemKey)); +bool PrivateKey::parse(const char *pemKey) +{ + return parse((const uint8_t *)pemKey, strlen_P(pemKey)); } -bool PrivateKey::parse(const uint8_t *derKey, size_t derLen) { - if (_key) { - brssl::free_private_key(_key); - _key = nullptr; - } - _key = brssl::read_private_key((const char *)derKey, derLen); - return _key ? true : false; +bool PrivateKey::parse(const uint8_t *derKey, size_t derLen) +{ + if (_key) + { + brssl::free_private_key(_key); + _key = nullptr; + } + _key = brssl::read_private_key((const char *)derKey, derLen); + return _key ? true : false; } -bool PrivateKey::isRSA() const { - if (!_key || _key->key_type != BR_KEYTYPE_RSA) { - return false; - } - return true; +bool PrivateKey::isRSA() const +{ + if (!_key || _key->key_type != BR_KEYTYPE_RSA) + { + return false; + } + return true; } -bool PrivateKey::isEC() const { - if (!_key || _key->key_type != BR_KEYTYPE_EC) { - return false; - } - return true; +bool PrivateKey::isEC() const +{ + if (!_key || _key->key_type != BR_KEYTYPE_EC) + { + return false; + } + return true; } -const br_rsa_private_key *PrivateKey::getRSA() const { - if (!_key || _key->key_type != BR_KEYTYPE_RSA) { - return nullptr; - } - return &_key->key.rsa; +const br_rsa_private_key *PrivateKey::getRSA() const +{ + if (!_key || _key->key_type != BR_KEYTYPE_RSA) + { + return nullptr; + } + return &_key->key.rsa; } -const br_ec_private_key *PrivateKey::getEC() const { - if (!_key || _key->key_type != BR_KEYTYPE_EC) { - return nullptr; - } - return &_key->key.ec; +const br_ec_private_key *PrivateKey::getEC() const +{ + if (!_key || _key->key_type != BR_KEYTYPE_EC) + { + return nullptr; + } + return &_key->key.ec; } // ----- Certificate Lists ----- -X509List::X509List() { - _count = 0; - _cert = nullptr; - _ta = nullptr; +X509List::X509List() +{ + _count = 0; + _cert = nullptr; + _ta = nullptr; } -X509List::X509List(const char *pemCert) { - _count = 0; - _cert = nullptr; - _ta = nullptr; - append(pemCert); +X509List::X509List(const char *pemCert) +{ + _count = 0; + _cert = nullptr; + _ta = nullptr; + append(pemCert); } -X509List::X509List(const uint8_t *derCert, size_t derLen) { - _count = 0; - _cert = nullptr; - _ta = nullptr; - append(derCert, derLen); +X509List::X509List(const uint8_t *derCert, size_t derLen) +{ + _count = 0; + _cert = nullptr; + _ta = nullptr; + append(derCert, derLen); } -X509List::~X509List() { - brssl::free_certificates(_cert, _count); // also frees cert - for (size_t i = 0; i < _count; i++) { - brssl::free_ta_contents(&_ta[i]); - } - free(_ta); +X509List::~X509List() +{ + brssl::free_certificates(_cert, _count); // also frees cert + for (size_t i = 0; i < _count; i++) + { + brssl::free_ta_contents(&_ta[i]); + } + free(_ta); } -bool X509List::append(const char *pemCert) { - return append((const uint8_t *)pemCert, strlen_P(pemCert)); +bool X509List::append(const char *pemCert) +{ + return append((const uint8_t *)pemCert, strlen_P(pemCert)); } -bool X509List::append(const uint8_t *derCert, size_t derLen) { - size_t numCerts; - br_x509_certificate *newCerts = brssl::read_certificates((const char *)derCert, derLen, &numCerts); - if (!newCerts) { - return false; - } +bool X509List::append(const uint8_t *derCert, size_t derLen) +{ + size_t numCerts; + br_x509_certificate *newCerts = brssl::read_certificates((const char *)derCert, derLen, &numCerts); + if (!newCerts) + { + return false; + } - // Add in the certificates - br_x509_certificate *saveCert = _cert; - _cert = (br_x509_certificate*)realloc(_cert, (numCerts + _count) * sizeof(br_x509_certificate)); - if (!_cert) { + // Add in the certificates + br_x509_certificate *saveCert = _cert; + _cert = (br_x509_certificate*)realloc(_cert, (numCerts + _count) * sizeof(br_x509_certificate)); + if (!_cert) + { + free(newCerts); + _cert = saveCert; + return false; + } + memcpy(&_cert[_count], newCerts, numCerts * sizeof(br_x509_certificate)); free(newCerts); - _cert = saveCert; - return false; - } - memcpy(&_cert[_count], newCerts, numCerts * sizeof(br_x509_certificate)); - free(newCerts); - - // Build TAs for each certificate - br_x509_trust_anchor *saveTa = _ta; - _ta = (br_x509_trust_anchor*)realloc(_ta, (numCerts + _count) * sizeof(br_x509_trust_anchor)); - if (!_ta) { - _ta = saveTa; - return false; - } - for (size_t i = 0; i < numCerts; i++) { - br_x509_trust_anchor *newTa = brssl::certificate_to_trust_anchor(&_cert[_count + i]); - if (newTa) { - _ta[_count + i ] = *newTa; - free(newTa); - } else { - return false; // OOM + + // Build TAs for each certificate + br_x509_trust_anchor *saveTa = _ta; + _ta = (br_x509_trust_anchor*)realloc(_ta, (numCerts + _count) * sizeof(br_x509_trust_anchor)); + if (!_ta) + { + _ta = saveTa; + return false; + } + for (size_t i = 0; i < numCerts; i++) + { + br_x509_trust_anchor *newTa = brssl::certificate_to_trust_anchor(&_cert[_count + i]); + if (newTa) + { + _ta[_count + i ] = *newTa; + free(newTa); + } + else + { + return false; // OOM + } } - } - _count += numCerts; + _count += numCerts; - return true; + return true; } // SHA256 hash for updater -void HashSHA256::begin() { - br_sha256_init( &_cc ); - memset( _sha256, 0, sizeof(_sha256) ); +void HashSHA256::begin() +{ + br_sha256_init(&_cc); + memset(_sha256, 0, sizeof(_sha256)); } -void HashSHA256::add(const void *data, uint32_t len) { - br_sha256_update( &_cc, data, len ); +void HashSHA256::add(const void *data, uint32_t len) +{ + br_sha256_update(&_cc, data, len); } -void HashSHA256::end() { - br_sha256_out( &_cc, _sha256 ); +void HashSHA256::end() +{ + br_sha256_out(&_cc, _sha256); } -int HashSHA256::len() { - return sizeof(_sha256); +int HashSHA256::len() +{ + return sizeof(_sha256); } -const void *HashSHA256::hash() { - return (const void*) _sha256; +const void *HashSHA256::hash() +{ + return (const void*) _sha256; } // SHA256 verifier uint32_t SigningVerifier::length() { - if (!_pubKey) { - return 0; - } else if (_pubKey->isRSA()) { - return _pubKey->getRSA()->nlen; - } else if (_pubKey->isEC()) { - return _pubKey->getEC()->qlen; - } else { - return 0; - } -} - -bool SigningVerifier::verify(UpdaterHashClass *hash, const void *signature, uint32_t signatureLen) { - if (!_pubKey || !hash || !signature || signatureLen != length()) return false; - - if (_pubKey->isRSA()) { - bool ret; - unsigned char vrf[hash->len()]; - br_rsa_pkcs1_vrfy vrfy = br_rsa_pkcs1_vrfy_get_default(); - ret = vrfy((const unsigned char *)signature, signatureLen, NULL, sizeof(vrf), _pubKey->getRSA(), vrf); - if (!ret || memcmp(vrf, hash->hash(), sizeof(vrf)) ) { - return false; - } else { - return true; - } - } else { - br_ecdsa_vrfy vrfy = br_ecdsa_vrfy_raw_get_default(); - // The EC verifier actually does the compare, unlike the RSA one - return vrfy(br_ec_get_default(), hash->hash(), hash->len(), _pubKey->getEC(), (const unsigned char *)signature, signatureLen); - } + if (!_pubKey) + { + return 0; + } + else if (_pubKey->isRSA()) + { + return _pubKey->getRSA()->nlen; + } + else if (_pubKey->isEC()) + { + return _pubKey->getEC()->qlen; + } + else + { + return 0; + } +} + +bool SigningVerifier::verify(UpdaterHashClass *hash, const void *signature, uint32_t signatureLen) +{ + if (!_pubKey || !hash || !signature || signatureLen != length()) + { + return false; + } + + if (_pubKey->isRSA()) + { + bool ret; + unsigned char vrf[hash->len()]; + br_rsa_pkcs1_vrfy vrfy = br_rsa_pkcs1_vrfy_get_default(); + ret = vrfy((const unsigned char *)signature, signatureLen, NULL, sizeof(vrf), _pubKey->getRSA(), vrf); + if (!ret || memcmp(vrf, hash->hash(), sizeof(vrf))) + { + return false; + } + else + { + return true; + } + } + else + { + br_ecdsa_vrfy vrfy = br_ecdsa_vrfy_raw_get_default(); + // The EC verifier actually does the compare, unlike the RSA one + return vrfy(br_ec_get_default(), hash->hash(), hash->len(), _pubKey->getEC(), (const unsigned char *)signature, signatureLen); + } }; #if !CORE_MOCK diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.h b/libraries/ESP8266WiFi/src/BearSSLHelpers.h index b7c16b03eb..cf575fbc28 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.h +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.h @@ -1,23 +1,23 @@ /* - WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries - - Mostly compatible with Arduino WiFi shield library and standard + WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries + - Mostly compatible with Arduino WiFi shield library and standard WiFiClient/ServerSecure (except for certificate handling). - Copyright (c) 2018 Earle F. Philhower, III + Copyright (c) 2018 Earle F. Philhower, III - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _BEARSSLHELPERS_H @@ -28,18 +28,21 @@ // Internal opaque structures, not needed by user applications -namespace brssl { - class public_key; - class private_key; +namespace brssl +{ +class public_key; +class private_key; }; -namespace BearSSL { +namespace BearSSL +{ // Holds either a single public RSA or EC key for use when BearSSL wants a pubkey. // Copies all associated data so no need to keep input PEM/DER keys. // All inputs can be either in RAM or PROGMEM. -class PublicKey { - public: +class PublicKey +{ +public: PublicKey(); PublicKey(const char *pemKey); PublicKey(const uint8_t *derKey, size_t derLen); @@ -57,15 +60,16 @@ class PublicKey { // Disable the copy constructor, we're pointer based PublicKey(const PublicKey& that) = delete; - private: +private: brssl::public_key *_key; }; // Holds either a single private RSA or EC key for use when BearSSL wants a secretkey. // Copies all associated data so no need to keep input PEM/DER keys. // All inputs can be either in RAM or PROGMEM. -class PrivateKey { - public: +class PrivateKey +{ +public: PrivateKey(); PrivateKey(const char *pemKey); PrivateKey(const uint8_t *derKey, size_t derLen); @@ -83,7 +87,7 @@ class PrivateKey { // Disable the copy constructor, we're pointer based PrivateKey(const PrivateKey& that) = delete; - private: +private: brssl::private_key *_key; }; @@ -93,8 +97,9 @@ class PrivateKey { // for a more memory efficient way). // Copies all associated data so no need to keep input PEM/DER certs. // All inputs can be either in RAM or PROGMEM. -class X509List { - public: +class X509List +{ +public: X509List(); X509List(const char *pemCert); X509List(const uint8_t *derCert, size_t derLen); @@ -104,20 +109,23 @@ class X509List { bool append(const uint8_t *derCert, size_t derLen); // Accessors - size_t getCount() const { - return _count; + size_t getCount() const + { + return _count; } - const br_x509_certificate *getX509Certs() const { - return _cert; + const br_x509_certificate *getX509Certs() const + { + return _cert; } - const br_x509_trust_anchor *getTrustAnchors() const { - return _ta; + const br_x509_trust_anchor *getTrustAnchors() const + { + return _ta; } // Disable the copy constructor, we're pointer based X509List(const X509List& that) = delete; - private: +private: size_t _count; br_x509_certificate *_cert; br_x509_trust_anchor *_ta; @@ -127,52 +135,64 @@ class X509List { // significantly faster. Completely optional. class WiFiClientSecure; -class Session { - friend class WiFiClientSecure; +class Session +{ + friend class WiFiClientSecure; - public: - Session() { memset(&_session, 0, sizeof(_session)); } - private: - br_ssl_session_parameters *getSession() { return &_session; } +public: + Session() + { + memset(&_session, 0, sizeof(_session)); + } +private: + br_ssl_session_parameters *getSession() + { + return &_session; + } // The actual BearSSL ession information br_ssl_session_parameters _session; }; // Updater SHA256 hash and signature verification -class HashSHA256 : public UpdaterHashClass { - public: +class HashSHA256 : public UpdaterHashClass +{ +public: virtual void begin() override; virtual void add(const void *data, uint32_t len) override; virtual void end() override; virtual int len() override; virtual const void *hash() override; - private: +private: br_sha256_context _cc; unsigned char _sha256[32]; }; -class SigningVerifier : public UpdaterVerifyClass { - public: +class SigningVerifier : public UpdaterVerifyClass +{ +public: virtual uint32_t length() override; virtual bool verify(UpdaterHashClass *hash, const void *signature, uint32_t signatureLen) override; - public: - SigningVerifier(PublicKey *pubKey) { _pubKey = pubKey; } +public: + SigningVerifier(PublicKey *pubKey) + { + _pubKey = pubKey; + } - private: +private: PublicKey *_pubKey; }; - + // Stack thunked versions of calls extern "C" { -extern unsigned char *thunk_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len); -extern void thunk_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len); -extern unsigned char *thunk_br_ssl_engine_recvrec_buf( const br_ssl_engine_context *cc, size_t *len); -extern void thunk_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len); -extern unsigned char *thunk_br_ssl_engine_sendapp_buf( const br_ssl_engine_context *cc, size_t *len); -extern void thunk_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len); -extern unsigned char *thunk_br_ssl_engine_sendrec_buf( const br_ssl_engine_context *cc, size_t *len); -extern void thunk_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len); + extern unsigned char *thunk_br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len); + extern void thunk_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len); + extern unsigned char *thunk_br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len); + extern void thunk_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len); + extern unsigned char *thunk_br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len); + extern void thunk_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len); + extern unsigned char *thunk_br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len); + extern void thunk_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len); }; }; diff --git a/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp b/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp index dbcb6e1031..87e7e2d6fb 100644 --- a/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp @@ -1,20 +1,20 @@ /* - CertStoreBearSSL.cpp - Library for Arduino ESP8266 - Copyright (c) 2018 Earle F. Philhower, III - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + CertStoreBearSSL.cpp - Library for Arduino ESP8266 + Copyright (c) 2018 Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "CertStoreBearSSL.h" @@ -27,184 +27,213 @@ #define DEBUG_BSSL(...) #endif -namespace BearSSL { +namespace BearSSL +{ extern "C" { - // Callback for the x509 decoder - static void dn_append(void *ctx, const void *buf, size_t len) { - br_sha256_context *sha1 = (br_sha256_context*)ctx; - br_sha256_update(sha1, buf, len); - } + // Callback for the x509 decoder + static void dn_append(void *ctx, const void *buf, size_t len) + { + br_sha256_context *sha1 = (br_sha256_context*)ctx; + br_sha256_update(sha1, buf, len); + } } -CertStore::CertInfo CertStore::_preprocessCert(uint32_t length, uint32_t offset, const void *raw) { - CertStore::CertInfo ci; +CertStore::CertInfo CertStore::_preprocessCert(uint32_t length, uint32_t offset, const void *raw) +{ + CertStore::CertInfo ci; - // Clear the CertInfo - memset(&ci, 0, sizeof(ci)); + // Clear the CertInfo + memset(&ci, 0, sizeof(ci)); - // Process it using SHA256, same as the hashed_dn - br_x509_decoder_context *ctx = new br_x509_decoder_context; - br_sha256_context *sha256 = new br_sha256_context; - if (!ctx || !sha256) { - DEBUG_BSSL("CertStore::_preprocessCert: OOM\n"); - return ci; - } + // Process it using SHA256, same as the hashed_dn + br_x509_decoder_context *ctx = new br_x509_decoder_context; + br_sha256_context *sha256 = new br_sha256_context; + if (!ctx || !sha256) + { + DEBUG_BSSL("CertStore::_preprocessCert: OOM\n"); + return ci; + } - br_sha256_init(sha256); - br_x509_decoder_init(ctx, dn_append, sha256, nullptr, nullptr); - br_x509_decoder_push(ctx, (const void*)raw, length); + br_sha256_init(sha256); + br_x509_decoder_init(ctx, dn_append, sha256, nullptr, nullptr); + br_x509_decoder_push(ctx, (const void*)raw, length); - // Copy result to structure - br_sha256_out(sha256, &ci.sha256); - ci.length = length; - ci.offset = offset; + // Copy result to structure + br_sha256_out(sha256, &ci.sha256); + ci.length = length; + ci.offset = offset; - // Clean up allocated memory - delete sha256; - delete ctx; + // Clean up allocated memory + delete sha256; + delete ctx; - // Return result - return ci; + // Return result + return ci; } -// The certs.ar file is a UNIX ar format file, concatenating all the +// The certs.ar file is a UNIX ar format file, concatenating all the // individual certificates into a single blob in a space-efficient way. -int CertStore::initCertStore(CertStoreFile *index, CertStoreFile *data) { - int count = 0; - uint32_t offset = 0; +int CertStore::initCertStore(CertStoreFile *index, CertStoreFile *data) +{ + int count = 0; + uint32_t offset = 0; - _index = index; - _data = data; - - if (!_index || !data) { - return 0; - } - - if (!_index->open(true)) { - return 0; - } - - if (!_data->open(false)) { - _index->close(); - return 0; - } + _index = index; + _data = data; - char magic[8]; - if (_data->read(magic, sizeof(magic)) != sizeof(magic) || - memcmp(magic, "!\n", sizeof(magic)) ) { - _data->close(); - _index->close(); - return 0; - } - offset += sizeof(magic); - - while (true) { - char fileHeader[60]; - // 0..15 = filename in ASCII - // 48...57 = length in decimal ASCII - uint32_t length; - if (data->read(fileHeader, sizeof(fileHeader)) != sizeof(fileHeader)) { - break; - } - offset += sizeof(fileHeader); - fileHeader[58] = 0; - if (1 != sscanf(fileHeader + 48, "%d", &length) || !length) { - break; + if (!_index || !data) + { + return 0; } - void *raw = malloc(length); - if (!raw) { - break; - } - if (_data->read(raw, length) != (ssize_t)length) { - free(raw); - break; + if (!_index->open(true)) + { + return 0; } - // If the filename starts with "//" then this is a rename file, skip it - if (fileHeader[0] != '/' || fileHeader[1] != '/') { - CertStore::CertInfo ci = _preprocessCert(length, offset, raw); - if (_index->write(&ci, sizeof(ci)) != (ssize_t)sizeof(ci)) { - free(raw); - break; - } - count++; + if (!_data->open(false)) + { + _index->close(); + return 0; } - offset += length; - free(raw); - if (offset & 1) { - char x; - _data->read(&x, 1); - offset++; + char magic[8]; + if (_data->read(magic, sizeof(magic)) != sizeof(magic) || + memcmp(magic, "!\n", sizeof(magic))) + { + _data->close(); + _index->close(); + return 0; + } + offset += sizeof(magic); + + while (true) + { + char fileHeader[60]; + // 0..15 = filename in ASCII + // 48...57 = length in decimal ASCII + uint32_t length; + if (data->read(fileHeader, sizeof(fileHeader)) != sizeof(fileHeader)) + { + break; + } + offset += sizeof(fileHeader); + fileHeader[58] = 0; + if (1 != sscanf(fileHeader + 48, "%d", &length) || !length) + { + break; + } + + void *raw = malloc(length); + if (!raw) + { + break; + } + if (_data->read(raw, length) != (ssize_t)length) + { + free(raw); + break; + } + + // If the filename starts with "//" then this is a rename file, skip it + if (fileHeader[0] != '/' || fileHeader[1] != '/') + { + CertStore::CertInfo ci = _preprocessCert(length, offset, raw); + if (_index->write(&ci, sizeof(ci)) != (ssize_t)sizeof(ci)) + { + free(raw); + break; + } + count++; + } + + offset += length; + free(raw); + if (offset & 1) + { + char x; + _data->read(&x, 1); + offset++; + } } - } - _data->close(); - _index->close(); - return count; + _data->close(); + _index->close(); + return count; } -void CertStore::installCertStore(br_x509_minimal_context *ctx) { - br_x509_minimal_set_dynamic(ctx, (void*)this, findHashedTA, freeHashedTA); +void CertStore::installCertStore(br_x509_minimal_context *ctx) +{ + br_x509_minimal_set_dynamic(ctx, (void*)this, findHashedTA, freeHashedTA); } -const br_x509_trust_anchor *CertStore::findHashedTA(void *ctx, void *hashed_dn, size_t len) { - CertStore *cs = static_cast(ctx); - CertStore::CertInfo ci; - - if (!cs || len != sizeof(ci.sha256) || !cs->_index || !cs->_data) { - return nullptr; - } +const br_x509_trust_anchor *CertStore::findHashedTA(void *ctx, void *hashed_dn, size_t len) +{ + CertStore *cs = static_cast(ctx); + CertStore::CertInfo ci; - if (!cs->_index->open(false)) { - return nullptr; - } - - while (cs->_index->read(&ci, sizeof(ci)) == sizeof(ci)) { - if (!memcmp(ci.sha256, hashed_dn, sizeof(ci.sha256))) { - cs->_index->close(); - uint8_t *der = (uint8_t*)malloc(ci.length); - if (!der) { - return nullptr; - } - if (!cs->_data->open(false)) { - free(der); + if (!cs || len != sizeof(ci.sha256) || !cs->_index || !cs->_data) + { return nullptr; - } - if (!cs->_data->seek(ci.offset)) { - cs->_data->close(); - free(der); - return nullptr; - } - if (cs->_data->read(der, ci.length) != (ssize_t)ci.length) { - free(der); - return nullptr; - } - cs->_data->close(); - cs->_x509 = new X509List(der, ci.length); - free(der); - if (!cs->_x509) { - DEBUG_BSSL("CertStore::findHashedTA: OOM\n"); - return nullptr; - } + } - br_x509_trust_anchor *ta = (br_x509_trust_anchor*)cs->_x509->getTrustAnchors(); - memcpy(ta->dn.data, ci.sha256, sizeof(ci.sha256)); - ta->dn.len = sizeof(ci.sha256); + if (!cs->_index->open(false)) + { + return nullptr; + } - return ta; + while (cs->_index->read(&ci, sizeof(ci)) == sizeof(ci)) + { + if (!memcmp(ci.sha256, hashed_dn, sizeof(ci.sha256))) + { + cs->_index->close(); + uint8_t *der = (uint8_t*)malloc(ci.length); + if (!der) + { + return nullptr; + } + if (!cs->_data->open(false)) + { + free(der); + return nullptr; + } + if (!cs->_data->seek(ci.offset)) + { + cs->_data->close(); + free(der); + return nullptr; + } + if (cs->_data->read(der, ci.length) != (ssize_t)ci.length) + { + free(der); + return nullptr; + } + cs->_data->close(); + cs->_x509 = new X509List(der, ci.length); + free(der); + if (!cs->_x509) + { + DEBUG_BSSL("CertStore::findHashedTA: OOM\n"); + return nullptr; + } + + br_x509_trust_anchor *ta = (br_x509_trust_anchor*)cs->_x509->getTrustAnchors(); + memcpy(ta->dn.data, ci.sha256, sizeof(ci.sha256)); + ta->dn.len = sizeof(ci.sha256); + + return ta; + } } - } - cs->_index->close(); - return nullptr; + cs->_index->close(); + return nullptr; } -void CertStore::freeHashedTA(void *ctx, const br_x509_trust_anchor *ta) { - CertStore *cs = static_cast(ctx); - (void) ta; // Unused - delete cs->_x509; - cs->_x509 = nullptr; +void CertStore::freeHashedTA(void *ctx, const br_x509_trust_anchor *ta) +{ + CertStore *cs = static_cast(ctx); + (void) ta; // Unused + delete cs->_x509; + cs->_x509 = nullptr; } } diff --git a/libraries/ESP8266WiFi/src/CertStoreBearSSL.h b/libraries/ESP8266WiFi/src/CertStoreBearSSL.h index cc5a3432ec..64a6229024 100644 --- a/libraries/ESP8266WiFi/src/CertStoreBearSSL.h +++ b/libraries/ESP8266WiFi/src/CertStoreBearSSL.h @@ -1,20 +1,20 @@ /* - CertStoreBearSSL.h - Library for Arduino ESP8266 - Copyright (c) 2018 Earle F. Philhower, III - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + CertStoreBearSSL.h - Library for Arduino ESP8266 + Copyright (c) 2018 Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CERTSTORE_BEARSSL_H @@ -28,7 +28,8 @@ // of a large set of certificates stored on SPIFFS of SD card to // be dynamically used when validating a X509 certificate -namespace BearSSL { +namespace BearSSL +{ // Subclass this and provide virtual functions appropriate for your storage. // Required because there are conflicting definitions for a "File" in the @@ -39,13 +40,14 @@ namespace BearSSL { // NOTE: This virtual class may migrate to a templated model in a future // release. Expect some changes to the interface, no matter what, as the // SD and SPIFFS filesystem get unified. -class CertStoreFile { - public: +class CertStoreFile +{ +public: CertStoreFile() {}; virtual ~CertStoreFile() {}; // The main API - virtual bool open(bool write=false) = 0; + virtual bool open(bool write = false) = 0; virtual bool seek(size_t absolute_pos) = 0; virtual ssize_t read(void *dest, size_t bytes) = 0; virtual ssize_t write(void *dest, size_t bytes) = 0; @@ -53,8 +55,9 @@ class CertStoreFile { }; -class CertStore { - public: +class CertStore +{ +public: CertStore() { }; ~CertStore() { }; @@ -64,7 +67,7 @@ class CertStore { // Installs the cert store into the X509 decoder (normally via static function callbacks) void installCertStore(br_x509_minimal_context *ctx); - protected: +protected: CertStoreFile *_index = nullptr; CertStoreFile *_data = nullptr; X509List *_x509 = nullptr; @@ -74,11 +77,12 @@ class CertStore { static void freeHashedTA(void *ctx, const br_x509_trust_anchor *ta); // The binary format of the index file - class CertInfo { + class CertInfo + { public: - uint8_t sha256[32]; - uint32_t offset; - uint32_t length; + uint8_t sha256[32]; + uint32_t offset; + uint32_t length; }; static CertInfo _preprocessCert(uint32_t length, uint32_t offset, const void *raw); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp index 146c968a58..b04f7f7395 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp @@ -1,26 +1,26 @@ /* - ESP8266WiFi.cpp - WiFi library for esp8266 + ESP8266WiFi.cpp - WiFi library for esp8266 - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Reworked on 28 Dec 2015 by Markus Sattler + Reworked on 28 Dec 2015 by Markus Sattler - */ +*/ #include "ESP8266WiFi.h" @@ -45,10 +45,11 @@ extern "C" { /** - * Output WiFi settings to an object derived from Print interface (like Serial). - * @param p Print interface - */ -void ESP8266WiFiClass::printDiag(Print& p) { + Output WiFi settings to an object derived from Print interface (like Serial). + @param p Print interface +*/ +void ESP8266WiFiClass::printDiag(Print& p) +{ const char* modes[] = { "NULL", "STA", "AP", "STA+AP" }; p.print("Mode: "); p.println(modes[wifi_get_opmode()]); @@ -75,7 +76,7 @@ void ESP8266WiFiClass::printDiag(Print& p) { char ssid[33]; //ssid can be up to 32chars, => plus null term memcpy(ssid, conf.ssid, sizeof(conf.ssid)); ssid[32] = 0; //nullterm in case of 32 char ssid - + p.print("SSID ("); p.print(strlen(ssid)); p.print("): "); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.h b/libraries/ESP8266WiFi/src/ESP8266WiFi.h index d26e1db6bd..a238b0ffd7 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.h @@ -1,23 +1,23 @@ /* - ESP8266WiFi.h - esp8266 Wifi support. - Based on WiFi.h from Arduino WiFi shield library. - Copyright (c) 2011-2014 Arduino. All right reserved. - Modified by Ivan Grokhotkov, December 2014 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + ESP8266WiFi.h - esp8266 Wifi support. + Based on WiFi.h from Arduino WiFi shield library. + Copyright (c) 2011-2014 Arduino. All right reserved. + Modified by Ivan Grokhotkov, December 2014 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef WiFi_h #define WiFi_h @@ -54,35 +54,36 @@ extern "C" { #endif -class ESP8266WiFiClass : public ESP8266WiFiGenericClass, public ESP8266WiFiSTAClass, public ESP8266WiFiScanClass, public ESP8266WiFiAPClass { - public: +class ESP8266WiFiClass : public ESP8266WiFiGenericClass, public ESP8266WiFiSTAClass, public ESP8266WiFiScanClass, public ESP8266WiFiAPClass +{ +public: - // workaround same function name with different signature - using ESP8266WiFiGenericClass::channel; + // workaround same function name with different signature + using ESP8266WiFiGenericClass::channel; - using ESP8266WiFiSTAClass::SSID; - using ESP8266WiFiSTAClass::RSSI; - using ESP8266WiFiSTAClass::BSSID; - using ESP8266WiFiSTAClass::BSSIDstr; + using ESP8266WiFiSTAClass::SSID; + using ESP8266WiFiSTAClass::RSSI; + using ESP8266WiFiSTAClass::BSSID; + using ESP8266WiFiSTAClass::BSSIDstr; - using ESP8266WiFiScanClass::SSID; - using ESP8266WiFiScanClass::encryptionType; - using ESP8266WiFiScanClass::RSSI; - using ESP8266WiFiScanClass::BSSID; - using ESP8266WiFiScanClass::BSSIDstr; - using ESP8266WiFiScanClass::channel; - using ESP8266WiFiScanClass::isHidden; + using ESP8266WiFiScanClass::SSID; + using ESP8266WiFiScanClass::encryptionType; + using ESP8266WiFiScanClass::RSSI; + using ESP8266WiFiScanClass::BSSID; + using ESP8266WiFiScanClass::BSSIDstr; + using ESP8266WiFiScanClass::channel; + using ESP8266WiFiScanClass::isHidden; - // ---------------------------------------------------------------------------------------------- - // ------------------------------------------- Debug -------------------------------------------- - // ---------------------------------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------- + // ------------------------------------------- Debug -------------------------------------------- + // ---------------------------------------------------------------------------------------------- - public: +public: - void printDiag(Print& dest); + void printDiag(Print& dest); - friend class WiFiClient; - friend class WiFiServer; + friend class WiFiClient; + friend class WiFiServer; }; diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp index 3f7dbc8eb3..94bdbc6190 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp @@ -1,383 +1,442 @@ -/* - ESP8266WiFiSTA.cpp - WiFi library for esp8266 - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Reworked on 28 Dec 2015 by Markus Sattler - - */ - -#include "ESP8266WiFi.h" -#include "ESP8266WiFiGeneric.h" -#include "ESP8266WiFiAP.h" - -extern "C" { -#include "c_types.h" -#include "ets_sys.h" -#include "os_type.h" -#include "osapi.h" -#include "mem.h" -#include "user_interface.h" -} - -#include "debug.h" - - - -// ----------------------------------------------------------------------------------------------------------------------- -// ---------------------------------------------------- Private functions ------------------------------------------------ -// ----------------------------------------------------------------------------------------------------------------------- - -static bool softap_config_equal(const softap_config& lhs, const softap_config& rhs); - - - -/** - * compare two AP configurations - * @param lhs softap_config - * @param rhs softap_config - * @return equal - */ -static bool softap_config_equal(const softap_config& lhs, const softap_config& rhs) { - if(strcmp(reinterpret_cast(lhs.ssid), reinterpret_cast(rhs.ssid)) != 0) { - return false; - } - if(strcmp(reinterpret_cast(lhs.password), reinterpret_cast(rhs.password)) != 0) { - return false; - } - if(lhs.channel != rhs.channel) { - return false; - } - if(lhs.ssid_hidden != rhs.ssid_hidden) { - return false; - } - if(lhs.max_connection != rhs.max_connection) { - return false; - } - if(lhs.beacon_interval != rhs.beacon_interval) { - return false; - } - if(lhs.authmode != rhs.authmode) { - return false; - } - return true; -} - -// ----------------------------------------------------------------------------------------------------------------------- -// ----------------------------------------------------- AP function ----------------------------------------------------- -// ----------------------------------------------------------------------------------------------------------------------- - - -/** - * Set up an access point - * @param ssid Pointer to the SSID (max 31 char). - * @param passphrase For WPA2 min 8 char, for open use NULL (max 63 char). - * @param channel WiFi channel number, 1 - 13. - * @param ssid_hidden Network cloaking (0 = broadcast SSID, 1 = hide SSID) - * @param max_connection Max simultaneous connected clients, 0 - 8. https://bbs.espressif.com/viewtopic.php?f=46&t=481&p=1832&hilit=max_connection#p1832 - */ -bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int channel, int ssid_hidden, int max_connection) { - - if(!WiFi.enableAP(true)) { - // enable AP failed - DEBUG_WIFI("[AP] enableAP failed!\n"); - return false; - } - - if(!ssid || strlen(ssid) == 0 || strlen(ssid) > 31) { - // fail SSID too long or missing! - DEBUG_WIFI("[AP] SSID too long or missing!\n"); - return false; - } - - if(passphrase && strlen(passphrase) > 0 && (strlen(passphrase) > 63 || strlen(passphrase) < 8)) { - // fail passphrase to long or short! - DEBUG_WIFI("[AP] fail passphrase to long or short!\n"); - return false; - } - - bool ret = true; - - struct softap_config conf; - strcpy(reinterpret_cast(conf.ssid), ssid); - conf.channel = channel; - conf.ssid_len = strlen(ssid); - conf.ssid_hidden = ssid_hidden; - conf.max_connection = max_connection; - conf.beacon_interval = 100; - - if(!passphrase || strlen(passphrase) == 0) { - conf.authmode = AUTH_OPEN; - *conf.password = 0; - } else { - conf.authmode = AUTH_WPA2_PSK; - strcpy(reinterpret_cast(conf.password), passphrase); - } - - struct softap_config conf_compare; - if(WiFi._persistent){ - wifi_softap_get_config_default(&conf_compare); - } - else { - wifi_softap_get_config(&conf_compare); - } - - if(!softap_config_equal(conf, conf_compare)) { - - ETS_UART_INTR_DISABLE(); - if(WiFi._persistent) { - ret = wifi_softap_set_config(&conf); - } else { - ret = wifi_softap_set_config_current(&conf); - } - ETS_UART_INTR_ENABLE(); - - if(!ret) { - DEBUG_WIFI("[AP] set_config failed!\n"); - return false; - } - - } else { - DEBUG_WIFI("[AP] softap config unchanged\n"); - } - - if(wifi_softap_dhcps_status() != DHCP_STARTED) { - DEBUG_WIFI("[AP] DHCP not started, starting...\n"); - if(!wifi_softap_dhcps_start()) { - DEBUG_WIFI("[AP] wifi_softap_dhcps_start failed!\n"); - ret = false; - } - } - - // check IP config - struct ip_info ip; - if(wifi_get_ip_info(SOFTAP_IF, &ip)) { - if(ip.ip.addr == 0x00000000) { - // Invalid config - DEBUG_WIFI("[AP] IP config Invalid resetting...\n"); - //192.168.244.1 , 192.168.244.1 , 255.255.255.0 - ret = softAPConfig(0x01F4A8C0, 0x01F4A8C0, 0x00FFFFFF); - if(!ret) { - DEBUG_WIFI("[AP] softAPConfig failed!\n"); - ret = false; - } - } - } else { - DEBUG_WIFI("[AP] wifi_get_ip_info failed!\n"); - ret = false; - } - - return ret; -} - -bool ESP8266WiFiAPClass::softAP(const String& ssid, const String& passphrase, int channel, int ssid_hidden, int max_connection) { - return softAP(ssid.c_str(), passphrase.c_str(), channel, ssid_hidden, max_connection); -} - -/** - * Configure access point - * @param local_ip access point IP - * @param gateway gateway IP (0.0.0.0 to disable) - * @param subnet subnet mask - */ -bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet) { - DEBUG_WIFI("[APConfig] local_ip: %s gateway: %s subnet: %s\n", local_ip.toString().c_str(), gateway.toString().c_str(), subnet.toString().c_str()); - if(!WiFi.enableAP(true)) { - // enable AP failed - DEBUG_WIFI("[APConfig] enableAP failed!\n"); - return false; - } - bool ret = true; - - if ( !local_ip.isV4() - || !subnet.isV4() -#if LWIP_IPV6 - // uninitialized gateway is valid - || gateway.isV6() -#endif - ) { - return false; - } - struct ip_info info; - info.ip.addr = local_ip.v4(); - info.gw.addr = gateway.v4(); - info.netmask.addr = subnet.v4(); - - if(!wifi_softap_dhcps_stop()) { - DEBUG_WIFI("[APConfig] wifi_softap_dhcps_stop failed!\n"); - } - - if(!wifi_set_ip_info(SOFTAP_IF, &info)) { - DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n"); - ret = false; - } - - struct dhcps_lease dhcp_lease; - IPAddress ip = local_ip; - ip[3] += 99; - dhcp_lease.start_ip.addr = ip.v4(); - DEBUG_WIFI("[APConfig] DHCP IP start: %s\n", ip.toString().c_str()); - - ip[3] += 100; - dhcp_lease.end_ip.addr = ip.v4(); - DEBUG_WIFI("[APConfig] DHCP IP end: %s\n", ip.toString().c_str()); - - if(!wifi_softap_set_dhcps_lease(&dhcp_lease)) { - DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n"); - ret = false; - } - - // set lease time to 720min --> 12h - if(!wifi_softap_set_dhcps_lease_time(720)) { - DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_lease_time failed!\n"); - ret = false; - } - - uint8 mode = info.gw.addr ? 1 : 0; - if(!wifi_softap_set_dhcps_offer_option(OFFER_ROUTER, &mode)) { - DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_offer_option failed!\n"); - ret = false; - } - - if(!wifi_softap_dhcps_start()) { - DEBUG_WIFI("[APConfig] wifi_softap_dhcps_start failed!\n"); - ret = false; - } - - // check config - if(wifi_get_ip_info(SOFTAP_IF, &info)) { - if(info.ip.addr == 0x00000000) { - DEBUG_WIFI("[APConfig] IP config Invalid?!\n"); - ret = false; - } else if(local_ip.v4() != info.ip.addr) { - ip = info.ip.addr; - DEBUG_WIFI("[APConfig] IP config not set correct?! new IP: %s\n", ip.toString().c_str()); - ret = false; - } - } else { - DEBUG_WIFI("[APConfig] wifi_get_ip_info failed!\n"); - ret = false; - } - - return ret; -} - - - -/** - * Disconnect from the network (close AP) - * @param wifioff disable mode? - * @return one value of wl_status_t enum - */ -bool ESP8266WiFiAPClass::softAPdisconnect(bool wifioff) { - bool ret; - struct softap_config conf; - *conf.ssid = 0; - *conf.password = 0; - conf.authmode = AUTH_OPEN; - ETS_UART_INTR_DISABLE(); - if(WiFi._persistent) { - ret = wifi_softap_set_config(&conf); - } else { - ret = wifi_softap_set_config_current(&conf); - } - ETS_UART_INTR_ENABLE(); - - if(!ret) { - DEBUG_WIFI("[APdisconnect] set_config failed!\n"); - } - - if(ret && wifioff) { - ret = WiFi.enableAP(false); - } - - return ret; -} - - -/** - * Get the count of the Station / client that are connected to the softAP interface - * @return Stations count - */ -uint8_t ESP8266WiFiAPClass::softAPgetStationNum() { - return wifi_softap_get_station_num(); -} - -/** - * Get the softAP interface IP address. - * @return IPAddress softAP IP - */ -IPAddress ESP8266WiFiAPClass::softAPIP() { - struct ip_info ip; - wifi_get_ip_info(SOFTAP_IF, &ip); - return IPAddress(ip.ip.addr); -} - - -/** - * Get the softAP interface MAC address. - * @param mac pointer to uint8_t array with length WL_MAC_ADDR_LENGTH - * @return pointer to uint8_t* - */ -uint8_t* ESP8266WiFiAPClass::softAPmacAddress(uint8_t* mac) { - wifi_get_macaddr(SOFTAP_IF, mac); - return mac; -} - -/** - * Get the softAP interface MAC address. - * @return String mac - */ -String ESP8266WiFiAPClass::softAPmacAddress(void) { - uint8_t mac[6]; - char macStr[18] = { 0 }; - wifi_get_macaddr(SOFTAP_IF, mac); - - sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return String(macStr); -} - -/** - * Get the configured(Not-In-Flash) softAP SSID name. - * @return String SSID. - */ -String ESP8266WiFiAPClass::softAPSSID() const { - struct softap_config config; - wifi_softap_get_config(&config); - char* name = reinterpret_cast(config.ssid); - char ssid[sizeof(config.ssid) + 1]; - memcpy(ssid, name, sizeof(config.ssid)); - ssid[sizeof(config.ssid)] = '\0'; - - return String(ssid); -} - -/** - * Get the configured(Not-In-Flash) softAP PSK or PASSWORD. - * @return String psk. - */ -String ESP8266WiFiAPClass::softAPPSK() const { - struct softap_config config; - wifi_softap_get_config(&config); - char* pass = reinterpret_cast(config.password); - char psk[sizeof(config.password) + 1]; - memcpy(psk, pass, sizeof(config.password)); - psk[sizeof(config.password)] = '\0'; - - return String(psk); -} +/* + ESP8266WiFiSTA.cpp - WiFi library for esp8266 + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Reworked on 28 Dec 2015 by Markus Sattler + +*/ + +#include "ESP8266WiFi.h" +#include "ESP8266WiFiGeneric.h" +#include "ESP8266WiFiAP.h" + +extern "C" { +#include "c_types.h" +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "mem.h" +#include "user_interface.h" +} + +#include "debug.h" + + + +// ----------------------------------------------------------------------------------------------------------------------- +// ---------------------------------------------------- Private functions ------------------------------------------------ +// ----------------------------------------------------------------------------------------------------------------------- + +static bool softap_config_equal(const softap_config& lhs, const softap_config& rhs); + + + +/** + compare two AP configurations + @param lhs softap_config + @param rhs softap_config + @return equal +*/ +static bool softap_config_equal(const softap_config& lhs, const softap_config& rhs) +{ + if (strcmp(reinterpret_cast(lhs.ssid), reinterpret_cast(rhs.ssid)) != 0) + { + return false; + } + if (strcmp(reinterpret_cast(lhs.password), reinterpret_cast(rhs.password)) != 0) + { + return false; + } + if (lhs.channel != rhs.channel) + { + return false; + } + if (lhs.ssid_hidden != rhs.ssid_hidden) + { + return false; + } + if (lhs.max_connection != rhs.max_connection) + { + return false; + } + if (lhs.beacon_interval != rhs.beacon_interval) + { + return false; + } + if (lhs.authmode != rhs.authmode) + { + return false; + } + return true; +} + +// ----------------------------------------------------------------------------------------------------------------------- +// ----------------------------------------------------- AP function ----------------------------------------------------- +// ----------------------------------------------------------------------------------------------------------------------- + + +/** + Set up an access point + @param ssid Pointer to the SSID (max 31 char). + @param passphrase For WPA2 min 8 char, for open use NULL (max 63 char). + @param channel WiFi channel number, 1 - 13. + @param ssid_hidden Network cloaking (0 = broadcast SSID, 1 = hide SSID) + @param max_connection Max simultaneous connected clients, 0 - 8. https://bbs.espressif.com/viewtopic.php?f=46&t=481&p=1832&hilit=max_connection#p1832 +*/ +bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int channel, int ssid_hidden, int max_connection) +{ + + if (!WiFi.enableAP(true)) + { + // enable AP failed + DEBUG_WIFI("[AP] enableAP failed!\n"); + return false; + } + + if (!ssid || strlen(ssid) == 0 || strlen(ssid) > 31) + { + // fail SSID too long or missing! + DEBUG_WIFI("[AP] SSID too long or missing!\n"); + return false; + } + + if (passphrase && strlen(passphrase) > 0 && (strlen(passphrase) > 63 || strlen(passphrase) < 8)) + { + // fail passphrase to long or short! + DEBUG_WIFI("[AP] fail passphrase to long or short!\n"); + return false; + } + + bool ret = true; + + struct softap_config conf; + strcpy(reinterpret_cast(conf.ssid), ssid); + conf.channel = channel; + conf.ssid_len = strlen(ssid); + conf.ssid_hidden = ssid_hidden; + conf.max_connection = max_connection; + conf.beacon_interval = 100; + + if (!passphrase || strlen(passphrase) == 0) + { + conf.authmode = AUTH_OPEN; + *conf.password = 0; + } + else + { + conf.authmode = AUTH_WPA2_PSK; + strcpy(reinterpret_cast(conf.password), passphrase); + } + + struct softap_config conf_compare; + if (WiFi._persistent) + { + wifi_softap_get_config_default(&conf_compare); + } + else + { + wifi_softap_get_config(&conf_compare); + } + + if (!softap_config_equal(conf, conf_compare)) + { + + ETS_UART_INTR_DISABLE(); + if (WiFi._persistent) + { + ret = wifi_softap_set_config(&conf); + } + else + { + ret = wifi_softap_set_config_current(&conf); + } + ETS_UART_INTR_ENABLE(); + + if (!ret) + { + DEBUG_WIFI("[AP] set_config failed!\n"); + return false; + } + + } + else + { + DEBUG_WIFI("[AP] softap config unchanged\n"); + } + + if (wifi_softap_dhcps_status() != DHCP_STARTED) + { + DEBUG_WIFI("[AP] DHCP not started, starting...\n"); + if (!wifi_softap_dhcps_start()) + { + DEBUG_WIFI("[AP] wifi_softap_dhcps_start failed!\n"); + ret = false; + } + } + + // check IP config + struct ip_info ip; + if (wifi_get_ip_info(SOFTAP_IF, &ip)) + { + if (ip.ip.addr == 0x00000000) + { + // Invalid config + DEBUG_WIFI("[AP] IP config Invalid resetting...\n"); + //192.168.244.1 , 192.168.244.1 , 255.255.255.0 + ret = softAPConfig(0x01F4A8C0, 0x01F4A8C0, 0x00FFFFFF); + if (!ret) + { + DEBUG_WIFI("[AP] softAPConfig failed!\n"); + ret = false; + } + } + } + else + { + DEBUG_WIFI("[AP] wifi_get_ip_info failed!\n"); + ret = false; + } + + return ret; +} + +bool ESP8266WiFiAPClass::softAP(const String& ssid, const String& passphrase, int channel, int ssid_hidden, int max_connection) +{ + return softAP(ssid.c_str(), passphrase.c_str(), channel, ssid_hidden, max_connection); +} + +/** + Configure access point + @param local_ip access point IP + @param gateway gateway IP (0.0.0.0 to disable) + @param subnet subnet mask +*/ +bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet) +{ + DEBUG_WIFI("[APConfig] local_ip: %s gateway: %s subnet: %s\n", local_ip.toString().c_str(), gateway.toString().c_str(), subnet.toString().c_str()); + if (!WiFi.enableAP(true)) + { + // enable AP failed + DEBUG_WIFI("[APConfig] enableAP failed!\n"); + return false; + } + bool ret = true; + + if (!local_ip.isV4() + || !subnet.isV4() +#if LWIP_IPV6 + // uninitialized gateway is valid + || gateway.isV6() +#endif + ) + { + return false; + } + struct ip_info info; + info.ip.addr = local_ip.v4(); + info.gw.addr = gateway.v4(); + info.netmask.addr = subnet.v4(); + + if (!wifi_softap_dhcps_stop()) + { + DEBUG_WIFI("[APConfig] wifi_softap_dhcps_stop failed!\n"); + } + + if (!wifi_set_ip_info(SOFTAP_IF, &info)) + { + DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n"); + ret = false; + } + + struct dhcps_lease dhcp_lease; + IPAddress ip = local_ip; + ip[3] += 99; + dhcp_lease.start_ip.addr = ip.v4(); + DEBUG_WIFI("[APConfig] DHCP IP start: %s\n", ip.toString().c_str()); + + ip[3] += 100; + dhcp_lease.end_ip.addr = ip.v4(); + DEBUG_WIFI("[APConfig] DHCP IP end: %s\n", ip.toString().c_str()); + + if (!wifi_softap_set_dhcps_lease(&dhcp_lease)) + { + DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n"); + ret = false; + } + + // set lease time to 720min --> 12h + if (!wifi_softap_set_dhcps_lease_time(720)) + { + DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_lease_time failed!\n"); + ret = false; + } + + uint8 mode = info.gw.addr ? 1 : 0; + if (!wifi_softap_set_dhcps_offer_option(OFFER_ROUTER, &mode)) + { + DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_offer_option failed!\n"); + ret = false; + } + + if (!wifi_softap_dhcps_start()) + { + DEBUG_WIFI("[APConfig] wifi_softap_dhcps_start failed!\n"); + ret = false; + } + + // check config + if (wifi_get_ip_info(SOFTAP_IF, &info)) + { + if (info.ip.addr == 0x00000000) + { + DEBUG_WIFI("[APConfig] IP config Invalid?!\n"); + ret = false; + } + else if (local_ip.v4() != info.ip.addr) + { + ip = info.ip.addr; + DEBUG_WIFI("[APConfig] IP config not set correct?! new IP: %s\n", ip.toString().c_str()); + ret = false; + } + } + else + { + DEBUG_WIFI("[APConfig] wifi_get_ip_info failed!\n"); + ret = false; + } + + return ret; +} + + + +/** + Disconnect from the network (close AP) + @param wifioff disable mode? + @return one value of wl_status_t enum +*/ +bool ESP8266WiFiAPClass::softAPdisconnect(bool wifioff) +{ + bool ret; + struct softap_config conf; + *conf.ssid = 0; + *conf.password = 0; + conf.authmode = AUTH_OPEN; + ETS_UART_INTR_DISABLE(); + if (WiFi._persistent) + { + ret = wifi_softap_set_config(&conf); + } + else + { + ret = wifi_softap_set_config_current(&conf); + } + ETS_UART_INTR_ENABLE(); + + if (!ret) + { + DEBUG_WIFI("[APdisconnect] set_config failed!\n"); + } + + if (ret && wifioff) + { + ret = WiFi.enableAP(false); + } + + return ret; +} + + +/** + Get the count of the Station / client that are connected to the softAP interface + @return Stations count +*/ +uint8_t ESP8266WiFiAPClass::softAPgetStationNum() +{ + return wifi_softap_get_station_num(); +} + +/** + Get the softAP interface IP address. + @return IPAddress softAP IP +*/ +IPAddress ESP8266WiFiAPClass::softAPIP() +{ + struct ip_info ip; + wifi_get_ip_info(SOFTAP_IF, &ip); + return IPAddress(ip.ip.addr); +} + + +/** + Get the softAP interface MAC address. + @param mac pointer to uint8_t array with length WL_MAC_ADDR_LENGTH + @return pointer to uint8_t +*/ +uint8_t* ESP8266WiFiAPClass::softAPmacAddress(uint8_t* mac) +{ + wifi_get_macaddr(SOFTAP_IF, mac); + return mac; +} + +/** + Get the softAP interface MAC address. + @return String mac +*/ +String ESP8266WiFiAPClass::softAPmacAddress(void) +{ + uint8_t mac[6]; + char macStr[18] = { 0 }; + wifi_get_macaddr(SOFTAP_IF, mac); + + sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return String(macStr); +} + +/** + Get the configured(Not-In-Flash) softAP SSID name. + @return String SSID. +*/ +String ESP8266WiFiAPClass::softAPSSID() const +{ + struct softap_config config; + wifi_softap_get_config(&config); + char* name = reinterpret_cast(config.ssid); + char ssid[sizeof(config.ssid) + 1]; + memcpy(ssid, name, sizeof(config.ssid)); + ssid[sizeof(config.ssid)] = '\0'; + + return String(ssid); +} + +/** + Get the configured(Not-In-Flash) softAP PSK or PASSWORD. + @return String psk. +*/ +String ESP8266WiFiAPClass::softAPPSK() const +{ + struct softap_config config; + wifi_softap_get_config(&config); + char* pass = reinterpret_cast(config.password); + char psk[sizeof(config.password) + 1]; + memcpy(psk, pass, sizeof(config.password)); + psk[sizeof(config.password)] = '\0'; + + return String(psk); +} diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h index 599c8e0e11..dd9bb2646d 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h @@ -1,58 +1,59 @@ -/* - ESP8266WiFiAP.h - esp8266 Wifi support. - Based on WiFi.h from Arduino WiFi shield library. - Copyright (c) 2011-2014 Arduino. All right reserved. - Modified by Ivan Grokhotkov, December 2014 - Reworked by Markus Sattler, December 2015 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ESP8266WIFIAP_H_ -#define ESP8266WIFIAP_H_ - - -#include "ESP8266WiFiType.h" -#include "ESP8266WiFiGeneric.h" - - -class ESP8266WiFiAPClass { - - // ---------------------------------------------------------------------------------------------- - // ----------------------------------------- AP function ---------------------------------------- - // ---------------------------------------------------------------------------------------------- - - public: - - bool softAP(const char* ssid, const char* passphrase = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4); - bool softAP(const String& ssid,const String& passphrase = emptyString,int channel = 1,int ssid_hidden = 0,int max_connection = 4); - bool softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet); - bool softAPdisconnect(bool wifioff = false); - - uint8_t softAPgetStationNum(); - - IPAddress softAPIP(); - - uint8_t* softAPmacAddress(uint8_t* mac); - String softAPmacAddress(void); - - String softAPSSID() const; - String softAPPSK() const; - - protected: - -}; - -#endif /* ESP8266WIFIAP_H_*/ +/* + ESP8266WiFiAP.h - esp8266 Wifi support. + Based on WiFi.h from Arduino WiFi shield library. + Copyright (c) 2011-2014 Arduino. All right reserved. + Modified by Ivan Grokhotkov, December 2014 + Reworked by Markus Sattler, December 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ESP8266WIFIAP_H_ +#define ESP8266WIFIAP_H_ + + +#include "ESP8266WiFiType.h" +#include "ESP8266WiFiGeneric.h" + + +class ESP8266WiFiAPClass +{ + + // ---------------------------------------------------------------------------------------------- + // ----------------------------------------- AP function ---------------------------------------- + // ---------------------------------------------------------------------------------------------- + +public: + + bool softAP(const char* ssid, const char* passphrase = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4); + bool softAP(const String& ssid, const String& passphrase = emptyString, int channel = 1, int ssid_hidden = 0, int max_connection = 4); + bool softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet); + bool softAPdisconnect(bool wifioff = false); + + uint8_t softAPgetStationNum(); + + IPAddress softAPIP(); + + uint8_t* softAPmacAddress(uint8_t* mac); + String softAPmacAddress(void); + + String softAPSSID() const; + String softAPPSK() const; + +protected: + +}; + +#endif /* ESP8266WIFIAP_H_*/ diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 31daf370a0..feb95c846e 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -1,624 +1,704 @@ -/* - ESP8266WiFiGeneric.cpp - WiFi library for esp8266 - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Reworked on 28 Dec 2015 by Markus Sattler - - */ - -#include -#include -#include "ESP8266WiFi.h" -#include "ESP8266WiFiGeneric.h" - -extern "C" { -#include "c_types.h" -#include "ets_sys.h" -#include "os_type.h" -#include "osapi.h" -#include "mem.h" -#include "user_interface.h" - -#include "lwip/opt.h" -#include "lwip/err.h" -#include "lwip/dns.h" -#include "lwip/init.h" // LWIP_VERSION_ -} - -#include "WiFiClient.h" -#include "WiFiUdp.h" -#include "debug.h" - -extern "C" void esp_schedule(); -extern "C" void esp_yield(); - - -// ----------------------------------------------------------------------------------------------------------------------- -// ------------------------------------------------- Generic WiFi function ----------------------------------------------- -// ----------------------------------------------------------------------------------------------------------------------- - -struct WiFiEventHandlerOpaque -{ - WiFiEventHandlerOpaque(WiFiEvent_t event, std::function handler) - : mEvent(event), mHandler(handler) - { - } - - void operator()(System_Event_t* e) - { - if (static_cast(e->event) == mEvent || mEvent == WIFI_EVENT_ANY) { - mHandler(e); - } - } - - bool canExpire() - { - return mCanExpire; - } - - WiFiEvent_t mEvent; - std::function mHandler; - bool mCanExpire = true; /* stopgap solution to handle deprecated void onEvent(cb, evt) case */ -}; - -static std::list sCbEventList; - -bool ESP8266WiFiGenericClass::_persistent = true; -WiFiMode_t ESP8266WiFiGenericClass::_forceSleepLastMode = WIFI_OFF; - -ESP8266WiFiGenericClass::ESP8266WiFiGenericClass() -{ - wifi_set_event_handler_cb((wifi_event_handler_cb_t) &ESP8266WiFiGenericClass::_eventCallback); -} - -void ESP8266WiFiGenericClass::onEvent(WiFiEventCb f, WiFiEvent_t event) -{ - WiFiEventHandler handler = std::make_shared(event, [f](System_Event_t* e) { - (*f)(static_cast(e->event)); - }); - handler->mCanExpire = false; - sCbEventList.push_back(handler); -} - -WiFiEventHandler ESP8266WiFiGenericClass::onStationModeConnected(std::function f) -{ - WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_CONNECTED, [f](System_Event_t* e) { - auto& src = e->event_info.connected; - WiFiEventStationModeConnected dst; - dst.ssid = String(reinterpret_cast(src.ssid)); - memcpy(dst.bssid, src.bssid, 6); - dst.channel = src.channel; - f(dst); - }); - sCbEventList.push_back(handler); - return handler; -} - -WiFiEventHandler ESP8266WiFiGenericClass::onStationModeDisconnected(std::function f) -{ - WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_DISCONNECTED, [f](System_Event_t* e){ - auto& src = e->event_info.disconnected; - WiFiEventStationModeDisconnected dst; - dst.ssid = String(reinterpret_cast(src.ssid)); - memcpy(dst.bssid, src.bssid, 6); - dst.reason = static_cast(src.reason); - f(dst); - }); - sCbEventList.push_back(handler); - return handler; -} - -WiFiEventHandler ESP8266WiFiGenericClass::onStationModeAuthModeChanged(std::function f) -{ - WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_AUTHMODE_CHANGE, [f](System_Event_t* e){ - auto& src = e->event_info.auth_change; - WiFiEventStationModeAuthModeChanged dst; - dst.oldMode = src.old_mode; - dst.newMode = src.new_mode; - f(dst); - }); - sCbEventList.push_back(handler); - return handler; -} - -WiFiEventHandler ESP8266WiFiGenericClass::onStationModeGotIP(std::function f) -{ - WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_GOT_IP, [f](System_Event_t* e){ - auto& src = e->event_info.got_ip; - WiFiEventStationModeGotIP dst; - dst.ip = src.ip.addr; - dst.mask = src.mask.addr; - dst.gw = src.gw.addr; - f(dst); - }); - sCbEventList.push_back(handler); - return handler; -} - -WiFiEventHandler ESP8266WiFiGenericClass::onStationModeDHCPTimeout(std::function f) -{ - WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_DHCP_TIMEOUT, [f](System_Event_t* e){ - (void) e; - f(); - }); - sCbEventList.push_back(handler); - return handler; -} - -WiFiEventHandler ESP8266WiFiGenericClass::onSoftAPModeStationConnected(std::function f) -{ - WiFiEventHandler handler = std::make_shared(WIFI_EVENT_SOFTAPMODE_STACONNECTED, [f](System_Event_t* e){ - auto& src = e->event_info.sta_connected; - WiFiEventSoftAPModeStationConnected dst; - memcpy(dst.mac, src.mac, 6); - dst.aid = src.aid; - f(dst); - }); - sCbEventList.push_back(handler); - return handler; -} - -WiFiEventHandler ESP8266WiFiGenericClass::onSoftAPModeStationDisconnected(std::function f) -{ - WiFiEventHandler handler = std::make_shared(WIFI_EVENT_SOFTAPMODE_STADISCONNECTED, [f](System_Event_t* e){ - auto& src = e->event_info.sta_disconnected; - WiFiEventSoftAPModeStationDisconnected dst; - memcpy(dst.mac, src.mac, 6); - dst.aid = src.aid; - f(dst); - }); - sCbEventList.push_back(handler); - return handler; -} - -WiFiEventHandler ESP8266WiFiGenericClass::onSoftAPModeProbeRequestReceived(std::function f) -{ - WiFiEventHandler handler = std::make_shared(WIFI_EVENT_SOFTAPMODE_PROBEREQRECVED, [f](System_Event_t* e){ - auto& src = e->event_info.ap_probereqrecved; - WiFiEventSoftAPModeProbeRequestReceived dst; - memcpy(dst.mac, src.mac, 6); - dst.rssi = src.rssi; - f(dst); - }); - sCbEventList.push_back(handler); - return handler; -} - -// WiFiEventHandler ESP8266WiFiGenericClass::onWiFiModeChange(std::function f) -// { -// WiFiEventHandler handler = std::make_shared(WIFI_EVENT_MODE_CHANGE, [f](System_Event_t* e){ -// WiFiEventModeChange& dst = *reinterpret_cast(&e->event_info); -// f(dst); -// }); -// sCbEventList.push_back(handler); -// return handler; -// } - -/** - * callback for WiFi events - * @param arg - */ -void ESP8266WiFiGenericClass::_eventCallback(void* arg) -{ - System_Event_t* event = reinterpret_cast(arg); - DEBUG_WIFI("wifi evt: %d\n", event->event); - - if(event->event == EVENT_STAMODE_DISCONNECTED) { - DEBUG_WIFI("STA disconnect: %d\n", event->event_info.disconnected.reason); - WiFiClient::stopAll(); - } - - for(auto it = std::begin(sCbEventList); it != std::end(sCbEventList); ) { - WiFiEventHandler &handler = *it; - if (handler->canExpire() && handler.unique()) { - it = sCbEventList.erase(it); - } - else { - (*handler)(event); - ++it; - } - } -} - -/** - * Return the current channel associated with the network - * @return channel (1-13) - */ -int32_t ESP8266WiFiGenericClass::channel(void) { - return wifi_get_channel(); -} - -/** - * set Sleep mode - * @param type sleep_type_t - * @return bool - */ -bool ESP8266WiFiGenericClass::setSleepMode(WiFiSleepType_t type, uint8_t listenInterval) { - - /** - * datasheet: - * - wifi_set_sleep_level(): - Set sleep level of modem sleep and light sleep - This configuration should be called before calling wifi_set_sleep_type - Modem-sleep and light sleep mode have minimum and maximum sleep levels. - - In minimum sleep level, station wakes up at every DTIM to receive - beacon. Broadcast data will not be lost because it is transmitted after - DTIM. However, it can not save much more power if DTIM period is short, - as specified in AP. - - In maximum sleep level, station wakes up at every listen interval to - receive beacon. Broadcast data may be lost because station may be in sleep - state at DTIM time. If listen interval is longer, more power will be saved, but - it’s very likely to lose more broadcast data. - - Default setting is minimum sleep level. - Further reading: https://routerguide.net/dtim-interval-period-best-setting/ - - wifi_set_listen_interval(): - Set listen interval of maximum sleep level for modem sleep and light sleep - It only works when sleep level is set as MAX_SLEEP_T - forum: https://github.com/espressif/ESP8266_NONOS_SDK/issues/165#issuecomment-416121920 - default value seems to be 3 (as recommended by https://routerguide.net/dtim-interval-period-best-setting/) - - call order: - wifi_set_sleep_level(MAX_SLEEP_T) (SDK3) - wifi_set_listen_interval (SDK3) - wifi_set_sleep_type (all SDKs) - - */ - -#ifdef NONOSDK3V0 - -#ifdef DEBUG_ESP_WIFI - if (listenInterval && type == WIFI_NONE_SLEEP) - DEBUG_WIFI_GENERIC("listenInterval not usable with WIFI_NONE_SLEEP\n"); -#endif - - if (type == WIFI_LIGHT_SLEEP || type == WIFI_MODEM_SLEEP) { - if (listenInterval) { - if (!wifi_set_sleep_level(MAX_SLEEP_T)) { - DEBUG_WIFI_GENERIC("wifi_set_sleep_level(MAX_SLEEP_T): error\n"); - return false; - } - if (listenInterval > 10) { - DEBUG_WIFI_GENERIC("listenInterval must be in [1..10]\n"); -#ifndef DEBUG_ESP_WIFI - // stay within datasheet range when not in debug mode - listenInterval = 10; -#endif - } - if (!wifi_set_listen_interval(listenInterval)) { - DEBUG_WIFI_GENERIC("wifi_set_listen_interval(%d): error\n", listenInterval); - return false; - } - } else { - if (!wifi_set_sleep_level(MIN_SLEEP_T)) { - DEBUG_WIFI_GENERIC("wifi_set_sleep_level(MIN_SLEEP_T): error\n"); - return false; - } - } - } -#else // !defined(NONOSDK3V0) - (void)listenInterval; -#endif // !defined(NONOSDK3V0) - - bool ret = wifi_set_sleep_type((sleep_type_t) type); - if (!ret) { - DEBUG_WIFI_GENERIC("wifi_set_sleep_type(%d): error\n", (int)type); - } - return ret; -} - -/** - * get Sleep mode - * @return sleep_type_t - */ -WiFiSleepType_t ESP8266WiFiGenericClass::getSleepMode() { - return (WiFiSleepType_t) wifi_get_sleep_type(); -} - -/** - * set phy Mode - * @param mode phy_mode_t - * @return bool - */ -bool ESP8266WiFiGenericClass::setPhyMode(WiFiPhyMode_t mode) { - return wifi_set_phy_mode((phy_mode_t) mode); -} - -/** - * get phy Mode - * @return phy_mode_t - */ -WiFiPhyMode_t ESP8266WiFiGenericClass::getPhyMode() { - return (WiFiPhyMode_t) wifi_get_phy_mode(); -} - -/** - * set the output power of WiFi - * @param dBm max: +20.5dBm min: 0dBm - */ -void ESP8266WiFiGenericClass::setOutputPower(float dBm) { - - if(dBm > 20.5) { - dBm = 20.5; - } else if(dBm < 0) { - dBm = 0; - } - - uint8_t val = (dBm*4.0f); - system_phy_set_max_tpw(val); -} - - -/** - * store WiFi config in SDK flash area - * @param persistent - */ -void ESP8266WiFiGenericClass::persistent(bool persistent) { - _persistent = persistent; -} - -/** - * gets the persistent state - * @return bool - */ -bool ESP8266WiFiGenericClass::getPersistent(){ - return _persistent; -} - -/** - * set new mode - * @param m WiFiMode_t - */ -bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { - if(_persistent){ - if(wifi_get_opmode() == (uint8) m && wifi_get_opmode_default() == (uint8) m){ - return true; - } - } else if(wifi_get_opmode() == (uint8) m){ - return true; - } - - bool ret = false; - - if (m != WIFI_STA && m != WIFI_AP_STA) - // calls lwIP's dhcp_stop(), - // safe to call even if not started - wifi_station_dhcpc_stop(); - - ETS_UART_INTR_DISABLE(); - if(_persistent) { - ret = wifi_set_opmode(m); - } else { - ret = wifi_set_opmode_current(m); - } - ETS_UART_INTR_ENABLE(); - - return ret; -} - -/** - * get WiFi mode - * @return WiFiMode - */ -WiFiMode_t ESP8266WiFiGenericClass::getMode() { - return (WiFiMode_t) wifi_get_opmode(); -} - -/** - * control STA mode - * @param enable bool - * @return ok - */ -bool ESP8266WiFiGenericClass::enableSTA(bool enable) { - - WiFiMode_t currentMode = getMode(); - bool isEnabled = ((currentMode & WIFI_STA) != 0); - - if(isEnabled != enable) { - if(enable) { - return mode((WiFiMode_t)(currentMode | WIFI_STA)); - } else { - return mode((WiFiMode_t)(currentMode & (~WIFI_STA))); - } - } else { - return true; - } -} - -/** - * control AP mode - * @param enable bool - * @return ok - */ -bool ESP8266WiFiGenericClass::enableAP(bool enable){ - - WiFiMode_t currentMode = getMode(); - bool isEnabled = ((currentMode & WIFI_AP) != 0); - - if(isEnabled != enable) { - if(enable) { - return mode((WiFiMode_t)(currentMode | WIFI_AP)); - } else { - return mode((WiFiMode_t)(currentMode & (~WIFI_AP))); - } - } else { - return true; - } -} - - -/** - * Disable WiFi for x us when value is not 0 - * @param sleep_time_in_us - * @return ok - */ -bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) { - _forceSleepLastMode = getMode(); - if(!mode(WIFI_OFF)) { - return false; - } - - if(sleepUs == 0) { - sleepUs = 0xFFFFFFF; - } - - wifi_fpm_set_sleep_type(MODEM_SLEEP_T); - wifi_fpm_open(); - return (wifi_fpm_do_sleep(sleepUs) == 0); -} - -/** - * wake up WiFi Modem - * @return ok - */ -bool ESP8266WiFiGenericClass::forceSleepWake() { - wifi_fpm_do_wakeup(); - wifi_fpm_close(); - - // restore last mode - if(mode(_forceSleepLastMode)) { - if((_forceSleepLastMode & WIFI_STA) != 0){ - wifi_station_connect(); - } - return true; - } - return false; -} - -/** - * Get listen interval of maximum sleep level for modem sleep and light sleep. - * @return interval - */ -uint8_t ESP8266WiFiGenericClass::getListenInterval () { -#ifndef NONOSDK3V0 - return 0; -#else - return wifi_get_listen_interval(); -#endif -} - -/** - * Get sleep level of modem sleep and light sleep - * @return true if max level - */ -bool ESP8266WiFiGenericClass::isSleepLevelMax () { -#ifndef NONOSDK3V0 - return false; -#else - return wifi_get_sleep_level() == MAX_SLEEP_T; -#endif -} - - -// ----------------------------------------------------------------------------------------------------------------------- -// ------------------------------------------------ Generic Network function --------------------------------------------- -// ----------------------------------------------------------------------------------------------------------------------- - -void wifi_dns_found_callback(const char *name, CONST ip_addr_t *ipaddr, void *callback_arg); - -static bool _dns_lookup_pending = false; - -/** - * Resolve the given hostname to an IP address. - * @param aHostname Name to be resolved - * @param aResult IPAddress structure to store the returned IP address - * @return 1 if aIPAddrString was successfully converted to an IP address, - * else 0 - */ -int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult) -{ - return hostByName(aHostname, aResult, 10000); -} - - -int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms) -{ - ip_addr_t addr; - aResult = static_cast(0); - - if(aResult.fromString(aHostname)) { - // Host name is a IP address use it! - DEBUG_WIFI_GENERIC("[hostByName] Host: %s is a IP!\n", aHostname); - return 1; - } - - DEBUG_WIFI_GENERIC("[hostByName] request IP for: %s\n", aHostname); - err_t err = dns_gethostbyname(aHostname, &addr, &wifi_dns_found_callback, &aResult); - if(err == ERR_OK) { - aResult = IPAddress(&addr); - } else if(err == ERR_INPROGRESS) { - _dns_lookup_pending = true; - delay(timeout_ms); - _dns_lookup_pending = false; - // will return here when dns_found_callback fires - if(aResult.isSet()) { - err = ERR_OK; - } - } - - if(err != 0) { - DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err); - } else { - DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str()); - } - - return (err == ERR_OK) ? 1 : 0; -} - -/** - * DNS callback - * @param name - * @param ipaddr - * @param callback_arg - */ -void wifi_dns_found_callback(const char *name, CONST ip_addr_t *ipaddr, void *callback_arg) -{ - (void) name; - if (!_dns_lookup_pending) { - return; - } - if(ipaddr) { - (*reinterpret_cast(callback_arg)) = IPAddress(ipaddr); - } - esp_schedule(); // resume the hostByName function -} - -//meant to be called from user-defined preinit() -void ESP8266WiFiGenericClass::preinitWiFiOff () { - // https://github.com/esp8266/Arduino/issues/2111#issuecomment-224251391 - // WiFi.persistent(false); - // WiFi.mode(WIFI_OFF); - // WiFi.forceSleepBegin(); - - //WiFi.mode(WIFI_OFF) equivalent: - // datasheet: - // Set Wi-Fi working mode to Station mode, SoftAP - // or Station + SoftAP, and do not update flash - // (not persistent) - wifi_set_opmode_current(WIFI_OFF); - - //WiFi.forceSleepBegin(/*default*/0) equivalent: - // sleep forever until wifi_fpm_do_wakeup() is called - wifi_fpm_set_sleep_type(MODEM_SLEEP_T); - wifi_fpm_open(); - wifi_fpm_do_sleep(0xFFFFFFF); - - // use WiFi.forceSleepWake() to wake WiFi up -} +/* + ESP8266WiFiGeneric.cpp - WiFi library for esp8266 + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Reworked on 28 Dec 2015 by Markus Sattler + +*/ + +#include +#include +#include "ESP8266WiFi.h" +#include "ESP8266WiFiGeneric.h" + +extern "C" { +#include "c_types.h" +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "mem.h" +#include "user_interface.h" + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/dns.h" +#include "lwip/init.h" // LWIP_VERSION_ +} + +#include "WiFiClient.h" +#include "WiFiUdp.h" +#include "debug.h" + +extern "C" void esp_schedule(); +extern "C" void esp_yield(); + + +// ----------------------------------------------------------------------------------------------------------------------- +// ------------------------------------------------- Generic WiFi function ----------------------------------------------- +// ----------------------------------------------------------------------------------------------------------------------- + +struct WiFiEventHandlerOpaque +{ + WiFiEventHandlerOpaque(WiFiEvent_t event, std::function handler) + : mEvent(event), mHandler(handler) + { + } + + void operator()(System_Event_t* e) + { + if (static_cast(e->event) == mEvent || mEvent == WIFI_EVENT_ANY) + { + mHandler(e); + } + } + + bool canExpire() + { + return mCanExpire; + } + + WiFiEvent_t mEvent; + std::function mHandler; + bool mCanExpire = true; /* stopgap solution to handle deprecated void onEvent(cb, evt) case */ +}; + +static std::list sCbEventList; + +bool ESP8266WiFiGenericClass::_persistent = true; +WiFiMode_t ESP8266WiFiGenericClass::_forceSleepLastMode = WIFI_OFF; + +ESP8266WiFiGenericClass::ESP8266WiFiGenericClass() +{ + wifi_set_event_handler_cb((wifi_event_handler_cb_t) &ESP8266WiFiGenericClass::_eventCallback); +} + +void ESP8266WiFiGenericClass::onEvent(WiFiEventCb f, WiFiEvent_t event) +{ + WiFiEventHandler handler = std::make_shared(event, [f](System_Event_t* e) + { + (*f)(static_cast(e->event)); + }); + handler->mCanExpire = false; + sCbEventList.push_back(handler); +} + +WiFiEventHandler ESP8266WiFiGenericClass::onStationModeConnected(std::function f) +{ + WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_CONNECTED, [f](System_Event_t* e) + { + auto& src = e->event_info.connected; + WiFiEventStationModeConnected dst; + dst.ssid = String(reinterpret_cast(src.ssid)); + memcpy(dst.bssid, src.bssid, 6); + dst.channel = src.channel; + f(dst); + }); + sCbEventList.push_back(handler); + return handler; +} + +WiFiEventHandler ESP8266WiFiGenericClass::onStationModeDisconnected(std::function f) +{ + WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_DISCONNECTED, [f](System_Event_t* e) + { + auto& src = e->event_info.disconnected; + WiFiEventStationModeDisconnected dst; + dst.ssid = String(reinterpret_cast(src.ssid)); + memcpy(dst.bssid, src.bssid, 6); + dst.reason = static_cast(src.reason); + f(dst); + }); + sCbEventList.push_back(handler); + return handler; +} + +WiFiEventHandler ESP8266WiFiGenericClass::onStationModeAuthModeChanged(std::function f) +{ + WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_AUTHMODE_CHANGE, [f](System_Event_t* e) + { + auto& src = e->event_info.auth_change; + WiFiEventStationModeAuthModeChanged dst; + dst.oldMode = src.old_mode; + dst.newMode = src.new_mode; + f(dst); + }); + sCbEventList.push_back(handler); + return handler; +} + +WiFiEventHandler ESP8266WiFiGenericClass::onStationModeGotIP(std::function f) +{ + WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_GOT_IP, [f](System_Event_t* e) + { + auto& src = e->event_info.got_ip; + WiFiEventStationModeGotIP dst; + dst.ip = src.ip.addr; + dst.mask = src.mask.addr; + dst.gw = src.gw.addr; + f(dst); + }); + sCbEventList.push_back(handler); + return handler; +} + +WiFiEventHandler ESP8266WiFiGenericClass::onStationModeDHCPTimeout(std::function f) +{ + WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_DHCP_TIMEOUT, [f](System_Event_t* e) + { + (void) e; + f(); + }); + sCbEventList.push_back(handler); + return handler; +} + +WiFiEventHandler ESP8266WiFiGenericClass::onSoftAPModeStationConnected(std::function f) +{ + WiFiEventHandler handler = std::make_shared(WIFI_EVENT_SOFTAPMODE_STACONNECTED, [f](System_Event_t* e) + { + auto& src = e->event_info.sta_connected; + WiFiEventSoftAPModeStationConnected dst; + memcpy(dst.mac, src.mac, 6); + dst.aid = src.aid; + f(dst); + }); + sCbEventList.push_back(handler); + return handler; +} + +WiFiEventHandler ESP8266WiFiGenericClass::onSoftAPModeStationDisconnected(std::function f) +{ + WiFiEventHandler handler = std::make_shared(WIFI_EVENT_SOFTAPMODE_STADISCONNECTED, [f](System_Event_t* e) + { + auto& src = e->event_info.sta_disconnected; + WiFiEventSoftAPModeStationDisconnected dst; + memcpy(dst.mac, src.mac, 6); + dst.aid = src.aid; + f(dst); + }); + sCbEventList.push_back(handler); + return handler; +} + +WiFiEventHandler ESP8266WiFiGenericClass::onSoftAPModeProbeRequestReceived(std::function f) +{ + WiFiEventHandler handler = std::make_shared(WIFI_EVENT_SOFTAPMODE_PROBEREQRECVED, [f](System_Event_t* e) + { + auto& src = e->event_info.ap_probereqrecved; + WiFiEventSoftAPModeProbeRequestReceived dst; + memcpy(dst.mac, src.mac, 6); + dst.rssi = src.rssi; + f(dst); + }); + sCbEventList.push_back(handler); + return handler; +} + +// WiFiEventHandler ESP8266WiFiGenericClass::onWiFiModeChange(std::function f) +// { +// WiFiEventHandler handler = std::make_shared(WIFI_EVENT_MODE_CHANGE, [f](System_Event_t* e){ +// WiFiEventModeChange& dst = *reinterpret_cast(&e->event_info); +// f(dst); +// }); +// sCbEventList.push_back(handler); +// return handler; +// } + +/** + callback for WiFi events + @param arg +*/ +void ESP8266WiFiGenericClass::_eventCallback(void* arg) +{ + System_Event_t* event = reinterpret_cast(arg); + DEBUG_WIFI("wifi evt: %d\n", event->event); + + if (event->event == EVENT_STAMODE_DISCONNECTED) + { + DEBUG_WIFI("STA disconnect: %d\n", event->event_info.disconnected.reason); + WiFiClient::stopAll(); + } + + for (auto it = std::begin(sCbEventList); it != std::end(sCbEventList);) + { + WiFiEventHandler &handler = *it; + if (handler->canExpire() && handler.unique()) + { + it = sCbEventList.erase(it); + } + else + { + (*handler)(event); + ++it; + } + } +} + +/** + Return the current channel associated with the network + @return channel (1-13) +*/ +int32_t ESP8266WiFiGenericClass::channel(void) +{ + return wifi_get_channel(); +} + +/** + set Sleep mode + @param type sleep_type_t + @return bool +*/ +bool ESP8266WiFiGenericClass::setSleepMode(WiFiSleepType_t type, uint8_t listenInterval) +{ + + /** + datasheet: + + wifi_set_sleep_level(): + Set sleep level of modem sleep and light sleep + This configuration should be called before calling wifi_set_sleep_type + Modem-sleep and light sleep mode have minimum and maximum sleep levels. + - In minimum sleep level, station wakes up at every DTIM to receive + beacon. Broadcast data will not be lost because it is transmitted after + DTIM. However, it can not save much more power if DTIM period is short, + as specified in AP. + - In maximum sleep level, station wakes up at every listen interval to + receive beacon. Broadcast data may be lost because station may be in sleep + state at DTIM time. If listen interval is longer, more power will be saved, but + it’s very likely to lose more broadcast data. + - Default setting is minimum sleep level. + Further reading: https://routerguide.net/dtim-interval-period-best-setting/ + + wifi_set_listen_interval(): + Set listen interval of maximum sleep level for modem sleep and light sleep + It only works when sleep level is set as MAX_SLEEP_T + forum: https://github.com/espressif/ESP8266_NONOS_SDK/issues/165#issuecomment-416121920 + default value seems to be 3 (as recommended by https://routerguide.net/dtim-interval-period-best-setting/) + + call order: + wifi_set_sleep_level(MAX_SLEEP_T) (SDK3) + wifi_set_listen_interval (SDK3) + wifi_set_sleep_type (all SDKs) + + */ + +#ifdef NONOSDK3V0 + +#ifdef DEBUG_ESP_WIFI + if (listenInterval && type == WIFI_NONE_SLEEP) + { + DEBUG_WIFI_GENERIC("listenInterval not usable with WIFI_NONE_SLEEP\n"); + } +#endif + + if (type == WIFI_LIGHT_SLEEP || type == WIFI_MODEM_SLEEP) + { + if (listenInterval) + { + if (!wifi_set_sleep_level(MAX_SLEEP_T)) + { + DEBUG_WIFI_GENERIC("wifi_set_sleep_level(MAX_SLEEP_T): error\n"); + return false; + } + if (listenInterval > 10) + { + DEBUG_WIFI_GENERIC("listenInterval must be in [1..10]\n"); +#ifndef DEBUG_ESP_WIFI + // stay within datasheet range when not in debug mode + listenInterval = 10; +#endif + } + if (!wifi_set_listen_interval(listenInterval)) + { + DEBUG_WIFI_GENERIC("wifi_set_listen_interval(%d): error\n", listenInterval); + return false; + } + } + else + { + if (!wifi_set_sleep_level(MIN_SLEEP_T)) + { + DEBUG_WIFI_GENERIC("wifi_set_sleep_level(MIN_SLEEP_T): error\n"); + return false; + } + } + } +#else // !defined(NONOSDK3V0) + (void)listenInterval; +#endif // !defined(NONOSDK3V0) + + bool ret = wifi_set_sleep_type((sleep_type_t) type); + if (!ret) + { + DEBUG_WIFI_GENERIC("wifi_set_sleep_type(%d): error\n", (int)type); + } + return ret; +} + +/** + get Sleep mode + @return sleep_type_t +*/ +WiFiSleepType_t ESP8266WiFiGenericClass::getSleepMode() +{ + return (WiFiSleepType_t) wifi_get_sleep_type(); +} + +/** + set phy Mode + @param mode phy_mode_t + @return bool +*/ +bool ESP8266WiFiGenericClass::setPhyMode(WiFiPhyMode_t mode) +{ + return wifi_set_phy_mode((phy_mode_t) mode); +} + +/** + get phy Mode + @return phy_mode_t +*/ +WiFiPhyMode_t ESP8266WiFiGenericClass::getPhyMode() +{ + return (WiFiPhyMode_t) wifi_get_phy_mode(); +} + +/** + set the output power of WiFi + @param dBm max: +20.5dBm min: 0dBm +*/ +void ESP8266WiFiGenericClass::setOutputPower(float dBm) +{ + + if (dBm > 20.5) + { + dBm = 20.5; + } + else if (dBm < 0) + { + dBm = 0; + } + + uint8_t val = (dBm * 4.0f); + system_phy_set_max_tpw(val); +} + + +/** + store WiFi config in SDK flash area + @param persistent +*/ +void ESP8266WiFiGenericClass::persistent(bool persistent) +{ + _persistent = persistent; +} + +/** + gets the persistent state + @return bool +*/ +bool ESP8266WiFiGenericClass::getPersistent() +{ + return _persistent; +} + +/** + set new mode + @param m WiFiMode_t +*/ +bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) +{ + if (_persistent) + { + if (wifi_get_opmode() == (uint8) m && wifi_get_opmode_default() == (uint8) m) + { + return true; + } + } + else if (wifi_get_opmode() == (uint8) m) + { + return true; + } + + bool ret = false; + + if (m != WIFI_STA && m != WIFI_AP_STA) + // calls lwIP's dhcp_stop(), + // safe to call even if not started + { + wifi_station_dhcpc_stop(); + } + + ETS_UART_INTR_DISABLE(); + if (_persistent) + { + ret = wifi_set_opmode(m); + } + else + { + ret = wifi_set_opmode_current(m); + } + ETS_UART_INTR_ENABLE(); + + return ret; +} + +/** + get WiFi mode + @return WiFiMode +*/ +WiFiMode_t ESP8266WiFiGenericClass::getMode() +{ + return (WiFiMode_t) wifi_get_opmode(); +} + +/** + control STA mode + @param enable bool + @return ok +*/ +bool ESP8266WiFiGenericClass::enableSTA(bool enable) +{ + + WiFiMode_t currentMode = getMode(); + bool isEnabled = ((currentMode & WIFI_STA) != 0); + + if (isEnabled != enable) + { + if (enable) + { + return mode((WiFiMode_t)(currentMode | WIFI_STA)); + } + else + { + return mode((WiFiMode_t)(currentMode & (~WIFI_STA))); + } + } + else + { + return true; + } +} + +/** + control AP mode + @param enable bool + @return ok +*/ +bool ESP8266WiFiGenericClass::enableAP(bool enable) +{ + + WiFiMode_t currentMode = getMode(); + bool isEnabled = ((currentMode & WIFI_AP) != 0); + + if (isEnabled != enable) + { + if (enable) + { + return mode((WiFiMode_t)(currentMode | WIFI_AP)); + } + else + { + return mode((WiFiMode_t)(currentMode & (~WIFI_AP))); + } + } + else + { + return true; + } +} + + +/** + Disable WiFi for x us when value is not 0 + @param sleep_time_in_us + @return ok +*/ +bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) +{ + _forceSleepLastMode = getMode(); + if (!mode(WIFI_OFF)) + { + return false; + } + + if (sleepUs == 0) + { + sleepUs = 0xFFFFFFF; + } + + wifi_fpm_set_sleep_type(MODEM_SLEEP_T); + wifi_fpm_open(); + return (wifi_fpm_do_sleep(sleepUs) == 0); +} + +/** + wake up WiFi Modem + @return ok +*/ +bool ESP8266WiFiGenericClass::forceSleepWake() +{ + wifi_fpm_do_wakeup(); + wifi_fpm_close(); + + // restore last mode + if (mode(_forceSleepLastMode)) + { + if ((_forceSleepLastMode & WIFI_STA) != 0) + { + wifi_station_connect(); + } + return true; + } + return false; +} + +/** + Get listen interval of maximum sleep level for modem sleep and light sleep. + @return interval +*/ +uint8_t ESP8266WiFiGenericClass::getListenInterval() +{ +#ifndef NONOSDK3V0 + return 0; +#else + return wifi_get_listen_interval(); +#endif +} + +/** + Get sleep level of modem sleep and light sleep + @return true if max level +*/ +bool ESP8266WiFiGenericClass::isSleepLevelMax() +{ +#ifndef NONOSDK3V0 + return false; +#else + return wifi_get_sleep_level() == MAX_SLEEP_T; +#endif +} + + +// ----------------------------------------------------------------------------------------------------------------------- +// ------------------------------------------------ Generic Network function --------------------------------------------- +// ----------------------------------------------------------------------------------------------------------------------- + +void wifi_dns_found_callback(const char *name, CONST ip_addr_t *ipaddr, void *callback_arg); + +static bool _dns_lookup_pending = false; + +/** + Resolve the given hostname to an IP address. + @param aHostname Name to be resolved + @param aResult IPAddress structure to store the returned IP address + @return 1 if aIPAddrString was successfully converted to an IP address, + else 0 +*/ +int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult) +{ + return hostByName(aHostname, aResult, 10000); +} + + +int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms) +{ + ip_addr_t addr; + aResult = static_cast(0); + + if (aResult.fromString(aHostname)) + { + // Host name is a IP address use it! + DEBUG_WIFI_GENERIC("[hostByName] Host: %s is a IP!\n", aHostname); + return 1; + } + + DEBUG_WIFI_GENERIC("[hostByName] request IP for: %s\n", aHostname); + err_t err = dns_gethostbyname(aHostname, &addr, &wifi_dns_found_callback, &aResult); + if (err == ERR_OK) + { + aResult = IPAddress(&addr); + } + else if (err == ERR_INPROGRESS) + { + _dns_lookup_pending = true; + delay(timeout_ms); + _dns_lookup_pending = false; + // will return here when dns_found_callback fires + if (aResult.isSet()) + { + err = ERR_OK; + } + } + + if (err != 0) + { + DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err); + } + else + { + DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str()); + } + + return (err == ERR_OK) ? 1 : 0; +} + +/** + DNS callback + @param name + @param ipaddr + @param callback_arg +*/ +void wifi_dns_found_callback(const char *name, CONST ip_addr_t *ipaddr, void *callback_arg) +{ + (void) name; + if (!_dns_lookup_pending) + { + return; + } + if (ipaddr) + { + (*reinterpret_cast(callback_arg)) = IPAddress(ipaddr); + } + esp_schedule(); // resume the hostByName function +} + +//meant to be called from user-defined preinit() +void ESP8266WiFiGenericClass::preinitWiFiOff() +{ + // https://github.com/esp8266/Arduino/issues/2111#issuecomment-224251391 + // WiFi.persistent(false); + // WiFi.mode(WIFI_OFF); + // WiFi.forceSleepBegin(); + + //WiFi.mode(WIFI_OFF) equivalent: + // datasheet: + // Set Wi-Fi working mode to Station mode, SoftAP + // or Station + SoftAP, and do not update flash + // (not persistent) + wifi_set_opmode_current(WIFI_OFF); + + //WiFi.forceSleepBegin(/*default*/0) equivalent: + // sleep forever until wifi_fpm_do_wakeup() is called + wifi_fpm_set_sleep_type(MODEM_SLEEP_T); + wifi_fpm_open(); + wifi_fpm_do_sleep(0xFFFFFFF); + + // use WiFi.forceSleepWake() to wake WiFi up +} diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index 60fdad5785..7a734ad292 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h @@ -1,115 +1,116 @@ -/* - ESP8266WiFiGeneric.h - esp8266 Wifi support. - Based on WiFi.h from Ardiono WiFi shield library. - Copyright (c) 2011-2014 Arduino. All right reserved. - Modified by Ivan Grokhotkov, December 2014 - Reworked by Markus Sattler, December 2015 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ESP8266WIFIGENERIC_H_ -#define ESP8266WIFIGENERIC_H_ - -#include "ESP8266WiFiType.h" -#include -#include - -#ifdef DEBUG_ESP_WIFI -#ifdef DEBUG_ESP_PORT -#define DEBUG_WIFI_GENERIC(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ##__VA_ARGS__ ) -#endif -#endif - -#ifndef DEBUG_WIFI_GENERIC -#define DEBUG_WIFI_GENERIC(...) do { (void)0; } while (0) -#endif - -struct WiFiEventHandlerOpaque; -typedef std::shared_ptr WiFiEventHandler; - -typedef void (*WiFiEventCb)(WiFiEvent_t); - -class ESP8266WiFiGenericClass { - // ---------------------------------------------------------------------------------------------- - // -------------------------------------- Generic WiFi function --------------------------------- - // ---------------------------------------------------------------------------------------------- - - public: - ESP8266WiFiGenericClass(); - - // Note: this function is deprecated. Use one of the functions below instead. - void onEvent(WiFiEventCb cb, WiFiEvent_t event = WIFI_EVENT_ANY) __attribute__((deprecated)); - - // Subscribe to specific event and get event information as an argument to the callback - WiFiEventHandler onStationModeConnected(std::function); - WiFiEventHandler onStationModeDisconnected(std::function); - WiFiEventHandler onStationModeAuthModeChanged(std::function); - WiFiEventHandler onStationModeGotIP(std::function); - WiFiEventHandler onStationModeDHCPTimeout(std::function); - WiFiEventHandler onSoftAPModeStationConnected(std::function); - WiFiEventHandler onSoftAPModeStationDisconnected(std::function); - WiFiEventHandler onSoftAPModeProbeRequestReceived(std::function); - // WiFiEventHandler onWiFiModeChange(std::function); - - int32_t channel(void); - - bool setSleepMode(WiFiSleepType_t type, uint8_t listenInterval = 0); - - WiFiSleepType_t getSleepMode(); - uint8_t getListenInterval (); - bool isSleepLevelMax (); - - bool setPhyMode(WiFiPhyMode_t mode); - WiFiPhyMode_t getPhyMode(); - - void setOutputPower(float dBm); - - void persistent(bool persistent); - - bool mode(WiFiMode_t); - WiFiMode_t getMode(); - - bool enableSTA(bool enable); - bool enableAP(bool enable); - - bool forceSleepBegin(uint32 sleepUs = 0); - bool forceSleepWake(); - - static void preinitWiFiOff (); //meant to be called in user-defined preinit() - - protected: - static bool _persistent; - static WiFiMode_t _forceSleepLastMode; - - static void _eventCallback(void *event); - - // ---------------------------------------------------------------------------------------------- - // ------------------------------------ Generic Network function -------------------------------- - // ---------------------------------------------------------------------------------------------- - - public: - - int hostByName(const char* aHostname, IPAddress& aResult); - int hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms); - bool getPersistent(); - protected: - - friend class ESP8266WiFiSTAClass; - friend class ESP8266WiFiScanClass; - friend class ESP8266WiFiAPClass; -}; - -#endif /* ESP8266WIFIGENERIC_H_ */ +/* + ESP8266WiFiGeneric.h - esp8266 Wifi support. + Based on WiFi.h from Ardiono WiFi shield library. + Copyright (c) 2011-2014 Arduino. All right reserved. + Modified by Ivan Grokhotkov, December 2014 + Reworked by Markus Sattler, December 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ESP8266WIFIGENERIC_H_ +#define ESP8266WIFIGENERIC_H_ + +#include "ESP8266WiFiType.h" +#include +#include + +#ifdef DEBUG_ESP_WIFI +#ifdef DEBUG_ESP_PORT +#define DEBUG_WIFI_GENERIC(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ##__VA_ARGS__ ) +#endif +#endif + +#ifndef DEBUG_WIFI_GENERIC +#define DEBUG_WIFI_GENERIC(...) do { (void)0; } while (0) +#endif + +struct WiFiEventHandlerOpaque; +typedef std::shared_ptr WiFiEventHandler; + +typedef void (*WiFiEventCb)(WiFiEvent_t); + +class ESP8266WiFiGenericClass +{ + // ---------------------------------------------------------------------------------------------- + // -------------------------------------- Generic WiFi function --------------------------------- + // ---------------------------------------------------------------------------------------------- + +public: + ESP8266WiFiGenericClass(); + + // Note: this function is deprecated. Use one of the functions below instead. + void onEvent(WiFiEventCb cb, WiFiEvent_t event = WIFI_EVENT_ANY) __attribute__((deprecated)); + + // Subscribe to specific event and get event information as an argument to the callback + WiFiEventHandler onStationModeConnected(std::function); + WiFiEventHandler onStationModeDisconnected(std::function); + WiFiEventHandler onStationModeAuthModeChanged(std::function); + WiFiEventHandler onStationModeGotIP(std::function); + WiFiEventHandler onStationModeDHCPTimeout(std::function); + WiFiEventHandler onSoftAPModeStationConnected(std::function); + WiFiEventHandler onSoftAPModeStationDisconnected(std::function); + WiFiEventHandler onSoftAPModeProbeRequestReceived(std::function); + // WiFiEventHandler onWiFiModeChange(std::function); + + int32_t channel(void); + + bool setSleepMode(WiFiSleepType_t type, uint8_t listenInterval = 0); + + WiFiSleepType_t getSleepMode(); + uint8_t getListenInterval(); + bool isSleepLevelMax(); + + bool setPhyMode(WiFiPhyMode_t mode); + WiFiPhyMode_t getPhyMode(); + + void setOutputPower(float dBm); + + void persistent(bool persistent); + + bool mode(WiFiMode_t); + WiFiMode_t getMode(); + + bool enableSTA(bool enable); + bool enableAP(bool enable); + + bool forceSleepBegin(uint32 sleepUs = 0); + bool forceSleepWake(); + + static void preinitWiFiOff(); //meant to be called in user-defined preinit() + +protected: + static bool _persistent; + static WiFiMode_t _forceSleepLastMode; + + static void _eventCallback(void *event); + + // ---------------------------------------------------------------------------------------------- + // ------------------------------------ Generic Network function -------------------------------- + // ---------------------------------------------------------------------------------------------- + +public: + + int hostByName(const char* aHostname, IPAddress& aResult); + int hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms); + bool getPersistent(); +protected: + + friend class ESP8266WiFiSTAClass; + friend class ESP8266WiFiScanClass; + friend class ESP8266WiFiAPClass; +}; + +#endif /* ESP8266WIFIGENERIC_H_ */ diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp index 3a88c7e69b..7e2905d3d6 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp @@ -1,265 +1,309 @@ -/** - * - * @file ESP8266WiFiMulti.cpp - * @date 16.05.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the esp8266 core for Arduino environment. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "ESP8266WiFiMulti.h" -#include -#include - -ESP8266WiFiMulti::ESP8266WiFiMulti() { -} - -ESP8266WiFiMulti::~ESP8266WiFiMulti() { - APlistClean(); -} - -bool ESP8266WiFiMulti::addAP(const char* ssid, const char *passphrase) { - return APlistAdd(ssid, passphrase); -} - -bool ESP8266WiFiMulti::existsAP(const char* ssid, const char *passphrase) { - return APlistExists(ssid, passphrase); -} - -wl_status_t ESP8266WiFiMulti::run(void) { - - wl_status_t status = WiFi.status(); - if(status == WL_DISCONNECTED || status == WL_NO_SSID_AVAIL || status == WL_IDLE_STATUS || status == WL_CONNECT_FAILED) { - - int8_t scanResult = WiFi.scanComplete(); - - if(scanResult == WIFI_SCAN_RUNNING) { - // scan is running, do nothing yet - status = WL_NO_SSID_AVAIL; - return status; - } - - if(scanResult == 0) { - // scan done, no ssids found. Start another scan. - DEBUG_WIFI_MULTI("[WIFI] scan done\n"); - DEBUG_WIFI_MULTI("[WIFI] no networks found\n"); - WiFi.scanDelete(); - DEBUG_WIFI_MULTI("\n\n"); - delay(0); - WiFi.disconnect(); - DEBUG_WIFI_MULTI("[WIFI] start scan\n"); - // scan wifi async mode - WiFi.scanNetworks(true); - return status; - } - - if(scanResult > 0) { - // scan done, analyze - WifiAPEntry bestNetwork { NULL, NULL }; - int bestNetworkDb = INT_MIN; - uint8 bestBSSID[6]; - int32_t bestChannel; - - DEBUG_WIFI_MULTI("[WIFI] scan done\n"); - delay(0); - - DEBUG_WIFI_MULTI("[WIFI] %d networks found\n", scanResult); - for(int8_t i = 0; i < scanResult; ++i) { - - String ssid_scan; - int32_t rssi_scan; - uint8_t sec_scan; - uint8_t* BSSID_scan; - int32_t chan_scan; - bool hidden_scan; - - WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan, hidden_scan); - - bool known = false; - for(auto entry : APlist) { - if(ssid_scan == entry.ssid) { // SSID match - known = true; - if(rssi_scan > bestNetworkDb) { // best network - if(sec_scan == ENC_TYPE_NONE || entry.passphrase) { // check for passphrase if not open wlan - bestNetworkDb = rssi_scan; - bestChannel = chan_scan; - bestNetwork = entry; - memcpy((void*) &bestBSSID, (void*) BSSID_scan, sizeof(bestBSSID)); - } - } - break; - } - } - - if(known) { - DEBUG_WIFI_MULTI(" ---> "); - } else { - DEBUG_WIFI_MULTI(" "); - } - - DEBUG_WIFI_MULTI(" %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c\n", i, chan_scan, BSSID_scan[0], BSSID_scan[1], BSSID_scan[2], BSSID_scan[3], BSSID_scan[4], BSSID_scan[5], ssid_scan.c_str(), rssi_scan, (sec_scan == ENC_TYPE_NONE) ? ' ' : '*'); - delay(0); - } - - // clean up ram - WiFi.scanDelete(); - - DEBUG_WIFI_MULTI("\n\n"); - delay(0); - - if(bestNetwork.ssid) { - DEBUG_WIFI_MULTI("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)\n", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb); - - WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID); - status = WiFi.status(); - - static const uint32_t connectTimeout = 5000; //5s timeout - - auto startTime = millis(); - // wait for connection, fail, or timeout - while(status != WL_CONNECTED && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED && (millis() - startTime) <= connectTimeout) { - delay(10); - status = WiFi.status(); - } - -#ifdef DEBUG_ESP_WIFI - IPAddress ip; - uint8_t * mac; - switch(status) { - case WL_CONNECTED: - ip = WiFi.localIP(); - mac = WiFi.BSSID(); - DEBUG_WIFI_MULTI("[WIFI] Connecting done.\n"); - DEBUG_WIFI_MULTI("[WIFI] SSID: %s\n", WiFi.SSID().c_str()); - DEBUG_WIFI_MULTI("[WIFI] IP: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); - DEBUG_WIFI_MULTI("[WIFI] MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - DEBUG_WIFI_MULTI("[WIFI] Channel: %d\n", WiFi.channel()); - break; - case WL_NO_SSID_AVAIL: - DEBUG_WIFI_MULTI("[WIFI] Connecting Failed AP not found.\n"); - break; - case WL_CONNECT_FAILED: - DEBUG_WIFI_MULTI("[WIFI] Connecting Failed.\n"); - break; - default: - DEBUG_WIFI_MULTI("[WIFI] Connecting Failed (%d).\n", status); - break; - } -#endif - } else { - DEBUG_WIFI_MULTI("[WIFI] no matching wifi found!\n"); - } - - return status; - } - - - // scan failed, or some other condition not handled above. Start another scan. - DEBUG_WIFI_MULTI("[WIFI] delete old wifi config...\n"); - WiFi.disconnect(); - - DEBUG_WIFI_MULTI("[WIFI] start scan\n"); - // scan wifi async mode - WiFi.scanNetworks(true); - } - return status; -} - -// ################################################################################## - -bool ESP8266WiFiMulti::APlistAdd(const char* ssid, const char *passphrase) { - - WifiAPEntry newAP; - - if(!ssid || *ssid == 0x00 || strlen(ssid) > 32) { - // fail SSID too long or missing! - DEBUG_WIFI_MULTI("[WIFI][APlistAdd] no ssid or ssid too long\n"); - return false; - } - - //for passphrase, max is 63 ascii + null. For psk, 64hex + null. - if(passphrase && strlen(passphrase) > 64) { - // fail passphrase too long! - DEBUG_WIFI_MULTI("[WIFI][APlistAdd] passphrase too long\n"); - return false; - } - - if(APlistExists(ssid, passphrase)) { - DEBUG_WIFI_MULTI("[WIFI][APlistAdd] SSID: %s already exists\n", ssid); - return true; - } - - newAP.ssid = strdup(ssid); - - if(!newAP.ssid) { - DEBUG_WIFI_MULTI("[WIFI][APlistAdd] fail newAP.ssid == 0\n"); - return false; - } - - if(passphrase) { - newAP.passphrase = strdup(passphrase); - } else { - newAP.passphrase = strdup(""); - } - - if(!newAP.passphrase) { - DEBUG_WIFI_MULTI("[WIFI][APlistAdd] fail newAP.passphrase == 0\n"); - free(newAP.ssid); - return false; - } - - APlist.push_back(newAP); - DEBUG_WIFI_MULTI("[WIFI][APlistAdd] add SSID: %s\n", newAP.ssid); - return true; -} - -bool ESP8266WiFiMulti::APlistExists(const char* ssid, const char *passphrase) { - if(!ssid || *ssid == 0x00 || strlen(ssid) > 32) { - // fail SSID too long or missing! - DEBUG_WIFI_MULTI("[WIFI][APlistExists] no ssid or ssid too long\n"); - return false; - } - for(auto entry : APlist) { - if(!strcmp(entry.ssid, ssid)) { - if(!passphrase) { - if(!strcmp(entry.passphrase, "")) { - return true; - } - } else { - if(!strcmp(entry.passphrase, passphrase)) { - return true; - } - } - } - } - return false; -} - -void ESP8266WiFiMulti::APlistClean(void) { - for(auto entry : APlist) { - if(entry.ssid) { - free(entry.ssid); - } - if(entry.passphrase) { - free(entry.passphrase); - } - } - APlist.clear(); -} - +/** + + @file ESP8266WiFiMulti.cpp + @date 16.05.2015 + @author Markus Sattler + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "ESP8266WiFiMulti.h" +#include +#include + +ESP8266WiFiMulti::ESP8266WiFiMulti() +{ +} + +ESP8266WiFiMulti::~ESP8266WiFiMulti() +{ + APlistClean(); +} + +bool ESP8266WiFiMulti::addAP(const char* ssid, const char *passphrase) +{ + return APlistAdd(ssid, passphrase); +} + +bool ESP8266WiFiMulti::existsAP(const char* ssid, const char *passphrase) +{ + return APlistExists(ssid, passphrase); +} + +wl_status_t ESP8266WiFiMulti::run(void) +{ + + wl_status_t status = WiFi.status(); + if (status == WL_DISCONNECTED || status == WL_NO_SSID_AVAIL || status == WL_IDLE_STATUS || status == WL_CONNECT_FAILED) + { + + int8_t scanResult = WiFi.scanComplete(); + + if (scanResult == WIFI_SCAN_RUNNING) + { + // scan is running, do nothing yet + status = WL_NO_SSID_AVAIL; + return status; + } + + if (scanResult == 0) + { + // scan done, no ssids found. Start another scan. + DEBUG_WIFI_MULTI("[WIFI] scan done\n"); + DEBUG_WIFI_MULTI("[WIFI] no networks found\n"); + WiFi.scanDelete(); + DEBUG_WIFI_MULTI("\n\n"); + delay(0); + WiFi.disconnect(); + DEBUG_WIFI_MULTI("[WIFI] start scan\n"); + // scan wifi async mode + WiFi.scanNetworks(true); + return status; + } + + if (scanResult > 0) + { + // scan done, analyze + WifiAPEntry bestNetwork { NULL, NULL }; + int bestNetworkDb = INT_MIN; + uint8 bestBSSID[6]; + int32_t bestChannel; + + DEBUG_WIFI_MULTI("[WIFI] scan done\n"); + delay(0); + + DEBUG_WIFI_MULTI("[WIFI] %d networks found\n", scanResult); + for (int8_t i = 0; i < scanResult; ++i) + { + + String ssid_scan; + int32_t rssi_scan; + uint8_t sec_scan; + uint8_t* BSSID_scan; + int32_t chan_scan; + bool hidden_scan; + + WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan, hidden_scan); + + bool known = false; + for (auto entry : APlist) + { + if (ssid_scan == entry.ssid) // SSID match + { + known = true; + if (rssi_scan > bestNetworkDb) // best network + { + if (sec_scan == ENC_TYPE_NONE || entry.passphrase) // check for passphrase if not open wlan + { + bestNetworkDb = rssi_scan; + bestChannel = chan_scan; + bestNetwork = entry; + memcpy((void*) &bestBSSID, (void*) BSSID_scan, sizeof(bestBSSID)); + } + } + break; + } + } + + if (known) + { + DEBUG_WIFI_MULTI(" ---> "); + } + else + { + DEBUG_WIFI_MULTI(" "); + } + + DEBUG_WIFI_MULTI(" %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c\n", i, chan_scan, BSSID_scan[0], BSSID_scan[1], BSSID_scan[2], BSSID_scan[3], BSSID_scan[4], BSSID_scan[5], ssid_scan.c_str(), rssi_scan, (sec_scan == ENC_TYPE_NONE) ? ' ' : '*'); + delay(0); + } + + // clean up ram + WiFi.scanDelete(); + + DEBUG_WIFI_MULTI("\n\n"); + delay(0); + + if (bestNetwork.ssid) + { + DEBUG_WIFI_MULTI("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)\n", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb); + + WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID); + status = WiFi.status(); + + static const uint32_t connectTimeout = 5000; //5s timeout + + auto startTime = millis(); + // wait for connection, fail, or timeout + while (status != WL_CONNECTED && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED && (millis() - startTime) <= connectTimeout) + { + delay(10); + status = WiFi.status(); + } + +#ifdef DEBUG_ESP_WIFI + IPAddress ip; + uint8_t * mac; + switch (status) + { + case WL_CONNECTED: + ip = WiFi.localIP(); + mac = WiFi.BSSID(); + DEBUG_WIFI_MULTI("[WIFI] Connecting done.\n"); + DEBUG_WIFI_MULTI("[WIFI] SSID: %s\n", WiFi.SSID().c_str()); + DEBUG_WIFI_MULTI("[WIFI] IP: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); + DEBUG_WIFI_MULTI("[WIFI] MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + DEBUG_WIFI_MULTI("[WIFI] Channel: %d\n", WiFi.channel()); + break; + case WL_NO_SSID_AVAIL: + DEBUG_WIFI_MULTI("[WIFI] Connecting Failed AP not found.\n"); + break; + case WL_CONNECT_FAILED: + DEBUG_WIFI_MULTI("[WIFI] Connecting Failed.\n"); + break; + default: + DEBUG_WIFI_MULTI("[WIFI] Connecting Failed (%d).\n", status); + break; + } +#endif + } + else + { + DEBUG_WIFI_MULTI("[WIFI] no matching wifi found!\n"); + } + + return status; + } + + + // scan failed, or some other condition not handled above. Start another scan. + DEBUG_WIFI_MULTI("[WIFI] delete old wifi config...\n"); + WiFi.disconnect(); + + DEBUG_WIFI_MULTI("[WIFI] start scan\n"); + // scan wifi async mode + WiFi.scanNetworks(true); + } + return status; +} + +// ################################################################################## + +bool ESP8266WiFiMulti::APlistAdd(const char* ssid, const char *passphrase) +{ + + WifiAPEntry newAP; + + if (!ssid || *ssid == 0x00 || strlen(ssid) > 32) + { + // fail SSID too long or missing! + DEBUG_WIFI_MULTI("[WIFI][APlistAdd] no ssid or ssid too long\n"); + return false; + } + + //for passphrase, max is 63 ascii + null. For psk, 64hex + null. + if (passphrase && strlen(passphrase) > 64) + { + // fail passphrase too long! + DEBUG_WIFI_MULTI("[WIFI][APlistAdd] passphrase too long\n"); + return false; + } + + if (APlistExists(ssid, passphrase)) + { + DEBUG_WIFI_MULTI("[WIFI][APlistAdd] SSID: %s already exists\n", ssid); + return true; + } + + newAP.ssid = strdup(ssid); + + if (!newAP.ssid) + { + DEBUG_WIFI_MULTI("[WIFI][APlistAdd] fail newAP.ssid == 0\n"); + return false; + } + + if (passphrase) + { + newAP.passphrase = strdup(passphrase); + } + else + { + newAP.passphrase = strdup(""); + } + + if (!newAP.passphrase) + { + DEBUG_WIFI_MULTI("[WIFI][APlistAdd] fail newAP.passphrase == 0\n"); + free(newAP.ssid); + return false; + } + + APlist.push_back(newAP); + DEBUG_WIFI_MULTI("[WIFI][APlistAdd] add SSID: %s\n", newAP.ssid); + return true; +} + +bool ESP8266WiFiMulti::APlistExists(const char* ssid, const char *passphrase) +{ + if (!ssid || *ssid == 0x00 || strlen(ssid) > 32) + { + // fail SSID too long or missing! + DEBUG_WIFI_MULTI("[WIFI][APlistExists] no ssid or ssid too long\n"); + return false; + } + for (auto entry : APlist) + { + if (!strcmp(entry.ssid, ssid)) + { + if (!passphrase) + { + if (!strcmp(entry.passphrase, "")) + { + return true; + } + } + else + { + if (!strcmp(entry.passphrase, passphrase)) + { + return true; + } + } + } + } + return false; +} + +void ESP8266WiFiMulti::APlistClean(void) +{ + for (auto entry : APlist) + { + if (entry.ssid) + { + free(entry.ssid); + } + if (entry.passphrase) + { + free(entry.passphrase); + } + } + APlist.clear(); +} + diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.h b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.h index 9417e2ba40..6ff8a80cd7 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.h @@ -1,68 +1,70 @@ -/** - * - * @file ESP8266WiFiMulti.h - * @date 16.05.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the esp8266 core for Arduino environment. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - - -#ifndef WIFICLIENTMULTI_H_ -#define WIFICLIENTMULTI_H_ - -#include "ESP8266WiFi.h" -#include - -#ifdef DEBUG_ESP_WIFI -#ifdef DEBUG_ESP_PORT -#define DEBUG_WIFI_MULTI(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ##__VA_ARGS__ ) -#endif -#endif - -#ifndef DEBUG_WIFI_MULTI -#define DEBUG_WIFI_MULTI(...) do { (void)0; } while (0) -#endif - -struct WifiAPEntry { - char * ssid; - char * passphrase; -}; - -typedef std::vector WifiAPlist; - -class ESP8266WiFiMulti { - public: - ESP8266WiFiMulti(); - ~ESP8266WiFiMulti(); - - bool addAP(const char* ssid, const char *passphrase = NULL); - bool existsAP(const char* ssid, const char *passphrase = NULL); - - wl_status_t run(void); - - private: - WifiAPlist APlist; - bool APlistAdd(const char* ssid, const char *passphrase = NULL); - bool APlistExists(const char* ssid, const char *passphrase = NULL); - void APlistClean(void); - -}; - -#endif /* WIFICLIENTMULTI_H_ */ +/** + + @file ESP8266WiFiMulti.h + @date 16.05.2015 + @author Markus Sattler + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef WIFICLIENTMULTI_H_ +#define WIFICLIENTMULTI_H_ + +#include "ESP8266WiFi.h" +#include + +#ifdef DEBUG_ESP_WIFI +#ifdef DEBUG_ESP_PORT +#define DEBUG_WIFI_MULTI(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ##__VA_ARGS__ ) +#endif +#endif + +#ifndef DEBUG_WIFI_MULTI +#define DEBUG_WIFI_MULTI(...) do { (void)0; } while (0) +#endif + +struct WifiAPEntry +{ + char * ssid; + char * passphrase; +}; + +typedef std::vector WifiAPlist; + +class ESP8266WiFiMulti +{ +public: + ESP8266WiFiMulti(); + ~ESP8266WiFiMulti(); + + bool addAP(const char* ssid, const char *passphrase = NULL); + bool existsAP(const char* ssid, const char *passphrase = NULL); + + wl_status_t run(void); + +private: + WifiAPlist APlist; + bool APlistAdd(const char* ssid, const char *passphrase = NULL); + bool APlistExists(const char* ssid, const char *passphrase = NULL); + void APlistClean(void); + +}; + +#endif /* WIFICLIENTMULTI_H_ */ diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA-WPS.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA-WPS.cpp index c7a92765be..f081ba5b81 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA-WPS.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA-WPS.cpp @@ -1,111 +1,121 @@ -/* - ESP8266WiFiSTA-WPS.cpp - WiFi library for esp8266 - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Reworked on 28 Dec 2015 by Markus Sattler - - */ - - -#include "ESP8266WiFi.h" -#include "ESP8266WiFiGeneric.h" -#include "ESP8266WiFiSTA.h" -#include "coredecls.h" // disable_extra4k_at_link_time() - -static void wifi_wps_status_cb(wps_cb_status status); - -/** - * WPS config - * so far only WPS_TYPE_PBC is supported (SDK 1.2.0) - * @return ok - */ -bool ESP8266WiFiSTAClass::beginWPSConfig(void) { - - // SYS ram is used by WPS, let's configure user stack inside user's HEAP - disable_extra4k_at_link_time(); - - if(!WiFi.enableSTA(true)) { - // enable STA failed - return false; - } - - disconnect(); - - DEBUGV("wps begin\n"); - - if(!wifi_wps_disable()) { - DEBUGV("wps disable failed\n"); - return false; - } - - // so far only WPS_TYPE_PBC is supported (SDK 1.2.0) - if(!wifi_wps_enable(WPS_TYPE_PBC)) { - DEBUGV("wps enable failed\n"); - return false; - } - - if(!wifi_set_wps_cb((wps_st_cb_t) &wifi_wps_status_cb)) { - DEBUGV("wps cb failed\n"); - return false; - } - - if(!wifi_wps_start()) { - DEBUGV("wps start failed\n"); - return false; - } - - esp_yield(); - // will return here when wifi_wps_status_cb fires - - return true; -} - -/** - * WPS callback - * @param status wps_cb_status - */ -void wifi_wps_status_cb(wps_cb_status status) { - DEBUGV("wps cb status: %d\r\n", status); - switch(status) { - case WPS_CB_ST_SUCCESS: - if(!wifi_wps_disable()) { - DEBUGV("wps disable failed\n"); - } - wifi_station_connect(); - break; - case WPS_CB_ST_FAILED: - DEBUGV("wps FAILED\n"); - break; - case WPS_CB_ST_TIMEOUT: - DEBUGV("wps TIMEOUT\n"); - break; - case WPS_CB_ST_WEP: - DEBUGV("wps WEP\n"); - break; - case WPS_CB_ST_UNK: - DEBUGV("wps UNKNOWN\n"); - if(!wifi_wps_disable()) { - DEBUGV("wps disable failed\n"); - } - break; - } - // TODO user function to get status - - esp_schedule(); // resume the beginWPSConfig function -} +/* + ESP8266WiFiSTA-WPS.cpp - WiFi library for esp8266 + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Reworked on 28 Dec 2015 by Markus Sattler + +*/ + + +#include "ESP8266WiFi.h" +#include "ESP8266WiFiGeneric.h" +#include "ESP8266WiFiSTA.h" +#include "coredecls.h" // disable_extra4k_at_link_time() + +static void wifi_wps_status_cb(wps_cb_status status); + +/** + WPS config + so far only WPS_TYPE_PBC is supported (SDK 1.2.0) + @return ok +*/ +bool ESP8266WiFiSTAClass::beginWPSConfig(void) +{ + + // SYS ram is used by WPS, let's configure user stack inside user's HEAP + disable_extra4k_at_link_time(); + + if (!WiFi.enableSTA(true)) + { + // enable STA failed + return false; + } + + disconnect(); + + DEBUGV("wps begin\n"); + + if (!wifi_wps_disable()) + { + DEBUGV("wps disable failed\n"); + return false; + } + + // so far only WPS_TYPE_PBC is supported (SDK 1.2.0) + if (!wifi_wps_enable(WPS_TYPE_PBC)) + { + DEBUGV("wps enable failed\n"); + return false; + } + + if (!wifi_set_wps_cb((wps_st_cb_t) &wifi_wps_status_cb)) + { + DEBUGV("wps cb failed\n"); + return false; + } + + if (!wifi_wps_start()) + { + DEBUGV("wps start failed\n"); + return false; + } + + esp_yield(); + // will return here when wifi_wps_status_cb fires + + return true; +} + +/** + WPS callback + @param status wps_cb_status +*/ +void wifi_wps_status_cb(wps_cb_status status) +{ + DEBUGV("wps cb status: %d\r\n", status); + switch (status) + { + case WPS_CB_ST_SUCCESS: + if (!wifi_wps_disable()) + { + DEBUGV("wps disable failed\n"); + } + wifi_station_connect(); + break; + case WPS_CB_ST_FAILED: + DEBUGV("wps FAILED\n"); + break; + case WPS_CB_ST_TIMEOUT: + DEBUGV("wps TIMEOUT\n"); + break; + case WPS_CB_ST_WEP: + DEBUGV("wps WEP\n"); + break; + case WPS_CB_ST_UNK: + DEBUGV("wps UNKNOWN\n"); + if (!wifi_wps_disable()) + { + DEBUGV("wps disable failed\n"); + } + break; + } + // TODO user function to get status + + esp_schedule(); // resume the beginWPSConfig function +} diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp index aae7aec7d2..a2087c4a84 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp @@ -1,763 +1,869 @@ -/* - ESP8266WiFiSTA.cpp - WiFi library for esp8266 - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Reworked on 28 Dec 2015 by Markus Sattler - - */ - -#include "ESP8266WiFi.h" -#include "ESP8266WiFiGeneric.h" -#include "ESP8266WiFiSTA.h" - -#include "c_types.h" -#include "ets_sys.h" -#include "os_type.h" -#include "osapi.h" -#include "mem.h" -#include "user_interface.h" -#include "smartconfig.h" - -extern "C" { -#include "lwip/err.h" -#include "lwip/dns.h" -#include "lwip/dhcp.h" -#include "lwip/init.h" // LWIP_VERSION_ -#if LWIP_IPV6 -#include "lwip/netif.h" // struct netif -#endif -} - -#include "debug.h" - -extern "C" void esp_schedule(); -extern "C" void esp_yield(); - -// ----------------------------------------------------------------------------------------------------------------------- -// ---------------------------------------------------- Private functions ------------------------------------------------ -// ----------------------------------------------------------------------------------------------------------------------- - -static bool sta_config_equal(const station_config& lhs, const station_config& rhs); - - -/** - * compare two STA configurations - * @param lhs station_config - * @param rhs station_config - * @return equal - */ -static bool sta_config_equal(const station_config& lhs, const station_config& rhs) { - -#ifdef NONOSDK3V0 - static_assert(sizeof(station_config) == 116, "struct station_config has changed, please update comparison function"); -#else - static_assert(sizeof(station_config) == 112, "struct station_config has changed, please update comparison function"); -#endif - - if(strncmp(reinterpret_cast(lhs.ssid), reinterpret_cast(rhs.ssid), sizeof(lhs.ssid)) != 0) { - return false; - } - - //in case of password, use strncmp with size 64 to cover 64byte psk case (no null term) - if(strncmp(reinterpret_cast(lhs.password), reinterpret_cast(rhs.password), sizeof(lhs.password)) != 0) { - return false; - } - - if(lhs.bssid_set != rhs.bssid_set) { - return false; - } - - if(lhs.bssid_set) { - if(memcmp(lhs.bssid, rhs.bssid, 6) != 0) { - return false; - } - } - - if(lhs.threshold.rssi != rhs.threshold.rssi) { - return false; - } - - if(lhs.threshold.authmode != rhs.threshold.authmode) { - return false; - } - -#ifdef NONOSDK3V0 - if (lhs.open_and_wep_mode_disable != rhs.open_and_wep_mode_disable) { - return false; - } -#endif - - return true; -} - -// ----------------------------------------------------------------------------------------------------------------------- -// ---------------------------------------------------- STA function ----------------------------------------------------- -// ----------------------------------------------------------------------------------------------------------------------- - -bool ESP8266WiFiSTAClass::_useStaticIp = false; -bool ESP8266WiFiSTAClass::_useInsecureWEP = false; - -/** - * Start Wifi connection - * if passphrase is set the most secure supported mode will be automatically selected - * @param ssid const char* Pointer to the SSID string. - * @param passphrase const char * Optional. Passphrase. Valid characters in a passphrase must be between ASCII 32-126 (decimal). - * @param bssid uint8_t[6] Optional. BSSID / MAC of AP - * @param channel Optional. Channel of AP - * @param connect Optional. call connect - * @return - */ -wl_status_t ESP8266WiFiSTAClass::begin(const char* ssid, const char *passphrase, int32_t channel, const uint8_t* bssid, bool connect) { - - if(!WiFi.enableSTA(true)) { - // enable STA failed - return WL_CONNECT_FAILED; - } - - if(!ssid || *ssid == 0x00 || strlen(ssid) > 32) { - // fail SSID too long or missing! - return WL_CONNECT_FAILED; - } - - int passphraseLen = passphrase == nullptr ? 0 : strlen(passphrase); - if(passphraseLen > 64) { - // fail passphrase too long! - return WL_CONNECT_FAILED; - } - - struct station_config conf; - conf.threshold.authmode = (passphraseLen == 0) ? AUTH_OPEN : (_useInsecureWEP ? AUTH_WEP : AUTH_WPA_PSK); - - if(strlen(ssid) == 32) - memcpy(reinterpret_cast(conf.ssid), ssid, 32); //copied in without null term - else - strcpy(reinterpret_cast(conf.ssid), ssid); - - if(passphrase) { - if (passphraseLen == 64) // it's not a passphrase, is the PSK, which is copied into conf.password without null term - memcpy(reinterpret_cast(conf.password), passphrase, 64); - else - strcpy(reinterpret_cast(conf.password), passphrase); - } else { - *conf.password = 0; - } - - conf.threshold.rssi = -127; -#ifdef NONOSDK3V0 - conf.open_and_wep_mode_disable = !(_useInsecureWEP || *conf.password == 0); -#endif - - if(bssid) { - conf.bssid_set = 1; - memcpy((void *) &conf.bssid[0], (void *) bssid, 6); - } else { - conf.bssid_set = 0; - } - - struct station_config conf_compare; - if(WiFi._persistent){ - wifi_station_get_config_default(&conf_compare); - } - else { - wifi_station_get_config(&conf_compare); - } - - if(sta_config_equal(conf_compare, conf)) { - DEBUGV("sta config unchanged"); - } - else { - ETS_UART_INTR_DISABLE(); - - if(WiFi._persistent) { - wifi_station_set_config(&conf); - } else { - wifi_station_set_config_current(&conf); - } - - ETS_UART_INTR_ENABLE(); - } - - ETS_UART_INTR_DISABLE(); - if(connect) { - wifi_station_connect(); - } - ETS_UART_INTR_ENABLE(); - - if(channel > 0 && channel <= 13) { - wifi_set_channel(channel); - } - - if(!_useStaticIp) { - wifi_station_dhcpc_start(); - } - - return status(); -} - -wl_status_t ESP8266WiFiSTAClass::begin(char* ssid, char *passphrase, int32_t channel, const uint8_t* bssid, bool connect) { - return begin((const char*) ssid, (const char*) passphrase, channel, bssid, connect); -} - -wl_status_t ESP8266WiFiSTAClass::begin(const String& ssid, const String& passphrase, int32_t channel, const uint8_t* bssid, bool connect) { - return begin(ssid.c_str(), passphrase.c_str(), channel, bssid, connect); -} - -/** - * Use to connect to SDK config. - * @return wl_status_t - */ -wl_status_t ESP8266WiFiSTAClass::begin() { - - if(!WiFi.enableSTA(true)) { - // enable STA failed - return WL_CONNECT_FAILED; - } - - ETS_UART_INTR_DISABLE(); - wifi_station_connect(); - ETS_UART_INTR_ENABLE(); - - if(!_useStaticIp) { - wifi_station_dhcpc_start(); - } - return status(); -} - -/** - * Change IP configuration settings disabling the dhcp client - * @param local_ip Static ip configuration - * @param gateway Static gateway configuration - * @param subnet Static Subnet mask - * @param dns1 Static DNS server 1 - * @param dns2 Static DNS server 2 - */ - -#if LWIP_VERSION_MAJOR != 1 -/* - About the following call in the end of ESP8266WiFiSTAClass::config(): - netif_set_addr(eagle_lwip_getif(STATION_IF), &info.ip, &info.netmask, &info.gw); - - With lwip2, it is needed to trigger IP address change. - Recall: when lwip2 is enabled, lwip1 api is still used by espressif firmware - https://github.com/d-a-v/esp82xx-nonos-linklayer/tree/25d5e8186f710a230221021cba97727dbfdfd953#how-it-works - - We need first to disable the lwIP API redirection for netif_set_addr() so lwip1's call will be linked: - https://github.com/d-a-v/esp82xx-nonos-linklayer/blob/25d5e8186f710a230221021cba97727dbfdfd953/glue-lwip/arch/cc.h#L122 - - We also need to declare its prototype using ip4_addr_t instead of ip_addr_t because lwIP-1.x never has IPv6. - No need to worry about this #undef, this call is only needed in lwip2, and never used in arduino core code. - */ -#undef netif_set_addr // need to call lwIP-v1.4 netif_set_addr() -extern "C" struct netif* eagle_lwip_getif (int netif_index); -extern "C" void netif_set_addr (struct netif* netif, ip4_addr_t* ip, ip4_addr_t* netmask, ip4_addr_t* gw); -#endif - -bool ESP8266WiFiSTAClass::config(IPAddress local_ip, IPAddress arg1, IPAddress arg2, IPAddress arg3, IPAddress dns2) { - - if(!WiFi.enableSTA(true)) { - return false; - } - - //ESP argument order is: ip, gateway, subnet, dns1 - //Arduino arg order is: ip, dns, gateway, subnet. - - //first, check whether dhcp should be used, which is when ip == 0 && gateway == 0 && subnet == 0. - bool espOrderUseDHCP = (local_ip == 0U && arg1 == 0U && arg2 == 0U); - bool arduinoOrderUseDHCP = (local_ip == 0U && arg2 == 0U && arg3 == 0U); - if (espOrderUseDHCP || arduinoOrderUseDHCP) { - _useStaticIp = false; - wifi_station_dhcpc_start(); - return true; - } - - //To allow compatibility, check first octet of 3rd arg. If 255, interpret as ESP order, otherwise Arduino order. - IPAddress gateway = arg1; - IPAddress subnet = arg2; - IPAddress dns1 = arg3; - - if(subnet[0] != 255) - { - //octet is not 255 => interpret as Arduino order - gateway = arg2; - subnet = arg3[0] == 0 ? IPAddress(255,255,255,0) : arg3; //arg order is arduino and 4th arg not given => assign it arduino default - dns1 = arg1; - } - - // check whether all is IPv4 (or gateway not set) - if (!(local_ip.isV4() && subnet.isV4() && (!gateway.isSet() || gateway.isV4()))) { - return false; - } - - //ip and gateway must be in the same subnet - if((local_ip.v4() & subnet.v4()) != (gateway.v4() & subnet.v4())) { - return false; - } - - struct ip_info info; - info.ip.addr = local_ip.v4(); - info.gw.addr = gateway.v4(); - info.netmask.addr = subnet.v4(); - - wifi_station_dhcpc_stop(); - if(wifi_set_ip_info(STATION_IF, &info)) { - _useStaticIp = true; - } else { - return false; - } - - if(dns1.isSet()) { - // Set DNS1-Server - dns_setserver(0, dns1); - } - - if(dns2.isSet()) { - // Set DNS2-Server - dns_setserver(1, dns2); - } - -#if LWIP_VERSION_MAJOR != 1 && !CORE_MOCK - // trigger address change by calling lwIP-v1.4 api - // (see explanation above) - netif_set_addr(eagle_lwip_getif(STATION_IF), &info.ip, &info.netmask, &info.gw); -#endif - - return true; -} - -/** - * will force a disconnect an then start reconnecting to AP - * @return ok - */ -bool ESP8266WiFiSTAClass::reconnect() { - if((WiFi.getMode() & WIFI_STA) != 0) { - if(wifi_station_disconnect()) { - return wifi_station_connect(); - } - } - return false; -} - -/** - * Disconnect from the network - * @param wifioff - * @return one value of wl_status_t enum - */ -bool ESP8266WiFiSTAClass::disconnect(bool wifioff) { - bool ret; - struct station_config conf; - *conf.ssid = 0; - *conf.password = 0; - - ETS_UART_INTR_DISABLE(); - if(WiFi._persistent) { - wifi_station_set_config(&conf); - } else { - wifi_station_set_config_current(&conf); - } - ret = wifi_station_disconnect(); - ETS_UART_INTR_ENABLE(); - - if(wifioff) { - WiFi.enableSTA(false); - } - - return ret; -} - -/** - * is STA interface connected? - * @return true if STA is connected to an AD - */ -bool ESP8266WiFiSTAClass::isConnected() { - return (status() == WL_CONNECTED); -} - - -/** - * Setting the ESP8266 station to connect to the AP (which is recorded) - * automatically or not when powered on. Enable auto-connect by default. - * @param autoConnect bool - * @return if saved - */ -bool ESP8266WiFiSTAClass::setAutoConnect(bool autoConnect) { - bool ret; - ETS_UART_INTR_DISABLE(); - ret = wifi_station_set_auto_connect(autoConnect); - ETS_UART_INTR_ENABLE(); - return ret; -} - -/** - * Checks if ESP8266 station mode will connect to AP - * automatically or not when it is powered on. - * @return auto connect - */ -bool ESP8266WiFiSTAClass::getAutoConnect() { - return (wifi_station_get_auto_connect() != 0); -} - -/** - * Set whether reconnect or not when the ESP8266 station is disconnected from AP. - * @param autoReconnect - * @return - */ -bool ESP8266WiFiSTAClass::setAutoReconnect(bool autoReconnect) { - return wifi_station_set_reconnect_policy(autoReconnect); -} - -/** - * get whether reconnect or not when the ESP8266 station is disconnected from AP. - * @return autoreconnect - */ -bool ESP8266WiFiSTAClass::getAutoReconnect() { - return wifi_station_get_reconnect_policy(); -} - -/** - * Wait for WiFi connection to reach a result - * returns the status reached or disconnect if STA is off - * @return wl_status_t - */ -uint8_t ESP8266WiFiSTAClass::waitForConnectResult() { - //1 and 3 have STA enabled - if((wifi_get_opmode() & 1) == 0) { - return WL_DISCONNECTED; - } - while(status() == WL_DISCONNECTED) { - delay(100); - } - return status(); -} - -/** - * Get the station interface IP address. - * @return IPAddress station IP - */ -IPAddress ESP8266WiFiSTAClass::localIP() { - struct ip_info ip; - wifi_get_ip_info(STATION_IF, &ip); - return IPAddress(ip.ip.addr); -} - -/** - * Get the station interface MAC address. - * @param mac pointer to uint8_t array with length WL_MAC_ADDR_LENGTH - * @return pointer to uint8_t * - */ -uint8_t* ESP8266WiFiSTAClass::macAddress(uint8_t* mac) { - wifi_get_macaddr(STATION_IF, mac); - return mac; -} - -/** - * Get the station interface MAC address. - * @return String mac - */ -String ESP8266WiFiSTAClass::macAddress(void) { - uint8_t mac[6]; - char macStr[18] = { 0 }; - wifi_get_macaddr(STATION_IF, mac); - - sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return String(macStr); -} - -/** - * Get the interface subnet mask address. - * @return IPAddress subnetMask - */ -IPAddress ESP8266WiFiSTAClass::subnetMask() { - struct ip_info ip; - wifi_get_ip_info(STATION_IF, &ip); - return IPAddress(ip.netmask.addr); -} - -/** - * Get the gateway ip address. - * @return IPAddress gatewayIP - */ -IPAddress ESP8266WiFiSTAClass::gatewayIP() { - struct ip_info ip; - wifi_get_ip_info(STATION_IF, &ip); - return IPAddress(ip.gw.addr); -} - -/** - * Get the DNS ip address. - * @param dns_no - * @return IPAddress DNS Server IP - */ -IPAddress ESP8266WiFiSTAClass::dnsIP(uint8_t dns_no) { -#if LWIP_VERSION_MAJOR == 1 - ip_addr_t dns_ip = dns_getserver(dns_no); - return IPAddress(dns_ip.addr); -#else - return IPAddress(dns_getserver(dns_no)); -#endif -} - - -/** - * Get ESP8266 station DHCP hostname - * @return hostname - */ -String ESP8266WiFiSTAClass::hostname(void) { - return wifi_station_get_hostname(); -} - -/** - * Set ESP8266 station DHCP hostname - * @param aHostname max length:24 - * @return ok - */ -bool ESP8266WiFiSTAClass::hostname(const char* aHostname) { - /* - vvvv RFC952 vvvv - ASSUMPTIONS - 1. A "name" (Net, Host, Gateway, or Domain name) is a text string up - to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus - sign (-), and period (.). Note that periods are only allowed when - they serve to delimit components of "domain style names". (See - RFC-921, "Domain Name System Implementation Schedule", for - background). No blank or space characters are permitted as part of a - name. No distinction is made between upper and lower case. The first - character must be an alpha character. The last character must not be - a minus sign or period. A host which serves as a GATEWAY should have - "-GATEWAY" or "-GW" as part of its name. Hosts which do not serve as - Internet gateways should not use "-GATEWAY" and "-GW" as part of - their names. A host which is a TAC should have "-TAC" as the last - part of its host name, if it is a DoD host. Single character names - or nicknames are not allowed. - ^^^^ RFC952 ^^^^ - - - 24 chars max - - only a..z A..Z 0..9 '-' - - no '-' as last char - */ - - size_t len = strlen(aHostname); - - if (len == 0 || len > 32) { - // nonos-sdk limit is 32 - // (dhcp hostname option minimum size is ~60) - DEBUG_WIFI_GENERIC("WiFi.(set)hostname(): empty or large(>32) name\n"); - return false; - } - - // check RFC compliance - bool compliant = (len <= 24); - for (size_t i = 0; compliant && i < len; i++) - if (!isalnum(aHostname[i]) && aHostname[i] != '-') - compliant = false; - if (aHostname[len - 1] == '-') - compliant = false; - - if (!compliant) { - DEBUG_WIFI_GENERIC("hostname '%s' is not compliant with RFC952\n", aHostname); - } - - bool ret = wifi_station_set_hostname(aHostname); - if (!ret) { - DEBUG_WIFI_GENERIC("WiFi.hostname(%s): wifi_station_set_hostname() failed\n", aHostname); - return false; - } - - // now we should inform dhcp server for this change, using lwip_renew() - // looping through all existing interface - // harmless for AP, also compatible with ethernet adapters (to come) - for (netif* intf = netif_list; intf; intf = intf->next) { - - // unconditionally update all known interfaces -#if LWIP_VERSION_MAJOR == 1 - intf->hostname = (char*)wifi_station_get_hostname(); -#else - intf->hostname = wifi_station_get_hostname(); -#endif - - if (netif_dhcp_data(intf) != nullptr) { - // renew already started DHCP leases - err_t lwipret = dhcp_renew(intf); - if (lwipret != ERR_OK) { - DEBUG_WIFI_GENERIC("WiFi.hostname(%s): lwIP error %d on interface %c%c (index %d)\n", - intf->hostname, (int)lwipret, intf->name[0], intf->name[1], intf->num); - ret = false; - } - } - } - - return ret && compliant; -} - -/** - * Return Connection status. - * @return one of the value defined in wl_status_t - * - */ -wl_status_t ESP8266WiFiSTAClass::status() { - station_status_t status = wifi_station_get_connect_status(); - - switch(status) { - case STATION_GOT_IP: - return WL_CONNECTED; - case STATION_NO_AP_FOUND: - return WL_NO_SSID_AVAIL; - case STATION_CONNECT_FAIL: - case STATION_WRONG_PASSWORD: - return WL_CONNECT_FAILED; - case STATION_IDLE: - return WL_IDLE_STATUS; - default: - return WL_DISCONNECTED; - } -} - -/** - * Return the current SSID associated with the network - * @return SSID - */ -String ESP8266WiFiSTAClass::SSID() const { - struct station_config conf; - wifi_station_get_config(&conf); - char tmp[33]; //ssid can be up to 32chars, => plus null term - memcpy(tmp, conf.ssid, sizeof(conf.ssid)); - tmp[32] = 0; //nullterm in case of 32 char ssid - return String(reinterpret_cast(tmp)); -} - -/** - * Return the current pre shared key associated with the network - * @return psk string - */ -String ESP8266WiFiSTAClass::psk() const { - struct station_config conf; - wifi_station_get_config(&conf); - char tmp[65]; //psk is 64 bytes hex => plus null term - memcpy(tmp, conf.password, sizeof(conf.password)); - tmp[64] = 0; //null term in case of 64 byte psk - return String(reinterpret_cast(tmp)); -} - -/** - * Return the current bssid / mac associated with the network if configured - * @return bssid uint8_t * - */ -uint8_t* ESP8266WiFiSTAClass::BSSID(void) { - static struct station_config conf; - wifi_station_get_config(&conf); - return reinterpret_cast(conf.bssid); -} - -/** - * Return the current bssid / mac associated with the network if configured - * @return String bssid mac - */ -String ESP8266WiFiSTAClass::BSSIDstr(void) { - struct station_config conf; - char mac[18] = { 0 }; - wifi_station_get_config(&conf); - sprintf(mac, "%02X:%02X:%02X:%02X:%02X:%02X", conf.bssid[0], conf.bssid[1], conf.bssid[2], conf.bssid[3], conf.bssid[4], conf.bssid[5]); - return String(mac); -} - -/** - * Return the current network RSSI. - * @return RSSI value - */ -int32_t ESP8266WiFiSTAClass::RSSI(void) { - return wifi_station_get_rssi(); -} - - - -// ----------------------------------------------------------------------------------------------------------------------- -// -------------------------------------------------- STA remote configure ----------------------------------------------- -// ----------------------------------------------------------------------------------------------------------------------- - -bool ESP8266WiFiSTAClass::_smartConfigStarted = false; -bool ESP8266WiFiSTAClass::_smartConfigDone = false; - -/** - * Start SmartConfig - */ -bool ESP8266WiFiSTAClass::beginSmartConfig() { - if(_smartConfigStarted) { - return false; - } - - if(!WiFi.enableSTA(true)) { - // enable STA failed - return false; - } - - if(smartconfig_start(reinterpret_cast(&ESP8266WiFiSTAClass::_smartConfigCallback), 1)) { - _smartConfigStarted = true; - _smartConfigDone = false; - return true; - } - return false; -} - - -/** - * Stop SmartConfig - */ -bool ESP8266WiFiSTAClass::stopSmartConfig() { - if(!_smartConfigStarted) { - return true; - } - - if(smartconfig_stop()) { - _smartConfigStarted = false; - return true; - } - return false; -} - -/** - * Query SmartConfig status, to decide when stop config - * @return smartConfig Done - */ -bool ESP8266WiFiSTAClass::smartConfigDone() { - if(!_smartConfigStarted) { - return false; - } - - return _smartConfigDone; -} - - -/** - * _smartConfigCallback - * @param st - * @param result - */ -void ESP8266WiFiSTAClass::_smartConfigCallback(uint32_t st, void* result) { - sc_status status = (sc_status) st; - if(status == SC_STATUS_LINK) { - station_config* sta_conf = reinterpret_cast(result); - - wifi_station_set_config(sta_conf); - wifi_station_disconnect(); - wifi_station_connect(); - - _smartConfigDone = true; - } else if(status == SC_STATUS_LINK_OVER) { - WiFi.stopSmartConfig(); - } -} +/* + ESP8266WiFiSTA.cpp - WiFi library for esp8266 + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Reworked on 28 Dec 2015 by Markus Sattler + +*/ + +#include "ESP8266WiFi.h" +#include "ESP8266WiFiGeneric.h" +#include "ESP8266WiFiSTA.h" + +#include "c_types.h" +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "mem.h" +#include "user_interface.h" +#include "smartconfig.h" + +extern "C" { +#include "lwip/err.h" +#include "lwip/dns.h" +#include "lwip/dhcp.h" +#include "lwip/init.h" // LWIP_VERSION_ +#if LWIP_IPV6 +#include "lwip/netif.h" // struct netif +#endif +} + +#include "debug.h" + +extern "C" void esp_schedule(); +extern "C" void esp_yield(); + +// ----------------------------------------------------------------------------------------------------------------------- +// ---------------------------------------------------- Private functions ------------------------------------------------ +// ----------------------------------------------------------------------------------------------------------------------- + +static bool sta_config_equal(const station_config& lhs, const station_config& rhs); + + +/** + compare two STA configurations + @param lhs station_config + @param rhs station_config + @return equal +*/ +static bool sta_config_equal(const station_config& lhs, const station_config& rhs) +{ + +#ifdef NONOSDK3V0 + static_assert(sizeof(station_config) == 116, "struct station_config has changed, please update comparison function"); +#else + static_assert(sizeof(station_config) == 112, "struct station_config has changed, please update comparison function"); +#endif + + if (strncmp(reinterpret_cast(lhs.ssid), reinterpret_cast(rhs.ssid), sizeof(lhs.ssid)) != 0) + { + return false; + } + + //in case of password, use strncmp with size 64 to cover 64byte psk case (no null term) + if (strncmp(reinterpret_cast(lhs.password), reinterpret_cast(rhs.password), sizeof(lhs.password)) != 0) + { + return false; + } + + if (lhs.bssid_set != rhs.bssid_set) + { + return false; + } + + if (lhs.bssid_set) + { + if (memcmp(lhs.bssid, rhs.bssid, 6) != 0) + { + return false; + } + } + + if (lhs.threshold.rssi != rhs.threshold.rssi) + { + return false; + } + + if (lhs.threshold.authmode != rhs.threshold.authmode) + { + return false; + } + +#ifdef NONOSDK3V0 + if (lhs.open_and_wep_mode_disable != rhs.open_and_wep_mode_disable) + { + return false; + } +#endif + + return true; +} + +// ----------------------------------------------------------------------------------------------------------------------- +// ---------------------------------------------------- STA function ----------------------------------------------------- +// ----------------------------------------------------------------------------------------------------------------------- + +bool ESP8266WiFiSTAClass::_useStaticIp = false; +bool ESP8266WiFiSTAClass::_useInsecureWEP = false; + +/** + Start Wifi connection + if passphrase is set the most secure supported mode will be automatically selected + @param ssid const char* Pointer to the SSID string. + @param passphrase const char * Optional. Passphrase. Valid characters in a passphrase must be between ASCII 32-126 (decimal). + @param bssid uint8_t[6] Optional. BSSID / MAC of AP + @param channel Optional. Channel of AP + @param connect Optional. call connect + @return +*/ +wl_status_t ESP8266WiFiSTAClass::begin(const char* ssid, const char *passphrase, int32_t channel, const uint8_t* bssid, bool connect) +{ + + if (!WiFi.enableSTA(true)) + { + // enable STA failed + return WL_CONNECT_FAILED; + } + + if (!ssid || *ssid == 0x00 || strlen(ssid) > 32) + { + // fail SSID too long or missing! + return WL_CONNECT_FAILED; + } + + int passphraseLen = passphrase == nullptr ? 0 : strlen(passphrase); + if (passphraseLen > 64) + { + // fail passphrase too long! + return WL_CONNECT_FAILED; + } + + struct station_config conf; + conf.threshold.authmode = (passphraseLen == 0) ? AUTH_OPEN : (_useInsecureWEP ? AUTH_WEP : AUTH_WPA_PSK); + + if (strlen(ssid) == 32) + { + memcpy(reinterpret_cast(conf.ssid), ssid, 32); //copied in without null term + } + else + { + strcpy(reinterpret_cast(conf.ssid), ssid); + } + + if (passphrase) + { + if (passphraseLen == 64) // it's not a passphrase, is the PSK, which is copied into conf.password without null term + { + memcpy(reinterpret_cast(conf.password), passphrase, 64); + } + else + { + strcpy(reinterpret_cast(conf.password), passphrase); + } + } + else + { + *conf.password = 0; + } + + conf.threshold.rssi = -127; +#ifdef NONOSDK3V0 + conf.open_and_wep_mode_disable = !(_useInsecureWEP || *conf.password == 0); +#endif + + if (bssid) + { + conf.bssid_set = 1; + memcpy((void *) &conf.bssid[0], (void *) bssid, 6); + } + else + { + conf.bssid_set = 0; + } + + struct station_config conf_compare; + if (WiFi._persistent) + { + wifi_station_get_config_default(&conf_compare); + } + else + { + wifi_station_get_config(&conf_compare); + } + + if (sta_config_equal(conf_compare, conf)) + { + DEBUGV("sta config unchanged"); + } + else + { + ETS_UART_INTR_DISABLE(); + + if (WiFi._persistent) + { + wifi_station_set_config(&conf); + } + else + { + wifi_station_set_config_current(&conf); + } + + ETS_UART_INTR_ENABLE(); + } + + ETS_UART_INTR_DISABLE(); + if (connect) + { + wifi_station_connect(); + } + ETS_UART_INTR_ENABLE(); + + if (channel > 0 && channel <= 13) + { + wifi_set_channel(channel); + } + + if (!_useStaticIp) + { + wifi_station_dhcpc_start(); + } + + return status(); +} + +wl_status_t ESP8266WiFiSTAClass::begin(char* ssid, char *passphrase, int32_t channel, const uint8_t* bssid, bool connect) +{ + return begin((const char*) ssid, (const char*) passphrase, channel, bssid, connect); +} + +wl_status_t ESP8266WiFiSTAClass::begin(const String& ssid, const String& passphrase, int32_t channel, const uint8_t* bssid, bool connect) +{ + return begin(ssid.c_str(), passphrase.c_str(), channel, bssid, connect); +} + +/** + Use to connect to SDK config. + @return wl_status_t +*/ +wl_status_t ESP8266WiFiSTAClass::begin() +{ + + if (!WiFi.enableSTA(true)) + { + // enable STA failed + return WL_CONNECT_FAILED; + } + + ETS_UART_INTR_DISABLE(); + wifi_station_connect(); + ETS_UART_INTR_ENABLE(); + + if (!_useStaticIp) + { + wifi_station_dhcpc_start(); + } + return status(); +} + +/** + Change IP configuration settings disabling the dhcp client + @param local_ip Static ip configuration + @param gateway Static gateway configuration + @param subnet Static Subnet mask + @param dns1 Static DNS server 1 + @param dns2 Static DNS server 2 +*/ + +#if LWIP_VERSION_MAJOR != 1 +/* + About the following call in the end of ESP8266WiFiSTAClass::config(): + netif_set_addr(eagle_lwip_getif(STATION_IF), &info.ip, &info.netmask, &info.gw); + + With lwip2, it is needed to trigger IP address change. + Recall: when lwip2 is enabled, lwip1 api is still used by espressif firmware + https://github.com/d-a-v/esp82xx-nonos-linklayer/tree/25d5e8186f710a230221021cba97727dbfdfd953#how-it-works + + We need first to disable the lwIP API redirection for netif_set_addr() so lwip1's call will be linked: + https://github.com/d-a-v/esp82xx-nonos-linklayer/blob/25d5e8186f710a230221021cba97727dbfdfd953/glue-lwip/arch/cc.h#L122 + + We also need to declare its prototype using ip4_addr_t instead of ip_addr_t because lwIP-1.x never has IPv6. + No need to worry about this #undef, this call is only needed in lwip2, and never used in arduino core code. +*/ +#undef netif_set_addr // need to call lwIP-v1.4 netif_set_addr() +extern "C" struct netif* eagle_lwip_getif(int netif_index); +extern "C" void netif_set_addr(struct netif* netif, ip4_addr_t* ip, ip4_addr_t* netmask, ip4_addr_t* gw); +#endif + +bool ESP8266WiFiSTAClass::config(IPAddress local_ip, IPAddress arg1, IPAddress arg2, IPAddress arg3, IPAddress dns2) +{ + + if (!WiFi.enableSTA(true)) + { + return false; + } + + //ESP argument order is: ip, gateway, subnet, dns1 + //Arduino arg order is: ip, dns, gateway, subnet. + + //first, check whether dhcp should be used, which is when ip == 0 && gateway == 0 && subnet == 0. + bool espOrderUseDHCP = (local_ip == 0U && arg1 == 0U && arg2 == 0U); + bool arduinoOrderUseDHCP = (local_ip == 0U && arg2 == 0U && arg3 == 0U); + if (espOrderUseDHCP || arduinoOrderUseDHCP) + { + _useStaticIp = false; + wifi_station_dhcpc_start(); + return true; + } + + //To allow compatibility, check first octet of 3rd arg. If 255, interpret as ESP order, otherwise Arduino order. + IPAddress gateway = arg1; + IPAddress subnet = arg2; + IPAddress dns1 = arg3; + + if (subnet[0] != 255) + { + //octet is not 255 => interpret as Arduino order + gateway = arg2; + subnet = arg3[0] == 0 ? IPAddress(255, 255, 255, 0) : arg3; //arg order is arduino and 4th arg not given => assign it arduino default + dns1 = arg1; + } + + // check whether all is IPv4 (or gateway not set) + if (!(local_ip.isV4() && subnet.isV4() && (!gateway.isSet() || gateway.isV4()))) + { + return false; + } + + //ip and gateway must be in the same subnet + if ((local_ip.v4() & subnet.v4()) != (gateway.v4() & subnet.v4())) + { + return false; + } + + struct ip_info info; + info.ip.addr = local_ip.v4(); + info.gw.addr = gateway.v4(); + info.netmask.addr = subnet.v4(); + + wifi_station_dhcpc_stop(); + if (wifi_set_ip_info(STATION_IF, &info)) + { + _useStaticIp = true; + } + else + { + return false; + } + + if (dns1.isSet()) + { + // Set DNS1-Server + dns_setserver(0, dns1); + } + + if (dns2.isSet()) + { + // Set DNS2-Server + dns_setserver(1, dns2); + } + +#if LWIP_VERSION_MAJOR != 1 && !CORE_MOCK + // trigger address change by calling lwIP-v1.4 api + // (see explanation above) + netif_set_addr(eagle_lwip_getif(STATION_IF), &info.ip, &info.netmask, &info.gw); +#endif + + return true; +} + +/** + will force a disconnect an then start reconnecting to AP + @return ok +*/ +bool ESP8266WiFiSTAClass::reconnect() +{ + if ((WiFi.getMode() & WIFI_STA) != 0) + { + if (wifi_station_disconnect()) + { + return wifi_station_connect(); + } + } + return false; +} + +/** + Disconnect from the network + @param wifioff + @return one value of wl_status_t enum +*/ +bool ESP8266WiFiSTAClass::disconnect(bool wifioff) +{ + bool ret; + struct station_config conf; + *conf.ssid = 0; + *conf.password = 0; + + ETS_UART_INTR_DISABLE(); + if (WiFi._persistent) + { + wifi_station_set_config(&conf); + } + else + { + wifi_station_set_config_current(&conf); + } + ret = wifi_station_disconnect(); + ETS_UART_INTR_ENABLE(); + + if (wifioff) + { + WiFi.enableSTA(false); + } + + return ret; +} + +/** + is STA interface connected? + @return true if STA is connected to an AD +*/ +bool ESP8266WiFiSTAClass::isConnected() +{ + return (status() == WL_CONNECTED); +} + + +/** + Setting the ESP8266 station to connect to the AP (which is recorded) + automatically or not when powered on. Enable auto-connect by default. + @param autoConnect bool + @return if saved +*/ +bool ESP8266WiFiSTAClass::setAutoConnect(bool autoConnect) +{ + bool ret; + ETS_UART_INTR_DISABLE(); + ret = wifi_station_set_auto_connect(autoConnect); + ETS_UART_INTR_ENABLE(); + return ret; +} + +/** + Checks if ESP8266 station mode will connect to AP + automatically or not when it is powered on. + @return auto connect +*/ +bool ESP8266WiFiSTAClass::getAutoConnect() +{ + return (wifi_station_get_auto_connect() != 0); +} + +/** + Set whether reconnect or not when the ESP8266 station is disconnected from AP. + @param autoReconnect + @return +*/ +bool ESP8266WiFiSTAClass::setAutoReconnect(bool autoReconnect) +{ + return wifi_station_set_reconnect_policy(autoReconnect); +} + +/** + get whether reconnect or not when the ESP8266 station is disconnected from AP. + @return autoreconnect +*/ +bool ESP8266WiFiSTAClass::getAutoReconnect() +{ + return wifi_station_get_reconnect_policy(); +} + +/** + Wait for WiFi connection to reach a result + returns the status reached or disconnect if STA is off + @return wl_status_t +*/ +uint8_t ESP8266WiFiSTAClass::waitForConnectResult() +{ + //1 and 3 have STA enabled + if ((wifi_get_opmode() & 1) == 0) + { + return WL_DISCONNECTED; + } + while (status() == WL_DISCONNECTED) + { + delay(100); + } + return status(); +} + +/** + Get the station interface IP address. + @return IPAddress station IP +*/ +IPAddress ESP8266WiFiSTAClass::localIP() +{ + struct ip_info ip; + wifi_get_ip_info(STATION_IF, &ip); + return IPAddress(ip.ip.addr); +} + +/** + Get the station interface MAC address. + @param mac pointer to uint8_t array with length WL_MAC_ADDR_LENGTH + @return pointer to uint8_t +*/ +uint8_t* ESP8266WiFiSTAClass::macAddress(uint8_t* mac) +{ + wifi_get_macaddr(STATION_IF, mac); + return mac; +} + +/** + Get the station interface MAC address. + @return String mac +*/ +String ESP8266WiFiSTAClass::macAddress(void) +{ + uint8_t mac[6]; + char macStr[18] = { 0 }; + wifi_get_macaddr(STATION_IF, mac); + + sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return String(macStr); +} + +/** + Get the interface subnet mask address. + @return IPAddress subnetMask +*/ +IPAddress ESP8266WiFiSTAClass::subnetMask() +{ + struct ip_info ip; + wifi_get_ip_info(STATION_IF, &ip); + return IPAddress(ip.netmask.addr); +} + +/** + Get the gateway ip address. + @return IPAddress gatewayIP +*/ +IPAddress ESP8266WiFiSTAClass::gatewayIP() +{ + struct ip_info ip; + wifi_get_ip_info(STATION_IF, &ip); + return IPAddress(ip.gw.addr); +} + +/** + Get the DNS ip address. + @param dns_no + @return IPAddress DNS Server IP +*/ +IPAddress ESP8266WiFiSTAClass::dnsIP(uint8_t dns_no) +{ +#if LWIP_VERSION_MAJOR == 1 + ip_addr_t dns_ip = dns_getserver(dns_no); + return IPAddress(dns_ip.addr); +#else + return IPAddress(dns_getserver(dns_no)); +#endif +} + + +/** + Get ESP8266 station DHCP hostname + @return hostname +*/ +String ESP8266WiFiSTAClass::hostname(void) +{ + return wifi_station_get_hostname(); +} + +/** + Set ESP8266 station DHCP hostname + @param aHostname max length:24 + @return ok +*/ +bool ESP8266WiFiSTAClass::hostname(const char* aHostname) +{ + /* + vvvv RFC952 vvvv + ASSUMPTIONS + 1. A "name" (Net, Host, Gateway, or Domain name) is a text string up + to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus + sign (-), and period (.). Note that periods are only allowed when + they serve to delimit components of "domain style names". (See + RFC-921, "Domain Name System Implementation Schedule", for + background). No blank or space characters are permitted as part of a + name. No distinction is made between upper and lower case. The first + character must be an alpha character. The last character must not be + a minus sign or period. A host which serves as a GATEWAY should have + "-GATEWAY" or "-GW" as part of its name. Hosts which do not serve as + Internet gateways should not use "-GATEWAY" and "-GW" as part of + their names. A host which is a TAC should have "-TAC" as the last + part of its host name, if it is a DoD host. Single character names + or nicknames are not allowed. + ^^^^ RFC952 ^^^^ + + - 24 chars max + - only a..z A..Z 0..9 '-' + - no '-' as last char + */ + + size_t len = strlen(aHostname); + + if (len == 0 || len > 32) + { + // nonos-sdk limit is 32 + // (dhcp hostname option minimum size is ~60) + DEBUG_WIFI_GENERIC("WiFi.(set)hostname(): empty or large(>32) name\n"); + return false; + } + + // check RFC compliance + bool compliant = (len <= 24); + for (size_t i = 0; compliant && i < len; i++) + if (!isalnum(aHostname[i]) && aHostname[i] != '-') + { + compliant = false; + } + if (aHostname[len - 1] == '-') + { + compliant = false; + } + + if (!compliant) + { + DEBUG_WIFI_GENERIC("hostname '%s' is not compliant with RFC952\n", aHostname); + } + + bool ret = wifi_station_set_hostname(aHostname); + if (!ret) + { + DEBUG_WIFI_GENERIC("WiFi.hostname(%s): wifi_station_set_hostname() failed\n", aHostname); + return false; + } + + // now we should inform dhcp server for this change, using lwip_renew() + // looping through all existing interface + // harmless for AP, also compatible with ethernet adapters (to come) + for (netif* intf = netif_list; intf; intf = intf->next) + { + + // unconditionally update all known interfaces +#if LWIP_VERSION_MAJOR == 1 + intf->hostname = (char*)wifi_station_get_hostname(); +#else + intf->hostname = wifi_station_get_hostname(); +#endif + + if (netif_dhcp_data(intf) != nullptr) + { + // renew already started DHCP leases + err_t lwipret = dhcp_renew(intf); + if (lwipret != ERR_OK) + { + DEBUG_WIFI_GENERIC("WiFi.hostname(%s): lwIP error %d on interface %c%c (index %d)\n", + intf->hostname, (int)lwipret, intf->name[0], intf->name[1], intf->num); + ret = false; + } + } + } + + return ret && compliant; +} + +/** + Return Connection status. + @return one of the value defined in wl_status_t + +*/ +wl_status_t ESP8266WiFiSTAClass::status() +{ + station_status_t status = wifi_station_get_connect_status(); + + switch (status) + { + case STATION_GOT_IP: + return WL_CONNECTED; + case STATION_NO_AP_FOUND: + return WL_NO_SSID_AVAIL; + case STATION_CONNECT_FAIL: + case STATION_WRONG_PASSWORD: + return WL_CONNECT_FAILED; + case STATION_IDLE: + return WL_IDLE_STATUS; + default: + return WL_DISCONNECTED; + } +} + +/** + Return the current SSID associated with the network + @return SSID +*/ +String ESP8266WiFiSTAClass::SSID() const +{ + struct station_config conf; + wifi_station_get_config(&conf); + char tmp[33]; //ssid can be up to 32chars, => plus null term + memcpy(tmp, conf.ssid, sizeof(conf.ssid)); + tmp[32] = 0; //nullterm in case of 32 char ssid + return String(reinterpret_cast(tmp)); +} + +/** + Return the current pre shared key associated with the network + @return psk string +*/ +String ESP8266WiFiSTAClass::psk() const +{ + struct station_config conf; + wifi_station_get_config(&conf); + char tmp[65]; //psk is 64 bytes hex => plus null term + memcpy(tmp, conf.password, sizeof(conf.password)); + tmp[64] = 0; //null term in case of 64 byte psk + return String(reinterpret_cast(tmp)); +} + +/** + Return the current bssid / mac associated with the network if configured + @return bssid uint8_t +*/ +uint8_t* ESP8266WiFiSTAClass::BSSID(void) +{ + static struct station_config conf; + wifi_station_get_config(&conf); + return reinterpret_cast(conf.bssid); +} + +/** + Return the current bssid / mac associated with the network if configured + @return String bssid mac +*/ +String ESP8266WiFiSTAClass::BSSIDstr(void) +{ + struct station_config conf; + char mac[18] = { 0 }; + wifi_station_get_config(&conf); + sprintf(mac, "%02X:%02X:%02X:%02X:%02X:%02X", conf.bssid[0], conf.bssid[1], conf.bssid[2], conf.bssid[3], conf.bssid[4], conf.bssid[5]); + return String(mac); +} + +/** + Return the current network RSSI. + @return RSSI value +*/ +int32_t ESP8266WiFiSTAClass::RSSI(void) +{ + return wifi_station_get_rssi(); +} + + + +// ----------------------------------------------------------------------------------------------------------------------- +// -------------------------------------------------- STA remote configure ----------------------------------------------- +// ----------------------------------------------------------------------------------------------------------------------- + +bool ESP8266WiFiSTAClass::_smartConfigStarted = false; +bool ESP8266WiFiSTAClass::_smartConfigDone = false; + +/** + Start SmartConfig +*/ +bool ESP8266WiFiSTAClass::beginSmartConfig() +{ + if (_smartConfigStarted) + { + return false; + } + + if (!WiFi.enableSTA(true)) + { + // enable STA failed + return false; + } + + if (smartconfig_start(reinterpret_cast(&ESP8266WiFiSTAClass::_smartConfigCallback), 1)) + { + _smartConfigStarted = true; + _smartConfigDone = false; + return true; + } + return false; +} + + +/** + Stop SmartConfig +*/ +bool ESP8266WiFiSTAClass::stopSmartConfig() +{ + if (!_smartConfigStarted) + { + return true; + } + + if (smartconfig_stop()) + { + _smartConfigStarted = false; + return true; + } + return false; +} + +/** + Query SmartConfig status, to decide when stop config + @return smartConfig Done +*/ +bool ESP8266WiFiSTAClass::smartConfigDone() +{ + if (!_smartConfigStarted) + { + return false; + } + + return _smartConfigDone; +} + + +/** + _smartConfigCallback + @param st + @param result +*/ +void ESP8266WiFiSTAClass::_smartConfigCallback(uint32_t st, void* result) +{ + sc_status status = (sc_status) st; + if (status == SC_STATUS_LINK) + { + station_config* sta_conf = reinterpret_cast(result); + + wifi_station_set_config(sta_conf); + wifi_station_disconnect(); + wifi_station_connect(); + + _smartConfigDone = true; + } + else if (status == SC_STATUS_LINK_OVER) + { + WiFi.stopSmartConfig(); + } +} diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h index f8c352cca7..f9adad792c 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h @@ -1,114 +1,121 @@ -/* - ESP8266WiFiSTA.h - esp8266 Wifi support. - Based on WiFi.h from Ardiono WiFi shield library. - Copyright (c) 2011-2014 Arduino. All right reserved. - Modified by Ivan Grokhotkov, December 2014 - Reworked by Markus Sattler, December 2015 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ESP8266WIFISTA_H_ -#define ESP8266WIFISTA_H_ - - -#include "ESP8266WiFiType.h" -#include "ESP8266WiFiGeneric.h" -#include "user_interface.h" - - -class ESP8266WiFiSTAClass { - // ---------------------------------------------------------------------------------------------- - // ---------------------------------------- STA function ---------------------------------------- - // ---------------------------------------------------------------------------------------------- - - public: - - wl_status_t begin(const char* ssid, const char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true); - wl_status_t begin(char* ssid, char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true); - wl_status_t begin(const String& ssid, const String& passphrase = emptyString, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true); - wl_status_t begin(); - - //The argument order for ESP is not the same as for Arduino. However, there is compatibility code under the hood - //to detect Arduino arg order, and handle it correctly. Be aware that the Arduino default value handling doesn't - //work here (see Arduino docs for gway/subnet defaults). In other words: at least 3 args must always be given. - bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = (uint32_t)0x00000000, IPAddress dns2 = (uint32_t)0x00000000); - - bool reconnect(); - bool disconnect(bool wifioff = false); - - bool isConnected(); - - bool setAutoConnect(bool autoConnect); - bool getAutoConnect(); - - bool setAutoReconnect(bool autoReconnect); - bool getAutoReconnect(); - - uint8_t waitForConnectResult(); - - // STA network info - IPAddress localIP(); - - uint8_t * macAddress(uint8_t* mac); - String macAddress(); - - IPAddress subnetMask(); - IPAddress gatewayIP(); - IPAddress dnsIP(uint8_t dns_no = 0); - - String hostname(); - bool hostname(const String& aHostname) { return hostname(aHostname.c_str()); } - bool hostname(const char* aHostname); - - // STA WiFi info - wl_status_t status(); - String SSID() const; - String psk() const; - - uint8_t * BSSID(); - String BSSIDstr(); - - int32_t RSSI(); - - static void enableInsecureWEP (bool enable = true) { _useInsecureWEP = enable; } - - protected: - - static bool _useStaticIp; - static bool _useInsecureWEP; - - // ---------------------------------------------------------------------------------------------- - // ------------------------------------ STA remote configure ----------------------------------- - // ---------------------------------------------------------------------------------------------- - - public: - - bool beginWPSConfig(void); - bool beginSmartConfig(); - bool stopSmartConfig(); - bool smartConfigDone(); - - protected: - - static bool _smartConfigStarted; - static bool _smartConfigDone; - - static void _smartConfigCallback(uint32_t status, void* result); - -}; - - -#endif /* ESP8266WIFISTA_H_ */ +/* + ESP8266WiFiSTA.h - esp8266 Wifi support. + Based on WiFi.h from Ardiono WiFi shield library. + Copyright (c) 2011-2014 Arduino. All right reserved. + Modified by Ivan Grokhotkov, December 2014 + Reworked by Markus Sattler, December 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ESP8266WIFISTA_H_ +#define ESP8266WIFISTA_H_ + + +#include "ESP8266WiFiType.h" +#include "ESP8266WiFiGeneric.h" +#include "user_interface.h" + + +class ESP8266WiFiSTAClass +{ + // ---------------------------------------------------------------------------------------------- + // ---------------------------------------- STA function ---------------------------------------- + // ---------------------------------------------------------------------------------------------- + +public: + + wl_status_t begin(const char* ssid, const char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true); + wl_status_t begin(char* ssid, char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true); + wl_status_t begin(const String& ssid, const String& passphrase = emptyString, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true); + wl_status_t begin(); + + //The argument order for ESP is not the same as for Arduino. However, there is compatibility code under the hood + //to detect Arduino arg order, and handle it correctly. Be aware that the Arduino default value handling doesn't + //work here (see Arduino docs for gway/subnet defaults). In other words: at least 3 args must always be given. + bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = (uint32_t)0x00000000, IPAddress dns2 = (uint32_t)0x00000000); + + bool reconnect(); + bool disconnect(bool wifioff = false); + + bool isConnected(); + + bool setAutoConnect(bool autoConnect); + bool getAutoConnect(); + + bool setAutoReconnect(bool autoReconnect); + bool getAutoReconnect(); + + uint8_t waitForConnectResult(); + + // STA network info + IPAddress localIP(); + + uint8_t * macAddress(uint8_t* mac); + String macAddress(); + + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsIP(uint8_t dns_no = 0); + + String hostname(); + bool hostname(const String& aHostname) + { + return hostname(aHostname.c_str()); + } + bool hostname(const char* aHostname); + + // STA WiFi info + wl_status_t status(); + String SSID() const; + String psk() const; + + uint8_t * BSSID(); + String BSSIDstr(); + + int32_t RSSI(); + + static void enableInsecureWEP(bool enable = true) + { + _useInsecureWEP = enable; + } + +protected: + + static bool _useStaticIp; + static bool _useInsecureWEP; + + // ---------------------------------------------------------------------------------------------- + // ------------------------------------ STA remote configure ----------------------------------- + // ---------------------------------------------------------------------------------------------- + +public: + + bool beginWPSConfig(void); + bool beginSmartConfig(); + bool stopSmartConfig(); + bool smartConfigDone(); + +protected: + + static bool _smartConfigStarted; + static bool _smartConfigDone; + + static void _smartConfigCallback(uint32_t status, void* result); + +}; + + +#endif /* ESP8266WIFISTA_H_ */ diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp index 1464692437..8900f5d57b 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp @@ -1,343 +1,386 @@ -/* - ESP8266WiFiScan.cpp - WiFi library for esp8266 - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Reworked on 28 Dec 2015 by Markus Sattler - - */ - -#include "ESP8266WiFi.h" -#include "ESP8266WiFiGeneric.h" -#include "ESP8266WiFiScan.h" - -extern "C" { -#include "c_types.h" -#include "ets_sys.h" -#include "os_type.h" -#include "osapi.h" -#include "mem.h" -#include "user_interface.h" -} - -#include "debug.h" - -extern "C" void esp_schedule(); -extern "C" void esp_yield(); - -// ----------------------------------------------------------------------------------------------------------------------- -// ---------------------------------------------------- Private functions ------------------------------------------------ -// ----------------------------------------------------------------------------------------------------------------------- - - - - -// ----------------------------------------------------------------------------------------------------------------------- -// ----------------------------------------------------- scan function --------------------------------------------------- -// ----------------------------------------------------------------------------------------------------------------------- - -bool ESP8266WiFiScanClass::_scanAsync = false; -bool ESP8266WiFiScanClass::_scanStarted = false; -bool ESP8266WiFiScanClass::_scanComplete = false; - -size_t ESP8266WiFiScanClass::_scanCount = 0; -void* ESP8266WiFiScanClass::_scanResult = 0; - -std::function ESP8266WiFiScanClass::_onComplete; - -/** - * Start scan WiFi networks available - * @param async run in async mode - * @param show_hidden show hidden networks - * @param channel scan only this channel (0 for all channels) - * @param ssid* scan for only this ssid (NULL for all ssid's) - * @return Number of discovered networks - */ -int8_t ESP8266WiFiScanClass::scanNetworks(bool async, bool show_hidden, uint8 channel, uint8* ssid) { - if(ESP8266WiFiScanClass::_scanStarted) { - return WIFI_SCAN_RUNNING; - } - - ESP8266WiFiScanClass::_scanAsync = async; - - WiFi.enableSTA(true); - - int status = wifi_station_get_connect_status(); - if(status != STATION_GOT_IP && status != STATION_IDLE) { - wifi_station_disconnect(); - } - - scanDelete(); - - struct scan_config config; - memset(&config, 0, sizeof(config)); - config.ssid = ssid; - config.channel = channel; - config.show_hidden = show_hidden; - if(wifi_station_scan(&config, reinterpret_cast(&ESP8266WiFiScanClass::_scanDone))) { - ESP8266WiFiScanClass::_scanComplete = false; - ESP8266WiFiScanClass::_scanStarted = true; - - if(ESP8266WiFiScanClass::_scanAsync) { - delay(0); // time for the OS to trigger the scan - return WIFI_SCAN_RUNNING; - } - - esp_yield(); - return ESP8266WiFiScanClass::_scanCount; - } else { - return WIFI_SCAN_FAILED; - } - -} - -/** - * Starts scanning WiFi networks available in async mode - * @param onComplete the event handler executed when the scan is done - * @param show_hidden show hidden networks - */ -void ESP8266WiFiScanClass::scanNetworksAsync(std::function onComplete, bool show_hidden) { - _onComplete = onComplete; - scanNetworks(true, show_hidden); -} - -/** - * called to get the scan state in Async mode - * @return scan result or status - * -1 if scan not fin - * -2 if scan not triggered - */ -int8_t ESP8266WiFiScanClass::scanComplete() { - - if(_scanStarted) { - return WIFI_SCAN_RUNNING; - } - - if(_scanComplete) { - return ESP8266WiFiScanClass::_scanCount; - } - - return WIFI_SCAN_FAILED; -} - -/** - * delete last scan result from RAM - */ -void ESP8266WiFiScanClass::scanDelete() { - if(ESP8266WiFiScanClass::_scanResult) { - delete[] reinterpret_cast(ESP8266WiFiScanClass::_scanResult); - ESP8266WiFiScanClass::_scanResult = 0; - ESP8266WiFiScanClass::_scanCount = 0; - } - _scanComplete = false; -} - - -/** - * loads all infos from a scanned wifi in to the ptr parameters - * @param networkItem uint8_t - * @param ssid const char** - * @param encryptionType uint8_t * - * @param RSSI int32_t * - * @param BSSID uint8_t ** - * @param channel int32_t * - * @param isHidden bool * - * @return (true if ok) - */ -bool ESP8266WiFiScanClass::getNetworkInfo(uint8_t i, String &ssid, uint8_t &encType, int32_t &rssi, uint8_t* &bssid, int32_t &channel, bool &isHidden) { - struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); - if(!it) { - return false; - } - - char ssid_copy[33]; // Ensure space for maximum len SSID (32) plus trailing 0 - memcpy(ssid_copy, it->ssid, sizeof(it->ssid)); - ssid_copy[32] = 0; // Potentially add 0-termination if none present earlier - ssid = (const char*) ssid_copy; - encType = encryptionType(i); - rssi = it->rssi; - bssid = it->bssid; // move ptr - channel = it->channel; - isHidden = (it->is_hidden != 0); - - return true; -} - - -/** - * Return the SSID discovered during the network scan. - * @param i specify from which network item want to get the information - * @return ssid string of the specified item on the networks scanned list - */ -String ESP8266WiFiScanClass::SSID(uint8_t i) { - struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); - if(!it) { - return ""; - } - char tmp[33]; //ssid can be up to 32chars, => plus null term - memcpy(tmp, it->ssid, sizeof(it->ssid)); - tmp[32] = 0; //nullterm in case of 32 char ssid - - return String(reinterpret_cast(tmp)); -} - - -/** - * Return the encryption type of the networks discovered during the scanNetworks - * @param i specify from which network item want to get the information - * @return encryption type (enum wl_enc_type) of the specified item on the networks scanned list - */ -uint8_t ESP8266WiFiScanClass::encryptionType(uint8_t i) { - struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); - if(!it) { - return -1; - } - - switch(it->authmode) { - case AUTH_OPEN: - return ENC_TYPE_NONE; - case AUTH_WEP: - return ENC_TYPE_WEP; - case AUTH_WPA_PSK: - return ENC_TYPE_TKIP; - case AUTH_WPA2_PSK: - return ENC_TYPE_CCMP; - case AUTH_WPA_WPA2_PSK: - return ENC_TYPE_AUTO; - default: - return -1; - } -} - -/** - * Return the RSSI of the networks discovered during the scanNetworks - * @param i specify from which network item want to get the information - * @return signed value of RSSI of the specified item on the networks scanned list - */ -int32_t ESP8266WiFiScanClass::RSSI(uint8_t i) { - struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); - if(!it) { - return 0; - } - return it->rssi; -} - - -/** - * return MAC / BSSID of scanned wifi - * @param i specify from which network item want to get the information - * @return uint8_t * MAC / BSSID of scanned wifi - */ -uint8_t * ESP8266WiFiScanClass::BSSID(uint8_t i) { - struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); - if(!it) { - return 0; - } - return it->bssid; -} - -/** - * return MAC / BSSID of scanned wifi - * @param i specify from which network item want to get the information - * @return String MAC / BSSID of scanned wifi - */ -String ESP8266WiFiScanClass::BSSIDstr(uint8_t i) { - char mac[18] = { 0 }; - struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); - if(!it) { - return String(""); - } - sprintf(mac, "%02X:%02X:%02X:%02X:%02X:%02X", it->bssid[0], it->bssid[1], it->bssid[2], it->bssid[3], it->bssid[4], it->bssid[5]); - return String(mac); -} - -int32_t ESP8266WiFiScanClass::channel(uint8_t i) { - struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); - if(!it) { - return 0; - } - return it->channel; -} - -/** - * return if the scanned wifi is Hidden (no SSID) - * @param networkItem specify from which network item want to get the information - * @return bool (true == hidden) - */ -bool ESP8266WiFiScanClass::isHidden(uint8_t i) { - struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); - if(!it) { - return false; - } - return (it->is_hidden != 0); -} - -/** - * private - * scan callback - * @param result void *arg - * @param status STATUS - */ -void ESP8266WiFiScanClass::_scanDone(void* result, int status) { - if(status != OK) { - ESP8266WiFiScanClass::_scanCount = 0; - ESP8266WiFiScanClass::_scanResult = 0; - } else { - - int i = 0; - bss_info* head = reinterpret_cast(result); - - for(bss_info* it = head; it; it = STAILQ_NEXT(it, next), ++i) - ; - ESP8266WiFiScanClass::_scanCount = i; - if(i == 0) { - ESP8266WiFiScanClass::_scanResult = 0; - } else { - bss_info* copied_info = new bss_info[i]; - i = 0; - for(bss_info* it = head; it; it = STAILQ_NEXT(it, next), ++i) { - memcpy(copied_info + i, it, sizeof(bss_info)); - } - - ESP8266WiFiScanClass::_scanResult = copied_info; - } - - } - - ESP8266WiFiScanClass::_scanStarted = false; - ESP8266WiFiScanClass::_scanComplete = true; - - if(!ESP8266WiFiScanClass::_scanAsync) { - esp_schedule(); - } else if (ESP8266WiFiScanClass::_onComplete) { - ESP8266WiFiScanClass::_onComplete(ESP8266WiFiScanClass::_scanCount); - ESP8266WiFiScanClass::_onComplete = nullptr; - } -} - -/** - * - * @param i specify from which network item want to get the information - * @return bss_info * - */ -void * ESP8266WiFiScanClass::_getScanInfoByIndex(int i) { - if(!ESP8266WiFiScanClass::_scanResult || (size_t) i > ESP8266WiFiScanClass::_scanCount) { - return 0; - } - return reinterpret_cast(ESP8266WiFiScanClass::_scanResult) + i; -} +/* + ESP8266WiFiScan.cpp - WiFi library for esp8266 + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Reworked on 28 Dec 2015 by Markus Sattler + +*/ + +#include "ESP8266WiFi.h" +#include "ESP8266WiFiGeneric.h" +#include "ESP8266WiFiScan.h" + +extern "C" { +#include "c_types.h" +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "mem.h" +#include "user_interface.h" +} + +#include "debug.h" + +extern "C" void esp_schedule(); +extern "C" void esp_yield(); + +// ----------------------------------------------------------------------------------------------------------------------- +// ---------------------------------------------------- Private functions ------------------------------------------------ +// ----------------------------------------------------------------------------------------------------------------------- + + + + +// ----------------------------------------------------------------------------------------------------------------------- +// ----------------------------------------------------- scan function --------------------------------------------------- +// ----------------------------------------------------------------------------------------------------------------------- + +bool ESP8266WiFiScanClass::_scanAsync = false; +bool ESP8266WiFiScanClass::_scanStarted = false; +bool ESP8266WiFiScanClass::_scanComplete = false; + +size_t ESP8266WiFiScanClass::_scanCount = 0; +void* ESP8266WiFiScanClass::_scanResult = 0; + +std::function ESP8266WiFiScanClass::_onComplete; + +/** + Start scan WiFi networks available + @param async run in async mode + @param show_hidden show hidden networks + @param channel scan only this channel (0 for all channels) + @param ssid* scan for only this ssid (NULL for all ssid's) + @return Number of discovered networks +*/ +int8_t ESP8266WiFiScanClass::scanNetworks(bool async, bool show_hidden, uint8 channel, uint8* ssid) +{ + if (ESP8266WiFiScanClass::_scanStarted) + { + return WIFI_SCAN_RUNNING; + } + + ESP8266WiFiScanClass::_scanAsync = async; + + WiFi.enableSTA(true); + + int status = wifi_station_get_connect_status(); + if (status != STATION_GOT_IP && status != STATION_IDLE) + { + wifi_station_disconnect(); + } + + scanDelete(); + + struct scan_config config; + memset(&config, 0, sizeof(config)); + config.ssid = ssid; + config.channel = channel; + config.show_hidden = show_hidden; + if (wifi_station_scan(&config, reinterpret_cast(&ESP8266WiFiScanClass::_scanDone))) + { + ESP8266WiFiScanClass::_scanComplete = false; + ESP8266WiFiScanClass::_scanStarted = true; + + if (ESP8266WiFiScanClass::_scanAsync) + { + delay(0); // time for the OS to trigger the scan + return WIFI_SCAN_RUNNING; + } + + esp_yield(); + return ESP8266WiFiScanClass::_scanCount; + } + else + { + return WIFI_SCAN_FAILED; + } + +} + +/** + Starts scanning WiFi networks available in async mode + @param onComplete the event handler executed when the scan is done + @param show_hidden show hidden networks +*/ +void ESP8266WiFiScanClass::scanNetworksAsync(std::function onComplete, bool show_hidden) +{ + _onComplete = onComplete; + scanNetworks(true, show_hidden); +} + +/** + called to get the scan state in Async mode + @return scan result or status + -1 if scan not fin + -2 if scan not triggered +*/ +int8_t ESP8266WiFiScanClass::scanComplete() +{ + + if (_scanStarted) + { + return WIFI_SCAN_RUNNING; + } + + if (_scanComplete) + { + return ESP8266WiFiScanClass::_scanCount; + } + + return WIFI_SCAN_FAILED; +} + +/** + delete last scan result from RAM +*/ +void ESP8266WiFiScanClass::scanDelete() +{ + if (ESP8266WiFiScanClass::_scanResult) + { + delete[] reinterpret_cast(ESP8266WiFiScanClass::_scanResult); + ESP8266WiFiScanClass::_scanResult = 0; + ESP8266WiFiScanClass::_scanCount = 0; + } + _scanComplete = false; +} + + +/** + loads all infos from a scanned wifi in to the ptr parameters + @param networkItem uint8_t + @param ssid const char* + @param encryptionType uint8_t + @param RSSI int32_t + @param BSSID uint8_t * + @param channel int32_t + @param isHidden bool + @return (true if ok) +*/ +bool ESP8266WiFiScanClass::getNetworkInfo(uint8_t i, String &ssid, uint8_t &encType, int32_t &rssi, uint8_t* &bssid, int32_t &channel, bool &isHidden) +{ + struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); + if (!it) + { + return false; + } + + char ssid_copy[33]; // Ensure space for maximum len SSID (32) plus trailing 0 + memcpy(ssid_copy, it->ssid, sizeof(it->ssid)); + ssid_copy[32] = 0; // Potentially add 0-termination if none present earlier + ssid = (const char*) ssid_copy; + encType = encryptionType(i); + rssi = it->rssi; + bssid = it->bssid; // move ptr + channel = it->channel; + isHidden = (it->is_hidden != 0); + + return true; +} + + +/** + Return the SSID discovered during the network scan. + @param i specify from which network item want to get the information + @return ssid string of the specified item on the networks scanned list +*/ +String ESP8266WiFiScanClass::SSID(uint8_t i) +{ + struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); + if (!it) + { + return ""; + } + char tmp[33]; //ssid can be up to 32chars, => plus null term + memcpy(tmp, it->ssid, sizeof(it->ssid)); + tmp[32] = 0; //nullterm in case of 32 char ssid + + return String(reinterpret_cast(tmp)); +} + + +/** + Return the encryption type of the networks discovered during the scanNetworks + @param i specify from which network item want to get the information + @return encryption type (enum wl_enc_type) of the specified item on the networks scanned list +*/ +uint8_t ESP8266WiFiScanClass::encryptionType(uint8_t i) +{ + struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); + if (!it) + { + return -1; + } + + switch (it->authmode) + { + case AUTH_OPEN: + return ENC_TYPE_NONE; + case AUTH_WEP: + return ENC_TYPE_WEP; + case AUTH_WPA_PSK: + return ENC_TYPE_TKIP; + case AUTH_WPA2_PSK: + return ENC_TYPE_CCMP; + case AUTH_WPA_WPA2_PSK: + return ENC_TYPE_AUTO; + default: + return -1; + } +} + +/** + Return the RSSI of the networks discovered during the scanNetworks + @param i specify from which network item want to get the information + @return signed value of RSSI of the specified item on the networks scanned list +*/ +int32_t ESP8266WiFiScanClass::RSSI(uint8_t i) +{ + struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); + if (!it) + { + return 0; + } + return it->rssi; +} + + +/** + return MAC / BSSID of scanned wifi + @param i specify from which network item want to get the information + @return uint8_t * MAC / BSSID of scanned wifi +*/ +uint8_t * ESP8266WiFiScanClass::BSSID(uint8_t i) +{ + struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); + if (!it) + { + return 0; + } + return it->bssid; +} + +/** + return MAC / BSSID of scanned wifi + @param i specify from which network item want to get the information + @return String MAC / BSSID of scanned wifi +*/ +String ESP8266WiFiScanClass::BSSIDstr(uint8_t i) +{ + char mac[18] = { 0 }; + struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); + if (!it) + { + return String(""); + } + sprintf(mac, "%02X:%02X:%02X:%02X:%02X:%02X", it->bssid[0], it->bssid[1], it->bssid[2], it->bssid[3], it->bssid[4], it->bssid[5]); + return String(mac); +} + +int32_t ESP8266WiFiScanClass::channel(uint8_t i) +{ + struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); + if (!it) + { + return 0; + } + return it->channel; +} + +/** + return if the scanned wifi is Hidden (no SSID) + @param networkItem specify from which network item want to get the information + @return bool (true == hidden) +*/ +bool ESP8266WiFiScanClass::isHidden(uint8_t i) +{ + struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); + if (!it) + { + return false; + } + return (it->is_hidden != 0); +} + +/** + private + scan callback + @param result void *arg + @param status STATUS +*/ +void ESP8266WiFiScanClass::_scanDone(void* result, int status) +{ + if (status != OK) + { + ESP8266WiFiScanClass::_scanCount = 0; + ESP8266WiFiScanClass::_scanResult = 0; + } + else + { + + int i = 0; + bss_info* head = reinterpret_cast(result); + + for (bss_info* it = head; it; it = STAILQ_NEXT(it, next), ++i) + ; + ESP8266WiFiScanClass::_scanCount = i; + if (i == 0) + { + ESP8266WiFiScanClass::_scanResult = 0; + } + else + { + bss_info* copied_info = new bss_info[i]; + i = 0; + for (bss_info* it = head; it; it = STAILQ_NEXT(it, next), ++i) + { + memcpy(copied_info + i, it, sizeof(bss_info)); + } + + ESP8266WiFiScanClass::_scanResult = copied_info; + } + + } + + ESP8266WiFiScanClass::_scanStarted = false; + ESP8266WiFiScanClass::_scanComplete = true; + + if (!ESP8266WiFiScanClass::_scanAsync) + { + esp_schedule(); + } + else if (ESP8266WiFiScanClass::_onComplete) + { + ESP8266WiFiScanClass::_onComplete(ESP8266WiFiScanClass::_scanCount); + ESP8266WiFiScanClass::_onComplete = nullptr; + } +} + +/** + + @param i specify from which network item want to get the information + @return bss_info +*/ +void * ESP8266WiFiScanClass::_getScanInfoByIndex(int i) +{ + if (!ESP8266WiFiScanClass::_scanResult || (size_t) i > ESP8266WiFiScanClass::_scanCount) + { + return 0; + } + return reinterpret_cast(ESP8266WiFiScanClass::_scanResult) + i; +} diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h index e799c4bd7d..636a44aeeb 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h @@ -1,71 +1,72 @@ -/* - ESP8266WiFiScan.h - esp8266 Wifi support. - Based on WiFi.h from Ardiono WiFi shield library. - Copyright (c) 2011-2014 Arduino. All right reserved. - Modified by Ivan Grokhotkov, December 2014 - Reworked by Markus Sattler, December 2015 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ESP8266WIFISCAN_H_ -#define ESP8266WIFISCAN_H_ - -#include "ESP8266WiFiType.h" -#include "ESP8266WiFiGeneric.h" - -class ESP8266WiFiScanClass { - - // ---------------------------------------------------------------------------------------------- - // ----------------------------------------- scan function -------------------------------------- - // ---------------------------------------------------------------------------------------------- - - public: - - int8_t scanNetworks(bool async = false, bool show_hidden = false, uint8 channel = 0, uint8* ssid = NULL); - void scanNetworksAsync(std::function onComplete, bool show_hidden = false); - - int8_t scanComplete(); - void scanDelete(); - - // scan result - bool getNetworkInfo(uint8_t networkItem, String &ssid, uint8_t &encryptionType, int32_t &RSSI, uint8_t* &BSSID, int32_t &channel, bool &isHidden); - - String SSID(uint8_t networkItem); - uint8_t encryptionType(uint8_t networkItem); - int32_t RSSI(uint8_t networkItem); - uint8_t * BSSID(uint8_t networkItem); - String BSSIDstr(uint8_t networkItem); - int32_t channel(uint8_t networkItem); - bool isHidden(uint8_t networkItem); - - protected: - - static bool _scanAsync; - static bool _scanStarted; - static bool _scanComplete; - - static size_t _scanCount; - static void* _scanResult; - - static std::function _onComplete; - - static void _scanDone(void* result, int status); - static void * _getScanInfoByIndex(int i); - -}; - - -#endif /* ESP8266WIFISCAN_H_ */ +/* + ESP8266WiFiScan.h - esp8266 Wifi support. + Based on WiFi.h from Ardiono WiFi shield library. + Copyright (c) 2011-2014 Arduino. All right reserved. + Modified by Ivan Grokhotkov, December 2014 + Reworked by Markus Sattler, December 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ESP8266WIFISCAN_H_ +#define ESP8266WIFISCAN_H_ + +#include "ESP8266WiFiType.h" +#include "ESP8266WiFiGeneric.h" + +class ESP8266WiFiScanClass +{ + + // ---------------------------------------------------------------------------------------------- + // ----------------------------------------- scan function -------------------------------------- + // ---------------------------------------------------------------------------------------------- + +public: + + int8_t scanNetworks(bool async = false, bool show_hidden = false, uint8 channel = 0, uint8* ssid = NULL); + void scanNetworksAsync(std::function onComplete, bool show_hidden = false); + + int8_t scanComplete(); + void scanDelete(); + + // scan result + bool getNetworkInfo(uint8_t networkItem, String &ssid, uint8_t &encryptionType, int32_t &RSSI, uint8_t* &BSSID, int32_t &channel, bool &isHidden); + + String SSID(uint8_t networkItem); + uint8_t encryptionType(uint8_t networkItem); + int32_t RSSI(uint8_t networkItem); + uint8_t * BSSID(uint8_t networkItem); + String BSSIDstr(uint8_t networkItem); + int32_t channel(uint8_t networkItem); + bool isHidden(uint8_t networkItem); + +protected: + + static bool _scanAsync; + static bool _scanStarted; + static bool _scanComplete; + + static size_t _scanCount; + static void* _scanResult; + + static std::function _onComplete; + + static void _scanDone(void* result, int status); + static void * _getScanInfoByIndex(int i); + +}; + + +#endif /* ESP8266WIFISCAN_H_ */ diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiType.h b/libraries/ESP8266WiFi/src/ESP8266WiFiType.h index c1fb2985be..d5d60d2268 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiType.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiType.h @@ -1,151 +1,151 @@ -/* - ESP8266WiFiType.h - esp8266 Wifi support. - Copyright (c) 2011-2014 Arduino. All right reserved. - Modified by Ivan Grokhotkov, December 2014 - Reworked by Markus Sattler, December 2015 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef ESP8266WIFITYPE_H_ -#define ESP8266WIFITYPE_H_ - -#include - -#define WIFI_SCAN_RUNNING (-1) -#define WIFI_SCAN_FAILED (-2) - -// Note: these enums need to be in sync with the SDK! - -// TODO: replace/deprecate/remove enum typedefs ending with _t below - -typedef enum WiFiMode -{ - WIFI_OFF = 0, WIFI_STA = 1, WIFI_AP = 2, WIFI_AP_STA = 3 -} WiFiMode_t; - -typedef enum WiFiPhyMode -{ - WIFI_PHY_MODE_11B = 1, WIFI_PHY_MODE_11G = 2, WIFI_PHY_MODE_11N = 3 -} WiFiPhyMode_t; - -typedef enum WiFiSleepType -{ - WIFI_NONE_SLEEP = 0, WIFI_LIGHT_SLEEP = 1, WIFI_MODEM_SLEEP = 2 -} WiFiSleepType_t; - - -typedef enum WiFiEvent -{ - WIFI_EVENT_STAMODE_CONNECTED = 0, - WIFI_EVENT_STAMODE_DISCONNECTED, - WIFI_EVENT_STAMODE_AUTHMODE_CHANGE, - WIFI_EVENT_STAMODE_GOT_IP, - WIFI_EVENT_STAMODE_DHCP_TIMEOUT, - WIFI_EVENT_SOFTAPMODE_STACONNECTED, - WIFI_EVENT_SOFTAPMODE_STADISCONNECTED, - WIFI_EVENT_SOFTAPMODE_PROBEREQRECVED, - WIFI_EVENT_MAX, - WIFI_EVENT_ANY = WIFI_EVENT_MAX, - WIFI_EVENT_MODE_CHANGE -} WiFiEvent_t; - -enum WiFiDisconnectReason -{ - WIFI_DISCONNECT_REASON_UNSPECIFIED = 1, - WIFI_DISCONNECT_REASON_AUTH_EXPIRE = 2, - WIFI_DISCONNECT_REASON_AUTH_LEAVE = 3, - WIFI_DISCONNECT_REASON_ASSOC_EXPIRE = 4, - WIFI_DISCONNECT_REASON_ASSOC_TOOMANY = 5, - WIFI_DISCONNECT_REASON_NOT_AUTHED = 6, - WIFI_DISCONNECT_REASON_NOT_ASSOCED = 7, - WIFI_DISCONNECT_REASON_ASSOC_LEAVE = 8, - WIFI_DISCONNECT_REASON_ASSOC_NOT_AUTHED = 9, - WIFI_DISCONNECT_REASON_DISASSOC_PWRCAP_BAD = 10, /* 11h */ - WIFI_DISCONNECT_REASON_DISASSOC_SUPCHAN_BAD = 11, /* 11h */ - WIFI_DISCONNECT_REASON_IE_INVALID = 13, /* 11i */ - WIFI_DISCONNECT_REASON_MIC_FAILURE = 14, /* 11i */ - WIFI_DISCONNECT_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, /* 11i */ - WIFI_DISCONNECT_REASON_GROUP_KEY_UPDATE_TIMEOUT = 16, /* 11i */ - WIFI_DISCONNECT_REASON_IE_IN_4WAY_DIFFERS = 17, /* 11i */ - WIFI_DISCONNECT_REASON_GROUP_CIPHER_INVALID = 18, /* 11i */ - WIFI_DISCONNECT_REASON_PAIRWISE_CIPHER_INVALID = 19, /* 11i */ - WIFI_DISCONNECT_REASON_AKMP_INVALID = 20, /* 11i */ - WIFI_DISCONNECT_REASON_UNSUPP_RSN_IE_VERSION = 21, /* 11i */ - WIFI_DISCONNECT_REASON_INVALID_RSN_IE_CAP = 22, /* 11i */ - WIFI_DISCONNECT_REASON_802_1X_AUTH_FAILED = 23, /* 11i */ - WIFI_DISCONNECT_REASON_CIPHER_SUITE_REJECTED = 24, /* 11i */ - - WIFI_DISCONNECT_REASON_BEACON_TIMEOUT = 200, - WIFI_DISCONNECT_REASON_NO_AP_FOUND = 201, - WIFI_DISCONNECT_REASON_AUTH_FAIL = 202, - WIFI_DISCONNECT_REASON_ASSOC_FAIL = 203, - WIFI_DISCONNECT_REASON_HANDSHAKE_TIMEOUT = 204, -}; - -struct WiFiEventModeChange -{ - WiFiMode oldMode; - WiFiMode newMode; -}; - -struct WiFiEventStationModeConnected -{ - String ssid; - uint8 bssid[6]; - uint8 channel; -}; - -struct WiFiEventStationModeDisconnected -{ - String ssid; - uint8 bssid[6]; - WiFiDisconnectReason reason; -}; - -struct WiFiEventStationModeAuthModeChanged -{ - uint8 oldMode; - uint8 newMode; -}; - -struct WiFiEventStationModeGotIP -{ - IPAddress ip; - IPAddress mask; - IPAddress gw; -}; - -struct WiFiEventSoftAPModeStationConnected -{ - uint8 mac[6]; - uint8 aid; -}; - -struct WiFiEventSoftAPModeStationDisconnected -{ - uint8 mac[6]; - uint8 aid; -}; - -struct WiFiEventSoftAPModeProbeRequestReceived -{ - int rssi; - uint8 mac[6]; -}; - - -#endif /* ESP8266WIFITYPE_H_ */ +/* + ESP8266WiFiType.h - esp8266 Wifi support. + Copyright (c) 2011-2014 Arduino. All right reserved. + Modified by Ivan Grokhotkov, December 2014 + Reworked by Markus Sattler, December 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef ESP8266WIFITYPE_H_ +#define ESP8266WIFITYPE_H_ + +#include + +#define WIFI_SCAN_RUNNING (-1) +#define WIFI_SCAN_FAILED (-2) + +// Note: these enums need to be in sync with the SDK! + +// TODO: replace/deprecate/remove enum typedefs ending with _t below + +typedef enum WiFiMode +{ + WIFI_OFF = 0, WIFI_STA = 1, WIFI_AP = 2, WIFI_AP_STA = 3 +} WiFiMode_t; + +typedef enum WiFiPhyMode +{ + WIFI_PHY_MODE_11B = 1, WIFI_PHY_MODE_11G = 2, WIFI_PHY_MODE_11N = 3 +} WiFiPhyMode_t; + +typedef enum WiFiSleepType +{ + WIFI_NONE_SLEEP = 0, WIFI_LIGHT_SLEEP = 1, WIFI_MODEM_SLEEP = 2 +} WiFiSleepType_t; + + +typedef enum WiFiEvent +{ + WIFI_EVENT_STAMODE_CONNECTED = 0, + WIFI_EVENT_STAMODE_DISCONNECTED, + WIFI_EVENT_STAMODE_AUTHMODE_CHANGE, + WIFI_EVENT_STAMODE_GOT_IP, + WIFI_EVENT_STAMODE_DHCP_TIMEOUT, + WIFI_EVENT_SOFTAPMODE_STACONNECTED, + WIFI_EVENT_SOFTAPMODE_STADISCONNECTED, + WIFI_EVENT_SOFTAPMODE_PROBEREQRECVED, + WIFI_EVENT_MAX, + WIFI_EVENT_ANY = WIFI_EVENT_MAX, + WIFI_EVENT_MODE_CHANGE +} WiFiEvent_t; + +enum WiFiDisconnectReason +{ + WIFI_DISCONNECT_REASON_UNSPECIFIED = 1, + WIFI_DISCONNECT_REASON_AUTH_EXPIRE = 2, + WIFI_DISCONNECT_REASON_AUTH_LEAVE = 3, + WIFI_DISCONNECT_REASON_ASSOC_EXPIRE = 4, + WIFI_DISCONNECT_REASON_ASSOC_TOOMANY = 5, + WIFI_DISCONNECT_REASON_NOT_AUTHED = 6, + WIFI_DISCONNECT_REASON_NOT_ASSOCED = 7, + WIFI_DISCONNECT_REASON_ASSOC_LEAVE = 8, + WIFI_DISCONNECT_REASON_ASSOC_NOT_AUTHED = 9, + WIFI_DISCONNECT_REASON_DISASSOC_PWRCAP_BAD = 10, /* 11h */ + WIFI_DISCONNECT_REASON_DISASSOC_SUPCHAN_BAD = 11, /* 11h */ + WIFI_DISCONNECT_REASON_IE_INVALID = 13, /* 11i */ + WIFI_DISCONNECT_REASON_MIC_FAILURE = 14, /* 11i */ + WIFI_DISCONNECT_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, /* 11i */ + WIFI_DISCONNECT_REASON_GROUP_KEY_UPDATE_TIMEOUT = 16, /* 11i */ + WIFI_DISCONNECT_REASON_IE_IN_4WAY_DIFFERS = 17, /* 11i */ + WIFI_DISCONNECT_REASON_GROUP_CIPHER_INVALID = 18, /* 11i */ + WIFI_DISCONNECT_REASON_PAIRWISE_CIPHER_INVALID = 19, /* 11i */ + WIFI_DISCONNECT_REASON_AKMP_INVALID = 20, /* 11i */ + WIFI_DISCONNECT_REASON_UNSUPP_RSN_IE_VERSION = 21, /* 11i */ + WIFI_DISCONNECT_REASON_INVALID_RSN_IE_CAP = 22, /* 11i */ + WIFI_DISCONNECT_REASON_802_1X_AUTH_FAILED = 23, /* 11i */ + WIFI_DISCONNECT_REASON_CIPHER_SUITE_REJECTED = 24, /* 11i */ + + WIFI_DISCONNECT_REASON_BEACON_TIMEOUT = 200, + WIFI_DISCONNECT_REASON_NO_AP_FOUND = 201, + WIFI_DISCONNECT_REASON_AUTH_FAIL = 202, + WIFI_DISCONNECT_REASON_ASSOC_FAIL = 203, + WIFI_DISCONNECT_REASON_HANDSHAKE_TIMEOUT = 204, +}; + +struct WiFiEventModeChange +{ + WiFiMode oldMode; + WiFiMode newMode; +}; + +struct WiFiEventStationModeConnected +{ + String ssid; + uint8 bssid[6]; + uint8 channel; +}; + +struct WiFiEventStationModeDisconnected +{ + String ssid; + uint8 bssid[6]; + WiFiDisconnectReason reason; +}; + +struct WiFiEventStationModeAuthModeChanged +{ + uint8 oldMode; + uint8 newMode; +}; + +struct WiFiEventStationModeGotIP +{ + IPAddress ip; + IPAddress mask; + IPAddress gw; +}; + +struct WiFiEventSoftAPModeStationConnected +{ + uint8 mac[6]; + uint8 aid; +}; + +struct WiFiEventSoftAPModeStationDisconnected +{ + uint8 mac[6]; + uint8 aid; +}; + +struct WiFiEventSoftAPModeProbeRequestReceived +{ + int rssi; + uint8 mac[6]; +}; + + +#endif /* ESP8266WIFITYPE_H_ */ diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 2d7b5ac46e..506a8d7778 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -1,32 +1,32 @@ /* - WiFiClient.cpp - TCP/IP client for esp8266, mostly compatible + WiFiClient.cpp - TCP/IP client for esp8266, mostly compatible with Arduino WiFi shield library - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LWIP_INTERNAL extern "C" { - #include "include/wl_definitions.h" - #include "osapi.h" - #include "ets_sys.h" +#include "include/wl_definitions.h" +#include "osapi.h" +#include "ets_sys.h" } #include "debug.h" @@ -46,27 +46,27 @@ uint16_t WiFiClient::_localPort = 0; static bool defaultNoDelay = false; // false == Nagle enabled by default static bool defaultSync = false; -bool getDefaultPrivateGlobalSyncValue () +bool getDefaultPrivateGlobalSyncValue() { return defaultSync; } -void WiFiClient::setDefaultNoDelay (bool noDelay) +void WiFiClient::setDefaultNoDelay(bool noDelay) { defaultNoDelay = noDelay; } -void WiFiClient::setDefaultSync (bool sync) +void WiFiClient::setDefaultSync(bool sync) { defaultSync = sync; } -bool WiFiClient::getDefaultNoDelay () +bool WiFiClient::getDefaultNoDelay() { return defaultNoDelay; } -bool WiFiClient::getDefaultSync () +bool WiFiClient::getDefaultSync() { return defaultSync; } @@ -76,14 +76,14 @@ WiFiClient* SList::_s_first = 0; WiFiClient::WiFiClient() -: _client(0) + : _client(0) { _timeout = 5000; WiFiClient::_add(this); } WiFiClient::WiFiClient(ClientContext* client) -: _client(client) + : _client(client) { _timeout = 5000; _client->ref(); @@ -97,7 +97,9 @@ WiFiClient::~WiFiClient() { WiFiClient::_remove(this); if (_client) + { _client->unref(); + } } WiFiClient::WiFiClient(const WiFiClient& other) @@ -106,19 +108,25 @@ WiFiClient::WiFiClient(const WiFiClient& other) _timeout = other._timeout; _localPort = other._localPort; if (_client) + { _client->ref(); + } WiFiClient::_add(this); } WiFiClient& WiFiClient::operator=(const WiFiClient& other) { - if (_client) + if (_client) + { _client->unref(); + } _client = other._client; _timeout = other._timeout; _localPort = other._localPort; if (_client) + { _client->ref(); + } return *this; } @@ -139,7 +147,8 @@ int WiFiClient::connect(const String& host, uint16_t port) int WiFiClient::connect(IPAddress ip, uint16_t port) { - if (_client) { + if (_client) + { stop(); _client->unref(); _client = nullptr; @@ -150,7 +159,8 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) // ever calling tcp_err // http://lists.gnu.org/archive/html/lwip-devel/2010-05/msg00001.html netif* interface = ip_route(ip); - if (!interface) { + if (!interface) + { DEBUGV("no route to host\r\n"); return 0; } @@ -158,9 +168,12 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) tcp_pcb* pcb = tcp_new(); if (!pcb) + { return 0; + } - if (_localPort > 0) { + if (_localPort > 0) + { pcb->local_port = _localPort++; } @@ -168,7 +181,8 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) _client->ref(); _client->setTimeout(_timeout); int res = _client->connect(ip, port); - if (res == 0) { + if (res == 0) + { _client->unref(); _client = nullptr; return 0; @@ -180,35 +194,45 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) return 1; } -void WiFiClient::setNoDelay(bool nodelay) { +void WiFiClient::setNoDelay(bool nodelay) +{ if (!_client) + { return; + } _client->setNoDelay(nodelay); } -bool WiFiClient::getNoDelay() const { +bool WiFiClient::getNoDelay() const +{ if (!_client) + { return false; + } return _client->getNoDelay(); } void WiFiClient::setSync(bool sync) { if (!_client) + { return; + } _client->setSync(sync); } bool WiFiClient::getSync() const { if (!_client) + { return false; + } return _client->getSync(); } -size_t WiFiClient::availableForWrite () +size_t WiFiClient::availableForWrite() { - return _client? _client->availableForWrite(): 0; + return _client ? _client->availableForWrite() : 0; } size_t WiFiClient::write(uint8_t b) @@ -255,11 +279,14 @@ size_t WiFiClient::write_P(PGM_P buf, size_t size) int WiFiClient::available() { if (!_client) + { return false; + } int result = _client->getSize(); - if (!result) { + if (!result) + { optimistic_yield(100); } return result; @@ -268,7 +295,9 @@ int WiFiClient::available() int WiFiClient::read() { if (!available()) + { return -1; + } return _client->read(); } @@ -282,26 +311,34 @@ int WiFiClient::read(uint8_t* buf, size_t size) int WiFiClient::peek() { if (!available()) + { return -1; + } return _client->peek(); } -size_t WiFiClient::peekBytes(uint8_t *buffer, size_t length) { +size_t WiFiClient::peekBytes(uint8_t *buffer, size_t length) +{ size_t count = 0; - if(!_client) { + if (!_client) + { return 0; } _startMillis = millis(); - while((available() < (int) length) && ((millis() - _startMillis) < _timeout)) { + while ((available() < (int) length) && ((millis() - _startMillis) < _timeout)) + { yield(); } - if(available() < (int) length) { + if (available() < (int) length) + { count = available(); - } else { + } + else + { count = length; } @@ -311,28 +348,38 @@ size_t WiFiClient::peekBytes(uint8_t *buffer, size_t length) { bool WiFiClient::flush(unsigned int maxWaitMs) { if (!_client) + { return true; + } if (maxWaitMs == 0) + { maxWaitMs = WIFICLIENT_MAX_FLUSH_WAIT_MS; + } return _client->wait_until_sent(maxWaitMs); } bool WiFiClient::stop(unsigned int maxWaitMs) { if (!_client) + { return true; + } bool ret = flush(maxWaitMs); // virtual, may be ssl's if (_client->close() != ERR_OK) + { ret = false; + } return ret; } uint8_t WiFiClient::connected() { if (!_client || _client->state() == CLOSED) + { return 0; + } return _client->state() == ESTABLISHED || available(); } @@ -340,7 +387,9 @@ uint8_t WiFiClient::connected() uint8_t WiFiClient::status() { if (!_client) + { return CLOSED; + } return _client->state(); } @@ -352,7 +401,9 @@ WiFiClient::operator bool() IPAddress WiFiClient::remoteIP() { if (!_client || !_client->getRemoteAddress()) + { return IPAddress(0U); + } return _client->getRemoteAddress(); } @@ -360,7 +411,9 @@ IPAddress WiFiClient::remoteIP() uint16_t WiFiClient::remotePort() { if (!_client) + { return 0; + } return _client->getRemotePort(); } @@ -368,7 +421,9 @@ uint16_t WiFiClient::remotePort() IPAddress WiFiClient::localIP() { if (!_client) + { return IPAddress(0U); + } return IPAddress(_client->getLocalAddress()); } @@ -376,50 +431,55 @@ IPAddress WiFiClient::localIP() uint16_t WiFiClient::localPort() { if (!_client) + { return 0; + } return _client->getLocalPort(); } void WiFiClient::stopAll() { - for (WiFiClient* it = _s_first; it; it = it->_next) { + for (WiFiClient* it = _s_first; it; it = it->_next) + { it->stop(); } } -void WiFiClient::stopAllExcept(WiFiClient* except) +void WiFiClient::stopAllExcept(WiFiClient* except) { - for (WiFiClient* it = _s_first; it; it = it->_next) { - if (it != except) { + for (WiFiClient* it = _s_first; it; it = it->_next) + { + if (it != except) + { it->stop(); } } } -void WiFiClient::keepAlive (uint16_t idle_sec, uint16_t intv_sec, uint8_t count) +void WiFiClient::keepAlive(uint16_t idle_sec, uint16_t intv_sec, uint8_t count) { _client->keepAlive(idle_sec, intv_sec, count); } -bool WiFiClient::isKeepAliveEnabled () const +bool WiFiClient::isKeepAliveEnabled() const { return _client->isKeepAliveEnabled(); } -uint16_t WiFiClient::getKeepAliveIdle () const +uint16_t WiFiClient::getKeepAliveIdle() const { return _client->getKeepAliveIdle(); } -uint16_t WiFiClient::getKeepAliveInterval () const +uint16_t WiFiClient::getKeepAliveInterval() const { return _client->getKeepAliveInterval(); } -uint8_t WiFiClient::getKeepAliveCount () const +uint8_t WiFiClient::getKeepAliveCount() const { return _client->getKeepAliveCount(); } diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 31f6d105a7..cedebacc36 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -1,22 +1,22 @@ /* - WiFiClient.h - Library for Arduino Wifi shield. - Copyright (c) 2011-2014 Arduino. All right reserved. + WiFiClient.h - Library for Arduino Wifi shield. + Copyright (c) 2011-2014 Arduino. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified by Ivan Grokhotkov, December 2014 - esp8266 support + Modified by Ivan Grokhotkov, December 2014 - esp8266 support */ #ifndef wificlient_h @@ -42,94 +42,108 @@ class ClientContext; class WiFiServer; -class WiFiClient : public Client, public SList { +class WiFiClient : public Client, public SList +{ protected: - WiFiClient(ClientContext* client); + WiFiClient(ClientContext* client); public: - WiFiClient(); - virtual ~WiFiClient(); - WiFiClient(const WiFiClient&); - WiFiClient& operator=(const WiFiClient&); - - uint8_t status(); - virtual int connect(IPAddress ip, uint16_t port) override; - virtual int connect(const char *host, uint16_t port) override; - virtual int connect(const String& host, uint16_t port); - virtual size_t write(uint8_t) override; - virtual size_t write(const uint8_t *buf, size_t size) override; - virtual size_t write_P(PGM_P buf, size_t size); - size_t write(Stream& stream); - - // This one is deprecated, use write(Stream& instead) - size_t write(Stream& stream, size_t unitSize) __attribute__ ((deprecated)); - - virtual int available() override; - virtual int read() override; - virtual int read(uint8_t *buf, size_t size) override; - virtual int peek() override; - virtual size_t peekBytes(uint8_t *buffer, size_t length); - size_t peekBytes(char *buffer, size_t length) { - return peekBytes((uint8_t *) buffer, length); - } - virtual void flush() override { (void)flush(0); } - virtual void stop() override { (void)stop(0); } - bool flush(unsigned int maxWaitMs); - bool stop(unsigned int maxWaitMs); - virtual uint8_t connected() override; - virtual operator bool() override; - - IPAddress remoteIP(); - uint16_t remotePort(); - IPAddress localIP(); - uint16_t localPort(); - - static void setLocalPortStart(uint16_t port) { _localPort = port; } - - size_t availableForWrite(); - - friend class WiFiServer; - - using Print::write; - - static void stopAll(); - static void stopAllExcept(WiFiClient * c); - - void keepAlive (uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT); - bool isKeepAliveEnabled () const; - uint16_t getKeepAliveIdle () const; - uint16_t getKeepAliveInterval () const; - uint8_t getKeepAliveCount () const; - void disableKeepAlive () { keepAlive(0, 0, 0); } - - // default NoDelay=False (Nagle=True=!NoDelay) - // Nagle is for shortly delaying outgoing data, to send less/bigger packets - // Nagle should be disabled for telnet-like/interactive streams - // Nagle is meaningless/ignored when Sync=true - static void setDefaultNoDelay (bool noDelay); - static bool getDefaultNoDelay (); - bool getNoDelay() const; - void setNoDelay(bool nodelay); - - // default Sync=false - // When sync is true, all writes are automatically flushed. - // This is slower but also does not allocate - // temporary memory for sending data - static void setDefaultSync (bool sync); - static bool getDefaultSync (); - bool getSync() const; - void setSync(bool sync); + WiFiClient(); + virtual ~WiFiClient(); + WiFiClient(const WiFiClient&); + WiFiClient& operator=(const WiFiClient&); + + uint8_t status(); + virtual int connect(IPAddress ip, uint16_t port) override; + virtual int connect(const char *host, uint16_t port) override; + virtual int connect(const String& host, uint16_t port); + virtual size_t write(uint8_t) override; + virtual size_t write(const uint8_t *buf, size_t size) override; + virtual size_t write_P(PGM_P buf, size_t size); + size_t write(Stream& stream); + + // This one is deprecated, use write(Stream& instead) + size_t write(Stream& stream, size_t unitSize) __attribute__((deprecated)); + + virtual int available() override; + virtual int read() override; + virtual int read(uint8_t *buf, size_t size) override; + virtual int peek() override; + virtual size_t peekBytes(uint8_t *buffer, size_t length); + size_t peekBytes(char *buffer, size_t length) + { + return peekBytes((uint8_t *) buffer, length); + } + virtual void flush() override + { + (void)flush(0); + } + virtual void stop() override + { + (void)stop(0); + } + bool flush(unsigned int maxWaitMs); + bool stop(unsigned int maxWaitMs); + virtual uint8_t connected() override; + virtual operator bool() override; + + IPAddress remoteIP(); + uint16_t remotePort(); + IPAddress localIP(); + uint16_t localPort(); + + static void setLocalPortStart(uint16_t port) + { + _localPort = port; + } + + size_t availableForWrite(); + + friend class WiFiServer; + + using Print::write; + + static void stopAll(); + static void stopAllExcept(WiFiClient * c); + + void keepAlive(uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT); + bool isKeepAliveEnabled() const; + uint16_t getKeepAliveIdle() const; + uint16_t getKeepAliveInterval() const; + uint8_t getKeepAliveCount() const; + void disableKeepAlive() + { + keepAlive(0, 0, 0); + } + + // default NoDelay=False (Nagle=True=!NoDelay) + // Nagle is for shortly delaying outgoing data, to send less/bigger packets + // Nagle should be disabled for telnet-like/interactive streams + // Nagle is meaningless/ignored when Sync=true + static void setDefaultNoDelay(bool noDelay); + static bool getDefaultNoDelay(); + bool getNoDelay() const; + void setNoDelay(bool nodelay); + + // default Sync=false + // When sync is true, all writes are automatically flushed. + // This is slower but also does not allocate + // temporary memory for sending data + static void setDefaultSync(bool sync); + static bool getDefaultSync(); + bool getSync() const; + void setSync(bool sync); protected: - static int8_t _s_connected(void* arg, void* tpcb, int8_t err); - static void _s_err(void* arg, int8_t err); + static int8_t _s_connected(void* arg, void* tpcb, int8_t err); + static void _s_err(void* arg, int8_t err); - int8_t _connected(void* tpcb, int8_t err); - void _err(int8_t err); + int8_t _connected(void* tpcb, int8_t err); + void _err(int8_t err); - ClientContext* _client; - static uint16_t _localPort; + ClientContext* _client; + static uint16_t _localPort; }; #endif diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecure.h b/libraries/ESP8266WiFi/src/WiFiClientSecure.h index 235f7e07f2..d907e23d0a 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecure.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecure.h @@ -1,22 +1,22 @@ /* - WiFiClientSecure.h - Variant of WiFiClient with TLS support - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + WiFiClientSecure.h - Variant of WiFiClient with TLS support + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,18 +24,18 @@ //using namespace axTLS; /********************************** - * !! Now BearSSL is the default !! - * - * While not advised, - * Use legacy API without updating with: - * -#define USING_AXTLS -#include -//#include -#include "WiFiClientSecureAxTLS.h" -using namespace axTLS; - * - * + !! Now BearSSL is the default !! + + While not advised, + Use legacy API without updating with: + + #define USING_AXTLS + #include + //#include + #include "WiFiClientSecureAxTLS.h" + using namespace axTLS; + + **********************************/ #include "WiFiClientSecureBearSSL.h" diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp index b47d7c21e6..85dc7c4c2a 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp @@ -1,22 +1,22 @@ /* - WiFiClientSecure.cpp - Variant of WiFiClient with TLS support - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + WiFiClientSecure.cpp - Variant of WiFiClient with TLS support + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -42,7 +42,8 @@ #include "include/SSLContext.h" #include "include/ClientContext.h" -namespace axTLS { +namespace axTLS +{ SSL_CTX* SSLContext::_ssl_client_ctx = nullptr; int SSLContext::_ssl_client_ctx_refcnt = 0; @@ -57,7 +58,7 @@ WiFiClientSecure::WiFiClientSecure() WiFiClientSecure::~WiFiClientSecure() { - _ssl = nullptr; + _ssl = nullptr; } // Only called by the WifiServerSecure, need to get the keys/certs loaded before beginning @@ -77,18 +78,25 @@ WiFiClientSecure::WiFiClientSecure(ClientContext* client, bool usePMEM, std::shared_ptr _new_ssl_shared(_new_ssl); _ssl = _new_ssl_shared; - if (usePMEM) { - if (rsakey && rsakeyLen) { + if (usePMEM) + { + if (rsakey && rsakeyLen) + { _ssl->loadObject_P(SSL_OBJ_RSA_KEY, rsakey, rsakeyLen); } - if (cert && certLen) { + if (cert && certLen) + { _ssl->loadObject_P(SSL_OBJ_X509_CERT, cert, certLen); } - } else { - if (rsakey && rsakeyLen) { + } + else + { + if (rsakey && rsakeyLen) + { _ssl->loadObject(SSL_OBJ_RSA_KEY, rsakey, rsakeyLen); } - if (cert && certLen) { + if (cert && certLen) + { _ssl->loadObject(SSL_OBJ_X509_CERT, cert, certLen); } } @@ -97,7 +105,8 @@ WiFiClientSecure::WiFiClientSecure(ClientContext* client, bool usePMEM, int WiFiClientSecure::connect(IPAddress ip, uint16_t port) { - if (!WiFiClient::connect(ip, port)) { + if (!WiFiClient::connect(ip, port)) + { return 0; } @@ -107,10 +116,12 @@ int WiFiClientSecure::connect(IPAddress ip, uint16_t port) int WiFiClientSecure::connect(const char* name, uint16_t port) { IPAddress remote_addr; - if (!WiFi.hostByName(name, remote_addr)) { + if (!WiFi.hostByName(name, remote_addr)) + { return 0; } - if (!WiFiClient::connect(remote_addr, port)) { + if (!WiFiClient::connect(remote_addr, port)) + { return 0; } return _connectSSL(name); @@ -123,13 +134,15 @@ int WiFiClientSecure::connect(const String& host, uint16_t port) int WiFiClientSecure::_connectSSL(const char* hostName) { - if (!_ssl) { + if (!_ssl) + { _ssl = std::make_shared(); } _ssl->connect(_client, hostName, _timeout); auto status = ssl_handshake_status(*_ssl); - if (status != SSL_OK) { + if (status != SSL_OK) + { _ssl = nullptr; return 0; } @@ -139,16 +152,19 @@ int WiFiClientSecure::_connectSSL(const char* hostName) size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) { - if (!_ssl) { + if (!_ssl) + { return 0; } int rc = _ssl->write(buf, size); - if (rc >= 0) { + if (rc >= 0) + { return rc; } - if (rc != SSL_CLOSE_NOTIFY) { + if (rc != SSL_CLOSE_NOTIFY) + { _ssl = nullptr; } @@ -174,22 +190,25 @@ size_t WiFiClientSecure::write(Stream& stream) { return 0; } - do { + do + { uint8_t temp[256]; // Temporary chunk size same as ClientContext countSent = 0; countRead = stream.readBytes(temp, sizeof(temp)); - if (countRead) { + if (countRead) + { countSent = write(temp, countRead); totalSent += countSent; } yield(); // Feed the WDT - } while ( (countSent == countRead) && (countSent > 0) ); + } while ((countSent == countRead) && (countSent > 0)); return totalSent; } int WiFiClientSecure::read(uint8_t *buf, size_t size) { - if (!_ssl) { + if (!_ssl) + { return 0; } @@ -198,7 +217,8 @@ int WiFiClientSecure::read(uint8_t *buf, size_t size) int WiFiClientSecure::read() { - if (!_ssl) { + if (!_ssl) + { return -1; } @@ -207,7 +227,8 @@ int WiFiClientSecure::read() int WiFiClientSecure::peek() { - if (!_ssl) { + if (!_ssl) + { return -1; } @@ -218,22 +239,28 @@ size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) { size_t count = 0; - if (!_ssl) { + if (!_ssl) + { return 0; } _startMillis = millis(); - while ((available() < (int) length) && ((millis() - _startMillis) < _timeout)) { + while ((available() < (int) length) && ((millis() - _startMillis) < _timeout)) + { yield(); } - if (!_ssl) { + if (!_ssl) + { return 0; } - if (available() < (int) length) { + if (available() < (int) length) + { count = available(); - } else { + } + else + { count = length; } @@ -242,7 +269,8 @@ size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) int WiFiClientSecure::available() { - if (!_ssl) { + if (!_ssl) + { return 0; } @@ -251,20 +279,23 @@ int WiFiClientSecure::available() /* -SSL TCP RX data connected -null x x N -!null x Y Y -Y Y x Y -x N N N -err x N N + SSL TCP RX data connected + null x x N + !null x Y Y + Y Y x Y + x N N N + err x N N */ uint8_t WiFiClientSecure::connected() { - if (_ssl) { - if (_ssl->hasData()) { + if (_ssl) + { + if (_ssl->hasData()) + { return true; } - if (_client && _client->state() == ESTABLISHED && _ssl->connected()) { + if (_client && _client->state() == ESTABLISHED && _ssl->connected()) + { return true; } } @@ -273,7 +304,8 @@ uint8_t WiFiClientSecure::connected() bool WiFiClientSecure::stop(unsigned int maxWaitMs) { - if (_ssl) { + if (_ssl) + { _ssl->stop(); } return WiFiClient::stop(maxWaitMs); @@ -281,12 +313,17 @@ bool WiFiClientSecure::stop(unsigned int maxWaitMs) static bool parseHexNibble(char pb, uint8_t* res) { - if (pb >= '0' && pb <= '9') { - *res = (uint8_t) (pb - '0'); return true; - } else if (pb >= 'a' && pb <= 'f') { - *res = (uint8_t) (pb - 'a' + 10); return true; - } else if (pb >= 'A' && pb <= 'F') { - *res = (uint8_t) (pb - 'A' + 10); return true; + if (pb >= '0' && pb <= '9') + { + *res = (uint8_t)(pb - '0'); return true; + } + else if (pb >= 'a' && pb <= 'f') + { + *res = (uint8_t)(pb - 'a' + 10); return true; + } + else if (pb >= 'A' && pb <= 'F') + { + *res = (uint8_t)(pb - 'A' + 10); return true; } return false; } @@ -295,23 +332,27 @@ static bool parseHexNibble(char pb, uint8_t* res) static bool matchName(const String& name, const String& domainName) { int wildcardPos = name.indexOf('*'); - if (wildcardPos == -1) { + if (wildcardPos == -1) + { // Not a wildcard, expect an exact match return name == domainName; } int firstDotPos = name.indexOf('.'); - if (wildcardPos > firstDotPos) { + if (wildcardPos > firstDotPos) + { // Wildcard is not part of leftmost component of domain name // Do not attempt to match (rfc6125 6.4.3.1) return false; } - if (wildcardPos != 0 || firstDotPos != 1) { + if (wildcardPos != 0 || firstDotPos != 1) + { // Matching of wildcards such as baz*.example.com and b*z.example.com // is optional. Maybe implement this in the future? return false; } int domainNameFirstDotPos = domainName.indexOf('.'); - if (domainNameFirstDotPos < 0) { + if (domainNameFirstDotPos < 0) + { return false; } return domainName.substring(domainNameFirstDotPos) == name.substring(firstDotPos); @@ -319,30 +360,36 @@ static bool matchName(const String& name, const String& domainName) bool WiFiClientSecure::verify(const char* fp, const char* domain_name) { - if (!_ssl) { + if (!_ssl) + { return false; } uint8_t sha1[20]; int len = strlen(fp); int pos = 0; - for (size_t i = 0; i < sizeof(sha1); ++i) { - while (pos < len && ((fp[pos] == ' ') || (fp[pos] == ':'))) { + for (size_t i = 0; i < sizeof(sha1); ++i) + { + while (pos < len && ((fp[pos] == ' ') || (fp[pos] == ':'))) + { ++pos; } - if (pos > len - 2) { + if (pos > len - 2) + { DEBUGV("pos:%d len:%d fingerprint too short\r\n", pos, len); return false; } uint8_t high, low; - if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos+1], &low)) { - DEBUGV("pos:%d len:%d invalid hex sequence: %c%c\r\n", pos, len, fp[pos], fp[pos+1]); + if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos + 1], &low)) + { + DEBUGV("pos:%d len:%d invalid hex sequence: %c%c\r\n", pos, len, fp[pos], fp[pos + 1]); return false; } pos += 2; sha1[i] = low | (high << 4); } - if (ssl_match_fingerprint(*_ssl, sha1) != 0) { + if (ssl_match_fingerprint(*_ssl, sha1) != 0) + { DEBUGV("fingerprint doesn't match\r\n"); return false; } @@ -352,16 +399,18 @@ bool WiFiClientSecure::verify(const char* fp, const char* domain_name) bool WiFiClientSecure::_verifyDN(const char* domain_name) { - DEBUGV("domain name: '%s'\r\n", (domain_name)?domain_name:"(null)"); + DEBUGV("domain name: '%s'\r\n", (domain_name) ? domain_name : "(null)"); String domain_name_str(domain_name); domain_name_str.toLowerCase(); const char* san = nullptr; int i = 0; - while ((san = ssl_get_cert_subject_alt_dnsname(*_ssl, i)) != nullptr) { + while ((san = ssl_get_cert_subject_alt_dnsname(*_ssl, i)) != nullptr) + { String san_str(san); san_str.toLowerCase(); - if (matchName(san_str, domain_name_str)) { + if (matchName(san_str, domain_name_str)) + { return true; } DEBUGV("SAN %d: '%s', no match\r\n", i, san); @@ -370,20 +419,23 @@ bool WiFiClientSecure::_verifyDN(const char* domain_name) const char* common_name = ssl_get_cert_dn(*_ssl, SSL_X509_CERT_COMMON_NAME); String common_name_str(common_name); common_name_str.toLowerCase(); - if (common_name && matchName(common_name_str, domain_name_str)) { + if (common_name && matchName(common_name_str, domain_name_str)) + { return true; } - DEBUGV("CN: '%s', no match\r\n", (common_name)?common_name:"(null)"); + DEBUGV("CN: '%s', no match\r\n", (common_name) ? common_name : "(null)"); return false; } bool WiFiClientSecure::verifyCertChain(const char* domain_name) { - if (!_ssl) { + if (!_ssl) + { return false; } - if (!_ssl->verifyCert()) { + if (!_ssl->verifyCert()) + { return false; } return _verifyDN(domain_name); @@ -391,7 +443,8 @@ bool WiFiClientSecure::verifyCertChain(const char* domain_name) void WiFiClientSecure::_initSSLContext() { - if (!_ssl) { + if (!_ssl) + { _ssl = std::make_shared(); } } @@ -459,37 +512,42 @@ void WiFiClientSecure::allowSelfSignedCerts() extern "C" int __ax_port_read(int fd, uint8_t* buffer, size_t count) { ClientContext* _client = SSLContext::getIOContext(fd); - if (!_client || (_client->state() != ESTABLISHED && !_client->getSize())) { + if (!_client || (_client->state() != ESTABLISHED && !_client->getSize())) + { errno = EIO; return -1; } size_t cb = _client->read((char*) buffer, count); - if (cb != count) { + if (cb != count) + { errno = EAGAIN; } - if (cb == 0) { + if (cb == 0) + { optimistic_yield(100); return -1; } return cb; } -extern "C" void ax_port_read() __attribute__ ((weak, alias("__ax_port_read"))); +extern "C" void ax_port_read() __attribute__((weak, alias("__ax_port_read"))); extern "C" int __ax_port_write(int fd, uint8_t* buffer, size_t count) { ClientContext* _client = SSLContext::getIOContext(fd); - if (!_client || _client->state() != ESTABLISHED) { + if (!_client || _client->state() != ESTABLISHED) + { errno = EIO; return -1; } size_t cb = _client->write(buffer, count); - if (cb != count) { + if (cb != count) + { errno = EAGAIN; } return cb; } -extern "C" void ax_port_write() __attribute__ ((weak, alias("__ax_port_write"))); +extern "C" void ax_port_write() __attribute__((weak, alias("__ax_port_write"))); extern "C" int __ax_get_file(const char *filename, uint8_t **buf) { @@ -497,12 +555,12 @@ extern "C" int __ax_get_file(const char *filename, uint8_t **buf) *buf = 0; return 0; } -extern "C" void ax_get_file() __attribute__ ((weak, alias("__ax_get_file"))); +extern "C" void ax_get_file() __attribute__((weak, alias("__ax_get_file"))); extern "C" void __ax_wdt_feed() { optimistic_yield(10000); } -extern "C" void ax_wdt_feed() __attribute__ ((weak, alias("__ax_wdt_feed"))); +extern "C" void ax_wdt_feed() __attribute__((weak, alias("__ax_wdt_feed"))); }; diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.h b/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.h index 9958c46203..213701fa80 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.h @@ -1,22 +1,22 @@ /* - WiFiClientSecure.h - Variant of WiFiClient with TLS support - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + WiFiClientSecure.h - Variant of WiFiClient with TLS support + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -26,67 +26,75 @@ #include "include/ssl.h" -namespace axTLS { +namespace axTLS +{ class SSLContext; -class WiFiClientSecure : public WiFiClient { +class WiFiClientSecure : public WiFiClient +{ public: - WiFiClientSecure() __attribute__((deprecated("Upgrade to BearSSL is advised, check https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/src/WiFiClientSecure.h#L25-L99"))); - ~WiFiClientSecure() override; - - int connect(IPAddress ip, uint16_t port) override; - int connect(const String& host, uint16_t port) override; - int connect(const char* name, uint16_t port) override; - - bool verify(const char* fingerprint, const char* domain_name); - bool verifyCertChain(const char* domain_name); - - uint8_t connected() override; - size_t write(const uint8_t *buf, size_t size) override; - size_t write_P(PGM_P buf, size_t size) override; - size_t write(Stream& stream); // Note this is not virtual - int read(uint8_t *buf, size_t size) override; - int available() override; - int read() override; - int peek() override; - size_t peekBytes(uint8_t *buffer, size_t length) override; - void stop() override { (void)stop(0); } - bool stop(unsigned int maxWaitMs); - - bool setCACert(const uint8_t* pk, size_t size); - bool setCertificate(const uint8_t* pk, size_t size); - bool setPrivateKey(const uint8_t* pk, size_t size); - - bool setCACert_P(PGM_VOID_P pk, size_t size); - bool setCertificate_P(PGM_VOID_P pk, size_t size); - bool setPrivateKey_P(PGM_VOID_P pk, size_t size); - - bool loadCACert(Stream& stream, size_t size); - bool loadCertificate(Stream& stream, size_t size); - bool loadPrivateKey(Stream& stream, size_t size); - - void allowSelfSignedCerts(); - - template - bool loadCertificate(TFile& file) { - return loadCertificate(file, file.size()); - } - - template - bool loadPrivateKey(TFile& file) { - return loadPrivateKey(file, file.size()); - } - - template - bool loadCACert(TFile& file) { - return loadCACert(file, file.size()); - } - -friend class WiFiServerSecure; // Needs access to custom constructor below + WiFiClientSecure() __attribute__((deprecated("Upgrade to BearSSL is advised, check https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/src/WiFiClientSecure.h#L25-L99"))); + ~WiFiClientSecure() override; + + int connect(IPAddress ip, uint16_t port) override; + int connect(const String& host, uint16_t port) override; + int connect(const char* name, uint16_t port) override; + + bool verify(const char* fingerprint, const char* domain_name); + bool verifyCertChain(const char* domain_name); + + uint8_t connected() override; + size_t write(const uint8_t *buf, size_t size) override; + size_t write_P(PGM_P buf, size_t size) override; + size_t write(Stream& stream); // Note this is not virtual + int read(uint8_t *buf, size_t size) override; + int available() override; + int read() override; + int peek() override; + size_t peekBytes(uint8_t *buffer, size_t length) override; + void stop() override + { + (void)stop(0); + } + bool stop(unsigned int maxWaitMs); + + bool setCACert(const uint8_t* pk, size_t size); + bool setCertificate(const uint8_t* pk, size_t size); + bool setPrivateKey(const uint8_t* pk, size_t size); + + bool setCACert_P(PGM_VOID_P pk, size_t size); + bool setCertificate_P(PGM_VOID_P pk, size_t size); + bool setPrivateKey_P(PGM_VOID_P pk, size_t size); + + bool loadCACert(Stream& stream, size_t size); + bool loadCertificate(Stream& stream, size_t size); + bool loadPrivateKey(Stream& stream, size_t size); + + void allowSelfSignedCerts(); + + template + bool loadCertificate(TFile& file) + { + return loadCertificate(file, file.size()); + } + + template + bool loadPrivateKey(TFile& file) + { + return loadPrivateKey(file, file.size()); + } + + template + bool loadCACert(TFile& file) + { + return loadCACert(file, file.size()); + } + + friend class WiFiServerSecure; // Needs access to custom constructor below protected: - // Only called by WiFiServerSecure - WiFiClientSecure(ClientContext* client, bool usePMEM, const uint8_t *rsakey, int rsakeyLen, const uint8_t *cert, int certLen); + // Only called by WiFiServerSecure + WiFiClientSecure(ClientContext* client, bool usePMEM, const uint8_t *rsakey, int rsakeyLen, const uint8_t *cert, int certLen); protected: void _initSSLContext(); diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 8d1eee72eb..1519905764 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -1,23 +1,23 @@ /* - WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries - - Mostly compatible with Arduino WiFi shield library and standard + WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries + - Mostly compatible with Arduino WiFi shield library and standard WiFiClient/ServerSecure (except for certificate handling). - Copyright (c) 2018 Earle F. Philhower, III + Copyright (c) 2018 Earle F. Philhower, III - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LWIP_INTERNAL @@ -64,1069 +64,1244 @@ extern "C" { #define DEBUG_BSSL(...) #endif -namespace BearSSL { - -void WiFiClientSecure::_clear() { - // TLS handshake may take more than the 5 second default timeout - _timeout = 15000; - - _sc = nullptr; - _sc_svr = nullptr; - _eng = nullptr; - _x509_minimal = nullptr; - _x509_insecure = nullptr; - _x509_knownkey = nullptr; - _iobuf_in = nullptr; - _iobuf_out = nullptr; - _now = 0; // You can override or ensure time() is correct w/configTime - _ta = nullptr; - setBufferSizes(16384, 512); // Minimum safe - _handshake_done = false; - _recvapp_buf = nullptr; - _recvapp_len = 0; - _oom_err = false; - _session = nullptr; - _cipher_list = nullptr; - _cipher_cnt = 0; +namespace BearSSL +{ + +void WiFiClientSecure::_clear() +{ + // TLS handshake may take more than the 5 second default timeout + _timeout = 15000; + + _sc = nullptr; + _sc_svr = nullptr; + _eng = nullptr; + _x509_minimal = nullptr; + _x509_insecure = nullptr; + _x509_knownkey = nullptr; + _iobuf_in = nullptr; + _iobuf_out = nullptr; + _now = 0; // You can override or ensure time() is correct w/configTime + _ta = nullptr; + setBufferSizes(16384, 512); // Minimum safe + _handshake_done = false; + _recvapp_buf = nullptr; + _recvapp_len = 0; + _oom_err = false; + _session = nullptr; + _cipher_list = nullptr; + _cipher_cnt = 0; } -void WiFiClientSecure::_clearAuthenticationSettings() { - _use_insecure = false; - _use_fingerprint = false; - _use_self_signed = false; - _knownkey = nullptr; - _sk = nullptr; - _ta = nullptr; - _axtls_ta = nullptr; - _axtls_chain = nullptr; - _axtls_sk = nullptr; +void WiFiClientSecure::_clearAuthenticationSettings() +{ + _use_insecure = false; + _use_fingerprint = false; + _use_self_signed = false; + _knownkey = nullptr; + _sk = nullptr; + _ta = nullptr; + _axtls_ta = nullptr; + _axtls_chain = nullptr; + _axtls_sk = nullptr; } -WiFiClientSecure::WiFiClientSecure() : WiFiClient() { - _clear(); - _clearAuthenticationSettings(); - _certStore = nullptr; // Don't want to remove cert store on a clear, should be long lived - stack_thunk_add_ref(); +WiFiClientSecure::WiFiClientSecure() : WiFiClient() +{ + _clear(); + _clearAuthenticationSettings(); + _certStore = nullptr; // Don't want to remove cert store on a clear, should be long lived + stack_thunk_add_ref(); } -WiFiClientSecure::WiFiClientSecure(const WiFiClientSecure &rhs) : WiFiClient(rhs) { - *this = rhs; - stack_thunk_add_ref(); +WiFiClientSecure::WiFiClientSecure(const WiFiClientSecure &rhs) : WiFiClient(rhs) +{ + *this = rhs; + stack_thunk_add_ref(); } -WiFiClientSecure::~WiFiClientSecure() { - if (_client) { - _client->unref(); - _client = nullptr; - } - _cipher_list = nullptr; // std::shared will free if last reference - _freeSSL(); - stack_thunk_del_ref(); - // Clean up any dangling axtls compat structures, if needed - _axtls_ta = nullptr; - _axtls_chain = nullptr; - _axtls_sk = nullptr; +WiFiClientSecure::~WiFiClientSecure() +{ + if (_client) + { + _client->unref(); + _client = nullptr; + } + _cipher_list = nullptr; // std::shared will free if last reference + _freeSSL(); + stack_thunk_del_ref(); + // Clean up any dangling axtls compat structures, if needed + _axtls_ta = nullptr; + _axtls_chain = nullptr; + _axtls_sk = nullptr; } WiFiClientSecure::WiFiClientSecure(ClientContext* client, - const X509List *chain, const PrivateKey *sk, - int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) { - _clear(); - _clearAuthenticationSettings(); - stack_thunk_add_ref(); - _iobuf_in_size = iobuf_in_size; - _iobuf_out_size = iobuf_out_size; - _client = client; - _client->ref(); - if (!_connectSSLServerRSA(chain, sk, client_CA_ta)) { - _client->unref(); - _client = nullptr; + const X509List *chain, const PrivateKey *sk, + int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) +{ _clear(); - } + _clearAuthenticationSettings(); + stack_thunk_add_ref(); + _iobuf_in_size = iobuf_in_size; + _iobuf_out_size = iobuf_out_size; + _client = client; + _client->ref(); + if (!_connectSSLServerRSA(chain, sk, client_CA_ta)) + { + _client->unref(); + _client = nullptr; + _clear(); + } } WiFiClientSecure::WiFiClientSecure(ClientContext *client, - const X509List *chain, - unsigned cert_issuer_key_type, const PrivateKey *sk, - int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) { - _clear(); - _clearAuthenticationSettings(); - stack_thunk_add_ref(); - _iobuf_in_size = iobuf_in_size; - _iobuf_out_size = iobuf_out_size; - _client = client; - _client->ref(); - if (!_connectSSLServerEC(chain, cert_issuer_key_type, sk, client_CA_ta)) { - _client->unref(); - _client = nullptr; + const X509List *chain, + unsigned cert_issuer_key_type, const PrivateKey *sk, + int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) +{ _clear(); - } + _clearAuthenticationSettings(); + stack_thunk_add_ref(); + _iobuf_in_size = iobuf_in_size; + _iobuf_out_size = iobuf_out_size; + _client = client; + _client->ref(); + if (!_connectSSLServerEC(chain, cert_issuer_key_type, sk, client_CA_ta)) + { + _client->unref(); + _client = nullptr; + _clear(); + } } -void WiFiClientSecure::setClientRSACert(const X509List *chain, const PrivateKey *sk) { - _chain = chain; - _sk = sk; +void WiFiClientSecure::setClientRSACert(const X509List *chain, const PrivateKey *sk) +{ + _chain = chain; + _sk = sk; } void WiFiClientSecure::setClientECCert(const X509List *chain, - const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) { - _chain = chain; - _sk = sk; - _allowed_usages = allowed_usages; - _cert_issuer_key_type = cert_issuer_key_type; + const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) +{ + _chain = chain; + _sk = sk; + _allowed_usages = allowed_usages; + _cert_issuer_key_type = cert_issuer_key_type; } -void WiFiClientSecure::setBufferSizes(int recv, int xmit) { - // Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately) - const int MAX_OUT_OVERHEAD = 85; - const int MAX_IN_OVERHEAD = 325; - - // The data buffers must be between 512B and 16KB - recv = std::max(512, std::min(16384, recv)); - xmit = std::max(512, std::min(16384, xmit)); - - // Add in overhead for SSL protocol - recv += MAX_IN_OVERHEAD; - xmit += MAX_OUT_OVERHEAD; - _iobuf_in_size = recv; - _iobuf_out_size = xmit; +void WiFiClientSecure::setBufferSizes(int recv, int xmit) +{ + // Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately) + const int MAX_OUT_OVERHEAD = 85; + const int MAX_IN_OVERHEAD = 325; + + // The data buffers must be between 512B and 16KB + recv = std::max(512, std::min(16384, recv)); + xmit = std::max(512, std::min(16384, xmit)); + + // Add in overhead for SSL protocol + recv += MAX_IN_OVERHEAD; + xmit += MAX_OUT_OVERHEAD; + _iobuf_in_size = recv; + _iobuf_out_size = xmit; } -bool WiFiClientSecure::stop(unsigned int maxWaitMs) { - bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush() - // Only if we've already connected, store session params and clear the connection options - if (_handshake_done) { - if (_session) { - br_ssl_engine_get_session_parameters(_eng, _session->getSession()); +bool WiFiClientSecure::stop(unsigned int maxWaitMs) +{ + bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush() + // Only if we've already connected, store session params and clear the connection options + if (_handshake_done) + { + if (_session) + { + br_ssl_engine_get_session_parameters(_eng, _session->getSession()); + } } - } - _freeSSL(); - return ret; + _freeSSL(); + return ret; } -bool WiFiClientSecure::flush(unsigned int maxWaitMs) { - (void) _run_until(BR_SSL_SENDAPP); - return WiFiClient::flush(maxWaitMs); +bool WiFiClientSecure::flush(unsigned int maxWaitMs) +{ + (void) _run_until(BR_SSL_SENDAPP); + return WiFiClient::flush(maxWaitMs); } -int WiFiClientSecure::connect(IPAddress ip, uint16_t port) { - if (!WiFiClient::connect(ip, port)) { - return 0; - } - return _connectSSL(nullptr); +int WiFiClientSecure::connect(IPAddress ip, uint16_t port) +{ + if (!WiFiClient::connect(ip, port)) + { + return 0; + } + return _connectSSL(nullptr); } -int WiFiClientSecure::connect(const char* name, uint16_t port) { - IPAddress remote_addr; - if (!WiFi.hostByName(name, remote_addr)) { - DEBUG_BSSL("connect: Name loopup failure\n"); - return 0; - } - if (!WiFiClient::connect(remote_addr, port)) { - DEBUG_BSSL("connect: Unable to connect TCP socket\n"); - return 0; - } - return _connectSSL(name); +int WiFiClientSecure::connect(const char* name, uint16_t port) +{ + IPAddress remote_addr; + if (!WiFi.hostByName(name, remote_addr)) + { + DEBUG_BSSL("connect: Name loopup failure\n"); + return 0; + } + if (!WiFiClient::connect(remote_addr, port)) + { + DEBUG_BSSL("connect: Unable to connect TCP socket\n"); + return 0; + } + return _connectSSL(name); } -int WiFiClientSecure::connect(const String& host, uint16_t port) { - return connect(host.c_str(), port); +int WiFiClientSecure::connect(const String& host, uint16_t port) +{ + return connect(host.c_str(), port); } -void WiFiClientSecure::_freeSSL() { - // These are smart pointers and will free if refcnt==0 - _sc = nullptr; - _sc_svr = nullptr; - _x509_minimal = nullptr; - _x509_insecure = nullptr; - _x509_knownkey = nullptr; - _iobuf_in = nullptr; - _iobuf_out = nullptr; - // Reset non-allocated ptrs (pointing to bits potentially free'd above) - _recvapp_buf = nullptr; - _recvapp_len = 0; - // This connection is toast - _handshake_done = false; +void WiFiClientSecure::_freeSSL() +{ + // These are smart pointers and will free if refcnt==0 + _sc = nullptr; + _sc_svr = nullptr; + _x509_minimal = nullptr; + _x509_insecure = nullptr; + _x509_knownkey = nullptr; + _iobuf_in = nullptr; + _iobuf_out = nullptr; + // Reset non-allocated ptrs (pointing to bits potentially free'd above) + _recvapp_buf = nullptr; + _recvapp_len = 0; + // This connection is toast + _handshake_done = false; } -bool WiFiClientSecure::_clientConnected() { - return (_client && _client->state() == ESTABLISHED); +bool WiFiClientSecure::_clientConnected() +{ + return (_client && _client->state() == ESTABLISHED); } -uint8_t WiFiClientSecure::connected() { - if (available() || (_clientConnected() && _handshake_done)) { - return true; - } - return false; +uint8_t WiFiClientSecure::connected() +{ + if (available() || (_clientConnected() && _handshake_done)) + { + return true; + } + return false; } -size_t WiFiClientSecure::_write(const uint8_t *buf, size_t size, bool pmem) { - size_t sent_bytes = 0; +size_t WiFiClientSecure::_write(const uint8_t *buf, size_t size, bool pmem) +{ + size_t sent_bytes = 0; - if (!connected() || !size || !_handshake_done) { - return 0; - } - - do { - // Ensure we yield if we need multiple fragments to avoid WDT - if (sent_bytes) { - optimistic_yield(1000); - } - - // Get BearSSL to a state where we can send - if (_run_until(BR_SSL_SENDAPP) < 0) { - break; - } - - if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { - size_t sendapp_len; - unsigned char *sendapp_buf = br_ssl_engine_sendapp_buf(_eng, &sendapp_len); - int to_send = size > sendapp_len ? sendapp_len : size; - if (pmem) { - memcpy_P(sendapp_buf, buf, to_send); - } else { - memcpy(sendapp_buf, buf, to_send); - } - br_ssl_engine_sendapp_ack(_eng, to_send); - br_ssl_engine_flush(_eng, 0); - flush(); - buf += to_send; - sent_bytes += to_send; - size -= to_send; - } else { - break; - } - } while (size); - - return sent_bytes; + if (!connected() || !size || !_handshake_done) + { + return 0; + } + + do + { + // Ensure we yield if we need multiple fragments to avoid WDT + if (sent_bytes) + { + optimistic_yield(1000); + } + + // Get BearSSL to a state where we can send + if (_run_until(BR_SSL_SENDAPP) < 0) + { + break; + } + + if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) + { + size_t sendapp_len; + unsigned char *sendapp_buf = br_ssl_engine_sendapp_buf(_eng, &sendapp_len); + int to_send = size > sendapp_len ? sendapp_len : size; + if (pmem) + { + memcpy_P(sendapp_buf, buf, to_send); + } + else + { + memcpy(sendapp_buf, buf, to_send); + } + br_ssl_engine_sendapp_ack(_eng, to_send); + br_ssl_engine_flush(_eng, 0); + flush(); + buf += to_send; + sent_bytes += to_send; + size -= to_send; + } + else + { + break; + } + } while (size); + + return sent_bytes; } -size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) { - return _write(buf, size, false); +size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) +{ + return _write(buf, size, false); } -size_t WiFiClientSecure::write_P(PGM_P buf, size_t size) { - return _write((const uint8_t *)buf, size, true); +size_t WiFiClientSecure::write_P(PGM_P buf, size_t size) +{ + return _write((const uint8_t *)buf, size, true); } // We have to manually read and send individual chunks. -size_t WiFiClientSecure::write(Stream& stream) { - size_t totalSent = 0; - size_t countRead; - size_t countSent; +size_t WiFiClientSecure::write(Stream& stream) +{ + size_t totalSent = 0; + size_t countRead; + size_t countSent; + + if (!connected() || !_handshake_done) + { + DEBUG_BSSL("write: Connect/handshake not completed yet\n"); + return 0; + } - if (!connected() || !_handshake_done) { - DEBUG_BSSL("write: Connect/handshake not completed yet\n"); - return 0; - } - - do { - uint8_t temp[256]; // Temporary chunk size same as ClientContext - countSent = 0; - countRead = stream.readBytes(temp, sizeof(temp)); - if (countRead) { - countSent = _write((const uint8_t*)temp, countRead, true); - totalSent += countSent; - } - yield(); // Feed the WDT - } while ((countSent == countRead) && (countSent > 0)); - return totalSent; + do + { + uint8_t temp[256]; // Temporary chunk size same as ClientContext + countSent = 0; + countRead = stream.readBytes(temp, sizeof(temp)); + if (countRead) + { + countSent = _write((const uint8_t*)temp, countRead, true); + totalSent += countSent; + } + yield(); // Feed the WDT + } while ((countSent == countRead) && (countSent > 0)); + return totalSent; } -int WiFiClientSecure::read(uint8_t *buf, size_t size) { - if (!ctx_present() || !_handshake_done) { - return -1; - } - - int avail = available(); - bool conn = connected(); - if (!avail && conn) { - return 0; // We're still connected, but nothing to read - } - if (!avail && !conn) { - DEBUG_BSSL("read: Not connected, none left available\n"); - return -1; - } +int WiFiClientSecure::read(uint8_t *buf, size_t size) +{ + if (!ctx_present() || !_handshake_done) + { + return -1; + } - if (avail) { - // Take data from the recvapp buffer - int to_copy = _recvapp_len < size ? _recvapp_len : size; - memcpy(buf, _recvapp_buf, to_copy); - br_ssl_engine_recvapp_ack(_eng, to_copy); - _recvapp_buf = nullptr; - _recvapp_len = 0; - return to_copy; - } + int avail = available(); + bool conn = connected(); + if (!avail && conn) + { + return 0; // We're still connected, but nothing to read + } + if (!avail && !conn) + { + DEBUG_BSSL("read: Not connected, none left available\n"); + return -1; + } - if (!conn) { - DEBUG_BSSL("read: Not connected\n"); - return -1; - } - return 0; // If we're connected, no error but no read. + if (avail) + { + // Take data from the recvapp buffer + int to_copy = _recvapp_len < size ? _recvapp_len : size; + memcpy(buf, _recvapp_buf, to_copy); + br_ssl_engine_recvapp_ack(_eng, to_copy); + _recvapp_buf = nullptr; + _recvapp_len = 0; + return to_copy; + } + + if (!conn) + { + DEBUG_BSSL("read: Not connected\n"); + return -1; + } + return 0; // If we're connected, no error but no read. } -int WiFiClientSecure::read() { - uint8_t c; - if (1 == read(&c, 1)) { - return c; - } - DEBUG_BSSL("read: failed\n"); - return -1; +int WiFiClientSecure::read() +{ + uint8_t c; + if (1 == read(&c, 1)) + { + return c; + } + DEBUG_BSSL("read: failed\n"); + return -1; } -int WiFiClientSecure::available() { - if (_recvapp_buf) { - return _recvapp_len; // Anything from last call? - } - _recvapp_buf = nullptr; - _recvapp_len = 0; - if (!ctx_present() || _run_until(BR_SSL_RECVAPP, false) < 0) { +int WiFiClientSecure::available() +{ + if (_recvapp_buf) + { + return _recvapp_len; // Anything from last call? + } + _recvapp_buf = nullptr; + _recvapp_len = 0; + if (!ctx_present() || _run_until(BR_SSL_RECVAPP, false) < 0) + { + return 0; + } + int st = br_ssl_engine_current_state(_eng); + if (st == BR_SSL_CLOSED) + { + return 0; // Nothing leftover, SSL is closed + } + if (st & BR_SSL_RECVAPP) + { + _recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len); + return _recvapp_len; + } + return 0; - } - int st = br_ssl_engine_current_state(_eng); - if (st == BR_SSL_CLOSED) { - return 0; // Nothing leftover, SSL is closed - } - if (st & BR_SSL_RECVAPP) { - _recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len); - return _recvapp_len; - } - - return 0; } -int WiFiClientSecure::peek() { - if (!ctx_present() || !available()) { - DEBUG_BSSL("peek: Not connected, none left available\n"); +int WiFiClientSecure::peek() +{ + if (!ctx_present() || !available()) + { + DEBUG_BSSL("peek: Not connected, none left available\n"); + return -1; + } + if (_recvapp_buf && _recvapp_len) + { + return _recvapp_buf[0]; + } + DEBUG_BSSL("peek: No data left\n"); return -1; - } - if (_recvapp_buf && _recvapp_len) { - return _recvapp_buf[0]; - } - DEBUG_BSSL("peek: No data left\n"); - return -1; } -size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) { - size_t to_copy = 0; - if (!ctx_present()) { - DEBUG_BSSL("peekBytes: Not connected\n"); - return 0; - } +size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) +{ + size_t to_copy = 0; + if (!ctx_present()) + { + DEBUG_BSSL("peekBytes: Not connected\n"); + return 0; + } - _startMillis = millis(); - while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) { - yield(); - } + _startMillis = millis(); + while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) + { + yield(); + } - to_copy = _recvapp_len < length ? _recvapp_len : length; - memcpy(buffer, _recvapp_buf, to_copy); - return to_copy; + to_copy = _recvapp_len < length ? _recvapp_len : length; + memcpy(buffer, _recvapp_buf, to_copy); + return to_copy; } -/* --- Copied almost verbatim from BEARSSL SSL_IO.C --- - Run the engine, until the specified target state is achieved, or - an error occurs. The target state is SENDAPP, RECVAPP, or the - combination of both (the combination matches either). When a match is - achieved, this function returns 0. On error, it returns -1. +/* --- Copied almost verbatim from BEARSSL SSL_IO.C --- + Run the engine, until the specified target state is achieved, or + an error occurs. The target state is SENDAPP, RECVAPP, or the + combination of both (the combination matches either). When a match is + achieved, this function returns 0. On error, it returns -1. */ -int WiFiClientSecure::_run_until(unsigned target, bool blocking) { - if (!ctx_present()) { - DEBUG_BSSL("_run_until: Not connected\n"); - return -1; - } - for (int no_work = 0; blocking || no_work < 2;) { - if (blocking) { - // Only for blocking operations can we afford to yield() - optimistic_yield(100); +int WiFiClientSecure::_run_until(unsigned target, bool blocking) +{ + if (!ctx_present()) + { + DEBUG_BSSL("_run_until: Not connected\n"); + return -1; } + for (int no_work = 0; blocking || no_work < 2;) + { + if (blocking) + { + // Only for blocking operations can we afford to yield() + optimistic_yield(100); + } - int state; - state = br_ssl_engine_current_state(_eng); - if (state & BR_SSL_CLOSED) { - return -1; - } + int state; + state = br_ssl_engine_current_state(_eng); + if (state & BR_SSL_CLOSED) + { + return -1; + } - if (!(_client->state() == ESTABLISHED) && !WiFiClient::available()) { - return (state & target) ? 0 : -1; - } + if (!(_client->state() == ESTABLISHED) && !WiFiClient::available()) + { + return (state & target) ? 0 : -1; + } - /* - If there is some record data to send, do it. This takes - precedence over everything else. - */ - if (state & BR_SSL_SENDREC) { - unsigned char *buf; - size_t len; - int wlen; - - buf = br_ssl_engine_sendrec_buf(_eng, &len); - wlen = WiFiClient::write(buf, len); - if (wlen <= 0) { /* - If we received a close_notify and we - still send something, then we have our - own response close_notify to send, and - the peer is allowed by RFC 5246 not to - wait for it. + If there is some record data to send, do it. This takes + precedence over everything else. */ - return -1; - } - if (wlen > 0) { - br_ssl_engine_sendrec_ack(_eng, wlen); - } - no_work = 0; - continue; - } - - /* - If we reached our target, then we are finished. - */ - if (state & target) { - return 0; - } + if (state & BR_SSL_SENDREC) + { + unsigned char *buf; + size_t len; + int wlen; + + buf = br_ssl_engine_sendrec_buf(_eng, &len); + wlen = WiFiClient::write(buf, len); + if (wlen <= 0) + { + /* + If we received a close_notify and we + still send something, then we have our + own response close_notify to send, and + the peer is allowed by RFC 5246 not to + wait for it. + */ + return -1; + } + if (wlen > 0) + { + br_ssl_engine_sendrec_ack(_eng, wlen); + } + no_work = 0; + continue; + } - /* - If some application data must be read, and we did not - exit, then this means that we are trying to write data, - and that's not possible until the application data is - read. This may happen if using a shared in/out buffer, - and the underlying protocol is not strictly half-duplex. - This is unrecoverable here, so we report an error. - */ - if (state & BR_SSL_RECVAPP) { - DEBUG_BSSL("_run_until: Fatal protocol state\n"); - return -1; - } + /* + If we reached our target, then we are finished. + */ + if (state & target) + { + return 0; + } - /* - If we reached that point, then either we are trying - to read data and there is some, or the engine is stuck - until a new record is obtained. - */ - if (state & BR_SSL_RECVREC) { - if (WiFiClient::available()) { - unsigned char *buf; - size_t len; - int rlen; - - buf = br_ssl_engine_recvrec_buf(_eng, &len); - rlen = WiFiClient::read(buf, len); - if (rlen < 0) { - return -1; + /* + If some application data must be read, and we did not + exit, then this means that we are trying to write data, + and that's not possible until the application data is + read. This may happen if using a shared in/out buffer, + and the underlying protocol is not strictly half-duplex. + This is unrecoverable here, so we report an error. + */ + if (state & BR_SSL_RECVAPP) + { + DEBUG_BSSL("_run_until: Fatal protocol state\n"); + return -1; } - if (rlen > 0) { - br_ssl_engine_recvrec_ack(_eng, rlen); + + /* + If we reached that point, then either we are trying + to read data and there is some, or the engine is stuck + until a new record is obtained. + */ + if (state & BR_SSL_RECVREC) + { + if (WiFiClient::available()) + { + unsigned char *buf; + size_t len; + int rlen; + + buf = br_ssl_engine_recvrec_buf(_eng, &len); + rlen = WiFiClient::read(buf, len); + if (rlen < 0) + { + return -1; + } + if (rlen > 0) + { + br_ssl_engine_recvrec_ack(_eng, rlen); + } + no_work = 0; + continue; + } } - no_work = 0; - continue; - } - } - /* - We can reach that point if the target RECVAPP, and - the state contains SENDAPP only. This may happen with - a shared in/out buffer. In that case, we must flush - the buffered data to "make room" for a new incoming - record. - */ - br_ssl_engine_flush(_eng, 0); + /* + We can reach that point if the target RECVAPP, and + the state contains SENDAPP only. This may happen with + a shared in/out buffer. In that case, we must flush + the buffered data to "make room" for a new incoming + record. + */ + br_ssl_engine_flush(_eng, 0); - no_work++; // We didn't actually advance here - } - // We only get here if we ran through the loop without getting anything done - return -1; + no_work++; // We didn't actually advance here + } + // We only get here if we ran through the loop without getting anything done + return -1; } -bool WiFiClientSecure::_wait_for_handshake() { - _handshake_done = false; - while (!_handshake_done && _clientConnected()) { - int ret = _run_until(BR_SSL_SENDAPP); - if (ret < 0) { - DEBUG_BSSL("_wait_for_handshake: failed\n"); - break; - } - if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { - _handshake_done = true; +bool WiFiClientSecure::_wait_for_handshake() +{ + _handshake_done = false; + while (!_handshake_done && _clientConnected()) + { + int ret = _run_until(BR_SSL_SENDAPP); + if (ret < 0) + { + DEBUG_BSSL("_wait_for_handshake: failed\n"); + break; + } + if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) + { + _handshake_done = true; + } + optimistic_yield(1000); } - optimistic_yield(1000); - } - return _handshake_done; + return _handshake_done; } -static uint8_t htoi (unsigned char c) +static uint8_t htoi(unsigned char c) { - if (c>='0' && c <='9') return c - '0'; - else if (c>='A' && c<='F') return 10 + c - 'A'; - else if (c>='a' && c<='f') return 10 + c - 'a'; - else return 255; + if (c >= '0' && c <= '9') + { + return c - '0'; + } + else if (c >= 'A' && c <= 'F') + { + return 10 + c - 'A'; + } + else if (c >= 'a' && c <= 'f') + { + return 10 + c - 'a'; + } + else + { + return 255; + } } // Set a fingerprint by parsing an ASCII string -bool WiFiClientSecure::setFingerprint(const char *fpStr) { - int idx = 0; - uint8_t c, d; - uint8_t fp[20]; - - while (idx < 20) { - c = pgm_read_byte(fpStr++); - if (!c) break; // String ended, done processing - d = pgm_read_byte(fpStr++); - if (!d) { - DEBUG_BSSL("setFingerprint: FP too short\n"); - return false; // Only half of the last hex digit, error - } - c = htoi(c); - d = htoi(d); - if ((c>15) || (d>15)) { - DEBUG_BSSL("setFingerprint: Invalid char\n"); - return false; // Error in one of the hex characters - } - fp[idx++] = (c<<4)|d; - - // Skip 0 or more spaces or colons - while ( pgm_read_byte(fpStr) && (pgm_read_byte(fpStr)==' ' || pgm_read_byte(fpStr)==':') ) { - fpStr++; - } - } - if ((idx != 20) || pgm_read_byte(fpStr)) { - DEBUG_BSSL("setFingerprint: Garbage at end of fp\n"); - return false; // Garbage at EOL or we didn't have enough hex digits - } - return setFingerprint(fp); +bool WiFiClientSecure::setFingerprint(const char *fpStr) +{ + int idx = 0; + uint8_t c, d; + uint8_t fp[20]; + + while (idx < 20) + { + c = pgm_read_byte(fpStr++); + if (!c) + { + break; // String ended, done processing + } + d = pgm_read_byte(fpStr++); + if (!d) + { + DEBUG_BSSL("setFingerprint: FP too short\n"); + return false; // Only half of the last hex digit, error + } + c = htoi(c); + d = htoi(d); + if ((c > 15) || (d > 15)) + { + DEBUG_BSSL("setFingerprint: Invalid char\n"); + return false; // Error in one of the hex characters + } + fp[idx++] = (c << 4) | d; + + // Skip 0 or more spaces or colons + while (pgm_read_byte(fpStr) && (pgm_read_byte(fpStr) == ' ' || pgm_read_byte(fpStr) == ':')) + { + fpStr++; + } + } + if ((idx != 20) || pgm_read_byte(fpStr)) + { + DEBUG_BSSL("setFingerprint: Garbage at end of fp\n"); + return false; // Garbage at EOL or we didn't have enough hex digits + } + return setFingerprint(fp); } extern "C" { - // BearSSL doesn't define a true insecure decoder, so we make one ourselves - // from the simple parser. It generates the issuer and subject hashes and - // the SHA1 fingerprint, only one (or none!) of which will be used to - // "verify" the certificate. - - // Private x509 decoder state - struct br_x509_insecure_context { - const br_x509_class *vtable; - bool done_cert; - const uint8_t *match_fingerprint; - br_sha1_context sha1_cert; - bool allow_self_signed; - br_sha256_context sha256_subject; - br_sha256_context sha256_issuer; - br_x509_decoder_context ctx; - }; - - // Callback for the x509_minimal subject DN - static void insecure_subject_dn_append(void *ctx, const void *buf, size_t len) { - br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx; - br_sha256_update(&xc->sha256_subject, buf, len); - } - - // Callback for the x509_minimal issuer DN - static void insecure_issuer_dn_append(void *ctx, const void *buf, size_t len) { - br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx; - br_sha256_update(&xc->sha256_issuer, buf, len); - } - - // Callback on the first byte of any certificate - static void insecure_start_chain(const br_x509_class **ctx, const char *server_name) { - br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx; - br_x509_decoder_init(&xc->ctx, insecure_subject_dn_append, xc, insecure_issuer_dn_append, xc); - xc->done_cert = false; - br_sha1_init(&xc->sha1_cert); - br_sha256_init(&xc->sha256_subject); - br_sha256_init(&xc->sha256_issuer); - (void)server_name; - } - - // Callback for each certificate present in the chain (but only operates - // on the first one by design). - static void insecure_start_cert(const br_x509_class **ctx, uint32_t length) { - (void) ctx; - (void) length; - } - - // Callback for each byte stream in the chain. Only process first cert. - static void insecure_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) { - br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx; - // Don't process anything but the first certificate in the chain - if (!xc->done_cert) { - br_sha1_update(&xc->sha1_cert, buf, len); - br_x509_decoder_push(&xc->ctx, (const void*)buf, len); - } - } - - // Callback on individual cert end. - static void insecure_end_cert(const br_x509_class **ctx) { - br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx; - xc->done_cert = true; - } - - // Callback when complete chain has been parsed. - // Return 0 on validation success, !0 on validation error - static unsigned insecure_end_chain(const br_x509_class **ctx) { - const br_x509_insecure_context *xc = (const br_x509_insecure_context *)ctx; - if (!xc->done_cert) { - DEBUG_BSSL("insecure_end_chain: No cert seen\n"); - return 1; // error - } - - // Handle SHA1 fingerprint matching - char res[20]; - br_sha1_out(&xc->sha1_cert, res); - if (xc->match_fingerprint && memcmp(res, xc->match_fingerprint, sizeof(res))) { - DEBUG_BSSL("insecure_end_chain: Received cert FP doesn't match\n"); - return BR_ERR_X509_NOT_TRUSTED; - } - - // Handle self-signer certificate acceptance - char res_issuer[32]; - char res_subject[32]; - br_sha256_out(&xc->sha256_issuer, res_issuer); - br_sha256_out(&xc->sha256_subject, res_subject); - if (xc->allow_self_signed && memcmp(res_subject, res_issuer, sizeof(res_issuer))) { - DEBUG_BSSL("insecure_end_chain: Didn't get self-signed cert\n"); - return BR_ERR_X509_NOT_TRUSTED; - } - - // Default (no validation at all) or no errors in prior checks = success. - return 0; - } - - // Return the public key from the validator (set by x509_minimal) - static const br_x509_pkey *insecure_get_pkey(const br_x509_class *const *ctx, unsigned *usages) { - const br_x509_insecure_context *xc = (const br_x509_insecure_context *)ctx; - if (usages != NULL) { - *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; // I said we were insecure! - } - return &xc->ctx.pkey; - } - - // Set up the x509 insecure data structures for BearSSL core to use. - void br_x509_insecure_init(br_x509_insecure_context *ctx, int _use_fingerprint, const uint8_t _fingerprint[20], int _allow_self_signed) { - static const br_x509_class br_x509_insecure_vtable PROGMEM = { - sizeof(br_x509_insecure_context), - insecure_start_chain, - insecure_start_cert, - insecure_append, - insecure_end_cert, - insecure_end_chain, - insecure_get_pkey + // BearSSL doesn't define a true insecure decoder, so we make one ourselves + // from the simple parser. It generates the issuer and subject hashes and + // the SHA1 fingerprint, only one (or none!) of which will be used to + // "verify" the certificate. + + // Private x509 decoder state + struct br_x509_insecure_context + { + const br_x509_class *vtable; + bool done_cert; + const uint8_t *match_fingerprint; + br_sha1_context sha1_cert; + bool allow_self_signed; + br_sha256_context sha256_subject; + br_sha256_context sha256_issuer; + br_x509_decoder_context ctx; }; - memset(ctx, 0, sizeof * ctx); - ctx->vtable = &br_x509_insecure_vtable; - ctx->done_cert = false; - ctx->match_fingerprint = _use_fingerprint ? _fingerprint : nullptr; - ctx->allow_self_signed = _allow_self_signed ? 1 : 0; - } - - // Some constants uses to init the server/client contexts - // Note that suites_P needs to be copied to RAM before use w/BearSSL! - // List copied verbatim from BearSSL/ssl_client_full.c - /* - * The "full" profile supports all implemented cipher suites. - * - * Rationale for suite order, from most important to least - * important rule: - * - * -- Don't use 3DES if AES or ChaCha20 is available. - * -- Try to have Forward Secrecy (ECDHE suite) if possible. - * -- When not using Forward Secrecy, ECDH key exchange is - * better than RSA key exchange (slightly more expensive on the - * client, but much cheaper on the server, and it implies smaller - * messages). - * -- ChaCha20+Poly1305 is better than AES/GCM (faster, smaller code). - * -- GCM is better than CCM and CBC. CCM is better than CBC. - * -- CCM is preferable over CCM_8 (with CCM_8, forgeries may succeed - * with probability 2^(-64)). - * -- AES-128 is preferred over AES-256 (AES-128 is already - * strong enough, and AES-256 is 40% more expensive). - */ - static const uint16_t suites_P[] PROGMEM = { + // Callback for the x509_minimal subject DN + static void insecure_subject_dn_append(void *ctx, const void *buf, size_t len) + { + br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx; + br_sha256_update(&xc->sha256_subject, buf, len); + } + + // Callback for the x509_minimal issuer DN + static void insecure_issuer_dn_append(void *ctx, const void *buf, size_t len) + { + br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx; + br_sha256_update(&xc->sha256_issuer, buf, len); + } + + // Callback on the first byte of any certificate + static void insecure_start_chain(const br_x509_class **ctx, const char *server_name) + { + br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx; + br_x509_decoder_init(&xc->ctx, insecure_subject_dn_append, xc, insecure_issuer_dn_append, xc); + xc->done_cert = false; + br_sha1_init(&xc->sha1_cert); + br_sha256_init(&xc->sha256_subject); + br_sha256_init(&xc->sha256_issuer); + (void)server_name; + } + + // Callback for each certificate present in the chain (but only operates + // on the first one by design). + static void insecure_start_cert(const br_x509_class **ctx, uint32_t length) + { + (void) ctx; + (void) length; + } + + // Callback for each byte stream in the chain. Only process first cert. + static void insecure_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) + { + br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx; + // Don't process anything but the first certificate in the chain + if (!xc->done_cert) + { + br_sha1_update(&xc->sha1_cert, buf, len); + br_x509_decoder_push(&xc->ctx, (const void*)buf, len); + } + } + + // Callback on individual cert end. + static void insecure_end_cert(const br_x509_class **ctx) + { + br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx; + xc->done_cert = true; + } + + // Callback when complete chain has been parsed. + // Return 0 on validation success, !0 on validation error + static unsigned insecure_end_chain(const br_x509_class **ctx) + { + const br_x509_insecure_context *xc = (const br_x509_insecure_context *)ctx; + if (!xc->done_cert) + { + DEBUG_BSSL("insecure_end_chain: No cert seen\n"); + return 1; // error + } + + // Handle SHA1 fingerprint matching + char res[20]; + br_sha1_out(&xc->sha1_cert, res); + if (xc->match_fingerprint && memcmp(res, xc->match_fingerprint, sizeof(res))) + { + DEBUG_BSSL("insecure_end_chain: Received cert FP doesn't match\n"); + return BR_ERR_X509_NOT_TRUSTED; + } + + // Handle self-signer certificate acceptance + char res_issuer[32]; + char res_subject[32]; + br_sha256_out(&xc->sha256_issuer, res_issuer); + br_sha256_out(&xc->sha256_subject, res_subject); + if (xc->allow_self_signed && memcmp(res_subject, res_issuer, sizeof(res_issuer))) + { + DEBUG_BSSL("insecure_end_chain: Didn't get self-signed cert\n"); + return BR_ERR_X509_NOT_TRUSTED; + } + + // Default (no validation at all) or no errors in prior checks = success. + return 0; + } + + // Return the public key from the validator (set by x509_minimal) + static const br_x509_pkey *insecure_get_pkey(const br_x509_class *const *ctx, unsigned *usages) + { + const br_x509_insecure_context *xc = (const br_x509_insecure_context *)ctx; + if (usages != NULL) + { + *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; // I said we were insecure! + } + return &xc->ctx.pkey; + } + + // Set up the x509 insecure data structures for BearSSL core to use. + void br_x509_insecure_init(br_x509_insecure_context *ctx, int _use_fingerprint, const uint8_t _fingerprint[20], int _allow_self_signed) + { + static const br_x509_class br_x509_insecure_vtable PROGMEM = + { + sizeof(br_x509_insecure_context), + insecure_start_chain, + insecure_start_cert, + insecure_append, + insecure_end_cert, + insecure_end_chain, + insecure_get_pkey + }; + + memset(ctx, 0, sizeof * ctx); + ctx->vtable = &br_x509_insecure_vtable; + ctx->done_cert = false; + ctx->match_fingerprint = _use_fingerprint ? _fingerprint : nullptr; + ctx->allow_self_signed = _allow_self_signed ? 1 : 0; + } + + // Some constants uses to init the server/client contexts + // Note that suites_P needs to be copied to RAM before use w/BearSSL! + // List copied verbatim from BearSSL/ssl_client_full.c + /* + The "full" profile supports all implemented cipher suites. + + Rationale for suite order, from most important to least + important rule: + + -- Don't use 3DES if AES or ChaCha20 is available. + -- Try to have Forward Secrecy (ECDHE suite) if possible. + -- When not using Forward Secrecy, ECDH key exchange is + better than RSA key exchange (slightly more expensive on the + client, but much cheaper on the server, and it implies smaller + messages). + -- ChaCha20+Poly1305 is better than AES/GCM (faster, smaller code). + -- GCM is better than CCM and CBC. CCM is better than CBC. + -- CCM is preferable over CCM_8 (with CCM_8, forgeries may succeed + with probability 2^(-64)). + -- AES-128 is preferred over AES-256 (AES-128 is already + strong enough, and AES-256 is 40% more expensive). + */ + static const uint16_t suites_P[] PROGMEM = + { #ifndef BEARSSL_SSL_BASIC - BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, - BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, - BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, - BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, - BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, - BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, - BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, - BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, - BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, - BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, - BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, - BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, - BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, - BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, - BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, - BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, - BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, - BR_TLS_RSA_WITH_AES_128_GCM_SHA256, - BR_TLS_RSA_WITH_AES_256_GCM_SHA384, - BR_TLS_RSA_WITH_AES_128_CCM, - BR_TLS_RSA_WITH_AES_256_CCM, - BR_TLS_RSA_WITH_AES_128_CCM_8, - BR_TLS_RSA_WITH_AES_256_CCM_8, + BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_RSA_WITH_AES_128_CCM, + BR_TLS_RSA_WITH_AES_256_CCM, + BR_TLS_RSA_WITH_AES_128_CCM_8, + BR_TLS_RSA_WITH_AES_256_CCM_8, #endif - BR_TLS_RSA_WITH_AES_128_CBC_SHA256, - BR_TLS_RSA_WITH_AES_256_CBC_SHA256, - BR_TLS_RSA_WITH_AES_128_CBC_SHA, - BR_TLS_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_RSA_WITH_AES_256_CBC_SHA256, + BR_TLS_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_RSA_WITH_AES_256_CBC_SHA, #ifndef BEARSSL_SSL_BASIC - BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, - BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, - BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, - BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA + BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA #endif - }; - - // For apps which want to use less secure but faster ciphers, only - static const uint16_t faster_suites_P[] PROGMEM = { - BR_TLS_RSA_WITH_AES_256_CBC_SHA256, - BR_TLS_RSA_WITH_AES_128_CBC_SHA256, - BR_TLS_RSA_WITH_AES_256_CBC_SHA, - BR_TLS_RSA_WITH_AES_128_CBC_SHA }; - - // Install hashes into the SSL engine - static void br_ssl_client_install_hashes(br_ssl_engine_context *eng) { - br_ssl_engine_set_hash(eng, br_md5_ID, &br_md5_vtable); - br_ssl_engine_set_hash(eng, br_sha1_ID, &br_sha1_vtable); - br_ssl_engine_set_hash(eng, br_sha224_ID, &br_sha224_vtable); - br_ssl_engine_set_hash(eng, br_sha256_ID, &br_sha256_vtable); - br_ssl_engine_set_hash(eng, br_sha384_ID, &br_sha384_vtable); - br_ssl_engine_set_hash(eng, br_sha512_ID, &br_sha512_vtable); - } - - static void br_x509_minimal_install_hashes(br_x509_minimal_context *x509) { - br_x509_minimal_set_hash(x509, br_md5_ID, &br_md5_vtable); - br_x509_minimal_set_hash(x509, br_sha1_ID, &br_sha1_vtable); - br_x509_minimal_set_hash(x509, br_sha224_ID, &br_sha224_vtable); - br_x509_minimal_set_hash(x509, br_sha256_ID, &br_sha256_vtable); - br_x509_minimal_set_hash(x509, br_sha384_ID, &br_sha384_vtable); - br_x509_minimal_set_hash(x509, br_sha512_ID, &br_sha512_vtable); - } - - // Default initializion for our SSL clients - static void br_ssl_client_base_init(br_ssl_client_context *cc, const uint16_t *cipher_list, int cipher_cnt) { - uint16_t suites[cipher_cnt]; - memcpy_P(suites, cipher_list, cipher_cnt * sizeof(cipher_list[0])); - br_ssl_client_zero(cc); - br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12); - br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); - br_ssl_client_set_default_rsapub(cc); - br_ssl_engine_set_default_rsavrfy(&cc->eng); + }; + + // For apps which want to use less secure but faster ciphers, only + static const uint16_t faster_suites_P[] PROGMEM = + { + BR_TLS_RSA_WITH_AES_256_CBC_SHA256, + BR_TLS_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_RSA_WITH_AES_128_CBC_SHA + }; + + // Install hashes into the SSL engine + static void br_ssl_client_install_hashes(br_ssl_engine_context *eng) + { + br_ssl_engine_set_hash(eng, br_md5_ID, &br_md5_vtable); + br_ssl_engine_set_hash(eng, br_sha1_ID, &br_sha1_vtable); + br_ssl_engine_set_hash(eng, br_sha224_ID, &br_sha224_vtable); + br_ssl_engine_set_hash(eng, br_sha256_ID, &br_sha256_vtable); + br_ssl_engine_set_hash(eng, br_sha384_ID, &br_sha384_vtable); + br_ssl_engine_set_hash(eng, br_sha512_ID, &br_sha512_vtable); + } + + static void br_x509_minimal_install_hashes(br_x509_minimal_context *x509) + { + br_x509_minimal_set_hash(x509, br_md5_ID, &br_md5_vtable); + br_x509_minimal_set_hash(x509, br_sha1_ID, &br_sha1_vtable); + br_x509_minimal_set_hash(x509, br_sha224_ID, &br_sha224_vtable); + br_x509_minimal_set_hash(x509, br_sha256_ID, &br_sha256_vtable); + br_x509_minimal_set_hash(x509, br_sha384_ID, &br_sha384_vtable); + br_x509_minimal_set_hash(x509, br_sha512_ID, &br_sha512_vtable); + } + + // Default initializion for our SSL clients + static void br_ssl_client_base_init(br_ssl_client_context *cc, const uint16_t *cipher_list, int cipher_cnt) + { + uint16_t suites[cipher_cnt]; + memcpy_P(suites, cipher_list, cipher_cnt * sizeof(cipher_list[0])); + br_ssl_client_zero(cc); + br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12); + br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); + br_ssl_client_set_default_rsapub(cc); + br_ssl_engine_set_default_rsavrfy(&cc->eng); #ifndef BEARSSL_SSL_BASIC - br_ssl_engine_set_default_ecdsa(&cc->eng); + br_ssl_engine_set_default_ecdsa(&cc->eng); #endif - br_ssl_client_install_hashes(&cc->eng); - br_ssl_engine_set_prf10(&cc->eng, &br_tls10_prf); - br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf); - br_ssl_engine_set_prf_sha384(&cc->eng, &br_tls12_sha384_prf); - br_ssl_engine_set_default_aes_cbc(&cc->eng); + br_ssl_client_install_hashes(&cc->eng); + br_ssl_engine_set_prf10(&cc->eng, &br_tls10_prf); + br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf); + br_ssl_engine_set_prf_sha384(&cc->eng, &br_tls12_sha384_prf); + br_ssl_engine_set_default_aes_cbc(&cc->eng); #ifndef BEARSSL_SSL_BASIC - br_ssl_engine_set_default_aes_gcm(&cc->eng); - br_ssl_engine_set_default_aes_ccm(&cc->eng); - br_ssl_engine_set_default_des_cbc(&cc->eng); - br_ssl_engine_set_default_chapol(&cc->eng); + br_ssl_engine_set_default_aes_gcm(&cc->eng); + br_ssl_engine_set_default_aes_ccm(&cc->eng); + br_ssl_engine_set_default_des_cbc(&cc->eng); + br_ssl_engine_set_default_chapol(&cc->eng); #endif - } + } } // Set custom list of ciphers -bool WiFiClientSecure::setCiphers(const uint16_t *cipherAry, int cipherCount) { - _cipher_list = nullptr; - _cipher_list = std::shared_ptr(new uint16_t[cipherCount], std::default_delete()); - if (!_cipher_list.get()) { - DEBUG_BSSL("setCiphers: list empty\n"); - return false; - } - memcpy_P(_cipher_list.get(), cipherAry, cipherCount * sizeof(uint16_t)); - _cipher_cnt = cipherCount; - return true; +bool WiFiClientSecure::setCiphers(const uint16_t *cipherAry, int cipherCount) +{ + _cipher_list = nullptr; + _cipher_list = std::shared_ptr(new uint16_t[cipherCount], std::default_delete()); + if (!_cipher_list.get()) + { + DEBUG_BSSL("setCiphers: list empty\n"); + return false; + } + memcpy_P(_cipher_list.get(), cipherAry, cipherCount * sizeof(uint16_t)); + _cipher_cnt = cipherCount; + return true; } -bool WiFiClientSecure::setCiphersLessSecure() { - return setCiphers(faster_suites_P, sizeof(faster_suites_P)/sizeof(faster_suites_P[0])); +bool WiFiClientSecure::setCiphersLessSecure() +{ + return setCiphers(faster_suites_P, sizeof(faster_suites_P) / sizeof(faster_suites_P[0])); } -bool WiFiClientSecure::setCiphers(std::vector list) { - return setCiphers(&list[0], list.size()); +bool WiFiClientSecure::setCiphers(std::vector list) +{ + return setCiphers(&list[0], list.size()); } // Installs the appropriate X509 cert validation method for a client connection -bool WiFiClientSecure::_installClientX509Validator() { - if (_use_insecure || _use_fingerprint || _use_self_signed) { - // Use common insecure x509 authenticator - _x509_insecure = std::make_shared(); - if (!_x509_insecure) { - DEBUG_BSSL("_installClientX509Validator: OOM for _x509_insecure\n"); - return false; - } - br_x509_insecure_init(_x509_insecure.get(), _use_fingerprint, _fingerprint, _use_self_signed); - br_ssl_engine_set_x509(_eng, &_x509_insecure->vtable); - } else if (_knownkey) { - // Simple, pre-known public key authenticator, ignores cert completely. - _x509_knownkey = std::make_shared(); - if (!_x509_knownkey) { - DEBUG_BSSL("_installClientX509Validator: OOM for _x509_knownkey\n"); - return false; - } - if (_knownkey->isRSA()) { - br_x509_knownkey_init_rsa(_x509_knownkey.get(), _knownkey->getRSA(), _knownkey_usages); - } else if (_knownkey->isEC()) { +bool WiFiClientSecure::_installClientX509Validator() +{ + if (_use_insecure || _use_fingerprint || _use_self_signed) + { + // Use common insecure x509 authenticator + _x509_insecure = std::make_shared(); + if (!_x509_insecure) + { + DEBUG_BSSL("_installClientX509Validator: OOM for _x509_insecure\n"); + return false; + } + br_x509_insecure_init(_x509_insecure.get(), _use_fingerprint, _fingerprint, _use_self_signed); + br_ssl_engine_set_x509(_eng, &_x509_insecure->vtable); + } + else if (_knownkey) + { + // Simple, pre-known public key authenticator, ignores cert completely. + _x509_knownkey = std::make_shared(); + if (!_x509_knownkey) + { + DEBUG_BSSL("_installClientX509Validator: OOM for _x509_knownkey\n"); + return false; + } + if (_knownkey->isRSA()) + { + br_x509_knownkey_init_rsa(_x509_knownkey.get(), _knownkey->getRSA(), _knownkey_usages); + } + else if (_knownkey->isEC()) + { #ifndef BEARSSL_SSL_BASIC - br_x509_knownkey_init_ec(_x509_knownkey.get(), _knownkey->getEC(), _knownkey_usages); + br_x509_knownkey_init_ec(_x509_knownkey.get(), _knownkey->getEC(), _knownkey_usages); #else - (void) _knownkey; - (void) _knownkey_usages; - DEBUG_BSSL("_installClientX509Validator: Attempting to use EC keys in minimal cipher mode (no EC)\n"); - return false; + (void) _knownkey; + (void) _knownkey_usages; + DEBUG_BSSL("_installClientX509Validator: Attempting to use EC keys in minimal cipher mode (no EC)\n"); + return false; #endif + } + br_ssl_engine_set_x509(_eng, &_x509_knownkey->vtable); } - br_ssl_engine_set_x509(_eng, &_x509_knownkey->vtable); - } else { - // X509 minimal validator. Checks dates, cert chain for trusted CA, etc. - _x509_minimal = std::make_shared(); - if (!_x509_minimal) { - DEBUG_BSSL("_installClientX509Validator: OOM for _x509_minimal\n"); - return false; - } - br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta ? _ta->getTrustAnchors() : nullptr, _ta ? _ta->getCount() : 0); - br_x509_minimal_set_rsa(_x509_minimal.get(), br_ssl_engine_get_rsavrfy(_eng)); + else + { + // X509 minimal validator. Checks dates, cert chain for trusted CA, etc. + _x509_minimal = std::make_shared(); + if (!_x509_minimal) + { + DEBUG_BSSL("_installClientX509Validator: OOM for _x509_minimal\n"); + return false; + } + br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta ? _ta->getTrustAnchors() : nullptr, _ta ? _ta->getCount() : 0); + br_x509_minimal_set_rsa(_x509_minimal.get(), br_ssl_engine_get_rsavrfy(_eng)); #ifndef BEARSSL_SSL_BASIC - br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng)); + br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng)); #endif - br_x509_minimal_install_hashes(_x509_minimal.get()); - if (_now) { - // Magic constants convert to x509 times - br_x509_minimal_set_time(_x509_minimal.get(), ((uint32_t)_now) / 86400 + 719528, ((uint32_t)_now) % 86400); - } - if (_certStore) { - _certStore->installCertStore(_x509_minimal.get()); + br_x509_minimal_install_hashes(_x509_minimal.get()); + if (_now) + { + // Magic constants convert to x509 times + br_x509_minimal_set_time(_x509_minimal.get(), ((uint32_t)_now) / 86400 + 719528, ((uint32_t)_now) % 86400); + } + if (_certStore) + { + _certStore->installCertStore(_x509_minimal.get()); + } + br_ssl_engine_set_x509(_eng, &_x509_minimal->vtable); } - br_ssl_engine_set_x509(_eng, &_x509_minimal->vtable); - } - return true; + return true; } // Called by connect() to do the actual SSL setup and handshake. // Returns if the SSL handshake succeeded. -bool WiFiClientSecure::_connectSSL(const char* hostName) { - DEBUG_BSSL("_connectSSL: start connection\n"); - _freeSSL(); - _oom_err = false; +bool WiFiClientSecure::_connectSSL(const char* hostName) +{ + DEBUG_BSSL("_connectSSL: start connection\n"); + _freeSSL(); + _oom_err = false; #ifdef DEBUG_ESP_SSL - // BearSSL will reject all connections unless an authentication option is set, warn in DEBUG builds - if (!_use_insecure && !_use_fingerprint && !_use_self_signed && !_knownkey && !_certStore && !_ta) { - DEBUG_BSSL("Connection *will* fail, no authentication method is setup\n"); - } + // BearSSL will reject all connections unless an authentication option is set, warn in DEBUG builds + if (!_use_insecure && !_use_fingerprint && !_use_self_signed && !_knownkey && !_certStore && !_ta) + { + DEBUG_BSSL("Connection *will* fail, no authentication method is setup\n"); + } #endif - _sc = std::make_shared(); - _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); - _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); + _sc = std::make_shared(); + _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr + _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); + _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); + + if (!_sc || !_iobuf_in || !_iobuf_out) + { + _freeSSL(); // Frees _sc, _iobuf* + _oom_err = true; + DEBUG_BSSL("_connectSSL: OOM error\n"); + return false; + } - if (!_sc || !_iobuf_in || !_iobuf_out) { - _freeSSL(); // Frees _sc, _iobuf* - _oom_err = true; - DEBUG_BSSL("_connectSSL: OOM error\n"); - return false; - } - - // If no cipher list yet set, use defaults - if (_cipher_list.get() == nullptr) { - br_ssl_client_base_init(_sc.get(), suites_P, sizeof(suites_P) / sizeof(suites_P[0])); - } else { - br_ssl_client_base_init(_sc.get(), _cipher_list.get(), _cipher_cnt); - } - // Only failure possible in the installation is OOM - if (!_installClientX509Validator()) { - _freeSSL(); - _oom_err = true; - DEBUG_BSSL("_connectSSL: Can't install x509 validator\n"); - return false; - } - br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); - - // Apply any client certificates, if supplied. - if (_sk && _sk->isRSA()) { - br_ssl_client_set_single_rsa(_sc.get(), _chain ? _chain->getX509Certs() : nullptr, _chain ? _chain->getCount() : 0, - _sk->getRSA(), br_rsa_pkcs1_sign_get_default()); - } else if (_sk && _sk->isEC()) { + // If no cipher list yet set, use defaults + if (_cipher_list.get() == nullptr) + { + br_ssl_client_base_init(_sc.get(), suites_P, sizeof(suites_P) / sizeof(suites_P[0])); + } + else + { + br_ssl_client_base_init(_sc.get(), _cipher_list.get(), _cipher_cnt); + } + // Only failure possible in the installation is OOM + if (!_installClientX509Validator()) + { + _freeSSL(); + _oom_err = true; + DEBUG_BSSL("_connectSSL: Can't install x509 validator\n"); + return false; + } + br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + + // Apply any client certificates, if supplied. + if (_sk && _sk->isRSA()) + { + br_ssl_client_set_single_rsa(_sc.get(), _chain ? _chain->getX509Certs() : nullptr, _chain ? _chain->getCount() : 0, + _sk->getRSA(), br_rsa_pkcs1_sign_get_default()); + } + else if (_sk && _sk->isEC()) + { #ifndef BEARSSL_SSL_BASIC - br_ssl_client_set_single_ec(_sc.get(), _chain ? _chain->getX509Certs() : nullptr, _chain ? _chain->getCount() : 0, - _sk->getEC(), _allowed_usages, - _cert_issuer_key_type, br_ec_get_default(), br_ecdsa_sign_asn1_get_default()); + br_ssl_client_set_single_ec(_sc.get(), _chain ? _chain->getX509Certs() : nullptr, _chain ? _chain->getCount() : 0, + _sk->getEC(), _allowed_usages, + _cert_issuer_key_type, br_ec_get_default(), br_ecdsa_sign_asn1_get_default()); #else - _freeSSL(); - DEBUG_BSSL("_connectSSL: Attempting to use EC cert in minimal cipher mode (no EC)\n"); - return false; + _freeSSL(); + DEBUG_BSSL("_connectSSL: Attempting to use EC cert in minimal cipher mode (no EC)\n"); + return false; #endif - } + } - // Restore session from the storage spot, if present - if (_session) { - br_ssl_engine_set_session_parameters(_eng, _session->getSession()); - } + // Restore session from the storage spot, if present + if (_session) + { + br_ssl_engine_set_session_parameters(_eng, _session->getSession()); + } - if (!br_ssl_client_reset(_sc.get(), hostName, _session?1:0)) { - _freeSSL(); - DEBUG_BSSL("_connectSSL: Can't reset client\n"); - return false; - } + if (!br_ssl_client_reset(_sc.get(), hostName, _session ? 1 : 0)) + { + _freeSSL(); + DEBUG_BSSL("_connectSSL: Can't reset client\n"); + return false; + } - auto ret = _wait_for_handshake(); + auto ret = _wait_for_handshake(); #ifdef DEBUG_ESP_SSL - if (!ret) { - char err[256]; - getLastSSLError(err, sizeof(err)); - DEBUG_BSSL("Couldn't connect. Error = '%s'\n", err); - } else { - DEBUG_BSSL("Connected!\n"); - } + if (!ret) + { + char err[256]; + getLastSSLError(err, sizeof(err)); + DEBUG_BSSL("Couldn't connect. Error = '%s'\n", err); + } + else + { + DEBUG_BSSL("Connected!\n"); + } #endif - return ret; + return ret; } // Slightly different X509 setup for servers who want to validate client // certificates, so factor it out as it's used in RSA and EC servers. -bool WiFiClientSecure::_installServerX509Validator(const X509List *client_CA_ta) { - if (client_CA_ta) { - _ta = client_CA_ta; - // X509 minimal validator. Checks dates, cert chain for trusted CA, etc. - _x509_minimal = std::make_shared(); - if (!_x509_minimal) { - _freeSSL(); - _oom_err = true; - DEBUG_BSSL("_installServerX509Validator: OOM for _x509_minimal\n"); - return false; - } - br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta->getTrustAnchors(), _ta->getCount()); - br_ssl_engine_set_default_rsavrfy(_eng); +bool WiFiClientSecure::_installServerX509Validator(const X509List *client_CA_ta) +{ + if (client_CA_ta) + { + _ta = client_CA_ta; + // X509 minimal validator. Checks dates, cert chain for trusted CA, etc. + _x509_minimal = std::make_shared(); + if (!_x509_minimal) + { + _freeSSL(); + _oom_err = true; + DEBUG_BSSL("_installServerX509Validator: OOM for _x509_minimal\n"); + return false; + } + br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta->getTrustAnchors(), _ta->getCount()); + br_ssl_engine_set_default_rsavrfy(_eng); #ifndef BEARSSL_SSL_BASIC - br_ssl_engine_set_default_ecdsa(_eng); + br_ssl_engine_set_default_ecdsa(_eng); #endif - br_x509_minimal_set_rsa(_x509_minimal.get(), br_ssl_engine_get_rsavrfy(_eng)); + br_x509_minimal_set_rsa(_x509_minimal.get(), br_ssl_engine_get_rsavrfy(_eng)); #ifndef BEARSSL_SSL_BASIC - br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng)); + br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng)); #endif - br_x509_minimal_install_hashes(_x509_minimal.get()); - if (_now) { - // Magic constants convert to x509 times - br_x509_minimal_set_time(_x509_minimal.get(), ((uint32_t)_now) / 86400 + 719528, ((uint32_t)_now) % 86400); - } - br_ssl_engine_set_x509(_eng, &_x509_minimal->vtable); - br_ssl_server_set_trust_anchor_names_alt(_sc_svr.get(), _ta->getTrustAnchors(), _ta->getCount()); - } - return true; + br_x509_minimal_install_hashes(_x509_minimal.get()); + if (_now) + { + // Magic constants convert to x509 times + br_x509_minimal_set_time(_x509_minimal.get(), ((uint32_t)_now) / 86400 + 719528, ((uint32_t)_now) % 86400); + } + br_ssl_engine_set_x509(_eng, &_x509_minimal->vtable); + br_ssl_server_set_trust_anchor_names_alt(_sc_svr.get(), _ta->getTrustAnchors(), _ta->getCount()); + } + return true; } // Called by WiFiServerBearSSL when an RSA cert/key is specified. bool WiFiClientSecure::_connectSSLServerRSA(const X509List *chain, - const PrivateKey *sk, - const X509List *client_CA_ta) { - _freeSSL(); - _oom_err = false; - _sc_svr = std::make_shared(); - _eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); - _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); - - if (!_sc_svr || !_iobuf_in || !_iobuf_out) { + const PrivateKey *sk, + const X509List *client_CA_ta) +{ _freeSSL(); - _oom_err = true; - DEBUG_BSSL("_connectSSLServerRSA: OOM error\n"); - return false; - } + _oom_err = false; + _sc_svr = std::make_shared(); + _eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr + _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); + _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); + + if (!_sc_svr || !_iobuf_in || !_iobuf_out) + { + _freeSSL(); + _oom_err = true; + DEBUG_BSSL("_connectSSLServerRSA: OOM error\n"); + return false; + } - br_ssl_server_init_full_rsa(_sc_svr.get(), chain ? chain->getX509Certs() : nullptr, chain ? chain->getCount() : 0, sk ? sk->getRSA() : nullptr); - br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); - if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) { - DEBUG_BSSL("_connectSSLServerRSA: Can't install serverX509check\n"); - return false; - } - if (!br_ssl_server_reset(_sc_svr.get())) { - _freeSSL(); - DEBUG_BSSL("_connectSSLServerRSA: Can't reset server ctx\n"); - return false; - } + br_ssl_server_init_full_rsa(_sc_svr.get(), chain ? chain->getX509Certs() : nullptr, chain ? chain->getCount() : 0, sk ? sk->getRSA() : nullptr); + br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) + { + DEBUG_BSSL("_connectSSLServerRSA: Can't install serverX509check\n"); + return false; + } + if (!br_ssl_server_reset(_sc_svr.get())) + { + _freeSSL(); + DEBUG_BSSL("_connectSSLServerRSA: Can't reset server ctx\n"); + return false; + } - return _wait_for_handshake(); + return _wait_for_handshake(); } // Called by WiFiServerBearSSL when an elliptic curve cert/key is specified. bool WiFiClientSecure::_connectSSLServerEC(const X509List *chain, - unsigned cert_issuer_key_type, const PrivateKey *sk, - const X509List *client_CA_ta) { + unsigned cert_issuer_key_type, const PrivateKey *sk, + const X509List *client_CA_ta) +{ #ifndef BEARSSL_SSL_BASIC - _freeSSL(); - _oom_err = false; - _sc_svr = std::make_shared(); - _eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); - _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); - - if (!_sc_svr || !_iobuf_in || !_iobuf_out) { _freeSSL(); - _oom_err = true; - DEBUG_BSSL("_connectSSLServerEC: OOM error\n"); - return false; - } + _oom_err = false; + _sc_svr = std::make_shared(); + _eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr + _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); + _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); + + if (!_sc_svr || !_iobuf_in || !_iobuf_out) + { + _freeSSL(); + _oom_err = true; + DEBUG_BSSL("_connectSSLServerEC: OOM error\n"); + return false; + } - br_ssl_server_init_full_ec(_sc_svr.get(), chain ? chain->getX509Certs() : nullptr, chain ? chain->getCount() : 0, - cert_issuer_key_type, sk ? sk->getEC() : nullptr); - br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); - if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) { - DEBUG_BSSL("_connectSSLServerEC: Can't install serverX509check\n"); - return false; - } - if (!br_ssl_server_reset(_sc_svr.get())) { - _freeSSL(); - DEBUG_BSSL("_connectSSLServerEC: Can't reset server ctx\n"); - return false; - } + br_ssl_server_init_full_ec(_sc_svr.get(), chain ? chain->getX509Certs() : nullptr, chain ? chain->getCount() : 0, + cert_issuer_key_type, sk ? sk->getEC() : nullptr); + br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) + { + DEBUG_BSSL("_connectSSLServerEC: Can't install serverX509check\n"); + return false; + } + if (!br_ssl_server_reset(_sc_svr.get())) + { + _freeSSL(); + DEBUG_BSSL("_connectSSLServerEC: Can't reset server ctx\n"); + return false; + } - return _wait_for_handshake(); + return _wait_for_handshake(); #else - (void) chain; - (void) cert_issuer_key_type; - (void) sk; - (void) client_CA_ta; - DEBUG_BSSL("_connectSSLServerEC: Attempting to use EC cert in minimal cipher mode (no EC)\n"); - return false; + (void) chain; + (void) cert_issuer_key_type; + (void) sk; + (void) client_CA_ta; + DEBUG_BSSL("_connectSSLServerEC: Attempting to use EC cert in minimal cipher mode (no EC)\n"); + return false; #endif } // Returns an error ID and possibly a string (if dest != null) of the last // BearSSL reported error. -int WiFiClientSecure::getLastSSLError(char *dest, size_t len) { - int err = 0; - const char *t = PSTR("OK"); - if (_sc || _sc_svr) { - err = br_ssl_engine_last_error(_eng); - } - if (_oom_err) { - err = -1000; - } - switch (err) { +int WiFiClientSecure::getLastSSLError(char *dest, size_t len) +{ + int err = 0; + const char *t = PSTR("OK"); + if (_sc || _sc_svr) + { + err = br_ssl_engine_last_error(_eng); + } + if (_oom_err) + { + err = -1000; + } + switch (err) + { case -1000: t = PSTR("Unable to allocate memory for SSL structures and buffers."); break; case BR_ERR_BAD_PARAM: t = PSTR("Caller-provided parameter is incorrect."); break; case BR_ERR_BAD_STATE: t = PSTR("Operation requested by the caller cannot be applied with the current context state (e.g. reading data while outgoing data is waiting to be sent)."); break; @@ -1187,49 +1362,57 @@ int WiFiClientSecure::getLastSSLError(char *dest, size_t len) { case BR_ERR_X509_WEAK_PUBLIC_KEY: t = PSTR("Public key found in certificate is too small."); break; case BR_ERR_X509_NOT_TRUSTED: t = PSTR("Chain could not be linked to a trust anchor."); break; default: t = PSTR("Unknown error code."); break; - } - if (dest) { - strncpy_P(dest, t, len); - dest[len - 1] = 0; - } - return err; + } + if (dest) + { + strncpy_P(dest, t, len); + dest[len - 1] = 0; + } + return err; } -bool WiFiClientSecure::probeMaxFragmentLength(const char* name, uint16_t port, uint16_t len) { - IPAddress remote_addr; - if (!WiFi.hostByName(name, remote_addr)) { - DEBUG_BSSL("probeMaxFragmentLength: Can't resolve host\n"); - return false; - } - return WiFiClientSecure::probeMaxFragmentLength(remote_addr, port, len); +bool WiFiClientSecure::probeMaxFragmentLength(const char* name, uint16_t port, uint16_t len) +{ + IPAddress remote_addr; + if (!WiFi.hostByName(name, remote_addr)) + { + DEBUG_BSSL("probeMaxFragmentLength: Can't resolve host\n"); + return false; + } + return WiFiClientSecure::probeMaxFragmentLength(remote_addr, port, len); } -bool WiFiClientSecure::probeMaxFragmentLength(const String& host, uint16_t port, uint16_t len) { - return WiFiClientSecure::probeMaxFragmentLength(host.c_str(), port, len); +bool WiFiClientSecure::probeMaxFragmentLength(const String& host, uint16_t port, uint16_t len) +{ + return WiFiClientSecure::probeMaxFragmentLength(host.c_str(), port, len); } // Helper function which aborts a TLS handshake by sending TLS // ClientAbort and ClientClose messages. -static bool _SendAbort(WiFiClient& probe, bool supportsLen) { - // If we're still connected, send the appropriate notice that - // we're aborting the handshake per RFCs. - static const uint8_t clientAbort_P[] PROGMEM = { - 0x15 /*alert*/, 0x03, 0x03 /*TLS 1.2*/, 0x00, 0x02, - 1, 90 /* warning: user_cancelled */ - }; - static const uint8_t clientClose_P[] PROGMEM = { - 0x15 /*alert*/, 0x03, 0x03 /*TLS 1.2*/, 0x00, 0x02, - 1, 0 /* warning: close_notify */ - }; - if (probe.connected()) { - uint8_t msg[sizeof(clientAbort_P)]; - memcpy_P(msg, clientAbort_P, sizeof(clientAbort_P)); - probe.write(msg, sizeof(clientAbort_P)); - memcpy_P(msg, clientClose_P, sizeof(clientClose_P)); - probe.write(msg, sizeof(clientClose_P)); - } - return supportsLen; +static bool _SendAbort(WiFiClient& probe, bool supportsLen) +{ + // If we're still connected, send the appropriate notice that + // we're aborting the handshake per RFCs. + static const uint8_t clientAbort_P[] PROGMEM = + { + 0x15 /*alert*/, 0x03, 0x03 /*TLS 1.2*/, 0x00, 0x02, + 1, 90 /* warning: user_cancelled */ + }; + static const uint8_t clientClose_P[] PROGMEM = + { + 0x15 /*alert*/, 0x03, 0x03 /*TLS 1.2*/, 0x00, 0x02, + 1, 0 /* warning: close_notify */ + }; + if (probe.connected()) + { + uint8_t msg[sizeof(clientAbort_P)]; + memcpy_P(msg, clientAbort_P, sizeof(clientAbort_P)); + probe.write(msg, sizeof(clientAbort_P)); + memcpy_P(msg, clientClose_P, sizeof(clientClose_P)); + probe.write(msg, sizeof(clientClose_P)); + } + return supportsLen; } // Checks for support of Maximum Frame Length Negotiation at the given @@ -1241,273 +1424,310 @@ static bool _SendAbort(WiFiClient& probe, bool supportsLen) { // TODO - Check the type of returned extensions and that the MFL is the exact // same one we sent. Not critical as only horribly broken servers would // return changed or add their own extensions. -bool WiFiClientSecure::probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len) { - // Hardcoded TLS 1.2 packets used throughout - static const uint8_t clientHelloHead_P[] PROGMEM = { - 0x16, 0x03, 0x03, 0x00, 0, // TLS header, change last 2 bytes to len - 0x01, 0x00, 0x00, 0, // Last 3 bytes == length - 0x03, 0x03, // Proto version TLS 1.2 - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Random (gmtime + rand[28]) - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x00, // Session ID - }; - // Followed by our cipher-suite, generated on-the-fly - // 0x00, 0x02, // cipher suite len - // 0xc0, 0x13, // BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - static const uint8_t clientHelloTail_P[] PROGMEM = { - 0x01, 0x00, // No compression - 0x00, 26 + 14 + 6 + 5, // Extension length - 0x00, 0x0d, 0x00, 0x16, 0x00, 0x14, 0x04, 0x03, 0x03, 0x03, 0x05, 0x03, - 0x06, 0x03, 0x02, 0x03, 0x04, 0x01, 0x03, 0x01, 0x05, 0x01, 0x06, - 0x01, 0x02, 0x01, // Supported signature algorithms - 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, - 0x00, 0x1d, // Supported groups - 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, // Supported EC formats - 0x00, 0x01, // Max Frag Len - 0x00, 0x01, // len of MaxFragLen - }; - // Followed by a 1-byte MFLN size requesst - // 0x04 // 2^12 = 4K - uint8_t mfl; - - switch (len) { +bool WiFiClientSecure::probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len) +{ + // Hardcoded TLS 1.2 packets used throughout + static const uint8_t clientHelloHead_P[] PROGMEM = + { + 0x16, 0x03, 0x03, 0x00, 0, // TLS header, change last 2 bytes to len + 0x01, 0x00, 0x00, 0, // Last 3 bytes == length + 0x03, 0x03, // Proto version TLS 1.2 + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Random (gmtime + rand[28]) + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x00, // Session ID + }; + // Followed by our cipher-suite, generated on-the-fly + // 0x00, 0x02, // cipher suite len + // 0xc0, 0x13, // BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + static const uint8_t clientHelloTail_P[] PROGMEM = + { + 0x01, 0x00, // No compression + 0x00, 26 + 14 + 6 + 5, // Extension length + 0x00, 0x0d, 0x00, 0x16, 0x00, 0x14, 0x04, 0x03, 0x03, 0x03, 0x05, 0x03, + 0x06, 0x03, 0x02, 0x03, 0x04, 0x01, 0x03, 0x01, 0x05, 0x01, 0x06, + 0x01, 0x02, 0x01, // Supported signature algorithms + 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, + 0x00, 0x1d, // Supported groups + 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, // Supported EC formats + 0x00, 0x01, // Max Frag Len + 0x00, 0x01, // len of MaxFragLen + }; + // Followed by a 1-byte MFLN size requesst + // 0x04 // 2^12 = 4K + uint8_t mfl; + + switch (len) + { case 512: mfl = 1; break; case 1024: mfl = 2; break; case 2048: mfl = 3; break; case 4096: mfl = 4; break; default: return false; // Invalid size - } - int ttlLen = sizeof(clientHelloHead_P) + (2 + sizeof(suites_P)) + (sizeof(clientHelloTail_P) + 1); - uint8_t *clientHello = new uint8_t[ttlLen]; - if (!clientHello) { - DEBUG_BSSL("probeMaxFragmentLength: OOM\n"); - return false; - } - memcpy_P(clientHello, clientHelloHead_P, sizeof(clientHelloHead_P)); - clientHello[sizeof(clientHelloHead_P) + 0] = sizeof(suites_P) >> 8; // MSB byte len - clientHello[sizeof(clientHelloHead_P) + 1] = sizeof(suites_P) & 0xff; // LSB byte len - for (size_t i = 0; i < sizeof(suites_P) / sizeof(suites_P[0]); i++) { - uint16_t flip = pgm_read_word(&suites_P[i]); - // Swap to network byte order - flip = ((flip >> 8) & 0xff) | ((flip & 0xff) << 8); - memcpy(clientHello + sizeof(clientHelloHead_P) + 2 + 2 * i, &flip, 2); - } - memcpy_P(clientHello + sizeof(clientHelloHead_P) + 2 + sizeof(suites_P), clientHelloTail_P, sizeof(clientHelloTail_P)); - clientHello[sizeof(clientHelloHead_P) + 2 + sizeof(suites_P) + sizeof(clientHelloTail_P)] = mfl; - - // Fix up TLS fragment length - clientHello[3] = (ttlLen - 5) >> 8; - clientHello[4] = (ttlLen - 5) & 0xff; - // Fix up ClientHello message length - clientHello[7] = (ttlLen - 5 - 4) >> 8; - clientHello[8] = (ttlLen - 5 - 4) & 0xff; - - WiFiClient probe; - probe.connect(ip, port); - if (!probe.connected()) { - delete[] clientHello; - DEBUG_BSSL("probeMaxFragmentLength: Can't connect\n"); - return false; - } + } + int ttlLen = sizeof(clientHelloHead_P) + (2 + sizeof(suites_P)) + (sizeof(clientHelloTail_P) + 1); + uint8_t *clientHello = new uint8_t[ttlLen]; + if (!clientHello) + { + DEBUG_BSSL("probeMaxFragmentLength: OOM\n"); + return false; + } + memcpy_P(clientHello, clientHelloHead_P, sizeof(clientHelloHead_P)); + clientHello[sizeof(clientHelloHead_P) + 0] = sizeof(suites_P) >> 8; // MSB byte len + clientHello[sizeof(clientHelloHead_P) + 1] = sizeof(suites_P) & 0xff; // LSB byte len + for (size_t i = 0; i < sizeof(suites_P) / sizeof(suites_P[0]); i++) + { + uint16_t flip = pgm_read_word(&suites_P[i]); + // Swap to network byte order + flip = ((flip >> 8) & 0xff) | ((flip & 0xff) << 8); + memcpy(clientHello + sizeof(clientHelloHead_P) + 2 + 2 * i, &flip, 2); + } + memcpy_P(clientHello + sizeof(clientHelloHead_P) + 2 + sizeof(suites_P), clientHelloTail_P, sizeof(clientHelloTail_P)); + clientHello[sizeof(clientHelloHead_P) + 2 + sizeof(suites_P) + sizeof(clientHelloTail_P)] = mfl; + + // Fix up TLS fragment length + clientHello[3] = (ttlLen - 5) >> 8; + clientHello[4] = (ttlLen - 5) & 0xff; + // Fix up ClientHello message length + clientHello[7] = (ttlLen - 5 - 4) >> 8; + clientHello[8] = (ttlLen - 5 - 4) & 0xff; + + WiFiClient probe; + probe.connect(ip, port); + if (!probe.connected()) + { + delete[] clientHello; + DEBUG_BSSL("probeMaxFragmentLength: Can't connect\n"); + return false; + } - int ret = probe.write(clientHello, ttlLen); - delete[] clientHello; // We're done w/the hello message - if (!probe.connected() || (ret != ttlLen)) { - DEBUG_BSSL("probeMaxFragmentLength: Protocol error\n"); - return false; - } - - bool supportsLen = false; - uint8_t fragResp[5]; - int fragLen; - uint8_t hand[4]; - int handLen; - uint8_t protoVer[2]; - uint8_t rand[32]; - uint8_t sessionLen; - uint8_t cipher[2]; - uint8_t comp; - uint8_t extBytes[2]; - uint16_t extLen; - - ret = probe.readBytes(fragResp, 5); - if (!probe.connected() || (ret != 5) || (fragResp[0] != 0x16) || (fragResp[1] != 0x03) || (fragResp[2] != 0x03)) { - // Short read, not a HANDSHAKE or not TLS 1.2, so it's not supported - return _SendAbort(probe, supportsLen); - } - fragLen = (fragResp[3] << 8) | fragResp[4]; - if (fragLen < 4 + 2 + 32 + 1 + 2 + 1) { - // Too short to have an extension - return _SendAbort(probe, supportsLen); - } + int ret = probe.write(clientHello, ttlLen); + delete[] clientHello; // We're done w/the hello message + if (!probe.connected() || (ret != ttlLen)) + { + DEBUG_BSSL("probeMaxFragmentLength: Protocol error\n"); + return false; + } - ret = probe.readBytes(hand, 4); - fragLen -= ret; - if ((ret != 4) || (hand[0] != 2)) { - // Short read or not server_hello - return _SendAbort(probe, supportsLen); - } - handLen = (hand[1] << 16) | (hand[2] << 8) | hand[3]; - if (handLen != fragLen) { - // Got some weird mismatch, this is invalid - return _SendAbort(probe, supportsLen); - } + bool supportsLen = false; + uint8_t fragResp[5]; + int fragLen; + uint8_t hand[4]; + int handLen; + uint8_t protoVer[2]; + uint8_t rand[32]; + uint8_t sessionLen; + uint8_t cipher[2]; + uint8_t comp; + uint8_t extBytes[2]; + uint16_t extLen; + + ret = probe.readBytes(fragResp, 5); + if (!probe.connected() || (ret != 5) || (fragResp[0] != 0x16) || (fragResp[1] != 0x03) || (fragResp[2] != 0x03)) + { + // Short read, not a HANDSHAKE or not TLS 1.2, so it's not supported + return _SendAbort(probe, supportsLen); + } + fragLen = (fragResp[3] << 8) | fragResp[4]; + if (fragLen < 4 + 2 + 32 + 1 + 2 + 1) + { + // Too short to have an extension + return _SendAbort(probe, supportsLen); + } - ret = probe.readBytes(protoVer, 2); - handLen -= ret; - if ((ret != 2) || (protoVer[0] != 0x03) || (protoVer[1] != 0x03)) { - // Short read or not tls 1.2, so can't do MFLN - return _SendAbort(probe, supportsLen); - } + ret = probe.readBytes(hand, 4); + fragLen -= ret; + if ((ret != 4) || (hand[0] != 2)) + { + // Short read or not server_hello + return _SendAbort(probe, supportsLen); + } + handLen = (hand[1] << 16) | (hand[2] << 8) | hand[3]; + if (handLen != fragLen) + { + // Got some weird mismatch, this is invalid + return _SendAbort(probe, supportsLen); + } - ret = probe.readBytes(rand, 32); - handLen -= ret; - if (ret != 32) { - // short read of random data - return _SendAbort(probe, supportsLen); - } + ret = probe.readBytes(protoVer, 2); + handLen -= ret; + if ((ret != 2) || (protoVer[0] != 0x03) || (protoVer[1] != 0x03)) + { + // Short read or not tls 1.2, so can't do MFLN + return _SendAbort(probe, supportsLen); + } - ret = probe.readBytes(&sessionLen, 1); - handLen -= ret; - if ((ret != 1) || (sessionLen > 32)) { - // short read of session len or invalid size - return _SendAbort(probe, supportsLen); - } - if (sessionLen) { - ret = probe.readBytes(rand, sessionLen); + ret = probe.readBytes(rand, 32); handLen -= ret; - if (ret != sessionLen) { - // short session id read - return _SendAbort(probe, supportsLen); + if (ret != 32) + { + // short read of random data + return _SendAbort(probe, supportsLen); } - } - ret = probe.readBytes(cipher, 2); - handLen -= ret; - if (ret != 2) { - // Short read...we don't check the cipher here - return _SendAbort(probe, supportsLen); - } + ret = probe.readBytes(&sessionLen, 1); + handLen -= ret; + if ((ret != 1) || (sessionLen > 32)) + { + // short read of session len or invalid size + return _SendAbort(probe, supportsLen); + } + if (sessionLen) + { + ret = probe.readBytes(rand, sessionLen); + handLen -= ret; + if (ret != sessionLen) + { + // short session id read + return _SendAbort(probe, supportsLen); + } + } - ret = probe.readBytes(&comp, 1); - handLen -= ret; - if ((ret != 1) || comp != 0) { - // short read or invalid compression - return _SendAbort(probe, supportsLen); - } + ret = probe.readBytes(cipher, 2); + handLen -= ret; + if (ret != 2) + { + // Short read...we don't check the cipher here + return _SendAbort(probe, supportsLen); + } + + ret = probe.readBytes(&comp, 1); + handLen -= ret; + if ((ret != 1) || comp != 0) + { + // short read or invalid compression + return _SendAbort(probe, supportsLen); + } - ret = probe.readBytes(extBytes, 2); - handLen -= ret; - extLen = extBytes[1] || (extBytes[0]<<8); - if ((extLen == 0) || (ret != 2)) { + ret = probe.readBytes(extBytes, 2); + handLen -= ret; + extLen = extBytes[1] || (extBytes[0] << 8); + if ((extLen == 0) || (ret != 2)) + { + return _SendAbort(probe, supportsLen); + } + + while (handLen > 0) + { + // Parse each extension and look for MFLN + uint8_t typeBytes[2]; + ret = probe.readBytes(typeBytes, 2); + handLen -= 2; + if ((ret != 2) || (handLen <= 0)) + { + return _SendAbort(probe, supportsLen); + } + uint8_t lenBytes[2]; + ret = probe.readBytes(lenBytes, 2); + handLen -= 2; + uint16_t extLen = lenBytes[1] | (lenBytes[0] << 8); + if ((ret != 2) || (handLen <= 0) || (extLen > 32) || (extLen > handLen)) + { + return _SendAbort(probe, supportsLen); + } + if ((typeBytes[0] == 0x00) && (typeBytes[1] == 0x01)) // MFLN extension! + { + // If present and 1-byte in length, it's supported + return _SendAbort(probe, extLen == 1 ? true : false); + } + // Skip the extension, move to next one + uint8_t junk[32]; + ret = probe.readBytes(junk, extLen); + handLen -= extLen; + if (ret != extLen) + { + return _SendAbort(probe, supportsLen); + } + } return _SendAbort(probe, supportsLen); - } - - while (handLen > 0) { - // Parse each extension and look for MFLN - uint8_t typeBytes[2]; - ret = probe.readBytes(typeBytes, 2); - handLen -= 2; - if ((ret != 2) || (handLen <= 0) ) { - return _SendAbort(probe, supportsLen); - } - uint8_t lenBytes[2]; - ret = probe.readBytes(lenBytes, 2); - handLen -= 2; - uint16_t extLen = lenBytes[1] | (lenBytes[0]<<8); - if ((ret != 2) || (handLen <= 0) || (extLen > 32) || (extLen > handLen) ) { - return _SendAbort(probe, supportsLen); - } - if ((typeBytes[0]==0x00) && (typeBytes[1]==0x01)) { // MFLN extension! - // If present and 1-byte in length, it's supported - return _SendAbort(probe, extLen==1 ? true : false); - } - // Skip the extension, move to next one - uint8_t junk[32]; - ret = probe.readBytes(junk, extLen); - handLen -= extLen; - if (ret != extLen) { - return _SendAbort(probe, supportsLen); - } - } - return _SendAbort(probe, supportsLen); } // AXTLS compatibility interfaces -bool WiFiClientSecure::setCACert(const uint8_t* pk, size_t size) { - _axtls_ta = nullptr; - _axtls_ta = std::shared_ptr(new X509List(pk, size)); - _ta = _axtls_ta.get(); - return _ta ? true : false; +bool WiFiClientSecure::setCACert(const uint8_t* pk, size_t size) +{ + _axtls_ta = nullptr; + _axtls_ta = std::shared_ptr(new X509List(pk, size)); + _ta = _axtls_ta.get(); + return _ta ? true : false; } -bool WiFiClientSecure::setCertificate(const uint8_t* pk, size_t size) { - _axtls_chain = nullptr; - _axtls_chain = std::shared_ptr(new X509List(pk, size)); - _chain = _axtls_chain.get(); - return _chain ? true : false; +bool WiFiClientSecure::setCertificate(const uint8_t* pk, size_t size) +{ + _axtls_chain = nullptr; + _axtls_chain = std::shared_ptr(new X509List(pk, size)); + _chain = _axtls_chain.get(); + return _chain ? true : false; } -bool WiFiClientSecure::setPrivateKey(const uint8_t* pk, size_t size) { - _axtls_sk = nullptr; - _axtls_sk = std::shared_ptr(new PrivateKey(pk, size)); - _sk = _axtls_sk.get(); - return _sk ? true : false; +bool WiFiClientSecure::setPrivateKey(const uint8_t* pk, size_t size) +{ + _axtls_sk = nullptr; + _axtls_sk = std::shared_ptr(new PrivateKey(pk, size)); + _sk = _axtls_sk.get(); + return _sk ? true : false; } -uint8_t *WiFiClientSecure::_streamLoad(Stream& stream, size_t size) { - uint8_t *dest = (uint8_t*)malloc(size); - if (!dest) { - return nullptr; - } - if (size != stream.readBytes(dest, size)) { - free(dest); - return nullptr; - } - return dest; +uint8_t *WiFiClientSecure::_streamLoad(Stream& stream, size_t size) +{ + uint8_t *dest = (uint8_t*)malloc(size); + if (!dest) + { + return nullptr; + } + if (size != stream.readBytes(dest, size)) + { + free(dest); + return nullptr; + } + return dest; } -bool WiFiClientSecure::loadCACert(Stream& stream, size_t size) { - uint8_t *dest = _streamLoad(stream, size); - bool ret = false; - if (dest) { +bool WiFiClientSecure::loadCACert(Stream& stream, size_t size) +{ + uint8_t *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) + { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - ret = setCACert(dest, size); + ret = setCACert(dest, size); #pragma GCC diagnostic pop - } - free(dest); - return ret; + } + free(dest); + return ret; } -bool WiFiClientSecure::loadCertificate(Stream& stream, size_t size) { - uint8_t *dest = _streamLoad(stream, size); - bool ret = false; - if (dest) { +bool WiFiClientSecure::loadCertificate(Stream& stream, size_t size) +{ + uint8_t *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) + { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - ret = setCertificate(dest, size); + ret = setCertificate(dest, size); #pragma GCC diagnostic pop - } - free(dest); - return ret; + } + free(dest); + return ret; } -bool WiFiClientSecure::loadPrivateKey(Stream& stream, size_t size) { - uint8_t *dest = _streamLoad(stream, size); - bool ret = false; - if (dest) { +bool WiFiClientSecure::loadPrivateKey(Stream& stream, size_t size) +{ + uint8_t *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) + { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - ret = setPrivateKey(dest, size); + ret = setPrivateKey(dest, size); #pragma GCC diagnostic pop - } - free(dest); - return ret; + } + free(dest); + return ret; } }; diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 54d47616ef..cc59d65367 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -1,23 +1,23 @@ /* - WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries - - Mostly compatible with Arduino WiFi shield library and standard + WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries + - Mostly compatible with Arduino WiFi shield library and standard WiFiClient/ServerSecure (except for certificate handling). - Copyright (c) 2018 Earle F. Philhower, III + Copyright (c) 2018 Earle F. Philhower, III - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -29,10 +29,12 @@ #include "BearSSLHelpers.h" #include "CertStoreBearSSL.h" -namespace BearSSL { +namespace BearSSL +{ -class WiFiClientSecure : public WiFiClient { - public: +class WiFiClientSecure : public WiFiClient +{ +public: WiFiClientSecure(); WiFiClientSecure(const WiFiClientSecure &rhs); ~WiFiClientSecure() override; @@ -44,11 +46,13 @@ class WiFiClientSecure : public WiFiClient { uint8_t connected() override; size_t write(const uint8_t *buf, size_t size) override; size_t write_P(PGM_P buf, size_t size) override; - size_t write(const char *buf) { - return write((const uint8_t*)buf, strlen(buf)); + size_t write(const char *buf) + { + return write((const uint8_t*)buf, strlen(buf)); } - size_t write_P(const char *buf) { - return write_P((PGM_P)buf, strlen_P(buf)); + size_t write_P(const char *buf) + { + return write_P((PGM_P)buf, strlen_P(buf)); } size_t write(Stream& stream); // Note this is not virtual int read(uint8_t *buf, size_t size) override; @@ -58,44 +62,59 @@ class WiFiClientSecure : public WiFiClient { size_t peekBytes(uint8_t *buffer, size_t length) override; bool flush(unsigned int maxWaitMs); bool stop(unsigned int maxWaitMs); - void flush() override { (void)flush(0); } - void stop() override { (void)stop(0); } + void flush() override + { + (void)flush(0); + } + void stop() override + { + (void)stop(0); + } // Allow sessions to be saved/restored automatically to a memory area - void setSession(Session *session) { _session = session; } + void setSession(Session *session) + { + _session = session; + } // Don't validate the chain, just accept whatever is given. VERY INSECURE! - void setInsecure() { - _clearAuthenticationSettings(); - _use_insecure = true; + void setInsecure() + { + _clearAuthenticationSettings(); + _use_insecure = true; } // Assume a given public key, don't validate or use cert info at all - void setKnownKey(const PublicKey *pk, unsigned usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN) { - _clearAuthenticationSettings(); - _knownkey = pk; - _knownkey_usages = usages; + void setKnownKey(const PublicKey *pk, unsigned usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN) + { + _clearAuthenticationSettings(); + _knownkey = pk; + _knownkey_usages = usages; } // Only check SHA1 fingerprint of certificate - bool setFingerprint(const uint8_t fingerprint[20]) { - _clearAuthenticationSettings(); - _use_fingerprint = true; - memcpy_P(_fingerprint, fingerprint, 20); - return true; + bool setFingerprint(const uint8_t fingerprint[20]) + { + _clearAuthenticationSettings(); + _use_fingerprint = true; + memcpy_P(_fingerprint, fingerprint, 20); + return true; } bool setFingerprint(const char *fpStr); // Accept any certificate that's self-signed - void allowSelfSignedCerts() { - _clearAuthenticationSettings(); - _use_self_signed = true; + void allowSelfSignedCerts() + { + _clearAuthenticationSettings(); + _use_self_signed = true; } // Install certificates of trusted CAs or specific site - void setTrustAnchors(const X509List *ta) { - _clearAuthenticationSettings(); - _ta = ta; + void setTrustAnchors(const X509List *ta) + { + _clearAuthenticationSettings(); + _ta = ta; } // In cases when NTP is not used, app must set a time manually to check cert validity - void setX509Time(time_t now) { - _now = now; + void setX509Time(time_t now) + { + _now = now; } // Install a client certificate for this connection, in case the server requires it (i.e. MQTT) void setClientRSACert(const X509List *cert, const PrivateKey *sk); @@ -106,16 +125,18 @@ class WiFiClientSecure : public WiFiClient { void setBufferSizes(int recv, int xmit); // Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection) - int getMFLNStatus() { - return connected() && br_ssl_engine_get_mfln_negotiated(_eng); + int getMFLNStatus() + { + return connected() && br_ssl_engine_get_mfln_negotiated(_eng); } // Return an error code and possibly a text string in a passed-in buffer with last SSL failure int getLastSSLError(char *dest = NULL, size_t len = 0); // Attach a preconfigured certificate store - void setCertStore(CertStore *certStore) { - _certStore = certStore; + void setCertStore(CertStore *certStore) + { + _certStore = certStore; } // Select specific ciphers (i.e. optimize for speed over security) @@ -132,7 +153,7 @@ class WiFiClientSecure : public WiFiClient { //////////////////////////////////////////////////// // AxTLS API deprecated warnings to help upgrading - #define AXTLS_DEPRECATED \ +#define AXTLS_DEPRECATED \ __attribute__((deprecated( \ "This is deprecated AxTLS API, " \ "check https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/src/WiFiClientSecure.h#L25-L99"))) @@ -148,57 +169,66 @@ class WiFiClientSecure : public WiFiClient { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - bool setCACert_P(PGM_VOID_P pk, size_t size) AXTLS_DEPRECATED { - return setCACert((const uint8_t *)pk, size); + bool setCACert_P(PGM_VOID_P pk, size_t size) AXTLS_DEPRECATED + { + return setCACert((const uint8_t *)pk, size); } - bool setCertificate_P(PGM_VOID_P pk, size_t size) AXTLS_DEPRECATED { - return setCertificate((const uint8_t *)pk, size); + bool setCertificate_P(PGM_VOID_P pk, size_t size) AXTLS_DEPRECATED + { + return setCertificate((const uint8_t *)pk, size); } - bool setPrivateKey_P(PGM_VOID_P pk, size_t size) AXTLS_DEPRECATED { - return setPrivateKey((const uint8_t *)pk, size); + bool setPrivateKey_P(PGM_VOID_P pk, size_t size) AXTLS_DEPRECATED + { + return setPrivateKey((const uint8_t *)pk, size); } #pragma GCC diagnostic pop template - bool loadCertificate(TFile& file) { - return loadCertificate(file, file.size()); + bool loadCertificate(TFile& file) + { + return loadCertificate(file, file.size()); } template - bool loadPrivateKey(TFile& file) { - return loadPrivateKey(file, file.size()); + bool loadPrivateKey(TFile& file) + { + return loadPrivateKey(file, file.size()); } template - bool loadCACert(TFile& file) { - return loadCACert(file, file.size()); + bool loadCACert(TFile& file) + { + return loadCACert(file, file.size()); } - bool verify(const char* fingerprint, const char* domain_name) AXTLS_DEPRECATED { - (void)fingerprint; - (void)domain_name; - return connected(); + bool verify(const char* fingerprint, const char* domain_name) AXTLS_DEPRECATED + { + (void)fingerprint; + (void)domain_name; + return connected(); } - bool verifyCertChain(const char* domain_name) AXTLS_DEPRECATED { - (void)domain_name; - return connected(); + bool verifyCertChain(const char* domain_name) AXTLS_DEPRECATED + { + (void)domain_name; + return connected(); } // AxTLS API deprecated section end ///////////////////////////////////// - private: +private: void _clear(); void _clearAuthenticationSettings(); // Only one of the following two should ever be != nullptr! std::shared_ptr _sc; std::shared_ptr _sc_svr; - inline bool ctx_present() { - return (_sc != nullptr) || (_sc_svr != nullptr); + inline bool ctx_present() + { + return (_sc != nullptr) || (_sc_svr != nullptr); } br_ssl_engine_context *_eng; // &_sc->eng, to allow for client or server contexts std::shared_ptr _x509_minimal; @@ -256,9 +286,9 @@ class WiFiClientSecure : public WiFiClient { // Methods for handling server.available() call which returns a client connection. friend class WiFiServerSecure; // Server needs to access these constructors WiFiClientSecure(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type, - const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta); + const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta); WiFiClientSecure(ClientContext* client, const X509List *chain, const PrivateKey *sk, - int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta); + int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta); // RSA keyed server bool _connectSSLServerRSA(const X509List *chain, const PrivateKey *sk, const X509List *client_CA_ta); diff --git a/libraries/ESP8266WiFi/src/WiFiServer.cpp b/libraries/ESP8266WiFi/src/WiFiServer.cpp index d9d47657df..ae634b20d1 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.cpp +++ b/libraries/ESP8266WiFi/src/WiFiServer.cpp @@ -1,31 +1,31 @@ /* - WiFiServer.cpp - TCP/IP server for esp8266, mostly compatible + WiFiServer.cpp - TCP/IP server for esp8266, mostly compatible with Arduino WiFi shield library - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LWIP_INTERNAL extern "C" { - #include "osapi.h" - #include "ets_sys.h" +#include "osapi.h" +#include "ets_sys.h" } #include "debug.h" @@ -38,47 +38,53 @@ extern "C" { #include WiFiServer::WiFiServer(const IPAddress& addr, uint16_t port) -: _port(port) -, _addr(addr) -, _pcb(nullptr) -, _unclaimed(nullptr) -, _discarded(nullptr) + : _port(port) + , _addr(addr) + , _pcb(nullptr) + , _unclaimed(nullptr) + , _discarded(nullptr) { } WiFiServer::WiFiServer(uint16_t port) -: _port(port) -, _addr(IP_ANY_TYPE) -, _pcb(nullptr) -, _unclaimed(nullptr) -, _discarded(nullptr) + : _port(port) + , _addr(IP_ANY_TYPE) + , _pcb(nullptr) + , _unclaimed(nullptr) + , _discarded(nullptr) { } -void WiFiServer::begin() { - begin(_port); +void WiFiServer::begin() +{ + begin(_port); } -void WiFiServer::begin(uint16_t port) { +void WiFiServer::begin(uint16_t port) +{ close(); _port = port; err_t err; tcp_pcb* pcb = tcp_new(); if (!pcb) + { return; + } pcb->so_options |= SOF_REUSEADDR; // (IPAddress _addr) operator-converted to (const ip_addr_t*) err = tcp_bind(pcb, _addr, _port); - if (err != ERR_OK) { + if (err != ERR_OK) + { tcp_close(pcb); return; } tcp_pcb* listen_pcb = tcp_listen(pcb); - if (!listen_pcb) { + if (!listen_pcb) + { tcp_close(pcb); return; } @@ -87,11 +93,13 @@ void WiFiServer::begin(uint16_t port) { tcp_arg(listen_pcb, (void*) this); } -void WiFiServer::setNoDelay(bool nodelay) { - _noDelay = nodelay? _ndTrue: _ndFalse; +void WiFiServer::setNoDelay(bool nodelay) +{ + _noDelay = nodelay ? _ndTrue : _ndFalse; } -bool WiFiServer::getNoDelay() { +bool WiFiServer::getNoDelay() +{ switch (_noDelay) { case _ndFalse: return false; @@ -100,15 +108,20 @@ bool WiFiServer::getNoDelay() { } } -bool WiFiServer::hasClient() { +bool WiFiServer::hasClient() +{ if (_unclaimed) + { return true; + } return false; } -WiFiClient WiFiServer::available(byte* status) { +WiFiClient WiFiServer::available(byte* status) +{ (void) status; - if (_unclaimed) { + if (_unclaimed) + { WiFiClient result(_unclaimed); _unclaimed = _unclaimed->next(); result.setNoDelay(getNoDelay()); @@ -120,29 +133,37 @@ WiFiClient WiFiServer::available(byte* status) { return WiFiClient(); } -uint8_t WiFiServer::status() { +uint8_t WiFiServer::status() +{ if (!_pcb) + { return CLOSED; + } return _pcb->state; } -void WiFiServer::close() { - if (!_pcb) { - return; +void WiFiServer::close() +{ + if (!_pcb) + { + return; } tcp_close(_pcb); _pcb = nullptr; } -void WiFiServer::stop() { +void WiFiServer::stop() +{ close(); } -size_t WiFiServer::write(uint8_t b) { +size_t WiFiServer::write(uint8_t b) +{ return write(&b, 1); } -size_t WiFiServer::write(const uint8_t *buffer, size_t size) { +size_t WiFiServer::write(const uint8_t *buffer, size_t size) +{ // write to all clients // not implemented (void) buffer; @@ -151,17 +172,23 @@ size_t WiFiServer::write(const uint8_t *buffer, size_t size) { } template -T* slist_append_tail(T* head, T* item) { +T* slist_append_tail(T* head, T* item) +{ if (!head) + { return item; + } T* last = head; - while(last->next()) + while (last->next()) + { last = last->next(); + } last->next(item); return head; } -long WiFiServer::_accept(tcp_pcb* apcb, long err) { +long WiFiServer::_accept(tcp_pcb* apcb, long err) +{ (void) err; DEBUGV("WS:ac\r\n"); ClientContext* client = new ClientContext(apcb, &WiFiServer::_s_discard, this); @@ -170,16 +197,19 @@ long WiFiServer::_accept(tcp_pcb* apcb, long err) { return ERR_OK; } -void WiFiServer::_discard(ClientContext* client) { +void WiFiServer::_discard(ClientContext* client) +{ (void) client; // _discarded = slist_append_tail(_discarded, client); DEBUGV("WS:dis\r\n"); } -long WiFiServer::_s_accept(void *arg, tcp_pcb* newpcb, long err) { +long WiFiServer::_s_accept(void *arg, tcp_pcb* newpcb, long err) +{ return reinterpret_cast(arg)->_accept(newpcb, err); } -void WiFiServer::_s_discard(void* server, ClientContext* ctx) { +void WiFiServer::_s_discard(void* server, ClientContext* ctx) +{ reinterpret_cast(server)->_discard(ctx); } diff --git a/libraries/ESP8266WiFi/src/WiFiServer.h b/libraries/ESP8266WiFi/src/WiFiServer.h index 81f2e9ab44..3504332053 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.h +++ b/libraries/ESP8266WiFi/src/WiFiServer.h @@ -1,31 +1,31 @@ /* - WiFiServer.h - Library for Arduino Wifi shield. - Copyright (c) 2011-2014 Arduino LLC. All right reserved. + WiFiServer.h - Library for Arduino Wifi shield. + Copyright (c) 2011-2014 Arduino LLC. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified by Ivan Grokhotkov, December 2014 - esp8266 support + Modified by Ivan Grokhotkov, December 2014 - esp8266 support */ #ifndef wifiserver_h #define wifiserver_h extern "C" { - #include "include/wl_definitions.h" +#include "include/wl_definitions.h" - struct tcp_pcb; + struct tcp_pcb; } #include "Server.h" @@ -34,41 +34,42 @@ extern "C" { class ClientContext; class WiFiClient; -class WiFiServer : public Server { - // Secure server needs access to all the private entries here +class WiFiServer : public Server +{ + // Secure server needs access to all the private entries here protected: - uint16_t _port; - IPAddress _addr; - tcp_pcb* _pcb; + uint16_t _port; + IPAddress _addr; + tcp_pcb* _pcb; - ClientContext* _unclaimed; - ClientContext* _discarded; - enum { _ndDefault, _ndFalse, _ndTrue } _noDelay = _ndDefault; + ClientContext* _unclaimed; + ClientContext* _discarded; + enum { _ndDefault, _ndFalse, _ndTrue } _noDelay = _ndDefault; public: - WiFiServer(const IPAddress& addr, uint16_t port); - WiFiServer(uint16_t port); - virtual ~WiFiServer() {} - WiFiClient available(uint8_t* status = NULL); - bool hasClient(); - void begin(); - void begin(uint16_t port); - void setNoDelay(bool nodelay); - bool getNoDelay(); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - uint8_t status(); - void close(); - void stop(); - - using Print::write; + WiFiServer(const IPAddress& addr, uint16_t port); + WiFiServer(uint16_t port); + virtual ~WiFiServer() {} + WiFiClient available(uint8_t* status = NULL); + bool hasClient(); + void begin(); + void begin(uint16_t port); + void setNoDelay(bool nodelay); + bool getNoDelay(); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + uint8_t status(); + void close(); + void stop(); + + using Print::write; protected: - long _accept(tcp_pcb* newpcb, long err); - void _discard(ClientContext* client); + long _accept(tcp_pcb* newpcb, long err); + void _discard(ClientContext* client); - static long _s_accept(void *arg, tcp_pcb* newpcb, long err); - static void _s_discard(void* server, ClientContext* ctx); + static long _s_accept(void *arg, tcp_pcb* newpcb, long err); + static void _s_discard(void* server, ClientContext* ctx); }; #endif diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecure.h b/libraries/ESP8266WiFi/src/WiFiServerSecure.h index 5167df562d..baa7140112 100644 --- a/libraries/ESP8266WiFi/src/WiFiServerSecure.h +++ b/libraries/ESP8266WiFi/src/WiFiServerSecure.h @@ -1,20 +1,20 @@ /* - WiFiServerSecure.h - Library for Arduino ESP8266 - Copyright (c) 2017 Earle F. Philhower, III + WiFiServerSecure.h - Library for Arduino ESP8266 + Copyright (c) 2017 Earle F. Philhower, III - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecureAxTLS.cpp b/libraries/ESP8266WiFi/src/WiFiServerSecureAxTLS.cpp index bba0be564a..3b09b354cf 100644 --- a/libraries/ESP8266WiFi/src/WiFiServerSecureAxTLS.cpp +++ b/libraries/ESP8266WiFi/src/WiFiServerSecureAxTLS.cpp @@ -1,29 +1,29 @@ /* - WiFiServerSecure.cpp - SSL server for esp8266, mostly compatible + WiFiServerSecure.cpp - SSL server for esp8266, mostly compatible with Arduino WiFi shield library - Copyright (c) 2017 Earle F. Philhower, III + Copyright (c) 2017 Earle F. Philhower, III - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LWIP_INTERNAL extern "C" { - #include "osapi.h" - #include "ets_sys.h" +#include "osapi.h" +#include "ets_sys.h" } #include "debug.h" @@ -38,7 +38,8 @@ extern "C" { #include "WiFiServerSecureAxTLS.h" -namespace axTLS { +namespace axTLS +{ WiFiServerSecure::WiFiServerSecure(IPAddress addr, uint16_t port) : WiFiServer(addr, port) { @@ -69,7 +70,8 @@ void WiFiServerSecure::setServerKeyAndCert_P(const uint8_t *key, int keyLen, con WiFiClientSecure WiFiServerSecure::available(uint8_t* status) { (void) status; // Unused - if (_unclaimed) { + if (_unclaimed) + { WiFiClientSecure result(_unclaimed, usePMEM, rsakey, rsakeyLen, cert, certLen); _unclaimed = _unclaimed->next(); result.setNoDelay(_noDelay); diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecureAxTLS.h b/libraries/ESP8266WiFi/src/WiFiServerSecureAxTLS.h index b309eb89ed..9649e67cc9 100644 --- a/libraries/ESP8266WiFi/src/WiFiServerSecureAxTLS.h +++ b/libraries/ESP8266WiFi/src/WiFiServerSecureAxTLS.h @@ -1,20 +1,20 @@ /* - WiFiServerSecure.h - Library for Arduino ESP8266 - Copyright (c) 2017 Earle F. Philhower, III - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + WiFiServerSecure.h - Library for Arduino ESP8266 + Copyright (c) 2017 Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef wifiserversecure_h @@ -22,24 +22,26 @@ #include "WiFiServer.h" -namespace axTLS { +namespace axTLS +{ class WiFiClientSecure; -class WiFiServerSecure : public WiFiServer { +class WiFiServerSecure : public WiFiServer +{ public: - WiFiServerSecure(IPAddress addr, uint16_t port); - WiFiServerSecure(uint16_t port); - void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); - void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); - virtual ~WiFiServerSecure() {} - WiFiClientSecure available(uint8_t* status = NULL); + WiFiServerSecure(IPAddress addr, uint16_t port); + WiFiServerSecure(uint16_t port); + void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); + void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); + virtual ~WiFiServerSecure() {} + WiFiClientSecure available(uint8_t* status = NULL); private: - bool usePMEM = false; - const uint8_t *rsakey = nullptr; - int rsakeyLen = 0; - const uint8_t *cert = nullptr; - int certLen = 0; + bool usePMEM = false; + const uint8_t *rsakey = nullptr; + int rsakeyLen = 0; + const uint8_t *cert = nullptr; + int certLen = 0; }; }; diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp index 087c90b4e8..57a9043d8a 100644 --- a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp @@ -1,22 +1,22 @@ /* - WiFiServerBearSSL.cpp - SSL server for esp8266, mostly compatible + WiFiServerBearSSL.cpp - SSL server for esp8266, mostly compatible with Arduino WiFi shield library - Copyright (c) 2018 Earle F. Philhower, III + Copyright (c) 2018 Earle F. Philhower, III - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LWIP_INTERNAL @@ -37,83 +37,99 @@ extern "C" { #include #include "WiFiServerSecureBearSSL.h" -namespace BearSSL { +namespace BearSSL +{ // Only need to call the standard server constructor -WiFiServerSecure::WiFiServerSecure(IPAddress addr, uint16_t port) : WiFiServer(addr, port) { - stack_thunk_add_ref(); +WiFiServerSecure::WiFiServerSecure(IPAddress addr, uint16_t port) : WiFiServer(addr, port) +{ + stack_thunk_add_ref(); } // Only need to call the standard server constructor -WiFiServerSecure::WiFiServerSecure(uint16_t port) : WiFiServer(port) { - stack_thunk_add_ref(); +WiFiServerSecure::WiFiServerSecure(uint16_t port) : WiFiServer(port) +{ + stack_thunk_add_ref(); } -WiFiServerSecure::WiFiServerSecure(const WiFiServerSecure &rhs) : WiFiServer(rhs) { - *this = rhs; - stack_thunk_add_ref(); +WiFiServerSecure::WiFiServerSecure(const WiFiServerSecure &rhs) : WiFiServer(rhs) +{ + *this = rhs; + stack_thunk_add_ref(); } -WiFiServerSecure::~WiFiServerSecure() { - stack_thunk_del_ref(); - _axtls_chain = nullptr; - _axtls_sk = nullptr; +WiFiServerSecure::~WiFiServerSecure() +{ + stack_thunk_del_ref(); + _axtls_chain = nullptr; + _axtls_sk = nullptr; } // Specify a RSA-signed certificate and key for the server. Only copies the pointer, the // caller needs to preserve this chain and key for the life of the object. -void WiFiServerSecure::setRSACert(const X509List *chain, const PrivateKey *sk) { - _chain = chain; - _sk = sk; +void WiFiServerSecure::setRSACert(const X509List *chain, const PrivateKey *sk) +{ + _chain = chain; + _sk = sk; } // Specify a EC-signed certificate and key for the server. Only copies the pointer, the // caller needs to preserve this chain and key for the life of the object. -void WiFiServerSecure::setECCert(const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk) { - _chain = chain; - _cert_issuer_key_type = cert_issuer_key_type; - _sk = sk; +void WiFiServerSecure::setECCert(const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk) +{ + _chain = chain; + _cert_issuer_key_type = cert_issuer_key_type; + _sk = sk; } // Return a client if there's an available connection waiting. If one is returned, // then any validation (i.e. client cert checking) will have succeeded. -WiFiClientSecure WiFiServerSecure::available(uint8_t* status) { - (void) status; // Unused - if (_unclaimed) { - if (_sk && _sk->isRSA()) { - WiFiClientSecure result(_unclaimed, _chain, _sk, _iobuf_in_size, _iobuf_out_size, _client_CA_ta); - _unclaimed = _unclaimed->next(); - result.setNoDelay(_noDelay); - DEBUGV("WS:av\r\n"); - return result; - } else if (_sk && _sk->isEC()) { - WiFiClientSecure result(_unclaimed, _chain, _cert_issuer_key_type, _sk, _iobuf_in_size, _iobuf_out_size, _client_CA_ta); - _unclaimed = _unclaimed->next(); - result.setNoDelay(_noDelay); - DEBUGV("WS:av\r\n"); - return result; - } else { - // No key was defined, so we can't actually accept and attempt accept() and SSL handshake. - DEBUGV("WS:nokey\r\n"); +WiFiClientSecure WiFiServerSecure::available(uint8_t* status) +{ + (void) status; // Unused + if (_unclaimed) + { + if (_sk && _sk->isRSA()) + { + WiFiClientSecure result(_unclaimed, _chain, _sk, _iobuf_in_size, _iobuf_out_size, _client_CA_ta); + _unclaimed = _unclaimed->next(); + result.setNoDelay(_noDelay); + DEBUGV("WS:av\r\n"); + return result; + } + else if (_sk && _sk->isEC()) + { + WiFiClientSecure result(_unclaimed, _chain, _cert_issuer_key_type, _sk, _iobuf_in_size, _iobuf_out_size, _client_CA_ta); + _unclaimed = _unclaimed->next(); + result.setNoDelay(_noDelay); + DEBUGV("WS:av\r\n"); + return result; + } + else + { + // No key was defined, so we can't actually accept and attempt accept() and SSL handshake. + DEBUGV("WS:nokey\r\n"); + } } - } - // Something weird, return a no-op object - optimistic_yield(1000); - return WiFiClientSecure(); + // Something weird, return a no-op object + optimistic_yield(1000); + return WiFiClientSecure(); } -void WiFiServerSecure::setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) { - _axtls_chain = nullptr; - _axtls_sk = nullptr; - _axtls_chain = std::shared_ptr(new X509List(cert, certLen)); - _axtls_sk = std::shared_ptr(new PrivateKey(key, keyLen)); - setRSACert(_axtls_chain.get(), _axtls_sk.get()); +void WiFiServerSecure::setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) +{ + _axtls_chain = nullptr; + _axtls_sk = nullptr; + _axtls_chain = std::shared_ptr(new X509List(cert, certLen)); + _axtls_sk = std::shared_ptr(new PrivateKey(key, keyLen)); + setRSACert(_axtls_chain.get(), _axtls_sk.get()); } -void WiFiServerSecure::setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) { - setServerKeyAndCert(key, keyLen, cert, certLen); +void WiFiServerSecure::setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) +{ + setServerKeyAndCert(key, keyLen, cert, certLen); } diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h index d104749f24..0696dc4a99 100644 --- a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h @@ -1,20 +1,20 @@ /* - WiFiServerBearSSL.h - Library for Arduino ESP8266 - Copyright (c) 2018 Earle F. Philhower, III - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + WiFiServerBearSSL.h - Library for Arduino ESP8266 + Copyright (c) 2018 Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef wifiserverbearssl_h @@ -25,21 +25,24 @@ #include "BearSSLHelpers.h" #include -namespace BearSSL { +namespace BearSSL +{ class WiFiClientSecure; -class WiFiServerSecure : public WiFiServer { - public: +class WiFiServerSecure : public WiFiServer +{ +public: WiFiServerSecure(IPAddress addr, uint16_t port); WiFiServerSecure(uint16_t port); WiFiServerSecure(const WiFiServerSecure &rhs); virtual ~WiFiServerSecure(); // Override the default buffer sizes, if you know what you're doing... - void setBufferSizes(int recv, int xmit) { - _iobuf_in_size = recv; - _iobuf_out_size = xmit; + void setBufferSizes(int recv, int xmit) + { + _iobuf_in_size = recv; + _iobuf_out_size = xmit; } // Set the server's RSA key and x509 certificate (required, pick one). @@ -51,8 +54,9 @@ class WiFiServerSecure : public WiFiServer { // Require client certificates validated against the passed in x509 trust anchor // Caller needs to preserve the cert throughout the life of the server. - void setClientTrustAnchor(const X509List *client_CA_ta) { - _client_CA_ta = client_CA_ta; + void setClientTrustAnchor(const X509List *client_CA_ta) + { + _client_CA_ta = client_CA_ta; } // If awaiting connection available and authenticated (i.e. client cert), return it. @@ -62,7 +66,7 @@ class WiFiServerSecure : public WiFiServer { void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); - private: +private: const X509List *_chain = nullptr; unsigned _cert_issuer_key_type = 0; const PrivateKey *_sk = nullptr; diff --git a/libraries/ESP8266WiFi/src/WiFiUdp.cpp b/libraries/ESP8266WiFi/src/WiFiUdp.cpp index a0a5c1d4e4..028497902e 100644 --- a/libraries/ESP8266WiFi/src/WiFiUdp.cpp +++ b/libraries/ESP8266WiFi/src/WiFiUdp.cpp @@ -1,23 +1,23 @@ /* - WiFiUdp.cpp - UDP client/server for esp8266, mostly compatible + WiFiUdp.cpp - UDP client/server for esp8266, mostly compatible with Arduino WiFi shield library - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LWIP_INTERNAL @@ -25,9 +25,9 @@ extern "C" { - #include "include/wl_definitions.h" - #include "osapi.h" - #include "ets_sys.h" +#include "include/wl_definitions.h" +#include "osapi.h" +#include "ets_sys.h" } #include "debug.h" @@ -53,7 +53,9 @@ WiFiUDP::WiFiUDP(const WiFiUDP& other) { _ctx = other._ctx; if (_ctx) + { _ctx->ref(); + } WiFiUDP::_add(this); } @@ -61,7 +63,9 @@ WiFiUDP& WiFiUDP::operator=(const WiFiUDP& rhs) { _ctx = rhs._ctx; if (_ctx) + { _ctx->ref(); + } return *this; } @@ -69,13 +73,16 @@ WiFiUDP::~WiFiUDP() { WiFiUDP::_remove(this); if (_ctx) + { _ctx->unref(); + } } /* Start WiFiUDP socket, listening at local port */ uint8_t WiFiUDP::begin(uint16_t port) { - if (_ctx) { + if (_ctx) + { _ctx->unref(); _ctx = 0; } @@ -87,35 +94,41 @@ uint8_t WiFiUDP::begin(uint16_t port) uint8_t WiFiUDP::beginMulticast(IPAddress interfaceAddr, IPAddress multicast, uint16_t port) { - if (_ctx) { + if (_ctx) + { _ctx->unref(); _ctx = 0; } - if (igmp_joingroup(interfaceAddr, multicast)!= ERR_OK) { + if (igmp_joingroup(interfaceAddr, multicast) != ERR_OK) + { return 0; } _ctx = new UdpContext; _ctx->ref(); ip_addr_t addr = IPADDR4_INIT(INADDR_ANY); - if (!_ctx->listen(&addr, port)) { + if (!_ctx->listen(&addr, port)) + { return 0; } return 1; } -/* return number of bytes available in the current packet, - will return zero if parsePacket hasn't been called yet */ -int WiFiUDP::available() { +/* return number of bytes available in the current packet, + will return zero if parsePacket hasn't been called yet */ +int WiFiUDP::available() +{ int result = 0; - if (_ctx) { + if (_ctx) + { result = static_cast(_ctx->getSize()); } - if (!result) { + if (!result) + { // yielding here will not make more data "available", // but it will prevent the system from going into WDT reset optimistic_yield(1000); @@ -127,7 +140,8 @@ int WiFiUDP::available() { /* Release any resources being used by this WiFiUDP instance */ void WiFiUDP::stop() { - if (_ctx) { + if (_ctx) + { _ctx->disconnect(); _ctx->unref(); } @@ -146,7 +160,8 @@ int WiFiUDP::beginPacket(const char *host, uint16_t port) int WiFiUDP::beginPacket(IPAddress ip, uint16_t port) { - if (!_ctx) { + if (!_ctx) + { _ctx = new UdpContext; _ctx->ref(); } @@ -154,13 +169,15 @@ int WiFiUDP::beginPacket(IPAddress ip, uint16_t port) } int WiFiUDP::beginPacketMulticast(IPAddress multicastAddress, uint16_t port, - IPAddress interfaceAddress, int ttl) + IPAddress interfaceAddress, int ttl) { - if (!_ctx) { + if (!_ctx) + { _ctx = new UdpContext; _ctx->ref(); } - if (!_ctx->connect(multicastAddress, port)) { + if (!_ctx->connect(multicastAddress, port)) + { return 0; } _ctx->setMulticastInterface(interfaceAddress); @@ -171,7 +188,9 @@ int WiFiUDP::beginPacketMulticast(IPAddress multicastAddress, uint16_t port, int WiFiUDP::endPacket() { if (!_ctx) + { return 0; + } return (_ctx->send()) ? 1 : 0; } @@ -184,7 +203,9 @@ size_t WiFiUDP::write(uint8_t byte) size_t WiFiUDP::write(const uint8_t *buffer, size_t size) { if (!_ctx) + { return 0; + } return _ctx->append(reinterpret_cast(buffer), size); } @@ -192,9 +213,12 @@ size_t WiFiUDP::write(const uint8_t *buffer, size_t size) int WiFiUDP::parsePacket() { if (!_ctx) + { return 0; + } - if (!_ctx->next()) { + if (!_ctx->next()) + { optimistic_yield(100); return 0; } @@ -205,7 +229,9 @@ int WiFiUDP::parsePacket() int WiFiUDP::read() { if (!_ctx) + { return -1; + } return _ctx->read(); } @@ -213,7 +239,9 @@ int WiFiUDP::read() int WiFiUDP::read(unsigned char* buffer, size_t len) { if (!_ctx) + { return 0; + } return _ctx->read(reinterpret_cast(buffer), len); } @@ -221,7 +249,9 @@ int WiFiUDP::read(unsigned char* buffer, size_t len) int WiFiUDP::peek() { if (!_ctx) + { return -1; + } return _ctx->peek(); } @@ -234,7 +264,9 @@ void WiFiUDP::flush() IPAddress WiFiUDP::remoteIP() { if (!_ctx) + { return INADDR_ANY; + } return _ctx->getRemoteAddress(); } @@ -242,7 +274,9 @@ IPAddress WiFiUDP::remoteIP() uint16_t WiFiUDP::remotePort() { if (!_ctx) + { return 0; + } return _ctx->getRemotePort(); } @@ -250,7 +284,9 @@ uint16_t WiFiUDP::remotePort() IPAddress WiFiUDP::destinationIP() const { if (!_ctx) + { return INADDR_ANY; + } return _ctx->getDestAddress(); } @@ -258,22 +294,28 @@ IPAddress WiFiUDP::destinationIP() const uint16_t WiFiUDP::localPort() const { if (!_ctx) + { return 0; + } return _ctx->getLocalPort(); } void WiFiUDP::stopAll() { - for (WiFiUDP* it = _s_first; it; it = it->_next) { + for (WiFiUDP* it = _s_first; it; it = it->_next) + { DEBUGV("%s %p %p\n", __func__, it, _s_first); it->stop(); } } -void WiFiUDP::stopAllExcept(WiFiUDP * exC) { - for (WiFiUDP* it = _s_first; it; it = it->_next) { - if (it->_ctx != exC->_ctx) { +void WiFiUDP::stopAllExcept(WiFiUDP * exC) +{ + for (WiFiUDP* it = _s_first; it; it = it->_next) + { + if (it->_ctx != exC->_ctx) + { DEBUGV("%s %p %p\n", __func__, it, _s_first); it->stop(); } diff --git a/libraries/ESP8266WiFi/src/WiFiUdp.h b/libraries/ESP8266WiFi/src/WiFiUdp.h index a1cf42be10..2761729bea 100644 --- a/libraries/ESP8266WiFi/src/WiFiUdp.h +++ b/libraries/ESP8266WiFi/src/WiFiUdp.h @@ -1,22 +1,22 @@ /* - WiFiUdp.h - Library for Arduino Wifi shield. - Copyright (c) 2011-2014 Arduino LLC. All right reserved. + WiFiUdp.h - Library for Arduino Wifi shield. + Copyright (c) 2011-2014 Arduino LLC. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Modified by Ivan Grokhotkov, January 2015 - esp8266 support + Modified by Ivan Grokhotkov, January 2015 - esp8266 support */ #ifndef WIFIUDP_H @@ -29,83 +29,90 @@ class UdpContext; -class WiFiUDP : public UDP, public SList { +class WiFiUDP : public UDP, public SList +{ private: - UdpContext* _ctx; + UdpContext* _ctx; public: - WiFiUDP(); // Constructor - WiFiUDP(const WiFiUDP& other); - WiFiUDP& operator=(const WiFiUDP& rhs); - virtual ~WiFiUDP(); - - operator bool() const { return _ctx != 0; } - - // initialize, start listening on specified port. - // Returns 1 if successful, 0 if there are no sockets available to use - uint8_t begin(uint16_t port) override; - // Finish with the UDP connetion - void stop() override; - // join a multicast group and listen on the given port - uint8_t beginMulticast(IPAddress interfaceAddr, IPAddress multicast, uint16_t port); - - // Sending UDP packets - - // Start building up a packet to send to the remote host specific in ip and port - // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port - int beginPacket(IPAddress ip, uint16_t port) override; - // Start building up a packet to send to the remote host specific in host and port - // Returns 1 if successful, 0 if there was a problem resolving the hostname or port - int beginPacket(const char *host, uint16_t port) override; - // Start building up a packet to send to the multicast address - // multicastAddress - muticast address to send to - // interfaceAddress - the local IP address of the interface that should be used - // use WiFi.localIP() or WiFi.softAPIP() depending on the interface you need - // ttl - multicast packet TTL (default is 1) - // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port - virtual int beginPacketMulticast(IPAddress multicastAddress, - uint16_t port, - IPAddress interfaceAddress, - int ttl = 1); - // Finish off this packet and send it - // Returns 1 if the packet was sent successfully, 0 if there was an error - int endPacket() override; - // Write a single byte into the packet - size_t write(uint8_t) override; - // Write size bytes from buffer into the packet - size_t write(const uint8_t *buffer, size_t size) override; - - using Print::write; - - // Start processing the next available incoming packet - // Returns the size of the packet in bytes, or 0 if no packets are available - int parsePacket() override; - // Number of bytes remaining in the current packet - int available() override; - // Read a single byte from the current packet - int read() override; - // Read up to len bytes from the current packet and place them into buffer - // Returns the number of bytes read, or 0 if none are available - int read(unsigned char* buffer, size_t len) override; - // Read up to len characters from the current packet and place them into buffer - // Returns the number of characters read, or 0 if none are available - int read(char* buffer, size_t len) override { return read((unsigned char*)buffer, len); }; - // Return the next byte from the current packet without moving on to the next byte - int peek() override; - void flush() override; // Finish reading the current packet - - // Return the IP address of the host who sent the current incoming packet - IPAddress remoteIP() override; - // Return the port of the host who sent the current incoming packet - uint16_t remotePort() override; - // Return the destination address for incoming packets, - // useful to distinguish multicast and ordinary packets - IPAddress destinationIP() const; - // Return the local port for outgoing packets - uint16_t localPort() const; - - static void stopAll(); - static void stopAllExcept(WiFiUDP * exC); + WiFiUDP(); // Constructor + WiFiUDP(const WiFiUDP& other); + WiFiUDP& operator=(const WiFiUDP& rhs); + virtual ~WiFiUDP(); + + operator bool() const + { + return _ctx != 0; + } + + // initialize, start listening on specified port. + // Returns 1 if successful, 0 if there are no sockets available to use + uint8_t begin(uint16_t port) override; + // Finish with the UDP connetion + void stop() override; + // join a multicast group and listen on the given port + uint8_t beginMulticast(IPAddress interfaceAddr, IPAddress multicast, uint16_t port); + + // Sending UDP packets + + // Start building up a packet to send to the remote host specific in ip and port + // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port + int beginPacket(IPAddress ip, uint16_t port) override; + // Start building up a packet to send to the remote host specific in host and port + // Returns 1 if successful, 0 if there was a problem resolving the hostname or port + int beginPacket(const char *host, uint16_t port) override; + // Start building up a packet to send to the multicast address + // multicastAddress - muticast address to send to + // interfaceAddress - the local IP address of the interface that should be used + // use WiFi.localIP() or WiFi.softAPIP() depending on the interface you need + // ttl - multicast packet TTL (default is 1) + // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port + virtual int beginPacketMulticast(IPAddress multicastAddress, + uint16_t port, + IPAddress interfaceAddress, + int ttl = 1); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + int endPacket() override; + // Write a single byte into the packet + size_t write(uint8_t) override; + // Write size bytes from buffer into the packet + size_t write(const uint8_t *buffer, size_t size) override; + + using Print::write; + + // Start processing the next available incoming packet + // Returns the size of the packet in bytes, or 0 if no packets are available + int parsePacket() override; + // Number of bytes remaining in the current packet + int available() override; + // Read a single byte from the current packet + int read() override; + // Read up to len bytes from the current packet and place them into buffer + // Returns the number of bytes read, or 0 if none are available + int read(unsigned char* buffer, size_t len) override; + // Read up to len characters from the current packet and place them into buffer + // Returns the number of characters read, or 0 if none are available + int read(char* buffer, size_t len) override + { + return read((unsigned char*)buffer, len); + }; + // Return the next byte from the current packet without moving on to the next byte + int peek() override; + void flush() override; // Finish reading the current packet + + // Return the IP address of the host who sent the current incoming packet + IPAddress remoteIP() override; + // Return the port of the host who sent the current incoming packet + uint16_t remotePort() override; + // Return the destination address for incoming packets, + // useful to distinguish multicast and ordinary packets + IPAddress destinationIP() const; + // Return the local port for outgoing packets + uint16_t localPort() const; + + static void stopAll(); + static void stopAllExcept(WiFiUDP * exC); }; diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 5be690f247..48ea2175d9 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -1,23 +1,23 @@ /* - ClientContext.h - TCP connection handling on top of lwIP + ClientContext.h - TCP connection handling on top of lwIP - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef CLIENTCONTEXT_H #define CLIENTCONTEXT_H @@ -31,7 +31,7 @@ extern "C" void esp_schedule(); #include "DataSource.h" -bool getDefaultPrivateGlobalSyncValue (); +bool getDefaultPrivateGlobalSyncValue(); class ClientContext { @@ -53,7 +53,8 @@ class ClientContext err_t abort() { - if(_pcb) { + if (_pcb) + { DEBUGV(":abort\r\n"); tcp_arg(_pcb, NULL); tcp_sent(_pcb, NULL); @@ -69,7 +70,8 @@ class ClientContext err_t close() { err_t err = ERR_OK; - if(_pcb) { + if (_pcb) + { DEBUGV(":close\r\n"); tcp_arg(_pcb, NULL); tcp_sent(_pcb, NULL); @@ -77,7 +79,8 @@ class ClientContext tcp_err(_pcb, NULL); tcp_poll(_pcb, NULL, 0); err = tcp_close(_pcb); - if(err != ERR_OK) { + if (err != ERR_OK) + { DEBUGV(":tc err %d\r\n", (int) err); tcp_abort(_pcb); err = ERR_ABRT; @@ -111,10 +114,12 @@ class ClientContext void unref() { DEBUGV(":ur %d\r\n", _refcnt); - if(--_refcnt == 0) { + if (--_refcnt == 0) + { discard_received(); close(); - if(_discard_cb) { + if (_discard_cb) + { _discard_cb(_discard_cb_arg, this); } DEBUGV(":del\r\n"); @@ -125,7 +130,8 @@ class ClientContext int connect(CONST ip_addr_t* addr, uint16_t port) { err_t err = tcp_connect(_pcb, addr, port, &ClientContext::_s_connected); - if (err != ERR_OK) { + if (err != ERR_OK) + { return 0; } _connect_pending = 1; @@ -133,11 +139,13 @@ class ClientContext // This delay will be interrupted by esp_schedule in the connect callback delay(_timeout_ms); _connect_pending = 0; - if (!_pcb) { + if (!_pcb) + { DEBUGV(":cabrt\r\n"); return 0; } - if (state() != ESTABLISHED) { + if (state() != ESTABLISHED) + { DEBUGV(":ctmo\r\n"); abort(); return 0; @@ -147,24 +155,29 @@ class ClientContext size_t availableForWrite() const { - return _pcb? tcp_sndbuf(_pcb): 0; + return _pcb ? tcp_sndbuf(_pcb) : 0; } void setNoDelay(bool nodelay) { - if(!_pcb) { + if (!_pcb) + { return; } - if(nodelay) { + if (nodelay) + { tcp_nagle_disable(_pcb); - } else { + } + else + { tcp_nagle_enable(_pcb); } } bool getNoDelay() const { - if(!_pcb) { + if (!_pcb) + { return false; } return tcp_nagle_disabled(_pcb); @@ -182,7 +195,8 @@ class ClientContext const ip_addr_t* getRemoteAddress() const { - if(!_pcb) { + if (!_pcb) + { return 0; } @@ -191,7 +205,8 @@ class ClientContext uint16_t getRemotePort() const { - if(!_pcb) { + if (!_pcb) + { return 0; } @@ -200,7 +215,8 @@ class ClientContext const ip_addr_t* getLocalAddress() const { - if(!_pcb) { + if (!_pcb) + { return 0; } @@ -209,7 +225,8 @@ class ClientContext uint16_t getLocalPort() const { - if(!_pcb) { + if (!_pcb) + { return 0; } @@ -218,7 +235,8 @@ class ClientContext size_t getSize() const { - if(!_rx_buf) { + if (!_rx_buf) + { return 0; } @@ -227,7 +245,8 @@ class ClientContext char read() { - if(!_rx_buf) { + if (!_rx_buf) + { return 0; } @@ -238,7 +257,8 @@ class ClientContext size_t read(char* dst, size_t size) { - if(!_rx_buf) { + if (!_rx_buf) + { return 0; } @@ -247,7 +267,8 @@ class ClientContext DEBUGV(":rd %d, %d, %d\r\n", size, _rx_buf->tot_len, _rx_buf_offset); size_t size_read = 0; - while(size) { + while (size) + { size_t buf_size = _rx_buf->len - _rx_buf_offset; size_t copy_size = (size < buf_size) ? size : buf_size; DEBUGV(":rdi %d, %d\r\n", buf_size, copy_size); @@ -262,7 +283,8 @@ class ClientContext char peek() const { - if(!_rx_buf) { + if (!_rx_buf) + { return 0; } @@ -271,7 +293,8 @@ class ClientContext size_t peekBytes(char *dst, size_t size) const { - if(!_rx_buf) { + if (!_rx_buf) + { return 0; } @@ -288,10 +311,12 @@ class ClientContext void discard_received() { - if(!_rx_buf) { + if (!_rx_buf) + { return; } - if(_pcb) { + if (_pcb) + { tcp_recved(_pcb, (size_t) _rx_buf->tot_len); } pbuf_free(_rx_buf); @@ -306,14 +331,18 @@ class ClientContext // option 2 / _write_some() not necessary since _datasource is always nullptr here if (!_pcb) + { return true; + } int prevsndbuf = -1; // wait for peer's acks to flush lwIP's output buffer uint32_t last_sent = millis(); - while (1) { - if (millis() - last_sent > (uint32_t) max_wait_ms) { + while (1) + { + if (millis() - last_sent > (uint32_t) max_wait_ms) + { #ifdef DEBUGV // wait until sent: timeout DEBUGV(":wustmo\n"); @@ -326,7 +355,8 @@ class ClientContext tcp_output(_pcb); int sndbuf = tcp_sndbuf(_pcb); - if (sndbuf != prevsndbuf) { + if (sndbuf != prevsndbuf) + { // send buffer has changed (or first iteration) prevsndbuf = sndbuf; // We just sent a bit, move timeout forward @@ -335,7 +365,8 @@ class ClientContext delay(0); // from sys or os context - if ((state() != ESTABLISHED) || (sndbuf == TCP_SND_BUF)) { + if ((state() != ESTABLISHED) || (sndbuf == TCP_SND_BUF)) + { break; } } @@ -346,7 +377,8 @@ class ClientContext uint8_t state() const { - if(!_pcb) { + if (!_pcb) + { return CLOSED; } @@ -355,7 +387,8 @@ class ClientContext size_t write(const uint8_t* data, size_t size) { - if (!_pcb) { + if (!_pcb) + { return 0; } return _write_from_source(new BufferDataSource(data, size)); @@ -363,7 +396,8 @@ class ClientContext size_t write(Stream& stream) { - if (!_pcb) { + if (!_pcb) + { return 0; } return _write_from_source(new BufferedStreamDataSource(stream, stream.available())); @@ -371,51 +405,55 @@ class ClientContext size_t write_P(PGM_P buf, size_t size) { - if (!_pcb) { + if (!_pcb) + { return 0; } ProgmemStream stream(buf, size); return _write_from_source(new BufferedStreamDataSource(stream, size)); } - void keepAlive (uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT) + void keepAlive(uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT) { - if (idle_sec && intv_sec && count) { + if (idle_sec && intv_sec && count) + { _pcb->so_options |= SOF_KEEPALIVE; _pcb->keep_idle = (uint32_t)1000 * idle_sec; _pcb->keep_intvl = (uint32_t)1000 * intv_sec; _pcb->keep_cnt = count; } else + { _pcb->so_options &= ~SOF_KEEPALIVE; + } } - bool isKeepAliveEnabled () const + bool isKeepAliveEnabled() const { return !!(_pcb->so_options & SOF_KEEPALIVE); } - uint16_t getKeepAliveIdle () const + uint16_t getKeepAliveIdle() const { - return isKeepAliveEnabled()? (_pcb->keep_idle + 500) / 1000: 0; + return isKeepAliveEnabled() ? (_pcb->keep_idle + 500) / 1000 : 0; } - uint16_t getKeepAliveInterval () const + uint16_t getKeepAliveInterval() const { - return isKeepAliveEnabled()? (_pcb->keep_intvl + 500) / 1000: 0; + return isKeepAliveEnabled() ? (_pcb->keep_intvl + 500) / 1000 : 0; } - uint8_t getKeepAliveCount () const + uint8_t getKeepAliveCount() const { - return isKeepAliveEnabled()? _pcb->keep_cnt: 0; + return isKeepAliveEnabled() ? _pcb->keep_cnt : 0; } - bool getSync () const + bool getSync() const { return _sync; } - void setSync (bool sync) + void setSync(bool sync) { _sync = sync; } @@ -429,7 +467,8 @@ class ClientContext void _notify_error() { - if (_connect_pending || _send_waiting) { + if (_connect_pending || _send_waiting) + { esp_schedule(); } } @@ -441,13 +480,17 @@ class ClientContext _datasource = ds; _written = 0; _op_start_time = millis(); - do { - if (_write_some()) { + do + { + if (_write_some()) + { _op_start_time = millis(); } - if (!_datasource->available() || _is_timeout() || state() == CLOSED) { - if (_is_timeout()) { + if (!_datasource->available() || _is_timeout() || state() == CLOSED) + { + if (_is_timeout()) + { DEBUGV(":wtmo\r\n"); } delete _datasource; @@ -457,18 +500,21 @@ class ClientContext ++_send_waiting; esp_yield(); - } while(true); + } while (true); _send_waiting = 0; if (_sync) + { wait_until_sent(); + } return _written; } bool _write_some() { - if (!_datasource || !_pcb) { + if (!_datasource || !_pcb) + { return false; } @@ -476,12 +522,17 @@ class ClientContext bool has_written = false; - while (_datasource) { + while (_datasource) + { if (state() == CLOSED) + { return false; + } size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), _datasource->available()); if (!next_chunk_size) + { break; + } const uint8_t* buf = _datasource->get_buffer(next_chunk_size); uint8_t flags = 0; @@ -491,23 +542,30 @@ class ClientContext // PUSH does not break Nagle // #5173: windows needs this flag // more info: https://lists.gnu.org/archive/html/lwip-users/2009-11/msg00018.html - flags |= TCP_WRITE_FLAG_MORE; // do not tcp-PuSH (yet) + { + flags |= TCP_WRITE_FLAG_MORE; // do not tcp-PuSH (yet) + } if (!_sync) // user data must be copied when data are sent but not yet acknowledged // (with sync, we wait for acknowledgment before returning to user) + { flags |= TCP_WRITE_FLAG_COPY; + } err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, _datasource->available(), (int)err); - if (err == ERR_OK) { + if (err == ERR_OK) + { _datasource->release_buffer(buf, next_chunk_size); _written += next_chunk_size; has_written = true; - } else { - // ERR_MEM(-1) is a valid error meaning - // "come back later". It leaves state() opened + } + else + { + // ERR_MEM(-1) is a valid error meaning + // "come back later". It leaves state() opened break; } } @@ -525,7 +583,8 @@ class ClientContext void _write_some_from_cb() { - if (_send_waiting == 1) { + if (_send_waiting == 1) + { _send_waiting--; esp_schedule(); } @@ -542,17 +601,24 @@ class ClientContext void _consume(size_t size) { - if(_pcb) + if (_pcb) + { tcp_recved(_pcb, size); + } ptrdiff_t left = _rx_buf->len - _rx_buf_offset - size; - if(left > 0) { + if (left > 0) + { _rx_buf_offset += size; - } else if(!_rx_buf->next) { + } + else if (!_rx_buf->next) + { DEBUGV(":c0 %d, %d\r\n", size, _rx_buf->tot_len); pbuf_free(_rx_buf); _rx_buf = 0; _rx_buf_offset = 0; - } else { + } + else + { DEBUGV(":c %d, %d, %d\r\n", size, _rx_buf->len, _rx_buf->tot_len); auto head = _rx_buf; _rx_buf = _rx_buf->next; @@ -566,17 +632,21 @@ class ClientContext { (void) pcb; (void) err; - if(pb == 0) { // connection closed + if (pb == 0) // connection closed + { DEBUGV(":rcl\r\n"); _notify_error(); abort(); return ERR_ABRT; } - if(_rx_buf) { + if (_rx_buf) + { DEBUGV(":rch %d, %d\r\n", _rx_buf->tot_len, pb->tot_len); pbuf_cat(_rx_buf, pb); - } else { + } + else + { DEBUGV(":rn %d\r\n", pb->tot_len); _rx_buf = pb; _rx_buf_offset = 0; diff --git a/libraries/ESP8266WiFi/src/include/DataSource.h b/libraries/ESP8266WiFi/src/include/DataSource.h index 2a0bfed260..cd3beb5a15 100644 --- a/libraries/ESP8266WiFi/src/include/DataSource.h +++ b/libraries/ESP8266WiFi/src/include/DataSource.h @@ -1,13 +1,14 @@ -/* DataSource.h - a read-only object similar to Stream, but with less methods - * Copyright (c) 2016 Ivan Grokhotkov. All rights reserved. - * This file is distributed under MIT license. - */ +/* DataSource.h - a read-only object similar to Stream, but with less methods + Copyright (c) 2016 Ivan Grokhotkov. All rights reserved. + This file is distributed under MIT license. +*/ #ifndef DATASOURCE_H #define DATASOURCE_H #include -class DataSource { +class DataSource +{ public: virtual ~DataSource() {} virtual size_t available() = 0; @@ -16,7 +17,8 @@ class DataSource { }; -class BufferDataSource : public DataSource { +class BufferDataSource : public DataSource +{ public: BufferDataSource(const uint8_t* data, size_t size) : _data(data), @@ -50,7 +52,8 @@ class BufferDataSource : public DataSource { }; template -class BufferedStreamDataSource : public DataSource { +class BufferedStreamDataSource : public DataSource +{ public: BufferedStreamDataSource(TStream& stream, size_t size) : _stream(stream), @@ -74,10 +77,12 @@ class BufferedStreamDataSource : public DataSource { const size_t min_buffer_size = size > stream_read ? size : stream_read; //Buffer too small? - if (_bufferSize < min_buffer_size) { + if (_bufferSize < min_buffer_size) + { uint8_t *new_buffer = new uint8_t[min_buffer_size]; //If stream reading is ahead, than some data is already in the old buffer and needs to be copied to new resized buffer - if (_buffer && stream_read > 0) { + if (_buffer && stream_read > 0) + { memcpy(new_buffer, _buffer.get(), stream_read); } _buffer.reset(new_buffer); @@ -86,7 +91,8 @@ class BufferedStreamDataSource : public DataSource { //Fetch remaining data from stream //If error in tcp_write in ClientContext::_write_some() occured earlier and therefore release_buffer was not called last time, than the requested stream data is already in the buffer. - if (size > stream_read) { + if (size > stream_read) + { //Remaining bytes to read from stream const size_t stream_rem = size - stream_read; const size_t cb = _stream.readBytes(reinterpret_cast(_buffer.get() + stream_read), stream_rem); @@ -100,18 +106,20 @@ class BufferedStreamDataSource : public DataSource { void release_buffer(const uint8_t* buffer, size_t size) override { - if (size == 0) { + if (size == 0) + { return; } (void)buffer; - _pos += size; + _pos += size; //Cannot release more than acquired through get_buffer assert(_pos <= _streamPos); //Release less than requested with get_buffer? - if (_pos < _streamPos) { + if (_pos < _streamPos) + { // Move unreleased stream data in buffer to front assert(_buffer); memmove(_buffer.get(), _buffer.get() + size, _streamPos - _pos); diff --git a/libraries/ESP8266WiFi/src/include/SSLContext.h b/libraries/ESP8266WiFi/src/include/SSLContext.h index f7d824fbf9..03985f1608 100644 --- a/libraries/ESP8266WiFi/src/include/SSLContext.h +++ b/libraries/ESP8266WiFi/src/include/SSLContext.h @@ -1,22 +1,22 @@ /* - SSLContext.h - Used by WiFiClientAxTLS - Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + SSLContext.h - Used by WiFiClientAxTLS + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -41,16 +41,20 @@ extern "C" #include #include "c_types.h" -namespace axTLS { +namespace axTLS +{ typedef struct BufferItem { BufferItem(const uint8_t* data_, size_t size_) - : size(size_), data(new uint8_t[size]) + : size(size_), data(new uint8_t[size]) { - if (data.get() != nullptr) { + if (data.get() != nullptr) + { memcpy(data.get(), data_, size); - } else { + } + else + { DEBUGV(":wcs alloc %d failed\r\n", size_); size = 0; } @@ -68,13 +72,18 @@ class SSLContext SSLContext(bool isServer = false) { _isServer = isServer; - if (!_isServer) { - if (_ssl_client_ctx_refcnt == 0) { + if (!_isServer) + { + if (_ssl_client_ctx_refcnt == 0) + { _ssl_client_ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER | SSL_DEBUG_OPTS | SSL_CONNECT_IN_PARTS | SSL_READ_BLOCKING | SSL_NO_DEFAULT_KEY, 0); } ++_ssl_client_ctx_refcnt; - } else { - if (_ssl_svr_ctx_refcnt == 0) { + } + else + { + if (_ssl_svr_ctx_refcnt == 0) + { _ssl_svr_ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER | SSL_DEBUG_OPTS | SSL_CONNECT_IN_PARTS | SSL_READ_BLOCKING | SSL_NO_DEFAULT_KEY, 0); } ++_ssl_svr_ctx_refcnt; @@ -83,20 +92,26 @@ class SSLContext ~SSLContext() { - if (io_ctx) { + if (io_ctx) + { io_ctx->unref(); io_ctx = nullptr; } _ssl = nullptr; - if (!_isServer) { + if (!_isServer) + { --_ssl_client_ctx_refcnt; - if (_ssl_client_ctx_refcnt == 0) { + if (_ssl_client_ctx_refcnt == 0) + { ssl_ctx_free(_ssl_client_ctx); _ssl_client_ctx = nullptr; } - } else { + } + else + { --_ssl_svr_ctx_refcnt; - if (_ssl_svr_ctx_refcnt == 0) { + if (_ssl_svr_ctx_refcnt == 0) + { ssl_ctx_free(_ssl_svr_ctx); _ssl_svr_ctx = nullptr; } @@ -112,10 +127,11 @@ class SSLContext { SSL_EXTENSIONS* ext = ssl_ext_new(); ssl_ext_set_host_name(ext, hostName); - if (_ssl) { - /* Creating a new TLS session on top of a new TCP connection. - ssl_free will want to send a close notify alert, but the old TCP connection - is already gone at this point, so reset io_ctx. */ + if (_ssl) + { + /* Creating a new TLS session on top of a new TCP connection. + ssl_free will want to send a close notify alert, but the old TCP connection + is already gone at this point, so reset io_ctx. */ io_ctx = nullptr; _ssl = nullptr; _available = 0; @@ -131,10 +147,12 @@ class SSLContext uint32_t t = millis(); - while (millis() - t < timeout_ms && ssl_handshake_status(_ssl.get()) != SSL_OK) { + while (millis() - t < timeout_ms && ssl_handshake_status(_ssl.get()) != SSL_OK) + { uint8_t* data; int rc = ssl_read(_ssl.get(), &data); - if (rc < SSL_OK) { + if (rc < SSL_OK) + { ssl_display_error(rc); break; } @@ -147,16 +165,18 @@ class SSLContext ctx->ref(); // Wrap the new SSL with a smart pointer, custom deleter to call ssl_free - SSL *_new_ssl = ssl_server_new(_ssl_svr_ctx, reinterpret_cast(this)); + SSL *_new_ssl = ssl_server_new(_ssl_svr_ctx, reinterpret_cast(this)); std::shared_ptr _new_ssl_shared(_new_ssl, _delete_shared_SSL); _ssl = _new_ssl_shared; uint32_t t = millis(); - while (millis() - t < timeout_ms && ssl_handshake_status(_ssl.get()) != SSL_OK) { + while (millis() - t < timeout_ms && ssl_handshake_status(_ssl.get()) != SSL_OK) + { uint8_t* data; int rc = ssl_read(_ssl.get(), &data); - if (rc < SSL_OK) { + if (rc < SSL_OK) + { ssl_display_error(rc); break; } @@ -165,7 +185,8 @@ class SSLContext void stop() { - if (io_ctx) { + if (io_ctx) + { io_ctx->unref(); } io_ctx = nullptr; @@ -173,17 +194,22 @@ class SSLContext bool connected() { - if (_isServer) { + if (_isServer) + { return _ssl != nullptr; - } else { + } + else + { return _ssl != nullptr && ssl_handshake_status(_ssl.get()) == SSL_OK; } } int read(uint8_t* dst, size_t size) { - if (!_available) { - if (!_readAll()) { + if (!_available) + { + if (!_readAll()) + { return 0; } } @@ -191,10 +217,12 @@ class SSLContext memcpy(dst, _read_ptr, will_copy); _read_ptr += will_copy; _available -= will_copy; - if (_available == 0) { + if (_available == 0) + { _read_ptr = nullptr; /* Send pending outgoing data, if any */ - if (_hasWriteBuffers()) { + if (_hasWriteBuffers()) + { _writeBuffersSend(); } } @@ -203,18 +231,22 @@ class SSLContext int read() { - if (!_available) { - if (!_readAll()) { + if (!_available) + { + if (!_readAll()) + { return -1; } } int result = _read_ptr[0]; ++_read_ptr; --_available; - if (_available == 0) { + if (_available == 0) + { _read_ptr = nullptr; /* Send pending outgoing data, if any */ - if (_hasWriteBuffers()) { + if (_hasWriteBuffers()) + { _writeBuffersSend(); } } @@ -223,30 +255,37 @@ class SSLContext int write(const uint8_t* src, size_t size) { - if (_isServer) { + if (_isServer) + { return _write(src, size); - } else if (!_available) { - if (_hasWriteBuffers()) { + } + else if (!_available) + { + if (_hasWriteBuffers()) + { int rc = _writeBuffersSend(); - if (rc < 0) { + if (rc < 0) + { return rc; } } return _write(src, size); } - /* Some received data is still present in the axtls fragment buffer. - We can't call ssl_write now, as that will overwrite the contents of - the fragment buffer, corrupting the received data. - Save a copy of the outgoing data, and call ssl_write when all - recevied data has been consumed by the application. + /* Some received data is still present in the axtls fragment buffer. + We can't call ssl_write now, as that will overwrite the contents of + the fragment buffer, corrupting the received data. + Save a copy of the outgoing data, and call ssl_write when all + recevied data has been consumed by the application. */ return _writeBufferAdd(src, size); } int peek() { - if (!_available) { - if (!_readAll()) { + if (!_available) + { + if (!_readAll()) + { return -1; } } @@ -255,8 +294,10 @@ class SSLContext size_t peekBytes(char *dst, size_t size) { - if (!_available) { - if (!_readAll()) { + if (!_available) + { + if (!_readAll()) + { return -1; } } @@ -269,9 +310,12 @@ class SSLContext int available() { auto cb = _available; - if (cb == 0) { + if (cb == 0) + { cb = _readAll(); - } else { + } + else + { optimistic_yield(100); } return cb; @@ -286,13 +330,15 @@ class SSLContext bool loadObject(int type, Stream& stream, size_t size) { std::unique_ptr buf(new uint8_t[size]); - if (!buf.get()) { + if (!buf.get()) + { DEBUGV("loadObject: failed to allocate memory\n"); return false; } size_t cb = stream.readBytes(buf.get(), size); - if (cb != size) { + if (cb != size) + { DEBUGV("loadObject: reading %u bytes, got %u\n", size, cb); return false; } @@ -303,14 +349,15 @@ class SSLContext bool loadObject_P(int type, PGM_VOID_P data, size_t size) { std::unique_ptr buf(new uint8_t[size]); - memcpy_P(buf.get(),data, size); + memcpy_P(buf.get(), data, size); return loadObject(type, buf.get(), size); } bool loadObject(int type, const uint8_t* data, size_t size) { - int rc = ssl_obj_memory_load(_isServer?_ssl_svr_ctx:_ssl_client_ctx, type, data, static_cast(size), nullptr); - if (rc != SSL_OK) { + int rc = ssl_obj_memory_load(_isServer ? _ssl_svr_ctx : _ssl_client_ctx, type, data, static_cast(size), nullptr); + if (rc != SSL_OK) + { DEBUGV("loadObject: ssl_obj_memory_load returned %d\n", rc); return false; } @@ -320,10 +367,13 @@ class SSLContext bool verifyCert() { int rc = ssl_verify_cert(_ssl.get()); - if (_allowSelfSignedCerts && rc == SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED)) { + if (_allowSelfSignedCerts && rc == SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED)) + { DEBUGV("Allowing self-signed certificate\n"); return true; - } else if (rc != SSL_OK) { + } + else if (rc != SSL_OK) + { DEBUGV("ssl_verify_cert returned %d\n", rc); ssl_display_error(rc); return false; @@ -343,7 +393,8 @@ class SSLContext static ClientContext* getIOContext(int fd) { - if (fd) { + if (fd) + { SSLContext *thisSSL = reinterpret_cast(fd); return thisSSL->io_ctx; } @@ -353,7 +404,8 @@ class SSLContext protected: int _readAll() { - if (!_ssl) { + if (!_ssl) + { return 0; } @@ -361,8 +413,10 @@ class SSLContext uint8_t* data; int rc = ssl_read(_ssl.get(), &data); - if (rc <= 0) { - if (rc < SSL_OK && rc != SSL_CLOSE_NOTIFY && rc != SSL_ERROR_CONN_LOST) { + if (rc <= 0) + { + if (rc < SSL_OK && rc != SSL_CLOSE_NOTIFY && rc != SSL_ERROR_CONN_LOST) + { _ssl = nullptr; } return 0; @@ -375,12 +429,14 @@ class SSLContext int _write(const uint8_t* src, size_t size) { - if (!_ssl) { + if (!_ssl) + { return 0; } int rc = ssl_write(_ssl.get(), src, size); - if (rc >= 0) { + if (rc >= 0) + { return rc; } DEBUGV(":wcs write rc=%d\r\n", rc); @@ -389,12 +445,14 @@ class SSLContext int _writeBufferAdd(const uint8_t* data, size_t size) { - if (!_ssl) { + if (!_ssl) + { return 0; } _writeBuffers.emplace_back(data, size); - if (_writeBuffers.back().data.get() == nullptr) { + if (_writeBuffers.back().data.get() == nullptr) + { _writeBuffers.pop_back(); return 0; } @@ -403,12 +461,15 @@ class SSLContext int _writeBuffersSend() { - while (!_writeBuffers.empty()) { + while (!_writeBuffers.empty()) + { auto& first = _writeBuffers.front(); int rc = _write(first.data.get(), first.size); _writeBuffers.pop_front(); - if (rc < 0) { - if (_hasWriteBuffers()) { + if (rc < 0) + { + if (_hasWriteBuffers()) + { DEBUGV(":wcs _writeBuffersSend dropping unsent data\r\n"); _writeBuffers.clear(); } diff --git a/libraries/ESP8266WiFi/src/include/UdpContext.h b/libraries/ESP8266WiFi/src/include/UdpContext.h index 8198ccea61..e0bb286a58 100644 --- a/libraries/ESP8266WiFi/src/include/UdpContext.h +++ b/libraries/ESP8266WiFi/src/include/UdpContext.h @@ -1,22 +1,22 @@ /* - UdpContext.h - UDP connection handling on top of lwIP + UdpContext.h - UDP connection handling on top of lwIP - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef UDPCONTEXT_H #define UDPCONTEXT_H @@ -24,8 +24,8 @@ class UdpContext; extern "C" { -void esp_yield(); -void esp_schedule(); + void esp_yield(); + void esp_schedule(); #include "lwip/init.h" // LWIP_VERSION_ #include } @@ -42,14 +42,14 @@ class UdpContext typedef std::function rxhandler_t; UdpContext() - : _pcb(0) - , _rx_buf(0) - , _first_buf_taken(false) - , _rx_buf_offset(0) - , _refcnt(0) - , _tx_buf_head(0) - , _tx_buf_cur(0) - , _tx_buf_offset(0) + : _pcb(0) + , _rx_buf(0) + , _first_buf_taken(false) + , _rx_buf_offset(0) + , _refcnt(0) + , _tx_buf_head(0) + , _tx_buf_cur(0) + , _tx_buf_offset(0) { _pcb = udp_new(); #ifdef LWIP_MAYBE_XCC @@ -83,9 +83,11 @@ class UdpContext void unref() { - if(this != 0) { + if (this != 0) + { DEBUGV(":ur %d\r\n", _refcnt); - if(--_refcnt == 0) { + if (--_refcnt == 0) + { delete this; } } @@ -142,7 +144,7 @@ class UdpContext if (!addr.isV4()) { - for (auto a: addrList) + for (auto a : addrList) if (a.addr() == addr) { // found the IPv6 address, @@ -179,14 +181,17 @@ class UdpContext // warning: handler is called from tcp stack context // esp_yield and non-reentrant functions which depend on it will fail - void onRx(rxhandler_t handler) { + void onRx(rxhandler_t handler) + { _on_rx = handler; } size_t getSize() const { if (!_rx_buf) + { return 0; + } return _rx_buf->len - _rx_buf_offset; } @@ -202,7 +207,8 @@ class UdpContext _rx_buf_offset = pos; } - bool isValidOffset(const size_t pos) const { + bool isValidOffset(const size_t pos) const + { return (pos <= _rx_buf->len); } @@ -224,14 +230,18 @@ class UdpContext uint16_t getLocalPort() const { if (!_pcb) + { return 0; + } return _pcb->local_port; } bool next() { if (!_rx_buf) + { return false; + } if (!_first_buf_taken) { _first_buf_taken = true; @@ -272,7 +282,9 @@ class UdpContext int read() { if (!_rx_buf || _rx_buf_offset >= _rx_buf->len) + { return -1; + } char c = reinterpret_cast(_rx_buf->payload)[_rx_buf_offset]; _consume(1); @@ -282,7 +294,9 @@ class UdpContext size_t read(char* dst, size_t size) { if (!_rx_buf) + { return 0; + } size_t max_size = _rx_buf->len - _rx_buf_offset; size = (size < max_size) ? size : max_size; @@ -297,7 +311,9 @@ class UdpContext int peek() const { if (!_rx_buf || _rx_buf_offset == _rx_buf->len) + { return -1; + } return reinterpret_cast(_rx_buf->payload)[_rx_buf_offset]; } @@ -306,7 +322,9 @@ class UdpContext { //XXX this does not follow Arduino's flush definition if (!_rx_buf) + { return; + } _consume(_rx_buf->len - _rx_buf_offset); } @@ -324,7 +342,7 @@ class UdpContext } size_t left_to_copy = size; - while(left_to_copy) + while (left_to_copy) { // size already used in current pbuf size_t used_cur = _tx_buf_offset - (_tx_buf_head->tot_len - _tx_buf_cur->tot_len); @@ -347,12 +365,15 @@ class UdpContext { size_t data_size = _tx_buf_offset; pbuf* tx_copy = pbuf_alloc(PBUF_TRANSPORT, data_size, PBUF_RAM); - if(!tx_copy){ + if (!tx_copy) + { DEBUGV("failed pbuf_alloc"); } - else{ + else + { uint8_t* dst = reinterpret_cast(tx_copy->payload); - for (pbuf* p = _tx_buf_head; p; p = p->next) { + for (pbuf* p = _tx_buf_head; p; p = p->next) + { size_t will_copy = (data_size < p->len) ? data_size : p->len; memcpy(dst, p->payload, will_copy); dst += will_copy; @@ -360,27 +381,33 @@ class UdpContext } } if (_tx_buf_head) + { pbuf_free(_tx_buf_head); + } _tx_buf_head = 0; _tx_buf_cur = 0; _tx_buf_offset = 0; - if(!tx_copy){ + if (!tx_copy) + { return false; } - if (!addr) { + if (!addr) + { addr = &_pcb->remote_ip; port = _pcb->remote_port; } #ifdef LWIP_MAYBE_XCC uint16_t old_ttl = _pcb->ttl; - if (ip_addr_ismulticast(addr)) { + if (ip_addr_ismulticast(addr)) + { _pcb->ttl = _mcast_ttl; } #endif err_t err = udp_sendto(_pcb, tx_copy, addr, port); - if (err != ERR_OK) { + if (err != ERR_OK) + { DEBUGV(":ust rc=%d\r\n", (int) err); } #ifdef LWIP_MAYBE_XCC @@ -408,11 +435,13 @@ class UdpContext size_t cur_size = _tx_buf_head->tot_len; if (size < cur_size) + { return; + } size_t grow_size = size - cur_size; - while(grow_size) + while (grow_size) { pbuf* pb = pbuf_alloc(PBUF_TRANSPORT, pbuf_unit_size, PBUF_RAM); if (!pb) @@ -421,7 +450,9 @@ class UdpContext } pbuf_cat(_tx_buf_head, pb); if (grow_size < pbuf_unit_size) + { return; + } grow_size -= pbuf_unit_size; } } @@ -429,20 +460,21 @@ class UdpContext void _consume(size_t size) { _rx_buf_offset += size; - if (_rx_buf_offset > _rx_buf->len) { + if (_rx_buf_offset > _rx_buf->len) + { _rx_buf_offset = _rx_buf->len; } } void _recv(udp_pcb *upcb, pbuf *pb, - const ip_addr_t *srcaddr, u16_t srcport) + const ip_addr_t *srcaddr, u16_t srcport) { (void) upcb; #if LWIP_VERSION_MAJOR == 1 - #define TEMPDSTADDR (¤t_iphdr_dest) +#define TEMPDSTADDR (¤t_iphdr_dest) #else - #define TEMPDSTADDR (ip_current_dest_addr()) +#define TEMPDSTADDR (ip_current_dest_addr()) #endif // chain this helper pbuf first @@ -470,7 +502,7 @@ class UdpContext return; } // construct in place - new(PBUF_ALIGNER(pb_helper->payload)) AddrHelper(srcaddr, TEMPDSTADDR, srcport); + new (PBUF_ALIGNER(pb_helper->payload)) AddrHelper(srcaddr, TEMPDSTADDR, srcport); // chain it pbuf_cat(_rx_buf, pb_helper); @@ -490,17 +522,18 @@ class UdpContext _rx_buf_offset = 0; } - if (_on_rx) { + if (_on_rx) + { _on_rx(); } - #undef TEMPDSTADDR +#undef TEMPDSTADDR } static void _s_recv(void *arg, - udp_pcb *upcb, pbuf *p, - CONST ip_addr_t *srcaddr, u16_t srcport) + udp_pcb *upcb, pbuf *p, + CONST ip_addr_t *srcaddr, u16_t srcport) { reinterpret_cast(arg)->_recv(upcb, p, srcaddr, srcport); } diff --git a/libraries/ESP8266WiFi/src/include/slist.h b/libraries/ESP8266WiFi/src/include/slist.h index 0606f72431..d75c22bb0b 100644 --- a/libraries/ESP8266WiFi/src/include/slist.h +++ b/libraries/ESP8266WiFi/src/include/slist.h @@ -2,36 +2,42 @@ #define SLIST_H template -class SList { +class SList +{ public: - SList() : _next(0) { } + SList() : _next(0) { } protected: - static void _add(T* self) { - T* tmp = _s_first; - _s_first = self; - self->_next = tmp; - } - - static void _remove(T* self) { - if (_s_first == self) { - _s_first = self->_next; - self->_next = 0; - return; + static void _add(T* self) + { + T* tmp = _s_first; + _s_first = self; + self->_next = tmp; } - for (T* prev = _s_first; prev->_next; prev = prev->_next) { - if (prev->_next == self) { - prev->_next = self->_next; - self->_next = 0; - return; - } + static void _remove(T* self) + { + if (_s_first == self) + { + _s_first = self->_next; + self->_next = 0; + return; + } + + for (T* prev = _s_first; prev->_next; prev = prev->_next) + { + if (prev->_next == self) + { + prev->_next = self->_next; + self->_next = 0; + return; + } + } } - } - static T* _s_first; - T* _next; + static T* _s_first; + T* _next; }; diff --git a/libraries/ESP8266WiFi/src/include/ssl.h b/libraries/ESP8266WiFi/src/include/ssl.h index 8879e4cb9a..203b3a146a 100644 --- a/libraries/ESP8266WiFi/src/include/ssl.h +++ b/libraries/ESP8266WiFi/src/include/ssl.h @@ -1,65 +1,65 @@ /* - * Copyright (c) 2007-2016, Cameron Rich - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * + Copyright (c) 2007-2016, Cameron Rich + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. * * Neither the name of the axTLS project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ /** - * @mainpage axTLS API - * - * @image html axolotl.jpg - * - * The axTLS library has features such as: - * - The TLSv1 SSL client/server protocol - * - No requirement to use any openssl libraries. - * - A choice between AES block (128/256 bit) and RC4 (128 bit) stream ciphers. - * - RSA encryption/decryption with variable sized keys (up to 4096 bits). - * - Certificate chaining and peer authentication. - * - Session resumption, session renegotiation. - * - ASN.1, X.509, PKCS#8, PKCS#12 keys/certificates with DER/PEM encoding. - * - Highly configurable compile time options. - * - Portable across many platforms (written in ANSI C), and has language - * bindings in C, C#, VB.NET, Java, Perl and Lua. - * - Partial openssl API compatibility (via a wrapper). - * - A very small footprint (around 50-60kB for the library in 'server-only' - * mode). - * - No dependencies on sockets - can use serial connections for example. - * - A very simple API - ~ 20 functions/methods. - * - * A list of these functions/methods are described below. - * - * @ref c_api - * - * @ref bigint_api - * - * @ref csharp_api - * - * @ref java_api - */ + @mainpage axTLS API + + @image html axolotl.jpg + + The axTLS library has features such as: + - The TLSv1 SSL client/server protocol + - No requirement to use any openssl libraries. + - A choice between AES block (128/256 bit) and RC4 (128 bit) stream ciphers. + - RSA encryption/decryption with variable sized keys (up to 4096 bits). + - Certificate chaining and peer authentication. + - Session resumption, session renegotiation. + - ASN.1, X.509, PKCS#8, PKCS#12 keys/certificates with DER/PEM encoding. + - Highly configurable compile time options. + - Portable across many platforms (written in ANSI C), and has language + bindings in C, C#, VB.NET, Java, Perl and Lua. + - Partial openssl API compatibility (via a wrapper). + - A very small footprint (around 50-60kB for the library in 'server-only' + mode). + - No dependencies on sockets - can use serial connections for example. + - A very simple API - ~ 20 functions/methods. + + A list of these functions/methods are described below. + + @ref c_api + + @ref bigint_api + + @ref csharp_api + + @ref java_api +*/ #ifndef HEADER_SSL_H #define HEADER_SSL_H @@ -187,391 +187,391 @@ typedef struct SSL_EXTENSIONS_ SSL_EXTENSIONS; #define SSL_OBJ_PKCS12 5 /** - * @defgroup c_api Standard C API - * @brief The standard interface in C. - * @{ - */ + @defgroup c_api Standard C API + @brief The standard interface in C. + @{ +*/ /** - * @brief Establish a new client/server context. - * - * This function is called before any client/server SSL connections are made. - * - * Each new connection will use the this context's private key and - * certificate chain. If a different certificate chain is required, then a - * different context needs to be be used. - * - * There are two threading models supported - a single thread with one - * SSL_CTX can support any number of SSL connections - and multiple threads can - * support one SSL_CTX object each (the default). But if a single SSL_CTX - * object uses many SSL objects in individual threads, then the - * CONFIG_SSL_CTX_MUTEXING option needs to be configured. - * - * @param options [in] Any particular options. At present the options - * supported are: - * - SSL_SERVER_VERIFY_LATER (client only): Don't stop a handshake if the server - * authentication fails. The certificate can be authenticated later with a - * call to ssl_verify_cert(). - * - SSL_CLIENT_AUTHENTICATION (server only): Enforce client authentication - * i.e. each handshake will include a "certificate request" message from the - * server. Only available if verification has been enabled. - * - SSL_DISPLAY_BYTES (full mode build only): Display the byte sequences - * during the handshake. - * - SSL_DISPLAY_STATES (full mode build only): Display the state changes - * during the handshake. - * - SSL_DISPLAY_CERTS (full mode build only): Display the certificates that - * are passed during a handshake. - * - SSL_DISPLAY_RSA (full mode build only): Display the RSA key details that - * are passed during a handshake. - * - SSL_CONNECT_IN_PARTS (client only): To use a non-blocking version of - * ssl_client_new(). - * @param num_sessions [in] The number of sessions to be used for session - * caching. If this value is 0, then there is no session caching. This option - * is not used in skeleton mode. - * @return A client/server context. - */ + @brief Establish a new client/server context. + + This function is called before any client/server SSL connections are made. + + Each new connection will use the this context's private key and + certificate chain. If a different certificate chain is required, then a + different context needs to be be used. + + There are two threading models supported - a single thread with one + SSL_CTX can support any number of SSL connections - and multiple threads can + support one SSL_CTX object each (the default). But if a single SSL_CTX + object uses many SSL objects in individual threads, then the + CONFIG_SSL_CTX_MUTEXING option needs to be configured. + + @param options [in] Any particular options. At present the options + supported are: + - SSL_SERVER_VERIFY_LATER (client only): Don't stop a handshake if the server + authentication fails. The certificate can be authenticated later with a + call to ssl_verify_cert(). + - SSL_CLIENT_AUTHENTICATION (server only): Enforce client authentication + i.e. each handshake will include a "certificate request" message from the + server. Only available if verification has been enabled. + - SSL_DISPLAY_BYTES (full mode build only): Display the byte sequences + during the handshake. + - SSL_DISPLAY_STATES (full mode build only): Display the state changes + during the handshake. + - SSL_DISPLAY_CERTS (full mode build only): Display the certificates that + are passed during a handshake. + - SSL_DISPLAY_RSA (full mode build only): Display the RSA key details that + are passed during a handshake. + - SSL_CONNECT_IN_PARTS (client only): To use a non-blocking version of + ssl_client_new(). + @param num_sessions [in] The number of sessions to be used for session + caching. If this value is 0, then there is no session caching. This option + is not used in skeleton mode. + @return A client/server context. +*/ EXP_FUNC SSL_CTX * STDCALL ssl_ctx_new(uint32_t options, int num_sessions); /** - * @brief Remove a client/server context. - * - * Frees any used resources used by this context. Each connection will be - * sent a "Close Notify" alert (if possible). - * @param ssl_ctx [in] The client/server context. - */ + @brief Remove a client/server context. + + Frees any used resources used by this context. Each connection will be + sent a "Close Notify" alert (if possible). + @param ssl_ctx [in] The client/server context. +*/ EXP_FUNC void STDCALL ssl_ctx_free(SSL_CTX *ssl_ctx); /** - * @brief Allocates new SSL extensions structure and returns pointer to it - * - * @return ssl_ext Pointer to SSL_EXTENSIONS structure - * - */ + @brief Allocates new SSL extensions structure and returns pointer to it + + @return ssl_ext Pointer to SSL_EXTENSIONS structure + +*/ EXP_FUNC SSL_EXTENSIONS * STDCALL ssl_ext_new(); /** - * @brief Set the host name for SNI extension - * @param ssl_ext pointer returned by ssl_ext_new - * @param host_name pointer to a zero-terminated string containing host name - */ + @brief Set the host name for SNI extension + @param ssl_ext pointer returned by ssl_ext_new + @param host_name pointer to a zero-terminated string containing host name +*/ EXP_FUNC void STDCALL ssl_ext_set_host_name(SSL_EXTENSIONS * ext, const char* host_name); /** - * @brief Set the maximum fragment size for the fragment size negotiation extension - * @param ssl_ext pointer returned by ssl_ext_new - * @param fragment_size fragment size, allowed values: 2^9, 2^10 ... 2^14 - */ + @brief Set the maximum fragment size for the fragment size negotiation extension + @param ssl_ext pointer returned by ssl_ext_new + @param fragment_size fragment size, allowed values: 2^9, 2^10 ... 2^14 +*/ EXP_FUNC void STDCALL ssl_ext_set_max_fragment_size(SSL_EXTENSIONS * ext, unsigned fragment_size); /** - * @brief Frees SSL extensions structure - * - * @param ssl_ext [in] Pointer to SSL_EXTENSION structure - * - */ + @brief Frees SSL extensions structure + + @param ssl_ext [in] Pointer to SSL_EXTENSION structure + +*/ EXP_FUNC void STDCALL ssl_ext_free(SSL_EXTENSIONS *ssl_ext); /** - * @brief (server only) Establish a new SSL connection to an SSL client. - * - * It is up to the application to establish the logical connection (whether it - * is a socket, serial connection etc). - * @param ssl_ctx [in] The server context. - * @param client_fd [in] The client's file descriptor. - * @return An SSL object reference. - */ + @brief (server only) Establish a new SSL connection to an SSL client. + + It is up to the application to establish the logical connection (whether it + is a socket, serial connection etc). + @param ssl_ctx [in] The server context. + @param client_fd [in] The client's file descriptor. + @return An SSL object reference. +*/ EXP_FUNC SSL * STDCALL ssl_server_new(SSL_CTX *ssl_ctx, int client_fd); /** - * @brief (client only) Establish a new SSL connection to an SSL server. - * - * It is up to the application to establish the initial logical connection - * (whether it is a socket, serial connection etc). - * - * This is a normally a blocking call - it will finish when the handshake is - * complete (or has failed). To use in non-blocking mode, set - * SSL_CONNECT_IN_PARTS in ssl_ctx_new(). - * @param ssl_ctx [in] The client context. - * @param client_fd [in] The client's file descriptor. - * @param session_id [in] A 32 byte session id for session resumption. This - * can be null if no session resumption is being used or required. This option - * is not used in skeleton mode. - * @param sess_id_size The size of the session id (max 32) - * @param ssl_ext pointer to a structure with the activated SSL extensions and their values - * @return An SSL object reference. Use ssl_handshake_status() to check - * if a handshake succeeded. - */ + @brief (client only) Establish a new SSL connection to an SSL server. + + It is up to the application to establish the initial logical connection + (whether it is a socket, serial connection etc). + + This is a normally a blocking call - it will finish when the handshake is + complete (or has failed). To use in non-blocking mode, set + SSL_CONNECT_IN_PARTS in ssl_ctx_new(). + @param ssl_ctx [in] The client context. + @param client_fd [in] The client's file descriptor. + @param session_id [in] A 32 byte session id for session resumption. This + can be null if no session resumption is being used or required. This option + is not used in skeleton mode. + @param sess_id_size The size of the session id (max 32) + @param ssl_ext pointer to a structure with the activated SSL extensions and their values + @return An SSL object reference. Use ssl_handshake_status() to check + if a handshake succeeded. +*/ EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const uint8_t *session_id, uint8_t sess_id_size, SSL_EXTENSIONS* ssl_ext); /** - * @brief Free any used resources on this connection. + @brief Free any used resources on this connection. - * A "Close Notify" message is sent on this connection (if possible). It is up - * to the application to close the socket or file descriptor. - * @param ssl [in] The ssl object reference. - */ + A "Close Notify" message is sent on this connection (if possible). It is up + to the application to close the socket or file descriptor. + @param ssl [in] The ssl object reference. +*/ EXP_FUNC void STDCALL ssl_free(SSL *ssl); /** - * @brief Read the SSL data stream. - * If the socket is non-blocking and data is blocked then SSO_OK will be - * returned. - * @param ssl [in] An SSL object reference. - * @param in_data [out] If the read was successful, a pointer to the read - * buffer will be here. Do NOT ever free this memory as this buffer is used in - * sucessive calls. If the call was unsuccessful, this value will be null. - * @return The number of decrypted bytes: - * - if > 0, then the handshaking is complete and we are returning the number - * of decrypted bytes. - * - SSL_OK if the handshaking stage is successful (but not yet complete). - * - < 0 if an error. - * @see ssl.h for the error code list. - * @note Use in_data before doing any successive ssl calls. - */ + @brief Read the SSL data stream. + If the socket is non-blocking and data is blocked then SSO_OK will be + returned. + @param ssl [in] An SSL object reference. + @param in_data [out] If the read was successful, a pointer to the read + buffer will be here. Do NOT ever free this memory as this buffer is used in + sucessive calls. If the call was unsuccessful, this value will be null. + @return The number of decrypted bytes: + - if > 0, then the handshaking is complete and we are returning the number + of decrypted bytes. + - SSL_OK if the handshaking stage is successful (but not yet complete). + - < 0 if an error. + @see ssl.h for the error code list. + @note Use in_data before doing any successive ssl calls. +*/ EXP_FUNC int STDCALL ssl_read(SSL *ssl, uint8_t **in_data); /** - * @brief Write to the SSL data stream. - * if the socket is non-blocking and data is blocked then a check is made - * to ensure that all data is sent (i.e. blocked mode is forced). - * @param ssl [in] An SSL obect reference. - * @param out_data [in] The data to be written - * @param out_len [in] The number of bytes to be written. - * @return The number of bytes sent, or if < 0 if an error. - * @see ssl.h for the error code list. - */ + @brief Write to the SSL data stream. + if the socket is non-blocking and data is blocked then a check is made + to ensure that all data is sent (i.e. blocked mode is forced). + @param ssl [in] An SSL obect reference. + @param out_data [in] The data to be written + @param out_len [in] The number of bytes to be written. + @return The number of bytes sent, or if < 0 if an error. + @see ssl.h for the error code list. +*/ EXP_FUNC int STDCALL ssl_write(SSL *ssl, const uint8_t *out_data, int out_len); /** - * @brief Calculate the size of the encrypted data from what you are about to send - * @param ssl [in] An SSL obect reference. - * @param out_len [in] The number of bytes to be written. - * @return The number of bytes that will be sent, or if < 0 if an error. - * @see ssl.h for the error code list. - */ + @brief Calculate the size of the encrypted data from what you are about to send + @param ssl [in] An SSL obect reference. + @param out_len [in] The number of bytes to be written. + @return The number of bytes that will be sent, or if < 0 if an error. + @see ssl.h for the error code list. +*/ EXP_FUNC int STDCALL ssl_calculate_write_length(SSL *ssl, int out_len); /** - * @brief Find an ssl object based on a file descriptor. - * - * Goes through the list of SSL objects maintained in a client/server context - * to look for a file descriptor match. - * @param ssl_ctx [in] The client/server context. - * @param client_fd [in] The file descriptor. - * @return A reference to the SSL object. Returns null if the object could not - * be found. - */ + @brief Find an ssl object based on a file descriptor. + + Goes through the list of SSL objects maintained in a client/server context + to look for a file descriptor match. + @param ssl_ctx [in] The client/server context. + @param client_fd [in] The file descriptor. + @return A reference to the SSL object. Returns null if the object could not + be found. +*/ EXP_FUNC SSL * STDCALL ssl_find(SSL_CTX *ssl_ctx, int client_fd); /** - * @brief Get the session id for a handshake. - * - * This will be a 32 byte sequence and is available after the first - * handshaking messages are sent. - * @param ssl [in] An SSL object reference. - * @return The session id as a 32 byte sequence. - * @note A SSLv23 handshake may have only 16 valid bytes. - */ + @brief Get the session id for a handshake. + + This will be a 32 byte sequence and is available after the first + handshaking messages are sent. + @param ssl [in] An SSL object reference. + @return The session id as a 32 byte sequence. + @note A SSLv23 handshake may have only 16 valid bytes. +*/ EXP_FUNC const uint8_t * STDCALL ssl_get_session_id(const SSL *ssl); /** - * @brief Get the session id size for a handshake. - * - * This will normally be 32 but could be 0 (no session id) or something else. - * @param ssl [in] An SSL object reference. - * @return The size of the session id. - */ + @brief Get the session id size for a handshake. + + This will normally be 32 but could be 0 (no session id) or something else. + @param ssl [in] An SSL object reference. + @return The size of the session id. +*/ EXP_FUNC uint8_t STDCALL ssl_get_session_id_size(const SSL *ssl); /** - * @brief Return the cipher id (in the SSL form). - * @param ssl [in] An SSL object reference. - * @return The cipher id. This will be one of the following: - * - SSL_AES128_SHA (0x2f) - * - SSL_AES256_SHA (0x35) - * - SSL_RC4_128_SHA (0x05) - * - SSL_RC4_128_MD5 (0x04) - */ + @brief Return the cipher id (in the SSL form). + @param ssl [in] An SSL object reference. + @return The cipher id. This will be one of the following: + - SSL_AES128_SHA (0x2f) + - SSL_AES256_SHA (0x35) + - SSL_RC4_128_SHA (0x05) + - SSL_RC4_128_MD5 (0x04) +*/ EXP_FUNC uint8_t STDCALL ssl_get_cipher_id(const SSL *ssl); /** - * @brief Return the status of the handshake. - * @param ssl [in] An SSL object reference. - * @return SSL_OK if the handshake is complete and ok. - * @see ssl.h for the error code list. - */ + @brief Return the status of the handshake. + @param ssl [in] An SSL object reference. + @return SSL_OK if the handshake is complete and ok. + @see ssl.h for the error code list. +*/ EXP_FUNC int STDCALL ssl_handshake_status(const SSL *ssl); /** - * @brief Retrieve various parameters about the axTLS engine. - * @param offset [in] The configuration offset. It will be one of the following: - * - SSL_BUILD_MODE The build mode. This will be one of the following: - * - SSL_BUILD_SERVER_ONLY (basic server mode) - * - SSL_BUILD_ENABLE_VERIFICATION (server can do client authentication) - * - SSL_BUILD_ENABLE_CLIENT (client/server capabilties) - * - SSL_BUILD_FULL_MODE (client/server with diagnostics) - * - SSL_BUILD_SKELETON_MODE (skeleton mode) - * - SSL_MAX_CERT_CFG_OFFSET The maximum number of certificates allowed. - * - SSL_MAX_CA_CERT_CFG_OFFSET The maximum number of CA certificates allowed. - * - SSL_HAS_PEM 1 if supported - * @return The value of the requested parameter. - */ + @brief Retrieve various parameters about the axTLS engine. + @param offset [in] The configuration offset. It will be one of the following: + - SSL_BUILD_MODE The build mode. This will be one of the following: + - SSL_BUILD_SERVER_ONLY (basic server mode) + - SSL_BUILD_ENABLE_VERIFICATION (server can do client authentication) + - SSL_BUILD_ENABLE_CLIENT (client/server capabilties) + - SSL_BUILD_FULL_MODE (client/server with diagnostics) + - SSL_BUILD_SKELETON_MODE (skeleton mode) + - SSL_MAX_CERT_CFG_OFFSET The maximum number of certificates allowed. + - SSL_MAX_CA_CERT_CFG_OFFSET The maximum number of CA certificates allowed. + - SSL_HAS_PEM 1 if supported + @return The value of the requested parameter. +*/ EXP_FUNC int STDCALL ssl_get_config(int offset); /** - * @brief Display why the handshake failed. - * - * This call is only useful in a 'full mode' build. The output is to stdout. - * @param error_code [in] An error code. - * @see ssl.h for the error code list. - */ + @brief Display why the handshake failed. + + This call is only useful in a 'full mode' build. The output is to stdout. + @param error_code [in] An error code. + @see ssl.h for the error code list. +*/ EXP_FUNC void STDCALL ssl_display_error(int error_code); /** - * @brief Authenticate a received certificate. - * - * This call is usually made by a client after a handshake is complete and the - * context is in SSL_SERVER_VERIFY_LATER mode. - * @param ssl [in] An SSL object reference. - * @return SSL_OK if the certificate is verified. - */ + @brief Authenticate a received certificate. + + This call is usually made by a client after a handshake is complete and the + context is in SSL_SERVER_VERIFY_LATER mode. + @param ssl [in] An SSL object reference. + @return SSL_OK if the certificate is verified. +*/ EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl); /** - * @brief Check if certificate fingerprint (SHA1) matches the one given. - * - * @param ssl [in] An SSL object reference. - * @param fp [in] SHA1 fingerprint to match against - * @return SSL_OK if the certificate is verified. - */ + @brief Check if certificate fingerprint (SHA1) matches the one given. + + @param ssl [in] An SSL object reference. + @param fp [in] SHA1 fingerprint to match against + @return SSL_OK if the certificate is verified. +*/ EXP_FUNC int STDCALL ssl_match_fingerprint(const SSL *ssl, const uint8_t* fp); /** - * @brief Check if SHA256 hash of Subject Public Key Info matches the one given. - * - * @param ssl [in] An SSL object reference. - * @param fp [in] SHA256 hash to match against - * @return SSL_OK if the certificate is verified. - */ + @brief Check if SHA256 hash of Subject Public Key Info matches the one given. + + @param ssl [in] An SSL object reference. + @param fp [in] SHA256 hash to match against + @return SSL_OK if the certificate is verified. +*/ EXP_FUNC int STDCALL ssl_match_spki_sha256(const SSL *ssl, const uint8_t* hash); /** - * @brief Retrieve an X.509 distinguished name component. - * - * When a handshake is complete and a certificate has been exchanged, then the - * details of the remote certificate can be retrieved. - * - * This will usually be used by a client to check that the server's common - * name matches the URL. - * - * @param ssl [in] An SSL object reference. - * @param component [in] one of: - * - SSL_X509_CERT_COMMON_NAME - * - SSL_X509_CERT_ORGANIZATION - * - SSL_X509_CERT_ORGANIZATIONAL_NAME - * - SSL_X509_CA_CERT_COMMON_NAME - * - SSL_X509_CA_CERT_ORGANIZATION - * - SSL_X509_CA_CERT_ORGANIZATIONAL_NAME - * @return The appropriate string (or null if not defined) - * @note Verification build mode must be enabled. - */ + @brief Retrieve an X.509 distinguished name component. + + When a handshake is complete and a certificate has been exchanged, then the + details of the remote certificate can be retrieved. + + This will usually be used by a client to check that the server's common + name matches the URL. + + @param ssl [in] An SSL object reference. + @param component [in] one of: + - SSL_X509_CERT_COMMON_NAME + - SSL_X509_CERT_ORGANIZATION + - SSL_X509_CERT_ORGANIZATIONAL_NAME + - SSL_X509_CA_CERT_COMMON_NAME + - SSL_X509_CA_CERT_ORGANIZATION + - SSL_X509_CA_CERT_ORGANIZATIONAL_NAME + @return The appropriate string (or null if not defined) + @note Verification build mode must be enabled. +*/ EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component); /** - * @brief Retrieve a Subject Alternative DNSName - * - * When a handshake is complete and a certificate has been exchanged, then the - * details of the remote certificate can be retrieved. - * - * This will usually be used by a client to check that the server's DNS - * name matches the URL. - * - * @param ssl [in] An SSL object reference. - * @param dnsindex [in] The index of the DNS name to retrieve. - * @return The appropriate string (or null if not defined) - * @note Verification build mode must be enabled. - */ + @brief Retrieve a Subject Alternative DNSName + + When a handshake is complete and a certificate has been exchanged, then the + details of the remote certificate can be retrieved. + + This will usually be used by a client to check that the server's DNS + name matches the URL. + + @param ssl [in] An SSL object reference. + @param dnsindex [in] The index of the DNS name to retrieve. + @return The appropriate string (or null if not defined) + @note Verification build mode must be enabled. +*/ EXP_FUNC const char * STDCALL ssl_get_cert_subject_alt_dnsname(const SSL *ssl, int dnsindex); /** - * @brief Force the client to perform its handshake again. - * - * For a client this involves sending another "client hello" message. - * For the server is means sending a "hello request" message. - * - * This is a blocking call on the client (until the handshake completes). - * - * @param ssl [in] An SSL object reference. - * @return SSL_OK if renegotiation instantiation was ok - */ + @brief Force the client to perform its handshake again. + + For a client this involves sending another "client hello" message. + For the server is means sending a "hello request" message. + + This is a blocking call on the client (until the handshake completes). + + @param ssl [in] An SSL object reference. + @return SSL_OK if renegotiation instantiation was ok +*/ EXP_FUNC int STDCALL ssl_renegotiate(SSL *ssl); /** - * @brief Process a file that is in binary DER or ASCII PEM format. - * - * These are temporary objects that are used to load private keys, - * certificates etc into memory. - * @param ssl_ctx [in] The client/server context. - * @param obj_type [in] The format of the file. Can be one of: - * - SSL_OBJ_X509_CERT (no password required) - * - SSL_OBJ_X509_CACERT (no password required) - * - SSL_OBJ_RSA_KEY (AES128/AES256 PEM encryption supported) - * - SSL_OBJ_PKCS8 (RC4-128 encrypted data supported) - * - SSL_OBJ_PKCS12 (RC4-128 encrypted data supported) - * - * PEM files are automatically detected (if supported). The object type is - * also detected, and so is not relevant for these types of files. - * @param filename [in] The location of a file in DER/PEM format. - * @param password [in] The password used. Can be null if not required. - * @return SSL_OK if all ok - * @note Not available in skeleton build mode. - */ + @brief Process a file that is in binary DER or ASCII PEM format. + + These are temporary objects that are used to load private keys, + certificates etc into memory. + @param ssl_ctx [in] The client/server context. + @param obj_type [in] The format of the file. Can be one of: + - SSL_OBJ_X509_CERT (no password required) + - SSL_OBJ_X509_CACERT (no password required) + - SSL_OBJ_RSA_KEY (AES128/AES256 PEM encryption supported) + - SSL_OBJ_PKCS8 (RC4-128 encrypted data supported) + - SSL_OBJ_PKCS12 (RC4-128 encrypted data supported) + + PEM files are automatically detected (if supported). The object type is + also detected, and so is not relevant for these types of files. + @param filename [in] The location of a file in DER/PEM format. + @param password [in] The password used. Can be null if not required. + @return SSL_OK if all ok + @note Not available in skeleton build mode. +*/ EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, const char *filename, const char *password); /** - * @brief Process binary data. - * - * These are temporary objects that are used to load private keys, - * certificates etc into memory. - * @param ssl_ctx [in] The client/server context. - * @param obj_type [in] The format of the memory data. - * @param data [in] The binary data to be loaded. - * @param len [in] The amount of data to be loaded. - * @param password [in] The password used. Can be null if not required. - * @return SSL_OK if all ok - * @see ssl_obj_load for more details on obj_type. - */ + @brief Process binary data. + + These are temporary objects that are used to load private keys, + certificates etc into memory. + @param ssl_ctx [in] The client/server context. + @param obj_type [in] The format of the memory data. + @param data [in] The binary data to be loaded. + @param len [in] The amount of data to be loaded. + @param password [in] The password used. Can be null if not required. + @return SSL_OK if all ok + @see ssl_obj_load for more details on obj_type. +*/ EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int obj_type, const uint8_t *data, int len, const char *password); #ifdef CONFIG_SSL_GENERATE_X509_CERT /** - * @brief Create an X.509 certificate. - * - * This certificate is a self-signed v1 cert with a fixed start/stop validity - * times. It is signed with an internal private key in ssl_ctx. - * - * @param ssl_ctx [in] The client/server context. - * @param options [in] Not used yet. - * @param dn [in] An array of distinguished name strings. The array is defined - * by: - * - SSL_X509_CERT_COMMON_NAME (0) - * - If SSL_X509_CERT_COMMON_NAME is empty or not defined, then the - * hostname will be used. - * - SSL_X509_CERT_ORGANIZATION (1) - * - If SSL_X509_CERT_ORGANIZATION is empty or not defined, then $USERNAME - * will be used. - * - SSL_X509_CERT_ORGANIZATIONAL_NAME (2) - * - SSL_X509_CERT_ORGANIZATIONAL_NAME is optional. - * @param cert_data [out] The certificate as a sequence of bytes. - * @return < 0 if an error, or the size of the certificate in bytes. - * @note cert_data must be freed when there is no more need for it. - */ + @brief Create an X.509 certificate. + + This certificate is a self-signed v1 cert with a fixed start/stop validity + times. It is signed with an internal private key in ssl_ctx. + + @param ssl_ctx [in] The client/server context. + @param options [in] Not used yet. + @param dn [in] An array of distinguished name strings. The array is defined + by: + - SSL_X509_CERT_COMMON_NAME (0) + - If SSL_X509_CERT_COMMON_NAME is empty or not defined, then the + hostname will be used. + - SSL_X509_CERT_ORGANIZATION (1) + - If SSL_X509_CERT_ORGANIZATION is empty or not defined, then $USERNAME + will be used. + - SSL_X509_CERT_ORGANIZATIONAL_NAME (2) + - SSL_X509_CERT_ORGANIZATIONAL_NAME is optional. + @param cert_data [out] The certificate as a sequence of bytes. + @return < 0 if an error, or the size of the certificate in bytes. + @note cert_data must be freed when there is no more need for it. +*/ EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data); #endif /** - * @brief Return the axTLS library version as a string. - */ + @brief Return the axTLS library version as a string. +*/ EXP_FUNC const char * STDCALL ssl_version(void); /** @} */ diff --git a/libraries/ESP8266WiFi/src/include/wl_definitions.h b/libraries/ESP8266WiFi/src/include/wl_definitions.h index 5c8c536602..371ca99aa1 100644 --- a/libraries/ESP8266WiFi/src/include/wl_definitions.h +++ b/libraries/ESP8266WiFi/src/include/wl_definitions.h @@ -1,27 +1,27 @@ /* - wl_definitions.h - Library for Arduino Wifi shield. - Copyright (c) 2011-2014 Arduino. All right reserved. + wl_definitions.h - Library for Arduino Wifi shield. + Copyright (c) 2011-2014 Arduino. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* - * wl_definitions.h - * - * Created on: Mar 6, 2011 - * Author: dlafauci - */ + wl_definitions.h + + Created on: Mar 6, 2011 + Author: dlafauci +*/ #ifndef WL_DEFINITIONS_H_ #define WL_DEFINITIONS_H_ @@ -47,7 +47,8 @@ //Maximum number of attempts to establish wifi connection #define WL_MAX_ATTEMPT_CONNECTION 10 -typedef enum { +typedef enum +{ WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library WL_IDLE_STATUS = 0, WL_NO_SSID_AVAIL = 1, @@ -59,28 +60,30 @@ typedef enum { } wl_status_t; /* Encryption modes */ -enum wl_enc_type { /* Values map to 802.11 encryption suites... */ - ENC_TYPE_WEP = 5, - ENC_TYPE_TKIP = 2, - ENC_TYPE_CCMP = 4, - /* ... except these two, 7 and 8 are reserved in 802.11-2007 */ - ENC_TYPE_NONE = 7, - ENC_TYPE_AUTO = 8 +enum wl_enc_type /* Values map to 802.11 encryption suites... */ +{ + ENC_TYPE_WEP = 5, + ENC_TYPE_TKIP = 2, + ENC_TYPE_CCMP = 4, + /* ... except these two, 7 and 8 are reserved in 802.11-2007 */ + ENC_TYPE_NONE = 7, + ENC_TYPE_AUTO = 8 }; #if !defined(LWIP_INTERNAL) && !defined(__LWIP_TCP_H__) -enum wl_tcp_state { - CLOSED = 0, - LISTEN = 1, - SYN_SENT = 2, - SYN_RCVD = 3, - ESTABLISHED = 4, - FIN_WAIT_1 = 5, - FIN_WAIT_2 = 6, - CLOSE_WAIT = 7, - CLOSING = 8, - LAST_ACK = 9, - TIME_WAIT = 10 +enum wl_tcp_state +{ + CLOSED = 0, + LISTEN = 1, + SYN_SENT = 2, + SYN_RCVD = 3, + ESTABLISHED = 4, + FIN_WAIT_1 = 5, + FIN_WAIT_2 = 6, + CLOSE_WAIT = 7, + CLOSING = 8, + LAST_ACK = 9, + TIME_WAIT = 10 }; #endif diff --git a/libraries/ESP8266WiFiMesh/src/CompatibilityLayer.cpp b/libraries/ESP8266WiFiMesh/src/CompatibilityLayer.cpp index fd926a9351..95b42354f3 100644 --- a/libraries/ESP8266WiFiMesh/src/CompatibilityLayer.cpp +++ b/libraries/ESP8266WiFiMesh/src/CompatibilityLayer.cpp @@ -1,22 +1,22 @@ /* - Old version of ESP8266WiFiMesh.cpp - Mesh network node - Sets up a Mesh Node which acts as a router, creating a Mesh Network with other nodes. All information - is passed in both directions, but it is up to the user what the data sent is and how it is dealt with. - - Copyright (c) 2015 Julian Fell. All rights reserved. - Updated 2018 by Anders Löfgren. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Old version of ESP8266WiFiMesh.cpp - Mesh network node + Sets up a Mesh Node which acts as a router, creating a Mesh Network with other nodes. All information + is passed in both directions, but it is up to the user what the data sent is and how it is dealt with. + + Copyright (c) 2015 Julian Fell. All rights reserved. + Updated 2018 by Anders Löfgren. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,13 +25,13 @@ /******************************************************************************************** -* NOTE! -* -* All method signatures in this file are deprecated and will be removed in core version 2.5.0. -* If you are still using these methods, please consider migrating to the new API shown in -* the ESP8266WiFiMesh.h source file. -* -* TODO: delete this file. + NOTE! + + All method signatures in this file are deprecated and will be removed in core version 2.5.0. + If you are still using these methods, please consider migrating to the new API shown in + the ESP8266WiFiMesh.h source file. + + TODO: delete this file. ********************************************************************************************/ @@ -40,7 +40,7 @@ #include #include -#include +#include #include #include "ESP8266WiFiMesh.h" @@ -51,131 +51,149 @@ // DEPRECATED! ESP8266WiFiMesh::ESP8266WiFiMesh(uint32_t chipID, ESP8266WiFiMesh::compatibilityLayerHandlerType handler) -: _server(SERVER_PORT) + : _server(SERVER_PORT) { - _chipID = chipID; - _SSID = String( String( SSID_PREFIX ) + String( _chipID ) ); - _ssidPrefix = String( SSID_PREFIX ); - _handler = handler; + _chipID = chipID; + _SSID = String(String(SSID_PREFIX) + String(_chipID)); + _ssidPrefix = String(SSID_PREFIX); + _handler = handler; } /** - * Wait for a WiFiClient to connect - * - * @returns: True if the client is ready, false otherwise. - * - */ + Wait for a WiFiClient to connect + + @returns: True if the client is ready, false otherwise. + +*/ // DEPRECATED! bool ESP8266WiFiMesh::waitForClient(WiFiClient &currClient, int maxWait) { - int wait = maxWait; - while(currClient.connected() && !currClient.available() && wait--) - delay(3); - - /* Return false if the client isn't ready to communicate */ - if (WiFi.status() == WL_DISCONNECTED || !currClient.connected()) - return false; - - return true; + int wait = maxWait; + while (currClient.connected() && !currClient.available() && wait--) + { + delay(3); + } + + /* Return false if the client isn't ready to communicate */ + if (WiFi.status() == WL_DISCONNECTED || !currClient.connected()) + { + return false; + } + + return true; } /** - * Send the supplied message then read back the other node's response - * and pass that to the user-supplied handler. - * - * @message The string to send to the node. - * @returns: True if the exchange was a succes, false otherwise. - * - */ + Send the supplied message then read back the other node's response + and pass that to the user-supplied handler. + + @message The string to send to the node. + @returns: True if the exchange was a succes, false otherwise. + +*/ // DEPRECATED! bool ESP8266WiFiMesh::exchangeInfo(const char *message, WiFiClient &currClient) { - currClient.println( message ); + currClient.println(message); - if (!waitForClient(currClient, 1000)) - return false; + if (!waitForClient(currClient, 1000)) + { + return false; + } - String response = currClient.readStringUntil('\r'); - currClient.readStringUntil('\n'); + String response = currClient.readStringUntil('\r'); + currClient.readStringUntil('\n'); - if (response.length() <= 2) - return false; + if (response.length() <= 2) + { + return false; + } - /* Pass data to user callback */ - _handler(response); - return true; + /* Pass data to user callback */ + _handler(response); + return true; } /** - * Connect to the AP at ssid, send them a message then disconnect. - * - * @targetSSID The name of the AP the other node has set up. - * @message The string to send to the node. - * - */ + Connect to the AP at ssid, send them a message then disconnect. + + @targetSSID The name of the AP the other node has set up. + @message The string to send to the node. + +*/ // DEPRECATED! void ESP8266WiFiMesh::connectToNode(const String &targetSSID, const char *message) { - WiFiClient currClient; - WiFi.begin( targetSSID.c_str() ); - - int wait = 1500; - while((WiFi.status() == WL_DISCONNECTED) && wait--) - delay(3); - - /* If the connection timed out */ - if (WiFi.status() != 3) - return; - - /* Connect to the node's server */ - if (!currClient.connect(SERVER_IP_ADDR, SERVER_PORT)) - return; - - if (!exchangeInfo(message, currClient)) - return; - - currClient.stop(); - WiFi.disconnect(); + WiFiClient currClient; + WiFi.begin(targetSSID.c_str()); + + int wait = 1500; + while ((WiFi.status() == WL_DISCONNECTED) && wait--) + { + delay(3); + } + + /* If the connection timed out */ + if (WiFi.status() != 3) + { + return; + } + + /* Connect to the node's server */ + if (!currClient.connect(SERVER_IP_ADDR, SERVER_PORT)) + { + return; + } + + if (!exchangeInfo(message, currClient)) + { + return; + } + + currClient.stop(); + WiFi.disconnect(); } // DEPRECATED! void ESP8266WiFiMesh::attemptScanKernel(const char *message) { - /* Scan for APs */ - int n = WiFi.scanNetworks(); - - for (int i = 0; i < n; ++i) { - String currentSSID = WiFi.SSID(i); - int index = currentSSID.indexOf( _ssidPrefix ); - uint32_t targetChipID = (currentSSID.substring(index + _ssidPrefix.length())).toInt(); - - /* Connect to any _suitable_ APs which contain _ssidPrefix */ - if (index >= 0 && (targetChipID < _chipID)) { - - WiFi.mode(WIFI_STA); - delay(100); - connectToNode(currentSSID, message); - WiFi.mode(WIFI_AP_STA); - delay(100); - } - } + /* Scan for APs */ + int n = WiFi.scanNetworks(); + + for (int i = 0; i < n; ++i) + { + String currentSSID = WiFi.SSID(i); + int index = currentSSID.indexOf(_ssidPrefix); + uint32_t targetChipID = (currentSSID.substring(index + _ssidPrefix.length())).toInt(); + + /* Connect to any _suitable_ APs which contain _ssidPrefix */ + if (index >= 0 && (targetChipID < _chipID)) + { + + WiFi.mode(WIFI_STA); + delay(100); + connectToNode(currentSSID, message); + WiFi.mode(WIFI_AP_STA); + delay(100); + } + } } // DEPRECATED! void ESP8266WiFiMesh::attemptScan(const String &message) { - attemptScanKernel(message.c_str()); + attemptScanKernel(message.c_str()); } // DEPRECATED! void ESP8266WiFiMesh::attemptScan(char *message) { - attemptScanKernel(message); + attemptScanKernel(message); } // DEPRECATED! template void ESP8266WiFiMesh::attemptScan(char (&message)[Size]) { - attemptScanKernel(message); + attemptScanKernel(message); } \ No newline at end of file diff --git a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp index fead562e6a..9c530bd6df 100644 --- a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp +++ b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp @@ -1,25 +1,25 @@ /* - ESP8266WiFiMesh.cpp - Mesh network node - Sets up a Mesh Node which acts as a router, creating a Mesh Network with other nodes. - - Copyright (c) 2015 Julian Fell. All rights reserved. - Updated 2018 by Anders Löfgren. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ESP8266WiFiMesh.cpp - Mesh network node + Sets up a Mesh Node which acts as a router, creating a Mesh Network with other nodes. + + Copyright (c) 2015 Julian Fell. All rights reserved. + Updated 2018 by Anders Löfgren. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include -#include +#include #include #include @@ -29,15 +29,15 @@ #define SERVER_IP_ADDR "192.168.4.1" const IPAddress ESP8266WiFiMesh::emptyIP = IPAddress(); -const uint32_t ESP8266WiFiMesh::lwipVersion203Signature[3] {2,0,3}; +const uint32_t ESP8266WiFiMesh::lwipVersion203Signature[3] {2, 0, 3}; String ESP8266WiFiMesh::lastSSID = ""; bool ESP8266WiFiMesh::staticIPActivated = false; // IP needs to be at the same subnet as server gateway (192.168.4 in this case). Station gateway ip must match ip for server. IPAddress ESP8266WiFiMesh::staticIP = emptyIP; -IPAddress ESP8266WiFiMesh::gateway = IPAddress(192,168,4,1); -IPAddress ESP8266WiFiMesh::subnetMask = IPAddress(255,255,255,0); +IPAddress ESP8266WiFiMesh::gateway = IPAddress(192, 168, 4, 1); +IPAddress ESP8266WiFiMesh::subnetMask = IPAddress(255, 255, 255, 0); ESP8266WiFiMesh *ESP8266WiFiMesh::apController = nullptr; std::vector ESP8266WiFiMesh::connectionQueue = {}; @@ -45,627 +45,716 @@ std::vector ESP8266WiFiMesh::latestTransmissionOutcomes = {} ESP8266WiFiMesh::~ESP8266WiFiMesh() { - deactivateAP(); + deactivateAP(); } -ESP8266WiFiMesh::ESP8266WiFiMesh(ESP8266WiFiMesh::requestHandlerType requestHandler, ESP8266WiFiMesh::responseHandlerType responseHandler, - ESP8266WiFiMesh::networkFilterType networkFilter, const String &meshPassword, const String &meshName, - const String &nodeID, bool verboseMode, uint8 meshWiFiChannel, uint16_t serverPort) - : _server(serverPort), _lwipVersion{0, 0, 0} +ESP8266WiFiMesh::ESP8266WiFiMesh(ESP8266WiFiMesh::requestHandlerType requestHandler, ESP8266WiFiMesh::responseHandlerType responseHandler, + ESP8266WiFiMesh::networkFilterType networkFilter, const String &meshPassword, const String &meshName, + const String &nodeID, bool verboseMode, uint8 meshWiFiChannel, uint16_t serverPort) + : _server(serverPort), _lwipVersion{0, 0, 0} { - storeLwipVersion(); - - updateNetworkNames(meshName, (nodeID != "" ? nodeID : uint64ToString(ESP.getChipId()))); - _requestHandler = requestHandler; - _responseHandler = responseHandler; - setWiFiChannel(meshWiFiChannel); - _serverPort = serverPort; - _meshPassword = meshPassword; - _verboseMode = verboseMode; - _networkFilter = networkFilter; + storeLwipVersion(); + + updateNetworkNames(meshName, (nodeID != "" ? nodeID : uint64ToString(ESP.getChipId()))); + _requestHandler = requestHandler; + _responseHandler = responseHandler; + setWiFiChannel(meshWiFiChannel); + _serverPort = serverPort; + _meshPassword = meshPassword; + _verboseMode = verboseMode; + _networkFilter = networkFilter; } void ESP8266WiFiMesh::updateNetworkNames(const String &newMeshName, const String &newNodeID) { - if(newMeshName != "") - _meshName = newMeshName; - if(newNodeID != "") - _nodeID = newNodeID; + if (newMeshName != "") + { + _meshName = newMeshName; + } + if (newNodeID != "") + { + _nodeID = newNodeID; + } + + String newSSID = _meshName + _nodeID; - String newSSID = _meshName + _nodeID; + if (_SSID != newSSID) + { + _SSID = newSSID; - if(_SSID != newSSID) - { - _SSID = newSSID; - - // Apply SSID changes to active AP. - if(isAPController()) - restartAP(); - } + // Apply SSID changes to active AP. + if (isAPController()) + { + restartAP(); + } + } } void ESP8266WiFiMesh::begin() { - //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// - if(_handler != NULL) - { - WiFi.mode(WIFI_AP_STA); - WiFi.softAP( _SSID.c_str() ); - _server.begin(); - } - else - { - //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// - if(!ESP8266WiFiMesh::getAPController()) // If there is no active AP controller - WiFi.mode(WIFI_STA); // WIFI_AP_STA mode automatically sets up an AP, so we can't use that as default. - - #ifdef ENABLE_STATIC_IP_OPTIMIZATION - if(atLeastLwipVersion(lwipVersion203Signature)) + //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// + if (_handler != NULL) { - verboseModePrint(F("lwIP version is at least 2.0.3. Static ip optimizations enabled.\n")); + WiFi.mode(WIFI_AP_STA); + WiFi.softAP(_SSID.c_str()); + _server.begin(); } else { - verboseModePrint(F("lwIP version is less than 2.0.3. Static ip optimizations DISABLED.\n")); + //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// + if (!ESP8266WiFiMesh::getAPController()) // If there is no active AP controller + { + WiFi.mode(WIFI_STA); // WIFI_AP_STA mode automatically sets up an AP, so we can't use that as default. + } + +#ifdef ENABLE_STATIC_IP_OPTIMIZATION + if (atLeastLwipVersion(lwipVersion203Signature)) + { + verboseModePrint(F("lwIP version is at least 2.0.3. Static ip optimizations enabled.\n")); + } + else + { + verboseModePrint(F("lwIP version is less than 2.0.3. Static ip optimizations DISABLED.\n")); + } +#endif } - #endif - } } void ESP8266WiFiMesh::setStaticIP(const IPAddress &newIP) { - // Comment out the line below to remove static IP and use DHCP instead. - // DHCP makes WiFi connection happen slower, but there is no need to care about manually giving different IPs to the nodes and less need to worry about used IPs giving "Server unavailable" issues. - // Static IP has faster connection times (50 % of DHCP) and will make sending of data to a node that is already transmitting data happen more reliably. - // Note that after WiFi.config(staticIP, gateway, subnetMask) is used, static IP will always be active, even for new connections, unless WiFi.config(0u,0u,0u); is called. - WiFi.config(newIP, gateway, subnetMask); - staticIPActivated = true; - staticIP = newIP; + // Comment out the line below to remove static IP and use DHCP instead. + // DHCP makes WiFi connection happen slower, but there is no need to care about manually giving different IPs to the nodes and less need to worry about used IPs giving "Server unavailable" issues. + // Static IP has faster connection times (50 % of DHCP) and will make sending of data to a node that is already transmitting data happen more reliably. + // Note that after WiFi.config(staticIP, gateway, subnetMask) is used, static IP will always be active, even for new connections, unless WiFi.config(0u,0u,0u); is called. + WiFi.config(newIP, gateway, subnetMask); + staticIPActivated = true; + staticIP = newIP; } IPAddress ESP8266WiFiMesh::getStaticIP() { - if(staticIPActivated) - return staticIP; + if (staticIPActivated) + { + return staticIP; + } - return emptyIP; + return emptyIP; } void ESP8266WiFiMesh::disableStaticIP() { - WiFi.config(0u,0u,0u); - yield(); - staticIPActivated = false; + WiFi.config(0u, 0u, 0u); + yield(); + staticIPActivated = false; } void ESP8266WiFiMesh::activateAP() { - // Deactivate active AP to avoid two servers using the same port, which can lead to crashes. - if(ESP8266WiFiMesh *currentAPController = ESP8266WiFiMesh::getAPController()) - currentAPController->deactivateAP(); + // Deactivate active AP to avoid two servers using the same port, which can lead to crashes. + if (ESP8266WiFiMesh *currentAPController = ESP8266WiFiMesh::getAPController()) + { + currentAPController->deactivateAP(); + } - WiFi.softAP( _SSID.c_str(), _meshPassword.c_str(), _meshWiFiChannel, _apHidden, _maxAPStations ); // Note that a maximum of 8 stations can be connected at a time to each AP - WiFi.mode(WIFI_AP_STA); + WiFi.softAP(_SSID.c_str(), _meshPassword.c_str(), _meshWiFiChannel, _apHidden, _maxAPStations); // Note that a maximum of 8 stations can be connected at a time to each AP + WiFi.mode(WIFI_AP_STA); - _server = WiFiServer(_serverPort); // Fixes an occasional crash bug that occurs when using the copy constructor to duplicate the AP controller. - _server.begin(); // Actually calls _server.stop()/_server.close() first. + _server = WiFiServer(_serverPort); // Fixes an occasional crash bug that occurs when using the copy constructor to duplicate the AP controller. + _server.begin(); // Actually calls _server.stop()/_server.close() first. - apController = this; + apController = this; } void ESP8266WiFiMesh::deactivateAP() { - if(isAPController()) - { - _server.stop(); - WiFi.softAPdisconnect(); - WiFi.mode(WIFI_STA); + if (isAPController()) + { + _server.stop(); + WiFi.softAPdisconnect(); + WiFi.mode(WIFI_STA); - // Since there is no active AP controller now, make the apController variable point to nothing. - apController = nullptr; - } + // Since there is no active AP controller now, make the apController variable point to nothing. + apController = nullptr; + } } void ESP8266WiFiMesh::restartAP() { - deactivateAP(); - yield(); - activateAP(); - yield(); + deactivateAP(); + yield(); + activateAP(); + yield(); } ESP8266WiFiMesh * ESP8266WiFiMesh::getAPController() { - return apController; + return apController; } bool ESP8266WiFiMesh::isAPController() { - return (this == apController); + return (this == apController); } void ESP8266WiFiMesh::setWiFiChannel(uint8 newWiFiChannel) { - assert(1 <= newWiFiChannel && newWiFiChannel <= 13); - - _meshWiFiChannel = newWiFiChannel; + assert(1 <= newWiFiChannel && newWiFiChannel <= 13); - // WiFi.channel() will change if this node connects to an AP with another channel, - // so there is no guarantee we are using _meshWiFiChannel. - // Also, we cannot change the WiFi channel while we are still connected to the other AP. - if(WiFi.channel() != _meshWiFiChannel && WiFi.status() != WL_CONNECTED) - { - // Apply changes to active AP. - if(isAPController()) - restartAP(); - } + _meshWiFiChannel = newWiFiChannel; + + // WiFi.channel() will change if this node connects to an AP with another channel, + // so there is no guarantee we are using _meshWiFiChannel. + // Also, we cannot change the WiFi channel while we are still connected to the other AP. + if (WiFi.channel() != _meshWiFiChannel && WiFi.status() != WL_CONNECTED) + { + // Apply changes to active AP. + if (isAPController()) + { + restartAP(); + } + } } uint8 ESP8266WiFiMesh::getWiFiChannel() { - return _meshWiFiChannel; + return _meshWiFiChannel; } void ESP8266WiFiMesh::setMeshName(const String &newMeshName) { - updateNetworkNames(newMeshName); + updateNetworkNames(newMeshName); } -String ESP8266WiFiMesh::getMeshName() {return _meshName;} +String ESP8266WiFiMesh::getMeshName() +{ + return _meshName; +} void ESP8266WiFiMesh::setNodeID(const String &newNodeID) { - updateNetworkNames("", newNodeID); + updateNetworkNames("", newNodeID); } -String ESP8266WiFiMesh::getNodeID() {return _nodeID;} +String ESP8266WiFiMesh::getNodeID() +{ + return _nodeID; +} void ESP8266WiFiMesh::setSSID(const String &newMeshName, const String &newNodeID) { - updateNetworkNames(newMeshName, newNodeID); + updateNetworkNames(newMeshName, newNodeID); } -String ESP8266WiFiMesh::getSSID() {return _SSID;} +String ESP8266WiFiMesh::getSSID() +{ + return _SSID; +} -void ESP8266WiFiMesh::setMessage(const String &newMessage) {_message = newMessage;} -String ESP8266WiFiMesh::getMessage() {return _message;} +void ESP8266WiFiMesh::setMessage(const String &newMessage) +{ + _message = newMessage; +} +String ESP8266WiFiMesh::getMessage() +{ + return _message; +} -void ESP8266WiFiMesh::setRequestHandler(ESP8266WiFiMesh::requestHandlerType requestHandler) {_requestHandler = requestHandler;} -ESP8266WiFiMesh::requestHandlerType ESP8266WiFiMesh::getRequestHandler() {return _requestHandler;} +void ESP8266WiFiMesh::setRequestHandler(ESP8266WiFiMesh::requestHandlerType requestHandler) +{ + _requestHandler = requestHandler; +} +ESP8266WiFiMesh::requestHandlerType ESP8266WiFiMesh::getRequestHandler() +{ + return _requestHandler; +} -void ESP8266WiFiMesh::setResponseHandler(ESP8266WiFiMesh::responseHandlerType responseHandler) {_responseHandler = responseHandler;} -ESP8266WiFiMesh::responseHandlerType ESP8266WiFiMesh::getResponseHandler() {return _responseHandler;} +void ESP8266WiFiMesh::setResponseHandler(ESP8266WiFiMesh::responseHandlerType responseHandler) +{ + _responseHandler = responseHandler; +} +ESP8266WiFiMesh::responseHandlerType ESP8266WiFiMesh::getResponseHandler() +{ + return _responseHandler; +} -void ESP8266WiFiMesh::setNetworkFilter(ESP8266WiFiMesh::networkFilterType networkFilter) {_networkFilter = networkFilter;} -ESP8266WiFiMesh::networkFilterType ESP8266WiFiMesh::getNetworkFilter() {return _networkFilter;} +void ESP8266WiFiMesh::setNetworkFilter(ESP8266WiFiMesh::networkFilterType networkFilter) +{ + _networkFilter = networkFilter; +} +ESP8266WiFiMesh::networkFilterType ESP8266WiFiMesh::getNetworkFilter() +{ + return _networkFilter; +} void ESP8266WiFiMesh::setScanHidden(bool scanHidden) { - _scanHidden = scanHidden; + _scanHidden = scanHidden; } -bool ESP8266WiFiMesh::getScanHidden() {return _scanHidden;} +bool ESP8266WiFiMesh::getScanHidden() +{ + return _scanHidden; +} void ESP8266WiFiMesh::setAPHidden(bool apHidden) { - if(_apHidden != apHidden) - { - _apHidden = apHidden; - - // Apply changes to active AP. - if(isAPController()) - restartAP(); - } + if (_apHidden != apHidden) + { + _apHidden = apHidden; + + // Apply changes to active AP. + if (isAPController()) + { + restartAP(); + } + } } -bool ESP8266WiFiMesh::getAPHidden() {return _apHidden;} +bool ESP8266WiFiMesh::getAPHidden() +{ + return _apHidden; +} void ESP8266WiFiMesh::setMaxAPStations(uint8_t maxAPStations) { - assert(maxAPStations <= 8); // Valid values are 0 to 8, but uint8_t is always at least 0. - - if(_maxAPStations != maxAPStations) - { - _maxAPStations = maxAPStations; - - // Apply changes to active AP. - if(isAPController()) - restartAP(); - } + assert(maxAPStations <= 8); // Valid values are 0 to 8, but uint8_t is always at least 0. + + if (_maxAPStations != maxAPStations) + { + _maxAPStations = maxAPStations; + + // Apply changes to active AP. + if (isAPController()) + { + restartAP(); + } + } } -bool ESP8266WiFiMesh::getMaxAPStations() {return _maxAPStations;} +bool ESP8266WiFiMesh::getMaxAPStations() +{ + return _maxAPStations; +} void ESP8266WiFiMesh::setConnectionAttemptTimeout(int32_t connectionAttemptTimeoutMs) { - _connectionAttemptTimeoutMs = connectionAttemptTimeoutMs; + _connectionAttemptTimeoutMs = connectionAttemptTimeoutMs; } -int32_t ESP8266WiFiMesh::getConnectionAttemptTimeout() {return _connectionAttemptTimeoutMs;} +int32_t ESP8266WiFiMesh::getConnectionAttemptTimeout() +{ + return _connectionAttemptTimeoutMs; +} void ESP8266WiFiMesh::setStationModeTimeout(int stationModeTimeoutMs) { - _stationModeTimeoutMs = stationModeTimeoutMs; + _stationModeTimeoutMs = stationModeTimeoutMs; } -int ESP8266WiFiMesh::getStationModeTimeout() {return _stationModeTimeoutMs;} +int ESP8266WiFiMesh::getStationModeTimeout() +{ + return _stationModeTimeoutMs; +} void ESP8266WiFiMesh::setAPModeTimeout(uint32_t apModeTimeoutMs) { - _apModeTimeoutMs = apModeTimeoutMs; + _apModeTimeoutMs = apModeTimeoutMs; } -uint32_t ESP8266WiFiMesh::getAPModeTimeout() {return _apModeTimeoutMs;} +uint32_t ESP8266WiFiMesh::getAPModeTimeout() +{ + return _apModeTimeoutMs; +} bool ESP8266WiFiMesh::latestTransmissionSuccessful() { - if(ESP8266WiFiMesh::latestTransmissionOutcomes.empty()) - return false; - else - for(TransmissionResult &transmissionResult : ESP8266WiFiMesh::latestTransmissionOutcomes) - if(transmissionResult.transmissionStatus != TS_TRANSMISSION_COMPLETE) + if (ESP8266WiFiMesh::latestTransmissionOutcomes.empty()) + { return false; + } + else + for (TransmissionResult &transmissionResult : ESP8266WiFiMesh::latestTransmissionOutcomes) + if (transmissionResult.transmissionStatus != TS_TRANSMISSION_COMPLETE) + { + return false; + } - return true; + return true; } /** - * Disconnect completely from a network. - */ + Disconnect completely from a network. +*/ void ESP8266WiFiMesh::fullStop(WiFiClient &currClient) { - currClient.stop(); - yield(); - WiFi.disconnect(); - yield(); + currClient.stop(); + yield(); + WiFi.disconnect(); + yield(); } /** - * Wait for a WiFiClient to transmit - * - * @returns: True if the client is ready, false otherwise. - * - */ + Wait for a WiFiClient to transmit + + @returns: True if the client is ready, false otherwise. + +*/ bool ESP8266WiFiMesh::waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait) { - uint32_t connectionStartTime = millis(); - uint32_t waitingTime = millis() - connectionStartTime; - while(currClient.connected() && !currClient.available() && waitingTime < maxWait) - { - delay(1); - waitingTime = millis() - connectionStartTime; - } + uint32_t connectionStartTime = millis(); + uint32_t waitingTime = millis() - connectionStartTime; + while (currClient.connected() && !currClient.available() && waitingTime < maxWait) + { + delay(1); + waitingTime = millis() - connectionStartTime; + } + + /* Return false if the client isn't ready to communicate */ + if (WiFi.status() == WL_DISCONNECTED && !currClient.available()) + { + verboseModePrint(F("Disconnected!")); + return false; + } - /* Return false if the client isn't ready to communicate */ - if (WiFi.status() == WL_DISCONNECTED && !currClient.available()) - { - verboseModePrint(F("Disconnected!")); - return false; - } - - return true; + return true; } /** - * Send the mesh instance's current message then read back the other node's response - * and pass that to the user-supplied responseHandler. - * - * @param currClient The client to which the message should be transmitted. - * @returns: A status code based on the outcome of the exchange. - * - */ + Send the mesh instance's current message then read back the other node's response + and pass that to the user-supplied responseHandler. + + @param currClient The client to which the message should be transmitted. + @returns: A status code based on the outcome of the exchange. + +*/ transmission_status_t ESP8266WiFiMesh::exchangeInfo(WiFiClient &currClient) { - verboseModePrint("Transmitting"); // Not storing strings in flash (via F()) to avoid performance impacts when using the string. - - currClient.print(getMessage() + "\r"); - yield(); + verboseModePrint("Transmitting"); // Not storing strings in flash (via F()) to avoid performance impacts when using the string. - if (!waitForClientTransmission(currClient, _stationModeTimeoutMs)) - { - fullStop(currClient); - return TS_CONNECTION_FAILED; - } + currClient.print(getMessage() + "\r"); + yield(); - if (!currClient.available()) - { - verboseModePrint(F("No response!")); - return TS_TRANSMISSION_FAILED; // WiFi.status() != WL_DISCONNECTED so we do not want to use fullStop(currClient) here since that would force the node to scan for WiFi networks. - } + if (!waitForClientTransmission(currClient, _stationModeTimeoutMs)) + { + fullStop(currClient); + return TS_CONNECTION_FAILED; + } - String response = currClient.readStringUntil('\r'); - yield(); - currClient.flush(); + if (!currClient.available()) + { + verboseModePrint(F("No response!")); + return TS_TRANSMISSION_FAILED; // WiFi.status() != WL_DISCONNECTED so we do not want to use fullStop(currClient) here since that would force the node to scan for WiFi networks. + } + + String response = currClient.readStringUntil('\r'); + yield(); + currClient.flush(); - /* Pass data to user callback */ - return _responseHandler(response, *this); + /* Pass data to user callback */ + return _responseHandler(response, *this); } /** - * Handle data transfer process with a connected AP. - * - * @returns: A status code based on the outcome of the data transfer attempt. - */ + Handle data transfer process with a connected AP. + + @returns: A status code based on the outcome of the data transfer attempt. +*/ transmission_status_t ESP8266WiFiMesh::attemptDataTransfer() { - // Unlike WiFi.mode(WIFI_AP);, WiFi.mode(WIFI_AP_STA); allows us to stay connected to the AP we connected to in STA mode, at the same time as we can receive connections from other stations. - // We cannot send data to the AP in STA_AP mode though, that requires STA mode. - // Switching to STA mode will disconnect all stations connected to the node AP (though they can request a reconnect even while we are in STA mode). - WiFiMode_t storedWiFiMode = WiFi.getMode(); - WiFi.mode(WIFI_STA); - delay(1); - transmission_status_t transmissionOutcome = attemptDataTransferKernel(); - WiFi.mode(storedWiFiMode); - delay(1); - - return transmissionOutcome; + // Unlike WiFi.mode(WIFI_AP);, WiFi.mode(WIFI_AP_STA); allows us to stay connected to the AP we connected to in STA mode, at the same time as we can receive connections from other stations. + // We cannot send data to the AP in STA_AP mode though, that requires STA mode. + // Switching to STA mode will disconnect all stations connected to the node AP (though they can request a reconnect even while we are in STA mode). + WiFiMode_t storedWiFiMode = WiFi.getMode(); + WiFi.mode(WIFI_STA); + delay(1); + transmission_status_t transmissionOutcome = attemptDataTransferKernel(); + WiFi.mode(storedWiFiMode); + delay(1); + + return transmissionOutcome; } /** - * Helper function that contains the core functionality for the data transfer process with a connected AP. - * - * @returns: A status code based on the outcome of the data transfer attempt. - */ + Helper function that contains the core functionality for the data transfer process with a connected AP. + + @returns: A status code based on the outcome of the data transfer attempt. +*/ transmission_status_t ESP8266WiFiMesh::attemptDataTransferKernel() { - WiFiClient currClient; - currClient.setTimeout(_stationModeTimeoutMs); - - /* Connect to the node's server */ - if (!currClient.connect(SERVER_IP_ADDR, _serverPort)) - { - fullStop(currClient); - verboseModePrint(F("Server unavailable")); - return TS_CONNECTION_FAILED; - } - - transmission_status_t transmissionOutcome = exchangeInfo(currClient); - if (transmissionOutcome <= 0) - { - verboseModePrint(F("Transmission failed during exchangeInfo.")); - return transmissionOutcome; - } - - currClient.stop(); - yield(); + WiFiClient currClient; + currClient.setTimeout(_stationModeTimeoutMs); + + /* Connect to the node's server */ + if (!currClient.connect(SERVER_IP_ADDR, _serverPort)) + { + fullStop(currClient); + verboseModePrint(F("Server unavailable")); + return TS_CONNECTION_FAILED; + } - return transmissionOutcome; + transmission_status_t transmissionOutcome = exchangeInfo(currClient); + if (transmissionOutcome <= 0) + { + verboseModePrint(F("Transmission failed during exchangeInfo.")); + return transmissionOutcome; + } + + currClient.stop(); + yield(); + + return transmissionOutcome; } void ESP8266WiFiMesh::initiateConnectionToAP(const String &targetSSID, int targetChannel, uint8_t *targetBSSID) { - if(targetChannel == NETWORK_INFO_DEFAULT_INT) - WiFi.begin( targetSSID.c_str(), _meshPassword.c_str() ); // Without giving channel and BSSID, connection time is longer. - else if(targetBSSID == NULL) - WiFi.begin( targetSSID.c_str(), _meshPassword.c_str(), targetChannel ); // Without giving channel and BSSID, connection time is longer. - else - WiFi.begin( targetSSID.c_str(), _meshPassword.c_str(), targetChannel, targetBSSID ); + if (targetChannel == NETWORK_INFO_DEFAULT_INT) + { + WiFi.begin(targetSSID.c_str(), _meshPassword.c_str()); // Without giving channel and BSSID, connection time is longer. + } + else if (targetBSSID == NULL) + { + WiFi.begin(targetSSID.c_str(), _meshPassword.c_str(), targetChannel); // Without giving channel and BSSID, connection time is longer. + } + else + { + WiFi.begin(targetSSID.c_str(), _meshPassword.c_str(), targetChannel, targetBSSID); + } } /** - * Connect to the AP at SSID and transmit the mesh instance's current message. - * - * @param targetSSID The name of the AP the other node has set up. - * @param targetChannel The WiFI channel of the AP the other node has set up. - * @param targetBSSID The mac address of the AP the other node has set up. - * @returns: A status code based on the outcome of the connection and data transfer process. - * - */ + Connect to the AP at SSID and transmit the mesh instance's current message. + + @param targetSSID The name of the AP the other node has set up. + @param targetChannel The WiFI channel of the AP the other node has set up. + @param targetBSSID The mac address of the AP the other node has set up. + @returns: A status code based on the outcome of the connection and data transfer process. + +*/ transmission_status_t ESP8266WiFiMesh::connectToNode(const String &targetSSID, int targetChannel, uint8_t *targetBSSID) { - if(staticIPActivated && lastSSID != "" && lastSSID != targetSSID) // So we only do this once per connection, in case there is a performance impact. - { - #ifdef ENABLE_STATIC_IP_OPTIMIZATION - if(atLeastLwipVersion(lwipVersion203Signature)) + if (staticIPActivated && lastSSID != "" && lastSSID != targetSSID) // So we only do this once per connection, in case there is a performance impact. { - // Can be used with Arduino core for ESP8266 version 2.4.2 or higher with lwIP2 enabled to keep static IP on even during network switches. - WiFiMode_t storedWiFiMode = WiFi.getMode(); - WiFi.mode(WIFI_OFF); - WiFi.mode(storedWiFiMode); - yield(); +#ifdef ENABLE_STATIC_IP_OPTIMIZATION + if (atLeastLwipVersion(lwipVersion203Signature)) + { + // Can be used with Arduino core for ESP8266 version 2.4.2 or higher with lwIP2 enabled to keep static IP on even during network switches. + WiFiMode_t storedWiFiMode = WiFi.getMode(); + WiFi.mode(WIFI_OFF); + WiFi.mode(storedWiFiMode); + yield(); + } + else + { + // Disable static IP so that we can connect to other servers via DHCP (DHCP is slower but required for connecting to more than one server, it seems (possible bug?)). + disableStaticIP(); + verboseModePrint(F("\nConnecting to a different network. Static IP deactivated to make this possible.")); + } +#else + // Disable static IP so that we can connect to other servers via DHCP (DHCP is slower but required for connecting to more than one server, it seems (possible bug?)). + disableStaticIP(); + verboseModePrint(F("\nConnecting to a different network. Static IP deactivated to make this possible.")); +#endif } - else + lastSSID = targetSSID; + + verboseModePrint(F("Connecting... "), false); + initiateConnectionToAP(targetSSID, targetChannel, targetBSSID); + + int connectionStartTime = millis(); + int attemptNumber = 1; + + int waitingTime = millis() - connectionStartTime; + while ((WiFi.status() == WL_DISCONNECTED) && waitingTime <= _connectionAttemptTimeoutMs) { - // Disable static IP so that we can connect to other servers via DHCP (DHCP is slower but required for connecting to more than one server, it seems (possible bug?)). - disableStaticIP(); - verboseModePrint(F("\nConnecting to a different network. Static IP deactivated to make this possible.")); + if (waitingTime > attemptNumber * _connectionAttemptTimeoutMs) // _connectionAttemptTimeoutMs can be replaced (lowered) if you want to limit the time allowed for each connection attempt. + { + verboseModePrint(F("... "), false); + WiFi.disconnect(); + yield(); + initiateConnectionToAP(targetSSID, targetChannel, targetBSSID); + attemptNumber++; + } + delay(1); + waitingTime = millis() - connectionStartTime; } - #else - // Disable static IP so that we can connect to other servers via DHCP (DHCP is slower but required for connecting to more than one server, it seems (possible bug?)). - disableStaticIP(); - verboseModePrint(F("\nConnecting to a different network. Static IP deactivated to make this possible.")); - #endif - } - lastSSID = targetSSID; - - verboseModePrint(F("Connecting... "), false); - initiateConnectionToAP(targetSSID, targetChannel, targetBSSID); - - int connectionStartTime = millis(); - int attemptNumber = 1; - - int waitingTime = millis() - connectionStartTime; - while((WiFi.status() == WL_DISCONNECTED) && waitingTime <= _connectionAttemptTimeoutMs) - { - if(waitingTime > attemptNumber * _connectionAttemptTimeoutMs) // _connectionAttemptTimeoutMs can be replaced (lowered) if you want to limit the time allowed for each connection attempt. + + verboseModePrint(String(waitingTime)); + + /* If the connection timed out */ + if (WiFi.status() != WL_CONNECTED) { - verboseModePrint(F("... "), false); - WiFi.disconnect(); - yield(); - initiateConnectionToAP(targetSSID, targetChannel, targetBSSID); - attemptNumber++; + verboseModePrint(F("Timeout")); + return TS_CONNECTION_FAILED; } - delay(1); - waitingTime = millis() - connectionStartTime; - } - verboseModePrint(String(waitingTime)); - - /* If the connection timed out */ - if (WiFi.status() != WL_CONNECTED) - { - verboseModePrint(F("Timeout")); - return TS_CONNECTION_FAILED; - } - - return attemptDataTransfer(); + return attemptDataTransfer(); } void ESP8266WiFiMesh::attemptTransmission(const String &message, bool concludingDisconnect, bool initialDisconnect, bool noScan, bool scanAllWiFiChannels) { - setMessage(message); - - if(initialDisconnect) - { - WiFi.disconnect(); - yield(); - } - - latestTransmissionOutcomes.clear(); - - if(WiFi.status() == WL_CONNECTED) - { - transmission_status_t transmissionResult = attemptDataTransfer(); - latestTransmissionOutcomes.push_back(TransmissionResult(connectionQueue.back(), transmissionResult)); - } - else - { - if(!noScan) + setMessage(message); + + if (initialDisconnect) { - verboseModePrint(F("Scanning... "), false); - - /* Scan for APs */ - connectionQueue.clear(); - - // If scanAllWiFiChannels is true or Arduino core for ESP8266 version < 2.4.2 scanning will cause the WiFi radio to cycle through all WiFi channels. - // This means existing WiFi connections are likely to break or work poorly if done frequently. - int n = 0; - #ifdef ENABLE_WIFI_SCAN_OPTIMIZATION - if(scanAllWiFiChannels) - { - n = WiFi.scanNetworks(false, _scanHidden); - } - else - { - // Scan function argument overview: scanNetworks(bool async = false, bool show_hidden = false, uint8 channel = 0, uint8* ssid = NULL) - n = WiFi.scanNetworks(false, _scanHidden, _meshWiFiChannel); - } - #else - n = WiFi.scanNetworks(false, _scanHidden); - #endif - - _networkFilter(n, *this); // Update the connectionQueue. + WiFi.disconnect(); + yield(); } - - for(NetworkInfo ¤tNetwork : connectionQueue) + + latestTransmissionOutcomes.clear(); + + if (WiFi.status() == WL_CONNECTED) { - WiFi.disconnect(); - yield(); - - String currentSSID = ""; - int currentWiFiChannel = NETWORK_INFO_DEFAULT_INT; - uint8_t *currentBSSID = NULL; - - // If an SSID has been assigned, it is prioritized over an assigned networkIndex since the networkIndex is more likely to change. - if(currentNetwork.SSID != "") - { - currentSSID = currentNetwork.SSID; - currentWiFiChannel = currentNetwork.wifiChannel; - currentBSSID = currentNetwork.BSSID; - } - else // Use only networkIndex - { - currentSSID = WiFi.SSID(currentNetwork.networkIndex); - currentWiFiChannel = WiFi.channel(currentNetwork.networkIndex); - currentBSSID = WiFi.BSSID(currentNetwork.networkIndex); - } - - if(_verboseMode) // Avoid string generation if not required - { - verboseModePrint(String(F("AP acquired: ")) + currentSSID + String(F(", Ch:")) + String(currentWiFiChannel) + " ", false); - - if(currentNetwork.networkIndex != NETWORK_INFO_DEFAULT_INT) + transmission_status_t transmissionResult = attemptDataTransfer(); + latestTransmissionOutcomes.push_back(TransmissionResult(connectionQueue.back(), transmissionResult)); + } + else + { + if (!noScan) { - verboseModePrint("(" + String(WiFi.RSSI(currentNetwork.networkIndex)) + String(F("dBm) ")) + - (WiFi.encryptionType(currentNetwork.networkIndex) == ENC_TYPE_NONE ? String(F("open")) : ""), false); + verboseModePrint(F("Scanning... "), false); + + /* Scan for APs */ + connectionQueue.clear(); + + // If scanAllWiFiChannels is true or Arduino core for ESP8266 version < 2.4.2 scanning will cause the WiFi radio to cycle through all WiFi channels. + // This means existing WiFi connections are likely to break or work poorly if done frequently. + int n = 0; +#ifdef ENABLE_WIFI_SCAN_OPTIMIZATION + if (scanAllWiFiChannels) + { + n = WiFi.scanNetworks(false, _scanHidden); + } + else + { + // Scan function argument overview: scanNetworks(bool async = false, bool show_hidden = false, uint8 channel = 0, uint8* ssid = NULL) + n = WiFi.scanNetworks(false, _scanHidden, _meshWiFiChannel); + } +#else + n = WiFi.scanNetworks(false, _scanHidden); +#endif + + _networkFilter(n, *this); // Update the connectionQueue. } - verboseModePrint(F("... "), false); - } - - transmission_status_t transmissionResult = connectToNode(currentSSID, currentWiFiChannel, currentBSSID); - - latestTransmissionOutcomes.push_back(TransmissionResult{.origin = currentNetwork, .transmissionStatus = transmissionResult}); + for (NetworkInfo ¤tNetwork : connectionQueue) + { + WiFi.disconnect(); + yield(); + + String currentSSID = ""; + int currentWiFiChannel = NETWORK_INFO_DEFAULT_INT; + uint8_t *currentBSSID = NULL; + + // If an SSID has been assigned, it is prioritized over an assigned networkIndex since the networkIndex is more likely to change. + if (currentNetwork.SSID != "") + { + currentSSID = currentNetwork.SSID; + currentWiFiChannel = currentNetwork.wifiChannel; + currentBSSID = currentNetwork.BSSID; + } + else // Use only networkIndex + { + currentSSID = WiFi.SSID(currentNetwork.networkIndex); + currentWiFiChannel = WiFi.channel(currentNetwork.networkIndex); + currentBSSID = WiFi.BSSID(currentNetwork.networkIndex); + } + + if (_verboseMode) // Avoid string generation if not required + { + verboseModePrint(String(F("AP acquired: ")) + currentSSID + String(F(", Ch:")) + String(currentWiFiChannel) + " ", false); + + if (currentNetwork.networkIndex != NETWORK_INFO_DEFAULT_INT) + { + verboseModePrint("(" + String(WiFi.RSSI(currentNetwork.networkIndex)) + String(F("dBm) ")) + + (WiFi.encryptionType(currentNetwork.networkIndex) == ENC_TYPE_NONE ? String(F("open")) : ""), false); + } + + verboseModePrint(F("... "), false); + } + + transmission_status_t transmissionResult = connectToNode(currentSSID, currentWiFiChannel, currentBSSID); + + latestTransmissionOutcomes.push_back(TransmissionResult{.origin = currentNetwork, .transmissionStatus = transmissionResult}); + } } - } - if(WiFi.status() == WL_CONNECTED && staticIP != emptyIP && !staticIPActivated) - { - verboseModePrint(F("Reactivating static IP to allow for faster re-connects.")); - setStaticIP(staticIP); - } + if (WiFi.status() == WL_CONNECTED && staticIP != emptyIP && !staticIPActivated) + { + verboseModePrint(F("Reactivating static IP to allow for faster re-connects.")); + setStaticIP(staticIP); + } - // If we do not want to be connected at end of transmission, disconnect here so we can re-enable static IP first (above). - if(concludingDisconnect) - { - WiFi.disconnect(); - yield(); - } + // If we do not want to be connected at end of transmission, disconnect here so we can re-enable static IP first (above). + if (concludingDisconnect) + { + WiFi.disconnect(); + yield(); + } } void ESP8266WiFiMesh::acceptRequest() { - //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// - if(_handler != NULL) - { - while (true) { - _client = _server.available(); - if (!_client) - break; - - if (!waitForClient(_client, _apModeTimeoutMs)) { - continue; - } - - /* Read in request and pass it to the supplied handler */ - String request = _client.readStringUntil('\r'); - _client.readStringUntil('\n'); - - String response = _handler(request); - - /* Send the response back to the client */ - if (_client.connected()) - _client.println(response); + //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// + if (_handler != NULL) + { + while (true) + { + _client = _server.available(); + if (!_client) + { + break; + } + + if (!waitForClient(_client, _apModeTimeoutMs)) + { + continue; + } + + /* Read in request and pass it to the supplied handler */ + String request = _client.readStringUntil('\r'); + _client.readStringUntil('\n'); + + String response = _handler(request); + + /* Send the response back to the client */ + if (_client.connected()) + { + _client.println(response); + } + } } - } - else - { - //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// - while (true) { - WiFiClient _client = _server.available(); - - if (!_client) - break; - - if (!waitForClientTransmission(_client, _apModeTimeoutMs) || !_client.available()) { - continue; - } - - /* Read in request and pass it to the supplied requestHandler */ - String request = _client.readStringUntil('\r'); - yield(); - _client.flush(); - - String response = _requestHandler(request, *this); - - /* Send the response back to the client */ - if (_client.connected()) - { - verboseModePrint("Responding"); // Not storing strings in flash (via F()) to avoid performance impacts when using the string. - _client.print(response + "\r"); - _client.flush(); - yield(); - } + else + { + //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// + while (true) + { + WiFiClient _client = _server.available(); + + if (!_client) + { + break; + } + + if (!waitForClientTransmission(_client, _apModeTimeoutMs) || !_client.available()) + { + continue; + } + + /* Read in request and pass it to the supplied requestHandler */ + String request = _client.readStringUntil('\r'); + yield(); + _client.flush(); + + String response = _requestHandler(request, *this); + + /* Send the response back to the client */ + if (_client.connected()) + { + verboseModePrint("Responding"); // Not storing strings in flash (via F()) to avoid performance impacts when using the string. + _client.print(response + "\r"); + _client.flush(); + yield(); + } + } } - } } diff --git a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h index ceca8f0ff4..5cf94beef6 100644 --- a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h +++ b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h @@ -1,27 +1,27 @@ /* - ESP8266WiFiMesh.h - Mesh network node - Sets up a Mesh Node which acts as a router, creating a Mesh Network with other nodes. - - Copyright (c) 2015 Julian Fell. All rights reserved. - Updated 2018 by Anders Löfgren. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ESP8266WiFiMesh.h - Mesh network node + Sets up a Mesh Node which acts as a router, creating a Mesh Network with other nodes. + + Copyright (c) 2015 Julian Fell. All rights reserved. + Updated 2018 by Anders Löfgren. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __WIFIMESH_H__ #define __WIFIMESH_H__ -#include +#include #include #include #include @@ -33,342 +33,343 @@ const String WIFI_MESH_EMPTY_STRING = ""; -class ESP8266WiFiMesh { +class ESP8266WiFiMesh +{ private: - String _SSID; - String _meshName; - String _nodeID; - uint16_t _serverPort; - String _meshPassword; - uint8 _meshWiFiChannel; - bool _verboseMode; - WiFiServer _server; - uint32_t _lwipVersion[3]; - static const uint32_t lwipVersion203Signature[3]; - String _message = WIFI_MESH_EMPTY_STRING; - bool _scanHidden = false; - bool _apHidden = false; - uint8_t _maxAPStations = 4; - int32_t _connectionAttemptTimeoutMs = 10000; - int _stationModeTimeoutMs = 5000; // int is the type used in the Arduino core for this particular API, not uint32_t, which is why we use int here. - uint32_t _apModeTimeoutMs = 4500; - - static String lastSSID; - static bool staticIPActivated; - static IPAddress staticIP; - static IPAddress gateway; - static IPAddress subnetMask; - static ESP8266WiFiMesh *apController; - - typedef std::function requestHandlerType; - typedef std::function responseHandlerType; - typedef std::function networkFilterType; - - requestHandlerType _requestHandler; - responseHandlerType _responseHandler; - networkFilterType _networkFilter; - - void updateNetworkNames(const String &newMeshName = WIFI_MESH_EMPTY_STRING, const String &newNodeID = WIFI_MESH_EMPTY_STRING); - void verboseModePrint(const String &stringToPrint, bool newline = true); - void fullStop(WiFiClient &currClient); - void initiateConnectionToAP(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL); - transmission_status_t connectToNode(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL); - transmission_status_t exchangeInfo(WiFiClient &currClient); - bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait); - transmission_status_t attemptDataTransfer(); - transmission_status_t attemptDataTransferKernel(); - void storeLwipVersion(); - bool atLeastLwipVersion(const uint32_t minLwipVersion[3]); - - - - - //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// - - typedef std::function compatibilityLayerHandlerType; - - String _ssidPrefix; - uint32_t _chipID; - - compatibilityLayerHandlerType _handler = NULL; - - WiFiClient _client; - - void connectToNode(const String &targetSSID, const char *message); - bool exchangeInfo(const char *message, WiFiClient &currClient); - bool waitForClient(WiFiClient &currClient, int maxWait); - void attemptScanKernel(const char *message); - - //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// - - - + String _SSID; + String _meshName; + String _nodeID; + uint16_t _serverPort; + String _meshPassword; + uint8 _meshWiFiChannel; + bool _verboseMode; + WiFiServer _server; + uint32_t _lwipVersion[3]; + static const uint32_t lwipVersion203Signature[3]; + String _message = WIFI_MESH_EMPTY_STRING; + bool _scanHidden = false; + bool _apHidden = false; + uint8_t _maxAPStations = 4; + int32_t _connectionAttemptTimeoutMs = 10000; + int _stationModeTimeoutMs = 5000; // int is the type used in the Arduino core for this particular API, not uint32_t, which is why we use int here. + uint32_t _apModeTimeoutMs = 4500; + + static String lastSSID; + static bool staticIPActivated; + static IPAddress staticIP; + static IPAddress gateway; + static IPAddress subnetMask; + static ESP8266WiFiMesh *apController; + + typedef std::function requestHandlerType; + typedef std::function responseHandlerType; + typedef std::function networkFilterType; + + requestHandlerType _requestHandler; + responseHandlerType _responseHandler; + networkFilterType _networkFilter; + + void updateNetworkNames(const String &newMeshName = WIFI_MESH_EMPTY_STRING, const String &newNodeID = WIFI_MESH_EMPTY_STRING); + void verboseModePrint(const String &stringToPrint, bool newline = true); + void fullStop(WiFiClient &currClient); + void initiateConnectionToAP(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL); + transmission_status_t connectToNode(const String &targetSSID, int targetChannel = NETWORK_INFO_DEFAULT_INT, uint8_t *targetBSSID = NULL); + transmission_status_t exchangeInfo(WiFiClient &currClient); + bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait); + transmission_status_t attemptDataTransfer(); + transmission_status_t attemptDataTransferKernel(); + void storeLwipVersion(); + bool atLeastLwipVersion(const uint32_t minLwipVersion[3]); + + + + + //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// + + typedef std::function compatibilityLayerHandlerType; + + String _ssidPrefix; + uint32_t _chipID; + + compatibilityLayerHandlerType _handler = NULL; + + WiFiClient _client; + + void connectToNode(const String &targetSSID, const char *message); + bool exchangeInfo(const char *message, WiFiClient &currClient); + bool waitForClient(WiFiClient &currClient, int maxWait); + void attemptScanKernel(const char *message); + + //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// + + + public: - //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// - - /** - * WiFiMesh Constructor method. Creates a WiFi Mesh Node, ready to be initialised. - * - * @chipID A unique identifier number for the node. - * @handler The callback handler for dealing with received messages. Takes a string as an argument which - * is the string received from another node and returns the string to send back. - * - */ - ESP8266WiFiMesh(uint32_t chipID, compatibilityLayerHandlerType handler); - - /** - * Scan for other nodes, and exchange the chosen message with any that are found. - * - * @message The message to send to all other nodes. - * - */ - void attemptScan(const String &message); - void attemptScan(char *message); - - template - void attemptScan(char (&message)[Size]); - - //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// - - ~ESP8266WiFiMesh(); - - /** - * WiFiMesh Constructor method. Creates a WiFi Mesh Node, ready to be initialised. - * - * @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which - * is the request string received from another node and returns the string to send back. - * @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which - * is the response string received from another node. Returns a transmission status code as a transmission_status_t. - * @param networkFilter The callback handler for deciding which WiFi networks to connect to. - * @param meshPassword The WiFi password for the mesh network. - * @param meshName The name of the mesh network. Used as prefix for the node SSID and to find other network nodes in the example network filter function. - * @param nodeID The id for this mesh node. Used as suffix for the node SSID. If set to "", the id will default to ESP.getChipId(). - * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. - * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. - * WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. - * This can cause problems if several ESP8266WiFiMesh instances exist on the same ESP8266 and use different WiFi channels. - * In such a case, whenever the station of one ESP8266WiFiMesh instance connects to an AP, it will silently force the - * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly - * make it impossible for other stations to detect the APs whose WiFi channels have changed. - * @param serverPort The server port used by the AP of the ESP8266WiFiMesh instance. If multiple APs exist on a single ESP8266, each requires a separate server port. - * If two AP:s on the same ESP8266 are using the same server port, they will not be able to have both server instances active at the same time. - * This is managed automatically by the activateAP method. - * - */ - ESP8266WiFiMesh(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, - const String &meshPassword, const String &meshName = "MeshNode_", const String &nodeID = WIFI_MESH_EMPTY_STRING, bool verboseMode = false, - uint8 meshWiFiChannel = 1, uint16_t serverPort = 4011); - - /** - * A vector that contains the NetworkInfo for each WiFi network to connect to. - * The connectionQueue vector is cleared before each new scan and filled via the networkFilter callback function once the scan completes. - * WiFi connections will start with connectionQueue[0] and then incrementally proceed to higher vector positions. - * Note that old network indicies often are invalidated whenever a new WiFi network scan occurs. - */ - static std::vector connectionQueue; - - /** - * A vector with the TransmissionResult for each AP to which a transmission was attempted during the latest attemptTransmission call. - * The latestTransmissionOutcomes vector is cleared before each new transmission attempt. - * Connection attempts are indexed in the same order they were attempted. - * Note that old network indicies often are invalidated whenever a new WiFi network scan occurs. - */ - static std::vector latestTransmissionOutcomes; - - /** - * @returns True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TS_TRANSMISSION_COMPLETE). False otherwise. - */ - static bool latestTransmissionSuccessful(); - - /** - * Initialises the node. - */ - void begin(); - - /** - * Each AP requires a separate server port. If two AP:s are using the same server port, they will not be able to have both server instances active at the same time. - * This is managed automatically by the activateAP method. - */ - void activateAP(); - void deactivateAP(); - void restartAP(); - - /** - * Get the ESP8266WiFiMesh instance currently in control of the ESP8266 AP. - * Note that the result will be nullptr when there is no active AP controller. - * If another instance takes control over the AP after the pointer is created, - * the created pointer will still point to the old AP instance. - * - * @returns A pointer to the ESP8266WiFiMesh instance currently in control of the ESP8266 AP, - * or nullptr if there is no active AP controller. - */ - static ESP8266WiFiMesh * getAPController(); - - /** - * Check if this ESP8266WiFiMesh instance is in control of the ESP8266 AP. - * - * @returns True if this ESP8266WiFiMesh instance is in control of the ESP8266 AP. False otherwise. - */ - bool isAPController(); - - /** - * Change the WiFi channel used by this ESP8266WiFiMesh instance. - * Will also change the WiFi channel for the active AP if this ESP8266WiFiMesh instance is the current AP controller and it is possible to change channel. - * - * WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. - * This can cause problems if several ESP8266WiFiMesh instances exist on the same ESP8266 and use different WiFi channels. - * In such a case, whenever the station of one ESP8266WiFiMesh instance connects to an AP, it will silently force the - * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly - * make it impossible for other stations to detect the APs whose WiFi channels have changed. - * - * @param newWiFiChannel The WiFi channel to change to. Valid values are integers from 1 to 13. - * - */ - void setWiFiChannel(uint8 newWiFiChannel); - uint8 getWiFiChannel(); - - /** - * Change the mesh name used by this ESP8266WiFiMesh instance. - * Will also change the mesh name (SSID prefix) for the active AP if this ESP8266WiFiMesh instance is the current AP controller. - * - * @param newMeshName The mesh name to change to. - */ - void setMeshName(const String &newMeshName); - String getMeshName(); - - /** - * Change the node id used by this ESP8266WiFiMesh instance. - * Will also change the node id (SSID suffix) for the active AP if this ESP8266WiFiMesh instance is the current AP controller. - * - * @param newNodeID The node id to change to. - */ - void setNodeID(const String &newNodeID); - String getNodeID(); - - /** - * Change the SSID (mesh name + node id) used by this ESP8266WiFiMesh instance. - * Will also change the SSID for the active AP if this ESP8266WiFiMesh instance is the current AP controller. - * - * @param newMeshName The mesh name to change to. Will be the SSID prefix. - * @param newNodeID The node id to change to. Will be the SSID suffix. - */ - void setSSID(const String &newMeshName, const String &newNodeID); - String getSSID(); - - /** - * Set the message that will be sent to other nodes when calling attemptTransmission. - * - * @param newMessage The message to send. - */ - void setMessage(const String &newMessage); - String getMessage(); - - /** - * If AP connection already exists, and the initialDisconnect argument is set to false, send message only to the already connected AP. - * Otherwise, scan for other networks, send the scan result to networkFilter and then transmit the message to the networks found in connectionQueue. - * - * @param message The message to send to other nodes. It will be stored in the class instance until replaced via attemptTransmission or setMessage. - * @param concludingDisconnect Disconnect from AP once transmission is complete. - * @param initialDisconnect Disconnect from any currently connected AP before attempting transmission. - * @param noScan Do not scan for new networks and do not call networkFilter function. Will only use the data already in connectionQueue for the transmission. - * @param scanAllWiFiChannels Scan all WiFi channels during a WiFi scan, instead of just the channel the ESP8266WiFiMesh instance is using. - * Scanning all WiFi channels takes about 2100 ms, compared to just 60 ms if only channel 1 (standard) is scanned. - * Note that if the ESP8266 has an active AP, that AP will switch WiFi channel to match that of any other AP the ESP8266 connects to. - * This can make it impossible for other nodes to detect the AP if they are scanning the wrong WiFi channel. - */ - void attemptTransmission(const String &message, bool concludingDisconnect = true, bool initialDisconnect = false, bool noScan = false, bool scanAllWiFiChannels = false); - - /** - * If any clients are connected, accept their requests and call the requestHandler function for each one. - */ - void acceptRequest(); - - /** - * Set a static IP address for the ESP8266 and activate use of static IP. - * The static IP needs to be at the same subnet as the server's gateway. - */ - void setStaticIP(const IPAddress &newIP); - IPAddress getStaticIP(); - void disableStaticIP(); - - /** - * An empty IPAddress. Used as default when no IP is set. - */ - static const IPAddress emptyIP; - - void setRequestHandler(requestHandlerType requestHandler); - requestHandlerType getRequestHandler(); - - void setResponseHandler(responseHandlerType responseHandler); - responseHandlerType getResponseHandler(); - - void setNetworkFilter(networkFilterType networkFilter); - networkFilterType getNetworkFilter(); - - /** - * Set whether scan results from this ESP8266WiFiMesh instance will include WiFi networks with hidden SSIDs. - * This is false by default. - * The SSID field of a found hidden network will be blank in the scan results. - * WiFi.isHidden(networkIndex) can be used to verify that a found network is hidden. - * - * @param scanHidden If true, WiFi networks with hidden SSIDs will be included in scan results. - */ - void setScanHidden(bool scanHidden); - bool getScanHidden(); - - /** - * Set whether the AP controlled by this ESP8266WiFiMesh instance will have a WiFi network with hidden SSID. - * This is false by default. - * Will also change the setting for the active AP if this ESP8266WiFiMesh instance is the current AP controller. - * - * @param apHidden If true, the WiFi network created will have a hidden SSID. - */ - void setAPHidden(bool apHidden); - bool getAPHidden(); - - /** - * Set the maximum number of stations that can simultaneously be connected to the AP controlled by this ESP8266WiFiMesh instance. - * This number is 4 by default. - * Once the max number has been reached, any other station that wants to connect will be forced to wait until an already connected station disconnects. - * The more stations that are connected, the more memory is required. - * Will also change the setting for the active AP if this ESP8266WiFiMesh instance is the current AP controller. - * - * @param maxAPStations The maximum number of simultaneous station connections allowed. Valid values are 0 to 8. - */ - void setMaxAPStations(uint8_t maxAPStations); - bool getMaxAPStations(); - - /** - * Set the timeout for each attempt to connect to another AP that occurs through the attemptTransmission method by this ESP8266WiFiMesh instance. - * The timeout is 10 000 ms by default. - * - * @param connectionAttemptTimeoutMs The timeout for each connection attempt, in milliseconds. - */ - void setConnectionAttemptTimeout(int32_t connectionAttemptTimeoutMs); - int32_t getConnectionAttemptTimeout(); - - /** - * Set the timeout to use for transmissions when this ESP8266WiFiMesh instance acts as a station (i.e. when connected to another AP). - * This will affect the timeout of the attemptTransmission method once a connection to an AP has been established. - * The timeout is 5 000 ms by default. - * - * @param stationModeTimeoutMs The timeout to use, in milliseconds. - */ - void setStationModeTimeout(int stationModeTimeoutMs); - int getStationModeTimeout(); - - /** - * Set the timeout to use for transmissions when this ESP8266WiFiMesh instance acts as an AP (i.e. when receiving connections from other stations). - * This will affect the timeout of the acceptRequest method. - * The timeout is 4 500 ms by default. - * Will also change the setting for the active AP if this ESP8266WiFiMesh instance is the current AP controller. - * - * @param apModeTimeoutMs The timeout to use, in milliseconds. - */ - void setAPModeTimeout(uint32_t apModeTimeoutMs); - uint32_t getAPModeTimeout(); + //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// + + /** + WiFiMesh Constructor method. Creates a WiFi Mesh Node, ready to be initialised. + + @chipID A unique identifier number for the node. + @handler The callback handler for dealing with received messages. Takes a string as an argument which + is the string received from another node and returns the string to send back. + + */ + ESP8266WiFiMesh(uint32_t chipID, compatibilityLayerHandlerType handler); + + /** + Scan for other nodes, and exchange the chosen message with any that are found. + + @message The message to send to all other nodes. + + */ + void attemptScan(const String &message); + void attemptScan(char *message); + + template + void attemptScan(char (&message)[Size]); + + //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// + + ~ESP8266WiFiMesh(); + + /** + WiFiMesh Constructor method. Creates a WiFi Mesh Node, ready to be initialised. + + @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which + is the request string received from another node and returns the string to send back. + @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which + is the response string received from another node. Returns a transmission status code as a transmission_status_t. + @param networkFilter The callback handler for deciding which WiFi networks to connect to. + @param meshPassword The WiFi password for the mesh network. + @param meshName The name of the mesh network. Used as prefix for the node SSID and to find other network nodes in the example network filter function. + @param nodeID The id for this mesh node. Used as suffix for the node SSID. If set to "", the id will default to ESP.getChipId(). + @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. + @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. + WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. + This can cause problems if several ESP8266WiFiMesh instances exist on the same ESP8266 and use different WiFi channels. + In such a case, whenever the station of one ESP8266WiFiMesh instance connects to an AP, it will silently force the + WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly + make it impossible for other stations to detect the APs whose WiFi channels have changed. + @param serverPort The server port used by the AP of the ESP8266WiFiMesh instance. If multiple APs exist on a single ESP8266, each requires a separate server port. + If two AP:s on the same ESP8266 are using the same server port, they will not be able to have both server instances active at the same time. + This is managed automatically by the activateAP method. + + */ + ESP8266WiFiMesh(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, + const String &meshPassword, const String &meshName = "MeshNode_", const String &nodeID = WIFI_MESH_EMPTY_STRING, bool verboseMode = false, + uint8 meshWiFiChannel = 1, uint16_t serverPort = 4011); + + /** + A vector that contains the NetworkInfo for each WiFi network to connect to. + The connectionQueue vector is cleared before each new scan and filled via the networkFilter callback function once the scan completes. + WiFi connections will start with connectionQueue[0] and then incrementally proceed to higher vector positions. + Note that old network indicies often are invalidated whenever a new WiFi network scan occurs. + */ + static std::vector connectionQueue; + + /** + A vector with the TransmissionResult for each AP to which a transmission was attempted during the latest attemptTransmission call. + The latestTransmissionOutcomes vector is cleared before each new transmission attempt. + Connection attempts are indexed in the same order they were attempted. + Note that old network indicies often are invalidated whenever a new WiFi network scan occurs. + */ + static std::vector latestTransmissionOutcomes; + + /** + @returns True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TS_TRANSMISSION_COMPLETE). False otherwise. + */ + static bool latestTransmissionSuccessful(); + + /** + Initialises the node. + */ + void begin(); + + /** + Each AP requires a separate server port. If two AP:s are using the same server port, they will not be able to have both server instances active at the same time. + This is managed automatically by the activateAP method. + */ + void activateAP(); + void deactivateAP(); + void restartAP(); + + /** + Get the ESP8266WiFiMesh instance currently in control of the ESP8266 AP. + Note that the result will be nullptr when there is no active AP controller. + If another instance takes control over the AP after the pointer is created, + the created pointer will still point to the old AP instance. + + @returns A pointer to the ESP8266WiFiMesh instance currently in control of the ESP8266 AP, + or nullptr if there is no active AP controller. + */ + static ESP8266WiFiMesh * getAPController(); + + /** + Check if this ESP8266WiFiMesh instance is in control of the ESP8266 AP. + + @returns True if this ESP8266WiFiMesh instance is in control of the ESP8266 AP. False otherwise. + */ + bool isAPController(); + + /** + Change the WiFi channel used by this ESP8266WiFiMesh instance. + Will also change the WiFi channel for the active AP if this ESP8266WiFiMesh instance is the current AP controller and it is possible to change channel. + + WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. + This can cause problems if several ESP8266WiFiMesh instances exist on the same ESP8266 and use different WiFi channels. + In such a case, whenever the station of one ESP8266WiFiMesh instance connects to an AP, it will silently force the + WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly + make it impossible for other stations to detect the APs whose WiFi channels have changed. + + @param newWiFiChannel The WiFi channel to change to. Valid values are integers from 1 to 13. + + */ + void setWiFiChannel(uint8 newWiFiChannel); + uint8 getWiFiChannel(); + + /** + Change the mesh name used by this ESP8266WiFiMesh instance. + Will also change the mesh name (SSID prefix) for the active AP if this ESP8266WiFiMesh instance is the current AP controller. + + @param newMeshName The mesh name to change to. + */ + void setMeshName(const String &newMeshName); + String getMeshName(); + + /** + Change the node id used by this ESP8266WiFiMesh instance. + Will also change the node id (SSID suffix) for the active AP if this ESP8266WiFiMesh instance is the current AP controller. + + @param newNodeID The node id to change to. + */ + void setNodeID(const String &newNodeID); + String getNodeID(); + + /** + Change the SSID (mesh name + node id) used by this ESP8266WiFiMesh instance. + Will also change the SSID for the active AP if this ESP8266WiFiMesh instance is the current AP controller. + + @param newMeshName The mesh name to change to. Will be the SSID prefix. + @param newNodeID The node id to change to. Will be the SSID suffix. + */ + void setSSID(const String &newMeshName, const String &newNodeID); + String getSSID(); + + /** + Set the message that will be sent to other nodes when calling attemptTransmission. + + @param newMessage The message to send. + */ + void setMessage(const String &newMessage); + String getMessage(); + + /** + If AP connection already exists, and the initialDisconnect argument is set to false, send message only to the already connected AP. + Otherwise, scan for other networks, send the scan result to networkFilter and then transmit the message to the networks found in connectionQueue. + + @param message The message to send to other nodes. It will be stored in the class instance until replaced via attemptTransmission or setMessage. + @param concludingDisconnect Disconnect from AP once transmission is complete. + @param initialDisconnect Disconnect from any currently connected AP before attempting transmission. + @param noScan Do not scan for new networks and do not call networkFilter function. Will only use the data already in connectionQueue for the transmission. + @param scanAllWiFiChannels Scan all WiFi channels during a WiFi scan, instead of just the channel the ESP8266WiFiMesh instance is using. + Scanning all WiFi channels takes about 2100 ms, compared to just 60 ms if only channel 1 (standard) is scanned. + Note that if the ESP8266 has an active AP, that AP will switch WiFi channel to match that of any other AP the ESP8266 connects to. + This can make it impossible for other nodes to detect the AP if they are scanning the wrong WiFi channel. + */ + void attemptTransmission(const String &message, bool concludingDisconnect = true, bool initialDisconnect = false, bool noScan = false, bool scanAllWiFiChannels = false); + + /** + If any clients are connected, accept their requests and call the requestHandler function for each one. + */ + void acceptRequest(); + + /** + Set a static IP address for the ESP8266 and activate use of static IP. + The static IP needs to be at the same subnet as the server's gateway. + */ + void setStaticIP(const IPAddress &newIP); + IPAddress getStaticIP(); + void disableStaticIP(); + + /** + An empty IPAddress. Used as default when no IP is set. + */ + static const IPAddress emptyIP; + + void setRequestHandler(requestHandlerType requestHandler); + requestHandlerType getRequestHandler(); + + void setResponseHandler(responseHandlerType responseHandler); + responseHandlerType getResponseHandler(); + + void setNetworkFilter(networkFilterType networkFilter); + networkFilterType getNetworkFilter(); + + /** + Set whether scan results from this ESP8266WiFiMesh instance will include WiFi networks with hidden SSIDs. + This is false by default. + The SSID field of a found hidden network will be blank in the scan results. + WiFi.isHidden(networkIndex) can be used to verify that a found network is hidden. + + @param scanHidden If true, WiFi networks with hidden SSIDs will be included in scan results. + */ + void setScanHidden(bool scanHidden); + bool getScanHidden(); + + /** + Set whether the AP controlled by this ESP8266WiFiMesh instance will have a WiFi network with hidden SSID. + This is false by default. + Will also change the setting for the active AP if this ESP8266WiFiMesh instance is the current AP controller. + + @param apHidden If true, the WiFi network created will have a hidden SSID. + */ + void setAPHidden(bool apHidden); + bool getAPHidden(); + + /** + Set the maximum number of stations that can simultaneously be connected to the AP controlled by this ESP8266WiFiMesh instance. + This number is 4 by default. + Once the max number has been reached, any other station that wants to connect will be forced to wait until an already connected station disconnects. + The more stations that are connected, the more memory is required. + Will also change the setting for the active AP if this ESP8266WiFiMesh instance is the current AP controller. + + @param maxAPStations The maximum number of simultaneous station connections allowed. Valid values are 0 to 8. + */ + void setMaxAPStations(uint8_t maxAPStations); + bool getMaxAPStations(); + + /** + Set the timeout for each attempt to connect to another AP that occurs through the attemptTransmission method by this ESP8266WiFiMesh instance. + The timeout is 10 000 ms by default. + + @param connectionAttemptTimeoutMs The timeout for each connection attempt, in milliseconds. + */ + void setConnectionAttemptTimeout(int32_t connectionAttemptTimeoutMs); + int32_t getConnectionAttemptTimeout(); + + /** + Set the timeout to use for transmissions when this ESP8266WiFiMesh instance acts as a station (i.e. when connected to another AP). + This will affect the timeout of the attemptTransmission method once a connection to an AP has been established. + The timeout is 5 000 ms by default. + + @param stationModeTimeoutMs The timeout to use, in milliseconds. + */ + void setStationModeTimeout(int stationModeTimeoutMs); + int getStationModeTimeout(); + + /** + Set the timeout to use for transmissions when this ESP8266WiFiMesh instance acts as an AP (i.e. when receiving connections from other stations). + This will affect the timeout of the acceptRequest method. + The timeout is 4 500 ms by default. + Will also change the setting for the active AP if this ESP8266WiFiMesh instance is the current AP controller. + + @param apModeTimeoutMs The timeout to use, in milliseconds. + */ + void setAPModeTimeout(uint32_t apModeTimeoutMs); + uint32_t getAPModeTimeout(); }; #endif diff --git a/libraries/ESP8266WiFiMesh/src/NetworkInfo.cpp b/libraries/ESP8266WiFiMesh/src/NetworkInfo.cpp index 10300dd7bf..77c2598da4 100644 --- a/libraries/ESP8266WiFiMesh/src/NetworkInfo.cpp +++ b/libraries/ESP8266WiFiMesh/src/NetworkInfo.cpp @@ -1,77 +1,77 @@ /* - * NetworkInfo - * Copyright (C) 2018 Anders Löfgren - * - * License (MIT license): - * - * 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. - */ + NetworkInfo + Copyright (C) 2018 Anders Löfgren + + License (MIT license): + + 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. +*/ #include "NetworkInfo.h" void NetworkInfo::copyBSSID(uint8_t newBSSID[6]) { - if(newBSSID != NULL) - { - if(BSSID == NULL) + if (newBSSID != NULL) { - BSSID = _bssidArray; + if (BSSID == NULL) + { + BSSID = _bssidArray; + } + + for (int i = 0; i < 6; i++) + { + BSSID[i] = newBSSID[i]; + } } - - for(int i = 0; i < 6; i++) + else { - BSSID[i] = newBSSID[i]; + BSSID = NULL; } - } - else - { - BSSID = NULL; - } } NetworkInfo::NetworkInfo(int newNetworkIndex, bool autofill) : networkIndex(newNetworkIndex) -{ - if(autofill) - { - SSID = WiFi.SSID(newNetworkIndex); - wifiChannel = WiFi.channel(newNetworkIndex); - copyBSSID(WiFi.BSSID(newNetworkIndex)); - } +{ + if (autofill) + { + SSID = WiFi.SSID(newNetworkIndex); + wifiChannel = WiFi.channel(newNetworkIndex); + copyBSSID(WiFi.BSSID(newNetworkIndex)); + } } -NetworkInfo::NetworkInfo(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex) : - SSID(newSSID), wifiChannel(newWiFiChannel), networkIndex(newNetworkIndex) +NetworkInfo::NetworkInfo(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex) : + SSID(newSSID), wifiChannel(newWiFiChannel), networkIndex(newNetworkIndex) { - copyBSSID(newBSSID); + copyBSSID(newBSSID); } NetworkInfo::NetworkInfo(const NetworkInfo &other) : SSID(other.SSID), wifiChannel(other.wifiChannel), networkIndex(other.networkIndex) { - copyBSSID(other.BSSID); + copyBSSID(other.BSSID); } NetworkInfo & NetworkInfo::operator=(const NetworkInfo &other) { - SSID = other.SSID; - wifiChannel = other.wifiChannel; - copyBSSID(other.BSSID); - networkIndex = other.networkIndex; - return *this; + SSID = other.SSID; + wifiChannel = other.wifiChannel; + copyBSSID(other.BSSID); + networkIndex = other.networkIndex; + return *this; } diff --git a/libraries/ESP8266WiFiMesh/src/NetworkInfo.h b/libraries/ESP8266WiFiMesh/src/NetworkInfo.h index 82fcd90c04..94e82357c5 100644 --- a/libraries/ESP8266WiFiMesh/src/NetworkInfo.h +++ b/libraries/ESP8266WiFiMesh/src/NetworkInfo.h @@ -1,27 +1,27 @@ /* - * NetworkInfo - * Copyright (C) 2018 Anders Löfgren - * - * License (MIT license): - * - * 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. - */ + NetworkInfo + Copyright (C) 2018 Anders Löfgren + + License (MIT license): + + 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. +*/ #ifndef __NETWORKINFO_H__ #define __NETWORKINFO_H__ @@ -30,40 +30,41 @@ const int NETWORK_INFO_DEFAULT_INT = -1; -class NetworkInfo { +class NetworkInfo +{ private: - uint8_t _bssidArray[6] {0}; + uint8_t _bssidArray[6] {0}; public: - String SSID = ""; - int wifiChannel = NETWORK_INFO_DEFAULT_INT; - uint8_t *BSSID = NULL; - int networkIndex = NETWORK_INFO_DEFAULT_INT; + String SSID = ""; + int wifiChannel = NETWORK_INFO_DEFAULT_INT; + uint8_t *BSSID = NULL; + int networkIndex = NETWORK_INFO_DEFAULT_INT; - /** - * @param autofill Automatically fill in the rest of the network info using newNetworkIndex and the WiFi scan results. - */ - NetworkInfo(int newNetworkIndex, bool autofill = true); + /** + @param autofill Automatically fill in the rest of the network info using newNetworkIndex and the WiFi scan results. + */ + NetworkInfo(int newNetworkIndex, bool autofill = true); - /** - * Without giving channel and BSSID, connection time is longer. - */ - NetworkInfo(const String &newSSID, int newWiFiChannel = NETWORK_INFO_DEFAULT_INT, uint8_t newBSSID[6] = NULL, int newNetworkIndex = NETWORK_INFO_DEFAULT_INT); + /** + Without giving channel and BSSID, connection time is longer. + */ + NetworkInfo(const String &newSSID, int newWiFiChannel = NETWORK_INFO_DEFAULT_INT, uint8_t newBSSID[6] = NULL, int newNetworkIndex = NETWORK_INFO_DEFAULT_INT); - NetworkInfo(const NetworkInfo &other); + NetworkInfo(const NetworkInfo &other); - NetworkInfo & operator=(const NetworkInfo &other); + NetworkInfo & operator=(const NetworkInfo &other); - // No need for explicit destructor with current class design + // No need for explicit destructor with current class design - /** - * Copy newBSSID into BSSID. - * Prefer this method for changing NetworkInfo BSSID, unless you actually want to change the BSSID pointer. - */ - void copyBSSID(uint8_t newBSSID[6]); + /** + Copy newBSSID into BSSID. + Prefer this method for changing NetworkInfo BSSID, unless you actually want to change the BSSID pointer. + */ + void copyBSSID(uint8_t newBSSID[6]); }; #endif diff --git a/libraries/ESP8266WiFiMesh/src/TransmissionResult.cpp b/libraries/ESP8266WiFiMesh/src/TransmissionResult.cpp index 81f9312461..328d25b0c0 100644 --- a/libraries/ESP8266WiFiMesh/src/TransmissionResult.cpp +++ b/libraries/ESP8266WiFiMesh/src/TransmissionResult.cpp @@ -1,42 +1,42 @@ /* - * TransmissionResult - * Copyright (C) 2018 Anders Löfgren - * - * License (MIT license): - * - * 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. - */ + TransmissionResult + Copyright (C) 2018 Anders Löfgren + + License (MIT license): + + 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. +*/ #include "TransmissionResult.h" -TransmissionResult::TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill) : - NetworkInfo(newNetworkIndex, autofill), transmissionStatus(newTransmissionStatus) +TransmissionResult::TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill) : + NetworkInfo(newNetworkIndex, autofill), transmissionStatus(newTransmissionStatus) { } -TransmissionResult::TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], transmission_status_t newTransmissionStatus) : - NetworkInfo(newSSID, newWiFiChannel, newBSSID), transmissionStatus(newTransmissionStatus) +TransmissionResult::TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], transmission_status_t newTransmissionStatus) : + NetworkInfo(newSSID, newWiFiChannel, newBSSID), transmissionStatus(newTransmissionStatus) { } TransmissionResult::TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex, transmission_status_t newTransmissionStatus) : - NetworkInfo(newSSID, newWiFiChannel, newBSSID, newNetworkIndex), transmissionStatus(newTransmissionStatus) + NetworkInfo(newSSID, newWiFiChannel, newBSSID, newNetworkIndex), transmissionStatus(newTransmissionStatus) { } -TransmissionResult::TransmissionResult(const NetworkInfo& origin, transmission_status_t newTransmissionStatus) : - NetworkInfo(origin), transmissionStatus(newTransmissionStatus) +TransmissionResult::TransmissionResult(const NetworkInfo& origin, transmission_status_t newTransmissionStatus) : + NetworkInfo(origin), transmissionStatus(newTransmissionStatus) { } diff --git a/libraries/ESP8266WiFiMesh/src/TransmissionResult.h b/libraries/ESP8266WiFiMesh/src/TransmissionResult.h index 8cc4cc020b..3d94c1af12 100644 --- a/libraries/ESP8266WiFiMesh/src/TransmissionResult.h +++ b/libraries/ESP8266WiFiMesh/src/TransmissionResult.h @@ -1,27 +1,27 @@ /* - * TransmissionResult - * Copyright (C) 2018 Anders Löfgren - * - * License (MIT license): - * - * 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. - */ + TransmissionResult + Copyright (C) 2018 Anders Löfgren + + License (MIT license): + + 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. +*/ #ifndef __TRANSMISSIONRESULT_H__ #define __TRANSMISSIONRESULT_H__ @@ -29,29 +29,30 @@ #include #include "NetworkInfo.h" -typedef enum +typedef enum { TS_CONNECTION_FAILED = -1, TS_TRANSMISSION_FAILED = 0, TS_TRANSMISSION_COMPLETE = 1 } transmission_status_t; -class TransmissionResult : public NetworkInfo { +class TransmissionResult : public NetworkInfo +{ public: - transmission_status_t transmissionStatus; + transmission_status_t transmissionStatus; - /** - * @param autofill Automatically fill in the rest of the network info using newNetworkIndex and the WiFi scan results. - */ - TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill = true); + /** + @param autofill Automatically fill in the rest of the network info using newNetworkIndex and the WiFi scan results. + */ + TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill = true); - TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], transmission_status_t newTransmissionStatus); + TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], transmission_status_t newTransmissionStatus); - TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex, transmission_status_t newTransmissionStatus); + TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex, transmission_status_t newTransmissionStatus); - TransmissionResult(const NetworkInfo& origin, transmission_status_t newTransmissionStatus); + TransmissionResult(const NetworkInfo& origin, transmission_status_t newTransmissionStatus); }; #endif diff --git a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp index be908b556e..678880c85a 100644 --- a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp +++ b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp @@ -1,58 +1,58 @@ /* - * TypeConversionFunctions - * Copyright (C) 2018 Anders Löfgren - * - * License (MIT license): - * - * 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. - */ + TypeConversionFunctions + Copyright (C) 2018 Anders Löfgren + + License (MIT license): + + 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. +*/ #include "TypeConversionFunctions.h" String uint64ToString(uint64_t number, byte base) { - assert(2 <= base && base <= 36); - - String result = ""; - - while(number > 0) - { - result = String((uint32_t)(number % base), base) + result; - number /= base; - } - - return (result == "" ? "0" : result); + assert(2 <= base && base <= 36); + + String result = ""; + + while (number > 0) + { + result = String((uint32_t)(number % base), base) + result; + number /= base; + } + + return (result == "" ? "0" : result); } uint64_t stringToUint64(const String &string, byte base) { - assert(2 <= base && base <= 36); - - uint64_t result = 0; - - char currentCharacter[1]; - for(uint32_t i = 0; i < string.length(); i++) - { - result *= base; - currentCharacter[0] = string.charAt(i); - result += strtoul(currentCharacter, NULL, base); - } - - return result; + assert(2 <= base && base <= 36); + + uint64_t result = 0; + + char currentCharacter[1]; + for (uint32_t i = 0; i < string.length(); i++) + { + result *= base; + currentCharacter[0] = string.charAt(i); + result += strtoul(currentCharacter, NULL, base); + } + + return result; } diff --git a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h index 5d42e414cc..36d602aa30 100644 --- a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h +++ b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h @@ -1,27 +1,27 @@ /* - * TypeConversionFunctions - * Copyright (C) 2018 Anders Löfgren - * - * License (MIT license): - * - * 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. - */ + TypeConversionFunctions + Copyright (C) 2018 Anders Löfgren + + License (MIT license): + + 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. +*/ #ifndef __TYPECONVERSIONFUNCTIONS_H__ #define __TYPECONVERSIONFUNCTIONS_H__ @@ -30,21 +30,21 @@ #include /** - * Note that using a base higher than 16 increases likelihood of randomly generating SSID strings containing controversial words. - * - * @param number The number to convert to a string with radix "base". - * @param base The radix to convert "number" into. Must be between 2 and 36. - * @returns A string of "number" encoded in radix "base". - */ + Note that using a base higher than 16 increases likelihood of randomly generating SSID strings containing controversial words. + + @param number The number to convert to a string with radix "base". + @param base The radix to convert "number" into. Must be between 2 and 36. + @returns A string of "number" encoded in radix "base". +*/ String uint64ToString(uint64_t number, byte base = 16); /** - * Note that using a base higher than 16 increases likelihood of randomly generating SSID strings containing controversial words. - * - * @param string The string to convert to uint64_t. String must use radix "base". - * @param base The radix of "string". Must be between 2 and 36. - * @returns A uint64_t of the string, using radix "base" during decoding. - */ + Note that using a base higher than 16 increases likelihood of randomly generating SSID strings containing controversial words. + + @param string The string to convert to uint64_t. String must use radix "base". + @param base The radix of "string". Must be between 2 and 36. + @returns A uint64_t of the string, using radix "base" during decoding. +*/ uint64_t stringToUint64(const String &string, byte base = 16); #endif diff --git a/libraries/ESP8266WiFiMesh/src/UtilityMethods.cpp b/libraries/ESP8266WiFiMesh/src/UtilityMethods.cpp index 795eacb638..95a6c4d0ca 100644 --- a/libraries/ESP8266WiFiMesh/src/UtilityMethods.cpp +++ b/libraries/ESP8266WiFiMesh/src/UtilityMethods.cpp @@ -1,81 +1,89 @@ /* - * TransmissionResult - * Copyright (C) 2018 Anders Löfgren - * - * License (MIT license): - * - * 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. - */ + TransmissionResult + Copyright (C) 2018 Anders Löfgren + + License (MIT license): + + 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. +*/ #include "TypeConversionFunctions.h" #include "ESP8266WiFiMesh.h" void ESP8266WiFiMesh::verboseModePrint(const String &stringToPrint, bool newline) { - if(_verboseMode) - { - if(newline) - Serial.println(stringToPrint); - else - Serial.print(stringToPrint); - } + if (_verboseMode) + { + if (newline) + { + Serial.println(stringToPrint); + } + else + { + Serial.print(stringToPrint); + } + } } /** - * Calculate the current lwIP version number and store the numbers in the _lwipVersion array. - * lwIP version can be changed in the "Tools" menu of Arduino IDE. - */ + Calculate the current lwIP version number and store the numbers in the _lwipVersion array. + lwIP version can be changed in the "Tools" menu of Arduino IDE. +*/ void ESP8266WiFiMesh::storeLwipVersion() { - // ESP.getFullVersion() looks something like: - // SDK:2.2.1(cfd48f3)/Core:win-2.5.0-dev/lwIP:2.0.3(STABLE-2_0_3_RELEASE/glue:arduino-2.4.1-10-g0c0d8c2)/BearSSL:94e9704 - String fullVersion = ESP.getFullVersion(); + // ESP.getFullVersion() looks something like: + // SDK:2.2.1(cfd48f3)/Core:win-2.5.0-dev/lwIP:2.0.3(STABLE-2_0_3_RELEASE/glue:arduino-2.4.1-10-g0c0d8c2)/BearSSL:94e9704 + String fullVersion = ESP.getFullVersion(); - int i = fullVersion.indexOf("lwIP:") + 5; - char currentChar = fullVersion.charAt(i); + int i = fullVersion.indexOf("lwIP:") + 5; + char currentChar = fullVersion.charAt(i); - for(int versionPart = 0; versionPart < 3; versionPart++) - { - while(!isdigit(currentChar)) + for (int versionPart = 0; versionPart < 3; versionPart++) { - currentChar = fullVersion.charAt(++i); + while (!isdigit(currentChar)) + { + currentChar = fullVersion.charAt(++i); + } + while (isdigit(currentChar)) + { + _lwipVersion[versionPart] = 10 * _lwipVersion[versionPart] + (currentChar - '0'); // Left shift and add digit value, in base 10. + currentChar = fullVersion.charAt(++i); + } } - while(isdigit(currentChar)) - { - _lwipVersion[versionPart] = 10 * _lwipVersion[versionPart] + (currentChar - '0'); // Left shift and add digit value, in base 10. - currentChar = fullVersion.charAt(++i); - } - } } /** - * Check if the code is running on a version of lwIP that is at least minLwipVersion. - */ + Check if the code is running on a version of lwIP that is at least minLwipVersion. +*/ bool ESP8266WiFiMesh::atLeastLwipVersion(const uint32_t minLwipVersion[3]) -{ - for(int versionPart = 0; versionPart < 3; versionPart++) - { - if(_lwipVersion[versionPart] > minLwipVersion[versionPart]) - return true; - else if(_lwipVersion[versionPart] < minLwipVersion[versionPart]) - return false; - } +{ + for (int versionPart = 0; versionPart < 3; versionPart++) + { + if (_lwipVersion[versionPart] > minLwipVersion[versionPart]) + { + return true; + } + else if (_lwipVersion[versionPart] < minLwipVersion[versionPart]) + { + return false; + } + } - return true; + return true; } diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp index bc599f84bf..545e3d1317 100644 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp @@ -1,27 +1,27 @@ /** - * - * @file ESP8266HTTPUpdate.cpp - * @date 21.06.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the ESP8266 Http Updater. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ + + @file ESP8266HTTPUpdate.cpp + @date 21.06.2015 + @author Markus Sattler + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the ESP8266 Http Updater. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ #include "ESP8266httpUpdate.h" #include @@ -30,12 +30,12 @@ extern "C" uint32_t _SPIFFS_start; extern "C" uint32_t _SPIFFS_end; ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) - : _httpClientTimeout(8000), _followRedirects(false), _ledPin(-1) + : _httpClientTimeout(8000), _followRedirects(false), _ledPin(-1) { } ESP8266HTTPUpdate::ESP8266HTTPUpdate(int httpClientTimeout) - : _httpClientTimeout(httpClientTimeout), _followRedirects(false), _ledPin(-1) + : _httpClientTimeout(httpClientTimeout), _followRedirects(false), _ledPin(-1) { } @@ -139,11 +139,14 @@ HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, co { (void)https; rebootOnUpdate(reboot); - if (httpsFingerprint.length() == 0) { + if (httpsFingerprint.length() == 0) + { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" return update(host, port, uri, currentVersion); - } else { + } + else + { return update(host, port, uri, currentVersion, httpsFingerprint); #pragma GCC diagnostic pop } @@ -192,27 +195,29 @@ HTTPUpdateResult ESP8266HTTPUpdate::update(WiFiClient& client, const String& hos } /** - * return error code as int - * @return int error code - */ + return error code as int + @return int error code +*/ int ESP8266HTTPUpdate::getLastError(void) { return _lastError; } /** - * return error code as String - * @return String error - */ + return error code as String + @return String error +*/ String ESP8266HTTPUpdate::getLastErrorString(void) { - if(_lastError == 0) { + if (_lastError == 0) + { return String(); // no error } // error from Update class - if(_lastError > 0) { + if (_lastError > 0) + { StreamString error; Update.printError(error); error.trim(); // remove line ending @@ -220,11 +225,13 @@ String ESP8266HTTPUpdate::getLastErrorString(void) } // error from http client - if(_lastError > -100) { + if (_lastError > -100) + { return String(F("HTTP error: ")) + HTTPClient::errorToString(_lastError); } - switch(_lastError) { + switch (_lastError) + { case HTTP_UE_TOO_LESS_SPACE: return F("Not Enough space"); case HTTP_UE_SERVER_NOT_REPORT_SIZE: @@ -248,11 +255,11 @@ String ESP8266HTTPUpdate::getLastErrorString(void) /** - * - * @param http HTTPClient * - * @param currentVersion const char * - * @return HTTPUpdateResult - */ + + @param http HTTPClient + @param currentVersion const char + @return HTTPUpdateResult +*/ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs) { @@ -271,13 +278,17 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& http.addHeader(F("x-ESP8266-chip-size"), String(ESP.getFlashChipRealSize())); http.addHeader(F("x-ESP8266-sdk-version"), ESP.getSdkVersion()); - if(spiffs) { + if (spiffs) + { http.addHeader(F("x-ESP8266-mode"), F("spiffs")); - } else { + } + else + { http.addHeader(F("x-ESP8266-mode"), F("sketch")); } - if(currentVersion && currentVersion[0] != 0x00) { + if (currentVersion && currentVersion[0] != 0x00) + { http.addHeader(F("x-ESP8266-version"), currentVersion); } @@ -291,7 +302,8 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& int code = http.GET(); int len = http.getSize(); - if(code <= 0) { + if (code <= 0) + { DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str()); _lastError = code; http.end(); @@ -304,7 +316,8 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code); DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len); - if(http.hasHeader("x-MD5")) { + if (http.hasHeader("x-MD5")) + { DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", http.header("x-MD5").c_str()); } @@ -312,31 +325,42 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace()); DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize()); - if(currentVersion && currentVersion[0] != 0x00) { - DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion.c_str() ); + if (currentVersion && currentVersion[0] != 0x00) + { + DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion.c_str()); } - switch(code) { + switch (code) + { case HTTP_CODE_OK: ///< OK (Start Update) - if(len > 0) { + if (len > 0) + { bool startUpdate = true; - if(spiffs) { + if (spiffs) + { size_t spiffsSize = ((size_t) &_SPIFFS_end - (size_t) &_SPIFFS_start); - if(len > (int) spiffsSize) { + if (len > (int) spiffsSize) + { DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len); startUpdate = false; } - } else { - if(len > (int) ESP.getFreeSketchSpace()) { + } + else + { + if (len > (int) ESP.getFreeSketchSpace()) + { DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len); startUpdate = false; } } - if(!startUpdate) { + if (!startUpdate) + { _lastError = HTTP_UE_TOO_LESS_SPACE; ret = HTTP_UPDATE_FAILED; - } else { + } + else + { WiFiClient * tcp = http.getStreamPtr(); @@ -347,17 +371,22 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& int command; - if(spiffs) { + if (spiffs) + { command = U_SPIFFS; DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate spiffs...\n"); - } else { + } + else + { command = U_FLASH; DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n"); } - if(!spiffs) { + if (!spiffs) + { uint8_t buf[4]; - if(tcp->peekBytes(&buf[0], 4) != 4) { + if (tcp->peekBytes(&buf[0], 4) != 4) + { DEBUG_HTTP_UPDATE("[httpUpdate] peekBytes magic header failed\n"); _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; http.end(); @@ -365,7 +394,8 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& } // check for valid first magic byte - if(buf[0] != 0xE9) { + if (buf[0] != 0xE9) + { DEBUG_HTTP_UPDATE("[httpUpdate] Magic header does not start with 0xE9\n"); _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; http.end(); @@ -376,28 +406,35 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4); // check if new bin fits to SPI flash - if(bin_flash_size > ESP.getFlashChipRealSize()) { + if (bin_flash_size > ESP.getFlashChipRealSize()) + { DEBUG_HTTP_UPDATE("[httpUpdate] New binary does not fit SPI Flash size\n"); _lastError = HTTP_UE_BIN_FOR_WRONG_FLASH; http.end(); return HTTP_UPDATE_FAILED; } } - if(runUpdate(*tcp, len, http.header("x-MD5"), command)) { + if (runUpdate(*tcp, len, http.header("x-MD5"), command)) + { ret = HTTP_UPDATE_OK; DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n"); http.end(); - if(_rebootOnUpdate && !spiffs) { + if (_rebootOnUpdate && !spiffs) + { ESP.restart(); } - } else { + } + else + { ret = HTTP_UPDATE_FAILED; DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n"); } } - } else { + } + else + { _lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE; ret = HTTP_UPDATE_FAILED; DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length was 0 or wasn't set by Server?!\n"); @@ -428,18 +465,19 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& } /** - * write Update to flash - * @param in Stream& - * @param size uint32_t - * @param md5 String - * @return true if Update ok - */ + write Update to flash + @param in Stream& + @param size uint32_t + @param md5 String + @return true if Update ok +*/ bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command) { StreamString error; - if(!Update.begin(size, command, _ledPin, _ledOn)) { + if (!Update.begin(size, command, _ledPin, _ledOn)) + { _lastError = Update.getError(); Update.printError(error); error.trim(); // remove line ending @@ -447,15 +485,18 @@ bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int com return false; } - if(md5.length()) { - if(!Update.setMD5(md5.c_str())) { + if (md5.length()) + { + if (!Update.setMD5(md5.c_str())) + { _lastError = HTTP_UE_SERVER_FAULTY_MD5; DEBUG_HTTP_UPDATE("[httpUpdate] Update.setMD5 failed! (%s)\n", md5.c_str()); return false; } } - if(Update.writeStream(in) != size) { + if (Update.writeStream(in) != size) + { _lastError = Update.getError(); Update.printError(error); error.trim(); // remove line ending @@ -463,7 +504,8 @@ bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int com return false; } - if(!Update.end()) { + if (!Update.end()) + { _lastError = Update.getError(); Update.printError(error); error.trim(); // remove line ending diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h index a61e071f70..5612a2f8d1 100644 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h @@ -1,27 +1,27 @@ /** - * - * @file ESP8266HTTPUpdate.h - * @date 21.06.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the ESP8266 Http Updater. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ + + @file ESP8266HTTPUpdate.h + @date 21.06.2015 + @author Markus Sattler + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the ESP8266 Http Updater. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ #ifndef ESP8266HTTPUPDATE_H_ #define ESP8266HTTPUPDATE_H_ @@ -54,7 +54,8 @@ #define HTTP_UE_BIN_VERIFY_HEADER_FAILED (-106) #define HTTP_UE_BIN_FOR_WRONG_FLASH (-107) -enum HTTPUpdateResult { +enum HTTPUpdateResult +{ HTTP_UPDATE_FAILED, HTTP_UPDATE_NO_UPDATES, HTTP_UPDATE_OK diff --git a/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp b/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp index 62d54a7edb..c784a0d790 100644 --- a/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp +++ b/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp @@ -1,10 +1,10 @@ #include /* - * MDNS responder global instance - * - * Class type that is instantiated depends on the type mapping in ESP8266mDNS.h - */ + MDNS responder global instance + + Class type that is instantiated depends on the type mapping in ESP8266mDNS.h +*/ #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) MDNSResponder MDNS; #endif diff --git a/libraries/ESP8266mDNS/src/ESP8266mDNS.h b/libraries/ESP8266mDNS/src/ESP8266mDNS.h index 2987f655c2..66d40b1b2e 100644 --- a/libraries/ESP8266mDNS/src/ESP8266mDNS.h +++ b/libraries/ESP8266mDNS/src/ESP8266mDNS.h @@ -1,44 +1,44 @@ /* - ESP8266mDNS.h - mDNSResponder for ESP8266 family - This file is part of the esp8266 core for Arduino environment. - - Legacy_ESP8266mDNS: - The well known, thouroughly tested (yet no flawless) default mDNS library for the ESP8266 family - - LEA_ESP8266mDNS: - An (currently) experimental mDNS implementation, that supports a lot more of mDNS features than Legacy_ESP8266mDNS, like: - - 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 - - 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) - - Announcing available services after successful probing - - Using fixed service TXT items or - - Using dynamic service TXT items for presented services (via callback) - - 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) - - Dynamic queries for DNS-SD services with cached and updated answers and user notifications - - Support for multi-homed client host domains - - See 'LEA_ESP8266mDNS/EPS8266mDNS.h' for more implementation details and usage informations. - See 'examples/mDNS_Clock' and 'examples/mDNS_ServiceMonitor' for implementation examples of the advanced features. - - LEA_ESP8266mDNS is (mostly) client source code compatible to 'Legacy_ESP8266mDNS', so it could be - use as a 'drop-in' replacement in existing projects. - - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ESP8266mDNS.h - mDNSResponder for ESP8266 family + This file is part of the esp8266 core for Arduino environment. + + Legacy_ESP8266mDNS: + The well known, thouroughly tested (yet no flawless) default mDNS library for the ESP8266 family + + LEA_ESP8266mDNS: + An (currently) experimental mDNS implementation, that supports a lot more of mDNS features than Legacy_ESP8266mDNS, like: + - 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 + - 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) + - Announcing available services after successful probing + - Using fixed service TXT items or + - Using dynamic service TXT items for presented services (via callback) + - 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) + - Dynamic queries for DNS-SD services with cached and updated answers and user notifications + - Support for multi-homed client host domains + + See 'LEA_ESP8266mDNS/EPS8266mDNS.h' for more implementation details and usage informations. + See 'examples/mDNS_Clock' and 'examples/mDNS_ServiceMonitor' for implementation examples of the advanced features. + + LEA_ESP8266mDNS is (mostly) client source code compatible to 'Legacy_ESP8266mDNS', so it could be + use as a 'drop-in' replacement in existing projects. + + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -47,10 +47,10 @@ #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) - // Maps the implementation to use to the global namespace type - //using MDNSResponder = Legacy_MDNSResponder::MDNSResponder; //legacy - using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; //new - - extern MDNSResponder MDNS; +// Maps the implementation to use to the global namespace type +//using MDNSResponder = Legacy_MDNSResponder::MDNSResponder; //legacy +using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; //new + +extern MDNSResponder MDNS; #endif diff --git a/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.cpp b/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.cpp index 80f55ad81a..af98b46b6b 100644 --- a/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.cpp +++ b/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.cpp @@ -1,31 +1,31 @@ /* -ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) -Version 1.1 -Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) -ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) -MDNS-SD Suport 2015 Hristo Gochkov -Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) - - -License (MIT license): - 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. + ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) + Version 1.1 + Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) + ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) + MDNS-SD Suport 2015 Hristo Gochkov + Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) + + + License (MIT license): + 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. */ @@ -44,9 +44,9 @@ License (MIT license): #include "debug.h" extern "C" { - #include "osapi.h" - #include "ets_sys.h" - #include "user_interface.h" +#include "osapi.h" +#include "ets_sys.h" +#include "user_interface.h" } #include "WiFiUdp.h" @@ -59,7 +59,8 @@ extern "C" { -namespace Legacy_MDNSResponder { +namespace Legacy_MDNSResponder +{ #ifdef DEBUG_ESP_MDNS @@ -94,1189 +95,1421 @@ static const IPAddress MDNS_MULTICAST_ADDR(224, 0, 0, 251); static const int MDNS_MULTICAST_TTL = 1; static const int MDNS_PORT = 5353; -struct MDNSService { - MDNSService* _next; - char _name[32]; - char _proto[4]; - uint16_t _port; - uint16_t _txtLen; // length of all txts - struct MDNSTxt * _txts; +struct MDNSService +{ + MDNSService* _next; + char _name[32]; + char _proto[4]; + uint16_t _port; + uint16_t _txtLen; // length of all txts + struct MDNSTxt * _txts; }; -struct MDNSTxt{ - MDNSTxt * _next; - String _txt; +struct MDNSTxt +{ + MDNSTxt * _next; + String _txt; }; -struct MDNSAnswer { - MDNSAnswer* next; - uint8_t ip[4]; - uint16_t port; - char *hostname; +struct MDNSAnswer +{ + MDNSAnswer* next; + uint8_t ip[4]; + uint16_t port; + char *hostname; }; -struct MDNSQuery { - char _service[32]; - char _proto[4]; +struct MDNSQuery +{ + char _service[32]; + char _proto[4]; }; -MDNSResponder::MDNSResponder() : _conn(0) { - _services = 0; - _instanceName = ""; - _answers = 0; - _query = 0; - _newQuery = false; - _waitingForAnswers = false; -} -MDNSResponder::~MDNSResponder() { - if (_query != 0) { - os_free(_query); +MDNSResponder::MDNSResponder() : _conn(0) +{ + _services = 0; + _instanceName = ""; + _answers = 0; _query = 0; - } - - // Clear answer list - MDNSAnswer *answer; - int numAnswers = _getNumAnswers(); - for (int n = numAnswers - 1; n >= 0; n--) { - answer = _getAnswerFromIdx(n); - os_free(answer->hostname); - os_free(answer); - answer = 0; - } - _answers = 0; - - if (_conn) { - _conn->unref(); - } + _newQuery = false; + _waitingForAnswers = false; } +MDNSResponder::~MDNSResponder() +{ + if (_query != 0) + { + os_free(_query); + _query = 0; + } -bool MDNSResponder::begin(const char* hostname){ - size_t n = strlen(hostname); - if (n > 63) { // max size for a single label. - return false; - } + // Clear answer list + MDNSAnswer *answer; + int numAnswers = _getNumAnswers(); + for (int n = numAnswers - 1; n >= 0; n--) + { + answer = _getAnswerFromIdx(n); + os_free(answer->hostname); + os_free(answer); + answer = 0; + } + _answers = 0; - // Copy in hostname characters as lowercase - _hostName = hostname; - _hostName.toLowerCase(); + if (_conn) + { + _conn->unref(); + } +} - // If instance name is not already set copy hostname to instance name - if (_instanceName.equals("") ) _instanceName=hostname; +bool MDNSResponder::begin(const char* hostname) +{ + size_t n = strlen(hostname); + if (n > 63) // max size for a single label. + { + return false; + } - _gotIPHandler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP& event){ - (void) event; - _restart(); - }); + // Copy in hostname characters as lowercase + _hostName = hostname; + _hostName.toLowerCase(); - _disconnectedHandler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected& event) { - (void) event; - _restart(); - }); + // If instance name is not already set copy hostname to instance name + if (_instanceName.equals("")) + { + _instanceName = hostname; + } + + _gotIPHandler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP & event) + { + (void) event; + _restart(); + }); - return _listen(); + _disconnectedHandler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected & event) + { + (void) event; + _restart(); + }); + + return _listen(); } -void MDNSResponder::notifyAPChange() { - _restart(); +void MDNSResponder::notifyAPChange() +{ + _restart(); } -void MDNSResponder::_restart() { - if (_conn) { - _conn->unref(); - _conn = nullptr; - } - _listen(); +void MDNSResponder::_restart() +{ + if (_conn) + { + _conn->unref(); + _conn = nullptr; + } + _listen(); } -bool MDNSResponder::_listen() { - // Open the MDNS socket if it isn't already open. - if (!_conn) { - #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.println("MDNS listening"); - #endif +bool MDNSResponder::_listen() +{ + // Open the MDNS socket if it isn't already open. + if (!_conn) + { +#ifdef DEBUG_ESP_MDNS_RX + DEBUG_ESP_PORT.println("MDNS listening"); +#endif - IPAddress mdns(MDNS_MULTICAST_ADDR); + IPAddress mdns(MDNS_MULTICAST_ADDR); - if (igmp_joingroup(IP4_ADDR_ANY4, mdns)!= ERR_OK) { - return false; - } + if (igmp_joingroup(IP4_ADDR_ANY4, mdns) != ERR_OK) + { + return false; + } - _conn = new UdpContext; - _conn->ref(); + _conn = new UdpContext; + _conn->ref(); - if (!_conn->listen(IP_ADDR_ANY, MDNS_PORT)) { - return false; + if (!_conn->listen(IP_ADDR_ANY, MDNS_PORT)) + { + return false; + } + _conn->setMulticastTTL(MDNS_MULTICAST_TTL); + _conn->onRx(std::bind(&MDNSResponder::update, this)); + _conn->connect(mdns, MDNS_PORT); } - _conn->setMulticastTTL(MDNS_MULTICAST_TTL); - _conn->onRx(std::bind(&MDNSResponder::update, this)); - _conn->connect(mdns, MDNS_PORT); - } - return true; + return true; } -void MDNSResponder::update() { - if (!_conn || !_conn->next()) - return; - _parsePacket(); +void MDNSResponder::update() +{ + if (!_conn || !_conn->next()) + { + return; + } + _parsePacket(); } -void MDNSResponder::setInstanceName(String name){ - if (name.length() > 63) - return; - _instanceName = name; +void MDNSResponder::setInstanceName(String name) +{ + if (name.length() > 63) + { + return; + } + _instanceName = name; } -bool MDNSResponder::addServiceTxt(char *name, char *proto, char *key, char *value){ - MDNSService* servicePtr; - - uint8_t txtLen = os_strlen(key) + os_strlen(value) + 1; // Add one for equals sign - txtLen += 1; //accounts for length byte added when building the txt responce - //Find the service - for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) { - //Checking Service names - if(strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0) { - //found a service name match - if (servicePtr->_txtLen + txtLen > 1300) - return false; //max txt record size - MDNSTxt *newtxt = new MDNSTxt; - newtxt->_txt = String(key) + "=" + String(value); - newtxt->_next = 0; - if(servicePtr->_txts == 0) { //no services have been added - //Adding First TXT to service - servicePtr->_txts = newtxt; - servicePtr->_txtLen += txtLen; - return true; - } else { - MDNSTxt * txtPtr = servicePtr->_txts; - while(txtPtr->_next != 0) { - txtPtr = txtPtr->_next; +bool MDNSResponder::addServiceTxt(char *name, char *proto, char *key, char *value) +{ + MDNSService* servicePtr; + + uint8_t txtLen = os_strlen(key) + os_strlen(value) + 1; // Add one for equals sign + txtLen += 1; //accounts for length byte added when building the txt responce + //Find the service + for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) + { + //Checking Service names + if (strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0) + { + //found a service name match + if (servicePtr->_txtLen + txtLen > 1300) + { + return false; //max txt record size + } + MDNSTxt *newtxt = new MDNSTxt; + newtxt->_txt = String(key) + "=" + String(value); + newtxt->_next = 0; + if (servicePtr->_txts == 0) //no services have been added + { + //Adding First TXT to service + servicePtr->_txts = newtxt; + servicePtr->_txtLen += txtLen; + return true; + } + else + { + MDNSTxt * txtPtr = servicePtr->_txts; + while (txtPtr->_next != 0) + { + txtPtr = txtPtr->_next; + } + //adding another TXT to service + txtPtr->_next = newtxt; + servicePtr->_txtLen += txtLen; + return true; + } } - //adding another TXT to service - txtPtr->_next = newtxt; - servicePtr->_txtLen += txtLen; - return true; - } } - } - return false; + return false; } -void MDNSResponder::addService(char *name, char *proto, uint16_t port){ - if(_getServicePort(name, proto) != 0) - return; - if(os_strlen(name) > 32 || os_strlen(proto) != 3) - return; //bad arguments - struct MDNSService *srv = (struct MDNSService*)(os_malloc(sizeof(struct MDNSService))); - os_strcpy(srv->_name, name); - os_strcpy(srv->_proto, proto); - srv->_port = port; - srv->_next = 0; - srv->_txts = 0; - srv->_txtLen = 0; - - if(_services == 0) { - _services = srv; - } else { - MDNSService* servicePtr = _services; - while(servicePtr->_next != 0) - servicePtr = servicePtr->_next; - servicePtr->_next = srv; - } - +void MDNSResponder::addService(char *name, char *proto, uint16_t port) +{ + if (_getServicePort(name, proto) != 0) + { + return; + } + if (os_strlen(name) > 32 || os_strlen(proto) != 3) + { + return; //bad arguments + } + struct MDNSService *srv = (struct MDNSService*)(os_malloc(sizeof(struct MDNSService))); + os_strcpy(srv->_name, name); + os_strcpy(srv->_proto, proto); + srv->_port = port; + srv->_next = 0; + srv->_txts = 0; + srv->_txtLen = 0; + + if (_services == 0) + { + _services = srv; + } + else + { + MDNSService* servicePtr = _services; + while (servicePtr->_next != 0) + { + servicePtr = servicePtr->_next; + } + servicePtr->_next = srv; + } + } -int MDNSResponder::queryService(char *service, char *proto) { +int MDNSResponder::queryService(char *service, char *proto) +{ #ifdef DEBUG_ESP_MDNS_TX - DEBUG_ESP_PORT.printf("queryService %s %s\n", service, proto); -#endif - while(_answers!=0){ - MDNSAnswer *currAnswer = _answers; - _answers = _answers->next; - os_free(currAnswer->hostname); - os_free(currAnswer); - currAnswer = 0; - } - if (_query != 0) { - os_free(_query); - _query = 0; - } - _query = (struct MDNSQuery*)(os_malloc(sizeof(struct MDNSQuery))); - os_strcpy(_query->_service, service); - os_strcpy(_query->_proto, proto); - _newQuery = true; - - char underscore[] = "_"; - - // build service name with _ - char serviceName[os_strlen(service) + 2]; - os_strcpy(serviceName, underscore); - os_strcat(serviceName, service); - size_t serviceNameLen = os_strlen(serviceName); - - //build proto name with _ - char protoName[5]; - os_strcpy(protoName, underscore); - os_strcat(protoName, proto); - size_t protoNameLen = 4; - - //local string - char localName[] = "local"; - size_t localNameLen = 5; - - //terminator - char terminator[] = "\0"; - - // Only supports sending one PTR query - uint8_t questionCount = 1; - - _waitingForAnswers = true; - for (int itfn = 0; itfn < 2; itfn++) { - struct ip_info ip_info; + DEBUG_ESP_PORT.printf("queryService %s %s\n", service, proto); +#endif + while (_answers != 0) + { + MDNSAnswer *currAnswer = _answers; + _answers = _answers->next; + os_free(currAnswer->hostname); + os_free(currAnswer); + currAnswer = 0; + } + if (_query != 0) + { + os_free(_query); + _query = 0; + } + _query = (struct MDNSQuery*)(os_malloc(sizeof(struct MDNSQuery))); + os_strcpy(_query->_service, service); + os_strcpy(_query->_proto, proto); + _newQuery = true; - wifi_get_ip_info((!itfn) ? SOFTAP_IF : STATION_IF, &ip_info); - if (!ip_info.ip.addr) - continue; - _conn->setMulticastInterface(IPAddress(ip_info.ip.addr)); + char underscore[] = "_"; - // Write the header - _conn->flush(); - uint8_t head[12] = { - 0x00, 0x00, //ID = 0 - 0x00, 0x00, //Flags = response + authoritative answer - 0x00, questionCount, //Question count - 0x00, 0x00, //Answer count - 0x00, 0x00, //Name server records - 0x00, 0x00 //Additional records - }; - _conn->append(reinterpret_cast(head), 12); + // build service name with _ + char serviceName[os_strlen(service) + 2]; + os_strcpy(serviceName, underscore); + os_strcat(serviceName, service); + size_t serviceNameLen = os_strlen(serviceName); + + //build proto name with _ + char protoName[5]; + os_strcpy(protoName, underscore); + os_strcat(protoName, proto); + size_t protoNameLen = 4; + + //local string + char localName[] = "local"; + size_t localNameLen = 5; + + //terminator + char terminator[] = "\0"; // Only supports sending one PTR query - // Send the Name field (eg. "_http._tcp.local") - _conn->append(reinterpret_cast(&serviceNameLen), 1); // lenght of "_" + service - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_" + service - _conn->append(reinterpret_cast(&protoNameLen), 1); // lenght of "_" + proto - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_" + proto - _conn->append(reinterpret_cast(&localNameLen), 1); // lenght of "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - //Send the type and class - uint8_t ptrAttrs[4] = { - 0x00, 0x0c, //PTR record query - 0x00, 0x01 //Class IN - }; - _conn->append(reinterpret_cast(ptrAttrs), 4); - _conn->send(); - } + uint8_t questionCount = 1; + + _waitingForAnswers = true; + for (int itfn = 0; itfn < 2; itfn++) + { + struct ip_info ip_info; + + wifi_get_ip_info((!itfn) ? SOFTAP_IF : STATION_IF, &ip_info); + if (!ip_info.ip.addr) + { + continue; + } + _conn->setMulticastInterface(IPAddress(ip_info.ip.addr)); + + // Write the header + _conn->flush(); + uint8_t head[12] = + { + 0x00, 0x00, //ID = 0 + 0x00, 0x00, //Flags = response + authoritative answer + 0x00, questionCount, //Question count + 0x00, 0x00, //Answer count + 0x00, 0x00, //Name server records + 0x00, 0x00 //Additional records + }; + _conn->append(reinterpret_cast(head), 12); + + // Only supports sending one PTR query + // Send the Name field (eg. "_http._tcp.local") + _conn->append(reinterpret_cast(&serviceNameLen), 1); // lenght of "_" + service + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_" + service + _conn->append(reinterpret_cast(&protoNameLen), 1); // lenght of "_" + proto + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_" + proto + _conn->append(reinterpret_cast(&localNameLen), 1); // lenght of "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + //Send the type and class + uint8_t ptrAttrs[4] = + { + 0x00, 0x0c, //PTR record query + 0x00, 0x01 //Class IN + }; + _conn->append(reinterpret_cast(ptrAttrs), 4); + _conn->send(); + } #ifdef DEBUG_ESP_MDNS_TX - DEBUG_ESP_PORT.println("Waiting for answers.."); + DEBUG_ESP_PORT.println("Waiting for answers.."); #endif - delay(1000); + delay(1000); - _waitingForAnswers = false; + _waitingForAnswers = false; - return _getNumAnswers(); + return _getNumAnswers(); } -String MDNSResponder::hostname(int idx) { - MDNSAnswer *answer = _getAnswerFromIdx(idx); - if (answer == 0) { - return String(); - } - return answer->hostname; +String MDNSResponder::hostname(int idx) +{ + MDNSAnswer *answer = _getAnswerFromIdx(idx); + if (answer == 0) + { + return String(); + } + return answer->hostname; } -IPAddress MDNSResponder::IP(int idx) { - MDNSAnswer *answer = _getAnswerFromIdx(idx); - if (answer == 0) { - return IPAddress(); - } - return IPAddress(answer->ip); +IPAddress MDNSResponder::IP(int idx) +{ + MDNSAnswer *answer = _getAnswerFromIdx(idx); + if (answer == 0) + { + return IPAddress(); + } + return IPAddress(answer->ip); } -uint16_t MDNSResponder::port(int idx) { - MDNSAnswer *answer = _getAnswerFromIdx(idx); - if (answer == 0) { - return 0; - } - return answer->port; +uint16_t MDNSResponder::port(int idx) +{ + MDNSAnswer *answer = _getAnswerFromIdx(idx); + if (answer == 0) + { + return 0; + } + return answer->port; } -MDNSAnswer* MDNSResponder::_getAnswerFromIdx(int idx) { - MDNSAnswer *answer = _answers; - while (answer != 0 && idx-- > 0) { - answer = answer->next; - } - if (idx > 0) { - return 0; - } - return answer; +MDNSAnswer* MDNSResponder::_getAnswerFromIdx(int idx) +{ + MDNSAnswer *answer = _answers; + while (answer != 0 && idx-- > 0) + { + answer = answer->next; + } + if (idx > 0) + { + return 0; + } + return answer; } -int MDNSResponder::_getNumAnswers() { - int numAnswers = 0; - MDNSAnswer *answer = _answers; - while (answer != 0) { - numAnswers++; - answer = answer->next; - } - return numAnswers; +int MDNSResponder::_getNumAnswers() +{ + int numAnswers = 0; + MDNSAnswer *answer = _answers; + while (answer != 0) + { + numAnswers++; + answer = answer->next; + } + return numAnswers; } -MDNSTxt * MDNSResponder::_getServiceTxt(char *name, char *proto){ - MDNSService* servicePtr; - for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) { - if(servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0){ - if (servicePtr->_txts == 0) - return nullptr; - return servicePtr->_txts; +MDNSTxt * MDNSResponder::_getServiceTxt(char *name, char *proto) +{ + MDNSService* servicePtr; + for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) + { + if (servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0) + { + if (servicePtr->_txts == 0) + { + return nullptr; + } + return servicePtr->_txts; + } } - } - return nullptr; + return nullptr; } -uint16_t MDNSResponder::_getServiceTxtLen(char *name, char *proto){ - MDNSService* servicePtr; - for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) { - if(servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0){ - if (servicePtr->_txts == 0) - return false; - return servicePtr->_txtLen; +uint16_t MDNSResponder::_getServiceTxtLen(char *name, char *proto) +{ + MDNSService* servicePtr; + for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) + { + if (servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0) + { + if (servicePtr->_txts == 0) + { + return false; + } + return servicePtr->_txtLen; + } } - } - return 0; + return 0; } -uint16_t MDNSResponder::_getServicePort(char *name, char *proto){ - MDNSService* servicePtr; - for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) { - if(servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0){ - return servicePtr->_port; +uint16_t MDNSResponder::_getServicePort(char *name, char *proto) +{ + MDNSService* servicePtr; + for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) + { + if (servicePtr->_port > 0 && strcmp(servicePtr->_name, name) == 0 && strcmp(servicePtr->_proto, proto) == 0) + { + return servicePtr->_port; + } } - } - return 0; + return 0; } -IPAddress MDNSResponder::_getRequestMulticastInterface(){ - struct ip_info ip_info; - bool match_ap = false; - if (wifi_get_opmode() & SOFTAP_MODE) { - const IPAddress& remote_ip = _conn->getRemoteAddress(); - wifi_get_ip_info(SOFTAP_IF, &ip_info); - IPAddress infoIp(ip_info.ip); - IPAddress infoMask(ip_info.netmask); - if (ip_info.ip.addr && ip_addr_netcmp((const ip_addr_t*)remote_ip, (const ip_addr_t*)infoIp, ip_2_ip4((const ip_addr_t*)infoMask))) - match_ap = true; - } - if (!match_ap) - wifi_get_ip_info(STATION_IF, &ip_info); - return IPAddress(ip_info.ip.addr); +IPAddress MDNSResponder::_getRequestMulticastInterface() +{ + struct ip_info ip_info; + bool match_ap = false; + if (wifi_get_opmode() & SOFTAP_MODE) + { + const IPAddress& remote_ip = _conn->getRemoteAddress(); + wifi_get_ip_info(SOFTAP_IF, &ip_info); + IPAddress infoIp(ip_info.ip); + IPAddress infoMask(ip_info.netmask); + if (ip_info.ip.addr && ip_addr_netcmp((const ip_addr_t*)remote_ip, (const ip_addr_t*)infoIp, ip_2_ip4((const ip_addr_t*)infoMask))) + { + match_ap = true; + } + } + if (!match_ap) + { + wifi_get_ip_info(STATION_IF, &ip_info); + } + return IPAddress(ip_info.ip.addr); } -void MDNSResponder::_parsePacket(){ - int i; - char tmp; - bool serviceParsed = false; - bool protoParsed = false; - bool localParsed = false; +void MDNSResponder::_parsePacket() +{ + int i; + char tmp; + bool serviceParsed = false; + bool protoParsed = false; + bool localParsed = false; - char hostName[255]; - uint8_t hostNameLen; + char hostName[255]; + uint8_t hostNameLen; - char serviceName[32]; - uint8_t serviceNameLen; - uint16_t servicePort = 0; + char serviceName[32]; + uint8_t serviceNameLen; + uint16_t servicePort = 0; - char protoName[32]; - protoName[0] = 0; - uint8_t protoNameLen = 0; + char protoName[32]; + protoName[0] = 0; + uint8_t protoNameLen = 0; - uint16_t packetHeader[6]; + uint16_t packetHeader[6]; - for(i=0; i<6; i++) - packetHeader[i] = _conn_read16(); + for (i = 0; i < 6; i++) + { + packetHeader[i] = _conn_read16(); + } - if ((packetHeader[1] & 0x8000) != 0) { // Read answers + if ((packetHeader[1] & 0x8000) != 0) // Read answers + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("Reading answers RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]); + DEBUG_ESP_PORT.printf("Reading answers RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]); #endif - if (!_waitingForAnswers) { + if (!_waitingForAnswers) + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.println("Not expecting any answers right now, returning"); + DEBUG_ESP_PORT.println("Not expecting any answers right now, returning"); #endif - _conn->flush(); - return; - } + _conn->flush(); + return; + } - int numAnswers = packetHeader[3] + packetHeader[5]; - // Assume that the PTR answer always comes first and that it is always accompanied by a TXT, SRV, AAAA (optional) and A answer in the same packet. - if (numAnswers < 4) { + int numAnswers = packetHeader[3] + packetHeader[5]; + // Assume that the PTR answer always comes first and that it is always accompanied by a TXT, SRV, AAAA (optional) and A answer in the same packet. + if (numAnswers < 4) + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("Expected a packet with 4 or more answers, got %u\n", numAnswers); + DEBUG_ESP_PORT.printf("Expected a packet with 4 or more answers, got %u\n", numAnswers); #endif - _conn->flush(); - return; - } - - uint8_t tmp8; - uint16_t answerPort = 0; - uint8_t answerIp[4] = { 0,0,0,0 }; - char answerHostName[255]; - bool serviceMatch = false; - MDNSAnswer *answer; - uint8_t partsCollected = 0; - uint8_t stringsRead = 0; + _conn->flush(); + return; + } - answerHostName[0] = '\0'; + uint8_t tmp8; + uint16_t answerPort = 0; + uint8_t answerIp[4] = { 0, 0, 0, 0 }; + char answerHostName[255]; + bool serviceMatch = false; + MDNSAnswer *answer; + uint8_t partsCollected = 0; + uint8_t stringsRead = 0; - // Clear answer list - if (_newQuery) { - int oldAnswers = _getNumAnswers(); - for (int n = oldAnswers - 1; n >= 0; n--) { - answer = _getAnswerFromIdx(n); - os_free(answer->hostname); - os_free(answer); - answer = 0; - } - _answers = 0; - _newQuery = false; - } + answerHostName[0] = '\0'; - while (numAnswers--) { - // Read name - stringsRead = 0; - size_t last_bufferpos = 0; - do { - tmp8 = _conn_read8(); - if (tmp8 == 0x00) { // End of name - break; + // Clear answer list + if (_newQuery) + { + int oldAnswers = _getNumAnswers(); + for (int n = oldAnswers - 1; n >= 0; n--) + { + answer = _getAnswerFromIdx(n); + os_free(answer->hostname); + os_free(answer); + answer = 0; + } + _answers = 0; + _newQuery = false; } - if (tmp8 & 0xC0) { // Compressed pointer - uint16_t offset = ((((uint16_t)tmp8) & ~0xC0) << 8) | _conn_read8(); - if (_conn->isValidOffset(offset)) { - if (0 == last_bufferpos) - last_bufferpos = _conn->tell(); + + while (numAnswers--) + { + // Read name + stringsRead = 0; + size_t last_bufferpos = 0; + do + { + tmp8 = _conn_read8(); + if (tmp8 == 0x00) // End of name + { + break; + } + if (tmp8 & 0xC0) // Compressed pointer + { + uint16_t offset = ((((uint16_t)tmp8) & ~0xC0) << 8) | _conn_read8(); + if (_conn->isValidOffset(offset)) + { + if (0 == last_bufferpos) + { + last_bufferpos = _conn->tell(); + } #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Compressed pointer, jumping from "); - DEBUG_ESP_PORT.print(last_bufferpos); - DEBUG_ESP_PORT.print(" to "); - DEBUG_ESP_PORT.println(offset); + DEBUG_ESP_PORT.print("Compressed pointer, jumping from "); + DEBUG_ESP_PORT.print(last_bufferpos); + DEBUG_ESP_PORT.print(" to "); + DEBUG_ESP_PORT.println(offset); #endif - _conn->seek(offset); - tmp8 = _conn_read8(); - } - else { + _conn->seek(offset); + tmp8 = _conn_read8(); + } + else + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Skipping malformed compressed pointer"); + DEBUG_ESP_PORT.print("Skipping malformed compressed pointer"); #endif - tmp8 = _conn_read8(); - break; - } - } - if(stringsRead > 3){ + tmp8 = _conn_read8(); + break; + } + } + if (stringsRead > 3) + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.println("failed to read the response name"); + DEBUG_ESP_PORT.println("failed to read the response name"); #endif - _conn->flush(); - return; - } - _conn_readS(serviceName, tmp8); - serviceName[tmp8] = '\0'; + _conn->flush(); + return; + } + _conn_readS(serviceName, tmp8); + serviceName[tmp8] = '\0'; #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf(" %d ", tmp8); - for (int n = 0; n < tmp8; n++) { - DEBUG_ESP_PORT.printf("%c", serviceName[n]); - } - DEBUG_ESP_PORT.println(); + DEBUG_ESP_PORT.printf(" %d ", tmp8); + for (int n = 0; n < tmp8; n++) + { + DEBUG_ESP_PORT.printf("%c", serviceName[n]); + } + DEBUG_ESP_PORT.println(); #endif - if (serviceName[0] == '_') { - if (strcmp(&serviceName[1], _query->_service) == 0) { - serviceMatch = true; + if (serviceName[0] == '_') + { + if (strcmp(&serviceName[1], _query->_service) == 0) + { + serviceMatch = true; #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("found matching service: %s\n", _query->_service); + DEBUG_ESP_PORT.printf("found matching service: %s\n", _query->_service); #endif - } - } - stringsRead++; - } while (true); - if (last_bufferpos > 0) - { - _conn->seek(last_bufferpos); + } + } + stringsRead++; + } while (true); + if (last_bufferpos > 0) + { + _conn->seek(last_bufferpos); #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Compressed pointer, jumping back to "); - DEBUG_ESP_PORT.println(last_bufferpos); + DEBUG_ESP_PORT.print("Compressed pointer, jumping back to "); + DEBUG_ESP_PORT.println(last_bufferpos); #endif - } - - uint16_t answerType = _conn_read16(); // Read type - uint16_t answerClass = _conn_read16(); // Read class - uint32_t answerTtl = _conn_read32(); // Read ttl - uint16_t answerRdlength = _conn_read16(); // Read rdlength - - (void) answerClass; - (void) answerTtl; - - if(answerRdlength > 255){ - if(answerType == MDNS_TYPE_TXT && answerRdlength < 1460){ - while(--answerRdlength) _conn->read(); - } else { + } + + uint16_t answerType = _conn_read16(); // Read type + uint16_t answerClass = _conn_read16(); // Read class + uint32_t answerTtl = _conn_read32(); // Read ttl + uint16_t answerRdlength = _conn_read16(); // Read rdlength + + (void) answerClass; + (void) answerTtl; + + if (answerRdlength > 255) + { + if (answerType == MDNS_TYPE_TXT && answerRdlength < 1460) + { + while (--answerRdlength) + { + _conn->read(); + } + } + else + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("Data len too long! %u\n", answerRdlength); + DEBUG_ESP_PORT.printf("Data len too long! %u\n", answerRdlength); #endif - _conn->flush(); - return; - } - } + _conn->flush(); + return; + } + } #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("type: %04x rdlength: %d\n", answerType, answerRdlength); + DEBUG_ESP_PORT.printf("type: %04x rdlength: %d\n", answerType, answerRdlength); #endif - if (answerType == MDNS_TYPE_PTR) { - partsCollected |= 0x01; - _conn_readS(hostName, answerRdlength); // Read rdata - if(hostName[answerRdlength-2] & 0xc0){ - memcpy(answerHostName, hostName+1, answerRdlength-3); - answerHostName[answerRdlength-3] = '\0'; - } + if (answerType == MDNS_TYPE_PTR) + { + partsCollected |= 0x01; + _conn_readS(hostName, answerRdlength); // Read rdata + if (hostName[answerRdlength - 2] & 0xc0) + { + memcpy(answerHostName, hostName + 1, answerRdlength - 3); + answerHostName[answerRdlength - 3] = '\0'; + } #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("PTR %d ", answerRdlength); - for (int n = 0; n < answerRdlength; n++) { - DEBUG_ESP_PORT.printf("%c", hostName[n]); - } - DEBUG_ESP_PORT.println(); + DEBUG_ESP_PORT.printf("PTR %d ", answerRdlength); + for (int n = 0; n < answerRdlength; n++) + { + DEBUG_ESP_PORT.printf("%c", hostName[n]); + } + DEBUG_ESP_PORT.println(); #endif - } + } - else if (answerType == MDNS_TYPE_TXT) { - partsCollected |= 0x02; - _conn_readS(hostName, answerRdlength); // Read rdata + else if (answerType == MDNS_TYPE_TXT) + { + partsCollected |= 0x02; + _conn_readS(hostName, answerRdlength); // Read rdata #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("TXT %d ", answerRdlength); - for (int n = 0; n < answerRdlength; n++) { - DEBUG_ESP_PORT.printf("%c", hostName[n]); - } - DEBUG_ESP_PORT.println(); + DEBUG_ESP_PORT.printf("TXT %d ", answerRdlength); + for (int n = 0; n < answerRdlength; n++) + { + DEBUG_ESP_PORT.printf("%c", hostName[n]); + } + DEBUG_ESP_PORT.println(); #endif - } - - else if (answerType == MDNS_TYPE_SRV) { - partsCollected |= 0x04; - uint16_t answerPrio = _conn_read16(); // Read priority - uint16_t answerWeight = _conn_read16(); // Read weight - answerPort = _conn_read16(); // Read port - last_bufferpos = 0; - - (void) answerPrio; - (void) answerWeight; - - // Read hostname - tmp8 = _conn_read8(); - if (tmp8 & 0xC0) { // Compressed pointer - uint16_t offset = ((((uint16_t)tmp8) & ~0xC0) << 8) | _conn_read8(); - if (_conn->isValidOffset(offset)) { - last_bufferpos = _conn->tell(); + } + + else if (answerType == MDNS_TYPE_SRV) + { + partsCollected |= 0x04; + uint16_t answerPrio = _conn_read16(); // Read priority + uint16_t answerWeight = _conn_read16(); // Read weight + answerPort = _conn_read16(); // Read port + last_bufferpos = 0; + + (void) answerPrio; + (void) answerWeight; + + // Read hostname + tmp8 = _conn_read8(); + if (tmp8 & 0xC0) // Compressed pointer + { + uint16_t offset = ((((uint16_t)tmp8) & ~0xC0) << 8) | _conn_read8(); + if (_conn->isValidOffset(offset)) + { + last_bufferpos = _conn->tell(); #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Compressed pointer, jumping from "); - DEBUG_ESP_PORT.print(last_bufferpos); - DEBUG_ESP_PORT.print(" to "); - DEBUG_ESP_PORT.println(offset); + DEBUG_ESP_PORT.print("Compressed pointer, jumping from "); + DEBUG_ESP_PORT.print(last_bufferpos); + DEBUG_ESP_PORT.print(" to "); + DEBUG_ESP_PORT.println(offset); #endif - _conn->seek(offset); - tmp8 = _conn_read8(); - } - else { + _conn->seek(offset); + tmp8 = _conn_read8(); + } + else + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Skipping malformed compressed pointer"); + DEBUG_ESP_PORT.print("Skipping malformed compressed pointer"); #endif - tmp8 = _conn_read8(); - break; - } - } - _conn_readS(answerHostName, tmp8); - answerHostName[tmp8] = '\0'; + tmp8 = _conn_read8(); + break; + } + } + _conn_readS(answerHostName, tmp8); + answerHostName[tmp8] = '\0'; #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("SRV %d ", tmp8); - for (int n = 0; n < tmp8; n++) { - DEBUG_ESP_PORT.printf("%02x ", answerHostName[n]); - } - DEBUG_ESP_PORT.printf("\n%s\n", answerHostName); + DEBUG_ESP_PORT.printf("SRV %d ", tmp8); + for (int n = 0; n < tmp8; n++) + { + DEBUG_ESP_PORT.printf("%02x ", answerHostName[n]); + } + DEBUG_ESP_PORT.printf("\n%s\n", answerHostName); #endif - if (last_bufferpos > 0) - { - _conn->seek(last_bufferpos); - tmp8 = 2; // Size of compression octets + if (last_bufferpos > 0) + { + _conn->seek(last_bufferpos); + tmp8 = 2; // Size of compression octets #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.print("Compressed pointer, jumping back to "); - DEBUG_ESP_PORT.println(last_bufferpos); + DEBUG_ESP_PORT.print("Compressed pointer, jumping back to "); + DEBUG_ESP_PORT.println(last_bufferpos); #endif - } - if (answerRdlength - (6 + 1 + tmp8) > 0) { // Skip any remaining rdata - _conn_readS(hostName, answerRdlength - (6 + 1 + tmp8)); - } - } - - else if (answerType == MDNS_TYPE_A) { - partsCollected |= 0x08; - for (int i = 0; i < 4; i++) { - answerIp[i] = _conn_read8(); - } - } - else { + } + if (answerRdlength - (6 + 1 + tmp8) > 0) // Skip any remaining rdata + { + _conn_readS(hostName, answerRdlength - (6 + 1 + tmp8)); + } + } + + else if (answerType == MDNS_TYPE_A) + { + partsCollected |= 0x08; + for (int i = 0; i < 4; i++) + { + answerIp[i] = _conn_read8(); + } + } + else + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("Ignoring unsupported type %02x\n", tmp8); + DEBUG_ESP_PORT.printf("Ignoring unsupported type %02x\n", tmp8); #endif - for (int n = 0; n < answerRdlength; n++) - (void)_conn_read8(); - } - - if ((partsCollected == 0x0F) && serviceMatch) { + for (int n = 0; n < answerRdlength; n++) + { + (void)_conn_read8(); + } + } + + if ((partsCollected == 0x0F) && serviceMatch) + { #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.println("All answers parsed, adding to _answers list.."); + DEBUG_ESP_PORT.println("All answers parsed, adding to _answers list.."); #endif - // Add new answer to answer list - if (_answers == 0) { - _answers = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer))); - answer = _answers; + // Add new answer to answer list + if (_answers == 0) + { + _answers = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer))); + answer = _answers; + } + else + { + answer = _answers; + while (answer->next != 0) + { + answer = answer->next; + } + answer->next = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer))); + answer = answer->next; + } + answer->next = 0; + answer->hostname = 0; + + // Populate new answer + answer->port = answerPort; + for (int i = 0; i < 4; i++) + { + answer->ip[i] = answerIp[i]; + } + answer->hostname = (char *)os_malloc(strlen(answerHostName) + 1); + os_strcpy(answer->hostname, answerHostName); + _conn->flush(); + return; + } } - else { - answer = _answers; - while (answer->next != 0) { - answer = answer->next; - } - answer->next = (struct MDNSAnswer*)(os_malloc(sizeof(struct MDNSAnswer))); - answer = answer->next; - } - answer->next = 0; - answer->hostname = 0; - // Populate new answer - answer->port = answerPort; - for (int i = 0; i < 4; i++) { - answer->ip[i] = answerIp[i]; - } - answer->hostname = (char *)os_malloc(strlen(answerHostName) + 1); - os_strcpy(answer->hostname, answerHostName); _conn->flush(); return; - } } - - _conn->flush(); - return; - } - // PARSE REQUEST NAME + // PARSE REQUEST NAME - hostNameLen = _conn_read8() % 255; - _conn_readS(hostName, hostNameLen); - hostName[hostNameLen] = '\0'; + hostNameLen = _conn_read8() % 255; + _conn_readS(hostName, hostNameLen); + hostName[hostNameLen] = '\0'; - if(hostName[0] == '_'){ - serviceParsed = true; - memcpy(serviceName, hostName+1, hostNameLen); - serviceNameLen = hostNameLen-1; - hostNameLen = 0; - } - - if(hostNameLen > 0 && !_hostName.equals(hostName) && !_instanceName.equals(hostName)){ -#ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_NO_HOST: %s\n", hostName); - DEBUG_ESP_PORT.printf("hostname: %s\n", _hostName.c_str() ); - DEBUG_ESP_PORT.printf("instance: %s\n", _instanceName.c_str() ); -#endif - _conn->flush(); - return; - } - - if(!serviceParsed){ - serviceNameLen = _conn_read8() % 255; - _conn_readS(serviceName, serviceNameLen); - serviceName[serviceNameLen] = '\0'; - - if(serviceName[0] == '_'){ - memmove(serviceName, serviceName+1, serviceNameLen); - serviceNameLen--; - serviceParsed = true; - } else if(serviceNameLen == 5 && strcmp("local", serviceName) == 0){ - tmp = _conn_read8(); - if(tmp == 0){ + if (hostName[0] == '_') + { serviceParsed = true; - serviceNameLen = 0; - protoParsed = true; - protoNameLen = 0; - localParsed = true; - } else { + memcpy(serviceName, hostName + 1, hostNameLen); + serviceNameLen = hostNameLen - 1; + hostNameLen = 0; + } + + if (hostNameLen > 0 && !_hostName.equals(hostName) && !_instanceName.equals(hostName)) + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_FQDN: %s\n", serviceName); + DEBUG_ESP_PORT.printf("ERR_NO_HOST: %s\n", hostName); + DEBUG_ESP_PORT.printf("hostname: %s\n", _hostName.c_str()); + DEBUG_ESP_PORT.printf("instance: %s\n", _instanceName.c_str()); #endif _conn->flush(); return; - } - } else { + } + + if (!serviceParsed) + { + serviceNameLen = _conn_read8() % 255; + _conn_readS(serviceName, serviceNameLen); + serviceName[serviceNameLen] = '\0'; + + if (serviceName[0] == '_') + { + memmove(serviceName, serviceName + 1, serviceNameLen); + serviceNameLen--; + serviceParsed = true; + } + else if (serviceNameLen == 5 && strcmp("local", serviceName) == 0) + { + tmp = _conn_read8(); + if (tmp == 0) + { + serviceParsed = true; + serviceNameLen = 0; + protoParsed = true; + protoNameLen = 0; + localParsed = true; + } + else + { +#ifdef DEBUG_ESP_MDNS_ERR + DEBUG_ESP_PORT.printf("ERR_FQDN: %s\n", serviceName); +#endif + _conn->flush(); + return; + } + } + else + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_SERVICE: %s\n", serviceName); + DEBUG_ESP_PORT.printf("ERR_SERVICE: %s\n", serviceName); #endif - _conn->flush(); - return; + _conn->flush(); + return; + } } - } - - if(!protoParsed){ - protoNameLen = _conn_read8() % 255; - _conn_readS(protoName, protoNameLen); - protoName[protoNameLen] = '\0'; - if(protoNameLen == 4 && protoName[0] == '_'){ - memmove(protoName, protoName+1, protoNameLen); - protoNameLen--; - protoParsed = true; - } else if(strcmp("services", serviceName) == 0 && strcmp("_dns-sd", protoName) == 0){ - _conn->flush(); - IPAddress interface = _getRequestMulticastInterface(); - _replyToTypeEnumRequest(interface); - return; - } else { + + if (!protoParsed) + { + protoNameLen = _conn_read8() % 255; + _conn_readS(protoName, protoNameLen); + protoName[protoNameLen] = '\0'; + if (protoNameLen == 4 && protoName[0] == '_') + { + memmove(protoName, protoName + 1, protoNameLen); + protoNameLen--; + protoParsed = true; + } + else if (strcmp("services", serviceName) == 0 && strcmp("_dns-sd", protoName) == 0) + { + _conn->flush(); + IPAddress interface = _getRequestMulticastInterface(); + _replyToTypeEnumRequest(interface); + return; + } + else + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_PROTO: %s\n", protoName); + DEBUG_ESP_PORT.printf("ERR_PROTO: %s\n", protoName); #endif - _conn->flush(); - return; + _conn->flush(); + return; + } } - } - - if(!localParsed){ - char localName[32]; - uint8_t localNameLen = _conn_read8() % 31; - _conn_readS(localName, localNameLen); - localName[localNameLen] = '\0'; - tmp = _conn_read8(); - if(localNameLen == 5 && strcmp("local", localName) == 0 && tmp == 0){ - localParsed = true; - } else { + + if (!localParsed) + { + char localName[32]; + uint8_t localNameLen = _conn_read8() % 31; + _conn_readS(localName, localNameLen); + localName[localNameLen] = '\0'; + tmp = _conn_read8(); + if (localNameLen == 5 && strcmp("local", localName) == 0 && tmp == 0) + { + localParsed = true; + } + else + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_FQDN: %s\n", localName); + DEBUG_ESP_PORT.printf("ERR_FQDN: %s\n", localName); #endif - _conn->flush(); - return; + _conn->flush(); + return; + } } - } - if(serviceNameLen > 0 && protoNameLen > 0){ - servicePort = _getServicePort(serviceName, protoName); - if(servicePort == 0){ + if (serviceNameLen > 0 && protoNameLen > 0) + { + servicePort = _getServicePort(serviceName, protoName); + if (servicePort == 0) + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_NO_SERVICE: %s\n", serviceName); + DEBUG_ESP_PORT.printf("ERR_NO_SERVICE: %s\n", serviceName); #endif - _conn->flush(); - return; + _conn->flush(); + return; + } } - } else if(serviceNameLen > 0 || protoNameLen > 0){ + else if (serviceNameLen > 0 || protoNameLen > 0) + { #ifdef DEBUG_ESP_MDNS_ERR - DEBUG_ESP_PORT.printf("ERR_SERVICE_PROTO: %s\n", serviceName); + DEBUG_ESP_PORT.printf("ERR_SERVICE_PROTO: %s\n", serviceName); #endif - _conn->flush(); - return; - } + _conn->flush(); + return; + } - // RESPOND + // RESPOND #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]); + DEBUG_ESP_PORT.printf("RX: REQ, ID:%u, Q:%u, A:%u, NS:%u, ADD:%u\n", packetHeader[0], packetHeader[2], packetHeader[3], packetHeader[4], packetHeader[5]); #endif - uint16_t currentType; - uint16_t currentClass; - - int numQuestions = packetHeader[2]; - if(numQuestions > 4) numQuestions = 4; - uint16_t questions[4]; - int question = 0; + uint16_t currentType; + uint16_t currentClass; - while(numQuestions--){ - currentType = _conn_read16(); - if(currentType & MDNS_NAME_REF){ //new header handle it better! - currentType = _conn_read16(); + int numQuestions = packetHeader[2]; + if (numQuestions > 4) + { + numQuestions = 4; } - currentClass = _conn_read16(); - if(currentClass & MDNS_CLASS_IN) questions[question++] = currentType; + uint16_t questions[4]; + int question = 0; - if(numQuestions > 0){ - if(_conn_read16() != 0xC00C){//new question but for another host/service - _conn->flush(); - numQuestions = 0; - } - } + while (numQuestions--) + { + currentType = _conn_read16(); + if (currentType & MDNS_NAME_REF) //new header handle it better! + { + currentType = _conn_read16(); + } + currentClass = _conn_read16(); + if (currentClass & MDNS_CLASS_IN) + { + questions[question++] = currentType; + } + + if (numQuestions > 0) + { + if (_conn_read16() != 0xC00C) //new question but for another host/service + { + _conn->flush(); + numQuestions = 0; + } + } #ifdef DEBUG_ESP_MDNS_RX - DEBUG_ESP_PORT.printf("REQ: "); - if(hostNameLen > 0) - DEBUG_ESP_PORT.printf("%s.", hostName); - if(serviceNameLen > 0) - DEBUG_ESP_PORT.printf("_%s.", serviceName); - if(protoNameLen > 0) - DEBUG_ESP_PORT.printf("_%s.", protoName); - DEBUG_ESP_PORT.printf("local. "); - - if(currentType == MDNS_TYPE_AAAA) - DEBUG_ESP_PORT.printf(" AAAA "); - else if(currentType == MDNS_TYPE_A) - DEBUG_ESP_PORT.printf(" A "); - else if(currentType == MDNS_TYPE_PTR) - DEBUG_ESP_PORT.printf(" PTR "); - else if(currentType == MDNS_TYPE_SRV) - DEBUG_ESP_PORT.printf(" SRV "); - else if(currentType == MDNS_TYPE_TXT) - DEBUG_ESP_PORT.printf(" TXT "); - else - DEBUG_ESP_PORT.printf(" 0x%04X ", currentType); - - if(currentClass == MDNS_CLASS_IN) - DEBUG_ESP_PORT.printf(" IN "); - else if(currentClass == MDNS_CLASS_IN_FLUSH_CACHE) - DEBUG_ESP_PORT.printf(" IN[F] "); - else - DEBUG_ESP_PORT.printf(" 0x%04X ", currentClass); - - DEBUG_ESP_PORT.printf("\n"); + DEBUG_ESP_PORT.printf("REQ: "); + if (hostNameLen > 0) + { + DEBUG_ESP_PORT.printf("%s.", hostName); + } + if (serviceNameLen > 0) + { + DEBUG_ESP_PORT.printf("_%s.", serviceName); + } + if (protoNameLen > 0) + { + DEBUG_ESP_PORT.printf("_%s.", protoName); + } + DEBUG_ESP_PORT.printf("local. "); + + if (currentType == MDNS_TYPE_AAAA) + { + DEBUG_ESP_PORT.printf(" AAAA "); + } + else if (currentType == MDNS_TYPE_A) + { + DEBUG_ESP_PORT.printf(" A "); + } + else if (currentType == MDNS_TYPE_PTR) + { + DEBUG_ESP_PORT.printf(" PTR "); + } + else if (currentType == MDNS_TYPE_SRV) + { + DEBUG_ESP_PORT.printf(" SRV "); + } + else if (currentType == MDNS_TYPE_TXT) + { + DEBUG_ESP_PORT.printf(" TXT "); + } + else + { + DEBUG_ESP_PORT.printf(" 0x%04X ", currentType); + } + + if (currentClass == MDNS_CLASS_IN) + { + DEBUG_ESP_PORT.printf(" IN "); + } + else if (currentClass == MDNS_CLASS_IN_FLUSH_CACHE) + { + DEBUG_ESP_PORT.printf(" IN[F] "); + } + else + { + DEBUG_ESP_PORT.printf(" 0x%04X ", currentClass); + } + + DEBUG_ESP_PORT.printf("\n"); #endif - } - uint8_t questionMask = 0; - uint8_t responseMask = 0; - for(i=0;i_next) { - if(servicePtr->_port > 0){ - char *service = servicePtr->_name; - char *proto = servicePtr->_proto; - //uint16_t port = servicePtr->_port; +void MDNSResponder::_replyToTypeEnumRequest(IPAddress multicastInterface) +{ + MDNSService* servicePtr; + for (servicePtr = _services; servicePtr; servicePtr = servicePtr->_next) + { + if (servicePtr->_port > 0) + { + char *service = servicePtr->_name; + char *proto = servicePtr->_proto; + //uint16_t port = servicePtr->_port; #ifdef DEBUG_ESP_MDNS_TX - DEBUG_ESP_PORT.printf("TX: service:%s, proto:%s\n", service, proto); + DEBUG_ESP_PORT.printf("TX: service:%s, proto:%s\n", service, proto); #endif - char sdHostName[] = "_services"; - size_t sdHostNameLen = 9; - char sdServiceName[] = "_dns-sd"; - size_t sdServiceNameLen = 7; - char sdProtoName[] = "_udp"; - size_t sdProtoNameLen = 4; - - char underscore[] = "_"; - - // build service name with _ - char serviceName[os_strlen(service) + 2]; - os_strcpy(serviceName, underscore); - os_strcat(serviceName, service); - size_t serviceNameLen = os_strlen(serviceName); - - //build proto name with _ - char protoName[5]; - os_strcpy(protoName, underscore); - os_strcat(protoName, proto); - size_t protoNameLen = 4; - - //local string - char localName[] = "local"; - size_t localNameLen = 5; - - //terminator - char terminator[] = "\0"; - - //Write the header - _conn->flush(); - uint8_t head[12] = { - 0x00, 0x00, //ID = 0 - 0x84, 0x00, //Flags = response + authoritative answer - 0x00, 0x00, //Question count - 0x00, 0x01, //Answer count - 0x00, 0x00, //Name server records - 0x00, 0x00, //Additional records - }; - _conn->append(reinterpret_cast(head), 12); - - // Send the Name field (ie. "_services._dns-sd._udp.local") - _conn->append(reinterpret_cast(&sdHostNameLen), 1); // length of "_services" - _conn->append(reinterpret_cast(sdHostName), sdHostNameLen); // "_services" - _conn->append(reinterpret_cast(&sdServiceNameLen), 1); // length of "_dns-sd" - _conn->append(reinterpret_cast(sdServiceName), sdServiceNameLen);// "_dns-sd" - _conn->append(reinterpret_cast(&sdProtoNameLen), 1); // length of "_udp" - _conn->append(reinterpret_cast(sdProtoName), sdProtoNameLen); // "_udp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - //Send the type, class, ttl and rdata length - uint8_t ptrDataLen = serviceNameLen + protoNameLen + localNameLen + 4; // 4 is three label sizes and the terminator - uint8_t ptrAttrs[10] = { - 0x00, 0x0c, //PTR record query - 0x00, 0x01, //Class IN - 0x00, 0x00, 0x11, 0x94, //TTL 4500 - 0x00, ptrDataLen, //RData length - }; - _conn->append(reinterpret_cast(ptrAttrs), 10); - - //Send the RData (ie. "_http._tcp.local") - _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" - _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - _conn->setMulticastInterface(multicastInterface); - _conn->send(); + char sdHostName[] = "_services"; + size_t sdHostNameLen = 9; + char sdServiceName[] = "_dns-sd"; + size_t sdServiceNameLen = 7; + char sdProtoName[] = "_udp"; + size_t sdProtoNameLen = 4; + + char underscore[] = "_"; + + // build service name with _ + char serviceName[os_strlen(service) + 2]; + os_strcpy(serviceName, underscore); + os_strcat(serviceName, service); + size_t serviceNameLen = os_strlen(serviceName); + + //build proto name with _ + char protoName[5]; + os_strcpy(protoName, underscore); + os_strcat(protoName, proto); + size_t protoNameLen = 4; + + //local string + char localName[] = "local"; + size_t localNameLen = 5; + + //terminator + char terminator[] = "\0"; + + //Write the header + _conn->flush(); + uint8_t head[12] = + { + 0x00, 0x00, //ID = 0 + 0x84, 0x00, //Flags = response + authoritative answer + 0x00, 0x00, //Question count + 0x00, 0x01, //Answer count + 0x00, 0x00, //Name server records + 0x00, 0x00, //Additional records + }; + _conn->append(reinterpret_cast(head), 12); + + // Send the Name field (ie. "_services._dns-sd._udp.local") + _conn->append(reinterpret_cast(&sdHostNameLen), 1); // length of "_services" + _conn->append(reinterpret_cast(sdHostName), sdHostNameLen); // "_services" + _conn->append(reinterpret_cast(&sdServiceNameLen), 1); // length of "_dns-sd" + _conn->append(reinterpret_cast(sdServiceName), sdServiceNameLen);// "_dns-sd" + _conn->append(reinterpret_cast(&sdProtoNameLen), 1); // length of "_udp" + _conn->append(reinterpret_cast(sdProtoName), sdProtoNameLen); // "_udp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + //Send the type, class, ttl and rdata length + uint8_t ptrDataLen = serviceNameLen + protoNameLen + localNameLen + 4; // 4 is three label sizes and the terminator + uint8_t ptrAttrs[10] = + { + 0x00, 0x0c, //PTR record query + 0x00, 0x01, //Class IN + 0x00, 0x00, 0x11, 0x94, //TTL 4500 + 0x00, ptrDataLen, //RData length + }; + _conn->append(reinterpret_cast(ptrAttrs), 10); + + //Send the RData (ie. "_http._tcp.local") + _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" + _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + _conn->setMulticastInterface(multicastInterface); + _conn->send(); + } } - } } -void MDNSResponder::_replyToInstanceRequest(uint8_t questionMask, uint8_t responseMask, char * service, char *proto, uint16_t port, IPAddress multicastInterface) { - int i; - if(questionMask == 0) return; - if(responseMask == 0) return; +void MDNSResponder::_replyToInstanceRequest(uint8_t questionMask, uint8_t responseMask, char * service, char *proto, uint16_t port, IPAddress multicastInterface) +{ + int i; + if (questionMask == 0) + { + return; + } + if (responseMask == 0) + { + return; + } #ifdef DEBUG_ESP_MDNS_TX DEBUG_ESP_PORT.printf("TX: qmask:%01X, rmask:%01X, service:%s, proto:%s, port:%u\n", questionMask, responseMask, service, proto, port); #endif - String instanceName = _instanceName; - size_t instanceNameLen = instanceName.length(); - - String hostName = _hostName; - size_t hostNameLen = hostName.length(); - - char underscore[] = "_"; - - // build service name with _ - char serviceName[os_strlen(service)+2]; - os_strcpy(serviceName,underscore); - os_strcat(serviceName, service); - size_t serviceNameLen = os_strlen(serviceName); - - //build proto name with _ - char protoName[5]; - os_strcpy(protoName,underscore); - os_strcat(protoName, proto); - size_t protoNameLen = 4; - - //local string - char localName[] = "local"; - size_t localNameLen = 5; - - //terminator - char terminator[] = "\0"; - - uint8_t answerMask = responseMask & questionMask; - uint8_t answerCount = 0; - uint8_t additionalMask = responseMask & ~questionMask; - uint8_t additionalCount = 0; - for(i=0;i<4;i++){ - if(answerMask & (1 << i)) - answerCount++; - if(additionalMask & (1 << i)) - additionalCount++; - } - - - //Write the header - _conn->flush(); - uint8_t head[12] = { - 0x00, 0x00, //ID = 0 - 0x84, 0x00, //Flags = response + authoritative answer - 0x00, 0x00, //Question count - 0x00, answerCount, //Answer count - 0x00, 0x00, //Name server records - 0x00, additionalCount, //Additional records - }; - _conn->append(reinterpret_cast(head), 12); - - for(int responseSection = 0; responseSection < 2; ++responseSection) { - - // PTR Response - if((responseSection == 0 ? answerMask : additionalMask) & 0x8){ - // Send the Name field (ie. "_http._tcp.local") - _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" - _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - //Send the type, class, ttl and rdata length - uint8_t ptrDataLen = instanceNameLen + serviceNameLen + protoNameLen + localNameLen + 5; // 5 is four label sizes and the terminator - uint8_t ptrAttrs[10] = { - 0x00, 0x0c, //PTR record query - 0x00, 0x01, //Class IN - 0x00, 0x00, 0x00, 0x78, //TTL 120 - 0x00, ptrDataLen, //RData length - }; - _conn->append(reinterpret_cast(ptrAttrs), 10); - - //Send the RData (ie. "My IOT device._http._tcp.local") - _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" - _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" - _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" - _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - } + String instanceName = _instanceName; + size_t instanceNameLen = instanceName.length(); - //TXT Responce - if((responseSection == 0 ? answerMask : additionalMask) & 0x4){ - //Send the name field (ie. "My IOT device._http._tcp.local") - _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" - _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" - _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" - _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - //Send the type, class, ttl and rdata length - uint8_t txtDataLen = _getServiceTxtLen(service,proto); - uint8_t txtAttrs[10] = { - 0x00, 0x10, //TXT record query - 0x80, 0x01, //Class IN, with cache flush - 0x00, 0x00, 0x11, 0x94, //TTL 4500 - 0x00, txtDataLen, //RData length - }; - _conn->append(reinterpret_cast(txtAttrs), 10); - - //Send the RData - MDNSTxt * txtPtr = _getServiceTxt(service,proto); - while(txtPtr !=0){ - uint8_t txtLen = txtPtr->_txt.length(); - _conn->append(reinterpret_cast(&txtLen), 1); // length of txt - _conn->append(reinterpret_cast(txtPtr->_txt.c_str()), txtLen);// the txt - txtPtr = txtPtr->_next; - } - } + String hostName = _hostName; + size_t hostNameLen = hostName.length(); + char underscore[] = "_"; - //SRV Responce - if((responseSection == 0 ? answerMask : additionalMask) & 0x2){ - //Send the name field (ie. "My IOT device._http._tcp.local") - _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" - _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" - _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" - _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" - _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" - _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - //Send the type, class, ttl, rdata length, priority and weight - uint8_t srvDataSize = hostNameLen + localNameLen + 3; // 3 is 2 lable size bytes and the terminator - srvDataSize += 6; // Size of Priority, weight and port - uint8_t srvAttrs[10] = { - 0x00, 0x21, //Type SRV - 0x80, 0x01, //Class IN, with cache flush - 0x00, 0x00, 0x00, 0x78, //TTL 120 - 0x00, srvDataSize, //RData length - }; - _conn->append(reinterpret_cast(srvAttrs), 10); - - //Send the RData Priority weight and port - uint8_t srvRData[6] = { - 0x00, 0x00, //Priority 0 - 0x00, 0x00, //Weight 0 - (uint8_t)((port >> 8) & 0xFF), (uint8_t)(port & 0xFF) - }; - _conn->append(reinterpret_cast(srvRData), 6); - //Send the RData (ie. "esp8266.local") - _conn->append(reinterpret_cast(&hostNameLen), 1); // length of "esp8266" - _conn->append(reinterpret_cast(hostName.c_str()), hostNameLen);// "esp8266" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator + // build service name with _ + char serviceName[os_strlen(service) + 2]; + os_strcpy(serviceName, underscore); + os_strcat(serviceName, service); + size_t serviceNameLen = os_strlen(serviceName); + //build proto name with _ + char protoName[5]; + os_strcpy(protoName, underscore); + os_strcat(protoName, proto); + size_t protoNameLen = 4; + + //local string + char localName[] = "local"; + size_t localNameLen = 5; + + //terminator + char terminator[] = "\0"; + + uint8_t answerMask = responseMask & questionMask; + uint8_t answerCount = 0; + uint8_t additionalMask = responseMask & ~questionMask; + uint8_t additionalCount = 0; + for (i = 0; i < 4; i++) + { + if (answerMask & (1 << i)) + { + answerCount++; + } + if (additionalMask & (1 << i)) + { + additionalCount++; + } } - // A Response - if((responseSection == 0 ? answerMask : additionalMask) & 0x1){ - //Send the RData (ie. "esp8266.local") - _conn->append(reinterpret_cast(&hostNameLen), 1); // length of "esp8266" - _conn->append(reinterpret_cast(hostName.c_str()), hostNameLen);// "esp8266" - _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" - _conn->append(reinterpret_cast(localName), localNameLen); // "local" - _conn->append(reinterpret_cast(&terminator), 1); // terminator - - uint8_t aaaAttrs[10] = { - 0x00, 0x01, //TYPE A - 0x80, 0x01, //Class IN, with cache flush - 0x00, 0x00, 0x00, 0x78, //TTL 120 - 0x00, 0x04, //DATA LEN - }; - _conn->append(reinterpret_cast(aaaAttrs), 10); - - // Send RData - uint32_t ip = multicastInterface; - uint8_t aaaRData[4] = { - (uint8_t)(ip & 0xFF), //IP first octet - (uint8_t)((ip >> 8) & 0xFF), //IP second octet - (uint8_t)((ip >> 16) & 0xFF), //IP third octet - (uint8_t)((ip >> 24) & 0xFF) //IP fourth octet - }; - _conn->append(reinterpret_cast(aaaRData), 4); + + //Write the header + _conn->flush(); + uint8_t head[12] = + { + 0x00, 0x00, //ID = 0 + 0x84, 0x00, //Flags = response + authoritative answer + 0x00, 0x00, //Question count + 0x00, answerCount, //Answer count + 0x00, 0x00, //Name server records + 0x00, additionalCount, //Additional records + }; + _conn->append(reinterpret_cast(head), 12); + + for (int responseSection = 0; responseSection < 2; ++responseSection) + { + + // PTR Response + if ((responseSection == 0 ? answerMask : additionalMask) & 0x8) + { + // Send the Name field (ie. "_http._tcp.local") + _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" + _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + //Send the type, class, ttl and rdata length + uint8_t ptrDataLen = instanceNameLen + serviceNameLen + protoNameLen + localNameLen + 5; // 5 is four label sizes and the terminator + uint8_t ptrAttrs[10] = + { + 0x00, 0x0c, //PTR record query + 0x00, 0x01, //Class IN + 0x00, 0x00, 0x00, 0x78, //TTL 120 + 0x00, ptrDataLen, //RData length + }; + _conn->append(reinterpret_cast(ptrAttrs), 10); + + //Send the RData (ie. "My IOT device._http._tcp.local") + _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" + _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" + _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" + _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + } + + //TXT Responce + if ((responseSection == 0 ? answerMask : additionalMask) & 0x4) + { + //Send the name field (ie. "My IOT device._http._tcp.local") + _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" + _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" + _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" + _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + //Send the type, class, ttl and rdata length + uint8_t txtDataLen = _getServiceTxtLen(service, proto); + uint8_t txtAttrs[10] = + { + 0x00, 0x10, //TXT record query + 0x80, 0x01, //Class IN, with cache flush + 0x00, 0x00, 0x11, 0x94, //TTL 4500 + 0x00, txtDataLen, //RData length + }; + _conn->append(reinterpret_cast(txtAttrs), 10); + + //Send the RData + MDNSTxt * txtPtr = _getServiceTxt(service, proto); + while (txtPtr != 0) + { + uint8_t txtLen = txtPtr->_txt.length(); + _conn->append(reinterpret_cast(&txtLen), 1); // length of txt + _conn->append(reinterpret_cast(txtPtr->_txt.c_str()), txtLen);// the txt + txtPtr = txtPtr->_next; + } + } + + + //SRV Responce + if ((responseSection == 0 ? answerMask : additionalMask) & 0x2) + { + //Send the name field (ie. "My IOT device._http._tcp.local") + _conn->append(reinterpret_cast(&instanceNameLen), 1); // length of "My IOT device" + _conn->append(reinterpret_cast(instanceName.c_str()), instanceNameLen);// "My IOT device" + _conn->append(reinterpret_cast(&serviceNameLen), 1); // length of "_http" + _conn->append(reinterpret_cast(serviceName), serviceNameLen); // "_http" + _conn->append(reinterpret_cast(&protoNameLen), 1); // length of "_tcp" + _conn->append(reinterpret_cast(protoName), protoNameLen); // "_tcp" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + //Send the type, class, ttl, rdata length, priority and weight + uint8_t srvDataSize = hostNameLen + localNameLen + 3; // 3 is 2 lable size bytes and the terminator + srvDataSize += 6; // Size of Priority, weight and port + uint8_t srvAttrs[10] = + { + 0x00, 0x21, //Type SRV + 0x80, 0x01, //Class IN, with cache flush + 0x00, 0x00, 0x00, 0x78, //TTL 120 + 0x00, srvDataSize, //RData length + }; + _conn->append(reinterpret_cast(srvAttrs), 10); + + //Send the RData Priority weight and port + uint8_t srvRData[6] = + { + 0x00, 0x00, //Priority 0 + 0x00, 0x00, //Weight 0 + (uint8_t)((port >> 8) & 0xFF), (uint8_t)(port & 0xFF) + }; + _conn->append(reinterpret_cast(srvRData), 6); + //Send the RData (ie. "esp8266.local") + _conn->append(reinterpret_cast(&hostNameLen), 1); // length of "esp8266" + _conn->append(reinterpret_cast(hostName.c_str()), hostNameLen);// "esp8266" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + } + + // A Response + if ((responseSection == 0 ? answerMask : additionalMask) & 0x1) + { + //Send the RData (ie. "esp8266.local") + _conn->append(reinterpret_cast(&hostNameLen), 1); // length of "esp8266" + _conn->append(reinterpret_cast(hostName.c_str()), hostNameLen);// "esp8266" + _conn->append(reinterpret_cast(&localNameLen), 1); // length "local" + _conn->append(reinterpret_cast(localName), localNameLen); // "local" + _conn->append(reinterpret_cast(&terminator), 1); // terminator + + uint8_t aaaAttrs[10] = + { + 0x00, 0x01, //TYPE A + 0x80, 0x01, //Class IN, with cache flush + 0x00, 0x00, 0x00, 0x78, //TTL 120 + 0x00, 0x04, //DATA LEN + }; + _conn->append(reinterpret_cast(aaaAttrs), 10); + + // Send RData + uint32_t ip = multicastInterface; + uint8_t aaaRData[4] = + { + (uint8_t)(ip & 0xFF), //IP first octet + (uint8_t)((ip >> 8) & 0xFF), //IP second octet + (uint8_t)((ip >> 16) & 0xFF), //IP third octet + (uint8_t)((ip >> 24) & 0xFF) //IP fourth octet + }; + _conn->append(reinterpret_cast(aaaRData), 4); + } } - } - _conn->setMulticastInterface(multicastInterface); - _conn->send(); + _conn->setMulticastInterface(multicastInterface); + _conn->send(); } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) diff --git a/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.h b/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.h index 6d241ae06e..9d3cfd2f62 100644 --- a/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.h +++ b/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.h @@ -1,43 +1,43 @@ /* -ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) -Version 1.1 -Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) -ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.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 -running on ESP8266 chip. Only support for resolving address queries is currently -implemented. - -Requirements: -- ESP8266WiFi library - -Usage: -- Include the ESP8266 Multicast DNS library in the sketch. -- 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 - Adafruit CC3000 class instance. Optionally provide a time to live (in seconds) - for the DNS record--the default is 1 hour. -- Call the update method in each iteration of the sketch's loop function. - -License (MIT license): - 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. + ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) + Version 1.1 + Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) + ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.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 + running on ESP8266 chip. Only support for resolving address queries is currently + implemented. + + Requirements: + - ESP8266WiFi library + + Usage: + - Include the ESP8266 Multicast DNS library in the sketch. + - 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 + Adafruit CC3000 class instance. Optionally provide a time to live (in seconds) + for the DNS record--the default is 1 hour. + - Call the update method in each iteration of the sketch's loop function. + + License (MIT license): + 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. */ #ifndef ESP8266MDNS_LEGACY_H @@ -54,95 +54,108 @@ License (MIT license): class UdpContext; -namespace Legacy_MDNSResponder { +namespace Legacy_MDNSResponder +{ struct MDNSService; struct MDNSTxt; struct MDNSAnswer; -class MDNSResponder { +class MDNSResponder +{ public: - MDNSResponder(); - ~MDNSResponder(); - bool begin(const char* hostName); - bool begin(const String& hostName) { - return begin(hostName.c_str()); - } - //for compatibility - bool begin(const char* hostName, IPAddress ip, uint32_t ttl=120){ - (void) ip; - (void) ttl; - return begin(hostName); - } - bool begin(const String& hostName, IPAddress ip, uint32_t ttl=120) { - return begin(hostName.c_str(), ip, ttl); - } - /* Application should call this whenever AP is configured/disabled */ - void notifyAPChange(); - void update(); - - void addService(char *service, char *proto, uint16_t port); - void addService(const char *service, const char *proto, uint16_t port){ - addService((char *)service, (char *)proto, port); - } - void addService(const String& service, const String& proto, uint16_t port){ - addService(service.c_str(), proto.c_str(), port); - } - - bool addServiceTxt(char *name, char *proto, char * key, 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); - } - 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()); - } - - int queryService(char *service, char *proto); - int queryService(const char *service, const char *proto){ - return queryService((char *)service, (char *)proto); - } - int queryService(const String& service, const String& proto){ - return queryService(service.c_str(), proto.c_str()); - } - String hostname(int idx); - IPAddress IP(int idx); - uint16_t port(int idx); - - void enableArduino(uint16_t port, bool auth=false); - - void setInstanceName(String name); - void setInstanceName(const char * name){ - setInstanceName(String(name)); - } - void setInstanceName(char * name){ - setInstanceName(String(name)); - } + MDNSResponder(); + ~MDNSResponder(); + bool begin(const char* hostName); + bool begin(const String& hostName) + { + return begin(hostName.c_str()); + } + //for compatibility + bool begin(const char* hostName, IPAddress ip, uint32_t ttl = 120) + { + (void) ip; + (void) ttl; + return begin(hostName); + } + bool begin(const String& hostName, IPAddress ip, uint32_t ttl = 120) + { + return begin(hostName.c_str(), ip, ttl); + } + /* Application should call this whenever AP is configured/disabled */ + void notifyAPChange(); + void update(); + + void addService(char *service, char *proto, uint16_t port); + void addService(const char *service, const char *proto, uint16_t port) + { + addService((char *)service, (char *)proto, port); + } + void addService(const String& service, const String& proto, uint16_t port) + { + addService(service.c_str(), proto.c_str(), port); + } + + bool addServiceTxt(char *name, char *proto, char * key, 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); + } + 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()); + } + + int queryService(char *service, char *proto); + int queryService(const char *service, const char *proto) + { + return queryService((char *)service, (char *)proto); + } + int queryService(const String& service, const String& proto) + { + return queryService(service.c_str(), proto.c_str()); + } + String hostname(int idx); + IPAddress IP(int idx); + uint16_t port(int idx); + + void enableArduino(uint16_t port, bool auth = false); + + void setInstanceName(String name); + void setInstanceName(const char * name) + { + setInstanceName(String(name)); + } + void setInstanceName(char * name) + { + setInstanceName(String(name)); + } private: - struct MDNSService * _services; - UdpContext* _conn; - String _hostName; - String _instanceName; - struct MDNSAnswer * _answers; - struct MDNSQuery * _query; - bool _newQuery; - bool _waitingForAnswers; - WiFiEventHandler _disconnectedHandler; - WiFiEventHandler _gotIPHandler; - - - uint16_t _getServicePort(char *service, char *proto); - MDNSTxt * _getServiceTxt(char *name, char *proto); - uint16_t _getServiceTxtLen(char *name, char *proto); - IPAddress _getRequestMulticastInterface(); - void _parsePacket(); - void _replyToTypeEnumRequest(IPAddress multicastInterface); - void _replyToInstanceRequest(uint8_t questionMask, uint8_t responseMask, char * service, char *proto, uint16_t port, IPAddress multicastInterface); - MDNSAnswer* _getAnswerFromIdx(int idx); - int _getNumAnswers(); - bool _listen(); - void _restart(); + struct MDNSService * _services; + UdpContext* _conn; + String _hostName; + String _instanceName; + struct MDNSAnswer * _answers; + struct MDNSQuery * _query; + bool _newQuery; + bool _waitingForAnswers; + WiFiEventHandler _disconnectedHandler; + WiFiEventHandler _gotIPHandler; + + + uint16_t _getServicePort(char *service, char *proto); + MDNSTxt * _getServiceTxt(char *name, char *proto); + uint16_t _getServiceTxtLen(char *name, char *proto); + IPAddress _getRequestMulticastInterface(); + void _parsePacket(); + void _replyToTypeEnumRequest(IPAddress multicastInterface); + void _replyToInstanceRequest(uint8_t questionMask, uint8_t responseMask, char * service, char *proto, uint16_t port, IPAddress multicastInterface); + MDNSAnswer* _getAnswerFromIdx(int idx); + int _getNumAnswers(); + bool _listen(); + void _restart(); }; } // namespace Legacy_MDNSResponder diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.cpp b/libraries/ESP8266mDNS/src/LEAmDNS.cpp index 7b40034254..5c15be96ff 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS.cpp @@ -1,1164 +1,1311 @@ -/* - * LEAmDNS.cpp - * - * License (MIT license): - * 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. - * - */ - -#include - -#include "LEAmDNS_Priv.h" - - -namespace esp8266 { - -/* - * LEAmDNS - */ -namespace MDNSImplementation { - -/** - * STRINGIZE - */ -#ifndef STRINGIZE - #define STRINGIZE(x) #x -#endif -#ifndef STRINGIZE_VALUE_OF - #define STRINGIZE_VALUE_OF(x) STRINGIZE(x) -#endif - - -/** - * INTERFACE - */ - -/** - * MDNSResponder::MDNSResponder - */ -MDNSResponder::MDNSResponder(void) -: m_pServices(0), - m_pUDPContext(0), - m_pcHostname(0), - m_pServiceQueries(0), - m_fnServiceTxtCallback(0), -#ifdef ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE - m_bPassivModeEnabled(true) { -#else - m_bPassivModeEnabled(false) { -#endif - -} - -/* - * MDNSResponder::~MDNSResponder - */ -MDNSResponder::~MDNSResponder(void) { - - _resetProbeStatus(false); - _releaseServiceQueries(); - _releaseHostname(); - _releaseUDPContext(); - _releaseServices(); -} - -/* - * MDNSResponder::begin - * - * Set the host domain (for probing) and install WiFi event handlers for - * IP assignment and disconnection management. In both cases, the MDNS responder - * is restarted (reset and restart probe status) - * Finally the responder is (re)started - * - */ -bool MDNSResponder::begin(const char* p_pcHostname) { - - bool bResult = false; - - if (0 == m_pUDPContext) { - if (_setHostname(p_pcHostname)) { - - m_GotIPHandler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP& pEvent) { - (void) pEvent; - // Ensure that _restart() runs in USER context - schedule_function(std::bind(&MDNSResponder::_restart, this)); - }); - - m_DisconnectedHandler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected& pEvent) { - (void) pEvent; - // Ensure that _restart() runs in USER context - schedule_function(std::bind(&MDNSResponder::_restart, this)); - }); - - bResult = _restart(); - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: FAILED for '%s'!\n"), (p_pcHostname ?: "-")); } ); - } - else { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: Ignoring multiple calls to begin (Ignored host domain: '%s')!\n"), (p_pcHostname ?: "-"));); - } - return bResult; -} - -/* - * MDNSResponder::begin (LEGACY) - */ -bool MDNSResponder::begin(const char* p_pcHostname, - IPAddress p_IPAddress, - uint32_t p_u32TTL /*= 120*/) { - - (void) p_IPAddress; - (void) p_u32TTL; - return begin(p_pcHostname); -} - -/* - * MDNSResponder::close - * - * Ends the MDNS responder. - * Announced services are unannounced (by multicasting a goodbye message) - * - */ -bool MDNSResponder::close(void) { - - m_GotIPHandler.reset(); // reset WiFi event callbacks. - m_DisconnectedHandler.reset(); - - _announce(false, true); - _resetProbeStatus(false); // Stop probing - - _releaseServiceQueries(); - _releaseUDPContext(); - _releaseHostname(); - - return true; -} - -/* - * MDNSResponder::end - * - * Ends the MDNS responder. - * for compatibility with esp32 - * - */ - -bool MDNSResponder::end(void) { - return close(); -} - -/* - * MDNSResponder::setHostname - * - * Replaces the current hostname and restarts probing. - * For services without own instance name (when the host name was used a instance - * name), the instance names are replaced also (and the probing is restarted). - * - */ -bool MDNSResponder::setHostname(const char* p_pcHostname) { - - bool bResult = false; - - if (_setHostname(p_pcHostname)) { - m_HostProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; - - // Replace 'auto-set' service names - bResult = true; - for (stcMDNSService* pService=m_pServices; ((bResult) && (pService)); pService=pService->m_pNext) { - if (pService->m_bAutoName) { - bResult = pService->setName(p_pcHostname); - pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; - } - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setHostname: FAILED for '%s'!\n"), (p_pcHostname ?: "-")); } ); - return bResult; -} - -/* - * MDNSResponder::setHostname (LEGACY) - */ -bool MDNSResponder::setHostname(String p_strHostname) { - - return setHostname(p_strHostname.c_str()); -} - - -/* - * SERVICES - */ - -/* - * MDNSResponder::addService - * - * Add service; using hostname if no name is explicitly provided for the service - * The usual '_' underline, which is prepended to service and protocol, eg. _http, - * may be given. If not, it is added automatically. - * - */ -MDNSResponder::hMDNSService MDNSResponder::addService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - uint16_t p_u16Port) { - - hMDNSService hResult = 0; - - if (((!p_pcName) || // NO name OR - (MDNS_DOMAIN_LABEL_MAXLENGTH >= os_strlen(p_pcName))) && // Fitting name - (p_pcService) && - (MDNS_SERVICE_NAME_LENGTH >= os_strlen(p_pcService)) && - (p_pcProtocol) && - ((MDNS_SERVICE_PROTOCOL_LENGTH - 1) != os_strlen(p_pcProtocol)) && - (p_u16Port)) { - - if (!_findService((p_pcName ?: m_pcHostname), p_pcService, p_pcProtocol)) { // Not already used - if (0 != (hResult = (hMDNSService)_allocService(p_pcName, p_pcService, p_pcProtocol, p_u16Port))) { - - // Start probing - ((stcMDNSService*)hResult)->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; - } - } - } // else: bad arguments - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addService: %s to add '%s.%s.%s'!\n"), (hResult ? "Succeeded" : "FAILED"), (p_pcName ?: "-"), p_pcService, p_pcProtocol); ); - DEBUG_EX_ERR(if (!hResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addService: FAILED to add '%s.%s.%s'!\n"), (p_pcName ?: "-"), p_pcService, p_pcProtocol); } ); - return hResult; -} - -/* - * MDNSResponder::removeService - * - * Unanounce a service (by sending a goodbye message) and remove it - * from the MDNS responder - * - */ -bool MDNSResponder::removeService(const MDNSResponder::hMDNSService p_hService) { - - stcMDNSService* pService = 0; - bool bResult = (((pService = _findService(p_hService))) && - (_announceService(*pService, false)) && - (_releaseService(pService))); - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeService: FAILED!\n")); } ); - return bResult; -} - -/* - * MDNSResponder::removeService - */ -bool MDNSResponder::removeService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol) { - - return removeService((hMDNSService)_findService((p_pcName ?: m_pcHostname), p_pcService, p_pcProtocol)); -} - -/* - * MDNSResponder::addService (LEGACY) - */ -bool MDNSResponder::addService(String p_strService, - String p_strProtocol, - uint16_t p_u16Port) { - - return (0 != addService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str(), p_u16Port)); -} - -/* - * MDNSResponder::setServiceName - */ -bool MDNSResponder::setServiceName(const MDNSResponder::hMDNSService p_hService, - const char* p_pcInstanceName) { - - stcMDNSService* pService = 0; - bool bResult = (((!p_pcInstanceName) || - (MDNS_DOMAIN_LABEL_MAXLENGTH >= os_strlen(p_pcInstanceName))) && - ((pService = _findService(p_hService))) && - (pService->setName(p_pcInstanceName)) && - ((pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart))); - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setServiceName: FAILED for '%s'!\n"), (p_pcInstanceName ?: "-")); } ); - return bResult; -} - -/* - * SERVICE TXT - */ - -/* - * MDNSResponder::addServiceTxt - * - * Add a static service TXT item ('Key'='Value') to a service. - * - */ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - const char* p_pcValue) { - - hMDNSTxt hTxt = 0; - stcMDNSService* pService = _findService(p_hService); - if (pService) { - hTxt = (hMDNSTxt)_addServiceTxt(pService, p_pcKey, p_pcValue, false); - } - DEBUG_EX_ERR(if (!hTxt) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addServiceTxt: FAILED for '%s=%s'!\n"), (p_pcKey ?: "-"), (p_pcValue ?: "-")); } ); - return hTxt; -} - -/* - * MDNSResponder::addServiceTxt (uint32_t) - * - * Formats: http://www.cplusplus.com/reference/cstdio/printf/ - */ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint32_t p_u32Value) { - char acBuffer[32]; *acBuffer = 0; - sprintf(acBuffer, "%u", p_u32Value); - - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::addServiceTxt (uint16_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint16_t p_u16Value) { - char acBuffer[16]; *acBuffer = 0; - sprintf(acBuffer, "%hu", p_u16Value); - - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::addServiceTxt (uint8_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint8_t p_u8Value) { - char acBuffer[8]; *acBuffer = 0; - sprintf(acBuffer, "%hhu", p_u8Value); - - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::addServiceTxt (int32_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int32_t p_i32Value) { - char acBuffer[32]; *acBuffer = 0; - sprintf(acBuffer, "%i", p_i32Value); - - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::addServiceTxt (int16_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int16_t p_i16Value) { - char acBuffer[16]; *acBuffer = 0; - sprintf(acBuffer, "%hi", p_i16Value); - - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::addServiceTxt (int8_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int8_t p_i8Value) { - char acBuffer[8]; *acBuffer = 0; - sprintf(acBuffer, "%hhi", p_i8Value); - - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::removeServiceTxt - * - * Remove a static service TXT item from a service. - */ -bool MDNSResponder::removeServiceTxt(const MDNSResponder::hMDNSService p_hService, - const MDNSResponder::hMDNSTxt p_hTxt) { - - bool bResult = false; - - stcMDNSService* pService = _findService(p_hService); - if (pService) { - stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_hTxt); - if (pTxt) { - bResult = _releaseServiceTxt(pService, pTxt); - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceTxt: FAILED!\n")); } ); - return bResult; -} - -/* - * MDNSResponder::removeServiceTxt - */ -bool MDNSResponder::removeServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey) { - - bool bResult = false; - - stcMDNSService* pService = _findService(p_hService); - if (pService) { - stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_pcKey); - if (pTxt) { - bResult = _releaseServiceTxt(pService, pTxt); - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceTxt: FAILED for '%s'!\n"), (p_pcKey ?: "-")); } ); - return bResult; -} - -/* - * MDNSResponder::removeServiceTxt - */ -bool MDNSResponder::removeServiceTxt(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - const char* p_pcKey) { - - bool bResult = false; - - stcMDNSService* pService = _findService((p_pcName ?: m_pcHostname), p_pcService, p_pcProtocol); - if (pService) { - stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_pcKey); - if (pTxt) { - bResult = _releaseServiceTxt(pService, pTxt); - } - } - return bResult; -} - -/* - * MDNSResponder::addServiceTxt (LEGACY) - */ -bool MDNSResponder::addServiceTxt(const char* p_pcService, - const char* p_pcProtocol, - const char* p_pcKey, - const char* p_pcValue) { - - return (0 != _addServiceTxt(_findService(m_pcHostname, p_pcService, p_pcProtocol), p_pcKey, p_pcValue, false)); -} - -/* - * MDNSResponder::addServiceTxt (LEGACY) - */ -bool MDNSResponder::addServiceTxt(String p_strService, - String p_strProtocol, - String p_strKey, - String p_strValue) { - - return (0 != _addServiceTxt(_findService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str()), p_strKey.c_str(), p_strValue.c_str(), false)); -} - -/* - * MDNSResponder::setDynamicServiceTxtCallback (global) - * - * Set a global callback for dynamic service TXT items. The callback is called, whenever - * service TXT items are needed. - * - */ -bool MDNSResponder::setDynamicServiceTxtCallback(MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) { - - m_fnServiceTxtCallback = p_fnCallback; - - return true; -} - -/* - * MDNSResponder::setDynamicServiceTxtCallback (service specific) - * - * Set a service specific callback for dynamic service TXT items. The callback is called, whenever - * service TXT items are needed for the given service. - * - */ -bool MDNSResponder::setDynamicServiceTxtCallback(MDNSResponder::hMDNSService p_hService, - MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) { - - bool bResult = false; - - stcMDNSService* pService = _findService(p_hService); - if (pService) { - pService->m_fnTxtCallback = p_fnCallback; - - bResult = true; - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setDynamicServiceTxtCallback: FAILED!\n")); } ); - return bResult; -} - -/* - * MDNSResponder::addDynamicServiceTxt - */ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - const char* p_pcValue) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addDynamicServiceTxt (%s=%s)\n"), p_pcKey, p_pcValue);); - - hMDNSTxt hTxt = 0; - - stcMDNSService* pService = _findService(p_hService); - if (pService) { - hTxt = _addServiceTxt(pService, p_pcKey, p_pcValue, true); - } - DEBUG_EX_ERR(if (!hTxt) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addDynamicServiceTxt: FAILED for '%s=%s'!\n"), (p_pcKey ?: "-"), (p_pcValue ?: "-")); } ); - return hTxt; -} - -/* - * MDNSResponder::addDynamicServiceTxt (uint32_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint32_t p_u32Value) { - - char acBuffer[32]; *acBuffer = 0; - sprintf(acBuffer, "%u", p_u32Value); - - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::addDynamicServiceTxt (uint16_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint16_t p_u16Value) { - - char acBuffer[16]; *acBuffer = 0; - sprintf(acBuffer, "%hu", p_u16Value); - - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::addDynamicServiceTxt (uint8_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint8_t p_u8Value) { - - char acBuffer[8]; *acBuffer = 0; - sprintf(acBuffer, "%hhu", p_u8Value); - - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::addDynamicServiceTxt (int32_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int32_t p_i32Value) { - - char acBuffer[32]; *acBuffer = 0; - sprintf(acBuffer, "%i", p_i32Value); - - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::addDynamicServiceTxt (int16_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int16_t p_i16Value) { - - char acBuffer[16]; *acBuffer = 0; - sprintf(acBuffer, "%hi", p_i16Value); - - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - * MDNSResponder::addDynamicServiceTxt (int8_t) - */ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int8_t p_i8Value) { - - char acBuffer[8]; *acBuffer = 0; - sprintf(acBuffer, "%hhi", p_i8Value); - - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} - - -/** - * STATIC SERVICE QUERY (LEGACY) - */ - -/* - * MDNSResponder::queryService - * - * Perform a (blocking) static service query. - * The arrived answers can be queried by calling: - * - answerHostname (or 'hostname') - * - answerIP (or 'IP') - * - answerPort (or 'port') - * - */ -uint32_t MDNSResponder::queryService(const char* p_pcService, - const char* p_pcProtocol, - const uint16_t p_u16Timeout /*= MDNS_QUERYSERVICES_WAIT_TIME*/) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService '%s.%s'\n"), p_pcService, p_pcProtocol);); - - uint32_t u32Result = 0; - - stcMDNSServiceQuery* pServiceQuery = 0; - if ((p_pcService) && - (os_strlen(p_pcService)) && - (p_pcProtocol) && - (os_strlen(p_pcProtocol)) && - (p_u16Timeout) && - (_removeLegacyServiceQuery()) && - ((pServiceQuery = _allocServiceQuery())) && - (_buildDomainForService(p_pcService, p_pcProtocol, pServiceQuery->m_ServiceTypeDomain))) { - - pServiceQuery->m_bLegacyQuery = true; - - if (_sendMDNSServiceQuery(*pServiceQuery)) { - // Wait for answers to arrive - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService: Waiting %u ms for answers...\n"), p_u16Timeout);); - delay(p_u16Timeout); - - // All answers should have arrived by now -> stop adding new answers - pServiceQuery->m_bAwaitingAnswers = false; - u32Result = pServiceQuery->answerCount(); - } - else { // FAILED to send query - _removeServiceQuery(pServiceQuery); - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService: INVALID input data!\n"), p_pcService, p_pcProtocol);); - } - return u32Result; -} - -/* - * MDNSResponder::removeQuery - * - * Remove the last static service query (and all answers). - * - */ -bool MDNSResponder::removeQuery(void) { - - return _removeLegacyServiceQuery(); -} - -/* - * MDNSResponder::queryService (LEGACY) - */ -uint32_t MDNSResponder::queryService(String p_strService, - String p_strProtocol) { - - return queryService(p_strService.c_str(), p_strProtocol.c_str()); -} - -/* - * MDNSResponder::answerHostname - */ -const char* MDNSResponder::answerHostname(const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - - if ((pSQAnswer) && - (pSQAnswer->m_HostDomain.m_u16NameLength) && - (!pSQAnswer->m_pcHostDomain)) { - - char* pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); - if (pcHostDomain) { - pSQAnswer->m_HostDomain.c_str(pcHostDomain); - } - } - return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); -} - -#ifdef MDNS_IP4_SUPPORT - /* - * MDNSResponder::answerIP - */ - IPAddress MDNSResponder::answerIP(const uint32_t p_u32AnswerIndex) { - - const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); - const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - const stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = (((pSQAnswer) && (pSQAnswer->m_pIP4Addresses)) ? pSQAnswer->IP4AddressAtIndex(0) : 0); - return (pIP4Address ? pIP4Address->m_IPAddress : IPAddress()); - } -#endif - -#ifdef MDNS_IP6_SUPPORT - /* - * MDNSResponder::answerIP6 - */ - IPAddress MDNSResponder::answerIP6(const uint32_t p_u32AnswerIndex) { - - const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); - const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - const stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = (((pSQAnswer) && (pSQAnswer->m_pIP6Addresses)) ? pSQAnswer->IP6AddressAtIndex(0) : 0); - return (pIP6Address ? pIP6Address->m_IPAddress : IP6Address()); - } -#endif - -/* - * MDNSResponder::answerPort - */ -uint16_t MDNSResponder::answerPort(const uint32_t p_u32AnswerIndex) { - - const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); - const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->m_u16Port : 0); -} - -/* - * MDNSResponder::hostname (LEGACY) - */ -String MDNSResponder::hostname(const uint32_t p_u32AnswerIndex) { - - return String(answerHostname(p_u32AnswerIndex)); -} - -/* - * MDNSResponder::IP (LEGACY) - */ -IPAddress MDNSResponder::IP(const uint32_t p_u32AnswerIndex) { - - return answerIP(p_u32AnswerIndex); -} - -/* - * MDNSResponder::port (LEGACY) - */ -uint16_t MDNSResponder::port(const uint32_t p_u32AnswerIndex) { - - return answerPort(p_u32AnswerIndex); -} - - -/** - * DYNAMIC SERVICE QUERY - */ - -/* - * MDNSResponder::installServiceQuery - * - * Add a dynamic service query and a corresponding callback to the MDNS responder. - * The callback will be called for every answer update. - * The answers can also be queried by calling: - * - answerServiceDomain - * - answerHostDomain - * - answerIP4Address/answerIP6Address - * - answerPort - * - answerTxts - * - */ -MDNSResponder::hMDNSServiceQuery MDNSResponder::installServiceQuery(const char* p_pcService, - const char* p_pcProtocol, - MDNSResponder::MDNSServiceQueryCallbackFunc p_fnCallback) { - hMDNSServiceQuery hResult = 0; - - stcMDNSServiceQuery* pServiceQuery = 0; - if ((p_pcService) && - (os_strlen(p_pcService)) && - (p_pcProtocol) && - (os_strlen(p_pcProtocol)) && - (p_fnCallback) && - ((pServiceQuery = _allocServiceQuery())) && - (_buildDomainForService(p_pcService, p_pcProtocol, pServiceQuery->m_ServiceTypeDomain))) { - - pServiceQuery->m_fnCallback = p_fnCallback; - pServiceQuery->m_bLegacyQuery = false; - - if (_sendMDNSServiceQuery(*pServiceQuery)) { - pServiceQuery->m_u8SentCount = 1; - pServiceQuery->m_ResendTimeout.reset(MDNS_DYNAMIC_QUERY_RESEND_DELAY); - - hResult = (hMDNSServiceQuery)pServiceQuery; - } - else { - _removeServiceQuery(pServiceQuery); - } - } - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: %s for '%s.%s'!\n\n"), (hResult ? "Succeeded" : "FAILED"), (p_pcService ?: "-"), (p_pcProtocol ?: "-"));); - DEBUG_EX_ERR(if (!hResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: FAILED for '%s.%s'!\n\n"), (p_pcService ?: "-"), (p_pcProtocol ?: "-")); } ); - return hResult; -} - -/* - * MDNSResponder::removeServiceQuery - * - * Remove a dynamic service query (and all collected answers) from the MDNS responder - * - */ -bool MDNSResponder::removeServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { - - stcMDNSServiceQuery* pServiceQuery = 0; - bool bResult = (((pServiceQuery = _findServiceQuery(p_hServiceQuery))) && - (_removeServiceQuery(pServiceQuery))); - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceQuery: FAILED!\n")); } ); - return bResult; -} - -/* - * MDNSResponder::answerCount - */ -uint32_t MDNSResponder::answerCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - return (pServiceQuery ? pServiceQuery->answerCount() : 0); -} - -std::vector MDNSResponder::answerInfo (const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { - std::vector tempVector; - for (uint32_t i=0;ianswerAtIndex(p_u32AnswerIndex) : 0); - // Fill m_pcServiceDomain (if not already done) - if ((pSQAnswer) && - (pSQAnswer->m_ServiceDomain.m_u16NameLength) && - (!pSQAnswer->m_pcServiceDomain)) { - - pSQAnswer->m_pcServiceDomain = pSQAnswer->allocServiceDomain(pSQAnswer->m_ServiceDomain.c_strLength()); - if (pSQAnswer->m_pcServiceDomain) { - pSQAnswer->m_ServiceDomain.c_str(pSQAnswer->m_pcServiceDomain); - } - } - return (pSQAnswer ? pSQAnswer->m_pcServiceDomain : 0); -} - -/* - * MDNSResponder::hasAnswerHostDomain - */ -bool MDNSResponder::hasAnswerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); -} - -/* - * MDNSResponder::answerHostDomain - * - * Returns the host domain for the given service. - * If not already existing, the string is allocated, filled and attached to the answer. - * - */ -const char* MDNSResponder::answerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - // Fill m_pcHostDomain (if not already done) - if ((pSQAnswer) && - (pSQAnswer->m_HostDomain.m_u16NameLength) && - (!pSQAnswer->m_pcHostDomain)) { - - pSQAnswer->m_pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); - if (pSQAnswer->m_pcHostDomain) { - pSQAnswer->m_HostDomain.c_str(pSQAnswer->m_pcHostDomain); - } - } - return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); -} - -#ifdef MDNS_IP4_SUPPORT - /* - * MDNSResponder::hasAnswerIP4Address - */ - bool MDNSResponder::hasAnswerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_IP4Address)); - } - - /* - * MDNSResponder::answerIP4AddressCount - */ - uint32_t MDNSResponder::answerIP4AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->IP4AddressCount() : 0); - } - - /* - * MDNSResponder::answerIP4Address - */ - IPAddress MDNSResponder::answerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = (pSQAnswer ? pSQAnswer->IP4AddressAtIndex(p_u32AddressIndex) : 0); - return (pIP4Address ? pIP4Address->m_IPAddress : IPAddress()); - } -#endif - -#ifdef MDNS_IP6_SUPPORT - /* - * MDNSResponder::hasAnswerIP6Address - */ - bool MDNSResponder::hasAnswerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostIP6Address)); - } - - /* - * MDNSResponder::answerIP6AddressCount - */ - uint32_t MDNSResponder::answerIP6AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->IP6AddressCount() : 0); - } - - /* - * MDNSResponder::answerIP6Address - */ - IPAddress MDNSResponder::answerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = (pSQAnswer ? pSQAnswer->IP6AddressAtIndex(p_u32AddressIndex) : 0); - return (pIP6Address ? pIP6Address->m_IPAddress : IPAddress()); - } -#endif - -/* - * MDNSResponder::hasAnswerPort - */ -bool MDNSResponder::hasAnswerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); -} - -/* - * MDNSResponder::answerPort - */ -uint16_t MDNSResponder::answerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->m_u16Port : 0); -} - -/* - * MDNSResponder::hasAnswerTxts - */ -bool MDNSResponder::hasAnswerTxts(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_Txts)); -} - -/* - * MDNSResponder::answerTxts - * - * Returns all TXT items for the given service as a ';'-separated string. - * If not already existing; the string is alloced, filled and attached to the answer. - * - */ -const char* MDNSResponder::answerTxts(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - // Fill m_pcTxts (if not already done) - if ((pSQAnswer) && - (pSQAnswer->m_Txts.m_pTxts) && - (!pSQAnswer->m_pcTxts)) { - - pSQAnswer->m_pcTxts = pSQAnswer->allocTxts(pSQAnswer->m_Txts.c_strLength()); - if (pSQAnswer->m_pcTxts) { - pSQAnswer->m_Txts.c_str(pSQAnswer->m_pcTxts); - } - } - return (pSQAnswer ? pSQAnswer->m_pcTxts : 0); -} - -/* - * PROBING - */ - -/* - * MDNSResponder::setProbeResultCallback - * - * Set a global callback for probe results. The callback is called, when probing - * for the host domain (or a service domain, without specific probe result callback) - * failes or succeedes. - * In the case of failure, the domain name should be changed via 'setHostname' or 'setServiceName'. - * When succeeded, the host or service domain will be announced by the MDNS responder. - * - */ -bool MDNSResponder::setHostProbeResultCallback(MDNSResponder::MDNSHostProbeFn p_fnCallback) { - - m_HostProbeInformation.m_fnHostProbeResultCallback = p_fnCallback; - - return true; -} - -bool MDNSResponder::setHostProbeResultCallback(MDNSHostProbeFn1 pfn) { - using namespace std::placeholders; - return setHostProbeResultCallback(std::bind(pfn, std::ref(*this), _1, _2)); -} - -/* - * MDNSResponder::setServiceProbeResultCallback - * - * Set a service specific callback for probe results. The callback is called, when probing - * for the service domain failes or succeedes. - * In the case of failure, the service name should be changed via 'setServiceName'. - * When succeeded, the service domain will be announced by the MDNS responder. - * - */ -bool MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSResponder::MDNSServiceProbeFn p_fnCallback) { - - bool bResult = false; - - stcMDNSService* pService = _findService(p_hService); - if (pService) { - pService->m_ProbeInformation.m_fnServiceProbeResultCallback = p_fnCallback; - - bResult = true; - } - return bResult; -} - -bool MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSResponder::MDNSServiceProbeFn1 p_fnCallback) { - using namespace std::placeholders; - return setServiceProbeResultCallback(p_hService, std::bind(p_fnCallback, std::ref(*this), _1, _2, _3)); -} - - -/* - * MISC - */ - -/* - * MDNSResponder::notifyAPChange - * - * Should be called, whenever the AP for the MDNS responder changes. - * A bit of this is caught by the event callbacks installed in the constructor. - * - */ -bool MDNSResponder::notifyAPChange(void) { - - return _restart(); -} - -/* - * MDNSResponder::update - * - * Should be called in every 'loop'. - * - */ -bool MDNSResponder::update(void) { - - if (m_bPassivModeEnabled) { - m_bPassivModeEnabled = false; - } - return _process(true); -} - -/* - * MDNSResponder::announce - * - * Should be called, if the 'configuration' changes. Mainly this will be changes in the TXT items... - */ -bool MDNSResponder::announce(void) { - - return (_announce(true, true)); -} - -/* - * MDNSResponder::enableArduino - * - * Enable the OTA update service. - * - */ -MDNSResponder::hMDNSService MDNSResponder::enableArduino(uint16_t p_u16Port, - bool p_bAuthUpload /*= false*/) { - - hMDNSService hService = addService(0, "arduino", "tcp", p_u16Port); - if (hService) { - if ((!addServiceTxt(hService, "tcp_check", "no")) || - (!addServiceTxt(hService, "ssh_upload", "no")) || - (!addServiceTxt(hService, "board", STRINGIZE_VALUE_OF(ARDUINO_BOARD))) || - (!addServiceTxt(hService, "auth_upload", (p_bAuthUpload) ? "yes" : "no"))) { - - removeService(hService); - hService = 0; - } - } - return hService; -} - - -} //namespace MDNSImplementation - -} //namespace esp8266 - - +/* + LEAmDNS.cpp + + License (MIT license): + 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. + +*/ + +#include + +#include "LEAmDNS_Priv.h" + + +namespace esp8266 +{ + +/* + LEAmDNS +*/ +namespace MDNSImplementation +{ + +/** + STRINGIZE +*/ +#ifndef STRINGIZE +#define STRINGIZE(x) #x +#endif +#ifndef STRINGIZE_VALUE_OF +#define STRINGIZE_VALUE_OF(x) STRINGIZE(x) +#endif + + +/** + INTERFACE +*/ + +/** + MDNSResponder::MDNSResponder +*/ +MDNSResponder::MDNSResponder(void) + : m_pServices(0), + m_pUDPContext(0), + m_pcHostname(0), + m_pServiceQueries(0), + m_fnServiceTxtCallback(0), +#ifdef ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE + m_bPassivModeEnabled(true) +{ +#else + m_bPassivModeEnabled(false) +{ +#endif + +} + +/* + MDNSResponder::~MDNSResponder +*/ +MDNSResponder::~MDNSResponder(void) +{ + + _resetProbeStatus(false); + _releaseServiceQueries(); + _releaseHostname(); + _releaseUDPContext(); + _releaseServices(); +} + +/* + MDNSResponder::begin + + Set the host domain (for probing) and install WiFi event handlers for + IP assignment and disconnection management. In both cases, the MDNS responder + is restarted (reset and restart probe status) + Finally the responder is (re)started + +*/ +bool MDNSResponder::begin(const char* p_pcHostname) +{ + + bool bResult = false; + + if (0 == m_pUDPContext) + { + if (_setHostname(p_pcHostname)) + { + + m_GotIPHandler = WiFi.onStationModeGotIP([this](const WiFiEventStationModeGotIP & pEvent) + { + (void) pEvent; + // Ensure that _restart() runs in USER context + schedule_function(std::bind(&MDNSResponder::_restart, this)); + }); + + m_DisconnectedHandler = WiFi.onStationModeDisconnected([this](const WiFiEventStationModeDisconnected & pEvent) + { + (void) pEvent; + // Ensure that _restart() runs in USER context + schedule_function(std::bind(&MDNSResponder::_restart, this)); + }); + + bResult = _restart(); + } + DEBUG_EX_ERR(if (!bResult) + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: FAILED for '%s'!\n"), (p_pcHostname ? : "-")); + }); + } + else + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: Ignoring multiple calls to begin (Ignored host domain: '%s')!\n"), (p_pcHostname ? : "-"));); + } + return bResult; +} + +/* + MDNSResponder::begin (LEGACY) +*/ +bool MDNSResponder::begin(const char* p_pcHostname, + IPAddress p_IPAddress, + uint32_t p_u32TTL /*= 120*/) +{ + + (void) p_IPAddress; + (void) p_u32TTL; + return begin(p_pcHostname); +} + +/* + MDNSResponder::close + + Ends the MDNS responder. + Announced services are unannounced (by multicasting a goodbye message) + +*/ +bool MDNSResponder::close(void) +{ + + m_GotIPHandler.reset(); // reset WiFi event callbacks. + m_DisconnectedHandler.reset(); + + _announce(false, true); + _resetProbeStatus(false); // Stop probing + + _releaseServiceQueries(); + _releaseUDPContext(); + _releaseHostname(); + + return true; +} + +/* + MDNSResponder::end + + Ends the MDNS responder. + for compatibility with esp32 + +*/ + +bool MDNSResponder::end(void) +{ + return close(); +} + +/* + MDNSResponder::setHostname + + Replaces the current hostname and restarts probing. + For services without own instance name (when the host name was used a instance + name), the instance names are replaced also (and the probing is restarted). + +*/ +bool MDNSResponder::setHostname(const char* p_pcHostname) +{ + + bool bResult = false; + + if (_setHostname(p_pcHostname)) + { + m_HostProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; + + // Replace 'auto-set' service names + bResult = true; + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) + { + if (pService->m_bAutoName) + { + bResult = pService->setName(p_pcHostname); + pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; + } + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setHostname: FAILED for '%s'!\n"), (p_pcHostname ? : "-")); + }); + return bResult; +} + +/* + MDNSResponder::setHostname (LEGACY) +*/ +bool MDNSResponder::setHostname(String p_strHostname) +{ + + return setHostname(p_strHostname.c_str()); +} + + +/* + SERVICES +*/ + +/* + MDNSResponder::addService + + Add service; using hostname if no name is explicitly provided for the service + The usual '_' underline, which is prepended to service and protocol, eg. _http, + may be given. If not, it is added automatically. + +*/ +MDNSResponder::hMDNSService MDNSResponder::addService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol, + uint16_t p_u16Port) +{ + + hMDNSService hResult = 0; + + if (((!p_pcName) || // NO name OR + (MDNS_DOMAIN_LABEL_MAXLENGTH >= os_strlen(p_pcName))) && // Fitting name + (p_pcService) && + (MDNS_SERVICE_NAME_LENGTH >= os_strlen(p_pcService)) && + (p_pcProtocol) && + ((MDNS_SERVICE_PROTOCOL_LENGTH - 1) != os_strlen(p_pcProtocol)) && + (p_u16Port)) + { + + if (!_findService((p_pcName ? : m_pcHostname), p_pcService, p_pcProtocol)) // Not already used + { + if (0 != (hResult = (hMDNSService)_allocService(p_pcName, p_pcService, p_pcProtocol, p_u16Port))) + { + + // Start probing + ((stcMDNSService*)hResult)->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; + } + } + } // else: bad arguments + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addService: %s to add '%s.%s.%s'!\n"), (hResult ? "Succeeded" : "FAILED"), (p_pcName ? : "-"), p_pcService, p_pcProtocol);); + DEBUG_EX_ERR(if (!hResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addService: FAILED to add '%s.%s.%s'!\n"), (p_pcName ? : "-"), p_pcService, p_pcProtocol); + }); + return hResult; +} + +/* + MDNSResponder::removeService + + Unanounce a service (by sending a goodbye message) and remove it + from the MDNS responder + +*/ +bool MDNSResponder::removeService(const MDNSResponder::hMDNSService p_hService) +{ + + stcMDNSService* pService = 0; + bool bResult = (((pService = _findService(p_hService))) && + (_announceService(*pService, false)) && + (_releaseService(pService))); + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeService: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::removeService +*/ +bool MDNSResponder::removeService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol) +{ + + return removeService((hMDNSService)_findService((p_pcName ? : m_pcHostname), p_pcService, p_pcProtocol)); +} + +/* + MDNSResponder::addService (LEGACY) +*/ +bool MDNSResponder::addService(String p_strService, + String p_strProtocol, + uint16_t p_u16Port) +{ + + return (0 != addService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str(), p_u16Port)); +} + +/* + MDNSResponder::setServiceName +*/ +bool MDNSResponder::setServiceName(const MDNSResponder::hMDNSService p_hService, + const char* p_pcInstanceName) +{ + + stcMDNSService* pService = 0; + bool bResult = (((!p_pcInstanceName) || + (MDNS_DOMAIN_LABEL_MAXLENGTH >= os_strlen(p_pcInstanceName))) && + ((pService = _findService(p_hService))) && + (pService->setName(p_pcInstanceName)) && + ((pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart))); + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setServiceName: FAILED for '%s'!\n"), (p_pcInstanceName ? : "-")); + }); + return bResult; +} + +/* + SERVICE TXT +*/ + +/* + MDNSResponder::addServiceTxt + + Add a static service TXT item ('Key'='Value') to a service. + +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + const char* p_pcValue) +{ + + hMDNSTxt hTxt = 0; + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + hTxt = (hMDNSTxt)_addServiceTxt(pService, p_pcKey, p_pcValue, false); + } + DEBUG_EX_ERR(if (!hTxt) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addServiceTxt: FAILED for '%s=%s'!\n"), (p_pcKey ? : "-"), (p_pcValue ? : "-")); + }); + return hTxt; +} + +/* + MDNSResponder::addServiceTxt (uint32_t) + + Formats: http://www.cplusplus.com/reference/cstdio/printf/ +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + uint32_t p_u32Value) +{ + char acBuffer[32]; *acBuffer = 0; + sprintf(acBuffer, "%u", p_u32Value); + + return addServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::addServiceTxt (uint16_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + uint16_t p_u16Value) +{ + char acBuffer[16]; *acBuffer = 0; + sprintf(acBuffer, "%hu", p_u16Value); + + return addServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::addServiceTxt (uint8_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + uint8_t p_u8Value) +{ + char acBuffer[8]; *acBuffer = 0; + sprintf(acBuffer, "%hhu", p_u8Value); + + return addServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::addServiceTxt (int32_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + int32_t p_i32Value) +{ + char acBuffer[32]; *acBuffer = 0; + sprintf(acBuffer, "%i", p_i32Value); + + return addServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::addServiceTxt (int16_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + int16_t p_i16Value) +{ + char acBuffer[16]; *acBuffer = 0; + sprintf(acBuffer, "%hi", p_i16Value); + + return addServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::addServiceTxt (int8_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + int8_t p_i8Value) +{ + char acBuffer[8]; *acBuffer = 0; + sprintf(acBuffer, "%hhi", p_i8Value); + + return addServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::removeServiceTxt + + Remove a static service TXT item from a service. +*/ +bool MDNSResponder::removeServiceTxt(const MDNSResponder::hMDNSService p_hService, + const MDNSResponder::hMDNSTxt p_hTxt) +{ + + bool bResult = false; + + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_hTxt); + if (pTxt) + { + bResult = _releaseServiceTxt(pService, pTxt); + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceTxt: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::removeServiceTxt +*/ +bool MDNSResponder::removeServiceTxt(const MDNSResponder::hMDNSService p_hService, + const char* p_pcKey) +{ + + bool bResult = false; + + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_pcKey); + if (pTxt) + { + bResult = _releaseServiceTxt(pService, pTxt); + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceTxt: FAILED for '%s'!\n"), (p_pcKey ? : "-")); + }); + return bResult; +} + +/* + MDNSResponder::removeServiceTxt +*/ +bool MDNSResponder::removeServiceTxt(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol, + const char* p_pcKey) +{ + + bool bResult = false; + + stcMDNSService* pService = _findService((p_pcName ? : m_pcHostname), p_pcService, p_pcProtocol); + if (pService) + { + stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_pcKey); + if (pTxt) + { + bResult = _releaseServiceTxt(pService, pTxt); + } + } + return bResult; +} + +/* + MDNSResponder::addServiceTxt (LEGACY) +*/ +bool MDNSResponder::addServiceTxt(const char* p_pcService, + const char* p_pcProtocol, + const char* p_pcKey, + const char* p_pcValue) +{ + + return (0 != _addServiceTxt(_findService(m_pcHostname, p_pcService, p_pcProtocol), p_pcKey, p_pcValue, false)); +} + +/* + MDNSResponder::addServiceTxt (LEGACY) +*/ +bool MDNSResponder::addServiceTxt(String p_strService, + String p_strProtocol, + String p_strKey, + String p_strValue) +{ + + return (0 != _addServiceTxt(_findService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str()), p_strKey.c_str(), p_strValue.c_str(), false)); +} + +/* + MDNSResponder::setDynamicServiceTxtCallback (global) + + Set a global callback for dynamic service TXT items. The callback is called, whenever + service TXT items are needed. + +*/ +bool MDNSResponder::setDynamicServiceTxtCallback(MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) +{ + + m_fnServiceTxtCallback = p_fnCallback; + + return true; +} + +/* + MDNSResponder::setDynamicServiceTxtCallback (service specific) + + Set a service specific callback for dynamic service TXT items. The callback is called, whenever + service TXT items are needed for the given service. + +*/ +bool MDNSResponder::setDynamicServiceTxtCallback(MDNSResponder::hMDNSService p_hService, + MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) +{ + + bool bResult = false; + + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + pService->m_fnTxtCallback = p_fnCallback; + + bResult = true; + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setDynamicServiceTxtCallback: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::addDynamicServiceTxt +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + const char* p_pcValue) +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addDynamicServiceTxt (%s=%s)\n"), p_pcKey, p_pcValue);); + + hMDNSTxt hTxt = 0; + + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + hTxt = _addServiceTxt(pService, p_pcKey, p_pcValue, true); + } + DEBUG_EX_ERR(if (!hTxt) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addDynamicServiceTxt: FAILED for '%s=%s'!\n"), (p_pcKey ? : "-"), (p_pcValue ? : "-")); + }); + return hTxt; +} + +/* + MDNSResponder::addDynamicServiceTxt (uint32_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + uint32_t p_u32Value) +{ + + char acBuffer[32]; *acBuffer = 0; + sprintf(acBuffer, "%u", p_u32Value); + + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::addDynamicServiceTxt (uint16_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + uint16_t p_u16Value) +{ + + char acBuffer[16]; *acBuffer = 0; + sprintf(acBuffer, "%hu", p_u16Value); + + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::addDynamicServiceTxt (uint8_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + uint8_t p_u8Value) +{ + + char acBuffer[8]; *acBuffer = 0; + sprintf(acBuffer, "%hhu", p_u8Value); + + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::addDynamicServiceTxt (int32_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + int32_t p_i32Value) +{ + + char acBuffer[32]; *acBuffer = 0; + sprintf(acBuffer, "%i", p_i32Value); + + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::addDynamicServiceTxt (int16_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + int16_t p_i16Value) +{ + + char acBuffer[16]; *acBuffer = 0; + sprintf(acBuffer, "%hi", p_i16Value); + + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); +} + +/* + MDNSResponder::addDynamicServiceTxt (int8_t) +*/ +MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, + const char* p_pcKey, + int8_t p_i8Value) +{ + + char acBuffer[8]; *acBuffer = 0; + sprintf(acBuffer, "%hhi", p_i8Value); + + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); +} + + +/** + STATIC SERVICE QUERY (LEGACY) +*/ + +/* + MDNSResponder::queryService + + Perform a (blocking) static service query. + The arrived answers can be queried by calling: + - answerHostname (or 'hostname') + - answerIP (or 'IP') + - answerPort (or 'port') + +*/ +uint32_t MDNSResponder::queryService(const char* p_pcService, + const char* p_pcProtocol, + const uint16_t p_u16Timeout /*= MDNS_QUERYSERVICES_WAIT_TIME*/) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService '%s.%s'\n"), p_pcService, p_pcProtocol);); + + uint32_t u32Result = 0; + + stcMDNSServiceQuery* pServiceQuery = 0; + if ((p_pcService) && + (os_strlen(p_pcService)) && + (p_pcProtocol) && + (os_strlen(p_pcProtocol)) && + (p_u16Timeout) && + (_removeLegacyServiceQuery()) && + ((pServiceQuery = _allocServiceQuery())) && + (_buildDomainForService(p_pcService, p_pcProtocol, pServiceQuery->m_ServiceTypeDomain))) + { + + pServiceQuery->m_bLegacyQuery = true; + + if (_sendMDNSServiceQuery(*pServiceQuery)) + { + // Wait for answers to arrive + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService: Waiting %u ms for answers...\n"), p_u16Timeout);); + delay(p_u16Timeout); + + // All answers should have arrived by now -> stop adding new answers + pServiceQuery->m_bAwaitingAnswers = false; + u32Result = pServiceQuery->answerCount(); + } + else // FAILED to send query + { + _removeServiceQuery(pServiceQuery); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService: INVALID input data!\n"), p_pcService, p_pcProtocol);); + } + return u32Result; +} + +/* + MDNSResponder::removeQuery + + Remove the last static service query (and all answers). + +*/ +bool MDNSResponder::removeQuery(void) +{ + + return _removeLegacyServiceQuery(); +} + +/* + MDNSResponder::queryService (LEGACY) +*/ +uint32_t MDNSResponder::queryService(String p_strService, + String p_strProtocol) +{ + + return queryService(p_strService.c_str(), p_strProtocol.c_str()); +} + +/* + MDNSResponder::answerHostname +*/ +const char* MDNSResponder::answerHostname(const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + + if ((pSQAnswer) && + (pSQAnswer->m_HostDomain.m_u16NameLength) && + (!pSQAnswer->m_pcHostDomain)) + { + + char* pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); + if (pcHostDomain) + { + pSQAnswer->m_HostDomain.c_str(pcHostDomain); + } + } + return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); +} + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::answerIP +*/ +IPAddress MDNSResponder::answerIP(const uint32_t p_u32AnswerIndex) +{ + + const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); + const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + const stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = (((pSQAnswer) && (pSQAnswer->m_pIP4Addresses)) ? pSQAnswer->IP4AddressAtIndex(0) : 0); + return (pIP4Address ? pIP4Address->m_IPAddress : IPAddress()); +} +#endif + +#ifdef MDNS_IP6_SUPPORT +/* + MDNSResponder::answerIP6 +*/ +IPAddress MDNSResponder::answerIP6(const uint32_t p_u32AnswerIndex) +{ + + const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); + const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + const stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = (((pSQAnswer) && (pSQAnswer->m_pIP6Addresses)) ? pSQAnswer->IP6AddressAtIndex(0) : 0); + return (pIP6Address ? pIP6Address->m_IPAddress : IP6Address()); +} +#endif + +/* + MDNSResponder::answerPort +*/ +uint16_t MDNSResponder::answerPort(const uint32_t p_u32AnswerIndex) +{ + + const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); + const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return (pSQAnswer ? pSQAnswer->m_u16Port : 0); +} + +/* + MDNSResponder::hostname (LEGACY) +*/ +String MDNSResponder::hostname(const uint32_t p_u32AnswerIndex) +{ + + return String(answerHostname(p_u32AnswerIndex)); +} + +/* + MDNSResponder::IP (LEGACY) +*/ +IPAddress MDNSResponder::IP(const uint32_t p_u32AnswerIndex) +{ + + return answerIP(p_u32AnswerIndex); +} + +/* + MDNSResponder::port (LEGACY) +*/ +uint16_t MDNSResponder::port(const uint32_t p_u32AnswerIndex) +{ + + return answerPort(p_u32AnswerIndex); +} + + +/** + DYNAMIC SERVICE QUERY +*/ + +/* + MDNSResponder::installServiceQuery + + Add a dynamic service query and a corresponding callback to the MDNS responder. + The callback will be called for every answer update. + The answers can also be queried by calling: + - answerServiceDomain + - answerHostDomain + - answerIP4Address/answerIP6Address + - answerPort + - answerTxts + +*/ +MDNSResponder::hMDNSServiceQuery MDNSResponder::installServiceQuery(const char* p_pcService, + const char* p_pcProtocol, + MDNSResponder::MDNSServiceQueryCallbackFunc p_fnCallback) +{ + hMDNSServiceQuery hResult = 0; + + stcMDNSServiceQuery* pServiceQuery = 0; + if ((p_pcService) && + (os_strlen(p_pcService)) && + (p_pcProtocol) && + (os_strlen(p_pcProtocol)) && + (p_fnCallback) && + ((pServiceQuery = _allocServiceQuery())) && + (_buildDomainForService(p_pcService, p_pcProtocol, pServiceQuery->m_ServiceTypeDomain))) + { + + pServiceQuery->m_fnCallback = p_fnCallback; + pServiceQuery->m_bLegacyQuery = false; + + if (_sendMDNSServiceQuery(*pServiceQuery)) + { + pServiceQuery->m_u8SentCount = 1; + pServiceQuery->m_ResendTimeout.reset(MDNS_DYNAMIC_QUERY_RESEND_DELAY); + + hResult = (hMDNSServiceQuery)pServiceQuery; + } + else + { + _removeServiceQuery(pServiceQuery); + } + } + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: %s for '%s.%s'!\n\n"), (hResult ? "Succeeded" : "FAILED"), (p_pcService ? : "-"), (p_pcProtocol ? : "-"));); + DEBUG_EX_ERR(if (!hResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: FAILED for '%s.%s'!\n\n"), (p_pcService ? : "-"), (p_pcProtocol ? : "-")); + }); + return hResult; +} + +/* + MDNSResponder::removeServiceQuery + + Remove a dynamic service query (and all collected answers) from the MDNS responder + +*/ +bool MDNSResponder::removeServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) +{ + + stcMDNSServiceQuery* pServiceQuery = 0; + bool bResult = (((pServiceQuery = _findServiceQuery(p_hServiceQuery))) && + (_removeServiceQuery(pServiceQuery))); + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceQuery: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::answerCount +*/ +uint32_t MDNSResponder::answerCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + return (pServiceQuery ? pServiceQuery->answerCount() : 0); +} + +std::vector MDNSResponder::answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) +{ + std::vector tempVector; + for (uint32_t i = 0; i < answerCount(p_hServiceQuery); i++) + { + tempVector.emplace_back(*this, p_hServiceQuery, i); + } + return tempVector; +} + +/* + MDNSResponder::answerServiceDomain + + Returns the domain for the given service. + If not already existing, the string is allocated, filled and attached to the answer. + +*/ +const char* MDNSResponder::answerServiceDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + // Fill m_pcServiceDomain (if not already done) + if ((pSQAnswer) && + (pSQAnswer->m_ServiceDomain.m_u16NameLength) && + (!pSQAnswer->m_pcServiceDomain)) + { + + pSQAnswer->m_pcServiceDomain = pSQAnswer->allocServiceDomain(pSQAnswer->m_ServiceDomain.c_strLength()); + if (pSQAnswer->m_pcServiceDomain) + { + pSQAnswer->m_ServiceDomain.c_str(pSQAnswer->m_pcServiceDomain); + } + } + return (pSQAnswer ? pSQAnswer->m_pcServiceDomain : 0); +} + +/* + MDNSResponder::hasAnswerHostDomain +*/ +bool MDNSResponder::hasAnswerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return ((pSQAnswer) && + (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); +} + +/* + MDNSResponder::answerHostDomain + + Returns the host domain for the given service. + If not already existing, the string is allocated, filled and attached to the answer. + +*/ +const char* MDNSResponder::answerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + // Fill m_pcHostDomain (if not already done) + if ((pSQAnswer) && + (pSQAnswer->m_HostDomain.m_u16NameLength) && + (!pSQAnswer->m_pcHostDomain)) + { + + pSQAnswer->m_pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); + if (pSQAnswer->m_pcHostDomain) + { + pSQAnswer->m_HostDomain.c_str(pSQAnswer->m_pcHostDomain); + } + } + return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); +} + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::hasAnswerIP4Address +*/ +bool MDNSResponder::hasAnswerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return ((pSQAnswer) && + (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_IP4Address)); +} + +/* + MDNSResponder::answerIP4AddressCount +*/ +uint32_t MDNSResponder::answerIP4AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return (pSQAnswer ? pSQAnswer->IP4AddressCount() : 0); +} + +/* + MDNSResponder::answerIP4Address +*/ +IPAddress MDNSResponder::answerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex, + const uint32_t p_u32AddressIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = (pSQAnswer ? pSQAnswer->IP4AddressAtIndex(p_u32AddressIndex) : 0); + return (pIP4Address ? pIP4Address->m_IPAddress : IPAddress()); +} +#endif + +#ifdef MDNS_IP6_SUPPORT +/* + MDNSResponder::hasAnswerIP6Address +*/ +bool MDNSResponder::hasAnswerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return ((pSQAnswer) && + (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostIP6Address)); +} + +/* + MDNSResponder::answerIP6AddressCount +*/ +uint32_t MDNSResponder::answerIP6AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return (pSQAnswer ? pSQAnswer->IP6AddressCount() : 0); +} + +/* + MDNSResponder::answerIP6Address +*/ +IPAddress MDNSResponder::answerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex, + const uint32_t p_u32AddressIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = (pSQAnswer ? pSQAnswer->IP6AddressAtIndex(p_u32AddressIndex) : 0); + return (pIP6Address ? pIP6Address->m_IPAddress : IPAddress()); +} +#endif + +/* + MDNSResponder::hasAnswerPort +*/ +bool MDNSResponder::hasAnswerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return ((pSQAnswer) && + (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); +} + +/* + MDNSResponder::answerPort +*/ +uint16_t MDNSResponder::answerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return (pSQAnswer ? pSQAnswer->m_u16Port : 0); +} + +/* + MDNSResponder::hasAnswerTxts +*/ +bool MDNSResponder::hasAnswerTxts(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return ((pSQAnswer) && + (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_Txts)); +} + +/* + MDNSResponder::answerTxts + + Returns all TXT items for the given service as a ';'-separated string. + If not already existing; the string is alloced, filled and attached to the answer. + +*/ +const char* MDNSResponder::answerTxts(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + // Fill m_pcTxts (if not already done) + if ((pSQAnswer) && + (pSQAnswer->m_Txts.m_pTxts) && + (!pSQAnswer->m_pcTxts)) + { + + pSQAnswer->m_pcTxts = pSQAnswer->allocTxts(pSQAnswer->m_Txts.c_strLength()); + if (pSQAnswer->m_pcTxts) + { + pSQAnswer->m_Txts.c_str(pSQAnswer->m_pcTxts); + } + } + return (pSQAnswer ? pSQAnswer->m_pcTxts : 0); +} + +/* + PROBING +*/ + +/* + MDNSResponder::setProbeResultCallback + + Set a global callback for probe results. The callback is called, when probing + for the host domain (or a service domain, without specific probe result callback) + failes or succeedes. + In the case of failure, the domain name should be changed via 'setHostname' or 'setServiceName'. + When succeeded, the host or service domain will be announced by the MDNS responder. + +*/ +bool MDNSResponder::setHostProbeResultCallback(MDNSResponder::MDNSHostProbeFn p_fnCallback) +{ + + m_HostProbeInformation.m_fnHostProbeResultCallback = p_fnCallback; + + return true; +} + +bool MDNSResponder::setHostProbeResultCallback(MDNSHostProbeFn1 pfn) +{ + using namespace std::placeholders; + return setHostProbeResultCallback(std::bind(pfn, std::ref(*this), _1, _2)); +} + +/* + MDNSResponder::setServiceProbeResultCallback + + Set a service specific callback for probe results. The callback is called, when probing + for the service domain failes or succeedes. + In the case of failure, the service name should be changed via 'setServiceName'. + When succeeded, the service domain will be announced by the MDNS responder. + +*/ +bool MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSResponder::MDNSServiceProbeFn p_fnCallback) +{ + + bool bResult = false; + + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + pService->m_ProbeInformation.m_fnServiceProbeResultCallback = p_fnCallback; + + bResult = true; + } + return bResult; +} + +bool MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSResponder::MDNSServiceProbeFn1 p_fnCallback) +{ + using namespace std::placeholders; + return setServiceProbeResultCallback(p_hService, std::bind(p_fnCallback, std::ref(*this), _1, _2, _3)); +} + + +/* + MISC +*/ + +/* + MDNSResponder::notifyAPChange + + Should be called, whenever the AP for the MDNS responder changes. + A bit of this is caught by the event callbacks installed in the constructor. + +*/ +bool MDNSResponder::notifyAPChange(void) +{ + + return _restart(); +} + +/* + MDNSResponder::update + + Should be called in every 'loop'. + +*/ +bool MDNSResponder::update(void) +{ + + if (m_bPassivModeEnabled) + { + m_bPassivModeEnabled = false; + } + return _process(true); +} + +/* + MDNSResponder::announce + + Should be called, if the 'configuration' changes. Mainly this will be changes in the TXT items... +*/ +bool MDNSResponder::announce(void) +{ + + return (_announce(true, true)); +} + +/* + MDNSResponder::enableArduino + + Enable the OTA update service. + +*/ +MDNSResponder::hMDNSService MDNSResponder::enableArduino(uint16_t p_u16Port, + bool p_bAuthUpload /*= false*/) +{ + + hMDNSService hService = addService(0, "arduino", "tcp", p_u16Port); + if (hService) + { + if ((!addServiceTxt(hService, "tcp_check", "no")) || + (!addServiceTxt(hService, "ssh_upload", "no")) || + (!addServiceTxt(hService, "board", STRINGIZE_VALUE_OF(ARDUINO_BOARD))) || + (!addServiceTxt(hService, "auth_upload", (p_bAuthUpload) ? "yes" : "no"))) + { + + removeService(hService); + hService = 0; + } + } + return hService; +} + + +} //namespace MDNSImplementation + +} //namespace esp8266 + + diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.h b/libraries/ESP8266mDNS/src/LEAmDNS.h index 03abcdbb88..afe5ee7e06 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS.h @@ -1,1410 +1,1461 @@ -/* - * LEAmDNS.h - * (c) 2018, LaborEtArs - * - * Version 0.9 beta - * - * Some notes (from LaborEtArs, 2018): - * 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 - * 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. - * - * 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 - * - 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 - * - Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak) - * - Announcing available services after successful probing - * - Using fixed service TXT items or - * - Using dynamic service TXT items for presented services (via callback) - * - 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) - * - Dynamic queries for DNS-SD services with cached and updated answers and user notifications - * - * - * Usage: - * 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 - * of the new features should be used. - * - * For presenting services: - * In 'setup()': - * 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);' - * (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 - * using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service specific - * 'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);' - * Call MDNS.begin("MyHostname"); - * - * 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 - * - * In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, void* p_pUserdata)': - * Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, "c#", "1");' - * - * In loop(): - * Call 'MDNS.update();' - * - * - * For querying services: - * Static: - * Call 'uint32_t u32AnswerCount = MDNS.queryService("http", "tcp");' - * Iterate answers by: 'for (uint32_t u=0; u // for UdpContext.h -#include "WiFiUdp.h" -#include "lwip/udp.h" -#include "debug.h" -#include "include/UdpContext.h" -#include -#include -#include - - -#include "ESP8266WiFi.h" - - -namespace esp8266 { - -/** - * LEAmDNS - */ -namespace MDNSImplementation { - -//this should be defined at build time -#ifndef ARDUINO_BOARD -#define ARDUINO_BOARD "generic" -#endif - -#define MDNS_IP4_SUPPORT -//#define MDNS_IP6_SUPPORT - - -#ifdef MDNS_IP4_SUPPORT - #define MDNS_IP4_SIZE 4 -#endif -#ifdef MDNS_IP6_SUPPORT - #define MDNS_IP6_SIZE 16 -#endif -/* - * Maximum length for all service txts for one service - */ -#define MDNS_SERVICE_TXT_MAXLENGTH 1300 -/* - * Maximum length for a full domain name eg. MyESP._http._tcp.local - */ -#define MDNS_DOMAIN_MAXLENGTH 256 -/* - * Maximum length of on label in a domain name (length info fits into 6 bits) - */ -#define MDNS_DOMAIN_LABEL_MAXLENGTH 63 -/* - * Maximum length of a service name eg. http - */ -#define MDNS_SERVICE_NAME_LENGTH 15 -/* - * Maximum length of a service protocol name eg. tcp - */ -#define MDNS_SERVICE_PROTOCOL_LENGTH 3 -/* - * Default timeout for static service queries - */ -#define MDNS_QUERYSERVICES_WAIT_TIME 1000 - - -/** - * MDNSResponder - */ -class MDNSResponder { -public: - /* INTERFACE */ - MDNSResponder(void); - virtual ~MDNSResponder(void); - - // Start the MDNS responder by setting the default hostname - // Later call MDNS::update() in every 'loop' to run the process loop - // (probing, announcing, responding, ...) - bool begin(const char* p_pcHostname); - bool begin(const String& p_strHostname) {return begin(p_strHostname.c_str());} - // for compatibility - bool begin(const char* p_pcHostname, - IPAddress p_IPAddress, // ignored - uint32_t p_u32TTL = 120); // ignored - bool begin(const String& p_strHostname, - IPAddress p_IPAddress, // ignored - uint32_t p_u32TTL = 120) { // ignored - return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL); - } - // Finish MDNS processing - bool close(void); - // for esp32 compatability - bool end(void); - // Change hostname (probing is restarted) - bool setHostname(const char* p_pcHostname); - // for compatibility... - bool setHostname(String p_strHostname); - - /** - * hMDNSService (opaque handle to access the service) - */ - typedef const void* hMDNSService; - - // Add a new service to the MDNS responder. If no name (instance name) is given (p_pcName = 0) - // the current hostname is used. If the hostname is changed later, the instance names for - // these 'auto-named' services are changed to the new name also (and probing is restarted). - // The usual '_' before p_pcService (eg. http) and protocol (eg. tcp) may be given. - hMDNSService addService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - uint16_t p_u16Port); - // Removes a service from the MDNS responder - bool removeService(const hMDNSService p_hService); - bool removeService(const char* p_pcInstanceName, - const char* p_pcServiceName, - const char* p_pcProtocol); - // for compatibility... - bool addService(String p_strServiceName, - String p_strProtocol, - uint16_t p_u16Port); - - - // Change the services instance name (and restart probing). - bool setServiceName(const hMDNSService p_hService, - const char* p_pcInstanceName); - //for compatibility - //Warning: this has the side effect of changing the hostname. - //TODO: implement instancename different from hostname - void setInstanceName(const char* p_pcHostname) {setHostname(p_pcHostname);} - // for esp32 compatibilty - void setInstanceName(const String& s_pcHostname) {setInstanceName(s_pcHostname.c_str());} - - /** - * hMDNSTxt (opaque handle to access the TXT items) - */ - typedef void* hMDNSTxt; - - // Add a (static) MDNS TXT item ('key' = 'value') to the service - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - const char* p_pcValue); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - uint32_t p_u32Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - uint16_t p_u16Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - uint8_t p_u8Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - int32_t p_i32Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - int16_t p_i16Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - int8_t p_i8Value); - - // Remove an existing (static) MDNS TXT item from the service - bool removeServiceTxt(const hMDNSService p_hService, - const hMDNSTxt p_hTxt); - bool removeServiceTxt(const hMDNSService p_hService, - const char* p_pcKey); - bool removeServiceTxt(const char* p_pcinstanceName, - const char* p_pcServiceName, - const char* p_pcProtocol, - const char* p_pcKey); - // for compatibility... - bool addServiceTxt(const char* p_pcService, - const char* p_pcProtocol, - const char* p_pcKey, - const char* p_pcValue); - bool addServiceTxt(String p_strService, - String p_strProtocol, - String p_strKey, - String p_strValue); - - /** - * MDNSDynamicServiceTxtCallbackFn - * Callback function for dynamic MDNS TXT items - */ - - typedef std::function MDNSDynamicServiceTxtCallbackFunc; - - // Set a global callback for dynamic MDNS TXT items. The callback function is called - // every time, a TXT item is needed for one of the installed services. - bool setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallbackFunc p_fnCallback); - // Set a service specific callback for dynamic MDNS TXT items. The callback function - // is called every time, a TXT item is needed for the given service. - bool setDynamicServiceTxtCallback(const hMDNSService p_hService, - MDNSDynamicServiceTxtCallbackFunc p_fnCallback); - - // Add a (dynamic) MDNS TXT item ('key' = 'value') to the service - // Dynamic TXT items are removed right after one-time use. So they need to be added - // every time the value s needed (via callback). - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - const char* p_pcValue); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - uint32_t p_u32Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - uint16_t p_u16Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - uint8_t p_u8Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - int32_t p_i32Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - int16_t p_i16Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - int8_t p_i8Value); - - // Perform a (static) service query. The function returns after p_u16Timeout milliseconds - // The answers (the number of received answers is returned) can be retrieved by calling - // - answerHostname (or hostname) - // - answerIP (or IP) - // - answerPort (or port) - uint32_t queryService(const char* p_pcService, - const char* p_pcProtocol, - const uint16_t p_u16Timeout = MDNS_QUERYSERVICES_WAIT_TIME); - bool removeQuery(void); - // for compatibility... - uint32_t queryService(String p_strService, - String p_strProtocol); - - const char* answerHostname(const uint32_t p_u32AnswerIndex); - IPAddress answerIP(const uint32_t p_u32AnswerIndex); - uint16_t answerPort(const uint32_t p_u32AnswerIndex); - // for compatibility... - String hostname(const uint32_t p_u32AnswerIndex); - IPAddress IP(const uint32_t p_u32AnswerIndex); - uint16_t port(const uint32_t p_u32AnswerIndex); - - /** - * hMDNSServiceQuery (opaque handle to access dynamic service queries) - */ - typedef const void* hMDNSServiceQuery; - - /** - * enuServiceQueryAnswerType - */ - typedef enum _enuServiceQueryAnswerType { - ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name - ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port - ServiceQueryAnswerType_Txts = (1 << 2), // TXT items -#ifdef MDNS_IP4_SUPPORT - ServiceQueryAnswerType_IP4Address = (1 << 3), // IP4 address -#endif -#ifdef MDNS_IP6_SUPPORT - ServiceQueryAnswerType_IP6Address = (1 << 4), // IP6 address -#endif - } enuServiceQueryAnswerType; - - enum class AnswerType : uint32_t { - Unknown = 0, - ServiceDomain = ServiceQueryAnswerType_ServiceDomain, - HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort, - Txt = ServiceQueryAnswerType_Txts, -#ifdef MDNS_IP4_SUPPORT - IP4Address = ServiceQueryAnswerType_IP4Address, -#endif -#ifdef MDNS_IP6_SUPPORT - IP6Address = ServiceQueryAnswerType_IP6Address, -#endif - }; - - /** - * MDNSServiceQueryCallbackFn - * Callback function for received answers for dynamic service queries - */ - struct MDNSServiceInfo; // forward declaration - typedef std::function MDNSServiceQueryCallbackFunc; - - // Install a dynamic service query. For every received answer (part) the given callback - // function is called. The query will be updated every time, the TTL for an answer - // has timed-out. - // The answers can also be retrieved by calling - // - answerCount - // - answerServiceDomain - // - hasAnswerHostDomain/answerHostDomain - // - hasAnswerIP4Address/answerIP4Address - // - hasAnswerIP6Address/answerIP6Address - // - hasAnswerPort/answerPort - // - hasAnswerTxts/answerTxts - hMDNSServiceQuery installServiceQuery(const char* p_pcService, - const char* p_pcProtocol, - MDNSServiceQueryCallbackFunc p_fnCallback); - // Remove a dynamic service query - bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery); - - uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery); - std::vector answerInfo (const MDNSResponder::hMDNSServiceQuery p_hServiceQuery); - - const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - bool hasAnswerHostDomain(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - const char* answerHostDomain(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); -#ifdef MDNS_IP4_SUPPORT - bool hasAnswerIP4Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - uint32_t answerIP4AddressCount(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - IPAddress answerIP4Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex); -#endif -#ifdef MDNS_IP6_SUPPORT - bool hasAnswerIP6Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - uint32_t answerIP6AddressCount(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - IPAddress answerIP6Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex); -#endif - bool hasAnswerPort(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - uint16_t answerPort(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - bool hasAnswerTxts(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - // Get the TXT items as a ';'-separated string - const char* answerTxts(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - - /** - * MDNSProbeResultCallbackFn - * Callback function for (host and service domain) probe results - */ - typedef std::function MDNSHostProbeFn; - - typedef std::function MDNSHostProbeFn1; - - typedef std::function MDNSServiceProbeFn; - - typedef std::function MDNSServiceProbeFn1; - - // Set a global callback function for host and service probe results - // The callback function is called, when the probing for the host domain - // (or a service domain, which hasn't got a service specific callback) - // Succeeds or fails. - // In case of failure, the failed domain name should be changed. - bool setHostProbeResultCallback(MDNSHostProbeFn p_fnCallback); - bool setHostProbeResultCallback(MDNSHostProbeFn1 p_fnCallback); - - // Set a service specific probe result callback - bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSServiceProbeFn p_fnCallback); - bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSServiceProbeFn1 p_fnCallback); - - // Application should call this whenever AP is configured/disabled - bool notifyAPChange(void); - - // 'update' should be called in every 'loop' to run the MDNS processing - bool update(void); - - // 'announce' can be called every time, the configuration of some service - // changes. Mainly, this would be changed content of TXT items. - bool announce(void); - - // Enable OTA update - hMDNSService enableArduino(uint16_t p_u16Port, - bool p_bAuthUpload = false); - - // Domain name helper - static bool indexDomain(char*& p_rpcDomain, - const char* p_pcDivider = "-", - const char* p_pcDefaultDomain = 0); - -protected: - /** STRUCTS **/ - /** - * MDNSServiceInfo, used in application callbacks - */ -public: - struct MDNSServiceInfo - { - MDNSServiceInfo(MDNSResponder& p_pM,MDNSResponder::hMDNSServiceQuery p_hS,uint32_t p_u32A) - : p_pMDNSResponder(p_pM), - p_hServiceQuery(p_hS), - p_u32AnswerIndex(p_u32A) - {}; - struct CompareKey - { - bool operator()(char const *a, char const *b) const - { - return strcmp(a, b) < 0; - } - }; - using KeyValueMap = std::map; - protected: - MDNSResponder& p_pMDNSResponder; - MDNSResponder::hMDNSServiceQuery p_hServiceQuery; - uint32_t p_u32AnswerIndex; - KeyValueMap keyValueMap; - public: - const char* serviceDomain(){ - return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex); - }; - bool hostDomainAvailable() - { - return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex)); - } - const char* hostDomain(){ - return (hostDomainAvailable()) ? - p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) : nullptr; - }; - bool hostPortAvailable() - { - return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex)); - } - uint16_t hostPort(){ - return (hostPortAvailable()) ? - p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) : 0; - }; - bool IP4AddressAvailable() - { - return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery,p_u32AnswerIndex )); - } - std::vector IP4Adresses(){ - std::vector internalIP; - if (IP4AddressAvailable()) { - uint16_t cntIP4Adress = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex); - for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2) { - internalIP.emplace_back(p_pMDNSResponder.answerIP4Address(p_hServiceQuery, p_u32AnswerIndex, u2)); - } - } - return internalIP; - }; - bool txtAvailable() - { - return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex)); - } - const char* strKeyValue (){ - return (txtAvailable()) ? - p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) : nullptr; - }; - const KeyValueMap& keyValues() - { - if (txtAvailable() && keyValueMap.size() == 0) - { - for (auto kv = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex);kv != nullptr;kv = kv->m_pNext) { - keyValueMap.emplace(std::pair(kv->m_pcKey,kv->m_pcValue)); - } - } - return keyValueMap; - } - const char* value(const char* key) - { - char* result = nullptr; - - for (stcMDNSServiceTxt* pTxt=p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); pTxt; pTxt=pTxt->m_pNext) { - if ((key) && - (0 == strcmp(pTxt->m_pcKey, key))) { - result = pTxt->m_pcValue; - break; - } - } - return result; - } - }; -protected: - - /** - * stcMDNSServiceTxt - */ - struct stcMDNSServiceTxt { - stcMDNSServiceTxt* m_pNext; - char* m_pcKey; - char* m_pcValue; - bool m_bTemp; - - stcMDNSServiceTxt(const char* p_pcKey = 0, - const char* p_pcValue = 0, - bool p_bTemp = false); - stcMDNSServiceTxt(const stcMDNSServiceTxt& p_Other); - ~stcMDNSServiceTxt(void); - - stcMDNSServiceTxt& operator=(const stcMDNSServiceTxt& p_Other); - bool clear(void); - - char* allocKey(size_t p_stLength); - bool setKey(const char* p_pcKey, - size_t p_stLength); - bool setKey(const char* p_pcKey); - bool releaseKey(void); - - char* allocValue(size_t p_stLength); - bool setValue(const char* p_pcValue, - size_t p_stLength); - bool setValue(const char* p_pcValue); - bool releaseValue(void); - - bool set(const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp = false); - - bool update(const char* p_pcValue); - - size_t length(void) const; - }; - - /** - * stcMDNSTxts - */ - struct stcMDNSServiceTxts { - stcMDNSServiceTxt* m_pTxts; - - stcMDNSServiceTxts(void); - stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other); - ~stcMDNSServiceTxts(void); - - stcMDNSServiceTxts& operator=(const stcMDNSServiceTxts& p_Other); - - bool clear(void); - - bool add(stcMDNSServiceTxt* p_pTxt); - bool remove(stcMDNSServiceTxt* p_pTxt); - - bool removeTempTxts(void); - - stcMDNSServiceTxt* find(const char* p_pcKey); - const stcMDNSServiceTxt* find(const char* p_pcKey) const; - stcMDNSServiceTxt* find(const stcMDNSServiceTxt* p_pTxt); - - uint16_t length(void) const; - - size_t c_strLength(void) const; - bool c_str(char* p_pcBuffer); - - size_t bufferLength(void) const; - bool buffer(char* p_pcBuffer); - - bool compare(const stcMDNSServiceTxts& p_Other) const; - bool operator==(const stcMDNSServiceTxts& p_Other) const; - bool operator!=(const stcMDNSServiceTxts& p_Other) const; - }; - - /** - * enuContentFlags - */ - typedef enum _enuContentFlags { - // Host - ContentFlag_A = 0x01, - ContentFlag_PTR_IP4 = 0x02, - ContentFlag_PTR_IP6 = 0x04, - ContentFlag_AAAA = 0x08, - // Service - ContentFlag_PTR_TYPE = 0x10, - ContentFlag_PTR_NAME = 0x20, - ContentFlag_TXT = 0x40, - ContentFlag_SRV = 0x80, - } enuContentFlags; - - /** - * stcMDNS_MsgHeader - */ - struct stcMDNS_MsgHeader { - uint16_t m_u16ID; // Identifier - bool m_1bQR : 1; // Query/Response flag - unsigned char m_4bOpcode : 4; // Operation code - bool m_1bAA : 1; // Authoritative Answer flag - bool m_1bTC : 1; // Truncation flag - bool m_1bRD : 1; // Recursion desired - bool m_1bRA : 1; // Recursion available - unsigned char m_3bZ : 3; // Zero - unsigned char m_4bRCode : 4; // Response code - uint16_t m_u16QDCount; // Question count - uint16_t m_u16ANCount; // Answer count - uint16_t m_u16NSCount; // Authority Record count - uint16_t m_u16ARCount; // Additional Record count - - stcMDNS_MsgHeader(uint16_t p_u16ID = 0, - bool p_bQR = false, - unsigned char p_ucOpcode = 0, - bool p_bAA = false, - bool p_bTC = false, - bool p_bRD = false, - bool p_bRA = false, - unsigned char p_ucRCode = 0, - uint16_t p_u16QDCount = 0, - uint16_t p_u16ANCount = 0, - uint16_t p_u16NSCount = 0, - uint16_t p_u16ARCount = 0); - }; - - /** - * stcMDNS_RRDomain - */ - struct stcMDNS_RRDomain { - char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name - uint16_t m_u16NameLength; // Length (incl. '\0') - - stcMDNS_RRDomain(void); - stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other); - - stcMDNS_RRDomain& operator=(const stcMDNS_RRDomain& p_Other); - - bool clear(void); - - bool addLabel(const char* p_pcLabel, - bool p_bPrependUnderline = false); - - bool compare(const stcMDNS_RRDomain& p_Other) const; - bool operator==(const stcMDNS_RRDomain& p_Other) const; - bool operator!=(const stcMDNS_RRDomain& p_Other) const; - bool operator>(const stcMDNS_RRDomain& p_Other) const; - - size_t c_strLength(void) const; - bool c_str(char* p_pcBuffer); - }; - - /** - * stcMDNS_RRAttributes - */ - struct stcMDNS_RRAttributes { - uint16_t m_u16Type; // Type - uint16_t m_u16Class; // Class, nearly always 'IN' - - stcMDNS_RRAttributes(uint16_t p_u16Type = 0, - uint16_t p_u16Class = 1 /*DNS_RRCLASS_IN Internet*/); - stcMDNS_RRAttributes(const stcMDNS_RRAttributes& p_Other); - - stcMDNS_RRAttributes& operator=(const stcMDNS_RRAttributes& p_Other); - }; - - /** - * stcMDNS_RRHeader - */ - struct stcMDNS_RRHeader { - stcMDNS_RRDomain m_Domain; - stcMDNS_RRAttributes m_Attributes; - - stcMDNS_RRHeader(void); - stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other); - - stcMDNS_RRHeader& operator=(const stcMDNS_RRHeader& p_Other); - - bool clear(void); - }; - - /** - * stcMDNS_RRQuestion - */ - struct stcMDNS_RRQuestion { - stcMDNS_RRQuestion* m_pNext; - stcMDNS_RRHeader m_Header; - bool m_bUnicast; // Unicast reply requested - - stcMDNS_RRQuestion(void); - }; - - /** - * enuAnswerType - */ - typedef enum _enuAnswerType { - AnswerType_A, - AnswerType_PTR, - AnswerType_TXT, - AnswerType_AAAA, - AnswerType_SRV, - AnswerType_Generic - } enuAnswerType; - - /** - * stcMDNS_RRAnswer - */ - struct stcMDNS_RRAnswer { - stcMDNS_RRAnswer* m_pNext; - const enuAnswerType m_AnswerType; - stcMDNS_RRHeader m_Header; - bool m_bCacheFlush; // Cache flush command bit - uint32_t m_u32TTL; // Validity time in seconds - - virtual ~stcMDNS_RRAnswer(void); - - enuAnswerType answerType(void) const; - - bool clear(void); - - protected: - stcMDNS_RRAnswer(enuAnswerType p_AnswerType, - const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - }; - -#ifdef MDNS_IP4_SUPPORT - /** - * stcMDNS_RRAnswerA - */ - struct stcMDNS_RRAnswerA : public stcMDNS_RRAnswer { - IPAddress m_IPAddress; - - stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerA(void); - - bool clear(void); - }; -#endif - - /** - * stcMDNS_RRAnswerPTR - */ - struct stcMDNS_RRAnswerPTR : public stcMDNS_RRAnswer { - stcMDNS_RRDomain m_PTRDomain; - - stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerPTR(void); - - bool clear(void); - }; - - /** - * stcMDNS_RRAnswerTXT - */ - struct stcMDNS_RRAnswerTXT : public stcMDNS_RRAnswer { - stcMDNSServiceTxts m_Txts; - - stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerTXT(void); - - bool clear(void); - }; - -#ifdef MDNS_IP6_SUPPORT - /** - * stcMDNS_RRAnswerAAAA - */ - struct stcMDNS_RRAnswerAAAA : public stcMDNS_RRAnswer { - //TODO: IP6Address m_IPAddress; - - stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerAAAA(void); - - bool clear(void); - }; -#endif - - /** - * stcMDNS_RRAnswerSRV - */ - struct stcMDNS_RRAnswerSRV : public stcMDNS_RRAnswer { - uint16_t m_u16Priority; - uint16_t m_u16Weight; - uint16_t m_u16Port; - stcMDNS_RRDomain m_SRVDomain; - - stcMDNS_RRAnswerSRV(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerSRV(void); - - bool clear(void); - }; - - /** - * stcMDNS_RRAnswerGeneric - */ - struct stcMDNS_RRAnswerGeneric : public stcMDNS_RRAnswer { - uint16_t m_u16RDLength; // Length of variable answer - uint8_t* m_pu8RDData; // Offset of start of variable answer in packet - - stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerGeneric(void); - - bool clear(void); - }; - - - /** - * enuProbingStatus - */ - typedef enum _enuProbingStatus { - ProbingStatus_WaitingForData, - ProbingStatus_ReadyToStart, - ProbingStatus_InProgress, - ProbingStatus_Done - } enuProbingStatus; - - /** - * stcProbeInformation - */ - struct stcProbeInformation { - enuProbingStatus m_ProbingStatus; - uint8_t m_u8SentCount; // Used for probes and announcements - esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements - //clsMDNSTimeFlag m_TimeFlag; // Used for probes and announcements - bool m_bConflict; - bool m_bTiebreakNeeded; - MDNSHostProbeFn m_fnHostProbeResultCallback; - MDNSServiceProbeFn m_fnServiceProbeResultCallback; - - stcProbeInformation(void); - - bool clear(bool p_bClearUserdata = false); - }; - - - /** - * stcMDNSService - */ - struct stcMDNSService { - stcMDNSService* m_pNext; - char* m_pcName; - bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied) - char* m_pcService; - char* m_pcProtocol; - uint16_t m_u16Port; - uint8_t m_u8ReplyMask; - stcMDNSServiceTxts m_Txts; - MDNSDynamicServiceTxtCallbackFunc m_fnTxtCallback; - stcProbeInformation m_ProbeInformation; - - stcMDNSService(const char* p_pcName = 0, - const char* p_pcService = 0, - const char* p_pcProtocol = 0); - ~stcMDNSService(void); - - bool setName(const char* p_pcName); - bool releaseName(void); - - bool setService(const char* p_pcService); - bool releaseService(void); - - bool setProtocol(const char* p_pcProtocol); - bool releaseProtocol(void); - }; - - /** - * stcMDNSServiceQuery - */ - struct stcMDNSServiceQuery { - /** - * stcAnswer - */ - struct stcAnswer { - /** - * stcTTL - */ - struct stcTTL { - /** - * timeoutLevel_t - */ - typedef uint8_t timeoutLevel_t; - /** - * TIMEOUTLEVELs - */ - const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0; - const timeoutLevel_t TIMEOUTLEVEL_BASE = 80; - const timeoutLevel_t TIMEOUTLEVEL_INTERVAL = 5; - const timeoutLevel_t TIMEOUTLEVEL_FINAL = 100; - - uint32_t m_u32TTL; - esp8266::polledTimeout::oneShotMs m_TTLTimeout; - timeoutLevel_t m_timeoutLevel; - - stcTTL(void); - bool set(uint32_t p_u32TTL); - - bool flagged(void); - bool restart(void); - - bool prepareDeletion(void); - bool finalTimeoutLevel(void) const; - - unsigned long timeout(void) const; - }; -#ifdef MDNS_IP4_SUPPORT - /** - * stcIP4Address - */ - struct stcIP4Address { - stcIP4Address* m_pNext; - IPAddress m_IPAddress; - stcTTL m_TTL; - - stcIP4Address(IPAddress p_IPAddress, - uint32_t p_u32TTL = 0); - }; -#endif -#ifdef MDNS_IP6_SUPPORT - /** - * stcIP6Address - */ - struct stcIP6Address { - stcIP6Address* m_pNext; - IP6Address m_IPAddress; - stcTTL m_TTL; - - stcIP6Address(IPAddress p_IPAddress, - uint32_t p_u32TTL = 0); - }; -#endif - - stcAnswer* m_pNext; - // The service domain is the first 'answer' (from PTR answer, using service and protocol) to be set - // Defines the key for additional answer, like host domain, etc. - stcMDNS_RRDomain m_ServiceDomain; // 1. level answer (PTR), eg. MyESP._http._tcp.local - char* m_pcServiceDomain; - stcTTL m_TTLServiceDomain; - stcMDNS_RRDomain m_HostDomain; // 2. level answer (SRV, using service domain), eg. esp8266.local - char* m_pcHostDomain; - uint16_t m_u16Port; // 2. level answer (SRV, using service domain), eg. 5000 - stcTTL m_TTLHostDomainAndPort; - stcMDNSServiceTxts m_Txts; // 2. level answer (TXT, using service domain), eg. c#=1 - char* m_pcTxts; - stcTTL m_TTLTxts; -#ifdef MDNS_IP4_SUPPORT - stcIP4Address* m_pIP4Addresses; // 3. level answer (A, using host domain), eg. 123.456.789.012 -#endif -#ifdef MDNS_IP6_SUPPORT - stcIP6Address* m_pIP6Addresses; // 3. level answer (AAAA, using host domain), eg. 1234::09 -#endif - uint32_t m_u32ContentFlags; - - stcAnswer(void); - ~stcAnswer(void); - - bool clear(void); - - char* allocServiceDomain(size_t p_stLength); - bool releaseServiceDomain(void); - - char* allocHostDomain(size_t p_stLength); - bool releaseHostDomain(void); - - char* allocTxts(size_t p_stLength); - bool releaseTxts(void); - -#ifdef MDNS_IP4_SUPPORT - bool releaseIP4Addresses(void); - bool addIP4Address(stcIP4Address* p_pIP4Address); - bool removeIP4Address(stcIP4Address* p_pIP4Address); - const stcIP4Address* findIP4Address(const IPAddress& p_IPAddress) const; - stcIP4Address* findIP4Address(const IPAddress& p_IPAddress); - uint32_t IP4AddressCount(void) const; - const stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index) const; - stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index); -#endif -#ifdef MDNS_IP6_SUPPORT - bool releaseIP6Addresses(void); - bool addIP6Address(stcIP6Address* p_pIP6Address); - bool removeIP6Address(stcIP6Address* p_pIP6Address); - const stcIP6Address* findIP6Address(const IPAddress& p_IPAddress) const; - stcIP6Address* findIP6Address(const IPAddress& p_IPAddress); - uint32_t IP6AddressCount(void) const; - const stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index) const; - stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index); -#endif - }; - - stcMDNSServiceQuery* m_pNext; - stcMDNS_RRDomain m_ServiceTypeDomain; // eg. _http._tcp.local - MDNSServiceQueryCallbackFunc m_fnCallback; - bool m_bLegacyQuery; - uint8_t m_u8SentCount; - esp8266::polledTimeout::oneShotMs m_ResendTimeout; - bool m_bAwaitingAnswers; - stcAnswer* m_pAnswers; - - stcMDNSServiceQuery(void); - ~stcMDNSServiceQuery(void); - - bool clear(void); - - uint32_t answerCount(void) const; - const stcAnswer* answerAtIndex(uint32_t p_u32Index) const; - stcAnswer* answerAtIndex(uint32_t p_u32Index); - uint32_t indexOfAnswer(const stcAnswer* p_pAnswer) const; - - bool addAnswer(stcAnswer* p_pAnswer); - bool removeAnswer(stcAnswer* p_pAnswer); - - stcAnswer* findAnswerForServiceDomain(const stcMDNS_RRDomain& p_ServiceDomain); - stcAnswer* findAnswerForHostDomain(const stcMDNS_RRDomain& p_HostDomain); - }; - - /** - * stcMDNSSendParameter - */ - struct stcMDNSSendParameter { - protected: - /** - * stcDomainCacheItem - */ - struct stcDomainCacheItem { - stcDomainCacheItem* m_pNext; - const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer) - bool m_bAdditionalData; // Opaque flag for special info (service domain included) - uint16_t m_u16Offset; // Offset in UDP output buffer - - stcDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint32_t p_u16Offset); - }; - - public: - uint16_t m_u16ID; // Query ID (used only in lagacy queries) - stcMDNS_RRQuestion* m_pQuestions; // A list of queries - uint8_t m_u8HostReplyMask; // Flags for reply components/answers - bool m_bLegacyQuery; // Flag: Legacy query - bool m_bResponse; // Flag: Response to a query - bool m_bAuthorative; // Flag: Authorative (owner) response - bool m_bCacheFlush; // Flag: Clients should flush their caches - bool m_bUnicast; // Flag: Unicast response - bool m_bUnannounce; // Flag: Unannounce service - uint16_t m_u16Offset; // Current offset in UDP write buffer (mainly for domain cache) - stcDomainCacheItem* m_pDomainCacheItems; // Cached host and service domains - - stcMDNSSendParameter(void); - ~stcMDNSSendParameter(void); - - bool clear(void); - - bool shiftOffset(uint16_t p_u16Shift); - - bool addDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint16_t p_u16Offset); - uint16_t findCachedDomainOffset(const void* p_pHostnameOrService, - bool p_bAdditionalData) const; - }; - - // Instance variables - stcMDNSService* m_pServices; - UdpContext* m_pUDPContext; - char* m_pcHostname; - stcMDNSServiceQuery* m_pServiceQueries; - WiFiEventHandler m_DisconnectedHandler; - WiFiEventHandler m_GotIPHandler; - MDNSDynamicServiceTxtCallbackFunc m_fnServiceTxtCallback; - bool m_bPassivModeEnabled; - stcProbeInformation m_HostProbeInformation; - - /** CONTROL **/ - /* MAINTENANCE */ - bool _process(bool p_bUserContext); - bool _restart(void); - - /* RECEIVING */ - bool _parseMessage(void); - bool _parseQuery(const stcMDNS_MsgHeader& p_Header); - - bool _parseResponse(const stcMDNS_MsgHeader& p_Header); - bool _processAnswers(const stcMDNS_RRAnswer* p_pPTRAnswers); - bool _processPTRAnswer(const stcMDNS_RRAnswerPTR* p_pPTRAnswer, - bool& p_rbFoundNewKeyAnswer); - bool _processSRVAnswer(const stcMDNS_RRAnswerSRV* p_pSRVAnswer, - bool& p_rbFoundNewKeyAnswer); - bool _processTXTAnswer(const stcMDNS_RRAnswerTXT* p_pTXTAnswer); -#ifdef MDNS_IP4_SUPPORT - bool _processAAnswer(const stcMDNS_RRAnswerA* p_pAAnswer); -#endif -#ifdef MDNS_IP6_SUPPORT - bool _processAAAAAnswer(const stcMDNS_RRAnswerAAAA* p_pAAAAAnswer); -#endif - - /* PROBING */ - bool _updateProbeStatus(void); - bool _resetProbeStatus(bool p_bRestart = true); - bool _hasProbesWaitingForAnswers(void) const; - bool _sendHostProbe(void); - bool _sendServiceProbe(stcMDNSService& p_rService); - bool _cancelProbingForHost(void); - bool _cancelProbingForService(stcMDNSService& p_rService); - - /* ANNOUNCE */ - bool _announce(bool p_bAnnounce, - bool p_bIncludeServices); - bool _announceService(stcMDNSService& p_rService, - bool p_bAnnounce = true); - - /* SERVICE QUERY CACHE */ - bool _hasServiceQueriesWaitingForAnswers(void) const; - bool _checkServiceQueryCache(void); - - /** TRANSFER **/ - /* SENDING */ - bool _sendMDNSMessage(stcMDNSSendParameter& p_SendParameter); - bool _sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, - int p_iWiFiOpMode); - bool _prepareMDNSMessage(stcMDNSSendParameter& p_SendParameter, - IPAddress p_IPAddress); - bool _sendMDNSServiceQuery(const stcMDNSServiceQuery& p_ServiceQuery); - bool _sendMDNSQuery(const stcMDNS_RRDomain& p_QueryDomain, - uint16_t p_u16QueryType, - stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0); - - IPAddress _getResponseMulticastInterface(int p_iWiFiOpModes) const; - - uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader, - bool* p_pbFullNameMatch = 0) const; - uint8_t _replyMaskForService(const stcMDNS_RRHeader& p_RRHeader, - const stcMDNSService& p_Service, - bool* p_pbFullNameMatch = 0) const; - - /* RESOURCE RECORD */ - bool _readRRQuestion(stcMDNS_RRQuestion& p_rQuestion); - bool _readRRAnswer(stcMDNS_RRAnswer*& p_rpAnswer); -#ifdef MDNS_IP4_SUPPORT - bool _readRRAnswerA(stcMDNS_RRAnswerA& p_rRRAnswerA, - uint16_t p_u16RDLength); -#endif - bool _readRRAnswerPTR(stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, - uint16_t p_u16RDLength); - bool _readRRAnswerTXT(stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, - uint16_t p_u16RDLength); -#ifdef MDNS_IP6_SUPPORT - bool _readRRAnswerAAAA(stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, - uint16_t p_u16RDLength); -#endif - bool _readRRAnswerSRV(stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, - uint16_t p_u16RDLength); - bool _readRRAnswerGeneric(stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, - uint16_t p_u16RDLength); - - bool _readRRHeader(stcMDNS_RRHeader& p_rHeader); - bool _readRRDomain(stcMDNS_RRDomain& p_rRRDomain); - bool _readRRDomain_Loop(stcMDNS_RRDomain& p_rRRDomain, - uint8_t p_u8Depth); - bool _readRRAttributes(stcMDNS_RRAttributes& p_rAttributes); - - /* DOMAIN NAMES */ - bool _buildDomainForHost(const char* p_pcHostname, - stcMDNS_RRDomain& p_rHostDomain) const; - bool _buildDomainForDNSSD(stcMDNS_RRDomain& p_rDNSSDDomain) const; - bool _buildDomainForService(const stcMDNSService& p_Service, - bool p_bIncludeName, - stcMDNS_RRDomain& p_rServiceDomain) const; - bool _buildDomainForService(const char* p_pcService, - const char* p_pcProtocol, - stcMDNS_RRDomain& p_rServiceDomain) const; -#ifdef MDNS_IP4_SUPPORT - bool _buildDomainForReverseIP4(IPAddress p_IP4Address, - stcMDNS_RRDomain& p_rReverseIP4Domain) const; -#endif -#ifdef MDNS_IP6_SUPPORT - bool _buildDomainForReverseIP6(IPAddress p_IP4Address, - stcMDNS_RRDomain& p_rReverseIP6Domain) const; -#endif - - /* UDP */ - bool _udpReadBuffer(unsigned char* p_pBuffer, - size_t p_stLength); - bool _udpRead8(uint8_t& p_ru8Value); - bool _udpRead16(uint16_t& p_ru16Value); - bool _udpRead32(uint32_t& p_ru32Value); - - bool _udpAppendBuffer(const unsigned char* p_pcBuffer, - size_t p_stLength); - bool _udpAppend8(uint8_t p_u8Value); - bool _udpAppend16(uint16_t p_u16Value); - bool _udpAppend32(uint32_t p_u32Value); - -#if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER - bool _udpDump(bool p_bMovePointer = false); - bool _udpDump(unsigned p_uOffset, - unsigned p_uLength); -#endif - - /* READ/WRITE MDNS STRUCTS */ - bool _readMDNSMsgHeader(stcMDNS_MsgHeader& p_rMsgHeader); - - bool _write8(uint8_t p_u8Value, - stcMDNSSendParameter& p_rSendParameter); - bool _write16(uint16_t p_u16Value, - stcMDNSSendParameter& p_rSendParameter); - bool _write32(uint32_t p_u32Value, - stcMDNSSendParameter& p_rSendParameter); - - bool _writeMDNSMsgHeader(const stcMDNS_MsgHeader& p_MsgHeader, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSRRAttributes(const stcMDNS_RRAttributes& p_Attributes, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSRRDomain(const stcMDNS_RRDomain& p_Domain, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSHostDomain(const char* m_pcHostname, - bool p_bPrependRDLength, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSServiceDomain(const stcMDNSService& p_Service, - bool p_bIncludeName, - bool p_bPrependRDLength, - stcMDNSSendParameter& p_rSendParameter); - - bool _writeMDNSQuestion(stcMDNS_RRQuestion& p_Question, - stcMDNSSendParameter& p_rSendParameter); - -#ifdef MDNS_IP4_SUPPORT - bool _writeMDNSAnswer_A(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); -#endif - bool _writeMDNSAnswer_PTR_TYPE(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_PTR_NAME(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_TXT(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); -#ifdef MDNS_IP6_SUPPORT - bool _writeMDNSAnswer_AAAA(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); -#endif - bool _writeMDNSAnswer_SRV(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); - - /** HELPERS **/ - /* UDP CONTEXT */ - bool _callProcess(void); - bool _allocUDPContext(void); - bool _releaseUDPContext(void); - - /* SERVICE QUERY */ - stcMDNSServiceQuery* _allocServiceQuery(void); - bool _removeServiceQuery(stcMDNSServiceQuery* p_pServiceQuery); - bool _removeLegacyServiceQuery(void); - stcMDNSServiceQuery* _findServiceQuery(hMDNSServiceQuery p_hServiceQuery); - stcMDNSServiceQuery* _findLegacyServiceQuery(void); - bool _releaseServiceQueries(void); - stcMDNSServiceQuery* _findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceDomain, - const stcMDNSServiceQuery* p_pPrevServiceQuery); - - /* HOSTNAME */ - bool _setHostname(const char* p_pcHostname); - bool _releaseHostname(void); - - /* SERVICE */ - stcMDNSService* _allocService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - uint16_t p_u16Port); - bool _releaseService(stcMDNSService* p_pService); - bool _releaseServices(void); - - stcMDNSService* _findService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol); - stcMDNSService* _findService(const hMDNSService p_hService); - - size_t _countServices(void) const; - - /* SERVICE TXT */ - stcMDNSServiceTxt* _allocServiceTxt(stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp); - bool _releaseServiceTxt(stcMDNSService* p_pService, - stcMDNSServiceTxt* p_pTxt); - stcMDNSServiceTxt* _updateServiceTxt(stcMDNSService* p_pService, - stcMDNSServiceTxt* p_pTxt, - const char* p_pcValue, - bool p_bTemp); - - stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, - const char* p_pcKey); - stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, - const hMDNSTxt p_hTxt); - - stcMDNSServiceTxt* _addServiceTxt(stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp); - - stcMDNSServiceTxt* _answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - - bool _collectServiceTxts(stcMDNSService& p_rService); - bool _releaseTempServiceTxts(stcMDNSService& p_rService); - const stcMDNSServiceTxt* _serviceTxts(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol); - - /* MISC */ -#if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER - bool _printRRDomain(const stcMDNS_RRDomain& p_rRRDomain) const; - bool _printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const; -#endif -}; - -}// namespace MDNSImplementation - -}// namespace esp8266 - -#endif // MDNS_H +/* + LEAmDNS.h + (c) 2018, LaborEtArs + + Version 0.9 beta + + Some notes (from LaborEtArs, 2018): + 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 + 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. + + 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 + - 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 + - Tiebreaking while probing is supportet in a very minimalistic way (the 'higher' IP address wins the tiebreak) + - Announcing available services after successful probing + - Using fixed service TXT items or + - Using dynamic service TXT items for presented services (via callback) + - 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) + - Dynamic queries for DNS-SD services with cached and updated answers and user notifications + + + Usage: + 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 + of the new features should be used. + + For presenting services: + In 'setup()': + 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);' + (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 + using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service specific + 'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);' + Call MDNS.begin("MyHostname"); + + 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 + + In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, void* p_pUserdata)': + Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, "c#", "1");' + + In loop(): + Call 'MDNS.update();' + + + For querying services: + Static: + Call 'uint32_t u32AnswerCount = MDNS.queryService("http", "tcp");' + Iterate answers by: 'for (uint32_t u=0; u // for UdpContext.h +#include "WiFiUdp.h" +#include "lwip/udp.h" +#include "debug.h" +#include "include/UdpContext.h" +#include +#include +#include + + +#include "ESP8266WiFi.h" + + +namespace esp8266 +{ + +/** + LEAmDNS +*/ +namespace MDNSImplementation +{ + +//this should be defined at build time +#ifndef ARDUINO_BOARD +#define ARDUINO_BOARD "generic" +#endif + +#define MDNS_IP4_SUPPORT +//#define MDNS_IP6_SUPPORT + + +#ifdef MDNS_IP4_SUPPORT +#define MDNS_IP4_SIZE 4 +#endif +#ifdef MDNS_IP6_SUPPORT +#define MDNS_IP6_SIZE 16 +#endif +/* + Maximum length for all service txts for one service +*/ +#define MDNS_SERVICE_TXT_MAXLENGTH 1300 +/* + Maximum length for a full domain name eg. MyESP._http._tcp.local +*/ +#define MDNS_DOMAIN_MAXLENGTH 256 +/* + Maximum length of on label in a domain name (length info fits into 6 bits) +*/ +#define MDNS_DOMAIN_LABEL_MAXLENGTH 63 +/* + Maximum length of a service name eg. http +*/ +#define MDNS_SERVICE_NAME_LENGTH 15 +/* + Maximum length of a service protocol name eg. tcp +*/ +#define MDNS_SERVICE_PROTOCOL_LENGTH 3 +/* + Default timeout for static service queries +*/ +#define MDNS_QUERYSERVICES_WAIT_TIME 1000 + + +/** + MDNSResponder +*/ +class MDNSResponder +{ +public: + /* INTERFACE */ + MDNSResponder(void); + virtual ~MDNSResponder(void); + + // Start the MDNS responder by setting the default hostname + // Later call MDNS::update() in every 'loop' to run the process loop + // (probing, announcing, responding, ...) + bool begin(const char* p_pcHostname); + bool begin(const String& p_strHostname) + { + return begin(p_strHostname.c_str()); + } + // for compatibility + bool begin(const char* p_pcHostname, + IPAddress p_IPAddress, // ignored + uint32_t p_u32TTL = 120); // ignored + bool begin(const String& p_strHostname, + IPAddress p_IPAddress, // ignored + uint32_t p_u32TTL = 120) // ignored + { + return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL); + } + // Finish MDNS processing + bool close(void); + // for esp32 compatability + bool end(void); + // Change hostname (probing is restarted) + bool setHostname(const char* p_pcHostname); + // for compatibility... + bool setHostname(String p_strHostname); + + /** + hMDNSService (opaque handle to access the service) + */ + typedef const void* hMDNSService; + + // Add a new service to the MDNS responder. If no name (instance name) is given (p_pcName = 0) + // the current hostname is used. If the hostname is changed later, the instance names for + // these 'auto-named' services are changed to the new name also (and probing is restarted). + // The usual '_' before p_pcService (eg. http) and protocol (eg. tcp) may be given. + hMDNSService addService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol, + uint16_t p_u16Port); + // Removes a service from the MDNS responder + bool removeService(const hMDNSService p_hService); + bool removeService(const char* p_pcInstanceName, + const char* p_pcServiceName, + const char* p_pcProtocol); + // for compatibility... + bool addService(String p_strServiceName, + String p_strProtocol, + uint16_t p_u16Port); + + + // Change the services instance name (and restart probing). + bool setServiceName(const hMDNSService p_hService, + const char* p_pcInstanceName); + //for compatibility + //Warning: this has the side effect of changing the hostname. + //TODO: implement instancename different from hostname + void setInstanceName(const char* p_pcHostname) + { + setHostname(p_pcHostname); + } + // for esp32 compatibilty + void setInstanceName(const String& s_pcHostname) + { + setInstanceName(s_pcHostname.c_str()); + } + + /** + hMDNSTxt (opaque handle to access the TXT items) + */ + typedef void* hMDNSTxt; + + // Add a (static) MDNS TXT item ('key' = 'value') to the service + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + const char* p_pcValue); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + uint32_t p_u32Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + uint16_t p_u16Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + uint8_t p_u8Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + int32_t p_i32Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + int16_t p_i16Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, + const char* p_pcKey, + int8_t p_i8Value); + + // Remove an existing (static) MDNS TXT item from the service + bool removeServiceTxt(const hMDNSService p_hService, + const hMDNSTxt p_hTxt); + bool removeServiceTxt(const hMDNSService p_hService, + const char* p_pcKey); + bool removeServiceTxt(const char* p_pcinstanceName, + const char* p_pcServiceName, + const char* p_pcProtocol, + const char* p_pcKey); + // for compatibility... + bool addServiceTxt(const char* p_pcService, + const char* p_pcProtocol, + const char* p_pcKey, + const char* p_pcValue); + bool addServiceTxt(String p_strService, + String p_strProtocol, + String p_strKey, + String p_strValue); + + /** + MDNSDynamicServiceTxtCallbackFn + Callback function for dynamic MDNS TXT items + */ + + typedef std::function MDNSDynamicServiceTxtCallbackFunc; + + // Set a global callback for dynamic MDNS TXT items. The callback function is called + // every time, a TXT item is needed for one of the installed services. + bool setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallbackFunc p_fnCallback); + // Set a service specific callback for dynamic MDNS TXT items. The callback function + // is called every time, a TXT item is needed for the given service. + bool setDynamicServiceTxtCallback(const hMDNSService p_hService, + MDNSDynamicServiceTxtCallbackFunc p_fnCallback); + + // Add a (dynamic) MDNS TXT item ('key' = 'value') to the service + // Dynamic TXT items are removed right after one-time use. So they need to be added + // every time the value s needed (via callback). + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + const char* p_pcValue); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + uint32_t p_u32Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + uint16_t p_u16Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + uint8_t p_u8Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + int32_t p_i32Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + int16_t p_i16Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, + const char* p_pcKey, + int8_t p_i8Value); + + // Perform a (static) service query. The function returns after p_u16Timeout milliseconds + // The answers (the number of received answers is returned) can be retrieved by calling + // - answerHostname (or hostname) + // - answerIP (or IP) + // - answerPort (or port) + uint32_t queryService(const char* p_pcService, + const char* p_pcProtocol, + const uint16_t p_u16Timeout = MDNS_QUERYSERVICES_WAIT_TIME); + bool removeQuery(void); + // for compatibility... + uint32_t queryService(String p_strService, + String p_strProtocol); + + const char* answerHostname(const uint32_t p_u32AnswerIndex); + IPAddress answerIP(const uint32_t p_u32AnswerIndex); + uint16_t answerPort(const uint32_t p_u32AnswerIndex); + // for compatibility... + String hostname(const uint32_t p_u32AnswerIndex); + IPAddress IP(const uint32_t p_u32AnswerIndex); + uint16_t port(const uint32_t p_u32AnswerIndex); + + /** + hMDNSServiceQuery (opaque handle to access dynamic service queries) + */ + typedef const void* hMDNSServiceQuery; + + /** + enuServiceQueryAnswerType + */ + typedef enum _enuServiceQueryAnswerType + { + ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name + ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port + ServiceQueryAnswerType_Txts = (1 << 2), // TXT items +#ifdef MDNS_IP4_SUPPORT + ServiceQueryAnswerType_IP4Address = (1 << 3), // IP4 address +#endif +#ifdef MDNS_IP6_SUPPORT + ServiceQueryAnswerType_IP6Address = (1 << 4), // IP6 address +#endif + } enuServiceQueryAnswerType; + + enum class AnswerType : uint32_t + { + Unknown = 0, + ServiceDomain = ServiceQueryAnswerType_ServiceDomain, + HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort, + Txt = ServiceQueryAnswerType_Txts, +#ifdef MDNS_IP4_SUPPORT + IP4Address = ServiceQueryAnswerType_IP4Address, +#endif +#ifdef MDNS_IP6_SUPPORT + IP6Address = ServiceQueryAnswerType_IP6Address, +#endif + }; + + /** + MDNSServiceQueryCallbackFn + Callback function for received answers for dynamic service queries + */ + struct MDNSServiceInfo; // forward declaration + typedef std::function MDNSServiceQueryCallbackFunc; + + // Install a dynamic service query. For every received answer (part) the given callback + // function is called. The query will be updated every time, the TTL for an answer + // has timed-out. + // The answers can also be retrieved by calling + // - answerCount + // - answerServiceDomain + // - hasAnswerHostDomain/answerHostDomain + // - hasAnswerIP4Address/answerIP4Address + // - hasAnswerIP6Address/answerIP6Address + // - hasAnswerPort/answerPort + // - hasAnswerTxts/answerTxts + hMDNSServiceQuery installServiceQuery(const char* p_pcService, + const char* p_pcProtocol, + MDNSServiceQueryCallbackFunc p_fnCallback); + // Remove a dynamic service query + bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery); + + uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery); + std::vector answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery); + + const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + bool hasAnswerHostDomain(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + const char* answerHostDomain(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); +#ifdef MDNS_IP4_SUPPORT + bool hasAnswerIP4Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + uint32_t answerIP4AddressCount(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + IPAddress answerIP4Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex, + const uint32_t p_u32AddressIndex); +#endif +#ifdef MDNS_IP6_SUPPORT + bool hasAnswerIP6Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + uint32_t answerIP6AddressCount(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + IPAddress answerIP6Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex, + const uint32_t p_u32AddressIndex); +#endif + bool hasAnswerPort(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + uint16_t answerPort(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + bool hasAnswerTxts(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + // Get the TXT items as a ';'-separated string + const char* answerTxts(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + + /** + MDNSProbeResultCallbackFn + Callback function for (host and service domain) probe results + */ + typedef std::function MDNSHostProbeFn; + + typedef std::function MDNSHostProbeFn1; + + typedef std::function MDNSServiceProbeFn; + + typedef std::function MDNSServiceProbeFn1; + + // Set a global callback function for host and service probe results + // The callback function is called, when the probing for the host domain + // (or a service domain, which hasn't got a service specific callback) + // Succeeds or fails. + // In case of failure, the failed domain name should be changed. + bool setHostProbeResultCallback(MDNSHostProbeFn p_fnCallback); + bool setHostProbeResultCallback(MDNSHostProbeFn1 p_fnCallback); + + // Set a service specific probe result callback + bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSServiceProbeFn p_fnCallback); + bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSServiceProbeFn1 p_fnCallback); + + // Application should call this whenever AP is configured/disabled + bool notifyAPChange(void); + + // 'update' should be called in every 'loop' to run the MDNS processing + bool update(void); + + // 'announce' can be called every time, the configuration of some service + // changes. Mainly, this would be changed content of TXT items. + bool announce(void); + + // Enable OTA update + hMDNSService enableArduino(uint16_t p_u16Port, + bool p_bAuthUpload = false); + + // Domain name helper + static bool indexDomain(char*& p_rpcDomain, + const char* p_pcDivider = "-", + const char* p_pcDefaultDomain = 0); + +protected: + /** STRUCTS **/ + /** + MDNSServiceInfo, used in application callbacks + */ +public: + struct MDNSServiceInfo + { + MDNSServiceInfo(MDNSResponder& p_pM, MDNSResponder::hMDNSServiceQuery p_hS, uint32_t p_u32A) + : p_pMDNSResponder(p_pM), + p_hServiceQuery(p_hS), + p_u32AnswerIndex(p_u32A) + {}; + struct CompareKey + { + bool operator()(char const *a, char const *b) const + { + return strcmp(a, b) < 0; + } + }; + using KeyValueMap = std::map; + protected: + MDNSResponder& p_pMDNSResponder; + MDNSResponder::hMDNSServiceQuery p_hServiceQuery; + uint32_t p_u32AnswerIndex; + KeyValueMap keyValueMap; + public: + const char* serviceDomain() + { + return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex); + }; + bool hostDomainAvailable() + { + return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex)); + } + const char* hostDomain() + { + return (hostDomainAvailable()) ? + p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) : nullptr; + }; + bool hostPortAvailable() + { + return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex)); + } + uint16_t hostPort() + { + return (hostPortAvailable()) ? + p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) : 0; + }; + bool IP4AddressAvailable() + { + return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery, p_u32AnswerIndex)); + } + std::vector IP4Adresses() + { + std::vector internalIP; + if (IP4AddressAvailable()) + { + uint16_t cntIP4Adress = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex); + for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2) + { + internalIP.emplace_back(p_pMDNSResponder.answerIP4Address(p_hServiceQuery, p_u32AnswerIndex, u2)); + } + } + return internalIP; + }; + bool txtAvailable() + { + return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex)); + } + const char* strKeyValue() + { + return (txtAvailable()) ? + p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) : nullptr; + }; + const KeyValueMap& keyValues() + { + if (txtAvailable() && keyValueMap.size() == 0) + { + for (auto kv = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); kv != nullptr; kv = kv->m_pNext) + { + keyValueMap.emplace(std::pair(kv->m_pcKey, kv->m_pcValue)); + } + } + return keyValueMap; + } + const char* value(const char* key) + { + char* result = nullptr; + + for (stcMDNSServiceTxt* pTxt = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); pTxt; pTxt = pTxt->m_pNext) + { + if ((key) && + (0 == strcmp(pTxt->m_pcKey, key))) + { + result = pTxt->m_pcValue; + break; + } + } + return result; + } + }; +protected: + + /** + stcMDNSServiceTxt + */ + struct stcMDNSServiceTxt + { + stcMDNSServiceTxt* m_pNext; + char* m_pcKey; + char* m_pcValue; + bool m_bTemp; + + stcMDNSServiceTxt(const char* p_pcKey = 0, + const char* p_pcValue = 0, + bool p_bTemp = false); + stcMDNSServiceTxt(const stcMDNSServiceTxt& p_Other); + ~stcMDNSServiceTxt(void); + + stcMDNSServiceTxt& operator=(const stcMDNSServiceTxt& p_Other); + bool clear(void); + + char* allocKey(size_t p_stLength); + bool setKey(const char* p_pcKey, + size_t p_stLength); + bool setKey(const char* p_pcKey); + bool releaseKey(void); + + char* allocValue(size_t p_stLength); + bool setValue(const char* p_pcValue, + size_t p_stLength); + bool setValue(const char* p_pcValue); + bool releaseValue(void); + + bool set(const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp = false); + + bool update(const char* p_pcValue); + + size_t length(void) const; + }; + + /** + stcMDNSTxts + */ + struct stcMDNSServiceTxts + { + stcMDNSServiceTxt* m_pTxts; + + stcMDNSServiceTxts(void); + stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other); + ~stcMDNSServiceTxts(void); + + stcMDNSServiceTxts& operator=(const stcMDNSServiceTxts& p_Other); + + bool clear(void); + + bool add(stcMDNSServiceTxt* p_pTxt); + bool remove(stcMDNSServiceTxt* p_pTxt); + + bool removeTempTxts(void); + + stcMDNSServiceTxt* find(const char* p_pcKey); + const stcMDNSServiceTxt* find(const char* p_pcKey) const; + stcMDNSServiceTxt* find(const stcMDNSServiceTxt* p_pTxt); + + uint16_t length(void) const; + + size_t c_strLength(void) const; + bool c_str(char* p_pcBuffer); + + size_t bufferLength(void) const; + bool buffer(char* p_pcBuffer); + + bool compare(const stcMDNSServiceTxts& p_Other) const; + bool operator==(const stcMDNSServiceTxts& p_Other) const; + bool operator!=(const stcMDNSServiceTxts& p_Other) const; + }; + + /** + enuContentFlags + */ + typedef enum _enuContentFlags + { + // Host + ContentFlag_A = 0x01, + ContentFlag_PTR_IP4 = 0x02, + ContentFlag_PTR_IP6 = 0x04, + ContentFlag_AAAA = 0x08, + // Service + ContentFlag_PTR_TYPE = 0x10, + ContentFlag_PTR_NAME = 0x20, + ContentFlag_TXT = 0x40, + ContentFlag_SRV = 0x80, + } enuContentFlags; + + /** + stcMDNS_MsgHeader + */ + struct stcMDNS_MsgHeader + { + uint16_t m_u16ID; // Identifier + bool m_1bQR : 1; // Query/Response flag + unsigned char m_4bOpcode : 4; // Operation code + bool m_1bAA : 1; // Authoritative Answer flag + bool m_1bTC : 1; // Truncation flag + bool m_1bRD : 1; // Recursion desired + bool m_1bRA : 1; // Recursion available + unsigned char m_3bZ : 3; // Zero + unsigned char m_4bRCode : 4; // Response code + uint16_t m_u16QDCount; // Question count + uint16_t m_u16ANCount; // Answer count + uint16_t m_u16NSCount; // Authority Record count + uint16_t m_u16ARCount; // Additional Record count + + stcMDNS_MsgHeader(uint16_t p_u16ID = 0, + bool p_bQR = false, + unsigned char p_ucOpcode = 0, + bool p_bAA = false, + bool p_bTC = false, + bool p_bRD = false, + bool p_bRA = false, + unsigned char p_ucRCode = 0, + uint16_t p_u16QDCount = 0, + uint16_t p_u16ANCount = 0, + uint16_t p_u16NSCount = 0, + uint16_t p_u16ARCount = 0); + }; + + /** + stcMDNS_RRDomain + */ + struct stcMDNS_RRDomain + { + char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name + uint16_t m_u16NameLength; // Length (incl. '\0') + + stcMDNS_RRDomain(void); + stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other); + + stcMDNS_RRDomain& operator=(const stcMDNS_RRDomain& p_Other); + + bool clear(void); + + bool addLabel(const char* p_pcLabel, + bool p_bPrependUnderline = false); + + bool compare(const stcMDNS_RRDomain& p_Other) const; + bool operator==(const stcMDNS_RRDomain& p_Other) const; + bool operator!=(const stcMDNS_RRDomain& p_Other) const; + bool operator>(const stcMDNS_RRDomain& p_Other) const; + + size_t c_strLength(void) const; + bool c_str(char* p_pcBuffer); + }; + + /** + stcMDNS_RRAttributes + */ + struct stcMDNS_RRAttributes + { + uint16_t m_u16Type; // Type + uint16_t m_u16Class; // Class, nearly always 'IN' + + stcMDNS_RRAttributes(uint16_t p_u16Type = 0, + uint16_t p_u16Class = 1 /*DNS_RRCLASS_IN Internet*/); + stcMDNS_RRAttributes(const stcMDNS_RRAttributes& p_Other); + + stcMDNS_RRAttributes& operator=(const stcMDNS_RRAttributes& p_Other); + }; + + /** + stcMDNS_RRHeader + */ + struct stcMDNS_RRHeader + { + stcMDNS_RRDomain m_Domain; + stcMDNS_RRAttributes m_Attributes; + + stcMDNS_RRHeader(void); + stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other); + + stcMDNS_RRHeader& operator=(const stcMDNS_RRHeader& p_Other); + + bool clear(void); + }; + + /** + stcMDNS_RRQuestion + */ + struct stcMDNS_RRQuestion + { + stcMDNS_RRQuestion* m_pNext; + stcMDNS_RRHeader m_Header; + bool m_bUnicast; // Unicast reply requested + + stcMDNS_RRQuestion(void); + }; + + /** + enuAnswerType + */ + typedef enum _enuAnswerType + { + AnswerType_A, + AnswerType_PTR, + AnswerType_TXT, + AnswerType_AAAA, + AnswerType_SRV, + AnswerType_Generic + } enuAnswerType; + + /** + stcMDNS_RRAnswer + */ + struct stcMDNS_RRAnswer + { + stcMDNS_RRAnswer* m_pNext; + const enuAnswerType m_AnswerType; + stcMDNS_RRHeader m_Header; + bool m_bCacheFlush; // Cache flush command bit + uint32_t m_u32TTL; // Validity time in seconds + + virtual ~stcMDNS_RRAnswer(void); + + enuAnswerType answerType(void) const; + + bool clear(void); + + protected: + stcMDNS_RRAnswer(enuAnswerType p_AnswerType, + const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + }; + +#ifdef MDNS_IP4_SUPPORT + /** + stcMDNS_RRAnswerA + */ + struct stcMDNS_RRAnswerA : public stcMDNS_RRAnswer + { + IPAddress m_IPAddress; + + stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerA(void); + + bool clear(void); + }; +#endif + + /** + stcMDNS_RRAnswerPTR + */ + struct stcMDNS_RRAnswerPTR : public stcMDNS_RRAnswer + { + stcMDNS_RRDomain m_PTRDomain; + + stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerPTR(void); + + bool clear(void); + }; + + /** + stcMDNS_RRAnswerTXT + */ + struct stcMDNS_RRAnswerTXT : public stcMDNS_RRAnswer + { + stcMDNSServiceTxts m_Txts; + + stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerTXT(void); + + bool clear(void); + }; + +#ifdef MDNS_IP6_SUPPORT + /** + stcMDNS_RRAnswerAAAA + */ + struct stcMDNS_RRAnswerAAAA : public stcMDNS_RRAnswer + { + //TODO: IP6Address m_IPAddress; + + stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerAAAA(void); + + bool clear(void); + }; +#endif + + /** + stcMDNS_RRAnswerSRV + */ + struct stcMDNS_RRAnswerSRV : public stcMDNS_RRAnswer + { + uint16_t m_u16Priority; + uint16_t m_u16Weight; + uint16_t m_u16Port; + stcMDNS_RRDomain m_SRVDomain; + + stcMDNS_RRAnswerSRV(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerSRV(void); + + bool clear(void); + }; + + /** + stcMDNS_RRAnswerGeneric + */ + struct stcMDNS_RRAnswerGeneric : public stcMDNS_RRAnswer + { + uint16_t m_u16RDLength; // Length of variable answer + uint8_t* m_pu8RDData; // Offset of start of variable answer in packet + + stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + ~stcMDNS_RRAnswerGeneric(void); + + bool clear(void); + }; + + + /** + enuProbingStatus + */ + typedef enum _enuProbingStatus + { + ProbingStatus_WaitingForData, + ProbingStatus_ReadyToStart, + ProbingStatus_InProgress, + ProbingStatus_Done + } enuProbingStatus; + + /** + stcProbeInformation + */ + struct stcProbeInformation + { + enuProbingStatus m_ProbingStatus; + uint8_t m_u8SentCount; // Used for probes and announcements + esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements + //clsMDNSTimeFlag m_TimeFlag; // Used for probes and announcements + bool m_bConflict; + bool m_bTiebreakNeeded; + MDNSHostProbeFn m_fnHostProbeResultCallback; + MDNSServiceProbeFn m_fnServiceProbeResultCallback; + + stcProbeInformation(void); + + bool clear(bool p_bClearUserdata = false); + }; + + + /** + stcMDNSService + */ + struct stcMDNSService + { + stcMDNSService* m_pNext; + char* m_pcName; + bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied) + char* m_pcService; + char* m_pcProtocol; + uint16_t m_u16Port; + uint8_t m_u8ReplyMask; + stcMDNSServiceTxts m_Txts; + MDNSDynamicServiceTxtCallbackFunc m_fnTxtCallback; + stcProbeInformation m_ProbeInformation; + + stcMDNSService(const char* p_pcName = 0, + const char* p_pcService = 0, + const char* p_pcProtocol = 0); + ~stcMDNSService(void); + + bool setName(const char* p_pcName); + bool releaseName(void); + + bool setService(const char* p_pcService); + bool releaseService(void); + + bool setProtocol(const char* p_pcProtocol); + bool releaseProtocol(void); + }; + + /** + stcMDNSServiceQuery + */ + struct stcMDNSServiceQuery + { + /** + stcAnswer + */ + struct stcAnswer + { + /** + stcTTL + */ + struct stcTTL + { + /** + timeoutLevel_t + */ + typedef uint8_t timeoutLevel_t; + /** + TIMEOUTLEVELs + */ + const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0; + const timeoutLevel_t TIMEOUTLEVEL_BASE = 80; + const timeoutLevel_t TIMEOUTLEVEL_INTERVAL = 5; + const timeoutLevel_t TIMEOUTLEVEL_FINAL = 100; + + uint32_t m_u32TTL; + esp8266::polledTimeout::oneShotMs m_TTLTimeout; + timeoutLevel_t m_timeoutLevel; + + stcTTL(void); + bool set(uint32_t p_u32TTL); + + bool flagged(void); + bool restart(void); + + bool prepareDeletion(void); + bool finalTimeoutLevel(void) const; + + unsigned long timeout(void) const; + }; +#ifdef MDNS_IP4_SUPPORT + /** + stcIP4Address + */ + struct stcIP4Address + { + stcIP4Address* m_pNext; + IPAddress m_IPAddress; + stcTTL m_TTL; + + stcIP4Address(IPAddress p_IPAddress, + uint32_t p_u32TTL = 0); + }; +#endif +#ifdef MDNS_IP6_SUPPORT + /** + stcIP6Address + */ + struct stcIP6Address + { + stcIP6Address* m_pNext; + IP6Address m_IPAddress; + stcTTL m_TTL; + + stcIP6Address(IPAddress p_IPAddress, + uint32_t p_u32TTL = 0); + }; +#endif + + stcAnswer* m_pNext; + // The service domain is the first 'answer' (from PTR answer, using service and protocol) to be set + // Defines the key for additional answer, like host domain, etc. + stcMDNS_RRDomain m_ServiceDomain; // 1. level answer (PTR), eg. MyESP._http._tcp.local + char* m_pcServiceDomain; + stcTTL m_TTLServiceDomain; + stcMDNS_RRDomain m_HostDomain; // 2. level answer (SRV, using service domain), eg. esp8266.local + char* m_pcHostDomain; + uint16_t m_u16Port; // 2. level answer (SRV, using service domain), eg. 5000 + stcTTL m_TTLHostDomainAndPort; + stcMDNSServiceTxts m_Txts; // 2. level answer (TXT, using service domain), eg. c#=1 + char* m_pcTxts; + stcTTL m_TTLTxts; +#ifdef MDNS_IP4_SUPPORT + stcIP4Address* m_pIP4Addresses; // 3. level answer (A, using host domain), eg. 123.456.789.012 +#endif +#ifdef MDNS_IP6_SUPPORT + stcIP6Address* m_pIP6Addresses; // 3. level answer (AAAA, using host domain), eg. 1234::09 +#endif + uint32_t m_u32ContentFlags; + + stcAnswer(void); + ~stcAnswer(void); + + bool clear(void); + + char* allocServiceDomain(size_t p_stLength); + bool releaseServiceDomain(void); + + char* allocHostDomain(size_t p_stLength); + bool releaseHostDomain(void); + + char* allocTxts(size_t p_stLength); + bool releaseTxts(void); + +#ifdef MDNS_IP4_SUPPORT + bool releaseIP4Addresses(void); + bool addIP4Address(stcIP4Address* p_pIP4Address); + bool removeIP4Address(stcIP4Address* p_pIP4Address); + const stcIP4Address* findIP4Address(const IPAddress& p_IPAddress) const; + stcIP4Address* findIP4Address(const IPAddress& p_IPAddress); + uint32_t IP4AddressCount(void) const; + const stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index) const; + stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index); +#endif +#ifdef MDNS_IP6_SUPPORT + bool releaseIP6Addresses(void); + bool addIP6Address(stcIP6Address* p_pIP6Address); + bool removeIP6Address(stcIP6Address* p_pIP6Address); + const stcIP6Address* findIP6Address(const IPAddress& p_IPAddress) const; + stcIP6Address* findIP6Address(const IPAddress& p_IPAddress); + uint32_t IP6AddressCount(void) const; + const stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index) const; + stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index); +#endif + }; + + stcMDNSServiceQuery* m_pNext; + stcMDNS_RRDomain m_ServiceTypeDomain; // eg. _http._tcp.local + MDNSServiceQueryCallbackFunc m_fnCallback; + bool m_bLegacyQuery; + uint8_t m_u8SentCount; + esp8266::polledTimeout::oneShotMs m_ResendTimeout; + bool m_bAwaitingAnswers; + stcAnswer* m_pAnswers; + + stcMDNSServiceQuery(void); + ~stcMDNSServiceQuery(void); + + bool clear(void); + + uint32_t answerCount(void) const; + const stcAnswer* answerAtIndex(uint32_t p_u32Index) const; + stcAnswer* answerAtIndex(uint32_t p_u32Index); + uint32_t indexOfAnswer(const stcAnswer* p_pAnswer) const; + + bool addAnswer(stcAnswer* p_pAnswer); + bool removeAnswer(stcAnswer* p_pAnswer); + + stcAnswer* findAnswerForServiceDomain(const stcMDNS_RRDomain& p_ServiceDomain); + stcAnswer* findAnswerForHostDomain(const stcMDNS_RRDomain& p_HostDomain); + }; + + /** + stcMDNSSendParameter + */ + struct stcMDNSSendParameter + { + protected: + /** + stcDomainCacheItem + */ + struct stcDomainCacheItem + { + stcDomainCacheItem* m_pNext; + const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer) + bool m_bAdditionalData; // Opaque flag for special info (service domain included) + uint16_t m_u16Offset; // Offset in UDP output buffer + + stcDomainCacheItem(const void* p_pHostnameOrService, + bool p_bAdditionalData, + uint32_t p_u16Offset); + }; + + public: + uint16_t m_u16ID; // Query ID (used only in lagacy queries) + stcMDNS_RRQuestion* m_pQuestions; // A list of queries + uint8_t m_u8HostReplyMask; // Flags for reply components/answers + bool m_bLegacyQuery; // Flag: Legacy query + bool m_bResponse; // Flag: Response to a query + bool m_bAuthorative; // Flag: Authorative (owner) response + bool m_bCacheFlush; // Flag: Clients should flush their caches + bool m_bUnicast; // Flag: Unicast response + bool m_bUnannounce; // Flag: Unannounce service + uint16_t m_u16Offset; // Current offset in UDP write buffer (mainly for domain cache) + stcDomainCacheItem* m_pDomainCacheItems; // Cached host and service domains + + stcMDNSSendParameter(void); + ~stcMDNSSendParameter(void); + + bool clear(void); + + bool shiftOffset(uint16_t p_u16Shift); + + bool addDomainCacheItem(const void* p_pHostnameOrService, + bool p_bAdditionalData, + uint16_t p_u16Offset); + uint16_t findCachedDomainOffset(const void* p_pHostnameOrService, + bool p_bAdditionalData) const; + }; + + // Instance variables + stcMDNSService* m_pServices; + UdpContext* m_pUDPContext; + char* m_pcHostname; + stcMDNSServiceQuery* m_pServiceQueries; + WiFiEventHandler m_DisconnectedHandler; + WiFiEventHandler m_GotIPHandler; + MDNSDynamicServiceTxtCallbackFunc m_fnServiceTxtCallback; + bool m_bPassivModeEnabled; + stcProbeInformation m_HostProbeInformation; + + /** CONTROL **/ + /* MAINTENANCE */ + bool _process(bool p_bUserContext); + bool _restart(void); + + /* RECEIVING */ + bool _parseMessage(void); + bool _parseQuery(const stcMDNS_MsgHeader& p_Header); + + bool _parseResponse(const stcMDNS_MsgHeader& p_Header); + bool _processAnswers(const stcMDNS_RRAnswer* p_pPTRAnswers); + bool _processPTRAnswer(const stcMDNS_RRAnswerPTR* p_pPTRAnswer, + bool& p_rbFoundNewKeyAnswer); + bool _processSRVAnswer(const stcMDNS_RRAnswerSRV* p_pSRVAnswer, + bool& p_rbFoundNewKeyAnswer); + bool _processTXTAnswer(const stcMDNS_RRAnswerTXT* p_pTXTAnswer); +#ifdef MDNS_IP4_SUPPORT + bool _processAAnswer(const stcMDNS_RRAnswerA* p_pAAnswer); +#endif +#ifdef MDNS_IP6_SUPPORT + bool _processAAAAAnswer(const stcMDNS_RRAnswerAAAA* p_pAAAAAnswer); +#endif + + /* PROBING */ + bool _updateProbeStatus(void); + bool _resetProbeStatus(bool p_bRestart = true); + bool _hasProbesWaitingForAnswers(void) const; + bool _sendHostProbe(void); + bool _sendServiceProbe(stcMDNSService& p_rService); + bool _cancelProbingForHost(void); + bool _cancelProbingForService(stcMDNSService& p_rService); + + /* ANNOUNCE */ + bool _announce(bool p_bAnnounce, + bool p_bIncludeServices); + bool _announceService(stcMDNSService& p_rService, + bool p_bAnnounce = true); + + /* SERVICE QUERY CACHE */ + bool _hasServiceQueriesWaitingForAnswers(void) const; + bool _checkServiceQueryCache(void); + + /** TRANSFER **/ + /* SENDING */ + bool _sendMDNSMessage(stcMDNSSendParameter& p_SendParameter); + bool _sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, + int p_iWiFiOpMode); + bool _prepareMDNSMessage(stcMDNSSendParameter& p_SendParameter, + IPAddress p_IPAddress); + bool _sendMDNSServiceQuery(const stcMDNSServiceQuery& p_ServiceQuery); + bool _sendMDNSQuery(const stcMDNS_RRDomain& p_QueryDomain, + uint16_t p_u16QueryType, + stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0); + + IPAddress _getResponseMulticastInterface(int p_iWiFiOpModes) const; + + uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader, + bool* p_pbFullNameMatch = 0) const; + uint8_t _replyMaskForService(const stcMDNS_RRHeader& p_RRHeader, + const stcMDNSService& p_Service, + bool* p_pbFullNameMatch = 0) const; + + /* RESOURCE RECORD */ + bool _readRRQuestion(stcMDNS_RRQuestion& p_rQuestion); + bool _readRRAnswer(stcMDNS_RRAnswer*& p_rpAnswer); +#ifdef MDNS_IP4_SUPPORT + bool _readRRAnswerA(stcMDNS_RRAnswerA& p_rRRAnswerA, + uint16_t p_u16RDLength); +#endif + bool _readRRAnswerPTR(stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, + uint16_t p_u16RDLength); + bool _readRRAnswerTXT(stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, + uint16_t p_u16RDLength); +#ifdef MDNS_IP6_SUPPORT + bool _readRRAnswerAAAA(stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, + uint16_t p_u16RDLength); +#endif + bool _readRRAnswerSRV(stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, + uint16_t p_u16RDLength); + bool _readRRAnswerGeneric(stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, + uint16_t p_u16RDLength); + + bool _readRRHeader(stcMDNS_RRHeader& p_rHeader); + bool _readRRDomain(stcMDNS_RRDomain& p_rRRDomain); + bool _readRRDomain_Loop(stcMDNS_RRDomain& p_rRRDomain, + uint8_t p_u8Depth); + bool _readRRAttributes(stcMDNS_RRAttributes& p_rAttributes); + + /* DOMAIN NAMES */ + bool _buildDomainForHost(const char* p_pcHostname, + stcMDNS_RRDomain& p_rHostDomain) const; + bool _buildDomainForDNSSD(stcMDNS_RRDomain& p_rDNSSDDomain) const; + bool _buildDomainForService(const stcMDNSService& p_Service, + bool p_bIncludeName, + stcMDNS_RRDomain& p_rServiceDomain) const; + bool _buildDomainForService(const char* p_pcService, + const char* p_pcProtocol, + stcMDNS_RRDomain& p_rServiceDomain) const; +#ifdef MDNS_IP4_SUPPORT + bool _buildDomainForReverseIP4(IPAddress p_IP4Address, + stcMDNS_RRDomain& p_rReverseIP4Domain) const; +#endif +#ifdef MDNS_IP6_SUPPORT + bool _buildDomainForReverseIP6(IPAddress p_IP4Address, + stcMDNS_RRDomain& p_rReverseIP6Domain) const; +#endif + + /* UDP */ + bool _udpReadBuffer(unsigned char* p_pBuffer, + size_t p_stLength); + bool _udpRead8(uint8_t& p_ru8Value); + bool _udpRead16(uint16_t& p_ru16Value); + bool _udpRead32(uint32_t& p_ru32Value); + + bool _udpAppendBuffer(const unsigned char* p_pcBuffer, + size_t p_stLength); + bool _udpAppend8(uint8_t p_u8Value); + bool _udpAppend16(uint16_t p_u16Value); + bool _udpAppend32(uint32_t p_u32Value); + +#if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER + bool _udpDump(bool p_bMovePointer = false); + bool _udpDump(unsigned p_uOffset, + unsigned p_uLength); +#endif + + /* READ/WRITE MDNS STRUCTS */ + bool _readMDNSMsgHeader(stcMDNS_MsgHeader& p_rMsgHeader); + + bool _write8(uint8_t p_u8Value, + stcMDNSSendParameter& p_rSendParameter); + bool _write16(uint16_t p_u16Value, + stcMDNSSendParameter& p_rSendParameter); + bool _write32(uint32_t p_u32Value, + stcMDNSSendParameter& p_rSendParameter); + + bool _writeMDNSMsgHeader(const stcMDNS_MsgHeader& p_MsgHeader, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSRRAttributes(const stcMDNS_RRAttributes& p_Attributes, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSRRDomain(const stcMDNS_RRDomain& p_Domain, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSHostDomain(const char* m_pcHostname, + bool p_bPrependRDLength, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSServiceDomain(const stcMDNSService& p_Service, + bool p_bIncludeName, + bool p_bPrependRDLength, + stcMDNSSendParameter& p_rSendParameter); + + bool _writeMDNSQuestion(stcMDNS_RRQuestion& p_Question, + stcMDNSSendParameter& p_rSendParameter); + +#ifdef MDNS_IP4_SUPPORT + bool _writeMDNSAnswer_A(IPAddress p_IPAddress, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, + stcMDNSSendParameter& p_rSendParameter); +#endif + bool _writeMDNSAnswer_PTR_TYPE(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_PTR_NAME(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_TXT(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); +#ifdef MDNS_IP6_SUPPORT + bool _writeMDNSAnswer_AAAA(IPAddress p_IPAddress, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, + stcMDNSSendParameter& p_rSendParameter); +#endif + bool _writeMDNSAnswer_SRV(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); + + /** HELPERS **/ + /* UDP CONTEXT */ + bool _callProcess(void); + bool _allocUDPContext(void); + bool _releaseUDPContext(void); + + /* SERVICE QUERY */ + stcMDNSServiceQuery* _allocServiceQuery(void); + bool _removeServiceQuery(stcMDNSServiceQuery* p_pServiceQuery); + bool _removeLegacyServiceQuery(void); + stcMDNSServiceQuery* _findServiceQuery(hMDNSServiceQuery p_hServiceQuery); + stcMDNSServiceQuery* _findLegacyServiceQuery(void); + bool _releaseServiceQueries(void); + stcMDNSServiceQuery* _findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceDomain, + const stcMDNSServiceQuery* p_pPrevServiceQuery); + + /* HOSTNAME */ + bool _setHostname(const char* p_pcHostname); + bool _releaseHostname(void); + + /* SERVICE */ + stcMDNSService* _allocService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol, + uint16_t p_u16Port); + bool _releaseService(stcMDNSService* p_pService); + bool _releaseServices(void); + + stcMDNSService* _findService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol); + stcMDNSService* _findService(const hMDNSService p_hService); + + size_t _countServices(void) const; + + /* SERVICE TXT */ + stcMDNSServiceTxt* _allocServiceTxt(stcMDNSService* p_pService, + const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp); + bool _releaseServiceTxt(stcMDNSService* p_pService, + stcMDNSServiceTxt* p_pTxt); + stcMDNSServiceTxt* _updateServiceTxt(stcMDNSService* p_pService, + stcMDNSServiceTxt* p_pTxt, + const char* p_pcValue, + bool p_bTemp); + + stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, + const char* p_pcKey); + stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, + const hMDNSTxt p_hTxt); + + stcMDNSServiceTxt* _addServiceTxt(stcMDNSService* p_pService, + const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp); + + stcMDNSServiceTxt* _answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + + bool _collectServiceTxts(stcMDNSService& p_rService); + bool _releaseTempServiceTxts(stcMDNSService& p_rService); + const stcMDNSServiceTxt* _serviceTxts(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol); + + /* MISC */ +#if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER + bool _printRRDomain(const stcMDNS_RRDomain& p_rRRDomain) const; + bool _printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const; +#endif +}; + +}// namespace MDNSImplementation + +}// namespace esp8266 + +#endif // MDNS_H diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp index d5bd4dde61..0ed04eb1c8 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp @@ -1,1830 +1,2132 @@ -/* - * LEAmDNS_Control.cpp - * - * License (MIT license): - * 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. - * - */ - -#include -#include -#include -#include -#include -#include - -/* - * ESP8266mDNS Control.cpp - */ - -extern "C" { - #include "user_interface.h" -} - -#include "LEAmDNS_lwIPdefs.h" -#include "LEAmDNS_Priv.h" - -namespace esp8266 { -/* - * LEAmDNS - */ -namespace MDNSImplementation { - -/** - * CONTROL - */ - - -/** - * MAINTENANCE - */ - -/* - * MDNSResponder::_process - * - * Run the MDNS process. - * Is called, every time the UDPContext receives data AND - * should be called in every 'loop' by calling 'MDNS::update()'. - * - */ -bool MDNSResponder::_process(bool p_bUserContext) { - - bool bResult = true; - - if (!p_bUserContext) { - - if ((m_pUDPContext) && // UDPContext available AND - (m_pUDPContext->next())) { // has content - - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _update: Calling _parseMessage\n"));); - bResult = _parseMessage(); - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parsePacket %s\n"), (bResult ? "succeeded" : "FAILED"));); - } - } - else { +/* + LEAmDNS_Control.cpp + + License (MIT license): + 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. + +*/ + +#include +#include +#include +#include +#include +#include + +/* + ESP8266mDNS Control.cpp +*/ + +extern "C" { +#include "user_interface.h" +} + +#include "LEAmDNS_lwIPdefs.h" +#include "LEAmDNS_Priv.h" + +namespace esp8266 +{ +/* + LEAmDNS +*/ +namespace MDNSImplementation +{ + +/** + CONTROL +*/ + + +/** + MAINTENANCE +*/ + +/* + MDNSResponder::_process + + Run the MDNS process. + Is called, every time the UDPContext receives data AND + should be called in every 'loop' by calling 'MDNS::update()'. + +*/ +bool MDNSResponder::_process(bool p_bUserContext) +{ + + bool bResult = true; + + if (!p_bUserContext) + { + + if ((m_pUDPContext) && // UDPContext available AND + (m_pUDPContext->next())) // has content + { + + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _update: Calling _parseMessage\n"));); + bResult = _parseMessage(); + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parsePacket %s\n"), (bResult ? "succeeded" : "FAILED"));); + } + } + else + { bResult = ((WiFi.isConnected() || // Either station is connected - WiFi.softAPgetStationNum()>0) && // Or AP has stations connected + WiFi.softAPgetStationNum() > 0) && // Or AP has stations connected (_updateProbeStatus()) && // Probing (_checkServiceQueryCache())); // Service query cache check - } - return bResult; -} - -/* - * MDNSResponder::_restart - */ -bool MDNSResponder::_restart(void) { - - return ((_resetProbeStatus(true)) && // Stop and restart probing - (_allocUDPContext())); // Restart UDP -} - - -/** - * RECEIVING - */ - -/* - * MDNSResponder::_parseMessage - */ -bool MDNSResponder::_parseMessage(void) { - DEBUG_EX_INFO( - unsigned long ulStartTime = millis(); - unsigned uStartMemory = ESP.getFreeHeap(); - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage (Time: %lu ms, heap: %u bytes, from %s(%u), to %s(%u))\n"), ulStartTime, uStartMemory, - IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), m_pUDPContext->getRemotePort(), - IPAddress(m_pUDPContext->getDestAddress()).toString().c_str(), m_pUDPContext->getLocalPort()); - ); - //DEBUG_EX_INFO(_udpDump();); - - bool bResult = false; - - stcMDNS_MsgHeader header; - if (_readMDNSMsgHeader(header)) { - if (0 == header.m_4bOpcode) { // A standard query - if (header.m_1bQR) { // Received a response -> answers to a query - //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Reading answers: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount);); - bResult = _parseResponse(header); - } - else { // Received a query (Questions) - //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Reading query: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount);); - bResult = _parseQuery(header); - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Received UNEXPECTED opcode:%u. Ignoring message!\n"), header.m_4bOpcode);); - m_pUDPContext->flush(); - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: FAILED to read header\n"));); - m_pUDPContext->flush(); - } - DEBUG_EX_INFO( - unsigned uFreeHeap = ESP.getFreeHeap(); - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Done (%s after %lu ms, ate %i bytes, remaining %u)\n\n"), (bResult ? "Succeeded" : "FAILED"), (millis() - ulStartTime), (uStartMemory - uFreeHeap), uFreeHeap); - ); - return bResult; -} - -/* - * MDNSResponder::_parseQuery - * - * Queries are of interest in two cases: - * 1. allow for tiebreaking while probing in the case of a race condition between two instances probing for - * the same name at the same time - * 2. provide answers to questions for our host domain or any presented service - * - * When reading the questions, a set of (planned) responses is created, eg. a reverse PTR question for the host domain - * gets an A (IP address) response, a PTR question for the _services._dns-sd domain gets a PTR (type) response for any - * registered service, ... - * - * As any mDNS responder should be able to handle 'legacy' queries (from DNS clients), this case is handled here also. - * Legacy queries have got only one (unicast) question and are directed to the local DNS port (not the multicast port). - * - * 1. - */ -bool MDNSResponder::_parseQuery(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) { - - bool bResult = true; - - stcMDNSSendParameter sendParameter; - uint8_t u8HostOrServiceReplies = 0; - for (uint16_t qd=0; ((bResult) && (qdm_pNext) { - // Define service replies, BUT only answer queries after probing is done - uint8_t u8ReplyMaskForQuestion = (((m_bPassivModeEnabled) || - (ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus)) - ? _replyMaskForService(questionRR.m_Header, *pService, 0) - : 0); - u8HostOrServiceReplies |= (pService->m_u8ReplyMask |= u8ReplyMaskForQuestion); - DEBUG_EX_INFO(if (u8ReplyMaskForQuestion) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service reply needed for (%s.%s.%s): 0x%X (%s)\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol, u8ReplyMaskForQuestion, IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str()); } ); - - // Check tiebreak need for service domain - if (ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) { - bool bFullNameMatch = false; - if ((_replyMaskForService(questionRR.m_Header, *pService, &bFullNameMatch)) && - (bFullNameMatch)) { - // We're in 'probing' state and someone is asking for this service domain: this might be - // a race-condition: Two services with the same domain names try simutanously to probe their domains - // See: RFC 6762, 8.2 (Tiebraking) - // However, we're using a max. reduced approach for tiebreaking here: The 'higher' SRV host wins! - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Possible race-condition for service domain %s.%s.%s detected while probing.\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - - pService->m_ProbeInformation.m_bTiebreakNeeded = true; - } - } - } - - // Handle unicast and legacy specialities - // If only one question asks for unicast reply, the whole reply packet is send unicast - if (((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) || // Unicast (maybe legacy) query OR - (questionRR.m_bUnicast)) && // Expressivly unicast query - (!sendParameter.m_bUnicast)) { - - sendParameter.m_bUnicast = true; - sendParameter.m_bCacheFlush = false; - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Unicast response for %s!\n"), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); - - if ((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) && // Unicast (maybe legacy) query AND - (1 == p_MsgHeader.m_u16QDCount) && // Only one question AND - ((sendParameter.m_u8HostReplyMask) || // Host replies OR - (u8HostOrServiceReplies))) { // Host or service replies available - // We're a match for this legacy query, BUT - // make sure, that the query comes from a local host - ip_info IPInfo_Local; - ip_info IPInfo_Remote; - if (((IPInfo_Remote.ip.addr = m_pUDPContext->getRemoteAddress())) && - (((wifi_get_ip_info(SOFTAP_IF, &IPInfo_Local)) && - (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, &IPInfo_Local.netmask))) || // Remote IP in SOFTAP's subnet OR - ((wifi_get_ip_info(STATION_IF, &IPInfo_Local)) && - (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, &IPInfo_Local.netmask))))) { // Remote IP in STATION's subnet - - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy query from local host %s!\n"), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); - - sendParameter.m_u16ID = p_MsgHeader.m_u16ID; - sendParameter.m_bLegacyQuery = true; - sendParameter.m_pQuestions = new stcMDNS_RRQuestion; - if ((bResult = (0 != sendParameter.m_pQuestions))) { - sendParameter.m_pQuestions->m_Header.m_Domain = questionRR.m_Header.m_Domain; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = questionRR.m_Header.m_Attributes.m_u16Type; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = questionRR.m_Header.m_Attributes.m_u16Class; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to add legacy question!\n"));); - } - } - else { - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy query from NON-LOCAL host!\n"));); - bResult = false; - } - } - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to read question!\n"));); - } - } // for questions - - //DEBUG_EX_INFO(if (u8HostOrServiceReplies) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Reply needed: %u (%s: %s->%s)\n"), u8HostOrServiceReplies, clsTimeSyncer::timestr(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), IPAddress(m_pUDPContext->getDestAddress()).toString().c_str()); } ); - - // Handle known answers - uint32_t u32Answers = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount); - DEBUG_EX_INFO(if ((u8HostOrServiceReplies) && (u32Answers)) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Known answers(%u):\n"), u32Answers); } ); - - for (uint32_t an=0; ((bResult) && (anm_Header.m_Attributes.m_u16Type) && // No ANY type answer - (DNS_RRCLASS_ANY != pKnownRRAnswer->m_Header.m_Attributes.m_u16Class)) { // No ANY class answer - - // Find match between planned answer (sendParameter.m_u8HostReplyMask) and this 'known answer' - uint8_t u8HostMatchMask = (sendParameter.m_u8HostReplyMask & _replyMaskForHost(pKnownRRAnswer->m_Header)); - if ((u8HostMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND - ((MDNS_HOST_TTL / 2) <= pKnownRRAnswer->m_u32TTL)) { // The TTL of the known answer is longer than half of the new host TTL (120s) - - // Compare contents - if (AnswerType_PTR == pKnownRRAnswer->answerType()) { - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain == hostDomain)) { - // Host domain match -#ifdef MDNS_IP4_SUPPORT - if (u8HostMatchMask & ContentFlag_PTR_IP4) { - // IP4 PTR was asked for, but is already known -> skipping - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP4 PTR already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP4; - } -#endif -#ifdef MDNS_IP6_SUPPORT - if (u8HostMatchMask & ContentFlag_PTR_IP6) { - // IP6 PTR was asked for, but is already known -> skipping - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP6 PTR already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP6; - } -#endif - } - } - else if (u8HostMatchMask & ContentFlag_A) { - // IP4 address was asked for -#ifdef MDNS_IP4_SUPPORT - if ((AnswerType_A == pKnownRRAnswer->answerType()) && - (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress == _getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE))) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP4 address already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_A; - } // else: RData NOT IP4 length !! -#endif - } - else if (u8HostMatchMask & ContentFlag_AAAA) { - // IP6 address was asked for -#ifdef MDNS_IP6_SUPPORT - if ((AnswerType_AAAA == pAnswerRR->answerType()) && - (((stcMDNS_RRAnswerAAAA*)pAnswerRR)->m_IPAddress == _getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE))) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP6 address already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_AAAA; - } // else: RData NOT IP6 length !! -#endif - } - } // Host match /*and TTL*/ - - // - // Check host tiebreak possibility - if (m_HostProbeInformation.m_bTiebreakNeeded) { - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (pKnownRRAnswer->m_Header.m_Domain == hostDomain)) { - // Host domain match -#ifdef MDNS_IP4_SUPPORT - if (AnswerType_A == pKnownRRAnswer->answerType()) { - IPAddress localIPAddress(_getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE)); - if (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress == localIPAddress) { - // SAME IP address -> We've received an old message from ourselfs (same IP) - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (was an old message)!\n"));); - m_HostProbeInformation.m_bTiebreakNeeded = false; - } - else { - if ((uint32_t)(((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress) > (uint32_t)localIPAddress) { // The OTHER IP is 'higher' -> LOST - // LOST tiebreak - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) LOST (lower)!\n"));); - _cancelProbingForHost(); - m_HostProbeInformation.m_bTiebreakNeeded = false; - } - else { // WON tiebreak - //TiebreakState = TiebreakState_Won; // We received an 'old' message from ourselfs -> Just ignore - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (higher IP)!\n"));); - m_HostProbeInformation.m_bTiebreakNeeded = false; - } - } - } -#endif -#ifdef MDNS_IP6_SUPPORT - if (AnswerType_AAAA == pAnswerRR->answerType()) { - // TODO - } -#endif - } - } // Host tiebreak possibility - - // Check service answers - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - - uint8_t u8ServiceMatchMask = (pService->m_u8ReplyMask & _replyMaskForService(pKnownRRAnswer->m_Header, *pService)); - - if ((u8ServiceMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND - ((MDNS_SERVICE_TTL / 2) <= pKnownRRAnswer->m_u32TTL)) { // The TTL of the known answer is longer than half of the new service TTL (4500s) - - if (AnswerType_PTR == pKnownRRAnswer->answerType()) { - stcMDNS_RRDomain serviceDomain; - if ((u8ServiceMatchMask & ContentFlag_PTR_TYPE) && - (_buildDomainForService(*pService, false, serviceDomain)) && - (serviceDomain == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service type PTR already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_PTR_TYPE; - } - if ((u8ServiceMatchMask & ContentFlag_PTR_NAME) && - (_buildDomainForService(*pService, true, serviceDomain)) && - (serviceDomain == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service name PTR already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_PTR_NAME; - } - } - else if (u8ServiceMatchMask & ContentFlag_SRV) { - DEBUG_EX_ERR(if (AnswerType_SRV != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: ERROR! INVALID answer type (SRV)!\n"));); - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (hostDomain == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) { // Host domain match - - if ((MDNS_SRV_PRIORITY == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Priority) && - (MDNS_SRV_WEIGHT == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Weight) && - (pService->m_u16Port == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Port)) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service SRV answer already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_SRV; - } // else: Small differences -> send update message - } - } - else if (u8ServiceMatchMask & ContentFlag_TXT) { - DEBUG_EX_ERR(if (AnswerType_TXT != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: ERROR! INVALID answer type (TXT)!\n"));); - _collectServiceTxts(*pService); - if (pService->m_Txts == ((stcMDNS_RRAnswerTXT*)pKnownRRAnswer)->m_Txts) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service TXT answer already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_TXT; - } - _releaseTempServiceTxts(*pService); - } - } // Service match and enough TTL - - // - // Check service tiebreak possibility - if (pService->m_ProbeInformation.m_bTiebreakNeeded) { - stcMDNS_RRDomain serviceDomain; - if ((_buildDomainForService(*pService, true, serviceDomain)) && - (pKnownRRAnswer->m_Header.m_Domain == serviceDomain)) { - // Service domain match - if (AnswerType_SRV == pKnownRRAnswer->answerType()) { - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (hostDomain == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) { // Host domain match - - // We've received an old message from ourselfs (same SRV) - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won (was an old message)!\n"));); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; - } - else { - if (((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain > hostDomain) { // The OTHER domain is 'higher' -> LOST - // LOST tiebreak - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) LOST (lower)!\n"));); - _cancelProbingForService(*pService); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; - } - else { // WON tiebreak - //TiebreakState = TiebreakState_Won; // We received an 'old' message from ourselfs -> Just ignore - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won (higher)!\n"));); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; - } - } - } - } - } // service tiebreak possibility - } // for services - } // ANY answers - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to read known answer!\n"));); - } - - if (pKnownRRAnswer) { - delete pKnownRRAnswer; - pKnownRRAnswer = 0; - } - } // for answers - - if (bResult) { - // Check, if a reply is needed - uint8_t u8ReplyNeeded = sendParameter.m_u8HostReplyMask; - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - u8ReplyNeeded |= pService->m_u8ReplyMask; - } - - if (u8ReplyNeeded) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Sending answer(0x%X)...\n"), u8ReplyNeeded);); - - sendParameter.m_bResponse = true; - sendParameter.m_bAuthorative = true; - - bResult = _sendMDNSMessage(sendParameter); - } - DEBUG_EX_INFO( - else { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: No reply needed\n")); - } - ); - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Something FAILED!\n"));); - m_pUDPContext->flush(); - } - - // - // Check and reset tiebreak-states - if (m_HostProbeInformation.m_bTiebreakNeeded) { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for host domain!\n"));); - m_HostProbeInformation.m_bTiebreakNeeded = false; - } - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - if (pService->m_ProbeInformation.m_bTiebreakNeeded) { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for service domain (%s.%s.%s)\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_parseResponse - * - * Responses are of interest in two cases: - * 1. find domain name conflicts while probing - * 2. get answers to service queries - * - * In both cases any included questions are ignored - * - * 1. If any answer has a domain name similar to one of the domain names we're planning to use (and are probing for), - * then we've got a 'probing conflict'. The conflict has to be solved on our side of the conflict (eg. by - * setting a new hostname and restart probing). The callback 'm_fnProbeResultCallback' is called with - * 'p_bProbeResult=false' in this case. - * - * 2. Service queries like '_http._tcp.local' will (if available) produce PTR, SRV, TXT and A/AAAA answers. - * All stored answers are pivoted by the service instance name (from the PTR record). Other answer parts, - * like host domain or IP address are than attached to this element. - * Any answer part carries a TTL, this is also stored (incl. the reception time); if the TTL is '0' the - * answer (part) is withdrawn by the sender and should be removed from any cache. RFC 6762, 10.1 proposes to - * set the caches TTL-value to 1 second in such a case and to delete the item only, if no update has - * has taken place in this second. - * Answer parts may arrive in 'unsorted' order, so they are grouped into three levels: - * Level 1: PRT - names the service instance (and is used as pivot), voids all other parts if is withdrawn or outdates - * Level 2: SRV - links the instance name to a host domain and port, voids A/AAAA parts if is withdrawn or outdates - * TXT - links the instance name to services TXTs - * Level 3: A/AAAA - links the host domain to an IP address - */ -bool MDNSResponder::_parseResponse(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse\n"));); - //DEBUG_EX_INFO(_udpDump();); - - bool bResult = false; - - // A response should be the result of a query or a probe - if ((_hasServiceQueriesWaitingForAnswers()) || // Waiting for query answers OR - (_hasProbesWaitingForAnswers())) { // Probe responses - - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received a response\n")); - //_udpDump(); - ); - - bResult = true; - // - // Ignore questions here - stcMDNS_RRQuestion dummyRRQ; - for (uint16_t qd=0; ((bResult) && (qdm_pNext = pCollectedRRAnswers; - pCollectedRRAnswers = pRRAnswer; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED to read answer!\n"));); - if (pRRAnswer) { - delete pRRAnswer; - pRRAnswer = 0; - } - bResult = false; - } - } // for answers - - // - // Process answers - if (bResult) { - bResult = ((!pCollectedRRAnswers) || - (_processAnswers(pCollectedRRAnswers))); - } - else { // Some failure while reading answers - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED to read answers!\n"));); - m_pUDPContext->flush(); - } - - // Delete collected answers - while (pCollectedRRAnswers) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: DELETING answer!\n"));); - stcMDNS_RRAnswer* pNextAnswer = pCollectedRRAnswers->m_pNext; - delete pCollectedRRAnswers; - pCollectedRRAnswers = pNextAnswer; - } - } - else { // Received an unexpected response -> ignore - /*DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received an unexpected response... ignoring!\nDUMP:\n")); - bool bDumpResult = true; - for (uint16_t qd=0; ((bDumpResult) && (qdflush(); - bResult = true; - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_processAnswers - * Host: - * A (0x01): eg. esp8266.local A OP TTL 123.456.789.012 - * AAAA (01Cx): eg. esp8266.local AAAA OP TTL 1234:5678::90 - * 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 - * Service: - * 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 - * 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 - * - */ -bool MDNSResponder::_processAnswers(const MDNSResponder::stcMDNS_RRAnswer* p_pAnswers) { - - bool bResult = false; - - if (p_pAnswers) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Processing answers...\n"));); - bResult = true; - - // Answers may arrive in an unexpected order. So we loop our answers as long, as we - // can connect new information to service queries - bool bFoundNewKeyAnswer; - do { - bFoundNewKeyAnswer = false; - - const stcMDNS_RRAnswer* pRRAnswer = p_pAnswers; - while ((pRRAnswer) && - (bResult)) { - // 1. level answer (PTR) - if (AnswerType_PTR == pRRAnswer->answerType()) { - // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local - bResult = _processPTRAnswer((stcMDNS_RRAnswerPTR*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new SRV or TXT answers to be linked to queries - } - // 2. level answers - // SRV -> host domain and port - else if (AnswerType_SRV == pRRAnswer->answerType()) { - // eg. MyESP_http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local - bResult = _processSRVAnswer((stcMDNS_RRAnswerSRV*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new A/AAAA answers to be linked to queries - } - // TXT -> Txts - else if (AnswerType_TXT == pRRAnswer->answerType()) { - // eg. MyESP_http._tcp.local TXT xxxx xx c#=1 - bResult = _processTXTAnswer((stcMDNS_RRAnswerTXT*)pRRAnswer); - } - // 3. level answers -#ifdef MDNS_IP4_SUPPORT - // A -> IP4Address - else if (AnswerType_A == pRRAnswer->answerType()) { - // eg. esp8266.local A xxxx xx 192.168.2.120 - bResult = _processAAnswer((stcMDNS_RRAnswerA*)pRRAnswer); - } -#endif -#ifdef MDNS_IP6_SUPPORT - // AAAA -> IP6Address - else if (AnswerType_AAAA == pRRAnswer->answerType()) { - // eg. esp8266.local AAAA xxxx xx 09cf::0c - bResult = _processAAAAAnswer((stcMDNS_RRAnswerAAAA*)pRRAnswer); - } -#endif - - // Finally check for probing conflicts - // Host domain - if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && - ((AnswerType_A == pRRAnswer->answerType()) || - (AnswerType_AAAA == pRRAnswer->answerType()))) { - - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (pRRAnswer->m_Header.m_Domain == hostDomain)) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found with: %s.local\n"), m_pcHostname);); - _cancelProbingForHost(); - } - } - // Service domains - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && - ((AnswerType_TXT == pRRAnswer->answerType()) || - (AnswerType_SRV == pRRAnswer->answerType()))) { - - stcMDNS_RRDomain serviceDomain; - if ((_buildDomainForService(*pService, true, serviceDomain)) && - (pRRAnswer->m_Header.m_Domain == serviceDomain)) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found with: %s.%s.%s\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - _cancelProbingForService(*pService); - } - } - } - - pRRAnswer = pRRAnswer->m_pNext; // Next collected answer - } // while (answers) - } while ((bFoundNewKeyAnswer) && - (bResult)); - } // else: No answers provided - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_processPTRAnswer - */ -bool MDNSResponder::_processPTRAnswer(const MDNSResponder::stcMDNS_RRAnswerPTR* p_pPTRAnswer, - bool& p_rbFoundNewKeyAnswer) { - - bool bResult = false; - - if ((bResult = (0 != p_pPTRAnswer))) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: Processing PTR answers...\n"));); - // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local - // Check pending service queries for eg. '_http._tcp' - - stcMDNSServiceQuery* pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, 0); - while (pServiceQuery) { - if (pServiceQuery->m_bAwaitingAnswers) { - // Find answer for service domain (eg. MyESP._http._tcp.local) - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pPTRAnswer->m_PTRDomain); - if (pSQAnswer) { // existing answer - if (p_pPTRAnswer->m_u32TTL) { // Received update message - pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); // Update TTL tag - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: Updated TTL(%lu) for "), p_pPTRAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - ); - } - else { // received goodbye-message - pSQAnswer->m_TTLServiceDomain.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - ); - } - } - else if ((p_pPTRAnswer->m_u32TTL) && // Not just a goodbye-message - ((pSQAnswer = new stcMDNSServiceQuery::stcAnswer))) { // Not yet included -> add answer - pSQAnswer->m_ServiceDomain = p_pPTRAnswer->m_PTRDomain; - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_ServiceDomain; - pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); - pSQAnswer->releaseServiceDomain(); - - bResult = pServiceQuery->addAnswer(pSQAnswer); - p_rbFoundNewKeyAnswer = true; - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this,(hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_ServiceDomain), true); - } - } - } - pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, pServiceQuery); - } - } // else: No p_pPTRAnswer - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_processSRVAnswer - */ -bool MDNSResponder::_processSRVAnswer(const MDNSResponder::stcMDNS_RRAnswerSRV* p_pSRVAnswer, - bool& p_rbFoundNewKeyAnswer) { - - bool bResult = false; - - if ((bResult = (0 != p_pSRVAnswer))) { - // eg. MyESP._http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pSRVAnswer->m_Header.m_Domain); - if (pSQAnswer) { // Answer for this service domain (eg. MyESP._http._tcp.local) available - if (p_pSRVAnswer->m_u32TTL) { // First or update message (TTL != 0) - pSQAnswer->m_TTLHostDomainAndPort.set(p_pSRVAnswer->m_u32TTL); // Update TTL tag - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: Updated TTL(%lu) for "), p_pSRVAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); - ); - // Host domain & Port - if ((pSQAnswer->m_HostDomain != p_pSRVAnswer->m_SRVDomain) || - (pSQAnswer->m_u16Port != p_pSRVAnswer->m_u16Port)) { - - pSQAnswer->m_HostDomain = p_pSRVAnswer->m_SRVDomain; - pSQAnswer->releaseHostDomain(); - pSQAnswer->m_u16Port = p_pSRVAnswer->m_u16Port; - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_HostDomainAndPort; - - p_rbFoundNewKeyAnswer = true; - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_HostDomainAndPort), true); - } - } - } - else { // Goodby message - pSQAnswer->m_TTLHostDomainAndPort.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); - ); - } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pSRVAnswer - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_processTXTAnswer - */ -bool MDNSResponder::_processTXTAnswer(const MDNSResponder::stcMDNS_RRAnswerTXT* p_pTXTAnswer) { - - bool bResult = false; - - if ((bResult = (0 != p_pTXTAnswer))) { - // eg. MyESP._http._tcp.local TXT xxxx xx c#=1 - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pTXTAnswer->m_Header.m_Domain); - if (pSQAnswer) { // Answer for this service domain (eg. MyESP._http._tcp.local) available - if (p_pTXTAnswer->m_u32TTL) { // First or update message - pSQAnswer->m_TTLTxts.set(p_pTXTAnswer->m_u32TTL); // Update TTL tag - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: Updated TTL(%lu) for "), p_pTXTAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); - ); - if (!pSQAnswer->m_Txts.compare(p_pTXTAnswer->m_Txts)) { - pSQAnswer->m_Txts = p_pTXTAnswer->m_Txts; - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_Txts; - pSQAnswer->releaseTxts(); - - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo , static_cast(ServiceQueryAnswerType_Txts), true); - } - } - } - else { // Goodby message - pSQAnswer->m_TTLTxts.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); - ); - } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pTXTAnswer - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: FAILED!\n")); }); - return bResult; -} - -#ifdef MDNS_IP4_SUPPORT - /* - * MDNSResponder::_processAAnswer - */ - bool MDNSResponder::_processAAnswer(const MDNSResponder::stcMDNS_RRAnswerA* p_pAAnswer) { - - bool bResult = false; - - if ((bResult = (0 != p_pAAnswer))) { - // eg. esp8266.local A xxxx xx 192.168.2.120 - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForHostDomain(p_pAAnswer->m_Header.m_Domain); - if (pSQAnswer) { // Answer for this host domain (eg. esp8266.local) available - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = pSQAnswer->findIP4Address(p_pAAnswer->m_IPAddress); - if (pIP4Address) { - // Already known IP4 address - if (p_pAAnswer->m_u32TTL) { // Valid TTL -> Update answers TTL - pIP4Address->m_TTL.set(p_pAAnswer->m_u32TTL); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%lu) for "), p_pAAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4Address (%s)\n"), pIP4Address->m_IPAddress.toString().c_str()); - ); - } - else { // 'Goodbye' message for known IP4 address - pIP4Address->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4 address (%s)\n"), pIP4Address->m_IPAddress.toString().c_str()); - ); - } - } - else { - // Until now unknown IP4 address -> Add (if the message isn't just a 'Goodbye' note) - if (p_pAAnswer->m_u32TTL) { // NOT just a 'Goodbye' message - pIP4Address = new stcMDNSServiceQuery::stcAnswer::stcIP4Address(p_pAAnswer->m_IPAddress, p_pAAnswer->m_u32TTL); - if ((pIP4Address) && - (pSQAnswer->addIP4Address(pIP4Address))) { - - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP4Address; - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo (*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_IP4Address), true); - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP4 address (%s)!\n"), p_pAAnswer->m_IPAddress.toString().c_str());); - } - } - } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pAAnswer - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED!\n")); }); - return bResult; - } -#endif - -#ifdef MDNS_IP6_SUPPORT - /* - * MDNSResponder::_processAAAAAnswer - */ - bool MDNSResponder::_processAAAAAnswer(const MDNSResponder::stcMDNS_RRAnswerAAAA* p_pAAAAAnswer) { - - bool bResult = false; - - if ((bResult = (0 != p_pAAAAAnswer))) { - // eg. esp8266.local AAAA xxxx xx 0bf3::0c - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForHostDomain(p_pAAAAAnswer->m_Header.m_Domain); - if (pSQAnswer) { // Answer for this host domain (eg. esp8266.local) available - stcIP6Address* pIP6Address = pSQAnswer->findIP6Address(p_pAAAAAnswer->m_IPAddress); - if (pIP6Address) { - // Already known IP6 address - if (p_pAAAAAnswer->m_u32TTL) { // Valid TTL -> Update answers TTL - pIP6Address->m_TTL.set(p_pAAAAAnswer->m_u32TTL); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%lu) for "), p_pAAAAAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), pIP6Address->m_IPAddress.toString().c_str()); - ); - } - else { // 'Goodbye' message for known IP6 address - pIP6Address->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), pIP6Address->m_IPAddress.toString().c_str()); - ); - } - } - else { - // Until now unknown IP6 address -> Add (if the message isn't just a 'Goodbye' note) - if (p_pAAAAAnswer->m_u32TTL) { // NOT just a 'Goodbye' message - pIP6Address = new stcIP6Address(p_pAAAAAnswer->m_IPAddress, p_pAAAAAnswer->m_u32TTL); - if ((pIP6Address) && - (pSQAnswer->addIP6Address(pIP6Address))) { - - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP6Address; - - if (pServiceQuery->m_fnCallback) { - pServiceQuery->m_fnCallback(this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer), ServiceQueryAnswerType_IP6Address, true, pServiceQuery->m_pUserdata); - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP6 address (%s)!\n"), p_pAAAAAnswer->m_IPAddress.toString().c_str());); - } - } - } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pAAAAAnswer - - return bResult; - } -#endif - - -/* - * PROBING - */ - -/* - * MDNSResponder::_updateProbeStatus - * - * Manages the (outgoing) probing process. - * - If probing has not been started yet (ProbingStatus_NotStarted), the initial delay (see RFC 6762) is determined and - * the process is started - * - After timeout (of initial or subsequential delay) a probe message is send out for three times. If the message has - * already been sent out three times, the probing has been successful and is finished. - * - * Conflict management is handled in '_parseResponse ff.' - * Tiebraking is handled in 'parseQuery ff.' - */ -bool MDNSResponder::_updateProbeStatus(void) { - - bool bResult = true; - - // - // Probe host domain - if ((ProbingStatus_ReadyToStart == m_HostProbeInformation.m_ProbingStatus) && // Ready to get started AND - //TODO: Fix the following to allow Ethernet shield or other interfaces - (_getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE) != IPAddress())) { // Has IP address - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Starting host probing...\n"));); - - // First probe delay SHOULD be random 0-250 ms - m_HostProbeInformation.m_Timeout.reset(rand() % MDNS_PROBE_DELAY); - m_HostProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; - } - else if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing AND - (m_HostProbeInformation.m_Timeout.expired())) { // Time for next probe - - if (MDNS_PROBE_COUNT > m_HostProbeInformation.m_u8SentCount) { // Send next probe - if ((bResult = _sendHostProbe())) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent host probe\n\n"));); - m_HostProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); - ++m_HostProbeInformation.m_u8SentCount; - } - } - else { // Probing finished - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host probing.\n"));); - m_HostProbeInformation.m_ProbingStatus = ProbingStatus_Done; - m_HostProbeInformation.m_Timeout.resetToNeverExpires(); - if (m_HostProbeInformation.m_fnHostProbeResultCallback) { - m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, true); - } - - // Prepare to announce host - m_HostProbeInformation.m_u8SentCount = 0; - m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Prepared host announcing.\n\n"));); - } - } // else: Probing already finished OR waiting for next time slot - else if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) && - (m_HostProbeInformation.m_Timeout.expired())) { - - if ((bResult = _announce(true, false))) { // Don't announce services here - ++m_HostProbeInformation.m_u8SentCount; - - if (MDNS_ANNOUNCE_COUNT > m_HostProbeInformation.m_u8SentCount) { - m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing host (%lu).\n\n"), m_HostProbeInformation.m_u8SentCount);); - } - else { - m_HostProbeInformation.m_Timeout.resetToNeverExpires(); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host announcing.\n\n"));); - } - } - } - - // - // Probe services - for (stcMDNSService* pService=m_pServices; ((bResult) && (pService)); pService=pService->m_pNext) { - if (ProbingStatus_ReadyToStart == pService->m_ProbeInformation.m_ProbingStatus) { // Ready to get started - - pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); // More or equal than first probe for host domain - pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; - } - else if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing AND - (pService->m_ProbeInformation.m_Timeout.expired())) { // Time for next probe - - if (MDNS_PROBE_COUNT > pService->m_ProbeInformation.m_u8SentCount) { // Send next probe - if ((bResult = _sendServiceProbe(*pService))) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent service probe (%u)\n\n"), (pService->m_ProbeInformation.m_u8SentCount + 1));); - pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); - ++pService->m_ProbeInformation.m_u8SentCount; - } - } - else { // Probing finished - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service probing %s.%s.%s\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_Done; - pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); - if (pService->m_ProbeInformation.m_fnServiceProbeResultCallback) { - pService->m_ProbeInformation.m_fnServiceProbeResultCallback(pService->m_pcName, pService, true); - } - // Prepare to announce service - pService->m_ProbeInformation.m_u8SentCount = 0; - pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Prepared service announcing.\n\n"));); - } - } // else: Probing already finished OR waiting for next time slot - else if ((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) && - (pService->m_ProbeInformation.m_Timeout.expired())) { - - if ((bResult = _announceService(*pService))) { // Announce service - ++pService->m_ProbeInformation.m_u8SentCount; - - if (MDNS_ANNOUNCE_COUNT > pService->m_ProbeInformation.m_u8SentCount) { - pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing service %s.%s.%s (%lu)\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_ProbeInformation.m_u8SentCount);); - } - else { - pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service announcing for %s.%s.%s\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - } - } - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: FAILED!\n\n")); }); - return bResult; -} - -/* - * MDNSResponder::_resetProbeStatus - * - * Resets the probe status. - * If 'p_bRestart' is set, the status is set to ProbingStatus_NotStarted. Consequently, - * when running 'updateProbeStatus' (which is done in every '_update' loop), the probing - * process is restarted. - */ -bool MDNSResponder::_resetProbeStatus(bool p_bRestart /*= true*/) { - - m_HostProbeInformation.clear(false); - m_HostProbeInformation.m_ProbingStatus = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); - - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - pService->m_ProbeInformation.clear(false); - pService->m_ProbeInformation.m_ProbingStatus = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); - } - return true; -} - -/* - * MDNSResponder::_hasProbesWaitingForAnswers - */ -bool MDNSResponder::_hasProbesWaitingForAnswers(void) const { - - bool bResult = ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing - (0 < m_HostProbeInformation.m_u8SentCount)); // And really probing - - for (stcMDNSService* pService=m_pServices; ((!bResult) && (pService)); pService=pService->m_pNext) { - bResult = ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing - (0 < pService->m_ProbeInformation.m_u8SentCount)); // And really probing - } - return bResult; -} - -/* - * MDNSResponder::_sendHostProbe - * - * Asks (probes) in the local network for the planned host domain - * - (eg. esp8266.local) - * - * To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in - * the 'knwon answers' section of the query. - * Host domain: - * - A/AAAA (eg. esp8266.esp -> 192.168.2.120) - */ -bool MDNSResponder::_sendHostProbe(void) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe (%s, %lu)\n"), m_pcHostname, millis());); - - bool bResult = true; - - // Requests for host domain - stcMDNSSendParameter sendParameter; - sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 - - sendParameter.m_pQuestions = new stcMDNS_RRQuestion; - if (((bResult = (0 != sendParameter.m_pQuestions))) && - ((bResult = _buildDomainForHost(m_pcHostname, sendParameter.m_pQuestions->m_Header.m_Domain)))) { - - //sendParameter.m_pQuestions->m_bUnicast = true; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet - - // Add known answers -#ifdef MDNS_IP4_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_A; // Add A answer -#endif -#ifdef MDNS_IP6_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // Add AAAA answer -#endif - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED to create host question!\n"));); - if (sendParameter.m_pQuestions) { - delete sendParameter.m_pQuestions; - sendParameter.m_pQuestions = 0; - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED!\n")); }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} - -/* - * MDNSResponder::_sendServiceProbe - * - * Asks (probes) in the local network for the planned service instance domain - * - (eg. MyESP._http._tcp.local). - * - * To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in - * the 'knwon answers' section of the query. - * Service domain: - * - SRV (eg. MyESP._http._tcp.local -> 5000 esp8266.local) - * - PTR NAME (eg. _http._tcp.local -> MyESP._http._tcp.local) (TODO: Check if needed, maybe TXT is better) - */ -bool MDNSResponder::_sendServiceProbe(stcMDNSService& p_rService) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe (%s.%s.%s, %lu)\n"), (p_rService.m_pcName ?: m_pcHostname), p_rService.m_pcService, p_rService.m_pcProtocol, millis());); - - bool bResult = true; - - // Requests for service instance domain - stcMDNSSendParameter sendParameter; - sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 - - sendParameter.m_pQuestions = new stcMDNS_RRQuestion; - if (((bResult = (0 != sendParameter.m_pQuestions))) && - ((bResult = _buildDomainForService(p_rService, true, sendParameter.m_pQuestions->m_Header.m_Domain)))) { - - sendParameter.m_pQuestions->m_bUnicast = true; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet - - // Add known answers - p_rService.m_u8ReplyMask = (ContentFlag_SRV | ContentFlag_PTR_NAME); // Add SRV and PTR NAME answers - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED to create service question!\n"));); - if (sendParameter.m_pQuestions) { - delete sendParameter.m_pQuestions; - sendParameter.m_pQuestions = 0; - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED!\n")); }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} - -/* - * MDNSResponder::_cancelProbingForHost - */ -bool MDNSResponder::_cancelProbingForHost(void) { - - bool bResult = false; - - m_HostProbeInformation.clear(false); - // Send host notification - if (m_HostProbeInformation.m_fnHostProbeResultCallback) { - m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, false); - - bResult = true; - } - - for (stcMDNSService* pService=m_pServices; ((!bResult) && (pService)); pService=pService->m_pNext) { - bResult = _cancelProbingForService(*pService); - } - return bResult; -} - -/* - * MDNSResponder::_cancelProbingForService - */ -bool MDNSResponder::_cancelProbingForService(stcMDNSService& p_rService) { - - bool bResult = false; - - p_rService.m_ProbeInformation.clear(false); - // Send notification - if (p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback) { - p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback(p_rService.m_pcName,&p_rService,false); - bResult = true; - } - return bResult; -} - - - -/** - * ANNOUNCING - */ - -/* - * MDNSResponder::_announce - * - * Announces the host domain: - * - A/AAAA (eg. esp8266.local -> 192.168.2.120) - * - PTR (eg. 192.168.2.120.in-addr.arpa -> esp8266.local) - * - * and all presented services: - * - PTR_TYPE (_services._dns-sd._udp.local -> _http._tcp.local) - * - PTR_NAME (eg. _http._tcp.local -> MyESP8266._http._tcp.local) - * - SRV (eg. MyESP8266._http._tcp.local -> 5000 esp8266.local) - * - TXT (eg. MyESP8266._http._tcp.local -> c#=1) - * - * Goodbye (Un-Announcing) for the host domain and all services is also handled here. - * Goodbye messages are created by setting the TTL for the answer to 0, this happens - * inside the '_writeXXXAnswer' procs via 'sendParameter.m_bUnannounce = true' - */ -bool MDNSResponder::_announce(bool p_bAnnounce, - bool p_bIncludeServices) { - - bool bResult = false; - - stcMDNSSendParameter sendParameter; - if (ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) { - - bResult = true; - - sendParameter.m_bResponse = true; // Announces are 'Unsolicited authorative responses' - sendParameter.m_bAuthorative = true; - sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers - - // Announce host - sendParameter.m_u8HostReplyMask = 0; - #ifdef MDNS_IP4_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_A; // A answer - sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP4; // PTR_IP4 answer - #endif - #ifdef MDNS_IP6_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // AAAA answer - sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP6; // PTR_IP6 answer - #endif - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: Announcing host %s (content 0x%X)\n"), m_pcHostname, sendParameter.m_u8HostReplyMask);); - - if (p_bIncludeServices) { - // Announce services (service type, name, SRV (location) and TXTs) - for (stcMDNSService* pService=m_pServices; ((bResult) && (pService)); pService=pService->m_pNext) { - if (ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) { - pService->m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: Announcing service %s.%s.%s (content %u)\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_u8ReplyMask);); - } - } - } - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: FAILED!\n")); }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} - -/* - * MDNSResponder::_announceService - */ -bool MDNSResponder::_announceService(stcMDNSService& p_rService, - bool p_bAnnounce /*= true*/) { - - bool bResult = false; - - stcMDNSSendParameter sendParameter; - if (ProbingStatus_Done == p_rService.m_ProbeInformation.m_ProbingStatus) { - - sendParameter.m_bResponse = true; // Announces are 'Unsolicited authorative responses' - sendParameter.m_bAuthorative = true; - sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers - - // DON'T announce host - sendParameter.m_u8HostReplyMask = 0; - - // Announce services (service type, name, SRV (location) and TXTs) - p_rService.m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: Announcing service %s.%s.%s (content 0x%X)\n"), (p_rService.m_pcName ?: m_pcHostname), p_rService.m_pcService, p_rService.m_pcProtocol, p_rService.m_u8ReplyMask);); - - bResult = true; - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: FAILED!\n")); }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} - - -/** - * SERVICE QUERY CACHE - */ - -/* - * MDNSResponder::_hasServiceQueriesWaitingForAnswers - */ -bool MDNSResponder::_hasServiceQueriesWaitingForAnswers(void) const { - - bool bOpenQueries = false; - - for (stcMDNSServiceQuery* pServiceQuery=m_pServiceQueries; pServiceQuery; pServiceQuery=pServiceQuery->m_pNext) { - if (pServiceQuery->m_bAwaitingAnswers) { - bOpenQueries = true; - break; - } - } - return bOpenQueries; -} - -/* - * MDNSResponder::_checkServiceQueryCache - * - * For any 'living' service query (m_bAwaitingAnswers == true) all available answers (their components) - * are checked for topicality based on the stored reception time and the answers TTL. - * When the components TTL is outlasted by more than 80%, a new question is generated, to get updated information. - * When no update arrived (in time), the component is removed from the answer (cache). - * - */ -bool MDNSResponder::_checkServiceQueryCache(void) { - - bool bResult = true; - - DEBUG_EX_INFO( - bool printedInfo = false; - ); - for (stcMDNSServiceQuery* pServiceQuery=m_pServiceQueries; ((bResult) && (pServiceQuery)); pServiceQuery=pServiceQuery->m_pNext) { - - // - // Resend dynamic service queries, if not already done often enough - if ((!pServiceQuery->m_bLegacyQuery) && - (MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) && - (pServiceQuery->m_ResendTimeout.expired())) { - - if ((bResult = _sendMDNSServiceQuery(*pServiceQuery))) { - ++pServiceQuery->m_u8SentCount; - pServiceQuery->m_ResendTimeout.reset((MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) - ? (MDNS_DYNAMIC_QUERY_RESEND_DELAY * (pServiceQuery->m_u8SentCount - 1)) - : esp8266::polledTimeout::oneShotMs::neverExpires); - } - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: %s to resend service query!"), (bResult ? "Succeeded" : "FAILED")); - printedInfo = true; - ); - } - - // - // Schedule updates for cached answers - if (pServiceQuery->m_bAwaitingAnswers) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->m_pAnswers; - while ((bResult) && - (pSQAnswer)) { - stcMDNSServiceQuery::stcAnswer* pNextSQAnswer = pSQAnswer->m_pNext; - - // 1. level answer - if ((bResult) && - (pSQAnswer->m_TTLServiceDomain.flagged())) { - - if (!pSQAnswer->m_TTLServiceDomain.finalTimeoutLevel()) { - - bResult = ((_sendMDNSServiceQuery(*pServiceQuery)) && - (pSQAnswer->m_TTLServiceDomain.restart())); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: PTR update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" %s\n"), (bResult ? "OK" : "FAILURE")); - printedInfo = true; - ); - } - else { - // Timed out! -> Delete - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_ServiceDomain), false); - } - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove PTR answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - printedInfo = true; - ); - - bResult = pServiceQuery->removeAnswer(pSQAnswer); - pSQAnswer = 0; - continue; // Don't use this answer anymore - } - } // ServiceDomain flagged - - // 2. level answers - // HostDomain & Port (from SRV) - if ((bResult) && - (pSQAnswer->m_TTLHostDomainAndPort.flagged())) { - - if (!pSQAnswer->m_TTLHostDomainAndPort.finalTimeoutLevel()) { - - bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_SRV)) && - (pSQAnswer->m_TTLHostDomainAndPort.restart())); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: SRV update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port %s\n"), (bResult ? "OK" : "FAILURE")); - printedInfo = true; - ); - } - else { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove SRV answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); - printedInfo = true; - ); - // Delete - pSQAnswer->m_HostDomain.clear(); - pSQAnswer->releaseHostDomain(); - pSQAnswer->m_u16Port = 0; - pSQAnswer->m_TTLHostDomainAndPort.set(0); - uint32_t u32ContentFlags = ServiceQueryAnswerType_HostDomainAndPort; - // As the host domain is the base for the IP4- and IP6Address, remove these too - #ifdef MDNS_IP4_SUPPORT - pSQAnswer->releaseIP4Addresses(); - u32ContentFlags |= ServiceQueryAnswerType_IP4Address; - #endif - #ifdef MDNS_IP6_SUPPORT - pSQAnswer->releaseIP6Addresses(); - u32ContentFlags |= ServiceQueryAnswerType_IP6Address; - #endif - - // Remove content flags for deleted answer parts - pSQAnswer->m_u32ContentFlags &= ~u32ContentFlags; - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo,static_cast(u32ContentFlags), false); - } - } - } // HostDomainAndPort flagged - - // Txts (from TXT) - if ((bResult) && - (pSQAnswer->m_TTLTxts.flagged())) { - - if (!pSQAnswer->m_TTLTxts.finalTimeoutLevel()) { - - bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_TXT)) && - (pSQAnswer->m_TTLTxts.restart())); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: TXT update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs %s\n"), (bResult ? "OK" : "FAILURE")); - printedInfo = true; - ); - } - else { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove TXT answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); - printedInfo = true; - ); - // Delete - pSQAnswer->m_Txts.clear(); - pSQAnswer->m_TTLTxts.set(0); - - // Remove content flags for deleted answer parts - pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_Txts; - - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_Txts), false); - } - } - } // TXTs flagged - - // 3. level answers -#ifdef MDNS_IP4_SUPPORT - // IP4Address (from A) - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = pSQAnswer->m_pIP4Addresses; - bool bAUpdateQuerySent = false; - while ((pIP4Address) && - (bResult)) { - - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pNextIP4Address = pIP4Address->m_pNext; // Get 'next' early, as 'current' may be deleted at the end... - - if (pIP4Address->m_TTL.flagged()) { - - if (!pIP4Address->m_TTL.finalTimeoutLevel()) { // Needs update - - if ((bAUpdateQuerySent) || - ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_A)))) { - - pIP4Address->m_TTL.restart(); - bAUpdateQuerySent = true; - - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: IP4 update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4 address (%s)\n"), (pIP4Address->m_IPAddress.toString().c_str())); - printedInfo = true; - ); - } - } - else { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove IP4 answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4 address\n")); - printedInfo = true; - ); - pSQAnswer->removeIP4Address(pIP4Address); - if (!pSQAnswer->m_pIP4Addresses) { // NO IP4 address left -> remove content flag - pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_IP4Address; - } - // Notify client - if (pServiceQuery->m_fnCallback) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_IP4Address), false); - } - } - } // IP4 flagged - - pIP4Address = pNextIP4Address; // Next - } // while -#endif -#ifdef MDNS_IP6_SUPPORT - // IP6Address (from AAAA) - stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = pSQAnswer->m_pIP6Addresses; - bool bAAAAUpdateQuerySent = false; - while ((pIP6Address) && - (bResult)) { - - stcMDNSServiceQuery::stcAnswer::stcIP6Address* pNextIP6Address = pIP6Address->m_pNext; // Get 'next' early, as 'current' may be deleted at the end... - - if (pIP6Address->m_TTL.flagged()) { - - if (!pIP6Address->m_TTL.finalTimeoutLevel()) { // Needs update - - if ((bAAAAUpdateQuerySent) || - ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_AAAA)))) { - - pIP6Address->m_TTL.restart(); - bAAAAUpdateQuerySent = true; - - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: IP6 update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), (pIP6Address->m_IPAddress.toString().c_str())); - printedInfo = true; - ); - } - } - else { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6Address\n")); - printedInfo = true; - ); - pSQAnswer->removeIP6Address(pIP6Address); - if (!pSQAnswer->m_pIP6Addresses) { // NO IP6 address left -> remove content flag - pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_IP6Address; - } - // Notify client - if (pServiceQuery->m_fnCallback) { - pServiceQuery->m_fnCallback(this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer), ServiceQueryAnswerType_IP6Address, false, pServiceQuery->m_pUserdata); - } - } - } // IP6 flagged - - pIP6Address = pNextIP6Address; // Next - } // while -#endif - pSQAnswer = pNextSQAnswer; - } - } - } - DEBUG_EX_INFO( - if (printedInfo) { - DEBUG_OUTPUT.printf_P(PSTR("\n")); - } - ); - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: FAILED!\n")); }); - return bResult; -} - - -/* - * MDNSResponder::_replyMaskForHost - * - * Determines the relavant host answers for the given question. - * - A question for the hostname (eg. esp8266.local) will result in an A/AAAA (eg. 192.168.2.129) reply. - * - A question for the reverse IP address (eg. 192-168.2.120.inarpa.arpa) will result in an PTR_IP4 (eg. esp8266.local) reply. - * - * In addition, a full name match (question domain == host domain) is marked. - */ -uint8_t MDNSResponder::_replyMaskForHost(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, - bool* p_pbFullNameMatch /*= 0*/) const { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost\n"));); - - uint8_t u8ReplyMask = 0; - (p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0); - - if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) || - (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) { - - if ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - // PTR request -#ifdef MDNS_IP4_SUPPORT - stcMDNS_RRDomain reverseIP4Domain; - if ((_buildDomainForReverseIP4(_getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE), reverseIP4Domain)) && - (p_RRHeader.m_Domain == reverseIP4Domain)) { - // Reverse domain match - u8ReplyMask |= ContentFlag_PTR_IP4; - } -#endif -#ifdef MDNS_IP6_SUPPORT - // TODO -#endif - } // Address qeuest - - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (p_RRHeader.m_Domain == hostDomain)) { // Host domain match - - (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); - -#ifdef MDNS_IP4_SUPPORT - if ((DNS_RRTYPE_A == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - // IP4 address request - u8ReplyMask |= ContentFlag_A; - } -#endif -#ifdef MDNS_IP6_SUPPORT - if ((DNS_RRTYPE_AAAA == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - // IP6 address request - u8ReplyMask |= ContentFlag_AAAA; - } -#endif - } - } - else { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); - } - DEBUG_EX_INFO(if (u8ReplyMask) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: 0x%X\n"), u8ReplyMask); } ); - return u8ReplyMask; -} - -/* - * MDNSResponder::_replyMaskForService - * - * Determines the relevant service answers for the given question - * - A PTR dns-sd service enum question (_services.dns-sd._udp.local) will result into an PTR_TYPE (eg. _http._tcp.local) answer - * - A PTR service type question (eg. _http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer - * - A PTR service name question (eg. MyESP._http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer - * - A SRV service name question (eg. MyESP._http._tcp.local) will result into an SRV (eg. 5000 MyESP.local) answer - * - A TXT service name question (eg. MyESP._http._tcp.local) will result into an TXT (eg. c#=1) answer - * - * In addition, a full name match (question domain == service instance domain) is marked. - */ -uint8_t MDNSResponder::_replyMaskForService(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, - const MDNSResponder::stcMDNSService& p_Service, - bool* p_pbFullNameMatch /*= 0*/) const { - - uint8_t u8ReplyMask = 0; - (p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0); - - if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) || - (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) { - - stcMDNS_RRDomain DNSSDDomain; - if ((_buildDomainForDNSSD(DNSSDDomain)) && // _services._dns-sd._udp.local - (p_RRHeader.m_Domain == DNSSDDomain) && - ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) { - // Common service info requested - u8ReplyMask |= ContentFlag_PTR_TYPE; - } - - stcMDNS_RRDomain serviceDomain; - if ((_buildDomainForService(p_Service, false, serviceDomain)) && // eg. _http._tcp.local - (p_RRHeader.m_Domain == serviceDomain) && - ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) { - // Special service info requested - u8ReplyMask |= ContentFlag_PTR_NAME; - } - - if ((_buildDomainForService(p_Service, true, serviceDomain)) && // eg. MyESP._http._tcp.local - (p_RRHeader.m_Domain == serviceDomain)) { - - (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); - - if ((DNS_RRTYPE_SRV == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - // Instance info SRV requested - u8ReplyMask |= ContentFlag_SRV; - } - if ((DNS_RRTYPE_TXT == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - // Instance info TXT requested - u8ReplyMask |= ContentFlag_TXT; - } - } - } - else { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); - } - DEBUG_EX_INFO(if (u8ReplyMask) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService(%s.%s.%s): 0x%X\n"), p_Service.m_pcName, p_Service.m_pcService, p_Service.m_pcProtocol, u8ReplyMask); } ); - return u8ReplyMask; -} - -} // namespace MDNSImplementation - -} // namespace esp8266 + } + return bResult; +} + +/* + MDNSResponder::_restart +*/ +bool MDNSResponder::_restart(void) +{ + + return ((_resetProbeStatus(true)) && // Stop and restart probing + (_allocUDPContext())); // Restart UDP +} + + +/** + RECEIVING +*/ + +/* + MDNSResponder::_parseMessage +*/ +bool MDNSResponder::_parseMessage(void) +{ + DEBUG_EX_INFO( + unsigned long ulStartTime = millis(); + unsigned uStartMemory = ESP.getFreeHeap(); + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage (Time: %lu ms, heap: %u bytes, from %s(%u), to %s(%u))\n"), ulStartTime, uStartMemory, + IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), m_pUDPContext->getRemotePort(), + IPAddress(m_pUDPContext->getDestAddress()).toString().c_str(), m_pUDPContext->getLocalPort()); + ); + //DEBUG_EX_INFO(_udpDump();); + + bool bResult = false; + + stcMDNS_MsgHeader header; + if (_readMDNSMsgHeader(header)) + { + if (0 == header.m_4bOpcode) // A standard query + { + if (header.m_1bQR) // Received a response -> answers to a query + { + //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Reading answers: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount);); + bResult = _parseResponse(header); + } + else // Received a query (Questions) + { + //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Reading query: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount);); + bResult = _parseQuery(header); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Received UNEXPECTED opcode:%u. Ignoring message!\n"), header.m_4bOpcode);); + m_pUDPContext->flush(); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: FAILED to read header\n"));); + m_pUDPContext->flush(); + } + DEBUG_EX_INFO( + unsigned uFreeHeap = ESP.getFreeHeap(); + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Done (%s after %lu ms, ate %i bytes, remaining %u)\n\n"), (bResult ? "Succeeded" : "FAILED"), (millis() - ulStartTime), (uStartMemory - uFreeHeap), uFreeHeap); + ); + return bResult; +} + +/* + MDNSResponder::_parseQuery + + Queries are of interest in two cases: + 1. allow for tiebreaking while probing in the case of a race condition between two instances probing for + the same name at the same time + 2. provide answers to questions for our host domain or any presented service + + When reading the questions, a set of (planned) responses is created, eg. a reverse PTR question for the host domain + gets an A (IP address) response, a PTR question for the _services._dns-sd domain gets a PTR (type) response for any + registered service, ... + + As any mDNS responder should be able to handle 'legacy' queries (from DNS clients), this case is handled here also. + Legacy queries have got only one (unicast) question and are directed to the local DNS port (not the multicast port). + + 1. +*/ +bool MDNSResponder::_parseQuery(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) +{ + + bool bResult = true; + + stcMDNSSendParameter sendParameter; + uint8_t u8HostOrServiceReplies = 0; + for (uint16_t qd = 0; ((bResult) && (qd < p_MsgHeader.m_u16QDCount)); ++qd) + { + + stcMDNS_RRQuestion questionRR; + if ((bResult = _readRRQuestion(questionRR))) + { + // Define host replies, BUT only answer queries after probing is done + u8HostOrServiceReplies = + sendParameter.m_u8HostReplyMask |= (((m_bPassivModeEnabled) || + (ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus)) + ? _replyMaskForHost(questionRR.m_Header, 0) + : 0); + DEBUG_EX_INFO(if (u8HostOrServiceReplies) + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Host reply needed 0x%X\n"), u8HostOrServiceReplies); + }); + + // Check tiebreak need for host domain + if (ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) + { + bool bFullNameMatch = false; + if ((_replyMaskForHost(questionRR.m_Header, &bFullNameMatch)) && + (bFullNameMatch)) + { + // We're in 'probing' state and someone is asking for our host domain: this might be + // a race-condition: Two host with the same domain names try simutanously to probe their domains + // See: RFC 6762, 8.2 (Tiebraking) + // However, we're using a max. reduced approach for tiebreaking here: The higher IP-address wins! + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Possible race-condition for host domain detected while probing.\n"));); + + m_HostProbeInformation.m_bTiebreakNeeded = true; + } + } + + // Define service replies + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + // Define service replies, BUT only answer queries after probing is done + uint8_t u8ReplyMaskForQuestion = (((m_bPassivModeEnabled) || + (ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus)) + ? _replyMaskForService(questionRR.m_Header, *pService, 0) + : 0); + u8HostOrServiceReplies |= (pService->m_u8ReplyMask |= u8ReplyMaskForQuestion); + DEBUG_EX_INFO(if (u8ReplyMaskForQuestion) + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service reply needed for (%s.%s.%s): 0x%X (%s)\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol, u8ReplyMaskForQuestion, IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str()); + }); + + // Check tiebreak need for service domain + if (ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) + { + bool bFullNameMatch = false; + if ((_replyMaskForService(questionRR.m_Header, *pService, &bFullNameMatch)) && + (bFullNameMatch)) + { + // We're in 'probing' state and someone is asking for this service domain: this might be + // a race-condition: Two services with the same domain names try simutanously to probe their domains + // See: RFC 6762, 8.2 (Tiebraking) + // However, we're using a max. reduced approach for tiebreaking here: The 'higher' SRV host wins! + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Possible race-condition for service domain %s.%s.%s detected while probing.\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + + pService->m_ProbeInformation.m_bTiebreakNeeded = true; + } + } + } + + // Handle unicast and legacy specialities + // If only one question asks for unicast reply, the whole reply packet is send unicast + if (((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) || // Unicast (maybe legacy) query OR + (questionRR.m_bUnicast)) && // Expressivly unicast query + (!sendParameter.m_bUnicast)) + { + + sendParameter.m_bUnicast = true; + sendParameter.m_bCacheFlush = false; + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Unicast response for %s!\n"), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); + + if ((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) && // Unicast (maybe legacy) query AND + (1 == p_MsgHeader.m_u16QDCount) && // Only one question AND + ((sendParameter.m_u8HostReplyMask) || // Host replies OR + (u8HostOrServiceReplies))) // Host or service replies available + { + // We're a match for this legacy query, BUT + // make sure, that the query comes from a local host + ip_info IPInfo_Local; + ip_info IPInfo_Remote; + if (((IPInfo_Remote.ip.addr = m_pUDPContext->getRemoteAddress())) && + (((wifi_get_ip_info(SOFTAP_IF, &IPInfo_Local)) && + (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, &IPInfo_Local.netmask))) || // Remote IP in SOFTAP's subnet OR + ((wifi_get_ip_info(STATION_IF, &IPInfo_Local)) && + (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, &IPInfo_Local.netmask))))) // Remote IP in STATION's subnet + { + + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy query from local host %s!\n"), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); + + sendParameter.m_u16ID = p_MsgHeader.m_u16ID; + sendParameter.m_bLegacyQuery = true; + sendParameter.m_pQuestions = new stcMDNS_RRQuestion; + if ((bResult = (0 != sendParameter.m_pQuestions))) + { + sendParameter.m_pQuestions->m_Header.m_Domain = questionRR.m_Header.m_Domain; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = questionRR.m_Header.m_Attributes.m_u16Type; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = questionRR.m_Header.m_Attributes.m_u16Class; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to add legacy question!\n"));); + } + } + else + { + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy query from NON-LOCAL host!\n"));); + bResult = false; + } + } + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to read question!\n"));); + } + } // for questions + + //DEBUG_EX_INFO(if (u8HostOrServiceReplies) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Reply needed: %u (%s: %s->%s)\n"), u8HostOrServiceReplies, clsTimeSyncer::timestr(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), IPAddress(m_pUDPContext->getDestAddress()).toString().c_str()); } ); + + // Handle known answers + uint32_t u32Answers = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount); + DEBUG_EX_INFO(if ((u8HostOrServiceReplies) && (u32Answers)) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Known answers(%u):\n"), u32Answers); + }); + + for (uint32_t an = 0; ((bResult) && (an < u32Answers)); ++an) + { + stcMDNS_RRAnswer* pKnownRRAnswer = 0; + if (((bResult = _readRRAnswer(pKnownRRAnswer))) && + (pKnownRRAnswer)) + { + + if ((DNS_RRTYPE_ANY != pKnownRRAnswer->m_Header.m_Attributes.m_u16Type) && // No ANY type answer + (DNS_RRCLASS_ANY != pKnownRRAnswer->m_Header.m_Attributes.m_u16Class)) // No ANY class answer + { + + // Find match between planned answer (sendParameter.m_u8HostReplyMask) and this 'known answer' + uint8_t u8HostMatchMask = (sendParameter.m_u8HostReplyMask & _replyMaskForHost(pKnownRRAnswer->m_Header)); + if ((u8HostMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND + ((MDNS_HOST_TTL / 2) <= pKnownRRAnswer->m_u32TTL)) // The TTL of the known answer is longer than half of the new host TTL (120s) + { + + // Compare contents + if (AnswerType_PTR == pKnownRRAnswer->answerType()) + { + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain == hostDomain)) + { + // Host domain match +#ifdef MDNS_IP4_SUPPORT + if (u8HostMatchMask & ContentFlag_PTR_IP4) + { + // IP4 PTR was asked for, but is already known -> skipping + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP4 PTR already known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP4; + } +#endif +#ifdef MDNS_IP6_SUPPORT + if (u8HostMatchMask & ContentFlag_PTR_IP6) + { + // IP6 PTR was asked for, but is already known -> skipping + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP6 PTR already known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP6; + } +#endif + } + } + else if (u8HostMatchMask & ContentFlag_A) + { + // IP4 address was asked for +#ifdef MDNS_IP4_SUPPORT + if ((AnswerType_A == pKnownRRAnswer->answerType()) && + (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress == _getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE))) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP4 address already known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_A; + } // else: RData NOT IP4 length !! +#endif + } + else if (u8HostMatchMask & ContentFlag_AAAA) + { + // IP6 address was asked for +#ifdef MDNS_IP6_SUPPORT + if ((AnswerType_AAAA == pAnswerRR->answerType()) && + (((stcMDNS_RRAnswerAAAA*)pAnswerRR)->m_IPAddress == _getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE))) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP6 address already known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_AAAA; + } // else: RData NOT IP6 length !! +#endif + } + } // Host match /*and TTL*/ + + // + // Check host tiebreak possibility + if (m_HostProbeInformation.m_bTiebreakNeeded) + { + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (pKnownRRAnswer->m_Header.m_Domain == hostDomain)) + { + // Host domain match +#ifdef MDNS_IP4_SUPPORT + if (AnswerType_A == pKnownRRAnswer->answerType()) + { + IPAddress localIPAddress(_getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE)); + if (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress == localIPAddress) + { + // SAME IP address -> We've received an old message from ourselfs (same IP) + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (was an old message)!\n"));); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } + else + { + if ((uint32_t)(((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress) > (uint32_t)localIPAddress) // The OTHER IP is 'higher' -> LOST + { + // LOST tiebreak + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) LOST (lower)!\n"));); + _cancelProbingForHost(); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } + else // WON tiebreak + { + //TiebreakState = TiebreakState_Won; // We received an 'old' message from ourselfs -> Just ignore + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (higher IP)!\n"));); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } + } + } +#endif +#ifdef MDNS_IP6_SUPPORT + if (AnswerType_AAAA == pAnswerRR->answerType()) + { + // TODO + } +#endif + } + } // Host tiebreak possibility + + // Check service answers + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + + uint8_t u8ServiceMatchMask = (pService->m_u8ReplyMask & _replyMaskForService(pKnownRRAnswer->m_Header, *pService)); + + if ((u8ServiceMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND + ((MDNS_SERVICE_TTL / 2) <= pKnownRRAnswer->m_u32TTL)) // The TTL of the known answer is longer than half of the new service TTL (4500s) + { + + if (AnswerType_PTR == pKnownRRAnswer->answerType()) + { + stcMDNS_RRDomain serviceDomain; + if ((u8ServiceMatchMask & ContentFlag_PTR_TYPE) && + (_buildDomainForService(*pService, false, serviceDomain)) && + (serviceDomain == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service type PTR already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_PTR_TYPE; + } + if ((u8ServiceMatchMask & ContentFlag_PTR_NAME) && + (_buildDomainForService(*pService, true, serviceDomain)) && + (serviceDomain == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service name PTR already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_PTR_NAME; + } + } + else if (u8ServiceMatchMask & ContentFlag_SRV) + { + DEBUG_EX_ERR(if (AnswerType_SRV != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: ERROR! INVALID answer type (SRV)!\n"));); + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (hostDomain == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) // Host domain match + { + + if ((MDNS_SRV_PRIORITY == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Priority) && + (MDNS_SRV_WEIGHT == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Weight) && + (pService->m_u16Port == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Port)) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service SRV answer already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_SRV; + } // else: Small differences -> send update message + } + } + else if (u8ServiceMatchMask & ContentFlag_TXT) + { + DEBUG_EX_ERR(if (AnswerType_TXT != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: ERROR! INVALID answer type (TXT)!\n"));); + _collectServiceTxts(*pService); + if (pService->m_Txts == ((stcMDNS_RRAnswerTXT*)pKnownRRAnswer)->m_Txts) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service TXT answer already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_TXT; + } + _releaseTempServiceTxts(*pService); + } + } // Service match and enough TTL + + // + // Check service tiebreak possibility + if (pService->m_ProbeInformation.m_bTiebreakNeeded) + { + stcMDNS_RRDomain serviceDomain; + if ((_buildDomainForService(*pService, true, serviceDomain)) && + (pKnownRRAnswer->m_Header.m_Domain == serviceDomain)) + { + // Service domain match + if (AnswerType_SRV == pKnownRRAnswer->answerType()) + { + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (hostDomain == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) // Host domain match + { + + // We've received an old message from ourselfs (same SRV) + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won (was an old message)!\n"));); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } + else + { + if (((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain > hostDomain) // The OTHER domain is 'higher' -> LOST + { + // LOST tiebreak + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) LOST (lower)!\n"));); + _cancelProbingForService(*pService); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } + else // WON tiebreak + { + //TiebreakState = TiebreakState_Won; // We received an 'old' message from ourselfs -> Just ignore + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won (higher)!\n"));); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } + } + } + } + } // service tiebreak possibility + } // for services + } // ANY answers + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to read known answer!\n"));); + } + + if (pKnownRRAnswer) + { + delete pKnownRRAnswer; + pKnownRRAnswer = 0; + } + } // for answers + + if (bResult) + { + // Check, if a reply is needed + uint8_t u8ReplyNeeded = sendParameter.m_u8HostReplyMask; + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + u8ReplyNeeded |= pService->m_u8ReplyMask; + } + + if (u8ReplyNeeded) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Sending answer(0x%X)...\n"), u8ReplyNeeded);); + + sendParameter.m_bResponse = true; + sendParameter.m_bAuthorative = true; + + bResult = _sendMDNSMessage(sendParameter); + } + DEBUG_EX_INFO( + else + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: No reply needed\n")); + } + ); + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Something FAILED!\n"));); + m_pUDPContext->flush(); + } + + // + // Check and reset tiebreak-states + if (m_HostProbeInformation.m_bTiebreakNeeded) + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for host domain!\n"));); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + if (pService->m_ProbeInformation.m_bTiebreakNeeded) + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for service domain (%s.%s.%s)\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_parseResponse + + Responses are of interest in two cases: + 1. find domain name conflicts while probing + 2. get answers to service queries + + In both cases any included questions are ignored + + 1. If any answer has a domain name similar to one of the domain names we're planning to use (and are probing for), + then we've got a 'probing conflict'. The conflict has to be solved on our side of the conflict (eg. by + setting a new hostname and restart probing). The callback 'm_fnProbeResultCallback' is called with + 'p_bProbeResult=false' in this case. + + 2. Service queries like '_http._tcp.local' will (if available) produce PTR, SRV, TXT and A/AAAA answers. + All stored answers are pivoted by the service instance name (from the PTR record). Other answer parts, + like host domain or IP address are than attached to this element. + Any answer part carries a TTL, this is also stored (incl. the reception time); if the TTL is '0' the + answer (part) is withdrawn by the sender and should be removed from any cache. RFC 6762, 10.1 proposes to + set the caches TTL-value to 1 second in such a case and to delete the item only, if no update has + has taken place in this second. + Answer parts may arrive in 'unsorted' order, so they are grouped into three levels: + Level 1: PRT - names the service instance (and is used as pivot), voids all other parts if is withdrawn or outdates + Level 2: SRV - links the instance name to a host domain and port, voids A/AAAA parts if is withdrawn or outdates + TXT - links the instance name to services TXTs + Level 3: A/AAAA - links the host domain to an IP address +*/ +bool MDNSResponder::_parseResponse(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse\n"));); + //DEBUG_EX_INFO(_udpDump();); + + bool bResult = false; + + // A response should be the result of a query or a probe + if ((_hasServiceQueriesWaitingForAnswers()) || // Waiting for query answers OR + (_hasProbesWaitingForAnswers())) // Probe responses + { + + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received a response\n")); + //_udpDump(); + ); + + bResult = true; + // + // Ignore questions here + stcMDNS_RRQuestion dummyRRQ; + for (uint16_t qd = 0; ((bResult) && (qd < p_MsgHeader.m_u16QDCount)); ++qd) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received a response containing a question... ignoring!\n"));); + bResult = _readRRQuestion(dummyRRQ); + } // for queries + + // + // Read and collect answers + stcMDNS_RRAnswer* pCollectedRRAnswers = 0; + uint32_t u32NumberOfAnswerRRs = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount); + for (uint32_t an = 0; ((bResult) && (an < u32NumberOfAnswerRRs)); ++an) + { + stcMDNS_RRAnswer* pRRAnswer = 0; + if (((bResult = _readRRAnswer(pRRAnswer))) && + (pRRAnswer)) + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: ADDING answer!\n"));); + pRRAnswer->m_pNext = pCollectedRRAnswers; + pCollectedRRAnswers = pRRAnswer; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED to read answer!\n"));); + if (pRRAnswer) + { + delete pRRAnswer; + pRRAnswer = 0; + } + bResult = false; + } + } // for answers + + // + // Process answers + if (bResult) + { + bResult = ((!pCollectedRRAnswers) || + (_processAnswers(pCollectedRRAnswers))); + } + else // Some failure while reading answers + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED to read answers!\n"));); + m_pUDPContext->flush(); + } + + // Delete collected answers + while (pCollectedRRAnswers) + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: DELETING answer!\n"));); + stcMDNS_RRAnswer* pNextAnswer = pCollectedRRAnswers->m_pNext; + delete pCollectedRRAnswers; + pCollectedRRAnswers = pNextAnswer; + } + } + else // Received an unexpected response -> ignore + { + /* DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received an unexpected response... ignoring!\nDUMP:\n")); + bool bDumpResult = true; + for (uint16_t qd=0; ((bDumpResult) && (qdflush(); + bResult = true; + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_processAnswers + Host: + A (0x01): eg. esp8266.local A OP TTL 123.456.789.012 + AAAA (01Cx): eg. esp8266.local AAAA OP TTL 1234:5678::90 + 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 + Service: + 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 + 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 + +*/ +bool MDNSResponder::_processAnswers(const MDNSResponder::stcMDNS_RRAnswer* p_pAnswers) +{ + + bool bResult = false; + + if (p_pAnswers) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Processing answers...\n"));); + bResult = true; + + // Answers may arrive in an unexpected order. So we loop our answers as long, as we + // can connect new information to service queries + bool bFoundNewKeyAnswer; + do + { + bFoundNewKeyAnswer = false; + + const stcMDNS_RRAnswer* pRRAnswer = p_pAnswers; + while ((pRRAnswer) && + (bResult)) + { + // 1. level answer (PTR) + if (AnswerType_PTR == pRRAnswer->answerType()) + { + // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local + bResult = _processPTRAnswer((stcMDNS_RRAnswerPTR*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new SRV or TXT answers to be linked to queries + } + // 2. level answers + // SRV -> host domain and port + else if (AnswerType_SRV == pRRAnswer->answerType()) + { + // eg. MyESP_http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local + bResult = _processSRVAnswer((stcMDNS_RRAnswerSRV*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new A/AAAA answers to be linked to queries + } + // TXT -> Txts + else if (AnswerType_TXT == pRRAnswer->answerType()) + { + // eg. MyESP_http._tcp.local TXT xxxx xx c#=1 + bResult = _processTXTAnswer((stcMDNS_RRAnswerTXT*)pRRAnswer); + } + // 3. level answers +#ifdef MDNS_IP4_SUPPORT + // A -> IP4Address + else if (AnswerType_A == pRRAnswer->answerType()) + { + // eg. esp8266.local A xxxx xx 192.168.2.120 + bResult = _processAAnswer((stcMDNS_RRAnswerA*)pRRAnswer); + } +#endif +#ifdef MDNS_IP6_SUPPORT + // AAAA -> IP6Address + else if (AnswerType_AAAA == pRRAnswer->answerType()) + { + // eg. esp8266.local AAAA xxxx xx 09cf::0c + bResult = _processAAAAAnswer((stcMDNS_RRAnswerAAAA*)pRRAnswer); + } +#endif + + // Finally check for probing conflicts + // Host domain + if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && + ((AnswerType_A == pRRAnswer->answerType()) || + (AnswerType_AAAA == pRRAnswer->answerType()))) + { + + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (pRRAnswer->m_Header.m_Domain == hostDomain)) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found with: %s.local\n"), m_pcHostname);); + _cancelProbingForHost(); + } + } + // Service domains + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && + ((AnswerType_TXT == pRRAnswer->answerType()) || + (AnswerType_SRV == pRRAnswer->answerType()))) + { + + stcMDNS_RRDomain serviceDomain; + if ((_buildDomainForService(*pService, true, serviceDomain)) && + (pRRAnswer->m_Header.m_Domain == serviceDomain)) + { + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found with: %s.%s.%s\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + _cancelProbingForService(*pService); + } + } + } + + pRRAnswer = pRRAnswer->m_pNext; // Next collected answer + } // while (answers) + } while ((bFoundNewKeyAnswer) && + (bResult)); + } // else: No answers provided + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_processPTRAnswer +*/ +bool MDNSResponder::_processPTRAnswer(const MDNSResponder::stcMDNS_RRAnswerPTR* p_pPTRAnswer, + bool& p_rbFoundNewKeyAnswer) +{ + + bool bResult = false; + + if ((bResult = (0 != p_pPTRAnswer))) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: Processing PTR answers...\n"));); + // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local + // Check pending service queries for eg. '_http._tcp' + + stcMDNSServiceQuery* pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, 0); + while (pServiceQuery) + { + if (pServiceQuery->m_bAwaitingAnswers) + { + // Find answer for service domain (eg. MyESP._http._tcp.local) + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pPTRAnswer->m_PTRDomain); + if (pSQAnswer) // existing answer + { + if (p_pPTRAnswer->m_u32TTL) // Received update message + { + pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); // Update TTL tag + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: Updated TTL(%lu) for "), p_pPTRAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + ); + } + else // received goodbye-message + { + pSQAnswer->m_TTLServiceDomain.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + ); + } + } + else if ((p_pPTRAnswer->m_u32TTL) && // Not just a goodbye-message + ((pSQAnswer = new stcMDNSServiceQuery::stcAnswer))) // Not yet included -> add answer + { + pSQAnswer->m_ServiceDomain = p_pPTRAnswer->m_PTRDomain; + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_ServiceDomain; + pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); + pSQAnswer->releaseServiceDomain(); + + bResult = pServiceQuery->addAnswer(pSQAnswer); + p_rbFoundNewKeyAnswer = true; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_ServiceDomain), true); + } + } + } + pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, pServiceQuery); + } + } // else: No p_pPTRAnswer + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_processSRVAnswer +*/ +bool MDNSResponder::_processSRVAnswer(const MDNSResponder::stcMDNS_RRAnswerSRV* p_pSRVAnswer, + bool& p_rbFoundNewKeyAnswer) +{ + + bool bResult = false; + + if ((bResult = (0 != p_pSRVAnswer))) + { + // eg. MyESP._http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pSRVAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this service domain (eg. MyESP._http._tcp.local) available + { + if (p_pSRVAnswer->m_u32TTL) // First or update message (TTL != 0) + { + pSQAnswer->m_TTLHostDomainAndPort.set(p_pSRVAnswer->m_u32TTL); // Update TTL tag + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: Updated TTL(%lu) for "), p_pSRVAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); + ); + // Host domain & Port + if ((pSQAnswer->m_HostDomain != p_pSRVAnswer->m_SRVDomain) || + (pSQAnswer->m_u16Port != p_pSRVAnswer->m_u16Port)) + { + + pSQAnswer->m_HostDomain = p_pSRVAnswer->m_SRVDomain; + pSQAnswer->releaseHostDomain(); + pSQAnswer->m_u16Port = p_pSRVAnswer->m_u16Port; + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_HostDomainAndPort; + + p_rbFoundNewKeyAnswer = true; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_HostDomainAndPort), true); + } + } + } + else // Goodby message + { + pSQAnswer->m_TTLHostDomainAndPort.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); + ); + } + } + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pSRVAnswer + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_processTXTAnswer +*/ +bool MDNSResponder::_processTXTAnswer(const MDNSResponder::stcMDNS_RRAnswerTXT* p_pTXTAnswer) +{ + + bool bResult = false; + + if ((bResult = (0 != p_pTXTAnswer))) + { + // eg. MyESP._http._tcp.local TXT xxxx xx c#=1 + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pTXTAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this service domain (eg. MyESP._http._tcp.local) available + { + if (p_pTXTAnswer->m_u32TTL) // First or update message + { + pSQAnswer->m_TTLTxts.set(p_pTXTAnswer->m_u32TTL); // Update TTL tag + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: Updated TTL(%lu) for "), p_pTXTAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); + ); + if (!pSQAnswer->m_Txts.compare(p_pTXTAnswer->m_Txts)) + { + pSQAnswer->m_Txts = p_pTXTAnswer->m_Txts; + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_Txts; + pSQAnswer->releaseTxts(); + + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_Txts), true); + } + } + } + else // Goodby message + { + pSQAnswer->m_TTLTxts.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); + ); + } + } + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pTXTAnswer + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: FAILED!\n")); + }); + return bResult; +} + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::_processAAnswer +*/ +bool MDNSResponder::_processAAnswer(const MDNSResponder::stcMDNS_RRAnswerA* p_pAAnswer) +{ + + bool bResult = false; + + if ((bResult = (0 != p_pAAnswer))) + { + // eg. esp8266.local A xxxx xx 192.168.2.120 + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForHostDomain(p_pAAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this host domain (eg. esp8266.local) available + { + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = pSQAnswer->findIP4Address(p_pAAnswer->m_IPAddress); + if (pIP4Address) + { + // Already known IP4 address + if (p_pAAnswer->m_u32TTL) // Valid TTL -> Update answers TTL + { + pIP4Address->m_TTL.set(p_pAAnswer->m_u32TTL); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%lu) for "), p_pAAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP4Address (%s)\n"), pIP4Address->m_IPAddress.toString().c_str()); + ); + } + else // 'Goodbye' message for known IP4 address + { + pIP4Address->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP4 address (%s)\n"), pIP4Address->m_IPAddress.toString().c_str()); + ); + } + } + else + { + // Until now unknown IP4 address -> Add (if the message isn't just a 'Goodbye' note) + if (p_pAAnswer->m_u32TTL) // NOT just a 'Goodbye' message + { + pIP4Address = new stcMDNSServiceQuery::stcAnswer::stcIP4Address(p_pAAnswer->m_IPAddress, p_pAAnswer->m_u32TTL); + if ((pIP4Address) && + (pSQAnswer->addIP4Address(pIP4Address))) + { + + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP4Address; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_IP4Address), true); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP4 address (%s)!\n"), p_pAAnswer->m_IPAddress.toString().c_str());); + } + } + } + } + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pAAnswer + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED!\n")); + }); + return bResult; +} +#endif + +#ifdef MDNS_IP6_SUPPORT +/* + MDNSResponder::_processAAAAAnswer +*/ +bool MDNSResponder::_processAAAAAnswer(const MDNSResponder::stcMDNS_RRAnswerAAAA* p_pAAAAAnswer) +{ + + bool bResult = false; + + if ((bResult = (0 != p_pAAAAAnswer))) + { + // eg. esp8266.local AAAA xxxx xx 0bf3::0c + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForHostDomain(p_pAAAAAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this host domain (eg. esp8266.local) available + { + stcIP6Address* pIP6Address = pSQAnswer->findIP6Address(p_pAAAAAnswer->m_IPAddress); + if (pIP6Address) + { + // Already known IP6 address + if (p_pAAAAAnswer->m_u32TTL) // Valid TTL -> Update answers TTL + { + pIP6Address->m_TTL.set(p_pAAAAAnswer->m_u32TTL); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%lu) for "), p_pAAAAAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), pIP6Address->m_IPAddress.toString().c_str()); + ); + } + else // 'Goodbye' message for known IP6 address + { + pIP6Address->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), pIP6Address->m_IPAddress.toString().c_str()); + ); + } + } + else + { + // Until now unknown IP6 address -> Add (if the message isn't just a 'Goodbye' note) + if (p_pAAAAAnswer->m_u32TTL) // NOT just a 'Goodbye' message + { + pIP6Address = new stcIP6Address(p_pAAAAAnswer->m_IPAddress, p_pAAAAAnswer->m_u32TTL); + if ((pIP6Address) && + (pSQAnswer->addIP6Address(pIP6Address))) + { + + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP6Address; + + if (pServiceQuery->m_fnCallback) + { + pServiceQuery->m_fnCallback(this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer), ServiceQueryAnswerType_IP6Address, true, pServiceQuery->m_pUserdata); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP6 address (%s)!\n"), p_pAAAAAnswer->m_IPAddress.toString().c_str());); + } + } + } + } + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pAAAAAnswer + + return bResult; +} +#endif + + +/* + PROBING +*/ + +/* + MDNSResponder::_updateProbeStatus + + Manages the (outgoing) probing process. + - If probing has not been started yet (ProbingStatus_NotStarted), the initial delay (see RFC 6762) is determined and + the process is started + - After timeout (of initial or subsequential delay) a probe message is send out for three times. If the message has + already been sent out three times, the probing has been successful and is finished. + + Conflict management is handled in '_parseResponse ff.' + Tiebraking is handled in 'parseQuery ff.' +*/ +bool MDNSResponder::_updateProbeStatus(void) +{ + + bool bResult = true; + + // + // Probe host domain + if ((ProbingStatus_ReadyToStart == m_HostProbeInformation.m_ProbingStatus) && // Ready to get started AND + //TODO: Fix the following to allow Ethernet shield or other interfaces + (_getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE) != IPAddress())) // Has IP address + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Starting host probing...\n"));); + + // First probe delay SHOULD be random 0-250 ms + m_HostProbeInformation.m_Timeout.reset(rand() % MDNS_PROBE_DELAY); + m_HostProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; + } + else if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing AND + (m_HostProbeInformation.m_Timeout.expired())) // Time for next probe + { + + if (MDNS_PROBE_COUNT > m_HostProbeInformation.m_u8SentCount) // Send next probe + { + if ((bResult = _sendHostProbe())) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent host probe\n\n"));); + m_HostProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); + ++m_HostProbeInformation.m_u8SentCount; + } + } + else // Probing finished + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host probing.\n"));); + m_HostProbeInformation.m_ProbingStatus = ProbingStatus_Done; + m_HostProbeInformation.m_Timeout.resetToNeverExpires(); + if (m_HostProbeInformation.m_fnHostProbeResultCallback) + { + m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, true); + } + + // Prepare to announce host + m_HostProbeInformation.m_u8SentCount = 0; + m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Prepared host announcing.\n\n"));); + } + } // else: Probing already finished OR waiting for next time slot + else if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) && + (m_HostProbeInformation.m_Timeout.expired())) + { + + if ((bResult = _announce(true, false))) // Don't announce services here + { + ++m_HostProbeInformation.m_u8SentCount; + + if (MDNS_ANNOUNCE_COUNT > m_HostProbeInformation.m_u8SentCount) + { + m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing host (%lu).\n\n"), m_HostProbeInformation.m_u8SentCount);); + } + else + { + m_HostProbeInformation.m_Timeout.resetToNeverExpires(); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host announcing.\n\n"));); + } + } + } + + // + // Probe services + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) + { + if (ProbingStatus_ReadyToStart == pService->m_ProbeInformation.m_ProbingStatus) // Ready to get started + { + + pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); // More or equal than first probe for host domain + pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; + } + else if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing AND + (pService->m_ProbeInformation.m_Timeout.expired())) // Time for next probe + { + + if (MDNS_PROBE_COUNT > pService->m_ProbeInformation.m_u8SentCount) // Send next probe + { + if ((bResult = _sendServiceProbe(*pService))) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent service probe (%u)\n\n"), (pService->m_ProbeInformation.m_u8SentCount + 1));); + pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); + ++pService->m_ProbeInformation.m_u8SentCount; + } + } + else // Probing finished + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service probing %s.%s.%s\n\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_Done; + pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); + if (pService->m_ProbeInformation.m_fnServiceProbeResultCallback) + { + pService->m_ProbeInformation.m_fnServiceProbeResultCallback(pService->m_pcName, pService, true); + } + // Prepare to announce service + pService->m_ProbeInformation.m_u8SentCount = 0; + pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Prepared service announcing.\n\n"));); + } + } // else: Probing already finished OR waiting for next time slot + else if ((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) && + (pService->m_ProbeInformation.m_Timeout.expired())) + { + + if ((bResult = _announceService(*pService))) // Announce service + { + ++pService->m_ProbeInformation.m_u8SentCount; + + if (MDNS_ANNOUNCE_COUNT > pService->m_ProbeInformation.m_u8SentCount) + { + pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing service %s.%s.%s (%lu)\n\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_ProbeInformation.m_u8SentCount);); + } + else + { + pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service announcing for %s.%s.%s\n\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + } + } + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: FAILED!\n\n")); + }); + return bResult; +} + +/* + MDNSResponder::_resetProbeStatus + + Resets the probe status. + If 'p_bRestart' is set, the status is set to ProbingStatus_NotStarted. Consequently, + when running 'updateProbeStatus' (which is done in every '_update' loop), the probing + process is restarted. +*/ +bool MDNSResponder::_resetProbeStatus(bool p_bRestart /*= true*/) +{ + + m_HostProbeInformation.clear(false); + m_HostProbeInformation.m_ProbingStatus = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); + + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + pService->m_ProbeInformation.clear(false); + pService->m_ProbeInformation.m_ProbingStatus = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); + } + return true; +} + +/* + MDNSResponder::_hasProbesWaitingForAnswers +*/ +bool MDNSResponder::_hasProbesWaitingForAnswers(void) const +{ + + bool bResult = ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing + (0 < m_HostProbeInformation.m_u8SentCount)); // And really probing + + for (stcMDNSService* pService = m_pServices; ((!bResult) && (pService)); pService = pService->m_pNext) + { + bResult = ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing + (0 < pService->m_ProbeInformation.m_u8SentCount)); // And really probing + } + return bResult; +} + +/* + MDNSResponder::_sendHostProbe + + Asks (probes) in the local network for the planned host domain + - (eg. esp8266.local) + + To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in + the 'knwon answers' section of the query. + Host domain: + - A/AAAA (eg. esp8266.esp -> 192.168.2.120) +*/ +bool MDNSResponder::_sendHostProbe(void) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe (%s, %lu)\n"), m_pcHostname, millis());); + + bool bResult = true; + + // Requests for host domain + stcMDNSSendParameter sendParameter; + sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 + + sendParameter.m_pQuestions = new stcMDNS_RRQuestion; + if (((bResult = (0 != sendParameter.m_pQuestions))) && + ((bResult = _buildDomainForHost(m_pcHostname, sendParameter.m_pQuestions->m_Header.m_Domain)))) + { + + //sendParameter.m_pQuestions->m_bUnicast = true; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet + + // Add known answers +#ifdef MDNS_IP4_SUPPORT + sendParameter.m_u8HostReplyMask |= ContentFlag_A; // Add A answer +#endif +#ifdef MDNS_IP6_SUPPORT + sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // Add AAAA answer +#endif + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED to create host question!\n"));); + if (sendParameter.m_pQuestions) + { + delete sendParameter.m_pQuestions; + sendParameter.m_pQuestions = 0; + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED!\n")); + }); + return ((bResult) && + (_sendMDNSMessage(sendParameter))); +} + +/* + MDNSResponder::_sendServiceProbe + + Asks (probes) in the local network for the planned service instance domain + - (eg. MyESP._http._tcp.local). + + To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in + the 'knwon answers' section of the query. + Service domain: + - SRV (eg. MyESP._http._tcp.local -> 5000 esp8266.local) + - PTR NAME (eg. _http._tcp.local -> MyESP._http._tcp.local) (TODO: Check if needed, maybe TXT is better) +*/ +bool MDNSResponder::_sendServiceProbe(stcMDNSService& p_rService) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe (%s.%s.%s, %lu)\n"), (p_rService.m_pcName ? : m_pcHostname), p_rService.m_pcService, p_rService.m_pcProtocol, millis());); + + bool bResult = true; + + // Requests for service instance domain + stcMDNSSendParameter sendParameter; + sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 + + sendParameter.m_pQuestions = new stcMDNS_RRQuestion; + if (((bResult = (0 != sendParameter.m_pQuestions))) && + ((bResult = _buildDomainForService(p_rService, true, sendParameter.m_pQuestions->m_Header.m_Domain)))) + { + + sendParameter.m_pQuestions->m_bUnicast = true; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet + + // Add known answers + p_rService.m_u8ReplyMask = (ContentFlag_SRV | ContentFlag_PTR_NAME); // Add SRV and PTR NAME answers + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED to create service question!\n"));); + if (sendParameter.m_pQuestions) + { + delete sendParameter.m_pQuestions; + sendParameter.m_pQuestions = 0; + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED!\n")); + }); + return ((bResult) && + (_sendMDNSMessage(sendParameter))); +} + +/* + MDNSResponder::_cancelProbingForHost +*/ +bool MDNSResponder::_cancelProbingForHost(void) +{ + + bool bResult = false; + + m_HostProbeInformation.clear(false); + // Send host notification + if (m_HostProbeInformation.m_fnHostProbeResultCallback) + { + m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, false); + + bResult = true; + } + + for (stcMDNSService* pService = m_pServices; ((!bResult) && (pService)); pService = pService->m_pNext) + { + bResult = _cancelProbingForService(*pService); + } + return bResult; +} + +/* + MDNSResponder::_cancelProbingForService +*/ +bool MDNSResponder::_cancelProbingForService(stcMDNSService& p_rService) +{ + + bool bResult = false; + + p_rService.m_ProbeInformation.clear(false); + // Send notification + if (p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback) + { + p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback(p_rService.m_pcName, &p_rService, false); + bResult = true; + } + return bResult; +} + + + +/** + ANNOUNCING +*/ + +/* + MDNSResponder::_announce + + Announces the host domain: + - A/AAAA (eg. esp8266.local -> 192.168.2.120) + - PTR (eg. 192.168.2.120.in-addr.arpa -> esp8266.local) + + and all presented services: + - PTR_TYPE (_services._dns-sd._udp.local -> _http._tcp.local) + - PTR_NAME (eg. _http._tcp.local -> MyESP8266._http._tcp.local) + - SRV (eg. MyESP8266._http._tcp.local -> 5000 esp8266.local) + - TXT (eg. MyESP8266._http._tcp.local -> c#=1) + + Goodbye (Un-Announcing) for the host domain and all services is also handled here. + Goodbye messages are created by setting the TTL for the answer to 0, this happens + inside the '_writeXXXAnswer' procs via 'sendParameter.m_bUnannounce = true' +*/ +bool MDNSResponder::_announce(bool p_bAnnounce, + bool p_bIncludeServices) +{ + + bool bResult = false; + + stcMDNSSendParameter sendParameter; + if (ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) + { + + bResult = true; + + sendParameter.m_bResponse = true; // Announces are 'Unsolicited authorative responses' + sendParameter.m_bAuthorative = true; + sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers + + // Announce host + sendParameter.m_u8HostReplyMask = 0; +#ifdef MDNS_IP4_SUPPORT + sendParameter.m_u8HostReplyMask |= ContentFlag_A; // A answer + sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP4; // PTR_IP4 answer +#endif +#ifdef MDNS_IP6_SUPPORT + sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // AAAA answer + sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP6; // PTR_IP6 answer +#endif + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: Announcing host %s (content 0x%X)\n"), m_pcHostname, sendParameter.m_u8HostReplyMask);); + + if (p_bIncludeServices) + { + // Announce services (service type, name, SRV (location) and TXTs) + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) + { + if (ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) + { + pService->m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: Announcing service %s.%s.%s (content %u)\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_u8ReplyMask);); + } + } + } + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: FAILED!\n")); + }); + return ((bResult) && + (_sendMDNSMessage(sendParameter))); +} + +/* + MDNSResponder::_announceService +*/ +bool MDNSResponder::_announceService(stcMDNSService& p_rService, + bool p_bAnnounce /*= true*/) +{ + + bool bResult = false; + + stcMDNSSendParameter sendParameter; + if (ProbingStatus_Done == p_rService.m_ProbeInformation.m_ProbingStatus) + { + + sendParameter.m_bResponse = true; // Announces are 'Unsolicited authorative responses' + sendParameter.m_bAuthorative = true; + sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers + + // DON'T announce host + sendParameter.m_u8HostReplyMask = 0; + + // Announce services (service type, name, SRV (location) and TXTs) + p_rService.m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: Announcing service %s.%s.%s (content 0x%X)\n"), (p_rService.m_pcName ? : m_pcHostname), p_rService.m_pcService, p_rService.m_pcProtocol, p_rService.m_u8ReplyMask);); + + bResult = true; + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: FAILED!\n")); + }); + return ((bResult) && + (_sendMDNSMessage(sendParameter))); +} + + +/** + SERVICE QUERY CACHE +*/ + +/* + MDNSResponder::_hasServiceQueriesWaitingForAnswers +*/ +bool MDNSResponder::_hasServiceQueriesWaitingForAnswers(void) const +{ + + bool bOpenQueries = false; + + for (stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; pServiceQuery; pServiceQuery = pServiceQuery->m_pNext) + { + if (pServiceQuery->m_bAwaitingAnswers) + { + bOpenQueries = true; + break; + } + } + return bOpenQueries; +} + +/* + MDNSResponder::_checkServiceQueryCache + + For any 'living' service query (m_bAwaitingAnswers == true) all available answers (their components) + are checked for topicality based on the stored reception time and the answers TTL. + When the components TTL is outlasted by more than 80%, a new question is generated, to get updated information. + When no update arrived (in time), the component is removed from the answer (cache). + +*/ +bool MDNSResponder::_checkServiceQueryCache(void) +{ + + bool bResult = true; + + DEBUG_EX_INFO( + bool printedInfo = false; + ); + for (stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; ((bResult) && (pServiceQuery)); pServiceQuery = pServiceQuery->m_pNext) + { + + // + // Resend dynamic service queries, if not already done often enough + if ((!pServiceQuery->m_bLegacyQuery) && + (MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) && + (pServiceQuery->m_ResendTimeout.expired())) + { + + if ((bResult = _sendMDNSServiceQuery(*pServiceQuery))) + { + ++pServiceQuery->m_u8SentCount; + pServiceQuery->m_ResendTimeout.reset((MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) + ? (MDNS_DYNAMIC_QUERY_RESEND_DELAY * (pServiceQuery->m_u8SentCount - 1)) + : esp8266::polledTimeout::oneShotMs::neverExpires); + } + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: %s to resend service query!"), (bResult ? "Succeeded" : "FAILED")); + printedInfo = true; + ); + } + + // + // Schedule updates for cached answers + if (pServiceQuery->m_bAwaitingAnswers) + { + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->m_pAnswers; + while ((bResult) && + (pSQAnswer)) + { + stcMDNSServiceQuery::stcAnswer* pNextSQAnswer = pSQAnswer->m_pNext; + + // 1. level answer + if ((bResult) && + (pSQAnswer->m_TTLServiceDomain.flagged())) + { + + if (!pSQAnswer->m_TTLServiceDomain.finalTimeoutLevel()) + { + + bResult = ((_sendMDNSServiceQuery(*pServiceQuery)) && + (pSQAnswer->m_TTLServiceDomain.restart())); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: PTR update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" %s\n"), (bResult ? "OK" : "FAILURE")); + printedInfo = true; + ); + } + else + { + // Timed out! -> Delete + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_ServiceDomain), false); + } + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove PTR answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + printedInfo = true; + ); + + bResult = pServiceQuery->removeAnswer(pSQAnswer); + pSQAnswer = 0; + continue; // Don't use this answer anymore + } + } // ServiceDomain flagged + + // 2. level answers + // HostDomain & Port (from SRV) + if ((bResult) && + (pSQAnswer->m_TTLHostDomainAndPort.flagged())) + { + + if (!pSQAnswer->m_TTLHostDomainAndPort.finalTimeoutLevel()) + { + + bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_SRV)) && + (pSQAnswer->m_TTLHostDomainAndPort.restart())); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: SRV update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port %s\n"), (bResult ? "OK" : "FAILURE")); + printedInfo = true; + ); + } + else + { + // Timed out! -> Delete + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove SRV answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); + printedInfo = true; + ); + // Delete + pSQAnswer->m_HostDomain.clear(); + pSQAnswer->releaseHostDomain(); + pSQAnswer->m_u16Port = 0; + pSQAnswer->m_TTLHostDomainAndPort.set(0); + uint32_t u32ContentFlags = ServiceQueryAnswerType_HostDomainAndPort; + // As the host domain is the base for the IP4- and IP6Address, remove these too +#ifdef MDNS_IP4_SUPPORT + pSQAnswer->releaseIP4Addresses(); + u32ContentFlags |= ServiceQueryAnswerType_IP4Address; +#endif +#ifdef MDNS_IP6_SUPPORT + pSQAnswer->releaseIP6Addresses(); + u32ContentFlags |= ServiceQueryAnswerType_IP6Address; +#endif + + // Remove content flags for deleted answer parts + pSQAnswer->m_u32ContentFlags &= ~u32ContentFlags; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(u32ContentFlags), false); + } + } + } // HostDomainAndPort flagged + + // Txts (from TXT) + if ((bResult) && + (pSQAnswer->m_TTLTxts.flagged())) + { + + if (!pSQAnswer->m_TTLTxts.finalTimeoutLevel()) + { + + bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_TXT)) && + (pSQAnswer->m_TTLTxts.restart())); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: TXT update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs %s\n"), (bResult ? "OK" : "FAILURE")); + printedInfo = true; + ); + } + else + { + // Timed out! -> Delete + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove TXT answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); + printedInfo = true; + ); + // Delete + pSQAnswer->m_Txts.clear(); + pSQAnswer->m_TTLTxts.set(0); + + // Remove content flags for deleted answer parts + pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_Txts; + + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_Txts), false); + } + } + } // TXTs flagged + + // 3. level answers +#ifdef MDNS_IP4_SUPPORT + // IP4Address (from A) + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = pSQAnswer->m_pIP4Addresses; + bool bAUpdateQuerySent = false; + while ((pIP4Address) && + (bResult)) + { + + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pNextIP4Address = pIP4Address->m_pNext; // Get 'next' early, as 'current' may be deleted at the end... + + if (pIP4Address->m_TTL.flagged()) + { + + if (!pIP4Address->m_TTL.finalTimeoutLevel()) // Needs update + { + + if ((bAUpdateQuerySent) || + ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_A)))) + { + + pIP4Address->m_TTL.restart(); + bAUpdateQuerySent = true; + + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: IP4 update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP4 address (%s)\n"), (pIP4Address->m_IPAddress.toString().c_str())); + printedInfo = true; + ); + } + } + else + { + // Timed out! -> Delete + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove IP4 answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP4 address\n")); + printedInfo = true; + ); + pSQAnswer->removeIP4Address(pIP4Address); + if (!pSQAnswer->m_pIP4Addresses) // NO IP4 address left -> remove content flag + { + pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_IP4Address; + } + // Notify client + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_IP4Address), false); + } + } + } // IP4 flagged + + pIP4Address = pNextIP4Address; // Next + } // while +#endif +#ifdef MDNS_IP6_SUPPORT + // IP6Address (from AAAA) + stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = pSQAnswer->m_pIP6Addresses; + bool bAAAAUpdateQuerySent = false; + while ((pIP6Address) && + (bResult)) + { + + stcMDNSServiceQuery::stcAnswer::stcIP6Address* pNextIP6Address = pIP6Address->m_pNext; // Get 'next' early, as 'current' may be deleted at the end... + + if (pIP6Address->m_TTL.flagged()) + { + + if (!pIP6Address->m_TTL.finalTimeoutLevel()) // Needs update + { + + if ((bAAAAUpdateQuerySent) || + ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_AAAA)))) + { + + pIP6Address->m_TTL.restart(); + bAAAAUpdateQuerySent = true; + + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: IP6 update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), (pIP6Address->m_IPAddress.toString().c_str())); + printedInfo = true; + ); + } + } + else + { + // Timed out! -> Delete + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP6Address\n")); + printedInfo = true; + ); + pSQAnswer->removeIP6Address(pIP6Address); + if (!pSQAnswer->m_pIP6Addresses) // NO IP6 address left -> remove content flag + { + pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_IP6Address; + } + // Notify client + if (pServiceQuery->m_fnCallback) + { + pServiceQuery->m_fnCallback(this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer), ServiceQueryAnswerType_IP6Address, false, pServiceQuery->m_pUserdata); + } + } + } // IP6 flagged + + pIP6Address = pNextIP6Address; // Next + } // while +#endif + pSQAnswer = pNextSQAnswer; + } + } + } + DEBUG_EX_INFO( + if (printedInfo) +{ + DEBUG_OUTPUT.printf_P(PSTR("\n")); + } + ); + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: FAILED!\n")); + }); + return bResult; +} + + +/* + MDNSResponder::_replyMaskForHost + + Determines the relavant host answers for the given question. + - A question for the hostname (eg. esp8266.local) will result in an A/AAAA (eg. 192.168.2.129) reply. + - A question for the reverse IP address (eg. 192-168.2.120.inarpa.arpa) will result in an PTR_IP4 (eg. esp8266.local) reply. + + In addition, a full name match (question domain == host domain) is marked. +*/ +uint8_t MDNSResponder::_replyMaskForHost(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, + bool* p_pbFullNameMatch /*= 0*/) const +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost\n"));); + + uint8_t u8ReplyMask = 0; + (p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0); + + if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) || + (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) + { + + if ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // PTR request +#ifdef MDNS_IP4_SUPPORT + stcMDNS_RRDomain reverseIP4Domain; + if ((_buildDomainForReverseIP4(_getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE), reverseIP4Domain)) && + (p_RRHeader.m_Domain == reverseIP4Domain)) + { + // Reverse domain match + u8ReplyMask |= ContentFlag_PTR_IP4; + } +#endif +#ifdef MDNS_IP6_SUPPORT + // TODO +#endif + } // Address qeuest + + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (p_RRHeader.m_Domain == hostDomain)) // Host domain match + { + + (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); + +#ifdef MDNS_IP4_SUPPORT + if ((DNS_RRTYPE_A == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // IP4 address request + u8ReplyMask |= ContentFlag_A; + } +#endif +#ifdef MDNS_IP6_SUPPORT + if ((DNS_RRTYPE_AAAA == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // IP6 address request + u8ReplyMask |= ContentFlag_AAAA; + } +#endif + } + } + else + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); + } + DEBUG_EX_INFO(if (u8ReplyMask) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: 0x%X\n"), u8ReplyMask); + }); + return u8ReplyMask; +} + +/* + MDNSResponder::_replyMaskForService + + Determines the relevant service answers for the given question + - A PTR dns-sd service enum question (_services.dns-sd._udp.local) will result into an PTR_TYPE (eg. _http._tcp.local) answer + - A PTR service type question (eg. _http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer + - A PTR service name question (eg. MyESP._http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer + - A SRV service name question (eg. MyESP._http._tcp.local) will result into an SRV (eg. 5000 MyESP.local) answer + - A TXT service name question (eg. MyESP._http._tcp.local) will result into an TXT (eg. c#=1) answer + + In addition, a full name match (question domain == service instance domain) is marked. +*/ +uint8_t MDNSResponder::_replyMaskForService(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, + const MDNSResponder::stcMDNSService& p_Service, + bool* p_pbFullNameMatch /*= 0*/) const +{ + + uint8_t u8ReplyMask = 0; + (p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0); + + if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) || + (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) + { + + stcMDNS_RRDomain DNSSDDomain; + if ((_buildDomainForDNSSD(DNSSDDomain)) && // _services._dns-sd._udp.local + (p_RRHeader.m_Domain == DNSSDDomain) && + ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) + { + // Common service info requested + u8ReplyMask |= ContentFlag_PTR_TYPE; + } + + stcMDNS_RRDomain serviceDomain; + if ((_buildDomainForService(p_Service, false, serviceDomain)) && // eg. _http._tcp.local + (p_RRHeader.m_Domain == serviceDomain) && + ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) + { + // Special service info requested + u8ReplyMask |= ContentFlag_PTR_NAME; + } + + if ((_buildDomainForService(p_Service, true, serviceDomain)) && // eg. MyESP._http._tcp.local + (p_RRHeader.m_Domain == serviceDomain)) + { + + (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); + + if ((DNS_RRTYPE_SRV == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // Instance info SRV requested + u8ReplyMask |= ContentFlag_SRV; + } + if ((DNS_RRTYPE_TXT == p_RRHeader.m_Attributes.m_u16Type) || + (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // Instance info TXT requested + u8ReplyMask |= ContentFlag_TXT; + } + } + } + else + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); + } + DEBUG_EX_INFO(if (u8ReplyMask) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService(%s.%s.%s): 0x%X\n"), p_Service.m_pcName, p_Service.m_pcService, p_Service.m_pcProtocol, u8ReplyMask); + }); + return u8ReplyMask; +} + +} // namespace MDNSImplementation + +} // namespace esp8266 diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp index d99a57f24a..ef777ee37c 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp @@ -1,750 +1,850 @@ -/* - * LEAmDNS_Helpers.cpp - * - * License (MIT license): - * 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. - * - */ - -#include "lwip/igmp.h" - -#include "LEAmDNS_lwIPdefs.h" -#include "LEAmDNS_Priv.h" - - -namespace { - -/* - * strrstr (static) - * - * Backwards search for p_pcPattern in p_pcString - * Based on: https://stackoverflow.com/a/1634398/2778898 - * - */ -const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pcPattern) { - - const char* pcResult = 0; - - size_t stStringLength = (p_pcString ? strlen(p_pcString) : 0); - size_t stPatternLength = (p_pcPattern ? strlen(p_pcPattern) : 0); - - if ((stStringLength) && - (stPatternLength) && - (stPatternLength <= stStringLength)) { - // Pattern is shorter or has the same length tham the string - - for (const char* s=(p_pcString + stStringLength - stPatternLength); s>=p_pcString; --s) { - if (0 == strncmp(s, p_pcPattern, stPatternLength)) { - pcResult = s; - break; - } - } - } - return pcResult; -} - - -} // anonymous - - - - - -namespace esp8266 { - -/* - * LEAmDNS - */ -namespace MDNSImplementation { - -/** - * HELPERS - */ - -/* - * MDNSResponder::indexDomain (static) - * - * 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 - * 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 no default is given, 'esp8266' is used. - * - */ -/*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain, - const char* p_pcDivider /*= "-"*/, - const char* p_pcDefaultDomain /*= 0*/) { - - bool bResult = false; - - // Ensure a divider exists; use '-' as default - const char* pcDivider = (p_pcDivider ?: "-"); - - if (p_rpcDomain) { - const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider); - if (pFoundDivider) { // maybe already extended - char* pEnd = 0; - unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10); - if ((ulIndex) && - ((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain)) && - (!*pEnd)) { // Valid (old) index found - - char acIndexBuffer[16]; - sprintf(acIndexBuffer, "%lu", (++ulIndex)); - size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider)) + strlen(acIndexBuffer) + 1); - char* pNewHostname = new char[stLength]; - if (pNewHostname) { - memcpy(pNewHostname, p_rpcDomain, (pFoundDivider - p_rpcDomain + strlen(pcDivider))); - pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0; - strcat(pNewHostname, acIndexBuffer); - - delete[] p_rpcDomain; - p_rpcDomain = pNewHostname; - - bResult = true; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); - } - } - else { - pFoundDivider = 0; // Flag the need to (base) extend the hostname - } - } - - 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' - char* pNewHostname = new char[stLength]; - if (pNewHostname) { - sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider); - - delete[] p_rpcDomain; - p_rpcDomain = pNewHostname; - - bResult = true; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); - } - } - } - else { - // No given host domain, use base or default - const char* cpcDefaultName = (p_pcDefaultDomain ?: "esp8266"); - - size_t stLength = strlen(cpcDefaultName) + 1; // '\0' - p_rpcDomain = new char[stLength]; - if (p_rpcDomain) { - strncpy(p_rpcDomain, cpcDefaultName, stLength); - bResult = true; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); - } - } - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] indexDomain: %s\n"), p_rpcDomain);); - return bResult; -} - - -/* - * UDP CONTEXT - */ - -bool MDNSResponder::_callProcess(void) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(), m_pUDPContext->getRemoteAddress().toString().c_str());); - - return _process(false); -} - -/* - * MDNSResponder::_allocUDPContext - * - * (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 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 - * is called from the WiFi stack side of the ESP stack system. - * - */ -bool MDNSResponder::_allocUDPContext(void) { - DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext");); - - bool bResult = false; - - _releaseUDPContext(); - -#ifdef MDNS_IP4_SUPPORT - ip_addr_t multicast_addr = DNS_MQUERY_IPV4_GROUP_INIT; -#endif -#ifdef MDNS_IP6_SUPPORT - //TODO: set multicast address (lwip_joingroup() is IPv4 only at the time of writing) - multicast_addr.addr = DNS_MQUERY_IPV6_GROUP_INIT; -#endif - if (ERR_OK == igmp_joingroup(IP4_ADDR_ANY4, ip_2_ip4(&multicast_addr))) { - m_pUDPContext = new UdpContext; - m_pUDPContext->ref(); - - if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT)) { - m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL); - m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this)); - - bResult = m_pUDPContext->connect(&multicast_addr, DNS_MQUERY_PORT); - } - } - return bResult; -} - -/* - * MDNSResponder::_releaseUDPContext - */ -bool MDNSResponder::_releaseUDPContext(void) { - - if (m_pUDPContext) { - m_pUDPContext->unref(); - m_pUDPContext = 0; - } - return true; -} - - -/* - * SERVICE QUERY - */ - -/* - * MDNSResponder::_allocServiceQuery - */ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void) { - - stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery; - if (pServiceQuery) { - // Link to query list - pServiceQuery->m_pNext = m_pServiceQueries; - m_pServiceQueries = pServiceQuery; - } - return m_pServiceQueries; -} - -/* - * MDNSResponder::_removeServiceQuery - */ -bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery) { - - bool bResult = false; - - if (p_pServiceQuery) { - stcMDNSServiceQuery* pPred = m_pServiceQueries; - while ((pPred) && - (pPred->m_pNext != p_pServiceQuery)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pServiceQuery->m_pNext; - delete p_pServiceQuery; - bResult = true; - } - else { // No predecesor - if (m_pServiceQueries == p_pServiceQuery) { - m_pServiceQueries = p_pServiceQuery->m_pNext; - delete p_pServiceQuery; - bResult = true; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseServiceQuery: INVALID service query!");); - } - } - } - return bResult; -} - -/* - * MDNSResponder::_removeLegacyServiceQuery - */ -bool MDNSResponder::_removeLegacyServiceQuery(void) { - - stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery(); - return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true); -} - -/* - * MDNSResponder::_findServiceQuery - * - * 'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existance) - * - */ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery) { - break; - } - pServiceQuery = pServiceQuery->m_pNext; - } - return pServiceQuery; -} - -/* - * MDNSResponder::_findLegacyServiceQuery - */ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void) { - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) { - if (pServiceQuery->m_bLegacyQuery) { - break; - } - pServiceQuery = pServiceQuery->m_pNext; - } - return pServiceQuery; -} - -/* - * MDNSResponder::_releaseServiceQueries - */ -bool MDNSResponder::_releaseServiceQueries(void) { - while (m_pServiceQueries) { - stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext; - delete m_pServiceQueries; - m_pServiceQueries = pNext; - } - return true; -} - -/* - * MDNSResponder::_findNextServiceQueryByServiceType - */ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceTypeDomain, - const stcMDNSServiceQuery* p_pPrevServiceQuery) { - stcMDNSServiceQuery* pMatchingServiceQuery = 0; - - stcMDNSServiceQuery* pServiceQuery = (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries); - while (pServiceQuery) { - if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain) { - pMatchingServiceQuery = pServiceQuery; - break; - } - pServiceQuery = pServiceQuery->m_pNext; - } - return pMatchingServiceQuery; -} - - -/* - * HOSTNAME - */ - -/* - * MDNSResponder::_setHostname - */ -bool MDNSResponder::_setHostname(const char* p_pcHostname) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"), p_pcHostname);); - - bool bResult = false; - - _releaseHostname(); - - size_t stLength = 0; - if ((p_pcHostname) && - (MDNS_DOMAIN_LABEL_MAXLENGTH >= (stLength = strlen(p_pcHostname)))) { // char max size for a single label - // Copy in hostname characters as lowercase - if ((bResult = (0 != (m_pcHostname = new char[stLength + 1])))) { -#ifdef MDNS_FORCE_LOWERCASE_HOSTNAME - size_t i = 0; - for (; i= strlen(p_pcName))) && - (p_pcService) && - (MDNS_SERVICE_NAME_LENGTH >= strlen(p_pcService)) && - (p_pcProtocol) && - (MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) && - (p_u16Port) && - (0 != (pService = new stcMDNSService)) && - (pService->setName(p_pcName ?: m_pcHostname)) && - (pService->setService(p_pcService)) && - (pService->setProtocol(p_pcProtocol))) { - - pService->m_bAutoName = (0 == p_pcName); - pService->m_u16Port = p_u16Port; - - // Add to list (or start list) - pService->m_pNext = m_pServices; - m_pServices = pService; - } - return pService; -} - -/* - * MDNSResponder::_releaseService - */ -bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService) { - - bool bResult = false; - - if (p_pService) { - stcMDNSService* pPred = m_pServices; - while ((pPred) && - (pPred->m_pNext != p_pService)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pService->m_pNext; - delete p_pService; - bResult = true; - } - else { // No predecesor - if (m_pServices == p_pService) { - m_pServices = p_pService->m_pNext; - delete p_pService; - bResult = true; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!");); - } - } - } - return bResult; -} - -/* - * MDNSResponder::_releaseServices - */ -bool MDNSResponder::_releaseServices(void) { - - stcMDNSService* pService = m_pServices; - while (pService) { - _releaseService(pService); - pService = m_pServices; - } - return true; -} - -/* - * MDNSResponder::_findService - */ -MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol) { - - stcMDNSService* pService = m_pServices; - while (pService) { - if ((0 == strcmp(pService->m_pcName, p_pcName)) && - (0 == strcmp(pService->m_pcService, p_pcService)) && - (0 == strcmp(pService->m_pcProtocol, p_pcProtocol))) { - - break; - } - pService = pService->m_pNext; - } - return pService; -} - -/* - * MDNSResponder::_findService - */ -MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService) { - - stcMDNSService* pService = m_pServices; - while (pService) { - if (p_hService == (hMDNSService)pService) { - break; - } - pService = pService->m_pNext; - } - return pService; -} - - -/* - * SERVICE TXT - */ - -/* - * MDNSResponder::_allocServiceTxt - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp) { - - stcMDNSServiceTxt* pTxt = 0; - - if ((p_pService) && - (p_pcKey) && - (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() + - 1 + // Length byte - (p_pcKey ? strlen(p_pcKey) : 0) + - 1 + // '=' - (p_pcValue ? strlen(p_pcValue) : 0)))) { - - pTxt = new stcMDNSServiceTxt; - if (pTxt) { - size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0); - pTxt->m_pcKey = new char[stLength + 1]; - if (pTxt->m_pcKey) { - strncpy(pTxt->m_pcKey, p_pcKey, stLength); pTxt->m_pcKey[stLength] = 0; - } - - if (p_pcValue) { - stLength = (p_pcValue ? strlen(p_pcValue) : 0); - pTxt->m_pcValue = new char[stLength + 1]; - if (pTxt->m_pcValue) { - strncpy(pTxt->m_pcValue, p_pcValue, stLength); pTxt->m_pcValue[stLength] = 0; - } - } - pTxt->m_bTemp = p_bTemp; - - // Add to list (or start list) - p_pService->m_Txts.add(pTxt); - } - } - return pTxt; -} - -/* - * MDNSResponder::_releaseServiceTxt - */ -bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService, - MDNSResponder::stcMDNSServiceTxt* p_pTxt) { - - return ((p_pService) && - (p_pTxt) && - (p_pService->m_Txts.remove(p_pTxt))); -} - -/* - * MDNSResponder::_updateServiceTxt - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService, - MDNSResponder::stcMDNSServiceTxt* p_pTxt, - const char* p_pcValue, - bool p_bTemp) { - - if ((p_pService) && - (p_pTxt) && - (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() - - (p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0) + - (p_pcValue ? strlen(p_pcValue) : 0)))) { - p_pTxt->update(p_pcValue); - p_pTxt->m_bTemp = p_bTemp; - } - return p_pTxt; -} - -/* - * MDNSResponder::_findServiceTxt - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const char* p_pcKey) { - - return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0); -} - -/* - * MDNSResponder::_findServiceTxt - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const hMDNSTxt p_hTxt) { - - return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt) : 0); -} - -/* - * MDNSResponder::_addServiceTxt - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp) { - stcMDNSServiceTxt* pResult = 0; - - if ((p_pService) && - (p_pcKey) && - (strlen(p_pcKey))) { - - stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey); - if (pTxt) { - pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp); - } - else { - pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp); - } - } - return pResult; -} - -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) { - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - // Fill m_pcTxts (if not already done) - return (pSQAnswer) ? pSQAnswer->m_Txts.m_pTxts : 0; -} - -/* - * MDNSResponder::_collectServiceTxts - */ -bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService) { - - // Call Dynamic service callbacks - if (m_fnServiceTxtCallback) { - m_fnServiceTxtCallback((hMDNSService)&p_rService); - } - if (p_rService.m_fnTxtCallback) { - p_rService.m_fnTxtCallback((hMDNSService)&p_rService); - } - return true; -} - -/* - * MDNSResponder::_releaseTempServiceTxts - */ -bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService) { - - return (p_rService.m_Txts.removeTempTxts()); -} - - -/* - * MISC - */ - -#ifdef DEBUG_ESP_MDNS_RESPONDER - /* - * MDNSResponder::_printRRDomain - */ - bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const { - - //DEBUG_OUTPUT.printf_P(PSTR("Domain: ")); - - const char* pCursor = p_RRDomain.m_acName; - uint8_t u8Length = *pCursor++; - if (u8Length) { - while (u8Length) { - for (uint8_t u=0; um_IPAddress.toString().c_str()); - break; -#endif - case DNS_RRTYPE_PTR: - DEBUG_OUTPUT.printf_P(PSTR("PTR ")); - _printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain); - break; - case DNS_RRTYPE_TXT: { - size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength(); - char* pTxts = new char[stTxtLength]; - if (pTxts) { - ((/*const c_str()!!*/stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts); - DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts); - delete[] pTxts; - } - break; - } -#ifdef MDNS_IP6_SUPPORT - case DNS_RRTYPE_AAAA: - DEBUG_OUTPUT.printf_P(PSTR("AAAA IP:%s"), ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); - break; -#endif - case DNS_RRTYPE_SRV: - DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "), ((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_u16Port); - _printRRDomain(((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_SRVDomain); - break; - default: - DEBUG_OUTPUT.printf_P(PSTR("generic ")); - break; - } - DEBUG_OUTPUT.printf_P(PSTR("\n")); - - return true; - } -#endif - -} // namespace MDNSImplementation - -} // namespace esp8266 - - - - +/* + LEAmDNS_Helpers.cpp + + License (MIT license): + 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. + +*/ + +#include "lwip/igmp.h" + +#include "LEAmDNS_lwIPdefs.h" +#include "LEAmDNS_Priv.h" + + +namespace +{ + +/* + strrstr (static) + + Backwards search for p_pcPattern in p_pcString + Based on: https://stackoverflow.com/a/1634398/2778898 + +*/ +const char* strrstr(const char*__restrict p_pcString, const char*__restrict p_pcPattern) +{ + + const char* pcResult = 0; + + size_t stStringLength = (p_pcString ? strlen(p_pcString) : 0); + size_t stPatternLength = (p_pcPattern ? strlen(p_pcPattern) : 0); + + if ((stStringLength) && + (stPatternLength) && + (stPatternLength <= stStringLength)) + { + // Pattern is shorter or has the same length tham the string + + for (const char* s = (p_pcString + stStringLength - stPatternLength); s >= p_pcString; --s) + { + if (0 == strncmp(s, p_pcPattern, stPatternLength)) + { + pcResult = s; + break; + } + } + } + return pcResult; +} + + +} // anonymous + + + + + +namespace esp8266 +{ + +/* + LEAmDNS +*/ +namespace MDNSImplementation +{ + +/** + HELPERS +*/ + +/* + MDNSResponder::indexDomain (static) + + 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 + 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 no default is given, 'esp8266' is used. + +*/ +/*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain, + const char* p_pcDivider /*= "-"*/, + const char* p_pcDefaultDomain /*= 0*/) +{ + + bool bResult = false; + + // Ensure a divider exists; use '-' as default + const char* pcDivider = (p_pcDivider ? : "-"); + + if (p_rpcDomain) + { + const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider); + if (pFoundDivider) // maybe already extended + { + char* pEnd = 0; + unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10); + if ((ulIndex) && + ((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain)) && + (!*pEnd)) // Valid (old) index found + { + + char acIndexBuffer[16]; + sprintf(acIndexBuffer, "%lu", (++ulIndex)); + size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider)) + strlen(acIndexBuffer) + 1); + char* pNewHostname = new char[stLength]; + if (pNewHostname) + { + memcpy(pNewHostname, p_rpcDomain, (pFoundDivider - p_rpcDomain + strlen(pcDivider))); + pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0; + strcat(pNewHostname, acIndexBuffer); + + delete[] p_rpcDomain; + p_rpcDomain = pNewHostname; + + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); + } + } + else + { + pFoundDivider = 0; // Flag the need to (base) extend the hostname + } + } + + 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' + char* pNewHostname = new char[stLength]; + if (pNewHostname) + { + sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider); + + delete[] p_rpcDomain; + p_rpcDomain = pNewHostname; + + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); + } + } + } + else + { + // No given host domain, use base or default + const char* cpcDefaultName = (p_pcDefaultDomain ? : "esp8266"); + + size_t stLength = strlen(cpcDefaultName) + 1; // '\0' + p_rpcDomain = new char[stLength]; + if (p_rpcDomain) + { + strncpy(p_rpcDomain, cpcDefaultName, stLength); + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); + } + } + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] indexDomain: %s\n"), p_rpcDomain);); + return bResult; +} + + +/* + UDP CONTEXT +*/ + +bool MDNSResponder::_callProcess(void) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(), m_pUDPContext->getRemoteAddress().toString().c_str());); + + return _process(false); +} + +/* + MDNSResponder::_allocUDPContext + + (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 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 + is called from the WiFi stack side of the ESP stack system. + +*/ +bool MDNSResponder::_allocUDPContext(void) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext");); + + bool bResult = false; + + _releaseUDPContext(); + +#ifdef MDNS_IP4_SUPPORT + ip_addr_t multicast_addr = DNS_MQUERY_IPV4_GROUP_INIT; +#endif +#ifdef MDNS_IP6_SUPPORT + //TODO: set multicast address (lwip_joingroup() is IPv4 only at the time of writing) + multicast_addr.addr = DNS_MQUERY_IPV6_GROUP_INIT; +#endif + if (ERR_OK == igmp_joingroup(IP4_ADDR_ANY4, ip_2_ip4(&multicast_addr))) + { + m_pUDPContext = new UdpContext; + m_pUDPContext->ref(); + + if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT)) + { + m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL); + m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this)); + + bResult = m_pUDPContext->connect(&multicast_addr, DNS_MQUERY_PORT); + } + } + return bResult; +} + +/* + MDNSResponder::_releaseUDPContext +*/ +bool MDNSResponder::_releaseUDPContext(void) +{ + + if (m_pUDPContext) + { + m_pUDPContext->unref(); + m_pUDPContext = 0; + } + return true; +} + + +/* + SERVICE QUERY +*/ + +/* + MDNSResponder::_allocServiceQuery +*/ +MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void) +{ + + stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery; + if (pServiceQuery) + { + // Link to query list + pServiceQuery->m_pNext = m_pServiceQueries; + m_pServiceQueries = pServiceQuery; + } + return m_pServiceQueries; +} + +/* + MDNSResponder::_removeServiceQuery +*/ +bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery) +{ + + bool bResult = false; + + if (p_pServiceQuery) + { + stcMDNSServiceQuery* pPred = m_pServiceQueries; + while ((pPred) && + (pPred->m_pNext != p_pServiceQuery)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pServiceQuery->m_pNext; + delete p_pServiceQuery; + bResult = true; + } + else // No predecesor + { + if (m_pServiceQueries == p_pServiceQuery) + { + m_pServiceQueries = p_pServiceQuery->m_pNext; + delete p_pServiceQuery; + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseServiceQuery: INVALID service query!");); + } + } + } + return bResult; +} + +/* + MDNSResponder::_removeLegacyServiceQuery +*/ +bool MDNSResponder::_removeLegacyServiceQuery(void) +{ + + stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery(); + return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true); +} + +/* + MDNSResponder::_findServiceQuery + + 'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existance) + +*/ +MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) +{ + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery) + { + break; + } + pServiceQuery = pServiceQuery->m_pNext; + } + return pServiceQuery; +} + +/* + MDNSResponder::_findLegacyServiceQuery +*/ +MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void) +{ + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) + { + if (pServiceQuery->m_bLegacyQuery) + { + break; + } + pServiceQuery = pServiceQuery->m_pNext; + } + return pServiceQuery; +} + +/* + MDNSResponder::_releaseServiceQueries +*/ +bool MDNSResponder::_releaseServiceQueries(void) +{ + while (m_pServiceQueries) + { + stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext; + delete m_pServiceQueries; + m_pServiceQueries = pNext; + } + return true; +} + +/* + MDNSResponder::_findNextServiceQueryByServiceType +*/ +MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceTypeDomain, + const stcMDNSServiceQuery* p_pPrevServiceQuery) +{ + stcMDNSServiceQuery* pMatchingServiceQuery = 0; + + stcMDNSServiceQuery* pServiceQuery = (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries); + while (pServiceQuery) + { + if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain) + { + pMatchingServiceQuery = pServiceQuery; + break; + } + pServiceQuery = pServiceQuery->m_pNext; + } + return pMatchingServiceQuery; +} + + +/* + HOSTNAME +*/ + +/* + MDNSResponder::_setHostname +*/ +bool MDNSResponder::_setHostname(const char* p_pcHostname) +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"), p_pcHostname);); + + bool bResult = false; + + _releaseHostname(); + + size_t stLength = 0; + if ((p_pcHostname) && + (MDNS_DOMAIN_LABEL_MAXLENGTH >= (stLength = strlen(p_pcHostname)))) // char max size for a single label + { + // Copy in hostname characters as lowercase + if ((bResult = (0 != (m_pcHostname = new char[stLength + 1])))) + { +#ifdef MDNS_FORCE_LOWERCASE_HOSTNAME + size_t i = 0; + for (; i < stLength; ++i) + { + m_pcHostname[i] = (isupper(p_pcHostname[i]) ? tolower(p_pcHostname[i]) : p_pcHostname[i]); + } + m_pcHostname[i] = 0; +#else + strncpy(m_pcHostname, p_pcHostname, (stLength + 1)); +#endif + } + } + return bResult; +} + +/* + MDNSResponder::_releaseHostname +*/ +bool MDNSResponder::_releaseHostname(void) +{ + + if (m_pcHostname) + { + delete[] m_pcHostname; + m_pcHostname = 0; + } + return true; +} + + +/* + SERVICE +*/ + +/* + MDNSResponder::_allocService +*/ +MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol, + uint16_t p_u16Port) +{ + + stcMDNSService* pService = 0; + if (((!p_pcName) || + (MDNS_DOMAIN_LABEL_MAXLENGTH >= strlen(p_pcName))) && + (p_pcService) && + (MDNS_SERVICE_NAME_LENGTH >= strlen(p_pcService)) && + (p_pcProtocol) && + (MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) && + (p_u16Port) && + (0 != (pService = new stcMDNSService)) && + (pService->setName(p_pcName ? : m_pcHostname)) && + (pService->setService(p_pcService)) && + (pService->setProtocol(p_pcProtocol))) + { + + pService->m_bAutoName = (0 == p_pcName); + pService->m_u16Port = p_u16Port; + + // Add to list (or start list) + pService->m_pNext = m_pServices; + m_pServices = pService; + } + return pService; +} + +/* + MDNSResponder::_releaseService +*/ +bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService) +{ + + bool bResult = false; + + if (p_pService) + { + stcMDNSService* pPred = m_pServices; + while ((pPred) && + (pPred->m_pNext != p_pService)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pService->m_pNext; + delete p_pService; + bResult = true; + } + else // No predecesor + { + if (m_pServices == p_pService) + { + m_pServices = p_pService->m_pNext; + delete p_pService; + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!");); + } + } + } + return bResult; +} + +/* + MDNSResponder::_releaseServices +*/ +bool MDNSResponder::_releaseServices(void) +{ + + stcMDNSService* pService = m_pServices; + while (pService) + { + _releaseService(pService); + pService = m_pServices; + } + return true; +} + +/* + MDNSResponder::_findService +*/ +MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol) +{ + + stcMDNSService* pService = m_pServices; + while (pService) + { + if ((0 == strcmp(pService->m_pcName, p_pcName)) && + (0 == strcmp(pService->m_pcService, p_pcService)) && + (0 == strcmp(pService->m_pcProtocol, p_pcProtocol))) + { + + break; + } + pService = pService->m_pNext; + } + return pService; +} + +/* + MDNSResponder::_findService +*/ +MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService) +{ + + stcMDNSService* pService = m_pServices; + while (pService) + { + if (p_hService == (hMDNSService)pService) + { + break; + } + pService = pService->m_pNext; + } + return pService; +} + + +/* + SERVICE TXT +*/ + +/* + MDNSResponder::_allocServiceTxt +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService, + const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp) +{ + + stcMDNSServiceTxt* pTxt = 0; + + if ((p_pService) && + (p_pcKey) && + (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() + + 1 + // Length byte + (p_pcKey ? strlen(p_pcKey) : 0) + + 1 + // '=' + (p_pcValue ? strlen(p_pcValue) : 0)))) + { + + pTxt = new stcMDNSServiceTxt; + if (pTxt) + { + size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0); + pTxt->m_pcKey = new char[stLength + 1]; + if (pTxt->m_pcKey) + { + strncpy(pTxt->m_pcKey, p_pcKey, stLength); pTxt->m_pcKey[stLength] = 0; + } + + if (p_pcValue) + { + stLength = (p_pcValue ? strlen(p_pcValue) : 0); + pTxt->m_pcValue = new char[stLength + 1]; + if (pTxt->m_pcValue) + { + strncpy(pTxt->m_pcValue, p_pcValue, stLength); pTxt->m_pcValue[stLength] = 0; + } + } + pTxt->m_bTemp = p_bTemp; + + // Add to list (or start list) + p_pService->m_Txts.add(pTxt); + } + } + return pTxt; +} + +/* + MDNSResponder::_releaseServiceTxt +*/ +bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService, + MDNSResponder::stcMDNSServiceTxt* p_pTxt) +{ + + return ((p_pService) && + (p_pTxt) && + (p_pService->m_Txts.remove(p_pTxt))); +} + +/* + MDNSResponder::_updateServiceTxt +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService, + MDNSResponder::stcMDNSServiceTxt* p_pTxt, + const char* p_pcValue, + bool p_bTemp) +{ + + if ((p_pService) && + (p_pTxt) && + (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() - + (p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0) + + (p_pcValue ? strlen(p_pcValue) : 0)))) + { + p_pTxt->update(p_pcValue); + p_pTxt->m_bTemp = p_bTemp; + } + return p_pTxt; +} + +/* + MDNSResponder::_findServiceTxt +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, + const char* p_pcKey) +{ + + return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0); +} + +/* + MDNSResponder::_findServiceTxt +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, + const hMDNSTxt p_hTxt) +{ + + return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt) : 0); +} + +/* + MDNSResponder::_addServiceTxt +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService, + const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp) +{ + stcMDNSServiceTxt* pResult = 0; + + if ((p_pService) && + (p_pcKey) && + (strlen(p_pcKey))) + { + + stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey); + if (pTxt) + { + pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp); + } + else + { + pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp); + } + } + return pResult; +} + +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) +{ + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + // Fill m_pcTxts (if not already done) + return (pSQAnswer) ? pSQAnswer->m_Txts.m_pTxts : 0; +} + +/* + MDNSResponder::_collectServiceTxts +*/ +bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService) +{ + + // Call Dynamic service callbacks + if (m_fnServiceTxtCallback) + { + m_fnServiceTxtCallback((hMDNSService)&p_rService); + } + if (p_rService.m_fnTxtCallback) + { + p_rService.m_fnTxtCallback((hMDNSService)&p_rService); + } + return true; +} + +/* + MDNSResponder::_releaseTempServiceTxts +*/ +bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService) +{ + + return (p_rService.m_Txts.removeTempTxts()); +} + + +/* + MISC +*/ + +#ifdef DEBUG_ESP_MDNS_RESPONDER +/* + MDNSResponder::_printRRDomain +*/ +bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const +{ + + //DEBUG_OUTPUT.printf_P(PSTR("Domain: ")); + + const char* pCursor = p_RRDomain.m_acName; + uint8_t u8Length = *pCursor++; + if (u8Length) + { + while (u8Length) + { + for (uint8_t u = 0; u < u8Length; ++u) + { + DEBUG_OUTPUT.printf_P(PSTR("%c"), *(pCursor++)); + } + u8Length = *pCursor++; + if (u8Length) + { + DEBUG_OUTPUT.printf_P(PSTR(".")); + } + } + } + else // empty domain + { + DEBUG_OUTPUT.printf_P(PSTR("-empty-")); + } + //DEBUG_OUTPUT.printf_P(PSTR("\n")); + + return true; +} + +/* + MDNSResponder::_printRRAnswer +*/ +bool MDNSResponder::_printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const +{ + + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] RRAnswer: ")); + _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); + switch (p_RRAnswer.m_Header.m_Attributes.m_u16Type & (~0x8000)) // Topmost bit might carry 'cache flush' flag + { +#ifdef MDNS_IP4_SUPPORT + case DNS_RRTYPE_A: + DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((const stcMDNS_RRAnswerA*)&p_RRAnswer)->m_IPAddress.toString().c_str()); + break; +#endif + case DNS_RRTYPE_PTR: + DEBUG_OUTPUT.printf_P(PSTR("PTR ")); + _printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain); + break; + case DNS_RRTYPE_TXT: + { + size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength(); + char* pTxts = new char[stTxtLength]; + if (pTxts) + { + ((/*const c_str()!!*/stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts); + DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts); + delete[] pTxts; + } + break; + } +#ifdef MDNS_IP6_SUPPORT + case DNS_RRTYPE_AAAA: + DEBUG_OUTPUT.printf_P(PSTR("AAAA IP:%s"), ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); + break; +#endif + case DNS_RRTYPE_SRV: + DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "), ((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_u16Port); + _printRRDomain(((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_SRVDomain); + break; + default: + DEBUG_OUTPUT.printf_P(PSTR("generic ")); + break; + } + DEBUG_OUTPUT.printf_P(PSTR("\n")); + + return true; +} +#endif + +} // namespace MDNSImplementation + +} // namespace esp8266 + + + + diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h b/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h index 76ed927291..ddf75c715b 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h @@ -1,177 +1,179 @@ -/* - * LEAmDNS_Priv.h - * - * License (MIT license): - * 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. - * - */ - -#ifndef MDNS_PRIV_H -#define MDNS_PRIV_H - -namespace esp8266 { - -/* - * LEAmDNS - */ - -namespace MDNSImplementation { - -// Enable class debug functions -#define ESP_8266_MDNS_INCLUDE -//#define DEBUG_ESP_MDNS_RESPONDER - - -#ifndef LWIP_OPEN_SRC - #define LWIP_OPEN_SRC -#endif - -// -// If ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE is defined, the mDNS responder ignores a successful probing -// This allows to drive the responder in a environment, where 'update()' isn't called in the loop -//#define ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE - -// Enable/disable debug trace macros -#ifdef DEBUG_ESP_MDNS_RESPONDER -#define DEBUG_ESP_MDNS_INFO -#define DEBUG_ESP_MDNS_ERR -#define DEBUG_ESP_MDNS_TX -#define DEBUG_ESP_MDNS_RX -#endif - -#ifdef DEBUG_ESP_MDNS_RESPONDER - #ifdef DEBUG_ESP_MDNS_INFO - #define DEBUG_EX_INFO(A) A - #else - #define DEBUG_EX_INFO(A) - #endif - #ifdef DEBUG_ESP_MDNS_ERR - #define DEBUG_EX_ERR(A) A - #else - #define DEBUG_EX_ERR(A) - #endif - #ifdef DEBUG_ESP_MDNS_TX - #define DEBUG_EX_TX(A) A - #else - #define DEBUG_EX_TX(A) - #endif - #ifdef DEBUG_ESP_MDNS_RX - #define DEBUG_EX_RX(A) A - #else - #define DEBUG_EX_RX(A) - #endif - - #ifdef DEBUG_ESP_PORT - #define DEBUG_OUTPUT DEBUG_ESP_PORT - #else - #define DEBUG_OUTPUT Serial - #endif -#else - #define DEBUG_EX_INFO(A) - #define DEBUG_EX_ERR(A) - #define DEBUG_EX_TX(A) - #define DEBUG_EX_RX(A) -#endif - - -/* Replaced by 'lwip/prot/dns.h' definitions -#ifdef MDNS_IP4_SUPPORT - #define MDNS_MULTICAST_ADDR_IP4 (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT -#endif -#ifdef MDNS_IP6_SUPPORT - #define MDNS_MULTICAST_ADDR_IP6 (IPAddress("FF02::FB")) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT -#endif*/ -//#define MDNS_MULTICAST_PORT 5353 - -/* - * This is NOT the TTL (Time-To-Live) for MDNS records, but the - * subnet level distance MDNS records should travel. - * 1 sets the subnet distance to 'local', which is default for MDNS. - * (Btw.: 255 would set it to 'as far as possible' -> internet) - * - * However, RFC 3171 seems to force 255 instead - */ -#define MDNS_MULTICAST_TTL 255/*1*/ - -/* - * This is the MDNS record TTL - * Host level records are set to 2min (120s) - * service level records are set to 75min (4500s) - */ -#define MDNS_HOST_TTL 120 -#define MDNS_SERVICE_TTL 4500 - -/* - * Compressed labels are flaged by the two topmost bits of the length byte being set - */ -#define MDNS_DOMAIN_COMPRESS_MARK 0xC0 -/* - * Avoid endless recursion because of malformed compressed labels - */ -#define MDNS_DOMAIN_MAX_REDIRCTION 6 - -/* - * Default service priority and weight in SRV answers - */ -#define MDNS_SRV_PRIORITY 0 -#define MDNS_SRV_WEIGHT 0 - -/* - * 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 service queries; the delay is multiplied by the resent number in '_checkServiceQueryCache' - */ -#define MDNS_PROBE_DELAY 250 -#define MDNS_PROBE_COUNT 3 -#define MDNS_ANNOUNCE_DELAY 1000 -#define MDNS_ANNOUNCE_COUNT 8 -#define MDNS_DYNAMIC_QUERY_RESEND_COUNT 5 -#define MDNS_DYNAMIC_QUERY_RESEND_DELAY 5000 - - -/* - * Force host domain to use only lowercase letters - */ -//#define MDNS_FORCE_LOWERCASE_HOSTNAME - -/* - * 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. - * - * USE_PGM_PRINTF and F - */ -#define USE_PGM_PRINTF - -#ifdef USE_PGM_PRINTF -#else - #ifdef F - #undef F - #endif - #define F(A) A -#endif - -} // namespace MDNSImplementation - -} // namespace esp8266 - -// Include the main header, so the submodlues only need to include this header -#include "LEAmDNS.h" - - -#endif // MDNS_PRIV_H +/* + LEAmDNS_Priv.h + + License (MIT license): + 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. + +*/ + +#ifndef MDNS_PRIV_H +#define MDNS_PRIV_H + +namespace esp8266 +{ + +/* + LEAmDNS +*/ + +namespace MDNSImplementation +{ + +// Enable class debug functions +#define ESP_8266_MDNS_INCLUDE +//#define DEBUG_ESP_MDNS_RESPONDER + + +#ifndef LWIP_OPEN_SRC +#define LWIP_OPEN_SRC +#endif + +// +// If ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE is defined, the mDNS responder ignores a successful probing +// This allows to drive the responder in a environment, where 'update()' isn't called in the loop +//#define ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE + +// Enable/disable debug trace macros +#ifdef DEBUG_ESP_MDNS_RESPONDER +#define DEBUG_ESP_MDNS_INFO +#define DEBUG_ESP_MDNS_ERR +#define DEBUG_ESP_MDNS_TX +#define DEBUG_ESP_MDNS_RX +#endif + +#ifdef DEBUG_ESP_MDNS_RESPONDER +#ifdef DEBUG_ESP_MDNS_INFO +#define DEBUG_EX_INFO(A) A +#else +#define DEBUG_EX_INFO(A) +#endif +#ifdef DEBUG_ESP_MDNS_ERR +#define DEBUG_EX_ERR(A) A +#else +#define DEBUG_EX_ERR(A) +#endif +#ifdef DEBUG_ESP_MDNS_TX +#define DEBUG_EX_TX(A) A +#else +#define DEBUG_EX_TX(A) +#endif +#ifdef DEBUG_ESP_MDNS_RX +#define DEBUG_EX_RX(A) A +#else +#define DEBUG_EX_RX(A) +#endif + +#ifdef DEBUG_ESP_PORT +#define DEBUG_OUTPUT DEBUG_ESP_PORT +#else +#define DEBUG_OUTPUT Serial +#endif +#else +#define DEBUG_EX_INFO(A) +#define DEBUG_EX_ERR(A) +#define DEBUG_EX_TX(A) +#define DEBUG_EX_RX(A) +#endif + + +/* Replaced by 'lwip/prot/dns.h' definitions + #ifdef MDNS_IP4_SUPPORT + #define MDNS_MULTICAST_ADDR_IP4 (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT + #endif + #ifdef MDNS_IP6_SUPPORT + #define MDNS_MULTICAST_ADDR_IP6 (IPAddress("FF02::FB")) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT + #endif*/ +//#define MDNS_MULTICAST_PORT 5353 + +/* + This is NOT the TTL (Time-To-Live) for MDNS records, but the + subnet level distance MDNS records should travel. + 1 sets the subnet distance to 'local', which is default for MDNS. + (Btw.: 255 would set it to 'as far as possible' -> internet) + + However, RFC 3171 seems to force 255 instead +*/ +#define MDNS_MULTICAST_TTL 255/*1*/ + +/* + This is the MDNS record TTL + Host level records are set to 2min (120s) + service level records are set to 75min (4500s) +*/ +#define MDNS_HOST_TTL 120 +#define MDNS_SERVICE_TTL 4500 + +/* + Compressed labels are flaged by the two topmost bits of the length byte being set +*/ +#define MDNS_DOMAIN_COMPRESS_MARK 0xC0 +/* + Avoid endless recursion because of malformed compressed labels +*/ +#define MDNS_DOMAIN_MAX_REDIRCTION 6 + +/* + Default service priority and weight in SRV answers +*/ +#define MDNS_SRV_PRIORITY 0 +#define MDNS_SRV_WEIGHT 0 + +/* + 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 service queries; the delay is multiplied by the resent number in '_checkServiceQueryCache' +*/ +#define MDNS_PROBE_DELAY 250 +#define MDNS_PROBE_COUNT 3 +#define MDNS_ANNOUNCE_DELAY 1000 +#define MDNS_ANNOUNCE_COUNT 8 +#define MDNS_DYNAMIC_QUERY_RESEND_COUNT 5 +#define MDNS_DYNAMIC_QUERY_RESEND_DELAY 5000 + + +/* + Force host domain to use only lowercase letters +*/ +//#define MDNS_FORCE_LOWERCASE_HOSTNAME + +/* + 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. + + USE_PGM_PRINTF and F +*/ +#define USE_PGM_PRINTF + +#ifdef USE_PGM_PRINTF +#else +#ifdef F +#undef F +#endif +#define F(A) A +#endif + +} // namespace MDNSImplementation + +} // namespace esp8266 + +// Include the main header, so the submodlues only need to include this header +#include "LEAmDNS.h" + + +#endif // MDNS_PRIV_H diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp index e54beb50db..ce475de3ba 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp @@ -1,2218 +1,2476 @@ -/* - * LEAmDNS_Structs.cpp - * - * License (MIT license): - * 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. - * - */ - -#include "LEAmDNS_Priv.h" -#include "LEAmDNS_lwIPdefs.h" - -namespace esp8266 { - -/* - * LEAmDNS - */ -namespace MDNSImplementation { - -/** - * STRUCTS - */ - -/** - * MDNSResponder::stcMDNSServiceTxt - * - * One MDNS TXT item. - * m_pcValue may be '\0'. - * Objects can be chained together (list, m_pNext). - * A 'm_bTemp' flag differentiates between static and dynamic items. - * Output as byte array 'c#=1' is supported. - */ - -/* - * MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt constructor - */ -MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const char* p_pcKey /*= 0*/, - const char* p_pcValue /*= 0*/, - bool p_bTemp /*= false*/) -: m_pNext(0), - m_pcKey(0), - m_pcValue(0), - m_bTemp(p_bTemp) { - - setKey(p_pcKey); - setValue(p_pcValue); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt copy-constructor - */ -MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const MDNSResponder::stcMDNSServiceTxt& p_Other) -: m_pNext(0), - m_pcKey(0), - m_pcValue(0), - m_bTemp(false) { - - operator=(p_Other); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt destructor - */ -MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::operator= - */ -MDNSResponder::stcMDNSServiceTxt& MDNSResponder::stcMDNSServiceTxt::operator=(const MDNSResponder::stcMDNSServiceTxt& p_Other) { - - if (&p_Other != this) { - clear(); - set(p_Other.m_pcKey, p_Other.m_pcValue, p_Other.m_bTemp); - } - return *this; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::clear - */ -bool MDNSResponder::stcMDNSServiceTxt::clear(void) { - - releaseKey(); - releaseValue(); - return true; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::allocKey - */ -char* MDNSResponder::stcMDNSServiceTxt::allocKey(size_t p_stLength) { - - releaseKey(); - if (p_stLength) { - m_pcKey = new char[p_stLength + 1]; - } - return m_pcKey; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::setKey - */ -bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey, - size_t p_stLength) { - - bool bResult = false; - - releaseKey(); - if (p_stLength) { - if (allocKey(p_stLength)) { - strncpy(m_pcKey, p_pcKey, p_stLength); - m_pcKey[p_stLength] = 0; - bResult = true; - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::setKey - */ -bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey) { - - return setKey(p_pcKey, (p_pcKey ? strlen(p_pcKey) : 0)); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::releaseKey - */ -bool MDNSResponder::stcMDNSServiceTxt::releaseKey(void) { - - if (m_pcKey) { - delete[] m_pcKey; - m_pcKey = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::allocValue - */ -char* MDNSResponder::stcMDNSServiceTxt::allocValue(size_t p_stLength) { - - releaseValue(); - if (p_stLength) { - m_pcValue = new char[p_stLength + 1]; - } - return m_pcValue; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::setValue - */ -bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue, - size_t p_stLength) { - - bool bResult = false; - - releaseValue(); - if (p_stLength) { - if (allocValue(p_stLength)) { - strncpy(m_pcValue, p_pcValue, p_stLength); - m_pcValue[p_stLength] = 0; - bResult = true; - } - } - else { // No value -> also OK - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::setValue - */ -bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue) { - - return setValue(p_pcValue, (p_pcValue ? strlen(p_pcValue) : 0)); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::releaseValue - */ -bool MDNSResponder::stcMDNSServiceTxt::releaseValue(void) { - - if (m_pcValue) { - delete[] m_pcValue; - m_pcValue = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceTxt::set - */ -bool MDNSResponder::stcMDNSServiceTxt::set(const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp /*= false*/) { - - m_bTemp = p_bTemp; - return ((setKey(p_pcKey)) && - (setValue(p_pcValue))); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::update - */ -bool MDNSResponder::stcMDNSServiceTxt::update(const char* p_pcValue) { - - return setValue(p_pcValue); -} - -/* - * MDNSResponder::stcMDNSServiceTxt::length - * - * length of eg. 'c#=1' without any closing '\0' - */ -size_t MDNSResponder::stcMDNSServiceTxt::length(void) const { - - size_t stLength = 0; - if (m_pcKey) { - stLength += strlen(m_pcKey); // Key - stLength += 1; // '=' - stLength += (m_pcValue ? strlen(m_pcValue) : 0); // Value - } - return stLength; -} - - -/** - * MDNSResponder::stcMDNSServiceTxts - * - * A list of zero or more MDNS TXT items. - * Dynamic TXT items can be removed by 'removeTempTxts'. - * A TXT item can be looke up by its 'key' member. - * Export as ';'-separated byte array is supported. - * Export as 'length byte coded' byte array is supported. - * Comparision ((all A TXT items in B and equal) AND (all B TXT items in A and equal)) is supported. - * - */ - -/* - * MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts contructor - */ -MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(void) -: m_pTxts(0) { - -} - -/* - * MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts copy-constructor - */ -MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other) -: m_pTxts(0) { - - operator=(p_Other); -} - -/* - * MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts destructor - */ -MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSServiceTxts::operator= - */ -MDNSResponder::stcMDNSServiceTxts& MDNSResponder::stcMDNSServiceTxts::operator=(const stcMDNSServiceTxts& p_Other) { - - if (this != &p_Other) { - clear(); - - for (stcMDNSServiceTxt* pOtherTxt=p_Other.m_pTxts; pOtherTxt; pOtherTxt=pOtherTxt->m_pNext) { - add(new stcMDNSServiceTxt(*pOtherTxt)); - } - } - return *this; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::clear - */ -bool MDNSResponder::stcMDNSServiceTxts::clear(void) { - - while (m_pTxts) { - stcMDNSServiceTxt* pNext = m_pTxts->m_pNext; - delete m_pTxts; - m_pTxts = pNext; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::add - */ -bool MDNSResponder::stcMDNSServiceTxts::add(MDNSResponder::stcMDNSServiceTxt* p_pTxt) { - - bool bResult = false; - - if (p_pTxt) { - p_pTxt->m_pNext = m_pTxts; - m_pTxts = p_pTxt; - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::remove - */ -bool MDNSResponder::stcMDNSServiceTxts::remove(stcMDNSServiceTxt* p_pTxt) { - - bool bResult = false; - - if (p_pTxt) { - stcMDNSServiceTxt* pPred = m_pTxts; - while ((pPred) && - (pPred->m_pNext != p_pTxt)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pTxt->m_pNext; - delete p_pTxt; - bResult = true; - } - else if (m_pTxts == p_pTxt) { // No predecesor, but first item - m_pTxts = p_pTxt->m_pNext; - delete p_pTxt; - bResult = true; - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::removeTempTxts - */ -bool MDNSResponder::stcMDNSServiceTxts::removeTempTxts(void) { - - bool bResult = true; - - stcMDNSServiceTxt* pTxt = m_pTxts; - while ((bResult) && - (pTxt)) { - stcMDNSServiceTxt* pNext = pTxt->m_pNext; - if (pTxt->m_bTemp) { - bResult = remove(pTxt); - } - pTxt = pNext; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::find - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) { - - stcMDNSServiceTxt* pResult = 0; - - for (stcMDNSServiceTxt* pTxt=m_pTxts; pTxt; pTxt=pTxt->m_pNext) { - if ((p_pcKey) && - (0 == strcmp(pTxt->m_pcKey, p_pcKey))) { - pResult = pTxt; - break; - } - } - return pResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::find - */ -const MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) const { - - const stcMDNSServiceTxt* pResult = 0; - - for (const stcMDNSServiceTxt* pTxt=m_pTxts; pTxt; pTxt=pTxt->m_pNext) { - if ((p_pcKey) && - (0 == strcmp(pTxt->m_pcKey, p_pcKey))) { - - pResult = pTxt; - break; - } - } - return pResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::find - */ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const stcMDNSServiceTxt* p_pTxt) { - - stcMDNSServiceTxt* pResult = 0; - - for (stcMDNSServiceTxt* pTxt=m_pTxts; pTxt; pTxt=pTxt->m_pNext) { - if (p_pTxt == pTxt) { - pResult = pTxt; - break; - } - } - return pResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::length - */ -uint16_t MDNSResponder::stcMDNSServiceTxts::length(void) const { - - uint16_t u16Length = 0; - - stcMDNSServiceTxt* pTxt = m_pTxts; - while (pTxt) { - u16Length += 1; // Length byte - u16Length += pTxt->length(); // Text - pTxt = pTxt->m_pNext; - } - return u16Length; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::c_strLength - * - * (incl. closing '\0'). Length bytes place is used for delimiting ';' and closing '\0' - */ -size_t MDNSResponder::stcMDNSServiceTxts::c_strLength(void) const { - - return length(); -} - -/* - * MDNSResponder::stcMDNSServiceTxts::c_str - */ -bool MDNSResponder::stcMDNSServiceTxts::c_str(char* p_pcBuffer) { - - bool bResult = false; - - if (p_pcBuffer) { - bResult = true; - - *p_pcBuffer = 0; - for (stcMDNSServiceTxt* pTxt=m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) { - size_t stLength; - if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) { - if (pTxt != m_pTxts) { - *p_pcBuffer++ = ';'; - } - strncpy(p_pcBuffer, pTxt->m_pcKey, stLength); p_pcBuffer[stLength] = 0; - p_pcBuffer += stLength; - *p_pcBuffer++ = '='; - if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) { - strncpy(p_pcBuffer, pTxt->m_pcValue, stLength); p_pcBuffer[stLength] = 0; - p_pcBuffer += stLength; - } - } - } - *p_pcBuffer++ = 0; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::bufferLength - * - * (incl. closing '\0'). - */ -size_t MDNSResponder::stcMDNSServiceTxts::bufferLength(void) const { - - return (length() + 1); -} - -/* - * MDNSResponder::stcMDNSServiceTxts::toBuffer - */ -bool MDNSResponder::stcMDNSServiceTxts::buffer(char* p_pcBuffer) { - - bool bResult = false; - - if (p_pcBuffer) { - bResult = true; - - *p_pcBuffer = 0; - for (stcMDNSServiceTxt* pTxt=m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) { - *(unsigned char*)p_pcBuffer++ = pTxt->length(); - size_t stLength; - if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) { - memcpy(p_pcBuffer, pTxt->m_pcKey, stLength); - p_pcBuffer += stLength; - *p_pcBuffer++ = '='; - if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) { - memcpy(p_pcBuffer, pTxt->m_pcValue, stLength); - p_pcBuffer += stLength; - } - } - } - *p_pcBuffer++ = 0; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::compare - */ -bool MDNSResponder::stcMDNSServiceTxts::compare(const MDNSResponder::stcMDNSServiceTxts& p_Other) const { - - bool bResult = false; - - if ((bResult = (length() == p_Other.length()))) { - // Compare A->B - for (const stcMDNSServiceTxt* pTxt=m_pTxts; ((bResult) && (pTxt)); pTxt=pTxt->m_pNext) { - const stcMDNSServiceTxt* pOtherTxt = p_Other.find(pTxt->m_pcKey); - bResult = ((pOtherTxt) && - (pTxt->m_pcValue) && - (pOtherTxt->m_pcValue) && - (strlen(pTxt->m_pcValue) == strlen(pOtherTxt->m_pcValue)) && - (0 == strcmp(pTxt->m_pcValue, pOtherTxt->m_pcValue))); - } - // Compare B->A - for (const stcMDNSServiceTxt* pOtherTxt=p_Other.m_pTxts; ((bResult) && (pOtherTxt)); pOtherTxt=pOtherTxt->m_pNext) { - const stcMDNSServiceTxt* pTxt = find(pOtherTxt->m_pcKey); - bResult = ((pTxt) && - (pOtherTxt->m_pcValue) && - (pTxt->m_pcValue) && - (strlen(pOtherTxt->m_pcValue) == strlen(pTxt->m_pcValue)) && - (0 == strcmp(pOtherTxt->m_pcValue, pTxt->m_pcValue))); - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceTxts::operator== - */ -bool MDNSResponder::stcMDNSServiceTxts::operator==(const stcMDNSServiceTxts& p_Other) const { - - return compare(p_Other); -} - -/* - * MDNSResponder::stcMDNSServiceTxts::operator!= - */ -bool MDNSResponder::stcMDNSServiceTxts::operator!=(const stcMDNSServiceTxts& p_Other) const { - - return !compare(p_Other); -} - - -/** - * MDNSResponder::stcMDNS_MsgHeader - * - * A MDNS message haeder. - * - */ - -/* - * MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader - */ -MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader(uint16_t p_u16ID /*= 0*/, - bool p_bQR /*= false*/, - unsigned char p_ucOpcode /*= 0*/, - bool p_bAA /*= false*/, - bool p_bTC /*= false*/, - bool p_bRD /*= false*/, - bool p_bRA /*= false*/, - unsigned char p_ucRCode /*= 0*/, - uint16_t p_u16QDCount /*= 0*/, - uint16_t p_u16ANCount /*= 0*/, - uint16_t p_u16NSCount /*= 0*/, - uint16_t p_u16ARCount /*= 0*/) -: m_u16ID(p_u16ID), - m_1bQR(p_bQR), m_4bOpcode(p_ucOpcode), m_1bAA(p_bAA), m_1bTC(p_bTC), m_1bRD(p_bRD), - m_1bRA(p_bRA), m_3bZ(0), m_4bRCode(p_ucRCode), - m_u16QDCount(p_u16QDCount), - m_u16ANCount(p_u16ANCount), - m_u16NSCount(p_u16NSCount), - m_u16ARCount(p_u16ARCount) { - -} - - -/** - * MDNSResponder::stcMDNS_RRDomain - * - * A MDNS domain object. - * The labels of the domain are stored (DNS-like encoded) in 'm_acName': - * [length byte]varlength label[length byte]varlength label[0] - * 'm_u16NameLength' stores the used length of 'm_acName'. - * Dynamic label addition is supported. - * Comparison is supported. - * Export as byte array 'esp8266.local' is supported. - * - */ - -/* - * MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain constructor - */ -MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(void) -: m_u16NameLength(0) { - - clear(); -} - -/* - * MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain copy-constructor - */ -MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other) -: m_u16NameLength(0) { - - operator=(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRDomain::operator = - */ -MDNSResponder::stcMDNS_RRDomain& MDNSResponder::stcMDNS_RRDomain::operator=(const stcMDNS_RRDomain& p_Other) { - - if (&p_Other != this) { - memcpy(m_acName, p_Other.m_acName, sizeof(m_acName)); - m_u16NameLength = p_Other.m_u16NameLength; - } - return *this; -} - -/* - * MDNSResponder::stcMDNS_RRDomain::clear - */ -bool MDNSResponder::stcMDNS_RRDomain::clear(void) { - - memset(m_acName, 0, sizeof(m_acName)); - m_u16NameLength = 0; - return true; -} - -/* - * MDNSResponder::stcMDNS_RRDomain::addLabel - */ -bool MDNSResponder::stcMDNS_RRDomain::addLabel(const char* p_pcLabel, - bool p_bPrependUnderline /*= false*/) { - - bool bResult = false; - - size_t stLength = (p_pcLabel - ? (strlen(p_pcLabel) + (p_bPrependUnderline ? 1 : 0)) - : 0); - if ((MDNS_DOMAIN_LABEL_MAXLENGTH >= stLength) && - (MDNS_DOMAIN_MAXLENGTH >= (m_u16NameLength + (1 + stLength)))) { - // Length byte - m_acName[m_u16NameLength] = (unsigned char)stLength; // Might be 0! - ++m_u16NameLength; - // Label - if (stLength) { - if (p_bPrependUnderline) { - m_acName[m_u16NameLength++] = '_'; - --stLength; - } - strncpy(&(m_acName[m_u16NameLength]), p_pcLabel, stLength); m_acName[m_u16NameLength + stLength] = 0; - m_u16NameLength += stLength; - } - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNS_RRDomain::compare - */ -bool MDNSResponder::stcMDNS_RRDomain::compare(const stcMDNS_RRDomain& p_Other) const { - - bool bResult = false; - - if (m_u16NameLength == p_Other.m_u16NameLength) { - const char* pT = m_acName; - const char* pO = p_Other.m_acName; - while ((pT) && - (pO) && - (*((unsigned char*)pT) == *((unsigned char*)pO)) && // Same length AND - (0 == strncasecmp((pT + 1), (pO + 1), *((unsigned char*)pT)))) { // Same content - if (*((unsigned char*)pT)) { // Not 0 - pT += (1 + *((unsigned char*)pT)); // Shift by length byte and lenght - pO += (1 + *((unsigned char*)pO)); - } - else { // Is 0 -> Successfully reached the end - bResult = true; - break; - } - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNS_RRDomain::operator == - */ -bool MDNSResponder::stcMDNS_RRDomain::operator==(const stcMDNS_RRDomain& p_Other) const { - - return compare(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRDomain::operator != - */ -bool MDNSResponder::stcMDNS_RRDomain::operator!=(const stcMDNS_RRDomain& p_Other) const { - - return !compare(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRDomain::operator > - */ -bool MDNSResponder::stcMDNS_RRDomain::operator>(const stcMDNS_RRDomain& p_Other) const { - - // TODO: Check, if this is a good idea... - return !compare(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRDomain::c_strLength - */ -size_t MDNSResponder::stcMDNS_RRDomain::c_strLength(void) const { - - size_t stLength = 0; - - unsigned char* pucLabelLength = (unsigned char*)m_acName; - while (*pucLabelLength) { - stLength += (*pucLabelLength + 1 /* +1 for '.' or '\0'*/); - pucLabelLength += (*pucLabelLength + 1); - } - return stLength; -} - -/* - * MDNSResponder::stcMDNS_RRDomain::c_str - */ -bool MDNSResponder::stcMDNS_RRDomain::c_str(char* p_pcBuffer) { - - bool bResult = false; - - if (p_pcBuffer) { - *p_pcBuffer = 0; - unsigned char* pucLabelLength = (unsigned char*)m_acName; - while (*pucLabelLength) { - memcpy(p_pcBuffer, (const char*)(pucLabelLength + 1), *pucLabelLength); - p_pcBuffer += *pucLabelLength; - pucLabelLength += (*pucLabelLength + 1); - *p_pcBuffer++ = (*pucLabelLength ? '.' : '\0'); - } - bResult = true; - } - return bResult; -} - - -/** - * MDNSResponder::stcMDNS_RRAttributes - * - * A MDNS attributes object. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes constructor - */ -MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(uint16_t p_u16Type /*= 0*/, - uint16_t p_u16Class /*= 1 DNS_RRCLASS_IN Internet*/) -: m_u16Type(p_u16Type), - m_u16Class(p_u16Class) { - -} - -/* - * MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes copy-constructor - */ -MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Other) { - - operator=(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRAttributes::operator = - */ -MDNSResponder::stcMDNS_RRAttributes& MDNSResponder::stcMDNS_RRAttributes::operator=(const MDNSResponder::stcMDNS_RRAttributes& p_Other) { - - if (&p_Other != this) { - m_u16Type = p_Other.m_u16Type; - m_u16Class = p_Other.m_u16Class; - } - return *this; -} - - -/** - * MDNSResponder::stcMDNS_RRHeader - * - * A MDNS record header (domain and attributes) object. - * - */ - -/* - * MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader constructor - */ -MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(void) { - -} - -/* - * MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader copy-constructor - */ -MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other) { - - operator=(p_Other); -} - -/* - * MDNSResponder::stcMDNS_RRHeader::operator = - */ -MDNSResponder::stcMDNS_RRHeader& MDNSResponder::stcMDNS_RRHeader::operator=(const MDNSResponder::stcMDNS_RRHeader& p_Other) { - - if (&p_Other != this) { - m_Domain = p_Other.m_Domain; - m_Attributes = p_Other.m_Attributes; - } - return *this; -} - -/* - * MDNSResponder::stcMDNS_RRHeader::clear - */ -bool MDNSResponder::stcMDNS_RRHeader::clear(void) { - - m_Domain.clear(); - return true; -} - - -/** - * MDNSResponder::stcMDNS_RRQuestion - * - * A MDNS question record object (header + question flags) - * - */ - -/* - * MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion constructor - */ -MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion(void) -: m_pNext(0), - m_bUnicast(false) { - -} - - -/** - * MDNSResponder::stcMDNS_RRAnswer - * - * A MDNS answer record object (header + answer content). - * This is a 'virtual' base class for all other MDNS answer classes. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer constructor - */ -MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer(enuAnswerType p_AnswerType, - const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) -: m_pNext(0), - m_AnswerType(p_AnswerType), - m_Header(p_Header), - m_u32TTL(p_u32TTL) { - - // Extract 'cache flush'-bit - m_bCacheFlush = (m_Header.m_Attributes.m_u16Class & 0x8000); - m_Header.m_Attributes.m_u16Class &= (~0x8000); -} - -/* - * MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer destructor - */ -MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer(void) { - -} - -/* - * MDNSResponder::stcMDNS_RRAnswer::answerType - */ -MDNSResponder::enuAnswerType MDNSResponder::stcMDNS_RRAnswer::answerType(void) const { - - return m_AnswerType; -} - -/* - * MDNSResponder::stcMDNS_RRAnswer::clear - */ -bool MDNSResponder::stcMDNS_RRAnswer::clear(void) { - - m_pNext = 0; - m_Header.clear(); - return true; -} - - -/** - * MDNSResponder::stcMDNS_RRAnswerA - * - * A MDNS A answer object. - * Extends the base class by an IP4 address member. - * - */ - -#ifdef MDNS_IP4_SUPPORT - /* - * MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA constructor - */ - MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) - : stcMDNS_RRAnswer(AnswerType_A, p_Header, p_u32TTL), - m_IPAddress(0, 0, 0, 0) { - - } - - /* - * MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA destructor - */ - MDNSResponder::stcMDNS_RRAnswerA::~stcMDNS_RRAnswerA(void) { - - clear(); - } - - /* - * MDNSResponder::stcMDNS_RRAnswerA::clear - */ - bool MDNSResponder::stcMDNS_RRAnswerA::clear(void) { - - m_IPAddress = IPAddress(0, 0, 0, 0); - return true; - } -#endif - - -/** - * MDNSResponder::stcMDNS_RRAnswerPTR - * - * A MDNS PTR answer object. - * Extends the base class by a MDNS domain member. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR constructor - */ -MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) -: stcMDNS_RRAnswer(AnswerType_PTR, p_Header, p_u32TTL) { - -} - -/* - * MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR destructor - */ -MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNS_RRAnswerPTR::clear - */ -bool MDNSResponder::stcMDNS_RRAnswerPTR::clear(void) { - - m_PTRDomain.clear(); - return true; -} - - -/** - * MDNSResponder::stcMDNS_RRAnswerTXT - * - * A MDNS TXT answer object. - * Extends the base class by a MDNS TXT items list member. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT constructor - */ -MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) -: stcMDNS_RRAnswer(AnswerType_TXT, p_Header, p_u32TTL) { - -} - -/* - * MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT destructor - */ -MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNS_RRAnswerTXT::clear - */ -bool MDNSResponder::stcMDNS_RRAnswerTXT::clear(void) { - - m_Txts.clear(); - return true; -} - - -/** - * MDNSResponder::stcMDNS_RRAnswerAAAA - * - * A MDNS AAAA answer object. - * (Should) extend the base class by an IP6 address member. - * - */ - -#ifdef MDNS_IP6_SUPPORT - /* - * MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA constructor - */ - MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) - : stcMDNS_RRAnswer(AnswerType_AAAA, p_Header, p_u32TTL) { - - } - - /* - * MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA destructor - */ - MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA(void) { - - clear(); - } - - /* - * MDNSResponder::stcMDNS_RRAnswerAAAA::clear - */ - bool MDNSResponder::stcMDNS_RRAnswerAAAA::clear(void) { - - return true; - } -#endif - - -/** - * MDNSResponder::stcMDNS_RRAnswerSRV - * - * A MDNS SRV answer object. - * Extends the base class by a port member. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV constructor - */ -MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) -: stcMDNS_RRAnswer(AnswerType_SRV, p_Header, p_u32TTL), - m_u16Priority(0), - m_u16Weight(0), - m_u16Port(0) { - -} - -/* - * MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV destructor - */ -MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNS_RRAnswerSRV::clear - */ -bool MDNSResponder::stcMDNS_RRAnswerSRV::clear(void) { - - m_u16Priority = 0; - m_u16Weight = 0; - m_u16Port = 0; - m_SRVDomain.clear(); - return true; -} - - -/** - * MDNSResponder::stcMDNS_RRAnswerGeneric - * - * An unknown (generic) MDNS answer object. - * Extends the base class by a RDATA buffer member. - * - */ - -/* - * MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric constructor - */ -MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) -: stcMDNS_RRAnswer(AnswerType_Generic, p_Header, p_u32TTL), - m_u16RDLength(0), - m_pu8RDData(0) { - -} - -/* - * MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric destructor - */ -MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNS_RRAnswerGeneric::clear - */ -bool MDNSResponder::stcMDNS_RRAnswerGeneric::clear(void) { - - if (m_pu8RDData) { - delete[] m_pu8RDData; - m_pu8RDData = 0; - } - m_u16RDLength = 0; - - return true; -} - - -/** - * MDNSResponder::stcProbeInformation - * - * Probing status information for a host or service domain - * - */ - -/* - * MDNSResponder::stcProbeInformation::stcProbeInformation constructor - */ -MDNSResponder::stcProbeInformation::stcProbeInformation(void) -: m_ProbingStatus(ProbingStatus_WaitingForData), - m_u8SentCount(0), - m_Timeout(esp8266::polledTimeout::oneShotMs::neverExpires), - m_bConflict(false), - m_bTiebreakNeeded(false), - m_fnHostProbeResultCallback(0), - m_fnServiceProbeResultCallback(0) { -} - -/* - * MDNSResponder::stcProbeInformation::clear - */ -bool MDNSResponder::stcProbeInformation::clear(bool p_bClearUserdata /*= false*/) { - - m_ProbingStatus = ProbingStatus_WaitingForData; - m_u8SentCount = 0; - m_Timeout.resetToNeverExpires(); - m_bConflict = false; - m_bTiebreakNeeded = false; - if (p_bClearUserdata) { - m_fnHostProbeResultCallback = 0; - m_fnServiceProbeResultCallback = 0; - } - return true; -} - -/** - * MDNSResponder::stcMDNSService - * - * A MDNS service object (to be announced by the MDNS responder) - * The service instance may be '\0'; in this case the hostname is used - * and the flag m_bAutoName is set. If the hostname changes, all 'auto- - * named' services are renamed also. - * m_u8Replymask is used while preparing a response to a MDNS query. It is - * resetted in '_sendMDNSMessage' afterwards. - */ - -/* - * MDNSResponder::stcMDNSService::stcMDNSService constructor - */ -MDNSResponder::stcMDNSService::stcMDNSService(const char* p_pcName /*= 0*/, - const char* p_pcService /*= 0*/, - const char* p_pcProtocol /*= 0*/) -: m_pNext(0), - m_pcName(0), - m_bAutoName(false), - m_pcService(0), - m_pcProtocol(0), - m_u16Port(0), - m_u8ReplyMask(0), - m_fnTxtCallback(0) { - - setName(p_pcName); - setService(p_pcService); - setProtocol(p_pcProtocol); -} - -/* - * MDNSResponder::stcMDNSService::~stcMDNSService destructor - */ -MDNSResponder::stcMDNSService::~stcMDNSService(void) { - - releaseName(); - releaseService(); - releaseProtocol(); -} - -/* - * MDNSResponder::stcMDNSService::setName - */ -bool MDNSResponder::stcMDNSService::setName(const char* p_pcName) { - - bool bResult = false; - - releaseName(); - size_t stLength = (p_pcName ? strlen(p_pcName) : 0); - if (stLength) { - if ((bResult = (0 != (m_pcName = new char[stLength + 1])))) { - strncpy(m_pcName, p_pcName, stLength); - m_pcName[stLength] = 0; - } - } - else { - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSService::releaseName - */ -bool MDNSResponder::stcMDNSService::releaseName(void) { - - if (m_pcName) { - delete[] m_pcName; - m_pcName = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSService::setService - */ -bool MDNSResponder::stcMDNSService::setService(const char* p_pcService) { - - bool bResult = false; - - releaseService(); - size_t stLength = (p_pcService ? strlen(p_pcService) : 0); - if (stLength) { - if ((bResult = (0 != (m_pcService = new char[stLength + 1])))) { - strncpy(m_pcService, p_pcService, stLength); - m_pcService[stLength] = 0; - } - } - else { - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSService::releaseService - */ -bool MDNSResponder::stcMDNSService::releaseService(void) { - - if (m_pcService) { - delete[] m_pcService; - m_pcService = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSService::setProtocol - */ -bool MDNSResponder::stcMDNSService::setProtocol(const char* p_pcProtocol) { - - bool bResult = false; - - releaseProtocol(); - size_t stLength = (p_pcProtocol ? strlen(p_pcProtocol) : 0); - if (stLength) { - if ((bResult = (0 != (m_pcProtocol = new char[stLength + 1])))) { - strncpy(m_pcProtocol, p_pcProtocol, stLength); - m_pcProtocol[stLength] = 0; - } - } - else { - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSService::releaseProtocol - */ -bool MDNSResponder::stcMDNSService::releaseProtocol(void) { - - if (m_pcProtocol) { - delete[] m_pcProtocol; - m_pcProtocol = 0; - } - return true; -} - - -/** - * MDNSResponder::stcMDNSServiceQuery - * - * A MDNS service query object. - * Service queries may be static or dynamic. - * As the static service query is processed in the blocking function 'queryService', - * only one static service service may exist. The processing of the answers is done - * on the WiFi-stack side of the ESP stack structure (via 'UDPContext.onRx(_update)'). - * - */ - -/** - * MDNSResponder::stcMDNSServiceQuery::stcAnswer - * - * One answer for a service query. - * Every answer must contain - * - a service instance entry (pivot), - * and may contain - * - a host domain, - * - a port - * - an IP4 address - * (- an IP6 address) - * - a MDNS TXTs - * The existance of a component is flaged in 'm_u32ContentFlags'. - * For every answer component a TTL value is maintained. - * Answer objects can be connected to a linked list. - * - * For the host domain, service domain and TXTs components, a char array - * representation can be retrieved (which is created on demand). - * - */ - -/** - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL - * - * The TTL (Time-To-Live) for an specific answer content. - * The 80% and outdated states are calculated based on the current time (millis) - * and the 'set' time (also millis). - * If the answer is scheduled for an update, the corresponding flag should be set. - * - * / - -/ * - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor - * / -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(uint32_t p_u32TTL / *= 0* /) -: m_bUpdateScheduled(false) { - - set(p_u32TTL * 1000); -} - -/ * - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set - * / -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) { - - m_TTLTimeFlag.restart(p_u32TTL * 1000); - m_bUpdateScheduled = false; - - return true; -} - -/ * - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent - * / -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent(void) const { - - return ((m_TTLTimeFlag.getTimeout()) && - (!m_bUpdateScheduled) && - (m_TTLTimeFlag.hypotheticalTimeout((m_TTLTimeFlag.getTimeout() * 800) / 1000))); -} - -/ * - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated - * / -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated(void) const { - - return ((m_TTLTimeFlag.getTimeout()) && - (m_TTLTimeFlag.flagged())); -}*/ - - -/** - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL - * - * The TTL (Time-To-Live) for an specific answer content. - * The 80% and outdated states are calculated based on the current time (millis) - * and the 'set' time (also millis). - * If the answer is scheduled for an update, the corresponding flag should be set. - * - */ - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(void) -: m_u32TTL(0), - m_TTLTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), - m_timeoutLevel(TIMEOUTLEVEL_UNSET) { - -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) { - - m_u32TTL = p_u32TTL; - if (m_u32TTL) { - m_timeoutLevel = TIMEOUTLEVEL_BASE; // Set to 80% - m_TTLTimeout.reset(timeout()); - } - else { - m_timeoutLevel = TIMEOUTLEVEL_UNSET; // undef - m_TTLTimeout.resetToNeverExpires(); - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) { - - return ((m_u32TTL) && - (TIMEOUTLEVEL_UNSET != m_timeoutLevel) && - (m_TTLTimeout.expired())); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart(void) { - - bool bResult = true; - - if ((TIMEOUTLEVEL_BASE <= m_timeoutLevel) && // >= 80% AND - (TIMEOUTLEVEL_FINAL > m_timeoutLevel)) { // < 100% - - m_timeoutLevel += TIMEOUTLEVEL_INTERVAL; // increment by 5% - m_TTLTimeout.reset(timeout()); - } - else { - bResult = false; - m_TTLTimeout.resetToNeverExpires(); - m_timeoutLevel = TIMEOUTLEVEL_UNSET; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion(void) { - - m_timeoutLevel = TIMEOUTLEVEL_FINAL; - m_TTLTimeout.reset(1 * 1000); // See RFC 6762, 10.1 - - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel(void) const { - - return (TIMEOUTLEVEL_FINAL == m_timeoutLevel); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout - */ -unsigned long MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout(void) const { - - uint32_t u32Timeout = esp8266::polledTimeout::oneShotMs::neverExpires; - - if (TIMEOUTLEVEL_BASE == m_timeoutLevel) { // 80% - u32Timeout = (m_u32TTL * 800); // to milliseconds - } - else if ((TIMEOUTLEVEL_BASE < m_timeoutLevel) && // >80% AND - (TIMEOUTLEVEL_FINAL >= m_timeoutLevel)) { // <= 100% - - u32Timeout = (m_u32TTL * 50); - } // else: invalid - return u32Timeout; -} - - -#ifdef MDNS_IP4_SUPPORT -/** - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address - * - */ - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address constructor - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address(IPAddress p_IPAddress, - uint32_t p_u32TTL /*= 0*/) -: m_pNext(0), - m_IPAddress(p_IPAddress) { - - m_TTL.set(p_u32TTL); -} -#endif - - -/** - * MDNSResponder::stcMDNSServiceQuery::stcAnswer - */ - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer constructor - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer(void) -: m_pNext(0), - m_pcServiceDomain(0), - m_pcHostDomain(0), - m_u16Port(0), - m_pcTxts(0), -#ifdef MDNS_IP4_SUPPORT - m_pIP4Addresses(0), -#endif -#ifdef MDNS_IP6_SUPPORT - m_pIP6Addresses(0), -#endif - m_u32ContentFlags(0) { -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer destructor - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear(void) { - - return ((releaseTxts()) && -#ifdef MDNS_IP4_SUPPORT - (releaseIP4Addresses()) && -#endif -#ifdef MDNS_IP6_SUPPORT - (releaseIP6Addresses()) -#endif - (releaseHostDomain()) && - (releaseServiceDomain())); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain - * - * Alloc memory for the char array representation of the service domain. - * - */ -char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain(size_t p_stLength) { - - releaseServiceDomain(); - if (p_stLength) { - m_pcServiceDomain = new char[p_stLength]; - } - return m_pcServiceDomain; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain(void) { - - if (m_pcServiceDomain) { - delete[] m_pcServiceDomain; - m_pcServiceDomain = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain - * - * Alloc memory for the char array representation of the host domain. - * - */ -char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain(size_t p_stLength) { - - releaseHostDomain(); - if (p_stLength) { - m_pcHostDomain = new char[p_stLength]; - } - return m_pcHostDomain; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain(void) { - - if (m_pcHostDomain) { - delete[] m_pcHostDomain; - m_pcHostDomain = 0; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts - * - * Alloc memory for the char array representation of the TXT items. - * - */ -char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts(size_t p_stLength) { - - releaseTxts(); - if (p_stLength) { - m_pcTxts = new char[p_stLength]; - } - return m_pcTxts; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts(void) { - - if (m_pcTxts) { - delete[] m_pcTxts; - m_pcTxts = 0; - } - return true; -} - -#ifdef MDNS_IP4_SUPPORT -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses(void) { - - while (m_pIP4Addresses) { - stcIP4Address* pNext = m_pIP4Addresses->m_pNext; - delete m_pIP4Addresses; - m_pIP4Addresses = pNext; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) { - - bool bResult = false; - - if (p_pIP4Address) { - p_pIP4Address->m_pNext = m_pIP4Addresses; - m_pIP4Addresses = p_pIP4Address; - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) { - - bool bResult = false; - - if (p_pIP4Address) { - stcIP4Address* pPred = m_pIP4Addresses; - while ((pPred) && - (pPred->m_pNext != p_pIP4Address)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pIP4Address->m_pNext; - delete p_pIP4Address; - bResult = true; - } - else if (m_pIP4Addresses == p_pIP4Address) { // No predecesor, but first item - m_pIP4Addresses = p_pIP4Address->m_pNext; - delete p_pIP4Address; - bResult = true; - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address (const) - */ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) const { - - return (stcIP4Address*)(((const stcAnswer*)this)->findIP4Address(p_IPAddress)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) { - - stcIP4Address* pIP4Address = m_pIP4Addresses; - while (pIP4Address) { - if (pIP4Address->m_IPAddress == p_IPAddress) { - break; - } - pIP4Address = pIP4Address->m_pNext; - } - return pIP4Address; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount - */ -uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount(void) const { - - uint32_t u32Count = 0; - - stcIP4Address* pIP4Address = m_pIP4Addresses; - while (pIP4Address) { - ++u32Count; - pIP4Address = pIP4Address->m_pNext; - } - return u32Count; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) { - - return (stcIP4Address*)(((const stcAnswer*)this)->IP4AddressAtIndex(p_u32Index)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex (const) - */ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) const { - - const stcIP4Address* pIP4Address = 0; - - if (((uint32_t)(-1) != p_u32Index) && - (m_pIP4Addresses)) { - - uint32_t u32Index; - for (pIP4Address=m_pIP4Addresses, u32Index=0; ((pIP4Address) && (u32Indexm_pNext, ++u32Index); - } - return pIP4Address; -} -#endif - -#ifdef MDNS_IP6_SUPPORT -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses(void) { - - while (m_pIP6Addresses) { - stcIP6Address* pNext = m_pIP6Addresses->m_pNext; - delete m_pIP6Addresses; - m_pIP6Addresses = pNext; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) { - - bool bResult = false; - - if (p_pIP6Address) { - p_pIP6Address->m_pNext = m_pIP6Addresses; - m_pIP6Addresses = p_pIP6Address; - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) { - - bool bResult = false; - - if (p_pIP6Address) { - stcIP6Address* pPred = m_pIP6Addresses; - while ((pPred) && - (pPred->m_pNext != p_pIP6Address)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pIP6Address->m_pNext; - delete p_pIP6Address; - bResult = true; - } - else if (m_pIP6Addresses == p_pIP6Address) { // No predecesor, but first item - m_pIP6Addresses = p_pIP6Address->m_pNext; - delete p_pIP6Address; - bResult = true; - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IP6Address& p_IPAddress) { - - return (stcIP6Address*)(((const stcAnswer*)this)->findIP6Address(p_IPAddress)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address (const) - */ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IPAddress& p_IPAddress) const { - - const stcIP6Address* pIP6Address = m_pIP6Addresses; - while (pIP6Address) { - if (p_IP6Address->m_IPAddress == p_IPAddress) { - break; - } - pIP6Address = pIP6Address->m_pNext; - } - return pIP6Address; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount - */ -uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount(void) const { - - uint32_t u32Count = 0; - - stcIP6Address* pIP6Address = m_pIP6Addresses; - while (pIP6Address) { - ++u32Count; - pIP6Address = pIP6Address->m_pNext; - } - return u32Count; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex (const) - */ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) const { - - return (stcIP6Address*)(((const stcAnswer*)this)->IP6AddressAtIndex(p_u32Index)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) { - - stcIP6Address* pIP6Address = 0; - - if (((uint32_t)(-1) != p_u32Index) && - (m_pIP6Addresses)) { - - uint32_t u32Index; - for (pIP6Address=m_pIP6Addresses, u32Index=0; ((pIP6Address) && (u32Indexm_pNext, ++u32Index); - } - return pIP6Address; -} -#endif - - -/** - * MDNSResponder::stcMDNSServiceQuery - * - * A service query object. - * A static query is flaged via 'm_bLegacyQuery'; while the function 'queryService' - * is waiting for answers, the internal flag 'm_bAwaitingAnswers' is set. When the - * timeout is reached, the flag is removed. These two flags are only used for static - * service queries. - * All answers to the service query are stored in 'm_pAnswers' list. - * Individual answers may be addressed by index (in the list of answers). - * Every time a answer component is added (or changes) in a dynamic service query, - * the callback 'm_fnCallback' is called. - * The answer list may be searched by service and host domain. - * - * Service query object may be connected to a linked list. - */ - -/* - * MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery constructor - */ -MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery(void) -: m_pNext(0), - m_fnCallback(0), - m_bLegacyQuery(false), - m_u8SentCount(0), - m_ResendTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), - m_bAwaitingAnswers(true), - m_pAnswers(0) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery destructor - */ -MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::clear - */ -bool MDNSResponder::stcMDNSServiceQuery::clear(void) { - - m_fnCallback = 0; - m_bLegacyQuery = false; - m_u8SentCount = 0; - m_ResendTimeout.resetToNeverExpires(); - m_bAwaitingAnswers = true; - while (m_pAnswers) { - stcAnswer* pNext = m_pAnswers->m_pNext; - delete m_pAnswers; - m_pAnswers = pNext; - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::answerCount - */ -uint32_t MDNSResponder::stcMDNSServiceQuery::answerCount(void) const { - - uint32_t u32Count = 0; - - stcAnswer* pAnswer = m_pAnswers; - while (pAnswer) { - ++u32Count; - pAnswer = pAnswer->m_pNext; - } - return u32Count; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::answerAtIndex - */ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) const { - - const stcAnswer* pAnswer = 0; - - if (((uint32_t)(-1) != p_u32Index) && - (m_pAnswers)) { - - uint32_t u32Index; - for (pAnswer=m_pAnswers, u32Index=0; ((pAnswer) && (u32Indexm_pNext, ++u32Index); - } - return pAnswer; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::answerAtIndex - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) { - - return (stcAnswer*)(((const stcMDNSServiceQuery*)this)->answerAtIndex(p_u32Index)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::indexOfAnswer - */ -uint32_t MDNSResponder::stcMDNSServiceQuery::indexOfAnswer(const MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) const { - - uint32_t u32Index = 0; - - for (const stcAnswer* pAnswer=m_pAnswers; pAnswer; pAnswer=pAnswer->m_pNext, ++u32Index) { - if (pAnswer == p_pAnswer) { - return u32Index; - } - } - return ((uint32_t)(-1)); -} - -/* - * MDNSResponder::stcMDNSServiceQuery::addAnswer - */ -bool MDNSResponder::stcMDNSServiceQuery::addAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) { - - bool bResult = false; - - if (p_pAnswer) { - p_pAnswer->m_pNext = m_pAnswers; - m_pAnswers = p_pAnswer; - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::removeAnswer - */ -bool MDNSResponder::stcMDNSServiceQuery::removeAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) { - - bool bResult = false; - - if (p_pAnswer) { - stcAnswer* pPred = m_pAnswers; - while ((pPred) && - (pPred->m_pNext != p_pAnswer)) { - pPred = pPred->m_pNext; - } - if (pPred) { - pPred->m_pNext = p_pAnswer->m_pNext; - delete p_pAnswer; - bResult = true; - } - else if (m_pAnswers == p_pAnswer) { // No predecesor, but first item - m_pAnswers = p_pAnswer->m_pNext; - delete p_pAnswer; - bResult = true; - } - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain(const MDNSResponder::stcMDNS_RRDomain& p_ServiceDomain) { - - stcAnswer* pAnswer = m_pAnswers; - while (pAnswer) { - if (pAnswer->m_ServiceDomain == p_ServiceDomain) { - break; - } - pAnswer = pAnswer->m_pNext; - } - return pAnswer; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain - */ -MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain(const MDNSResponder::stcMDNS_RRDomain& p_HostDomain) { - - stcAnswer* pAnswer = m_pAnswers; - while (pAnswer) { - if (pAnswer->m_HostDomain == p_HostDomain) { - break; - } - pAnswer = pAnswer->m_pNext; - } - return pAnswer; -} - - -/** - * MDNSResponder::stcMDNSSendParameter - * - * A 'collection' of properties and flags for one MDNS query or response. - * Mainly managed by the 'Control' functions. - * The current offset in the UPD output buffer is tracked to be able to do - * a simple host or service domain compression. - * - */ - -/** - * MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem - * - * A cached host or service domain, incl. the offset in the UDP output buffer. - * - */ - -/* - * MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem constructor - */ -MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint32_t p_u16Offset) -: m_pNext(0), - m_pHostnameOrService(p_pHostnameOrService), - m_bAdditionalData(p_bAdditionalData), - m_u16Offset(p_u16Offset) { - -} - -/** - * MDNSResponder::stcMDNSSendParameter - */ - -/* - * MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter constructor - */ -MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter(void) -: m_pQuestions(0), - m_pDomainCacheItems(0) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter destructor - */ -MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter(void) { - - clear(); -} - -/* - * MDNSResponder::stcMDNSSendParameter::clear - */ -bool MDNSResponder::stcMDNSSendParameter::clear(void) { - - m_u16ID = 0; - m_u8HostReplyMask = 0; - m_u16Offset = 0; - - m_bLegacyQuery = false; - m_bResponse = false; - m_bAuthorative = false; - m_bUnicast = false; - m_bUnannounce = false; - - m_bCacheFlush = true; - - while (m_pQuestions) { - stcMDNS_RRQuestion* pNext = m_pQuestions->m_pNext; - delete m_pQuestions; - m_pQuestions = pNext; - } - while (m_pDomainCacheItems) { - stcDomainCacheItem* pNext = m_pDomainCacheItems->m_pNext; - delete m_pDomainCacheItems; - m_pDomainCacheItems = pNext; - } - return true; -} - -/* - * MDNSResponder::stcMDNSSendParameter::shiftOffset - */ -bool MDNSResponder::stcMDNSSendParameter::shiftOffset(uint16_t p_u16Shift) { - - m_u16Offset += p_u16Shift; - return true; -} - -/* - * MDNSResponder::stcMDNSSendParameter::addDomainCacheItem - */ -bool MDNSResponder::stcMDNSSendParameter::addDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint16_t p_u16Offset) { - - bool bResult = false; - - stcDomainCacheItem* pNewItem = 0; - if ((p_pHostnameOrService) && - (p_u16Offset) && - ((pNewItem = new stcDomainCacheItem(p_pHostnameOrService, p_bAdditionalData, p_u16Offset)))) { - - pNewItem->m_pNext = m_pDomainCacheItems; - bResult = ((m_pDomainCacheItems = pNewItem)); - } - return bResult; -} - -/* - * MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset - */ -uint16_t MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset(const void* p_pHostnameOrService, - bool p_bAdditionalData) const { - - const stcDomainCacheItem* pCacheItem = m_pDomainCacheItems; - - for (; pCacheItem; pCacheItem=pCacheItem->m_pNext) { - if ((pCacheItem->m_pHostnameOrService == p_pHostnameOrService) && - (pCacheItem->m_bAdditionalData == p_bAdditionalData)) { // Found cache item - break; - } - } - return (pCacheItem ? pCacheItem->m_u16Offset : 0); -} - -} // namespace MDNSImplementation - -} // namespace esp8266 - - - +/* + LEAmDNS_Structs.cpp + + License (MIT license): + 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. + +*/ + +#include "LEAmDNS_Priv.h" +#include "LEAmDNS_lwIPdefs.h" + +namespace esp8266 +{ + +/* + LEAmDNS +*/ +namespace MDNSImplementation +{ + +/** + STRUCTS +*/ + +/** + MDNSResponder::stcMDNSServiceTxt + + One MDNS TXT item. + m_pcValue may be '\0'. + Objects can be chained together (list, m_pNext). + A 'm_bTemp' flag differentiates between static and dynamic items. + Output as byte array 'c#=1' is supported. +*/ + +/* + MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt constructor +*/ +MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const char* p_pcKey /*= 0*/, + const char* p_pcValue /*= 0*/, + bool p_bTemp /*= false*/) + : m_pNext(0), + m_pcKey(0), + m_pcValue(0), + m_bTemp(p_bTemp) +{ + + setKey(p_pcKey); + setValue(p_pcValue); +} + +/* + MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt copy-constructor +*/ +MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const MDNSResponder::stcMDNSServiceTxt& p_Other) + : m_pNext(0), + m_pcKey(0), + m_pcValue(0), + m_bTemp(false) +{ + + operator=(p_Other); +} + +/* + MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt destructor +*/ +MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSServiceTxt::operator= +*/ +MDNSResponder::stcMDNSServiceTxt& MDNSResponder::stcMDNSServiceTxt::operator=(const MDNSResponder::stcMDNSServiceTxt& p_Other) +{ + + if (&p_Other != this) + { + clear(); + set(p_Other.m_pcKey, p_Other.m_pcValue, p_Other.m_bTemp); + } + return *this; +} + +/* + MDNSResponder::stcMDNSServiceTxt::clear +*/ +bool MDNSResponder::stcMDNSServiceTxt::clear(void) +{ + + releaseKey(); + releaseValue(); + return true; +} + +/* + MDNSResponder::stcMDNSServiceTxt::allocKey +*/ +char* MDNSResponder::stcMDNSServiceTxt::allocKey(size_t p_stLength) +{ + + releaseKey(); + if (p_stLength) + { + m_pcKey = new char[p_stLength + 1]; + } + return m_pcKey; +} + +/* + MDNSResponder::stcMDNSServiceTxt::setKey +*/ +bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey, + size_t p_stLength) +{ + + bool bResult = false; + + releaseKey(); + if (p_stLength) + { + if (allocKey(p_stLength)) + { + strncpy(m_pcKey, p_pcKey, p_stLength); + m_pcKey[p_stLength] = 0; + bResult = true; + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxt::setKey +*/ +bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey) +{ + + return setKey(p_pcKey, (p_pcKey ? strlen(p_pcKey) : 0)); +} + +/* + MDNSResponder::stcMDNSServiceTxt::releaseKey +*/ +bool MDNSResponder::stcMDNSServiceTxt::releaseKey(void) +{ + + if (m_pcKey) + { + delete[] m_pcKey; + m_pcKey = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceTxt::allocValue +*/ +char* MDNSResponder::stcMDNSServiceTxt::allocValue(size_t p_stLength) +{ + + releaseValue(); + if (p_stLength) + { + m_pcValue = new char[p_stLength + 1]; + } + return m_pcValue; +} + +/* + MDNSResponder::stcMDNSServiceTxt::setValue +*/ +bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue, + size_t p_stLength) +{ + + bool bResult = false; + + releaseValue(); + if (p_stLength) + { + if (allocValue(p_stLength)) + { + strncpy(m_pcValue, p_pcValue, p_stLength); + m_pcValue[p_stLength] = 0; + bResult = true; + } + } + else // No value -> also OK + { + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxt::setValue +*/ +bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue) +{ + + return setValue(p_pcValue, (p_pcValue ? strlen(p_pcValue) : 0)); +} + +/* + MDNSResponder::stcMDNSServiceTxt::releaseValue +*/ +bool MDNSResponder::stcMDNSServiceTxt::releaseValue(void) +{ + + if (m_pcValue) + { + delete[] m_pcValue; + m_pcValue = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceTxt::set +*/ +bool MDNSResponder::stcMDNSServiceTxt::set(const char* p_pcKey, + const char* p_pcValue, + bool p_bTemp /*= false*/) +{ + + m_bTemp = p_bTemp; + return ((setKey(p_pcKey)) && + (setValue(p_pcValue))); +} + +/* + MDNSResponder::stcMDNSServiceTxt::update +*/ +bool MDNSResponder::stcMDNSServiceTxt::update(const char* p_pcValue) +{ + + return setValue(p_pcValue); +} + +/* + MDNSResponder::stcMDNSServiceTxt::length + + length of eg. 'c#=1' without any closing '\0' +*/ +size_t MDNSResponder::stcMDNSServiceTxt::length(void) const +{ + + size_t stLength = 0; + if (m_pcKey) + { + stLength += strlen(m_pcKey); // Key + stLength += 1; // '=' + stLength += (m_pcValue ? strlen(m_pcValue) : 0); // Value + } + return stLength; +} + + +/** + MDNSResponder::stcMDNSServiceTxts + + A list of zero or more MDNS TXT items. + Dynamic TXT items can be removed by 'removeTempTxts'. + A TXT item can be looke up by its 'key' member. + Export as ';'-separated byte array is supported. + Export as 'length byte coded' byte array is supported. + Comparision ((all A TXT items in B and equal) AND (all B TXT items in A and equal)) is supported. + +*/ + +/* + MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts contructor +*/ +MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(void) + : m_pTxts(0) +{ + +} + +/* + MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts copy-constructor +*/ +MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other) + : m_pTxts(0) +{ + + operator=(p_Other); +} + +/* + MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts destructor +*/ +MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSServiceTxts::operator= +*/ +MDNSResponder::stcMDNSServiceTxts& MDNSResponder::stcMDNSServiceTxts::operator=(const stcMDNSServiceTxts& p_Other) +{ + + if (this != &p_Other) + { + clear(); + + for (stcMDNSServiceTxt* pOtherTxt = p_Other.m_pTxts; pOtherTxt; pOtherTxt = pOtherTxt->m_pNext) + { + add(new stcMDNSServiceTxt(*pOtherTxt)); + } + } + return *this; +} + +/* + MDNSResponder::stcMDNSServiceTxts::clear +*/ +bool MDNSResponder::stcMDNSServiceTxts::clear(void) +{ + + while (m_pTxts) + { + stcMDNSServiceTxt* pNext = m_pTxts->m_pNext; + delete m_pTxts; + m_pTxts = pNext; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceTxts::add +*/ +bool MDNSResponder::stcMDNSServiceTxts::add(MDNSResponder::stcMDNSServiceTxt* p_pTxt) +{ + + bool bResult = false; + + if (p_pTxt) + { + p_pTxt->m_pNext = m_pTxts; + m_pTxts = p_pTxt; + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::remove +*/ +bool MDNSResponder::stcMDNSServiceTxts::remove(stcMDNSServiceTxt* p_pTxt) +{ + + bool bResult = false; + + if (p_pTxt) + { + stcMDNSServiceTxt* pPred = m_pTxts; + while ((pPred) && + (pPred->m_pNext != p_pTxt)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pTxt->m_pNext; + delete p_pTxt; + bResult = true; + } + else if (m_pTxts == p_pTxt) // No predecesor, but first item + { + m_pTxts = p_pTxt->m_pNext; + delete p_pTxt; + bResult = true; + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::removeTempTxts +*/ +bool MDNSResponder::stcMDNSServiceTxts::removeTempTxts(void) +{ + + bool bResult = true; + + stcMDNSServiceTxt* pTxt = m_pTxts; + while ((bResult) && + (pTxt)) + { + stcMDNSServiceTxt* pNext = pTxt->m_pNext; + if (pTxt->m_bTemp) + { + bResult = remove(pTxt); + } + pTxt = pNext; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::find +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) +{ + + stcMDNSServiceTxt* pResult = 0; + + for (stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + { + if ((p_pcKey) && + (0 == strcmp(pTxt->m_pcKey, p_pcKey))) + { + pResult = pTxt; + break; + } + } + return pResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::find +*/ +const MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) const +{ + + const stcMDNSServiceTxt* pResult = 0; + + for (const stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + { + if ((p_pcKey) && + (0 == strcmp(pTxt->m_pcKey, p_pcKey))) + { + + pResult = pTxt; + break; + } + } + return pResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::find +*/ +MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const stcMDNSServiceTxt* p_pTxt) +{ + + stcMDNSServiceTxt* pResult = 0; + + for (stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + { + if (p_pTxt == pTxt) + { + pResult = pTxt; + break; + } + } + return pResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::length +*/ +uint16_t MDNSResponder::stcMDNSServiceTxts::length(void) const +{ + + uint16_t u16Length = 0; + + stcMDNSServiceTxt* pTxt = m_pTxts; + while (pTxt) + { + u16Length += 1; // Length byte + u16Length += pTxt->length(); // Text + pTxt = pTxt->m_pNext; + } + return u16Length; +} + +/* + MDNSResponder::stcMDNSServiceTxts::c_strLength + + (incl. closing '\0'). Length bytes place is used for delimiting ';' and closing '\0' +*/ +size_t MDNSResponder::stcMDNSServiceTxts::c_strLength(void) const +{ + + return length(); +} + +/* + MDNSResponder::stcMDNSServiceTxts::c_str +*/ +bool MDNSResponder::stcMDNSServiceTxts::c_str(char* p_pcBuffer) +{ + + bool bResult = false; + + if (p_pcBuffer) + { + bResult = true; + + *p_pcBuffer = 0; + for (stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) + { + size_t stLength; + if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) + { + if (pTxt != m_pTxts) + { + *p_pcBuffer++ = ';'; + } + strncpy(p_pcBuffer, pTxt->m_pcKey, stLength); p_pcBuffer[stLength] = 0; + p_pcBuffer += stLength; + *p_pcBuffer++ = '='; + if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) + { + strncpy(p_pcBuffer, pTxt->m_pcValue, stLength); p_pcBuffer[stLength] = 0; + p_pcBuffer += stLength; + } + } + } + *p_pcBuffer++ = 0; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::bufferLength + + (incl. closing '\0'). +*/ +size_t MDNSResponder::stcMDNSServiceTxts::bufferLength(void) const +{ + + return (length() + 1); +} + +/* + MDNSResponder::stcMDNSServiceTxts::toBuffer +*/ +bool MDNSResponder::stcMDNSServiceTxts::buffer(char* p_pcBuffer) +{ + + bool bResult = false; + + if (p_pcBuffer) + { + bResult = true; + + *p_pcBuffer = 0; + for (stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) + { + *(unsigned char*)p_pcBuffer++ = pTxt->length(); + size_t stLength; + if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) + { + memcpy(p_pcBuffer, pTxt->m_pcKey, stLength); + p_pcBuffer += stLength; + *p_pcBuffer++ = '='; + if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) + { + memcpy(p_pcBuffer, pTxt->m_pcValue, stLength); + p_pcBuffer += stLength; + } + } + } + *p_pcBuffer++ = 0; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::compare +*/ +bool MDNSResponder::stcMDNSServiceTxts::compare(const MDNSResponder::stcMDNSServiceTxts& p_Other) const +{ + + bool bResult = false; + + if ((bResult = (length() == p_Other.length()))) + { + // Compare A->B + for (const stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) + { + const stcMDNSServiceTxt* pOtherTxt = p_Other.find(pTxt->m_pcKey); + bResult = ((pOtherTxt) && + (pTxt->m_pcValue) && + (pOtherTxt->m_pcValue) && + (strlen(pTxt->m_pcValue) == strlen(pOtherTxt->m_pcValue)) && + (0 == strcmp(pTxt->m_pcValue, pOtherTxt->m_pcValue))); + } + // Compare B->A + for (const stcMDNSServiceTxt* pOtherTxt = p_Other.m_pTxts; ((bResult) && (pOtherTxt)); pOtherTxt = pOtherTxt->m_pNext) + { + const stcMDNSServiceTxt* pTxt = find(pOtherTxt->m_pcKey); + bResult = ((pTxt) && + (pOtherTxt->m_pcValue) && + (pTxt->m_pcValue) && + (strlen(pOtherTxt->m_pcValue) == strlen(pTxt->m_pcValue)) && + (0 == strcmp(pOtherTxt->m_pcValue, pTxt->m_pcValue))); + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceTxts::operator== +*/ +bool MDNSResponder::stcMDNSServiceTxts::operator==(const stcMDNSServiceTxts& p_Other) const +{ + + return compare(p_Other); +} + +/* + MDNSResponder::stcMDNSServiceTxts::operator!= +*/ +bool MDNSResponder::stcMDNSServiceTxts::operator!=(const stcMDNSServiceTxts& p_Other) const +{ + + return !compare(p_Other); +} + + +/** + MDNSResponder::stcMDNS_MsgHeader + + A MDNS message haeder. + +*/ + +/* + MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader +*/ +MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader(uint16_t p_u16ID /*= 0*/, + bool p_bQR /*= false*/, + unsigned char p_ucOpcode /*= 0*/, + bool p_bAA /*= false*/, + bool p_bTC /*= false*/, + bool p_bRD /*= false*/, + bool p_bRA /*= false*/, + unsigned char p_ucRCode /*= 0*/, + uint16_t p_u16QDCount /*= 0*/, + uint16_t p_u16ANCount /*= 0*/, + uint16_t p_u16NSCount /*= 0*/, + uint16_t p_u16ARCount /*= 0*/) + : m_u16ID(p_u16ID), + m_1bQR(p_bQR), m_4bOpcode(p_ucOpcode), m_1bAA(p_bAA), m_1bTC(p_bTC), m_1bRD(p_bRD), + m_1bRA(p_bRA), m_3bZ(0), m_4bRCode(p_ucRCode), + m_u16QDCount(p_u16QDCount), + m_u16ANCount(p_u16ANCount), + m_u16NSCount(p_u16NSCount), + m_u16ARCount(p_u16ARCount) +{ + +} + + +/** + MDNSResponder::stcMDNS_RRDomain + + A MDNS domain object. + The labels of the domain are stored (DNS-like encoded) in 'm_acName': + [length byte]varlength label[length byte]varlength label[0] + 'm_u16NameLength' stores the used length of 'm_acName'. + Dynamic label addition is supported. + Comparison is supported. + Export as byte array 'esp8266.local' is supported. + +*/ + +/* + MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain constructor +*/ +MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(void) + : m_u16NameLength(0) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain copy-constructor +*/ +MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other) + : m_u16NameLength(0) +{ + + operator=(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRDomain::operator = +*/ +MDNSResponder::stcMDNS_RRDomain& MDNSResponder::stcMDNS_RRDomain::operator=(const stcMDNS_RRDomain& p_Other) +{ + + if (&p_Other != this) + { + memcpy(m_acName, p_Other.m_acName, sizeof(m_acName)); + m_u16NameLength = p_Other.m_u16NameLength; + } + return *this; +} + +/* + MDNSResponder::stcMDNS_RRDomain::clear +*/ +bool MDNSResponder::stcMDNS_RRDomain::clear(void) +{ + + memset(m_acName, 0, sizeof(m_acName)); + m_u16NameLength = 0; + return true; +} + +/* + MDNSResponder::stcMDNS_RRDomain::addLabel +*/ +bool MDNSResponder::stcMDNS_RRDomain::addLabel(const char* p_pcLabel, + bool p_bPrependUnderline /*= false*/) +{ + + bool bResult = false; + + size_t stLength = (p_pcLabel + ? (strlen(p_pcLabel) + (p_bPrependUnderline ? 1 : 0)) + : 0); + if ((MDNS_DOMAIN_LABEL_MAXLENGTH >= stLength) && + (MDNS_DOMAIN_MAXLENGTH >= (m_u16NameLength + (1 + stLength)))) + { + // Length byte + m_acName[m_u16NameLength] = (unsigned char)stLength; // Might be 0! + ++m_u16NameLength; + // Label + if (stLength) + { + if (p_bPrependUnderline) + { + m_acName[m_u16NameLength++] = '_'; + --stLength; + } + strncpy(&(m_acName[m_u16NameLength]), p_pcLabel, stLength); m_acName[m_u16NameLength + stLength] = 0; + m_u16NameLength += stLength; + } + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNS_RRDomain::compare +*/ +bool MDNSResponder::stcMDNS_RRDomain::compare(const stcMDNS_RRDomain& p_Other) const +{ + + bool bResult = false; + + if (m_u16NameLength == p_Other.m_u16NameLength) + { + const char* pT = m_acName; + const char* pO = p_Other.m_acName; + while ((pT) && + (pO) && + (*((unsigned char*)pT) == *((unsigned char*)pO)) && // Same length AND + (0 == strncasecmp((pT + 1), (pO + 1), *((unsigned char*)pT)))) // Same content + { + if (*((unsigned char*)pT)) // Not 0 + { + pT += (1 + * ((unsigned char*)pT)); // Shift by length byte and lenght + pO += (1 + * ((unsigned char*)pO)); + } + else // Is 0 -> Successfully reached the end + { + bResult = true; + break; + } + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNS_RRDomain::operator == +*/ +bool MDNSResponder::stcMDNS_RRDomain::operator==(const stcMDNS_RRDomain& p_Other) const +{ + + return compare(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRDomain::operator != +*/ +bool MDNSResponder::stcMDNS_RRDomain::operator!=(const stcMDNS_RRDomain& p_Other) const +{ + + return !compare(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRDomain::operator > +*/ +bool MDNSResponder::stcMDNS_RRDomain::operator>(const stcMDNS_RRDomain& p_Other) const +{ + + // TODO: Check, if this is a good idea... + return !compare(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRDomain::c_strLength +*/ +size_t MDNSResponder::stcMDNS_RRDomain::c_strLength(void) const +{ + + size_t stLength = 0; + + unsigned char* pucLabelLength = (unsigned char*)m_acName; + while (*pucLabelLength) + { + stLength += (*pucLabelLength + 1 /* +1 for '.' or '\0'*/); + pucLabelLength += (*pucLabelLength + 1); + } + return stLength; +} + +/* + MDNSResponder::stcMDNS_RRDomain::c_str +*/ +bool MDNSResponder::stcMDNS_RRDomain::c_str(char* p_pcBuffer) +{ + + bool bResult = false; + + if (p_pcBuffer) + { + *p_pcBuffer = 0; + unsigned char* pucLabelLength = (unsigned char*)m_acName; + while (*pucLabelLength) + { + memcpy(p_pcBuffer, (const char*)(pucLabelLength + 1), *pucLabelLength); + p_pcBuffer += *pucLabelLength; + pucLabelLength += (*pucLabelLength + 1); + *p_pcBuffer++ = (*pucLabelLength ? '.' : '\0'); + } + bResult = true; + } + return bResult; +} + + +/** + MDNSResponder::stcMDNS_RRAttributes + + A MDNS attributes object. + +*/ + +/* + MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes constructor +*/ +MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(uint16_t p_u16Type /*= 0*/, + uint16_t p_u16Class /*= 1 DNS_RRCLASS_IN Internet*/) + : m_u16Type(p_u16Type), + m_u16Class(p_u16Class) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes copy-constructor +*/ +MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Other) +{ + + operator=(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRAttributes::operator = +*/ +MDNSResponder::stcMDNS_RRAttributes& MDNSResponder::stcMDNS_RRAttributes::operator=(const MDNSResponder::stcMDNS_RRAttributes& p_Other) +{ + + if (&p_Other != this) + { + m_u16Type = p_Other.m_u16Type; + m_u16Class = p_Other.m_u16Class; + } + return *this; +} + + +/** + MDNSResponder::stcMDNS_RRHeader + + A MDNS record header (domain and attributes) object. + +*/ + +/* + MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader constructor +*/ +MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(void) +{ + +} + +/* + MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader copy-constructor +*/ +MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other) +{ + + operator=(p_Other); +} + +/* + MDNSResponder::stcMDNS_RRHeader::operator = +*/ +MDNSResponder::stcMDNS_RRHeader& MDNSResponder::stcMDNS_RRHeader::operator=(const MDNSResponder::stcMDNS_RRHeader& p_Other) +{ + + if (&p_Other != this) + { + m_Domain = p_Other.m_Domain; + m_Attributes = p_Other.m_Attributes; + } + return *this; +} + +/* + MDNSResponder::stcMDNS_RRHeader::clear +*/ +bool MDNSResponder::stcMDNS_RRHeader::clear(void) +{ + + m_Domain.clear(); + return true; +} + + +/** + MDNSResponder::stcMDNS_RRQuestion + + A MDNS question record object (header + question flags) + +*/ + +/* + MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion constructor +*/ +MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion(void) + : m_pNext(0), + m_bUnicast(false) +{ + +} + + +/** + MDNSResponder::stcMDNS_RRAnswer + + A MDNS answer record object (header + answer content). + This is a 'virtual' base class for all other MDNS answer classes. + +*/ + +/* + MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer constructor +*/ +MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer(enuAnswerType p_AnswerType, + const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : m_pNext(0), + m_AnswerType(p_AnswerType), + m_Header(p_Header), + m_u32TTL(p_u32TTL) +{ + + // Extract 'cache flush'-bit + m_bCacheFlush = (m_Header.m_Attributes.m_u16Class & 0x8000); + m_Header.m_Attributes.m_u16Class &= (~0x8000); +} + +/* + MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer destructor +*/ +MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer(void) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswer::answerType +*/ +MDNSResponder::enuAnswerType MDNSResponder::stcMDNS_RRAnswer::answerType(void) const +{ + + return m_AnswerType; +} + +/* + MDNSResponder::stcMDNS_RRAnswer::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswer::clear(void) +{ + + m_pNext = 0; + m_Header.clear(); + return true; +} + + +/** + MDNSResponder::stcMDNS_RRAnswerA + + A MDNS A answer object. + Extends the base class by an IP4 address member. + +*/ + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA constructor +*/ +MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA(const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_A, p_Header, p_u32TTL), + m_IPAddress(0, 0, 0, 0) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA destructor +*/ +MDNSResponder::stcMDNS_RRAnswerA::~stcMDNS_RRAnswerA(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerA::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerA::clear(void) +{ + + m_IPAddress = IPAddress(0, 0, 0, 0); + return true; +} +#endif + + +/** + MDNSResponder::stcMDNS_RRAnswerPTR + + A MDNS PTR answer object. + Extends the base class by a MDNS domain member. + +*/ + +/* + MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR constructor +*/ +MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR(const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_PTR, p_Header, p_u32TTL) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR destructor +*/ +MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerPTR::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerPTR::clear(void) +{ + + m_PTRDomain.clear(); + return true; +} + + +/** + MDNSResponder::stcMDNS_RRAnswerTXT + + A MDNS TXT answer object. + Extends the base class by a MDNS TXT items list member. + +*/ + +/* + MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT constructor +*/ +MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT(const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_TXT, p_Header, p_u32TTL) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT destructor +*/ +MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerTXT::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerTXT::clear(void) +{ + + m_Txts.clear(); + return true; +} + + +/** + MDNSResponder::stcMDNS_RRAnswerAAAA + + A MDNS AAAA answer object. + (Should) extend the base class by an IP6 address member. + +*/ + +#ifdef MDNS_IP6_SUPPORT +/* + MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA constructor +*/ +MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA(const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_AAAA, p_Header, p_u32TTL) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA destructor +*/ +MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerAAAA::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerAAAA::clear(void) +{ + + return true; +} +#endif + + +/** + MDNSResponder::stcMDNS_RRAnswerSRV + + A MDNS SRV answer object. + Extends the base class by a port member. + +*/ + +/* + MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV constructor +*/ +MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV(const MDNSResponder::stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_SRV, p_Header, p_u32TTL), + m_u16Priority(0), + m_u16Weight(0), + m_u16Port(0) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV destructor +*/ +MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerSRV::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerSRV::clear(void) +{ + + m_u16Priority = 0; + m_u16Weight = 0; + m_u16Port = 0; + m_SRVDomain.clear(); + return true; +} + + +/** + MDNSResponder::stcMDNS_RRAnswerGeneric + + An unknown (generic) MDNS answer object. + Extends the base class by a RDATA buffer member. + +*/ + +/* + MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric constructor +*/ +MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL) + : stcMDNS_RRAnswer(AnswerType_Generic, p_Header, p_u32TTL), + m_u16RDLength(0), + m_pu8RDData(0) +{ + +} + +/* + MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric destructor +*/ +MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNS_RRAnswerGeneric::clear +*/ +bool MDNSResponder::stcMDNS_RRAnswerGeneric::clear(void) +{ + + if (m_pu8RDData) + { + delete[] m_pu8RDData; + m_pu8RDData = 0; + } + m_u16RDLength = 0; + + return true; +} + + +/** + MDNSResponder::stcProbeInformation + + Probing status information for a host or service domain + +*/ + +/* + MDNSResponder::stcProbeInformation::stcProbeInformation constructor +*/ +MDNSResponder::stcProbeInformation::stcProbeInformation(void) + : m_ProbingStatus(ProbingStatus_WaitingForData), + m_u8SentCount(0), + m_Timeout(esp8266::polledTimeout::oneShotMs::neverExpires), + m_bConflict(false), + m_bTiebreakNeeded(false), + m_fnHostProbeResultCallback(0), + m_fnServiceProbeResultCallback(0) +{ +} + +/* + MDNSResponder::stcProbeInformation::clear +*/ +bool MDNSResponder::stcProbeInformation::clear(bool p_bClearUserdata /*= false*/) +{ + + m_ProbingStatus = ProbingStatus_WaitingForData; + m_u8SentCount = 0; + m_Timeout.resetToNeverExpires(); + m_bConflict = false; + m_bTiebreakNeeded = false; + if (p_bClearUserdata) + { + m_fnHostProbeResultCallback = 0; + m_fnServiceProbeResultCallback = 0; + } + return true; +} + +/** + MDNSResponder::stcMDNSService + + A MDNS service object (to be announced by the MDNS responder) + The service instance may be '\0'; in this case the hostname is used + and the flag m_bAutoName is set. If the hostname changes, all 'auto- + named' services are renamed also. + m_u8Replymask is used while preparing a response to a MDNS query. It is + resetted in '_sendMDNSMessage' afterwards. +*/ + +/* + MDNSResponder::stcMDNSService::stcMDNSService constructor +*/ +MDNSResponder::stcMDNSService::stcMDNSService(const char* p_pcName /*= 0*/, + const char* p_pcService /*= 0*/, + const char* p_pcProtocol /*= 0*/) + : m_pNext(0), + m_pcName(0), + m_bAutoName(false), + m_pcService(0), + m_pcProtocol(0), + m_u16Port(0), + m_u8ReplyMask(0), + m_fnTxtCallback(0) +{ + + setName(p_pcName); + setService(p_pcService); + setProtocol(p_pcProtocol); +} + +/* + MDNSResponder::stcMDNSService::~stcMDNSService destructor +*/ +MDNSResponder::stcMDNSService::~stcMDNSService(void) +{ + + releaseName(); + releaseService(); + releaseProtocol(); +} + +/* + MDNSResponder::stcMDNSService::setName +*/ +bool MDNSResponder::stcMDNSService::setName(const char* p_pcName) +{ + + bool bResult = false; + + releaseName(); + size_t stLength = (p_pcName ? strlen(p_pcName) : 0); + if (stLength) + { + if ((bResult = (0 != (m_pcName = new char[stLength + 1])))) + { + strncpy(m_pcName, p_pcName, stLength); + m_pcName[stLength] = 0; + } + } + else + { + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSService::releaseName +*/ +bool MDNSResponder::stcMDNSService::releaseName(void) +{ + + if (m_pcName) + { + delete[] m_pcName; + m_pcName = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSService::setService +*/ +bool MDNSResponder::stcMDNSService::setService(const char* p_pcService) +{ + + bool bResult = false; + + releaseService(); + size_t stLength = (p_pcService ? strlen(p_pcService) : 0); + if (stLength) + { + if ((bResult = (0 != (m_pcService = new char[stLength + 1])))) + { + strncpy(m_pcService, p_pcService, stLength); + m_pcService[stLength] = 0; + } + } + else + { + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSService::releaseService +*/ +bool MDNSResponder::stcMDNSService::releaseService(void) +{ + + if (m_pcService) + { + delete[] m_pcService; + m_pcService = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSService::setProtocol +*/ +bool MDNSResponder::stcMDNSService::setProtocol(const char* p_pcProtocol) +{ + + bool bResult = false; + + releaseProtocol(); + size_t stLength = (p_pcProtocol ? strlen(p_pcProtocol) : 0); + if (stLength) + { + if ((bResult = (0 != (m_pcProtocol = new char[stLength + 1])))) + { + strncpy(m_pcProtocol, p_pcProtocol, stLength); + m_pcProtocol[stLength] = 0; + } + } + else + { + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSService::releaseProtocol +*/ +bool MDNSResponder::stcMDNSService::releaseProtocol(void) +{ + + if (m_pcProtocol) + { + delete[] m_pcProtocol; + m_pcProtocol = 0; + } + return true; +} + + +/** + MDNSResponder::stcMDNSServiceQuery + + A MDNS service query object. + Service queries may be static or dynamic. + As the static service query is processed in the blocking function 'queryService', + only one static service service may exist. The processing of the answers is done + on the WiFi-stack side of the ESP stack structure (via 'UDPContext.onRx(_update)'). + +*/ + +/** + MDNSResponder::stcMDNSServiceQuery::stcAnswer + + One answer for a service query. + Every answer must contain + - a service instance entry (pivot), + and may contain + - a host domain, + - a port + - an IP4 address + (- an IP6 address) + - a MDNS TXTs + The existance of a component is flaged in 'm_u32ContentFlags'. + For every answer component a TTL value is maintained. + Answer objects can be connected to a linked list. + + For the host domain, service domain and TXTs components, a char array + representation can be retrieved (which is created on demand). + +*/ + +/** + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL + + The TTL (Time-To-Live) for an specific answer content. + The 80% and outdated states are calculated based on the current time (millis) + and the 'set' time (also millis). + If the answer is scheduled for an update, the corresponding flag should be set. + + / + + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor + / + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(uint32_t p_u32TTL / *= 0* /) + : m_bUpdateScheduled(false) { + + set(p_u32TTL * 1000); + } + + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set + / + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) { + + m_TTLTimeFlag.restart(p_u32TTL * 1000); + m_bUpdateScheduled = false; + + return true; + } + + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent + / + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent(void) const { + + return ((m_TTLTimeFlag.getTimeout()) && + (!m_bUpdateScheduled) && + (m_TTLTimeFlag.hypotheticalTimeout((m_TTLTimeFlag.getTimeout() * 800) / 1000))); + } + + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated + / + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated(void) const { + + return ((m_TTLTimeFlag.getTimeout()) && + (m_TTLTimeFlag.flagged())); + }*/ + + +/** + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL + + The TTL (Time-To-Live) for an specific answer content. + The 80% and outdated states are calculated based on the current time (millis) + and the 'set' time (also millis). + If the answer is scheduled for an update, the corresponding flag should be set. + +*/ + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(void) + : m_u32TTL(0), + m_TTLTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), + m_timeoutLevel(TIMEOUTLEVEL_UNSET) +{ + +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) +{ + + m_u32TTL = p_u32TTL; + if (m_u32TTL) + { + m_timeoutLevel = TIMEOUTLEVEL_BASE; // Set to 80% + m_TTLTimeout.reset(timeout()); + } + else + { + m_timeoutLevel = TIMEOUTLEVEL_UNSET; // undef + m_TTLTimeout.resetToNeverExpires(); + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) +{ + + return ((m_u32TTL) && + (TIMEOUTLEVEL_UNSET != m_timeoutLevel) && + (m_TTLTimeout.expired())); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart(void) +{ + + bool bResult = true; + + if ((TIMEOUTLEVEL_BASE <= m_timeoutLevel) && // >= 80% AND + (TIMEOUTLEVEL_FINAL > m_timeoutLevel)) // < 100% + { + + m_timeoutLevel += TIMEOUTLEVEL_INTERVAL; // increment by 5% + m_TTLTimeout.reset(timeout()); + } + else + { + bResult = false; + m_TTLTimeout.resetToNeverExpires(); + m_timeoutLevel = TIMEOUTLEVEL_UNSET; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion(void) +{ + + m_timeoutLevel = TIMEOUTLEVEL_FINAL; + m_TTLTimeout.reset(1 * 1000); // See RFC 6762, 10.1 + + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel(void) const +{ + + return (TIMEOUTLEVEL_FINAL == m_timeoutLevel); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout +*/ +unsigned long MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout(void) const +{ + + uint32_t u32Timeout = esp8266::polledTimeout::oneShotMs::neverExpires; + + if (TIMEOUTLEVEL_BASE == m_timeoutLevel) // 80% + { + u32Timeout = (m_u32TTL * 800); // to milliseconds + } + else if ((TIMEOUTLEVEL_BASE < m_timeoutLevel) && // >80% AND + (TIMEOUTLEVEL_FINAL >= m_timeoutLevel)) // <= 100% + { + + u32Timeout = (m_u32TTL * 50); + } // else: invalid + return u32Timeout; +} + + +#ifdef MDNS_IP4_SUPPORT +/** + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address + +*/ + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address constructor +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address(IPAddress p_IPAddress, + uint32_t p_u32TTL /*= 0*/) + : m_pNext(0), + m_IPAddress(p_IPAddress) +{ + + m_TTL.set(p_u32TTL); +} +#endif + + +/** + MDNSResponder::stcMDNSServiceQuery::stcAnswer +*/ + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer constructor +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer(void) + : m_pNext(0), + m_pcServiceDomain(0), + m_pcHostDomain(0), + m_u16Port(0), + m_pcTxts(0), +#ifdef MDNS_IP4_SUPPORT + m_pIP4Addresses(0), +#endif +#ifdef MDNS_IP6_SUPPORT + m_pIP6Addresses(0), +#endif + m_u32ContentFlags(0) +{ +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer destructor +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear(void) +{ + + return ((releaseTxts()) && +#ifdef MDNS_IP4_SUPPORT + (releaseIP4Addresses()) && +#endif +#ifdef MDNS_IP6_SUPPORT + (releaseIP6Addresses()) +#endif + (releaseHostDomain()) && + (releaseServiceDomain())); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain + + Alloc memory for the char array representation of the service domain. + +*/ +char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain(size_t p_stLength) +{ + + releaseServiceDomain(); + if (p_stLength) + { + m_pcServiceDomain = new char[p_stLength]; + } + return m_pcServiceDomain; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain(void) +{ + + if (m_pcServiceDomain) + { + delete[] m_pcServiceDomain; + m_pcServiceDomain = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain + + Alloc memory for the char array representation of the host domain. + +*/ +char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain(size_t p_stLength) +{ + + releaseHostDomain(); + if (p_stLength) + { + m_pcHostDomain = new char[p_stLength]; + } + return m_pcHostDomain; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain(void) +{ + + if (m_pcHostDomain) + { + delete[] m_pcHostDomain; + m_pcHostDomain = 0; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts + + Alloc memory for the char array representation of the TXT items. + +*/ +char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts(size_t p_stLength) +{ + + releaseTxts(); + if (p_stLength) + { + m_pcTxts = new char[p_stLength]; + } + return m_pcTxts; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts(void) +{ + + if (m_pcTxts) + { + delete[] m_pcTxts; + m_pcTxts = 0; + } + return true; +} + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses(void) +{ + + while (m_pIP4Addresses) + { + stcIP4Address* pNext = m_pIP4Addresses->m_pNext; + delete m_pIP4Addresses; + m_pIP4Addresses = pNext; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) +{ + + bool bResult = false; + + if (p_pIP4Address) + { + p_pIP4Address->m_pNext = m_pIP4Addresses; + m_pIP4Addresses = p_pIP4Address; + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) +{ + + bool bResult = false; + + if (p_pIP4Address) + { + stcIP4Address* pPred = m_pIP4Addresses; + while ((pPred) && + (pPred->m_pNext != p_pIP4Address)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pIP4Address->m_pNext; + delete p_pIP4Address; + bResult = true; + } + else if (m_pIP4Addresses == p_pIP4Address) // No predecesor, but first item + { + m_pIP4Addresses = p_pIP4Address->m_pNext; + delete p_pIP4Address; + bResult = true; + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address (const) +*/ +const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) const +{ + + return (stcIP4Address*)(((const stcAnswer*)this)->findIP4Address(p_IPAddress)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) +{ + + stcIP4Address* pIP4Address = m_pIP4Addresses; + while (pIP4Address) + { + if (pIP4Address->m_IPAddress == p_IPAddress) + { + break; + } + pIP4Address = pIP4Address->m_pNext; + } + return pIP4Address; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount +*/ +uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount(void) const +{ + + uint32_t u32Count = 0; + + stcIP4Address* pIP4Address = m_pIP4Addresses; + while (pIP4Address) + { + ++u32Count; + pIP4Address = pIP4Address->m_pNext; + } + return u32Count; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) +{ + + return (stcIP4Address*)(((const stcAnswer*)this)->IP4AddressAtIndex(p_u32Index)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex (const) +*/ +const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) const +{ + + const stcIP4Address* pIP4Address = 0; + + if (((uint32_t)(-1) != p_u32Index) && + (m_pIP4Addresses)) + { + + uint32_t u32Index; + for (pIP4Address = m_pIP4Addresses, u32Index = 0; ((pIP4Address) && (u32Index < p_u32Index)); pIP4Address = pIP4Address->m_pNext, ++u32Index); + } + return pIP4Address; +} +#endif + +#ifdef MDNS_IP6_SUPPORT +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses(void) +{ + + while (m_pIP6Addresses) + { + stcIP6Address* pNext = m_pIP6Addresses->m_pNext; + delete m_pIP6Addresses; + m_pIP6Addresses = pNext; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) +{ + + bool bResult = false; + + if (p_pIP6Address) + { + p_pIP6Address->m_pNext = m_pIP6Addresses; + m_pIP6Addresses = p_pIP6Address; + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) +{ + + bool bResult = false; + + if (p_pIP6Address) + { + stcIP6Address* pPred = m_pIP6Addresses; + while ((pPred) && + (pPred->m_pNext != p_pIP6Address)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pIP6Address->m_pNext; + delete p_pIP6Address; + bResult = true; + } + else if (m_pIP6Addresses == p_pIP6Address) // No predecesor, but first item + { + m_pIP6Addresses = p_pIP6Address->m_pNext; + delete p_pIP6Address; + bResult = true; + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IP6Address& p_IPAddress) +{ + + return (stcIP6Address*)(((const stcAnswer*)this)->findIP6Address(p_IPAddress)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address (const) +*/ +const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IPAddress& p_IPAddress) const +{ + + const stcIP6Address* pIP6Address = m_pIP6Addresses; + while (pIP6Address) + { + if (p_IP6Address->m_IPAddress == p_IPAddress) + { + break; + } + pIP6Address = pIP6Address->m_pNext; + } + return pIP6Address; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount +*/ +uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount(void) const +{ + + uint32_t u32Count = 0; + + stcIP6Address* pIP6Address = m_pIP6Addresses; + while (pIP6Address) + { + ++u32Count; + pIP6Address = pIP6Address->m_pNext; + } + return u32Count; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex (const) +*/ +const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) const +{ + + return (stcIP6Address*)(((const stcAnswer*)this)->IP6AddressAtIndex(p_u32Index)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) +{ + + stcIP6Address* pIP6Address = 0; + + if (((uint32_t)(-1) != p_u32Index) && + (m_pIP6Addresses)) + { + + uint32_t u32Index; + for (pIP6Address = m_pIP6Addresses, u32Index = 0; ((pIP6Address) && (u32Index < p_u32Index)); pIP6Address = pIP6Address->m_pNext, ++u32Index); + } + return pIP6Address; +} +#endif + + +/** + MDNSResponder::stcMDNSServiceQuery + + A service query object. + A static query is flaged via 'm_bLegacyQuery'; while the function 'queryService' + is waiting for answers, the internal flag 'm_bAwaitingAnswers' is set. When the + timeout is reached, the flag is removed. These two flags are only used for static + service queries. + All answers to the service query are stored in 'm_pAnswers' list. + Individual answers may be addressed by index (in the list of answers). + Every time a answer component is added (or changes) in a dynamic service query, + the callback 'm_fnCallback' is called. + The answer list may be searched by service and host domain. + + Service query object may be connected to a linked list. +*/ + +/* + MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery constructor +*/ +MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery(void) + : m_pNext(0), + m_fnCallback(0), + m_bLegacyQuery(false), + m_u8SentCount(0), + m_ResendTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), + m_bAwaitingAnswers(true), + m_pAnswers(0) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery destructor +*/ +MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSServiceQuery::clear +*/ +bool MDNSResponder::stcMDNSServiceQuery::clear(void) +{ + + m_fnCallback = 0; + m_bLegacyQuery = false; + m_u8SentCount = 0; + m_ResendTimeout.resetToNeverExpires(); + m_bAwaitingAnswers = true; + while (m_pAnswers) + { + stcAnswer* pNext = m_pAnswers->m_pNext; + delete m_pAnswers; + m_pAnswers = pNext; + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::answerCount +*/ +uint32_t MDNSResponder::stcMDNSServiceQuery::answerCount(void) const +{ + + uint32_t u32Count = 0; + + stcAnswer* pAnswer = m_pAnswers; + while (pAnswer) + { + ++u32Count; + pAnswer = pAnswer->m_pNext; + } + return u32Count; +} + +/* + MDNSResponder::stcMDNSServiceQuery::answerAtIndex +*/ +const MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) const +{ + + const stcAnswer* pAnswer = 0; + + if (((uint32_t)(-1) != p_u32Index) && + (m_pAnswers)) + { + + uint32_t u32Index; + for (pAnswer = m_pAnswers, u32Index = 0; ((pAnswer) && (u32Index < p_u32Index)); pAnswer = pAnswer->m_pNext, ++u32Index); + } + return pAnswer; +} + +/* + MDNSResponder::stcMDNSServiceQuery::answerAtIndex +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) +{ + + return (stcAnswer*)(((const stcMDNSServiceQuery*)this)->answerAtIndex(p_u32Index)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::indexOfAnswer +*/ +uint32_t MDNSResponder::stcMDNSServiceQuery::indexOfAnswer(const MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) const +{ + + uint32_t u32Index = 0; + + for (const stcAnswer* pAnswer = m_pAnswers; pAnswer; pAnswer = pAnswer->m_pNext, ++u32Index) + { + if (pAnswer == p_pAnswer) + { + return u32Index; + } + } + return ((uint32_t)(-1)); +} + +/* + MDNSResponder::stcMDNSServiceQuery::addAnswer +*/ +bool MDNSResponder::stcMDNSServiceQuery::addAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) +{ + + bool bResult = false; + + if (p_pAnswer) + { + p_pAnswer->m_pNext = m_pAnswers; + m_pAnswers = p_pAnswer; + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::removeAnswer +*/ +bool MDNSResponder::stcMDNSServiceQuery::removeAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) +{ + + bool bResult = false; + + if (p_pAnswer) + { + stcAnswer* pPred = m_pAnswers; + while ((pPred) && + (pPred->m_pNext != p_pAnswer)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pAnswer->m_pNext; + delete p_pAnswer; + bResult = true; + } + else if (m_pAnswers == p_pAnswer) // No predecesor, but first item + { + m_pAnswers = p_pAnswer->m_pNext; + delete p_pAnswer; + bResult = true; + } + } + return bResult; +} + +/* + MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain(const MDNSResponder::stcMDNS_RRDomain& p_ServiceDomain) +{ + + stcAnswer* pAnswer = m_pAnswers; + while (pAnswer) + { + if (pAnswer->m_ServiceDomain == p_ServiceDomain) + { + break; + } + pAnswer = pAnswer->m_pNext; + } + return pAnswer; +} + +/* + MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain +*/ +MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain(const MDNSResponder::stcMDNS_RRDomain& p_HostDomain) +{ + + stcAnswer* pAnswer = m_pAnswers; + while (pAnswer) + { + if (pAnswer->m_HostDomain == p_HostDomain) + { + break; + } + pAnswer = pAnswer->m_pNext; + } + return pAnswer; +} + + +/** + MDNSResponder::stcMDNSSendParameter + + A 'collection' of properties and flags for one MDNS query or response. + Mainly managed by the 'Control' functions. + The current offset in the UPD output buffer is tracked to be able to do + a simple host or service domain compression. + +*/ + +/** + MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem + + A cached host or service domain, incl. the offset in the UDP output buffer. + +*/ + +/* + MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem constructor +*/ +MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem(const void* p_pHostnameOrService, + bool p_bAdditionalData, + uint32_t p_u16Offset) + : m_pNext(0), + m_pHostnameOrService(p_pHostnameOrService), + m_bAdditionalData(p_bAdditionalData), + m_u16Offset(p_u16Offset) +{ + +} + +/** + MDNSResponder::stcMDNSSendParameter +*/ + +/* + MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter constructor +*/ +MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter(void) + : m_pQuestions(0), + m_pDomainCacheItems(0) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter destructor +*/ +MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter(void) +{ + + clear(); +} + +/* + MDNSResponder::stcMDNSSendParameter::clear +*/ +bool MDNSResponder::stcMDNSSendParameter::clear(void) +{ + + m_u16ID = 0; + m_u8HostReplyMask = 0; + m_u16Offset = 0; + + m_bLegacyQuery = false; + m_bResponse = false; + m_bAuthorative = false; + m_bUnicast = false; + m_bUnannounce = false; + + m_bCacheFlush = true; + + while (m_pQuestions) + { + stcMDNS_RRQuestion* pNext = m_pQuestions->m_pNext; + delete m_pQuestions; + m_pQuestions = pNext; + } + while (m_pDomainCacheItems) + { + stcDomainCacheItem* pNext = m_pDomainCacheItems->m_pNext; + delete m_pDomainCacheItems; + m_pDomainCacheItems = pNext; + } + return true; +} + +/* + MDNSResponder::stcMDNSSendParameter::shiftOffset +*/ +bool MDNSResponder::stcMDNSSendParameter::shiftOffset(uint16_t p_u16Shift) +{ + + m_u16Offset += p_u16Shift; + return true; +} + +/* + MDNSResponder::stcMDNSSendParameter::addDomainCacheItem +*/ +bool MDNSResponder::stcMDNSSendParameter::addDomainCacheItem(const void* p_pHostnameOrService, + bool p_bAdditionalData, + uint16_t p_u16Offset) +{ + + bool bResult = false; + + stcDomainCacheItem* pNewItem = 0; + if ((p_pHostnameOrService) && + (p_u16Offset) && + ((pNewItem = new stcDomainCacheItem(p_pHostnameOrService, p_bAdditionalData, p_u16Offset)))) + { + + pNewItem->m_pNext = m_pDomainCacheItems; + bResult = ((m_pDomainCacheItems = pNewItem)); + } + return bResult; +} + +/* + MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset +*/ +uint16_t MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset(const void* p_pHostnameOrService, + bool p_bAdditionalData) const +{ + + const stcDomainCacheItem* pCacheItem = m_pDomainCacheItems; + + for (; pCacheItem; pCacheItem = pCacheItem->m_pNext) + { + if ((pCacheItem->m_pHostnameOrService == p_pHostnameOrService) && + (pCacheItem->m_bAdditionalData == p_bAdditionalData)) // Found cache item + { + break; + } + } + return (pCacheItem ? pCacheItem->m_u16Offset : 0); +} + +} // namespace MDNSImplementation + +} // namespace esp8266 + + + diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp index c61baf87a3..3383d2e14b 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp @@ -1,1636 +1,1836 @@ -/* - * LEAmDNS_Transfer.cpp - * - * License (MIT license): - * 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. - * - */ - -extern "C" { - #include "user_interface.h" -} - -#include "LEAmDNS_lwIPdefs.h" -#include "LEAmDNS_Priv.h" - - -namespace esp8266 { - -/* - * LEAmDNS - */ -namespace MDNSImplementation { - -/** - * CONST STRINGS - */ -static const char* scpcLocal = "local"; -static const char* scpcServices = "services"; -static const char* scpcDNSSD = "dns-sd"; -static const char* scpcUDP = "udp"; -//static const char* scpcTCP = "tcp"; - -#ifdef MDNS_IP4_SUPPORT - static const char* scpcReverseIP4Domain = "in-addr"; -#endif -#ifdef MDNS_IP6_SUPPORT - static const char* scpcReverseIP6Domain = "ip6"; -#endif -static const char* scpcReverseTopDomain = "arpa"; - -/** - * TRANSFER - */ - - -/** - * SENDING - */ - -/* - * MDNSResponder::_sendMDNSMessage - * - * Unicast responses are prepared and sent directly to the querier. - * Multicast responses or queries are transferred to _sendMDNSMessage_Multicast - * - * Any reply flags in installed services are removed at the end! - * - */ -bool MDNSResponder::_sendMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - - bool bResult = true; - - if (p_rSendParameter.m_bResponse) { - if (p_rSendParameter.m_bUnicast) { // Unicast response -> Send to querier - DEBUG_EX_ERR(if (!m_pUDPContext->getRemoteAddress()) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage: MISSING remote address for response!\n")); }); - IPAddress ipRemote; - ipRemote = m_pUDPContext->getRemoteAddress(); - bResult = ((_prepareMDNSMessage(p_rSendParameter, _getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE))) && - (m_pUDPContext->send(ipRemote, m_pUDPContext->getRemotePort()))); - } - else { // Multicast response -> Send via the same network interface, that received the query - bResult = _sendMDNSMessage_Multicast(p_rSendParameter, (SOFTAP_MODE | STATION_MODE)); - } - } - else { // Multicast query -> Send by all available network interfaces - const int caiWiFiOpModes[2] = { SOFTAP_MODE, STATION_MODE }; - for (int iInterfaceId=0; ((bResult) && (iInterfaceId<=1)); ++iInterfaceId) { - if (wifi_get_opmode() & caiWiFiOpModes[iInterfaceId]) { - bResult = _sendMDNSMessage_Multicast(p_rSendParameter, caiWiFiOpModes[iInterfaceId]); - } - } - } - - // Finally clear service reply masks - for (stcMDNSService* pService=m_pServices; pService; pService=pService->m_pNext) { - pService->m_u8ReplyMask = 0; - } - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_sendMDNSMessage_Multicast - * - * Fills the UDP output buffer (via _prepareMDNSMessage) and sends the buffer - * via the selected WiFi interface (Station or AP) - */ -bool MDNSResponder::_sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, - int p_iWiFiOpMode) { - bool bResult = false; - - IPAddress fromIPAddress; - fromIPAddress = _getResponseMulticastInterface(p_iWiFiOpMode); - m_pUDPContext->setMulticastInterface(fromIPAddress); - -#ifdef MDNS_IP4_SUPPORT - IPAddress toMulticastAddress(DNS_MQUERY_IPV4_GROUP_INIT); -#endif -#ifdef MDNS_IP6_SUPPORT - //TODO: set multicast address - IPAddress toMulticastAddress(DNS_MQUERY_IPV6_GROUP_INIT); -#endif - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: Will send to '%s'.\n"), toMulticastAddress.toString().c_str());); - bResult = ((_prepareMDNSMessage(p_rSendParameter, fromIPAddress)) && - (m_pUDPContext->send(toMulticastAddress, DNS_MQUERY_PORT))); - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_prepareMDNSMessage - * - * The MDNS message is composed in a two-step process. - * In the first loop 'only' the header informations (mainly number of answers) are collected, - * while in the seconds loop, the header and all queries and answers are written to the UDP - * output buffer. - * - */ -bool MDNSResponder::_prepareMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, - IPAddress p_IPAddress) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage\n"));); - bool bResult = true; - - // Prepare header; count answers - stcMDNS_MsgHeader msgHeader(0, p_rSendParameter.m_bResponse, 0, p_rSendParameter.m_bAuthorative); - // If this is a response, the answers are anwers, - // else this is a query or probe and the answers go into auth section - uint16_t& ru16Answers = (p_rSendParameter.m_bResponse - ? msgHeader.m_u16ANCount - : msgHeader.m_u16NSCount); - - /** - * enuSequence - */ - enum enuSequence { - Sequence_Count = 0, - Sequence_Send = 1 - }; - - // Two step sequence: 'Count' and 'Send' - for (uint32_t sequence=Sequence_Count; ((bResult) && (sequence<=Sequence_Send)); ++sequence) { - DEBUG_EX_INFO( - if (Sequence_Send == sequence) { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), - (unsigned)msgHeader.m_u16ID, - (unsigned)msgHeader.m_1bQR, (unsigned)msgHeader.m_4bOpcode, (unsigned)msgHeader.m_1bAA, (unsigned)msgHeader.m_1bTC, (unsigned)msgHeader.m_1bRD, - (unsigned)msgHeader.m_1bRA, (unsigned)msgHeader.m_4bRCode, - (unsigned)msgHeader.m_u16QDCount, - (unsigned)msgHeader.m_u16ANCount, - (unsigned)msgHeader.m_u16NSCount, - (unsigned)msgHeader.m_u16ARCount); - } - ); - // Count/send - // Header - bResult = ((Sequence_Count == sequence) - ? true - : _writeMDNSMsgHeader(msgHeader, p_rSendParameter)); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSMsgHeader FAILED!\n"));); - // Questions - for (stcMDNS_RRQuestion* pQuestion=p_rSendParameter.m_pQuestions; ((bResult) && (pQuestion)); pQuestion=pQuestion->m_pNext) { - ((Sequence_Count == sequence) - ? ++msgHeader.m_u16QDCount - : (bResult = _writeMDNSQuestion(*pQuestion, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSQuestion FAILED!\n"));); - } - - // Answers and authorative answers -#ifdef MDNS_IP4_SUPPORT - if ((bResult) && - (p_rSendParameter.m_u8HostReplyMask & ContentFlag_A)) { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_A(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(A) FAILED!\n"));); - } - if ((bResult) && - (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP4)) { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_PTR_IP4(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP4 FAILED!\n"));); - } -#endif -#ifdef MDNS_IP6_SUPPORT - if ((bResult) && - (p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA)) { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(A) FAILED!\n"));); - } - if ((bResult) && - (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP6)) { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_PTR_IP6(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP6 FAILED!\n"));); - } -#endif - - for (stcMDNSService* pService=m_pServices; ((bResult) && (pService)); pService=pService->m_pNext) { - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_PTR_TYPE)) { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_PTR_TYPE(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_TYPE FAILED!\n"));); - } - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_PTR_NAME)) { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_PTR_NAME(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_NAME FAILED!\n"));); - } - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_SRV)) { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_SRV(A) FAILED!\n"));); - } - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_TXT)) { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_TXT(A) FAILED!\n"));); - } - } // for services - - // Additional answers -#ifdef MDNS_IP4_SUPPORT - bool bNeedsAdditionalAnswerA = false; -#endif -#ifdef MDNS_IP6_SUPPORT - bool bNeedsAdditionalAnswerAAAA = false; -#endif - for (stcMDNSService* pService=m_pServices; ((bResult) && (pService)); pService=pService->m_pNext) { - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_PTR_NAME) && // If PTR_NAME is requested, AND - (!(pService->m_u8ReplyMask & ContentFlag_SRV))) { // NOT SRV -> add SRV as additional answer - ((Sequence_Count == sequence) - ? ++msgHeader.m_u16ARCount - : (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_SRV(B) FAILED!\n"));); - } - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_PTR_NAME) && // If PTR_NAME is requested, AND - (!(pService->m_u8ReplyMask & ContentFlag_TXT))) { // NOT TXT -> add TXT as additional answer - ((Sequence_Count == sequence) - ? ++msgHeader.m_u16ARCount - : (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_TXT(B) FAILED!\n"));); - } - if ((pService->m_u8ReplyMask & (ContentFlag_PTR_NAME | ContentFlag_SRV)) || // If service instance name or SRV OR - (p_rSendParameter.m_u8HostReplyMask & (ContentFlag_A | ContentFlag_AAAA))) { // any host IP address is requested -#ifdef MDNS_IP4_SUPPORT - if ((bResult) && - (!(p_rSendParameter.m_u8HostReplyMask & ContentFlag_A))) { // Add IP4 address - bNeedsAdditionalAnswerA = true; - } -#endif -#ifdef MDNS_IP6_SUPPORT - if ((bResult) && - (!(p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA))) { // Add IP6 address - bNeedsAdditionalAnswerAAAA = true; - } -#endif - } - } // for services - - // Answer A needed? -#ifdef MDNS_IP4_SUPPORT - if ((bResult) && - (bNeedsAdditionalAnswerA)) { - ((Sequence_Count == sequence) - ? ++msgHeader.m_u16ARCount - : (bResult = _writeMDNSAnswer_A(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(B) FAILED!\n"));); - } -#endif -#ifdef MDNS_IP6_SUPPORT - // Answer AAAA needed? - if ((bResult) && - (bNeedsAdditionalAnswerAAAA)) { - ((Sequence_Count == sequence) - ? ++msgHeader.m_u16ARCount - : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(B) FAILED!\n"));); - } -#endif - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: Loop %i FAILED!\n"), sequence);); - } // for sequence - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: FAILED!\n"));); - return bResult; -} - -/* - * MDNSResponder::_sendMDNSServiceQuery - * - * Creates and sends a PTR query for the given service domain. - * - */ -bool MDNSResponder::_sendMDNSServiceQuery(const MDNSResponder::stcMDNSServiceQuery& p_ServiceQuery) { - - return _sendMDNSQuery(p_ServiceQuery.m_ServiceTypeDomain, DNS_RRTYPE_PTR); -} - -/* - * MDNSResponder::_sendMDNSQuery - * - * Creates and sends a query for the given domain and query type. - * - */ -bool MDNSResponder::_sendMDNSQuery(const MDNSResponder::stcMDNS_RRDomain& p_QueryDomain, - uint16_t p_u16QueryType, - stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers /*= 0*/) { - - bool bResult = false; - - stcMDNSSendParameter sendParameter; - if (0 != ((sendParameter.m_pQuestions = new stcMDNS_RRQuestion))) { - sendParameter.m_pQuestions->m_Header.m_Domain = p_QueryDomain; - - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = p_u16QueryType; - // It seems, that some mDNS implementations don't support 'unicast response' questions... - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (/*0x8000 |*/ DNS_RRCLASS_IN); // /*Unicast &*/ INternet - - // TODO: Add knwon answer to the query - (void)p_pKnownAnswers; - - bResult = _sendMDNSMessage(sendParameter); - } // else: FAILED to alloc question - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSQuery: FAILED to alloc question!\n"));); - return bResult; -} - -/* - * MDNSResponder::_getResponseMulticastInterface - * - * Selects the appropriate interface for responses. - * If AP mode is enabled and the remote contact is in the APs local net, then the - * AP interface is used to send the response. - * Otherwise the Station interface (if available) is used. - * - */ -IPAddress MDNSResponder::_getResponseMulticastInterface(int p_iWiFiOpModes) const { - - ip_info IPInfo_Local; - bool bFoundMatch = false; - - if ((p_iWiFiOpModes & SOFTAP_MODE) && - (wifi_get_opmode() & SOFTAP_MODE)) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _getResponseMulticastInterface: SOFTAP_MODE\n"));); - // Get remote IP address - IPAddress IP_Remote; - IP_Remote = m_pUDPContext->getRemoteAddress(); - // Get local (AP) IP address - wifi_get_ip_info(SOFTAP_IF, &IPInfo_Local); - - if ((IPInfo_Local.ip.addr) && // Has local AP IP address AND - (ip4_addr_netcmp(ip_2_ip4((const ip_addr_t*)IP_Remote), &IPInfo_Local.ip, &IPInfo_Local.netmask))) { // Remote address is in the same subnet as the AP - bFoundMatch = true; - } - } - if ((!bFoundMatch) && - (p_iWiFiOpModes & STATION_MODE) && - (wifi_get_opmode() & STATION_MODE)) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _getResponseMulticastInterface: STATION_MODE\n"));); - // Get local (STATION) IP address - wifi_get_ip_info(STATION_IF, &IPInfo_Local); - } - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _getResponseMulticastInterface(%i): %s\n"), p_iWiFiOpModes, IPAddress(IPInfo_Local.ip).toString().c_str());); - return IPAddress(IPInfo_Local.ip); -} - - -/** - * HELPERS - */ - -/** - * RESOURCE RECORDS - */ - -/* - * MDNSResponder::_readRRQuestion - * - * Reads a question (eg. MyESP._http._tcp.local ANY IN) from the UPD input buffer. - * - */ -bool MDNSResponder::_readRRQuestion(MDNSResponder::stcMDNS_RRQuestion& p_rRRQuestion) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion\n"));); - - bool bResult = false; - - if ((bResult = _readRRHeader(p_rRRQuestion.m_Header))) { - // Extract unicast flag from class field - p_rRRQuestion.m_bUnicast = (p_rRRQuestion.m_Header.m_Attributes.m_u16Class & 0x8000); - p_rRRQuestion.m_Header.m_Attributes.m_u16Class &= (~0x8000); - - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion ")); - _printRRDomain(p_rRRQuestion.m_Header.m_Domain); - DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X %s\n"), (unsigned)p_rRRQuestion.m_Header.m_Attributes.m_u16Type, (unsigned)p_rRRQuestion.m_Header.m_Attributes.m_u16Class, (p_rRRQuestion.m_bUnicast ? "Unicast" : "Multicast")); - ); - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_readRRAnswer - * - * Reads an answer (eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local) - * from the UDP input buffer. - * After reading the domain and type info, the further processing of the answer - * is transferred the answer specific reading functions. - * Unknown answer types are processed by the generic answer reader (to remove them - * from the input buffer). - * - */ -bool MDNSResponder::_readRRAnswer(MDNSResponder::stcMDNS_RRAnswer*& p_rpRRAnswer) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer\n"));); - - bool bResult = false; - - stcMDNS_RRHeader header; - uint32_t u32TTL; - uint16_t u16RDLength; - if ((_readRRHeader(header)) && - (_udpRead32(u32TTL)) && - (_udpRead16(u16RDLength))) { - - /*DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: Reading 0x%04X answer (class:0x%04X, TTL:%u, RDLength:%u) for "), header.m_Attributes.m_u16Type, header.m_Attributes.m_u16Class, u32TTL, u16RDLength); - _printRRDomain(header.m_Domain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - );*/ - - switch (header.m_Attributes.m_u16Type & (~0x8000)) { // Topmost bit might carry 'cache flush' flag -#ifdef MDNS_IP4_SUPPORT - case DNS_RRTYPE_A: - p_rpRRAnswer = new stcMDNS_RRAnswerA(header, u32TTL); - bResult = _readRRAnswerA(*(stcMDNS_RRAnswerA*&)p_rpRRAnswer, u16RDLength); - break; -#endif - case DNS_RRTYPE_PTR: - p_rpRRAnswer = new stcMDNS_RRAnswerPTR(header, u32TTL); - bResult = _readRRAnswerPTR(*(stcMDNS_RRAnswerPTR*&)p_rpRRAnswer, u16RDLength); - break; - case DNS_RRTYPE_TXT: - p_rpRRAnswer = new stcMDNS_RRAnswerTXT(header, u32TTL); - bResult = _readRRAnswerTXT(*(stcMDNS_RRAnswerTXT*&)p_rpRRAnswer, u16RDLength); - break; -#ifdef MDNS_IP6_SUPPORT - case DNS_RRTYPE_AAAA: - p_rpRRAnswer = new stcMDNS_RRAnswerAAAA(header, u32TTL); - bResult = _readRRAnswerAAAA(*(stcMDNS_RRAnswerAAAA*&)p_rpRRAnswer, u16RDLength); - break; -#endif - case DNS_RRTYPE_SRV: - p_rpRRAnswer = new stcMDNS_RRAnswerSRV(header, u32TTL); - bResult = _readRRAnswerSRV(*(stcMDNS_RRAnswerSRV*&)p_rpRRAnswer, u16RDLength); - break; - default: - p_rpRRAnswer = new stcMDNS_RRAnswerGeneric(header, u32TTL); - bResult = _readRRAnswerGeneric(*(stcMDNS_RRAnswerGeneric*&)p_rpRRAnswer, u16RDLength); - break; - } - DEBUG_EX_INFO( - if ((bResult) && - (p_rpRRAnswer)) { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: ")); - _printRRDomain(p_rpRRAnswer->m_Header.m_Domain); - DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, RDLength:%u "), p_rpRRAnswer->m_Header.m_Attributes.m_u16Type, p_rpRRAnswer->m_Header.m_Attributes.m_u16Class, p_rpRRAnswer->m_u32TTL, u16RDLength); - switch (header.m_Attributes.m_u16Type & (~0x8000)) { // Topmost bit might carry 'cache flush' flag -#ifdef MDNS_IP4_SUPPORT - case DNS_RRTYPE_A: - DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); - break; -#endif - case DNS_RRTYPE_PTR: - DEBUG_OUTPUT.printf_P(PSTR("PTR ")); - _printRRDomain(((stcMDNS_RRAnswerPTR*&)p_rpRRAnswer)->m_PTRDomain); - break; - case DNS_RRTYPE_TXT: { - size_t stTxtLength = ((stcMDNS_RRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_strLength(); - char* pTxts = new char[stTxtLength]; - if (pTxts) { - ((stcMDNS_RRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_str(pTxts); - DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts); - delete[] pTxts; - } - break; - } -#ifdef MDNS_IP6_SUPPORT - case DNS_RRTYPE_AAAA: - DEBUG_OUTPUT.printf_P(PSTR("AAAA IP:%s"), ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); - break; -#endif - case DNS_RRTYPE_SRV: - DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "), ((stcMDNS_RRAnswerSRV*&)p_rpRRAnswer)->m_u16Port); - _printRRDomain(((stcMDNS_RRAnswerSRV*&)p_rpRRAnswer)->m_SRVDomain); - break; - default: - DEBUG_OUTPUT.printf_P(PSTR("generic ")); - break; - } - DEBUG_OUTPUT.printf_P(PSTR("\n")); - } - else { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: FAILED to read specific answer of type 0x%04X!\n"), p_rpRRAnswer->m_Header.m_Attributes.m_u16Type); - } - ); // DEBUG_EX_INFO - } - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: FAILED!\n"));); - return bResult; -} - -#ifdef MDNS_IP4_SUPPORT - /* - * MDNSResponder::_readRRAnswerA - */ - bool MDNSResponder::_readRRAnswerA(MDNSResponder::stcMDNS_RRAnswerA& p_rRRAnswerA, - uint16_t p_u16RDLength) { - - uint32_t u32IP4Address; - bool bResult = ((MDNS_IP4_SIZE == p_u16RDLength) && - (_udpReadBuffer((unsigned char*)&u32IP4Address, MDNS_IP4_SIZE)) && - ((p_rRRAnswerA.m_IPAddress = IPAddress(u32IP4Address)))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerA: FAILED!\n"));); - return bResult; - } -#endif - -/* - * MDNSResponder::_readRRAnswerPTR - */ -bool MDNSResponder::_readRRAnswerPTR(MDNSResponder::stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, - uint16_t p_u16RDLength) { - - bool bResult = ((p_u16RDLength) && - (_readRRDomain(p_rRRAnswerPTR.m_PTRDomain))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerPTR: FAILED!\n"));); - return bResult; -} - -/* - * MDNSResponder::_readRRAnswerTXT - * - * Read TXT items from a buffer like 4c#=15ff=20 - */ -bool MDNSResponder::_readRRAnswerTXT(MDNSResponder::stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, - uint16_t p_u16RDLength) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: RDLength:%u\n"), p_u16RDLength);); - bool bResult = true; - - p_rRRAnswerTXT.clear(); - if (p_u16RDLength) { - bResult = false; - - unsigned char* pucBuffer = new unsigned char[p_u16RDLength]; - if (pucBuffer) { - if (_udpReadBuffer(pucBuffer, p_u16RDLength)) { - bResult = true; - - const unsigned char* pucCursor = pucBuffer; - while ((pucCursor < (pucBuffer + p_u16RDLength)) && - (bResult)) { - bResult = false; - - stcMDNSServiceTxt* pTxt = 0; - unsigned char ucLength = *pucCursor++; // Length of the next txt item - if (ucLength) { - DEBUG_EX_INFO( - static char sacBuffer[64]; *sacBuffer = 0; - uint8_t u8MaxLength = ((ucLength > (sizeof(sacBuffer) - 1)) ? (sizeof(sacBuffer) - 1) : ucLength); - os_strncpy(sacBuffer, (const char*)pucCursor, u8MaxLength); sacBuffer[u8MaxLength] = 0; - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: Item(%u): %s\n"), ucLength, sacBuffer); - ); - - unsigned char* pucEqualSign = (unsigned char*)os_strchr((const char*)pucCursor, '='); // Position of the '=' sign - unsigned char ucKeyLength; - if ((pucEqualSign) && - ((ucKeyLength = (pucEqualSign - pucCursor)))) { - unsigned char ucValueLength = (ucLength - (pucEqualSign - pucCursor + 1)); - bResult = (((pTxt = new stcMDNSServiceTxt)) && - (pTxt->setKey((const char*)pucCursor, ucKeyLength)) && - (pTxt->setValue((const char*)(pucEqualSign + 1), ucValueLength))); - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: INVALID TXT format (No '=')!\n"));); - } - pucCursor += ucLength; - } - else { // no/zero length TXT - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: TXT answer contains no items.\n"));); - bResult = true; - } - - if ((bResult) && - (pTxt)) { // Everythings fine so far - // Link TXT item to answer TXTs - pTxt->m_pNext = p_rRRAnswerTXT.m_Txts.m_pTxts; - p_rRRAnswerTXT.m_Txts.m_pTxts = pTxt; - } - else { // At least no TXT (migth be OK, if length was 0) OR an error - if (!bResult) { - DEBUG_EX_ERR( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to read TXT item!\n")); - DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n")); - _udpDump((m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - ); - } - if (pTxt) { - delete pTxt; - pTxt = 0; - } - p_rRRAnswerTXT.clear(); - } - } // while - - DEBUG_EX_ERR( - if (!bResult) { // Some failure - DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n")); - _udpDump((m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - } - ); - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to read TXT content!\n"));); - } - // Clean up - delete[] pucBuffer; - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to alloc buffer for TXT content!\n"));); - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: WARNING! No content!\n"));); - } - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED!\n"));); - return bResult; -} - -#ifdef MDNS_IP6_SUPPORT - bool MDNSResponder::_readRRAnswerAAAA(MDNSResponder::stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, - uint16_t p_u16RDLength) { - bool bResult = false; - // TODO: Implement - return bResult; - } -#endif - -/* - * MDNSResponder::_readRRAnswerSRV - */ -bool MDNSResponder::_readRRAnswerSRV(MDNSResponder::stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, - uint16_t p_u16RDLength) { - - bool bResult = (((3 * sizeof(uint16_t)) < p_u16RDLength) && - (_udpRead16(p_rRRAnswerSRV.m_u16Priority)) && - (_udpRead16(p_rRRAnswerSRV.m_u16Weight)) && - (_udpRead16(p_rRRAnswerSRV.m_u16Port)) && - (_readRRDomain(p_rRRAnswerSRV.m_SRVDomain))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerSRV: FAILED!\n"));); - return bResult; -} - -/* - * MDNSResponder::_readRRAnswerGeneric - */ -bool MDNSResponder::_readRRAnswerGeneric(MDNSResponder::stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, - uint16_t p_u16RDLength) { - bool bResult = (0 == p_u16RDLength); - - p_rRRAnswerGeneric.clear(); - if (((p_rRRAnswerGeneric.m_u16RDLength = p_u16RDLength)) && - ((p_rRRAnswerGeneric.m_pu8RDData = new unsigned char[p_rRRAnswerGeneric.m_u16RDLength]))) { - - bResult = _udpReadBuffer(p_rRRAnswerGeneric.m_pu8RDData, p_rRRAnswerGeneric.m_u16RDLength); - } - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerGeneric: FAILED!\n"));); - return bResult; -} - -/* - * MDNSResponder::_readRRHeader - */ -bool MDNSResponder::_readRRHeader(MDNSResponder::stcMDNS_RRHeader& p_rRRHeader) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRHeader\n"));); - - bool bResult = ((_readRRDomain(p_rRRHeader.m_Domain)) && - (_readRRAttributes(p_rRRHeader.m_Attributes))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRHeader: FAILED!\n"));); - return bResult; -} - -/* - * MDNSResponder::_readRRDomain - * - * Reads a (maybe multilevel compressed) domain from the UDP input buffer. - * - */ -bool MDNSResponder::_readRRDomain(MDNSResponder::stcMDNS_RRDomain& p_rRRDomain) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain\n"));); - - bool bResult = ((p_rRRDomain.clear()) && - (_readRRDomain_Loop(p_rRRDomain, 0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain: FAILED!\n"));); - return bResult; -} - -/* - * MDNSResponder::_readRRDomain_Loop - * - * Reads a domain from the UDP input buffer. For every compression level, the functions - * calls itself recursively. To avoid endless recursion because of malformed MDNS records, - * the maximum recursion depth is set by MDNS_DOMAIN_MAX_REDIRCTION. - * - */ -bool MDNSResponder::_readRRDomain_Loop(MDNSResponder::stcMDNS_RRDomain& p_rRRDomain, - uint8_t p_u8Depth) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u)\n"), p_u8Depth);); - - bool bResult = false; - - if (MDNS_DOMAIN_MAX_REDIRCTION >= p_u8Depth) { - bResult = true; - - uint8_t u8Len = 0; - do { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Offset:%u p0:%02x\n"), p_u8Depth, m_pUDPContext->tell(), m_pUDPContext->peek());); - _udpRead8(u8Len); - - if (u8Len & MDNS_DOMAIN_COMPRESS_MARK) { - // Compressed label(s) - uint16_t u16Offset = ((u8Len & ~MDNS_DOMAIN_COMPRESS_MARK) << 8); // Implicit BE to LE conversion! - _udpRead8(u8Len); - u16Offset |= u8Len; - - if (m_pUDPContext->isValidOffset(u16Offset)) { - size_t stCurrentPosition = m_pUDPContext->tell(); // Prepare return from recursion - - //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Redirecting from %u to %u!\n"), p_u8Depth, stCurrentPosition, u16Offset);); - m_pUDPContext->seek(u16Offset); - if (_readRRDomain_Loop(p_rRRDomain, p_u8Depth + 1)) { // Do recursion - //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Succeeded to read redirected label! Returning to %u\n"), p_u8Depth, stCurrentPosition);); - m_pUDPContext->seek(stCurrentPosition); // Restore after recursion - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): FAILED to read redirected label!\n"), p_u8Depth);); - bResult = false; - } - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): INVALID offset in redirection!\n"), p_u8Depth);); - bResult = false; - } - break; - } - else { - // Normal (uncompressed) label (maybe '\0' only) - if (MDNS_DOMAIN_MAXLENGTH > (p_rRRDomain.m_u16NameLength + u8Len)) { - // Add length byte - p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength] = u8Len; - ++(p_rRRDomain.m_u16NameLength); - if (u8Len) { // Add name - if ((bResult = _udpReadBuffer((unsigned char*)&(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength]), u8Len))) { - /*DEBUG_EX_INFO( - p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength + u8Len] = 0; // Closing '\0' for printing - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Domain label (%u): %s\n"), p_u8Depth, (unsigned)(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength - 1]), &(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength])); - );*/ - - p_rRRDomain.m_u16NameLength += u8Len; - } - } - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(2) offset:%u p0:%x\n"), m_pUDPContext->tell(), m_pUDPContext->peek());); - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): ERROR! Domain name too long (%u + %u)!\n"), p_u8Depth, p_rRRDomain.m_u16NameLength, u8Len);); - bResult = false; - break; - } - } - } while ((bResult) && - (0 != u8Len)); - } - else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): ERROR! Too many redirections!\n"), p_u8Depth);); - } - return bResult; -} - -/* - * MDNSResponder::_readRRAttributes - */ -bool MDNSResponder::_readRRAttributes(MDNSResponder::stcMDNS_RRAttributes& p_rRRAttributes) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAttributes\n"));); - - bool bResult = ((_udpRead16(p_rRRAttributes.m_u16Type)) && - (_udpRead16(p_rRRAttributes.m_u16Class))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAttributes: FAILED!\n"));); - return bResult; -} - - -/* - * DOMAIN NAMES - */ - -/* - * MDNSResponder::_buildDomainForHost - * - * Builds a MDNS host domain (eg. esp8266.local) for the given hostname. - * - */ -bool MDNSResponder::_buildDomainForHost(const char* p_pcHostname, - MDNSResponder::stcMDNS_RRDomain& p_rHostDomain) const { - - p_rHostDomain.clear(); - bool bResult = ((p_pcHostname) && - (*p_pcHostname) && - (p_rHostDomain.addLabel(p_pcHostname)) && - (p_rHostDomain.addLabel(scpcLocal)) && - (p_rHostDomain.addLabel(0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForHost: FAILED!\n"));); - return bResult; -} - -/* - * MDNSResponder::_buildDomainForDNSSD - * - * Builds the '_services._dns-sd._udp.local' domain. - * Used while detecting generic service enum question (DNS-SD) and answering these questions. - * - */ -bool MDNSResponder::_buildDomainForDNSSD(MDNSResponder::stcMDNS_RRDomain& p_rDNSSDDomain) const { - - p_rDNSSDDomain.clear(); - bool bResult = ((p_rDNSSDDomain.addLabel(scpcServices, true)) && - (p_rDNSSDDomain.addLabel(scpcDNSSD, true)) && - (p_rDNSSDDomain.addLabel(scpcUDP, true)) && - (p_rDNSSDDomain.addLabel(scpcLocal)) && - (p_rDNSSDDomain.addLabel(0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForDNSSD: FAILED!\n"));); - return bResult; -} - -/* - * MDNSResponder::_buildDomainForService - * - * Builds the domain for the given service (eg. _http._tcp.local or - * MyESP._http._tcp.local (if p_bIncludeName is set)). - * - */ -bool MDNSResponder::_buildDomainForService(const MDNSResponder::stcMDNSService& p_Service, - bool p_bIncludeName, - MDNSResponder::stcMDNS_RRDomain& p_rServiceDomain) const { - - p_rServiceDomain.clear(); - bool bResult = (((!p_bIncludeName) || - (p_rServiceDomain.addLabel(p_Service.m_pcName))) && - (p_rServiceDomain.addLabel(p_Service.m_pcService, true)) && - (p_rServiceDomain.addLabel(p_Service.m_pcProtocol, true)) && - (p_rServiceDomain.addLabel(scpcLocal)) && - (p_rServiceDomain.addLabel(0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForService: FAILED!\n"));); - return bResult; -} - -/* - * MDNSResponder::_buildDomainForService - * - * Builds the domain for the given service properties (eg. _http._tcp.local). - * The usual prepended '_' are added, if missing in the input strings. - * - */ -bool MDNSResponder::_buildDomainForService(const char* p_pcService, - const char* p_pcProtocol, - MDNSResponder::stcMDNS_RRDomain& p_rServiceDomain) const { - - p_rServiceDomain.clear(); - bool bResult = ((p_pcService) && - (p_pcProtocol) && - (p_rServiceDomain.addLabel(p_pcService, ('_' != *p_pcService))) && - (p_rServiceDomain.addLabel(p_pcProtocol, ('_' != *p_pcProtocol))) && - (p_rServiceDomain.addLabel(scpcLocal)) && - (p_rServiceDomain.addLabel(0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForService: FAILED for (%s.%s)!\n"), (p_pcService ?: "-"), (p_pcProtocol ?: "-"));); - return bResult; -} - -#ifdef MDNS_IP4_SUPPORT - /* - * MDNSResponder::_buildDomainForReverseIP4 - * - * The IP4 address is stringized by printing the four address bytes into a char buffer in reverse order - * and adding 'in-addr.arpa' (eg. 012.789.456.123.in-addr.arpa). - * Used while detecting reverse IP4 questions and answering these - */ - bool MDNSResponder::_buildDomainForReverseIP4(IPAddress p_IP4Address, - MDNSResponder::stcMDNS_RRDomain& p_rReverseIP4Domain) const { - - bool bResult = true; - - p_rReverseIP4Domain.clear(); - - char acBuffer[32]; - for (int i=MDNS_IP4_SIZE; ((bResult) && (i>=1)); --i) { - itoa(p_IP4Address[i - 1], acBuffer, 10); - bResult = p_rReverseIP4Domain.addLabel(acBuffer); - } - bResult = ((bResult) && - (p_rReverseIP4Domain.addLabel(scpcReverseIP4Domain)) && - (p_rReverseIP4Domain.addLabel(scpcReverseTopDomain)) && - (p_rReverseIP4Domain.addLabel(0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForReverseIP4: FAILED!\n"));); - return bResult; - } -#endif - -#ifdef MDNS_IP6_SUPPORT - /* - * MDNSResponder::_buildDomainForReverseIP6 - * - * Used while detecting reverse IP6 questions and answering these - */ - bool MDNSResponder::_buildDomainForReverseIP6(IPAddress p_IP4Address, - MDNSResponder::stcMDNS_RRDomain& p_rReverseIP6Domain) const { - // TODO: Implement - return false; - } -#endif - - -/* - * UDP - */ - -/* - * MDNSResponder::_udpReadBuffer - */ -bool MDNSResponder::_udpReadBuffer(unsigned char* p_pBuffer, - size_t p_stLength) { - - bool bResult = ((m_pUDPContext) && - (true/*m_pUDPContext->getSize() > p_stLength*/) && - (p_pBuffer) && - (p_stLength) && - ((p_stLength == m_pUDPContext->read((char*)p_pBuffer, p_stLength)))); - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _udpReadBuffer: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_udpRead8 - */ -bool MDNSResponder::_udpRead8(uint8_t& p_ru8Value) { - - return _udpReadBuffer((unsigned char*)&p_ru8Value, sizeof(p_ru8Value)); -} - -/* - * MDNSResponder::_udpRead16 - */ -bool MDNSResponder::_udpRead16(uint16_t& p_ru16Value) { - - bool bResult = false; - - if (_udpReadBuffer((unsigned char*)&p_ru16Value, sizeof(p_ru16Value))) { - p_ru16Value = lwip_ntohs(p_ru16Value); - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::_udpRead32 - */ -bool MDNSResponder::_udpRead32(uint32_t& p_ru32Value) { - - bool bResult = false; - - if (_udpReadBuffer((unsigned char*)&p_ru32Value, sizeof(p_ru32Value))) { - p_ru32Value = lwip_ntohl(p_ru32Value); - bResult = true; - } - return bResult; -} - -/* - * MDNSResponder::_udpAppendBuffer - */ -bool MDNSResponder::_udpAppendBuffer(const unsigned char* p_pcBuffer, - size_t p_stLength) { - - bool bResult = ((m_pUDPContext) && - (p_pcBuffer) && - (p_stLength) && - (p_stLength == m_pUDPContext->append((const char*)p_pcBuffer, p_stLength))); - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _udpAppendBuffer: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_udpAppend8 - */ -bool MDNSResponder::_udpAppend8(uint8_t p_u8Value) { - - return (_udpAppendBuffer((unsigned char*)&p_u8Value, sizeof(p_u8Value))); -} - -/* - * MDNSResponder::_udpAppend16 - */ -bool MDNSResponder::_udpAppend16(uint16_t p_u16Value) { - - p_u16Value = lwip_htons(p_u16Value); - return (_udpAppendBuffer((unsigned char*)&p_u16Value, sizeof(p_u16Value))); -} - -/* - * MDNSResponder::_udpAppend32 - */ -bool MDNSResponder::_udpAppend32(uint32_t p_u32Value) { - - p_u32Value = lwip_htonl(p_u32Value); - return (_udpAppendBuffer((unsigned char*)&p_u32Value, sizeof(p_u32Value))); -} - -#ifdef DEBUG_ESP_MDNS_RESPONDER - /* - * MDNSResponder::_udpDump - */ - bool MDNSResponder::_udpDump(bool p_bMovePointer /*= false*/) { - - const uint8_t cu8BytesPerLine = 16; - - uint32_t u32StartPosition = m_pUDPContext->tell(); - DEBUG_OUTPUT.println("UDP Context Dump:"); - uint32_t u32Counter = 0; - uint8_t u8Byte = 0; - - while (_udpRead8(u8Byte)) { - DEBUG_OUTPUT.printf_P(PSTR("%02x %s"), u8Byte, ((++u32Counter % cu8BytesPerLine) ? "" : "\n")); - } - DEBUG_OUTPUT.printf_P(PSTR("%sDone: %u bytes\n"), (((u32Counter) && (u32Counter % cu8BytesPerLine)) ? "\n" : ""), u32Counter); - - if (!p_bMovePointer) { // Restore - m_pUDPContext->seek(u32StartPosition); - } - return true; - } - - /* - * MDNSResponder::_udpDump - */ - bool MDNSResponder::_udpDump(unsigned p_uOffset, - unsigned p_uLength) { - - if ((m_pUDPContext) && - (m_pUDPContext->isValidOffset(p_uOffset))) { - unsigned uCurrentPosition = m_pUDPContext->tell(); // Remember start position - - m_pUDPContext->seek(p_uOffset); - uint8_t u8Byte; - for (unsigned u=0; ((useek(uCurrentPosition); - } - return true; - } -#endif - - -/** - * READ/WRITE MDNS STRUCTS - */ - -/* - * MDNSResponder::_readMDNSMsgHeader - * - * Read a MDNS header from the UDP input buffer. - * | 8 | 8 | 8 | 8 | - * 00| Identifier | Flags & Codes | - * 01| Question count | Answer count | - * 02| NS answer count | Ad answer count | - * - * All 16-bit and 32-bit elements need to be translated form network coding to host coding (done in _udpRead16 and _udpRead32) - * In addition, bitfield memory order is undefined in C standard (GCC doesn't order them in the coded direction...), so they - * need some mapping here - */ -bool MDNSResponder::_readMDNSMsgHeader(MDNSResponder::stcMDNS_MsgHeader& p_rMsgHeader) { - - bool bResult = false; - - uint8_t u8B1; - uint8_t u8B2; - if ((_udpRead16(p_rMsgHeader.m_u16ID)) && - (_udpRead8(u8B1))&& - (_udpRead8(u8B2)) && - (_udpRead16(p_rMsgHeader.m_u16QDCount)) && - (_udpRead16(p_rMsgHeader.m_u16ANCount)) && - (_udpRead16(p_rMsgHeader.m_u16NSCount)) && - (_udpRead16(p_rMsgHeader.m_u16ARCount))) { - - p_rMsgHeader.m_1bQR = (u8B1 & 0x80); // Query/Responde flag - p_rMsgHeader.m_4bOpcode = (u8B1 & 0x78); // Operation code (0: Standard query, others ignored) - p_rMsgHeader.m_1bAA = (u8B1 & 0x04); // Authorative answer - p_rMsgHeader.m_1bTC = (u8B1 & 0x02); // Truncation flag - p_rMsgHeader.m_1bRD = (u8B1 & 0x01); // Recursion desired - - p_rMsgHeader.m_1bRA = (u8B2 & 0x80); // Recursion available - p_rMsgHeader.m_3bZ = (u8B2 & 0x70); // Zero - p_rMsgHeader.m_4bRCode = (u8B2 & 0x0F); // Response code - - /*DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readMDNSMsgHeader: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), - (unsigned)p_rMsgHeader.m_u16ID, - (unsigned)p_rMsgHeader.m_1bQR, (unsigned)p_rMsgHeader.m_4bOpcode, (unsigned)p_rMsgHeader.m_1bAA, (unsigned)p_rMsgHeader.m_1bTC, (unsigned)p_rMsgHeader.m_1bRD, - (unsigned)p_rMsgHeader.m_1bRA, (unsigned)p_rMsgHeader.m_4bRCode, - (unsigned)p_rMsgHeader.m_u16QDCount, - (unsigned)p_rMsgHeader.m_u16ANCount, - (unsigned)p_rMsgHeader.m_u16NSCount, - (unsigned)p_rMsgHeader.m_u16ARCount););*/ - bResult = true; - } - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readMDNSMsgHeader: FAILED!\n")); } ); - return bResult; -} - -/* - * MDNSResponder::_write8 - */ -bool MDNSResponder::_write8(uint8_t p_u8Value, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - - return ((_udpAppend8(p_u8Value)) && - (p_rSendParameter.shiftOffset(sizeof(p_u8Value)))); -} - -/* - * MDNSResponder::_write16 - */ -bool MDNSResponder::_write16(uint16_t p_u16Value, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - - return ((_udpAppend16(p_u16Value)) && - (p_rSendParameter.shiftOffset(sizeof(p_u16Value)))); -} - -/* - * MDNSResponder::_write32 - */ -bool MDNSResponder::_write32(uint32_t p_u32Value, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - - return ((_udpAppend32(p_u32Value)) && - (p_rSendParameter.shiftOffset(sizeof(p_u32Value)))); -} - -/* - * MDNSResponder::_writeMDNSMsgHeader - * - * Write MDNS header to the UDP output buffer. - * - * All 16-bit and 32-bit elements need to be translated form host coding to network coding (done in _udpAppend16 and _udpAppend32) - * In addition, bitfield memory order is undefined in C standard (GCC doesn't order them in the coded direction...), so they - * need some mapping here - */ -bool MDNSResponder::_writeMDNSMsgHeader(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - /*DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSMsgHeader: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), - (unsigned)p_MsgHeader.m_u16ID, - (unsigned)p_MsgHeader.m_1bQR, (unsigned)p_MsgHeader.m_4bOpcode, (unsigned)p_MsgHeader.m_1bAA, (unsigned)p_MsgHeader.m_1bTC, (unsigned)p_MsgHeader.m_1bRD, - (unsigned)p_MsgHeader.m_1bRA, (unsigned)p_MsgHeader.m_4bRCode, - (unsigned)p_MsgHeader.m_u16QDCount, - (unsigned)p_MsgHeader.m_u16ANCount, - (unsigned)p_MsgHeader.m_u16NSCount, - (unsigned)p_MsgHeader.m_u16ARCount););*/ - - uint8_t u8B1((p_MsgHeader.m_1bQR << 7) | (p_MsgHeader.m_4bOpcode << 3) | (p_MsgHeader.m_1bAA << 2) | (p_MsgHeader.m_1bTC << 1) | (p_MsgHeader.m_1bRD)); - uint8_t u8B2((p_MsgHeader.m_1bRA << 7) | (p_MsgHeader.m_3bZ << 4) | (p_MsgHeader.m_4bRCode)); - bool bResult = ((_write16(p_MsgHeader.m_u16ID, p_rSendParameter)) && - (_write8(u8B1, p_rSendParameter)) && - (_write8(u8B2, p_rSendParameter)) && - (_write16(p_MsgHeader.m_u16QDCount, p_rSendParameter)) && - (_write16(p_MsgHeader.m_u16ANCount, p_rSendParameter)) && - (_write16(p_MsgHeader.m_u16NSCount, p_rSendParameter)) && - (_write16(p_MsgHeader.m_u16ARCount, p_rSendParameter))); - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSMsgHeader: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_writeRRAttributes - */ -bool MDNSResponder::_writeMDNSRRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Attributes, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - - bool bResult = ((_write16(p_Attributes.m_u16Type, p_rSendParameter)) && - (_write16(p_Attributes.m_u16Class, p_rSendParameter))); - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSRRAttributes: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_writeMDNSRRDomain - */ -bool MDNSResponder::_writeMDNSRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_Domain, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - - bool bResult = ((_udpAppendBuffer((const unsigned char*)p_Domain.m_acName, p_Domain.m_u16NameLength)) && - (p_rSendParameter.shiftOffset(p_Domain.m_u16NameLength))); - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSRRDomain: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_writeMDNSHostDomain - * - * Write a host domain to the UDP output buffer. - * If the domain record is part of the answer, the records length is - * prepended (p_bPrependRDLength is set). - * - * A very simple form of name compression is applied here: - * If the domain is written to the UDP output buffer, the write offset is stored - * together with a domain id (the pointer) in a p_rSendParameter substructure (cache). - * If the same domain (pointer) should be written to the UDP output later again, - * the old offset is retrieved from the cache, marked as a compressed domain offset - * and written to the output buffer. - * - */ -bool MDNSResponder::_writeMDNSHostDomain(const char* p_pcHostname, - bool p_bPrependRDLength, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - - // The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV' - uint16_t u16CachedDomainOffset = p_rSendParameter.findCachedDomainOffset((const void*)p_pcHostname, false); - - stcMDNS_RRDomain hostDomain; - bool bResult = (u16CachedDomainOffset - // Found cached domain -> mark as compressed domain - ? ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset - ((!p_bPrependRDLength) || - (_write16(2, p_rSendParameter))) && // Length of 'Cxxx' - (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) - (_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter))) - // No cached domain -> add this domain to cache and write full domain name - : ((_buildDomainForHost(p_pcHostname, hostDomain)) && // eg. esp8266.local - ((!p_bPrependRDLength) || - (_write16(hostDomain.m_u16NameLength, p_rSendParameter))) && // RDLength (if needed) - (p_rSendParameter.addDomainCacheItem((const void*)p_pcHostname, false, p_rSendParameter.m_u16Offset)) && - (_writeMDNSRRDomain(hostDomain, p_rSendParameter)))); - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSHostDomain: FAILED!\n")); }); - return bResult; - -} - -/* - * MDNSResponder::_writeMDNSServiceDomain - * - * Write a service domain to the UDP output buffer. - * If the domain record is part of the answer, the records length is - * prepended (p_bPrependRDLength is set). - * - * A very simple form of name compression is applied here: see '_writeMDNSHostDomain' - * The cache differentiates of course between service domains which includes - * the instance name (p_bIncludeName is set) and thoose who don't. - * - */ -bool MDNSResponder::_writeMDNSServiceDomain(const MDNSResponder::stcMDNSService& p_Service, - bool p_bIncludeName, - bool p_bPrependRDLength, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - - // The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV' - uint16_t u16CachedDomainOffset = p_rSendParameter.findCachedDomainOffset((const void*)&p_Service, p_bIncludeName); - - stcMDNS_RRDomain serviceDomain; - bool bResult = (u16CachedDomainOffset - // Found cached domain -> mark as compressed domain - ? ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset - ((!p_bPrependRDLength) || - (_write16(2, p_rSendParameter))) && // Lenght of 'Cxxx' - (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) - (_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter))) - // No cached domain -> add this domain to cache and write full domain name - : ((_buildDomainForService(p_Service, p_bIncludeName, serviceDomain)) && // eg. MyESP._http._tcp.local - ((!p_bPrependRDLength) || - (_write16(serviceDomain.m_u16NameLength, p_rSendParameter))) && // RDLength (if needed) - (p_rSendParameter.addDomainCacheItem((const void*)&p_Service, p_bIncludeName, p_rSendParameter.m_u16Offset)) && - (_writeMDNSRRDomain(serviceDomain, p_rSendParameter)))); - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSServiceDomain: FAILED!\n")); }); - return bResult; - -} - -/* - * MDNSResponder::_writeMDNSQuestion - * - * Write a MDNS question to the UDP output buffer - * - * QNAME (host/service domain, eg. esp8266.local) - * QTYPE (16bit, eg. ANY) - * QCLASS (16bit, eg. IN) - * - */ -bool MDNSResponder::_writeMDNSQuestion(MDNSResponder::stcMDNS_RRQuestion& p_Question, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSQuestion\n"));); - - bool bResult = ((_writeMDNSRRDomain(p_Question.m_Header.m_Domain, p_rSendParameter)) && - (_writeMDNSRRAttributes(p_Question.m_Header.m_Attributes, p_rSendParameter))); - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSQuestion: FAILED!\n")); }); - return bResult; - -} - - -#ifdef MDNS_IP4_SUPPORT - /* - * MDNSResponder::_writeMDNSAnswer_A - * - * Write a MDNS A answer to the UDP output buffer. - * - * NAME (var, host/service domain, eg. esp8266.local - * TYPE (16bit, eg. A) - * CLASS (16bit, eg. IN) - * TTL (32bit, eg. 120) - * RDLENGTH (16bit, eg 4) - * RDATA (var, eg. 123.456.789.012) - * - * eg. esp8266.local A 0x8001 120 4 123.456.789.012 - * Ref: http://www.zytrax.com/books/dns/ch8/a.html - */ - bool MDNSResponder::_writeMDNSAnswer_A(IPAddress p_IPAddress, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_A (%s)\n"), p_IPAddress.toString().c_str());); - - stcMDNS_RRAttributes attributes(DNS_RRTYPE_A, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - const unsigned char aucIPAddress[MDNS_IP4_SIZE] = { p_IPAddress[0], p_IPAddress[1], p_IPAddress[2], p_IPAddress[3] }; - bool bResult = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) && - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL - (_write16(MDNS_IP4_SIZE, p_rSendParameter)) && // RDLength - (_udpAppendBuffer(aucIPAddress, MDNS_IP4_SIZE)) && // RData - (p_rSendParameter.shiftOffset(MDNS_IP4_SIZE))); - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_A: FAILED!\n")); }); - return bResult; - - } - - /* - * MDNSResponder::_writeMDNSAnswer_PTR_IP4 - * - * Write a MDNS reverse IP4 PTR answer to the UDP output buffer. - * See: '_writeMDNSAnswer_A' - * - * eg. 012.789.456.123.in-addr.arpa PTR 0x8001 120 15 esp8266.local - * Used while answering reverse IP4 questions - */ - bool MDNSResponder::_writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP4 (%s)\n"), p_IPAddress.toString().c_str());); - - stcMDNS_RRDomain reverseIP4Domain; - stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - stcMDNS_RRDomain hostDomain; - bool bResult = ((_buildDomainForReverseIP4(p_IPAddress, reverseIP4Domain)) && // 012.789.456.123.in-addr.arpa - (_writeMDNSRRDomain(reverseIP4Domain, p_rSendParameter)) && - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL - (_writeMDNSHostDomain(m_pcHostname, true, p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local) - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP4: FAILED!\n")); }); - return bResult; - } -#endif - -/* - * MDNSResponder::_writeMDNSAnswer_PTR_TYPE - * - * Write a MDNS PTR answer to the UDP output buffer. - * See: '_writeMDNSAnswer_A' - * - * PTR all-services -> service type - * eg. _services._dns-sd._udp.local PTR 0x8001 5400 xx _http._tcp.local - * http://www.zytrax.com/books/dns/ch8/ptr.html - */ -bool MDNSResponder::_writeMDNSAnswer_PTR_TYPE(MDNSResponder::stcMDNSService& p_rService, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_TYPE\n"));); - - stcMDNS_RRDomain dnssdDomain; - stcMDNS_RRDomain serviceDomain; - stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, DNS_RRCLASS_IN); // No cache flush! only INternet - bool bResult = ((_buildDomainForDNSSD(dnssdDomain)) && // _services._dns-sd._udp.local - (_writeMDNSRRDomain(dnssdDomain, p_rSendParameter)) && - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL - (_writeMDNSServiceDomain(p_rService, false, true, p_rSendParameter))); // RDLength & RData (service domain, eg. _http._tcp.local) - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_TYPE: FAILED!\n")); }); - return bResult; -} - -/* - * MDNSResponder::_writeMDNSAnswer_PTR_NAME - * - * Write a MDNS PTR answer to the UDP output buffer. - * See: '_writeMDNSAnswer_A' - * - * PTR service type -> service name - * eg. _http.tcp.local PTR 0x8001 120 xx myESP._http._tcp.local - * http://www.zytrax.com/books/dns/ch8/ptr.html - */ -bool MDNSResponder::_writeMDNSAnswer_PTR_NAME(MDNSResponder::stcMDNSService& p_rService, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_NAME\n"));); - - stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, DNS_RRCLASS_IN); // No cache flush! only INternet - bool bResult = ((_writeMDNSServiceDomain(p_rService, false, false, p_rSendParameter)) && // _http._tcp.local - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL - (_writeMDNSServiceDomain(p_rService, true, true, p_rSendParameter))); // RDLength & RData (service domain, eg. MyESP._http._tcp.local) - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_NAME: FAILED!\n")); }); - return bResult; -} - - -/* - * MDNSResponder::_writeMDNSAnswer_TXT - * - * Write a MDNS TXT answer to the UDP output buffer. - * See: '_writeMDNSAnswer_A' - * - * The TXT items in the RDATA block are 'length byte encoded': [len]vardata - * - * eg. myESP._http._tcp.local TXT 0x8001 120 4 c#=1 - * http://www.zytrax.com/books/dns/ch8/txt.html - */ -bool MDNSResponder::_writeMDNSAnswer_TXT(MDNSResponder::stcMDNSService& p_rService, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT\n"));); - - bool bResult = false; - - stcMDNS_RRAttributes attributes(DNS_RRTYPE_TXT, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - - if ((_collectServiceTxts(p_rService)) && - (_writeMDNSServiceDomain(p_rService, true, false, p_rSendParameter)) && // MyESP._http._tcp.local - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL - (_write16(p_rService.m_Txts.length(), p_rSendParameter))) { // RDLength - - bResult = true; - // RData Txts - for (stcMDNSServiceTxt* pTxt=p_rService.m_Txts.m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) { - unsigned char ucLengthByte = pTxt->length(); - bResult = ((_udpAppendBuffer((unsigned char*)&ucLengthByte, sizeof(ucLengthByte))) && // Length - (p_rSendParameter.shiftOffset(sizeof(ucLengthByte))) && - ((size_t)os_strlen(pTxt->m_pcKey) == m_pUDPContext->append(pTxt->m_pcKey, os_strlen(pTxt->m_pcKey))) && // Key - (p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcKey))) && - (1 == m_pUDPContext->append("=", 1)) && // = - (p_rSendParameter.shiftOffset(1)) && - ((!pTxt->m_pcValue) || - (((size_t)os_strlen(pTxt->m_pcValue) == m_pUDPContext->append(pTxt->m_pcValue, os_strlen(pTxt->m_pcValue))) && // Value - (p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcValue)))))); - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT: FAILED to write %sTxt %s=%s!\n"), (pTxt->m_bTemp ? "temp. " : ""), (pTxt->m_pcKey ?: "?"), (pTxt->m_pcValue ?: "?")); }); - } - } - _releaseTempServiceTxts(p_rService); - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT: FAILED!\n")); }); - return bResult; -} - -#ifdef MDNS_IP6_SUPPORT - /* - * MDNSResponder::_writeMDNSAnswer_AAAA - * - * Write a MDNS AAAA answer to the UDP output buffer. - * See: '_writeMDNSAnswer_A' - * - * eg. esp8266.local AAAA 0x8001 120 16 xxxx::xx - * http://www.zytrax.com/books/dns/ch8/aaaa.html - */ - bool MDNSResponder::_writeMDNSAnswer_AAAA(IPAddress p_IPAddress, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA\n"));); - - stcMDNS_RRAttributes attributes(DNS_RRTYPE_AAAA, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - bool bResult = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) && // esp8266.local - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL - (_write16(MDNS_IP6_SIZE, p_rSendParameter)) && // RDLength - (false /*TODO: IP6 version of: _udpAppendBuffer((uint32_t)p_IPAddress, MDNS_IP4_SIZE)*/)); // RData - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA: FAILED!\n")); }); - return bResult; - } - - /* - * MDNSResponder::_writeMDNSAnswer_PTR_IP6 - * - * Write a MDNS reverse IP6 PTR answer to the UDP output buffer. - * See: '_writeMDNSAnswer_A' - * - * eg. xxxx::xx.in6.arpa PTR 0x8001 120 15 esp8266.local - * Used while answering reverse IP6 questions - */ - bool MDNSResponder::_writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP6\n"));); - - stcMDNS_RRDomain reverseIP6Domain; - stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - bool bResult = ((_buildDomainForReverseIP6(p_IPAddress, reverseIP6Domain)) && // xxxx::xx.ip6.arpa - (_writeMDNSRRDomain(reverseIP6Domain, p_rSendParameter)) && - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL - (_writeMDNSHostDomain(m_pcHostname, true, p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local) - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP6: FAILED!\n")); }); - return bResult; - } -#endif - -/* - * MDNSResponder::_writeMDNSAnswer_SRV - * - * eg. MyESP._http.tcp.local SRV 0x8001 120 0 0 60068 esp8266.local - * http://www.zytrax.com/books/dns/ch8/srv.html ???? Include instance name ???? - */ -bool MDNSResponder::_writeMDNSAnswer_SRV(MDNSResponder::stcMDNSService& p_rService, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_SRV\n"));); - - uint16_t u16CachedDomainOffset = (p_rSendParameter.m_bLegacyQuery - ? 0 - : p_rSendParameter.findCachedDomainOffset((const void*)m_pcHostname, false)); - - stcMDNS_RRAttributes attributes(DNS_RRTYPE_SRV, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - stcMDNS_RRDomain hostDomain; - bool bResult = ((_writeMDNSServiceDomain(p_rService, true, false, p_rSendParameter)) && // MyESP._http._tcp.local - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL - (!u16CachedDomainOffset - // No cache for domain name (or no compression allowed) - ? ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (_write16((sizeof(uint16_t /*Prio*/) + // RDLength - sizeof(uint16_t /*Weight*/) + - sizeof(uint16_t /*Port*/) + - hostDomain.m_u16NameLength), p_rSendParameter)) && // Domain length - (_write16(MDNS_SRV_PRIORITY, p_rSendParameter)) && // Priority - (_write16(MDNS_SRV_WEIGHT, p_rSendParameter)) && // Weight - (_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port - (p_rSendParameter.addDomainCacheItem((const void*)m_pcHostname, false, p_rSendParameter.m_u16Offset)) && - (_writeMDNSRRDomain(hostDomain, p_rSendParameter))) // Host, eg. esp8266.local - // Cache available for domain - : ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset - (_write16((sizeof(uint16_t /*Prio*/) + // RDLength - sizeof(uint16_t /*Weight*/) + - sizeof(uint16_t /*Port*/) + - 2), p_rSendParameter)) && // Length of 'C0xx' - (_write16(MDNS_SRV_PRIORITY, p_rSendParameter)) && // Priority - (_write16(MDNS_SRV_WEIGHT, p_rSendParameter)) && // Weight - (_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port - (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) - (_write8((uint8_t)u16CachedDomainOffset, p_rSendParameter))))); // Offset - - DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_SRV: FAILED!\n")); }); - return bResult; -} - -} // namespace MDNSImplementation - -} // namespace esp8266 - - - - - - +/* + LEAmDNS_Transfer.cpp + + License (MIT license): + 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. + +*/ + +extern "C" { +#include "user_interface.h" +} + +#include "LEAmDNS_lwIPdefs.h" +#include "LEAmDNS_Priv.h" + + +namespace esp8266 +{ + +/* + LEAmDNS +*/ +namespace MDNSImplementation +{ + +/** + CONST STRINGS +*/ +static const char* scpcLocal = "local"; +static const char* scpcServices = "services"; +static const char* scpcDNSSD = "dns-sd"; +static const char* scpcUDP = "udp"; +//static const char* scpcTCP = "tcp"; + +#ifdef MDNS_IP4_SUPPORT +static const char* scpcReverseIP4Domain = "in-addr"; +#endif +#ifdef MDNS_IP6_SUPPORT +static const char* scpcReverseIP6Domain = "ip6"; +#endif +static const char* scpcReverseTopDomain = "arpa"; + +/** + TRANSFER +*/ + + +/** + SENDING +*/ + +/* + MDNSResponder::_sendMDNSMessage + + Unicast responses are prepared and sent directly to the querier. + Multicast responses or queries are transferred to _sendMDNSMessage_Multicast + + Any reply flags in installed services are removed at the end! + +*/ +bool MDNSResponder::_sendMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + + bool bResult = true; + + if (p_rSendParameter.m_bResponse) + { + if (p_rSendParameter.m_bUnicast) // Unicast response -> Send to querier + { + DEBUG_EX_ERR(if (!m_pUDPContext->getRemoteAddress()) + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage: MISSING remote address for response!\n")); + }); + IPAddress ipRemote; + ipRemote = m_pUDPContext->getRemoteAddress(); + bResult = ((_prepareMDNSMessage(p_rSendParameter, _getResponseMulticastInterface(SOFTAP_MODE | STATION_MODE))) && + (m_pUDPContext->send(ipRemote, m_pUDPContext->getRemotePort()))); + } + else // Multicast response -> Send via the same network interface, that received the query + { + bResult = _sendMDNSMessage_Multicast(p_rSendParameter, (SOFTAP_MODE | STATION_MODE)); + } + } + else // Multicast query -> Send by all available network interfaces + { + const int caiWiFiOpModes[2] = { SOFTAP_MODE, STATION_MODE }; + for (int iInterfaceId = 0; ((bResult) && (iInterfaceId <= 1)); ++iInterfaceId) + { + if (wifi_get_opmode() & caiWiFiOpModes[iInterfaceId]) + { + bResult = _sendMDNSMessage_Multicast(p_rSendParameter, caiWiFiOpModes[iInterfaceId]); + } + } + } + + // Finally clear service reply masks + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + pService->m_u8ReplyMask = 0; + } + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_sendMDNSMessage_Multicast + + Fills the UDP output buffer (via _prepareMDNSMessage) and sends the buffer + via the selected WiFi interface (Station or AP) +*/ +bool MDNSResponder::_sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, + int p_iWiFiOpMode) +{ + bool bResult = false; + + IPAddress fromIPAddress; + fromIPAddress = _getResponseMulticastInterface(p_iWiFiOpMode); + m_pUDPContext->setMulticastInterface(fromIPAddress); + +#ifdef MDNS_IP4_SUPPORT + IPAddress toMulticastAddress(DNS_MQUERY_IPV4_GROUP_INIT); +#endif +#ifdef MDNS_IP6_SUPPORT + //TODO: set multicast address + IPAddress toMulticastAddress(DNS_MQUERY_IPV6_GROUP_INIT); +#endif + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: Will send to '%s'.\n"), toMulticastAddress.toString().c_str());); + bResult = ((_prepareMDNSMessage(p_rSendParameter, fromIPAddress)) && + (m_pUDPContext->send(toMulticastAddress, DNS_MQUERY_PORT))); + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_prepareMDNSMessage + + The MDNS message is composed in a two-step process. + In the first loop 'only' the header informations (mainly number of answers) are collected, + while in the seconds loop, the header and all queries and answers are written to the UDP + output buffer. + +*/ +bool MDNSResponder::_prepareMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, + IPAddress p_IPAddress) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage\n"));); + bool bResult = true; + + // Prepare header; count answers + stcMDNS_MsgHeader msgHeader(0, p_rSendParameter.m_bResponse, 0, p_rSendParameter.m_bAuthorative); + // If this is a response, the answers are anwers, + // else this is a query or probe and the answers go into auth section + uint16_t& ru16Answers = (p_rSendParameter.m_bResponse + ? msgHeader.m_u16ANCount + : msgHeader.m_u16NSCount); + + /** + enuSequence + */ + enum enuSequence + { + Sequence_Count = 0, + Sequence_Send = 1 + }; + + // Two step sequence: 'Count' and 'Send' + for (uint32_t sequence = Sequence_Count; ((bResult) && (sequence <= Sequence_Send)); ++sequence) + { + DEBUG_EX_INFO( + if (Sequence_Send == sequence) + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), + (unsigned)msgHeader.m_u16ID, + (unsigned)msgHeader.m_1bQR, (unsigned)msgHeader.m_4bOpcode, (unsigned)msgHeader.m_1bAA, (unsigned)msgHeader.m_1bTC, (unsigned)msgHeader.m_1bRD, + (unsigned)msgHeader.m_1bRA, (unsigned)msgHeader.m_4bRCode, + (unsigned)msgHeader.m_u16QDCount, + (unsigned)msgHeader.m_u16ANCount, + (unsigned)msgHeader.m_u16NSCount, + (unsigned)msgHeader.m_u16ARCount); + } + ); + // Count/send + // Header + bResult = ((Sequence_Count == sequence) + ? true + : _writeMDNSMsgHeader(msgHeader, p_rSendParameter)); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSMsgHeader FAILED!\n"));); + // Questions + for (stcMDNS_RRQuestion* pQuestion = p_rSendParameter.m_pQuestions; ((bResult) && (pQuestion)); pQuestion = pQuestion->m_pNext) + { + ((Sequence_Count == sequence) + ? ++msgHeader.m_u16QDCount + : (bResult = _writeMDNSQuestion(*pQuestion, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSQuestion FAILED!\n"));); + } + + // Answers and authorative answers +#ifdef MDNS_IP4_SUPPORT + if ((bResult) && + (p_rSendParameter.m_u8HostReplyMask & ContentFlag_A)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_A(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(A) FAILED!\n"));); + } + if ((bResult) && + (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP4)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_PTR_IP4(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP4 FAILED!\n"));); + } +#endif +#ifdef MDNS_IP6_SUPPORT + if ((bResult) && + (p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(A) FAILED!\n"));); + } + if ((bResult) && + (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP6)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_PTR_IP6(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP6 FAILED!\n"));); + } +#endif + + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) + { + if ((bResult) && + (pService->m_u8ReplyMask & ContentFlag_PTR_TYPE)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_PTR_TYPE(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_TYPE FAILED!\n"));); + } + if ((bResult) && + (pService->m_u8ReplyMask & ContentFlag_PTR_NAME)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_PTR_NAME(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_NAME FAILED!\n"));); + } + if ((bResult) && + (pService->m_u8ReplyMask & ContentFlag_SRV)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_SRV(A) FAILED!\n"));); + } + if ((bResult) && + (pService->m_u8ReplyMask & ContentFlag_TXT)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_TXT(A) FAILED!\n"));); + } + } // for services + + // Additional answers +#ifdef MDNS_IP4_SUPPORT + bool bNeedsAdditionalAnswerA = false; +#endif +#ifdef MDNS_IP6_SUPPORT + bool bNeedsAdditionalAnswerAAAA = false; +#endif + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) + { + if ((bResult) && + (pService->m_u8ReplyMask & ContentFlag_PTR_NAME) && // If PTR_NAME is requested, AND + (!(pService->m_u8ReplyMask & ContentFlag_SRV))) // NOT SRV -> add SRV as additional answer + { + ((Sequence_Count == sequence) + ? ++msgHeader.m_u16ARCount + : (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_SRV(B) FAILED!\n"));); + } + if ((bResult) && + (pService->m_u8ReplyMask & ContentFlag_PTR_NAME) && // If PTR_NAME is requested, AND + (!(pService->m_u8ReplyMask & ContentFlag_TXT))) // NOT TXT -> add TXT as additional answer + { + ((Sequence_Count == sequence) + ? ++msgHeader.m_u16ARCount + : (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_TXT(B) FAILED!\n"));); + } + if ((pService->m_u8ReplyMask & (ContentFlag_PTR_NAME | ContentFlag_SRV)) || // If service instance name or SRV OR + (p_rSendParameter.m_u8HostReplyMask & (ContentFlag_A | ContentFlag_AAAA))) // any host IP address is requested + { +#ifdef MDNS_IP4_SUPPORT + if ((bResult) && + (!(p_rSendParameter.m_u8HostReplyMask & ContentFlag_A))) // Add IP4 address + { + bNeedsAdditionalAnswerA = true; + } +#endif +#ifdef MDNS_IP6_SUPPORT + if ((bResult) && + (!(p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA))) // Add IP6 address + { + bNeedsAdditionalAnswerAAAA = true; + } +#endif + } + } // for services + + // Answer A needed? +#ifdef MDNS_IP4_SUPPORT + if ((bResult) && + (bNeedsAdditionalAnswerA)) + { + ((Sequence_Count == sequence) + ? ++msgHeader.m_u16ARCount + : (bResult = _writeMDNSAnswer_A(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(B) FAILED!\n"));); + } +#endif +#ifdef MDNS_IP6_SUPPORT + // Answer AAAA needed? + if ((bResult) && + (bNeedsAdditionalAnswerAAAA)) + { + ((Sequence_Count == sequence) + ? ++msgHeader.m_u16ARCount + : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(B) FAILED!\n"));); + } +#endif + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: Loop %i FAILED!\n"), sequence);); + } // for sequence + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: FAILED!\n"));); + return bResult; +} + +/* + MDNSResponder::_sendMDNSServiceQuery + + Creates and sends a PTR query for the given service domain. + +*/ +bool MDNSResponder::_sendMDNSServiceQuery(const MDNSResponder::stcMDNSServiceQuery& p_ServiceQuery) +{ + + return _sendMDNSQuery(p_ServiceQuery.m_ServiceTypeDomain, DNS_RRTYPE_PTR); +} + +/* + MDNSResponder::_sendMDNSQuery + + Creates and sends a query for the given domain and query type. + +*/ +bool MDNSResponder::_sendMDNSQuery(const MDNSResponder::stcMDNS_RRDomain& p_QueryDomain, + uint16_t p_u16QueryType, + stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers /*= 0*/) +{ + + bool bResult = false; + + stcMDNSSendParameter sendParameter; + if (0 != ((sendParameter.m_pQuestions = new stcMDNS_RRQuestion))) + { + sendParameter.m_pQuestions->m_Header.m_Domain = p_QueryDomain; + + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = p_u16QueryType; + // It seems, that some mDNS implementations don't support 'unicast response' questions... + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (/*0x8000 |*/ DNS_RRCLASS_IN); // /*Unicast &*/ INternet + + // TODO: Add knwon answer to the query + (void)p_pKnownAnswers; + + bResult = _sendMDNSMessage(sendParameter); + } // else: FAILED to alloc question + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSQuery: FAILED to alloc question!\n"));); + return bResult; +} + +/* + MDNSResponder::_getResponseMulticastInterface + + Selects the appropriate interface for responses. + If AP mode is enabled and the remote contact is in the APs local net, then the + AP interface is used to send the response. + Otherwise the Station interface (if available) is used. + +*/ +IPAddress MDNSResponder::_getResponseMulticastInterface(int p_iWiFiOpModes) const +{ + + ip_info IPInfo_Local; + bool bFoundMatch = false; + + if ((p_iWiFiOpModes & SOFTAP_MODE) && + (wifi_get_opmode() & SOFTAP_MODE)) + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _getResponseMulticastInterface: SOFTAP_MODE\n"));); + // Get remote IP address + IPAddress IP_Remote; + IP_Remote = m_pUDPContext->getRemoteAddress(); + // Get local (AP) IP address + wifi_get_ip_info(SOFTAP_IF, &IPInfo_Local); + + if ((IPInfo_Local.ip.addr) && // Has local AP IP address AND + (ip4_addr_netcmp(ip_2_ip4((const ip_addr_t*)IP_Remote), &IPInfo_Local.ip, &IPInfo_Local.netmask))) // Remote address is in the same subnet as the AP + { + bFoundMatch = true; + } + } + if ((!bFoundMatch) && + (p_iWiFiOpModes & STATION_MODE) && + (wifi_get_opmode() & STATION_MODE)) + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _getResponseMulticastInterface: STATION_MODE\n"));); + // Get local (STATION) IP address + wifi_get_ip_info(STATION_IF, &IPInfo_Local); + } + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _getResponseMulticastInterface(%i): %s\n"), p_iWiFiOpModes, IPAddress(IPInfo_Local.ip).toString().c_str());); + return IPAddress(IPInfo_Local.ip); +} + + +/** + HELPERS +*/ + +/** + RESOURCE RECORDS +*/ + +/* + MDNSResponder::_readRRQuestion + + Reads a question (eg. MyESP._http._tcp.local ANY IN) from the UPD input buffer. + +*/ +bool MDNSResponder::_readRRQuestion(MDNSResponder::stcMDNS_RRQuestion& p_rRRQuestion) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion\n"));); + + bool bResult = false; + + if ((bResult = _readRRHeader(p_rRRQuestion.m_Header))) + { + // Extract unicast flag from class field + p_rRRQuestion.m_bUnicast = (p_rRRQuestion.m_Header.m_Attributes.m_u16Class & 0x8000); + p_rRRQuestion.m_Header.m_Attributes.m_u16Class &= (~0x8000); + + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion ")); + _printRRDomain(p_rRRQuestion.m_Header.m_Domain); + DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X %s\n"), (unsigned)p_rRRQuestion.m_Header.m_Attributes.m_u16Type, (unsigned)p_rRRQuestion.m_Header.m_Attributes.m_u16Class, (p_rRRQuestion.m_bUnicast ? "Unicast" : "Multicast")); + ); + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_readRRAnswer + + Reads an answer (eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local) + from the UDP input buffer. + After reading the domain and type info, the further processing of the answer + is transferred the answer specific reading functions. + Unknown answer types are processed by the generic answer reader (to remove them + from the input buffer). + +*/ +bool MDNSResponder::_readRRAnswer(MDNSResponder::stcMDNS_RRAnswer*& p_rpRRAnswer) +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer\n"));); + + bool bResult = false; + + stcMDNS_RRHeader header; + uint32_t u32TTL; + uint16_t u16RDLength; + if ((_readRRHeader(header)) && + (_udpRead32(u32TTL)) && + (_udpRead16(u16RDLength))) + { + + /* DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: Reading 0x%04X answer (class:0x%04X, TTL:%u, RDLength:%u) for "), header.m_Attributes.m_u16Type, header.m_Attributes.m_u16Class, u32TTL, u16RDLength); + _printRRDomain(header.m_Domain); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + );*/ + + switch (header.m_Attributes.m_u16Type & (~0x8000)) // Topmost bit might carry 'cache flush' flag + { +#ifdef MDNS_IP4_SUPPORT + case DNS_RRTYPE_A: + p_rpRRAnswer = new stcMDNS_RRAnswerA(header, u32TTL); + bResult = _readRRAnswerA(*(stcMDNS_RRAnswerA*&)p_rpRRAnswer, u16RDLength); + break; +#endif + case DNS_RRTYPE_PTR: + p_rpRRAnswer = new stcMDNS_RRAnswerPTR(header, u32TTL); + bResult = _readRRAnswerPTR(*(stcMDNS_RRAnswerPTR*&)p_rpRRAnswer, u16RDLength); + break; + case DNS_RRTYPE_TXT: + p_rpRRAnswer = new stcMDNS_RRAnswerTXT(header, u32TTL); + bResult = _readRRAnswerTXT(*(stcMDNS_RRAnswerTXT*&)p_rpRRAnswer, u16RDLength); + break; +#ifdef MDNS_IP6_SUPPORT + case DNS_RRTYPE_AAAA: + p_rpRRAnswer = new stcMDNS_RRAnswerAAAA(header, u32TTL); + bResult = _readRRAnswerAAAA(*(stcMDNS_RRAnswerAAAA*&)p_rpRRAnswer, u16RDLength); + break; +#endif + case DNS_RRTYPE_SRV: + p_rpRRAnswer = new stcMDNS_RRAnswerSRV(header, u32TTL); + bResult = _readRRAnswerSRV(*(stcMDNS_RRAnswerSRV*&)p_rpRRAnswer, u16RDLength); + break; + default: + p_rpRRAnswer = new stcMDNS_RRAnswerGeneric(header, u32TTL); + bResult = _readRRAnswerGeneric(*(stcMDNS_RRAnswerGeneric*&)p_rpRRAnswer, u16RDLength); + break; + } + DEBUG_EX_INFO( + if ((bResult) && + (p_rpRRAnswer)) + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: ")); + _printRRDomain(p_rpRRAnswer->m_Header.m_Domain); + DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, RDLength:%u "), p_rpRRAnswer->m_Header.m_Attributes.m_u16Type, p_rpRRAnswer->m_Header.m_Attributes.m_u16Class, p_rpRRAnswer->m_u32TTL, u16RDLength); + switch (header.m_Attributes.m_u16Type & (~0x8000)) // Topmost bit might carry 'cache flush' flag + { +#ifdef MDNS_IP4_SUPPORT + case DNS_RRTYPE_A: + DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); + break; +#endif + case DNS_RRTYPE_PTR: + DEBUG_OUTPUT.printf_P(PSTR("PTR ")); + _printRRDomain(((stcMDNS_RRAnswerPTR*&)p_rpRRAnswer)->m_PTRDomain); + break; + case DNS_RRTYPE_TXT: + { + size_t stTxtLength = ((stcMDNS_RRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_strLength(); + char* pTxts = new char[stTxtLength]; + if (pTxts) + { + ((stcMDNS_RRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_str(pTxts); + DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts); + delete[] pTxts; + } + break; + } +#ifdef MDNS_IP6_SUPPORT + case DNS_RRTYPE_AAAA: + DEBUG_OUTPUT.printf_P(PSTR("AAAA IP:%s"), ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); + break; +#endif + case DNS_RRTYPE_SRV: + DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "), ((stcMDNS_RRAnswerSRV*&)p_rpRRAnswer)->m_u16Port); + _printRRDomain(((stcMDNS_RRAnswerSRV*&)p_rpRRAnswer)->m_SRVDomain); + break; + default: + DEBUG_OUTPUT.printf_P(PSTR("generic ")); + break; + } + DEBUG_OUTPUT.printf_P(PSTR("\n")); + } + else + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: FAILED to read specific answer of type 0x%04X!\n"), p_rpRRAnswer->m_Header.m_Attributes.m_u16Type); + } + ); // DEBUG_EX_INFO + } + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: FAILED!\n"));); + return bResult; +} + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::_readRRAnswerA +*/ +bool MDNSResponder::_readRRAnswerA(MDNSResponder::stcMDNS_RRAnswerA& p_rRRAnswerA, + uint16_t p_u16RDLength) +{ + + uint32_t u32IP4Address; + bool bResult = ((MDNS_IP4_SIZE == p_u16RDLength) && + (_udpReadBuffer((unsigned char*)&u32IP4Address, MDNS_IP4_SIZE)) && + ((p_rRRAnswerA.m_IPAddress = IPAddress(u32IP4Address)))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerA: FAILED!\n"));); + return bResult; +} +#endif + +/* + MDNSResponder::_readRRAnswerPTR +*/ +bool MDNSResponder::_readRRAnswerPTR(MDNSResponder::stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, + uint16_t p_u16RDLength) +{ + + bool bResult = ((p_u16RDLength) && + (_readRRDomain(p_rRRAnswerPTR.m_PTRDomain))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerPTR: FAILED!\n"));); + return bResult; +} + +/* + MDNSResponder::_readRRAnswerTXT + + Read TXT items from a buffer like 4c#=15ff=20 +*/ +bool MDNSResponder::_readRRAnswerTXT(MDNSResponder::stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, + uint16_t p_u16RDLength) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: RDLength:%u\n"), p_u16RDLength);); + bool bResult = true; + + p_rRRAnswerTXT.clear(); + if (p_u16RDLength) + { + bResult = false; + + unsigned char* pucBuffer = new unsigned char[p_u16RDLength]; + if (pucBuffer) + { + if (_udpReadBuffer(pucBuffer, p_u16RDLength)) + { + bResult = true; + + const unsigned char* pucCursor = pucBuffer; + while ((pucCursor < (pucBuffer + p_u16RDLength)) && + (bResult)) + { + bResult = false; + + stcMDNSServiceTxt* pTxt = 0; + unsigned char ucLength = *pucCursor++; // Length of the next txt item + if (ucLength) + { + DEBUG_EX_INFO( + static char sacBuffer[64]; *sacBuffer = 0; + uint8_t u8MaxLength = ((ucLength > (sizeof(sacBuffer) - 1)) ? (sizeof(sacBuffer) - 1) : ucLength); + os_strncpy(sacBuffer, (const char*)pucCursor, u8MaxLength); sacBuffer[u8MaxLength] = 0; + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: Item(%u): %s\n"), ucLength, sacBuffer); + ); + + unsigned char* pucEqualSign = (unsigned char*)os_strchr((const char*)pucCursor, '='); // Position of the '=' sign + unsigned char ucKeyLength; + if ((pucEqualSign) && + ((ucKeyLength = (pucEqualSign - pucCursor)))) + { + unsigned char ucValueLength = (ucLength - (pucEqualSign - pucCursor + 1)); + bResult = (((pTxt = new stcMDNSServiceTxt)) && + (pTxt->setKey((const char*)pucCursor, ucKeyLength)) && + (pTxt->setValue((const char*)(pucEqualSign + 1), ucValueLength))); + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: INVALID TXT format (No '=')!\n"));); + } + pucCursor += ucLength; + } + else // no/zero length TXT + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: TXT answer contains no items.\n"));); + bResult = true; + } + + if ((bResult) && + (pTxt)) // Everythings fine so far + { + // Link TXT item to answer TXTs + pTxt->m_pNext = p_rRRAnswerTXT.m_Txts.m_pTxts; + p_rRRAnswerTXT.m_Txts.m_pTxts = pTxt; + } + else // At least no TXT (migth be OK, if length was 0) OR an error + { + if (!bResult) + { + DEBUG_EX_ERR( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to read TXT item!\n")); + DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n")); + _udpDump((m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + ); + } + if (pTxt) + { + delete pTxt; + pTxt = 0; + } + p_rRRAnswerTXT.clear(); + } + } // while + + DEBUG_EX_ERR( + if (!bResult) // Some failure + { + DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n")); + _udpDump((m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + } + ); + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to read TXT content!\n"));); + } + // Clean up + delete[] pucBuffer; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to alloc buffer for TXT content!\n"));); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: WARNING! No content!\n"));); + } + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED!\n"));); + return bResult; +} + +#ifdef MDNS_IP6_SUPPORT +bool MDNSResponder::_readRRAnswerAAAA(MDNSResponder::stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, + uint16_t p_u16RDLength) +{ + bool bResult = false; + // TODO: Implement + return bResult; +} +#endif + +/* + MDNSResponder::_readRRAnswerSRV +*/ +bool MDNSResponder::_readRRAnswerSRV(MDNSResponder::stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, + uint16_t p_u16RDLength) +{ + + bool bResult = (((3 * sizeof(uint16_t)) < p_u16RDLength) && + (_udpRead16(p_rRRAnswerSRV.m_u16Priority)) && + (_udpRead16(p_rRRAnswerSRV.m_u16Weight)) && + (_udpRead16(p_rRRAnswerSRV.m_u16Port)) && + (_readRRDomain(p_rRRAnswerSRV.m_SRVDomain))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerSRV: FAILED!\n"));); + return bResult; +} + +/* + MDNSResponder::_readRRAnswerGeneric +*/ +bool MDNSResponder::_readRRAnswerGeneric(MDNSResponder::stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, + uint16_t p_u16RDLength) +{ + bool bResult = (0 == p_u16RDLength); + + p_rRRAnswerGeneric.clear(); + if (((p_rRRAnswerGeneric.m_u16RDLength = p_u16RDLength)) && + ((p_rRRAnswerGeneric.m_pu8RDData = new unsigned char[p_rRRAnswerGeneric.m_u16RDLength]))) + { + + bResult = _udpReadBuffer(p_rRRAnswerGeneric.m_pu8RDData, p_rRRAnswerGeneric.m_u16RDLength); + } + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerGeneric: FAILED!\n"));); + return bResult; +} + +/* + MDNSResponder::_readRRHeader +*/ +bool MDNSResponder::_readRRHeader(MDNSResponder::stcMDNS_RRHeader& p_rRRHeader) +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRHeader\n"));); + + bool bResult = ((_readRRDomain(p_rRRHeader.m_Domain)) && + (_readRRAttributes(p_rRRHeader.m_Attributes))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRHeader: FAILED!\n"));); + return bResult; +} + +/* + MDNSResponder::_readRRDomain + + Reads a (maybe multilevel compressed) domain from the UDP input buffer. + +*/ +bool MDNSResponder::_readRRDomain(MDNSResponder::stcMDNS_RRDomain& p_rRRDomain) +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain\n"));); + + bool bResult = ((p_rRRDomain.clear()) && + (_readRRDomain_Loop(p_rRRDomain, 0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain: FAILED!\n"));); + return bResult; +} + +/* + MDNSResponder::_readRRDomain_Loop + + Reads a domain from the UDP input buffer. For every compression level, the functions + calls itself recursively. To avoid endless recursion because of malformed MDNS records, + the maximum recursion depth is set by MDNS_DOMAIN_MAX_REDIRCTION. + +*/ +bool MDNSResponder::_readRRDomain_Loop(MDNSResponder::stcMDNS_RRDomain& p_rRRDomain, + uint8_t p_u8Depth) +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u)\n"), p_u8Depth);); + + bool bResult = false; + + if (MDNS_DOMAIN_MAX_REDIRCTION >= p_u8Depth) + { + bResult = true; + + uint8_t u8Len = 0; + do + { + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Offset:%u p0:%02x\n"), p_u8Depth, m_pUDPContext->tell(), m_pUDPContext->peek());); + _udpRead8(u8Len); + + if (u8Len & MDNS_DOMAIN_COMPRESS_MARK) + { + // Compressed label(s) + uint16_t u16Offset = ((u8Len & ~MDNS_DOMAIN_COMPRESS_MARK) << 8); // Implicit BE to LE conversion! + _udpRead8(u8Len); + u16Offset |= u8Len; + + if (m_pUDPContext->isValidOffset(u16Offset)) + { + size_t stCurrentPosition = m_pUDPContext->tell(); // Prepare return from recursion + + //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Redirecting from %u to %u!\n"), p_u8Depth, stCurrentPosition, u16Offset);); + m_pUDPContext->seek(u16Offset); + if (_readRRDomain_Loop(p_rRRDomain, p_u8Depth + 1)) // Do recursion + { + //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Succeeded to read redirected label! Returning to %u\n"), p_u8Depth, stCurrentPosition);); + m_pUDPContext->seek(stCurrentPosition); // Restore after recursion + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): FAILED to read redirected label!\n"), p_u8Depth);); + bResult = false; + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): INVALID offset in redirection!\n"), p_u8Depth);); + bResult = false; + } + break; + } + else + { + // Normal (uncompressed) label (maybe '\0' only) + if (MDNS_DOMAIN_MAXLENGTH > (p_rRRDomain.m_u16NameLength + u8Len)) + { + // Add length byte + p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength] = u8Len; + ++(p_rRRDomain.m_u16NameLength); + if (u8Len) // Add name + { + if ((bResult = _udpReadBuffer((unsigned char*) & (p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength]), u8Len))) + { + /* DEBUG_EX_INFO( + p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength + u8Len] = 0; // Closing '\0' for printing + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Domain label (%u): %s\n"), p_u8Depth, (unsigned)(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength - 1]), &(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength])); + );*/ + + p_rRRDomain.m_u16NameLength += u8Len; + } + } + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(2) offset:%u p0:%x\n"), m_pUDPContext->tell(), m_pUDPContext->peek());); + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): ERROR! Domain name too long (%u + %u)!\n"), p_u8Depth, p_rRRDomain.m_u16NameLength, u8Len);); + bResult = false; + break; + } + } + } while ((bResult) && + (0 != u8Len)); + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): ERROR! Too many redirections!\n"), p_u8Depth);); + } + return bResult; +} + +/* + MDNSResponder::_readRRAttributes +*/ +bool MDNSResponder::_readRRAttributes(MDNSResponder::stcMDNS_RRAttributes& p_rRRAttributes) +{ + //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAttributes\n"));); + + bool bResult = ((_udpRead16(p_rRRAttributes.m_u16Type)) && + (_udpRead16(p_rRRAttributes.m_u16Class))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAttributes: FAILED!\n"));); + return bResult; +} + + +/* + DOMAIN NAMES +*/ + +/* + MDNSResponder::_buildDomainForHost + + Builds a MDNS host domain (eg. esp8266.local) for the given hostname. + +*/ +bool MDNSResponder::_buildDomainForHost(const char* p_pcHostname, + MDNSResponder::stcMDNS_RRDomain& p_rHostDomain) const +{ + + p_rHostDomain.clear(); + bool bResult = ((p_pcHostname) && + (*p_pcHostname) && + (p_rHostDomain.addLabel(p_pcHostname)) && + (p_rHostDomain.addLabel(scpcLocal)) && + (p_rHostDomain.addLabel(0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForHost: FAILED!\n"));); + return bResult; +} + +/* + MDNSResponder::_buildDomainForDNSSD + + Builds the '_services._dns-sd._udp.local' domain. + Used while detecting generic service enum question (DNS-SD) and answering these questions. + +*/ +bool MDNSResponder::_buildDomainForDNSSD(MDNSResponder::stcMDNS_RRDomain& p_rDNSSDDomain) const +{ + + p_rDNSSDDomain.clear(); + bool bResult = ((p_rDNSSDDomain.addLabel(scpcServices, true)) && + (p_rDNSSDDomain.addLabel(scpcDNSSD, true)) && + (p_rDNSSDDomain.addLabel(scpcUDP, true)) && + (p_rDNSSDDomain.addLabel(scpcLocal)) && + (p_rDNSSDDomain.addLabel(0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForDNSSD: FAILED!\n"));); + return bResult; +} + +/* + MDNSResponder::_buildDomainForService + + Builds the domain for the given service (eg. _http._tcp.local or + MyESP._http._tcp.local (if p_bIncludeName is set)). + +*/ +bool MDNSResponder::_buildDomainForService(const MDNSResponder::stcMDNSService& p_Service, + bool p_bIncludeName, + MDNSResponder::stcMDNS_RRDomain& p_rServiceDomain) const +{ + + p_rServiceDomain.clear(); + bool bResult = (((!p_bIncludeName) || + (p_rServiceDomain.addLabel(p_Service.m_pcName))) && + (p_rServiceDomain.addLabel(p_Service.m_pcService, true)) && + (p_rServiceDomain.addLabel(p_Service.m_pcProtocol, true)) && + (p_rServiceDomain.addLabel(scpcLocal)) && + (p_rServiceDomain.addLabel(0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForService: FAILED!\n"));); + return bResult; +} + +/* + MDNSResponder::_buildDomainForService + + Builds the domain for the given service properties (eg. _http._tcp.local). + The usual prepended '_' are added, if missing in the input strings. + +*/ +bool MDNSResponder::_buildDomainForService(const char* p_pcService, + const char* p_pcProtocol, + MDNSResponder::stcMDNS_RRDomain& p_rServiceDomain) const +{ + + p_rServiceDomain.clear(); + bool bResult = ((p_pcService) && + (p_pcProtocol) && + (p_rServiceDomain.addLabel(p_pcService, ('_' != *p_pcService))) && + (p_rServiceDomain.addLabel(p_pcProtocol, ('_' != *p_pcProtocol))) && + (p_rServiceDomain.addLabel(scpcLocal)) && + (p_rServiceDomain.addLabel(0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForService: FAILED for (%s.%s)!\n"), (p_pcService ? : "-"), (p_pcProtocol ? : "-"));); + return bResult; +} + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::_buildDomainForReverseIP4 + + The IP4 address is stringized by printing the four address bytes into a char buffer in reverse order + and adding 'in-addr.arpa' (eg. 012.789.456.123.in-addr.arpa). + Used while detecting reverse IP4 questions and answering these +*/ +bool MDNSResponder::_buildDomainForReverseIP4(IPAddress p_IP4Address, + MDNSResponder::stcMDNS_RRDomain& p_rReverseIP4Domain) const +{ + + bool bResult = true; + + p_rReverseIP4Domain.clear(); + + char acBuffer[32]; + for (int i = MDNS_IP4_SIZE; ((bResult) && (i >= 1)); --i) + { + itoa(p_IP4Address[i - 1], acBuffer, 10); + bResult = p_rReverseIP4Domain.addLabel(acBuffer); + } + bResult = ((bResult) && + (p_rReverseIP4Domain.addLabel(scpcReverseIP4Domain)) && + (p_rReverseIP4Domain.addLabel(scpcReverseTopDomain)) && + (p_rReverseIP4Domain.addLabel(0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForReverseIP4: FAILED!\n"));); + return bResult; +} +#endif + +#ifdef MDNS_IP6_SUPPORT +/* + MDNSResponder::_buildDomainForReverseIP6 + + Used while detecting reverse IP6 questions and answering these +*/ +bool MDNSResponder::_buildDomainForReverseIP6(IPAddress p_IP4Address, + MDNSResponder::stcMDNS_RRDomain& p_rReverseIP6Domain) const +{ + // TODO: Implement + return false; +} +#endif + + +/* + UDP +*/ + +/* + MDNSResponder::_udpReadBuffer +*/ +bool MDNSResponder::_udpReadBuffer(unsigned char* p_pBuffer, + size_t p_stLength) +{ + + bool bResult = ((m_pUDPContext) && + (true/*m_pUDPContext->getSize() > p_stLength*/) && + (p_pBuffer) && + (p_stLength) && + ((p_stLength == m_pUDPContext->read((char*)p_pBuffer, p_stLength)))); + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _udpReadBuffer: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_udpRead8 +*/ +bool MDNSResponder::_udpRead8(uint8_t& p_ru8Value) +{ + + return _udpReadBuffer((unsigned char*)&p_ru8Value, sizeof(p_ru8Value)); +} + +/* + MDNSResponder::_udpRead16 +*/ +bool MDNSResponder::_udpRead16(uint16_t& p_ru16Value) +{ + + bool bResult = false; + + if (_udpReadBuffer((unsigned char*)&p_ru16Value, sizeof(p_ru16Value))) + { + p_ru16Value = lwip_ntohs(p_ru16Value); + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::_udpRead32 +*/ +bool MDNSResponder::_udpRead32(uint32_t& p_ru32Value) +{ + + bool bResult = false; + + if (_udpReadBuffer((unsigned char*)&p_ru32Value, sizeof(p_ru32Value))) + { + p_ru32Value = lwip_ntohl(p_ru32Value); + bResult = true; + } + return bResult; +} + +/* + MDNSResponder::_udpAppendBuffer +*/ +bool MDNSResponder::_udpAppendBuffer(const unsigned char* p_pcBuffer, + size_t p_stLength) +{ + + bool bResult = ((m_pUDPContext) && + (p_pcBuffer) && + (p_stLength) && + (p_stLength == m_pUDPContext->append((const char*)p_pcBuffer, p_stLength))); + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _udpAppendBuffer: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_udpAppend8 +*/ +bool MDNSResponder::_udpAppend8(uint8_t p_u8Value) +{ + + return (_udpAppendBuffer((unsigned char*)&p_u8Value, sizeof(p_u8Value))); +} + +/* + MDNSResponder::_udpAppend16 +*/ +bool MDNSResponder::_udpAppend16(uint16_t p_u16Value) +{ + + p_u16Value = lwip_htons(p_u16Value); + return (_udpAppendBuffer((unsigned char*)&p_u16Value, sizeof(p_u16Value))); +} + +/* + MDNSResponder::_udpAppend32 +*/ +bool MDNSResponder::_udpAppend32(uint32_t p_u32Value) +{ + + p_u32Value = lwip_htonl(p_u32Value); + return (_udpAppendBuffer((unsigned char*)&p_u32Value, sizeof(p_u32Value))); +} + +#ifdef DEBUG_ESP_MDNS_RESPONDER +/* + MDNSResponder::_udpDump +*/ +bool MDNSResponder::_udpDump(bool p_bMovePointer /*= false*/) +{ + + const uint8_t cu8BytesPerLine = 16; + + uint32_t u32StartPosition = m_pUDPContext->tell(); + DEBUG_OUTPUT.println("UDP Context Dump:"); + uint32_t u32Counter = 0; + uint8_t u8Byte = 0; + + while (_udpRead8(u8Byte)) + { + DEBUG_OUTPUT.printf_P(PSTR("%02x %s"), u8Byte, ((++u32Counter % cu8BytesPerLine) ? "" : "\n")); + } + DEBUG_OUTPUT.printf_P(PSTR("%sDone: %u bytes\n"), (((u32Counter) && (u32Counter % cu8BytesPerLine)) ? "\n" : ""), u32Counter); + + if (!p_bMovePointer) // Restore + { + m_pUDPContext->seek(u32StartPosition); + } + return true; +} + +/* + MDNSResponder::_udpDump +*/ +bool MDNSResponder::_udpDump(unsigned p_uOffset, + unsigned p_uLength) +{ + + if ((m_pUDPContext) && + (m_pUDPContext->isValidOffset(p_uOffset))) + { + unsigned uCurrentPosition = m_pUDPContext->tell(); // Remember start position + + m_pUDPContext->seek(p_uOffset); + uint8_t u8Byte; + for (unsigned u = 0; ((u < p_uLength) && (_udpRead8(u8Byte))); ++u) + { + DEBUG_OUTPUT.printf_P(PSTR("%02x "), u8Byte); + } + // Return to start position + m_pUDPContext->seek(uCurrentPosition); + } + return true; +} +#endif + + +/** + READ/WRITE MDNS STRUCTS +*/ + +/* + MDNSResponder::_readMDNSMsgHeader + + Read a MDNS header from the UDP input buffer. + | 8 | 8 | 8 | 8 | + 00| Identifier | Flags & Codes | + 01| Question count | Answer count | + 02| NS answer count | Ad answer count | + + All 16-bit and 32-bit elements need to be translated form network coding to host coding (done in _udpRead16 and _udpRead32) + In addition, bitfield memory order is undefined in C standard (GCC doesn't order them in the coded direction...), so they + need some mapping here +*/ +bool MDNSResponder::_readMDNSMsgHeader(MDNSResponder::stcMDNS_MsgHeader& p_rMsgHeader) +{ + + bool bResult = false; + + uint8_t u8B1; + uint8_t u8B2; + if ((_udpRead16(p_rMsgHeader.m_u16ID)) && + (_udpRead8(u8B1)) && + (_udpRead8(u8B2)) && + (_udpRead16(p_rMsgHeader.m_u16QDCount)) && + (_udpRead16(p_rMsgHeader.m_u16ANCount)) && + (_udpRead16(p_rMsgHeader.m_u16NSCount)) && + (_udpRead16(p_rMsgHeader.m_u16ARCount))) + { + + p_rMsgHeader.m_1bQR = (u8B1 & 0x80); // Query/Responde flag + p_rMsgHeader.m_4bOpcode = (u8B1 & 0x78); // Operation code (0: Standard query, others ignored) + p_rMsgHeader.m_1bAA = (u8B1 & 0x04); // Authorative answer + p_rMsgHeader.m_1bTC = (u8B1 & 0x02); // Truncation flag + p_rMsgHeader.m_1bRD = (u8B1 & 0x01); // Recursion desired + + p_rMsgHeader.m_1bRA = (u8B2 & 0x80); // Recursion available + p_rMsgHeader.m_3bZ = (u8B2 & 0x70); // Zero + p_rMsgHeader.m_4bRCode = (u8B2 & 0x0F); // Response code + + /* DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readMDNSMsgHeader: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), + (unsigned)p_rMsgHeader.m_u16ID, + (unsigned)p_rMsgHeader.m_1bQR, (unsigned)p_rMsgHeader.m_4bOpcode, (unsigned)p_rMsgHeader.m_1bAA, (unsigned)p_rMsgHeader.m_1bTC, (unsigned)p_rMsgHeader.m_1bRD, + (unsigned)p_rMsgHeader.m_1bRA, (unsigned)p_rMsgHeader.m_4bRCode, + (unsigned)p_rMsgHeader.m_u16QDCount, + (unsigned)p_rMsgHeader.m_u16ANCount, + (unsigned)p_rMsgHeader.m_u16NSCount, + (unsigned)p_rMsgHeader.m_u16ARCount););*/ + bResult = true; + } + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readMDNSMsgHeader: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_write8 +*/ +bool MDNSResponder::_write8(uint8_t p_u8Value, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + + return ((_udpAppend8(p_u8Value)) && + (p_rSendParameter.shiftOffset(sizeof(p_u8Value)))); +} + +/* + MDNSResponder::_write16 +*/ +bool MDNSResponder::_write16(uint16_t p_u16Value, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + + return ((_udpAppend16(p_u16Value)) && + (p_rSendParameter.shiftOffset(sizeof(p_u16Value)))); +} + +/* + MDNSResponder::_write32 +*/ +bool MDNSResponder::_write32(uint32_t p_u32Value, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + + return ((_udpAppend32(p_u32Value)) && + (p_rSendParameter.shiftOffset(sizeof(p_u32Value)))); +} + +/* + MDNSResponder::_writeMDNSMsgHeader + + Write MDNS header to the UDP output buffer. + + All 16-bit and 32-bit elements need to be translated form host coding to network coding (done in _udpAppend16 and _udpAppend32) + In addition, bitfield memory order is undefined in C standard (GCC doesn't order them in the coded direction...), so they + need some mapping here +*/ +bool MDNSResponder::_writeMDNSMsgHeader(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + /* DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSMsgHeader: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), + (unsigned)p_MsgHeader.m_u16ID, + (unsigned)p_MsgHeader.m_1bQR, (unsigned)p_MsgHeader.m_4bOpcode, (unsigned)p_MsgHeader.m_1bAA, (unsigned)p_MsgHeader.m_1bTC, (unsigned)p_MsgHeader.m_1bRD, + (unsigned)p_MsgHeader.m_1bRA, (unsigned)p_MsgHeader.m_4bRCode, + (unsigned)p_MsgHeader.m_u16QDCount, + (unsigned)p_MsgHeader.m_u16ANCount, + (unsigned)p_MsgHeader.m_u16NSCount, + (unsigned)p_MsgHeader.m_u16ARCount););*/ + + uint8_t u8B1((p_MsgHeader.m_1bQR << 7) | (p_MsgHeader.m_4bOpcode << 3) | (p_MsgHeader.m_1bAA << 2) | (p_MsgHeader.m_1bTC << 1) | (p_MsgHeader.m_1bRD)); + uint8_t u8B2((p_MsgHeader.m_1bRA << 7) | (p_MsgHeader.m_3bZ << 4) | (p_MsgHeader.m_4bRCode)); + bool bResult = ((_write16(p_MsgHeader.m_u16ID, p_rSendParameter)) && + (_write8(u8B1, p_rSendParameter)) && + (_write8(u8B2, p_rSendParameter)) && + (_write16(p_MsgHeader.m_u16QDCount, p_rSendParameter)) && + (_write16(p_MsgHeader.m_u16ANCount, p_rSendParameter)) && + (_write16(p_MsgHeader.m_u16NSCount, p_rSendParameter)) && + (_write16(p_MsgHeader.m_u16ARCount, p_rSendParameter))); + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSMsgHeader: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_writeRRAttributes +*/ +bool MDNSResponder::_writeMDNSRRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Attributes, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + + bool bResult = ((_write16(p_Attributes.m_u16Type, p_rSendParameter)) && + (_write16(p_Attributes.m_u16Class, p_rSendParameter))); + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSRRAttributes: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_writeMDNSRRDomain +*/ +bool MDNSResponder::_writeMDNSRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_Domain, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + + bool bResult = ((_udpAppendBuffer((const unsigned char*)p_Domain.m_acName, p_Domain.m_u16NameLength)) && + (p_rSendParameter.shiftOffset(p_Domain.m_u16NameLength))); + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSRRDomain: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_writeMDNSHostDomain + + Write a host domain to the UDP output buffer. + If the domain record is part of the answer, the records length is + prepended (p_bPrependRDLength is set). + + A very simple form of name compression is applied here: + If the domain is written to the UDP output buffer, the write offset is stored + together with a domain id (the pointer) in a p_rSendParameter substructure (cache). + If the same domain (pointer) should be written to the UDP output later again, + the old offset is retrieved from the cache, marked as a compressed domain offset + and written to the output buffer. + +*/ +bool MDNSResponder::_writeMDNSHostDomain(const char* p_pcHostname, + bool p_bPrependRDLength, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + + // The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV' + uint16_t u16CachedDomainOffset = p_rSendParameter.findCachedDomainOffset((const void*)p_pcHostname, false); + + stcMDNS_RRDomain hostDomain; + bool bResult = (u16CachedDomainOffset + // Found cached domain -> mark as compressed domain + ? ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset + ((!p_bPrependRDLength) || + (_write16(2, p_rSendParameter))) && // Length of 'Cxxx' + (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) + (_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter))) + // No cached domain -> add this domain to cache and write full domain name + : ((_buildDomainForHost(p_pcHostname, hostDomain)) && // eg. esp8266.local + ((!p_bPrependRDLength) || + (_write16(hostDomain.m_u16NameLength, p_rSendParameter))) && // RDLength (if needed) + (p_rSendParameter.addDomainCacheItem((const void*)p_pcHostname, false, p_rSendParameter.m_u16Offset)) && + (_writeMDNSRRDomain(hostDomain, p_rSendParameter)))); + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSHostDomain: FAILED!\n")); + }); + return bResult; + +} + +/* + MDNSResponder::_writeMDNSServiceDomain + + Write a service domain to the UDP output buffer. + If the domain record is part of the answer, the records length is + prepended (p_bPrependRDLength is set). + + A very simple form of name compression is applied here: see '_writeMDNSHostDomain' + The cache differentiates of course between service domains which includes + the instance name (p_bIncludeName is set) and thoose who don't. + +*/ +bool MDNSResponder::_writeMDNSServiceDomain(const MDNSResponder::stcMDNSService& p_Service, + bool p_bIncludeName, + bool p_bPrependRDLength, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + + // The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV' + uint16_t u16CachedDomainOffset = p_rSendParameter.findCachedDomainOffset((const void*)&p_Service, p_bIncludeName); + + stcMDNS_RRDomain serviceDomain; + bool bResult = (u16CachedDomainOffset + // Found cached domain -> mark as compressed domain + ? ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset + ((!p_bPrependRDLength) || + (_write16(2, p_rSendParameter))) && // Lenght of 'Cxxx' + (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) + (_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter))) + // No cached domain -> add this domain to cache and write full domain name + : ((_buildDomainForService(p_Service, p_bIncludeName, serviceDomain)) && // eg. MyESP._http._tcp.local + ((!p_bPrependRDLength) || + (_write16(serviceDomain.m_u16NameLength, p_rSendParameter))) && // RDLength (if needed) + (p_rSendParameter.addDomainCacheItem((const void*)&p_Service, p_bIncludeName, p_rSendParameter.m_u16Offset)) && + (_writeMDNSRRDomain(serviceDomain, p_rSendParameter)))); + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSServiceDomain: FAILED!\n")); + }); + return bResult; + +} + +/* + MDNSResponder::_writeMDNSQuestion + + Write a MDNS question to the UDP output buffer + + QNAME (host/service domain, eg. esp8266.local) + QTYPE (16bit, eg. ANY) + QCLASS (16bit, eg. IN) + +*/ +bool MDNSResponder::_writeMDNSQuestion(MDNSResponder::stcMDNS_RRQuestion& p_Question, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSQuestion\n"));); + + bool bResult = ((_writeMDNSRRDomain(p_Question.m_Header.m_Domain, p_rSendParameter)) && + (_writeMDNSRRAttributes(p_Question.m_Header.m_Attributes, p_rSendParameter))); + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSQuestion: FAILED!\n")); + }); + return bResult; + +} + + +#ifdef MDNS_IP4_SUPPORT +/* + MDNSResponder::_writeMDNSAnswer_A + + Write a MDNS A answer to the UDP output buffer. + + NAME (var, host/service domain, eg. esp8266.local + TYPE (16bit, eg. A) + CLASS (16bit, eg. IN) + TTL (32bit, eg. 120) + RDLENGTH (16bit, eg 4) + RDATA (var, eg. 123.456.789.012) + + eg. esp8266.local A 0x8001 120 4 123.456.789.012 + Ref: http://www.zytrax.com/books/dns/ch8/a.html +*/ +bool MDNSResponder::_writeMDNSAnswer_A(IPAddress p_IPAddress, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_A (%s)\n"), p_IPAddress.toString().c_str());); + + stcMDNS_RRAttributes attributes(DNS_RRTYPE_A, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet + const unsigned char aucIPAddress[MDNS_IP4_SIZE] = { p_IPAddress[0], p_IPAddress[1], p_IPAddress[2], p_IPAddress[3] }; + bool bResult = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) && + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL + (_write16(MDNS_IP4_SIZE, p_rSendParameter)) && // RDLength + (_udpAppendBuffer(aucIPAddress, MDNS_IP4_SIZE)) && // RData + (p_rSendParameter.shiftOffset(MDNS_IP4_SIZE))); + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_A: FAILED!\n")); + }); + return bResult; + +} + +/* + MDNSResponder::_writeMDNSAnswer_PTR_IP4 + + Write a MDNS reverse IP4 PTR answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' + + eg. 012.789.456.123.in-addr.arpa PTR 0x8001 120 15 esp8266.local + Used while answering reverse IP4 questions +*/ +bool MDNSResponder::_writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP4 (%s)\n"), p_IPAddress.toString().c_str());); + + stcMDNS_RRDomain reverseIP4Domain; + stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet + stcMDNS_RRDomain hostDomain; + bool bResult = ((_buildDomainForReverseIP4(p_IPAddress, reverseIP4Domain)) && // 012.789.456.123.in-addr.arpa + (_writeMDNSRRDomain(reverseIP4Domain, p_rSendParameter)) && + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL + (_writeMDNSHostDomain(m_pcHostname, true, p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local) + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP4: FAILED!\n")); + }); + return bResult; +} +#endif + +/* + MDNSResponder::_writeMDNSAnswer_PTR_TYPE + + Write a MDNS PTR answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' + + PTR all-services -> service type + eg. _services._dns-sd._udp.local PTR 0x8001 5400 xx _http._tcp.local + http://www.zytrax.com/books/dns/ch8/ptr.html +*/ +bool MDNSResponder::_writeMDNSAnswer_PTR_TYPE(MDNSResponder::stcMDNSService& p_rService, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_TYPE\n"));); + + stcMDNS_RRDomain dnssdDomain; + stcMDNS_RRDomain serviceDomain; + stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, DNS_RRCLASS_IN); // No cache flush! only INternet + bool bResult = ((_buildDomainForDNSSD(dnssdDomain)) && // _services._dns-sd._udp.local + (_writeMDNSRRDomain(dnssdDomain, p_rSendParameter)) && + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL + (_writeMDNSServiceDomain(p_rService, false, true, p_rSendParameter))); // RDLength & RData (service domain, eg. _http._tcp.local) + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_TYPE: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_writeMDNSAnswer_PTR_NAME + + Write a MDNS PTR answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' + + PTR service type -> service name + eg. _http.tcp.local PTR 0x8001 120 xx myESP._http._tcp.local + http://www.zytrax.com/books/dns/ch8/ptr.html +*/ +bool MDNSResponder::_writeMDNSAnswer_PTR_NAME(MDNSResponder::stcMDNSService& p_rService, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_NAME\n"));); + + stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, DNS_RRCLASS_IN); // No cache flush! only INternet + bool bResult = ((_writeMDNSServiceDomain(p_rService, false, false, p_rSendParameter)) && // _http._tcp.local + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL + (_writeMDNSServiceDomain(p_rService, true, true, p_rSendParameter))); // RDLength & RData (service domain, eg. MyESP._http._tcp.local) + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_NAME: FAILED!\n")); + }); + return bResult; +} + + +/* + MDNSResponder::_writeMDNSAnswer_TXT + + Write a MDNS TXT answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' + + The TXT items in the RDATA block are 'length byte encoded': [len]vardata + + eg. myESP._http._tcp.local TXT 0x8001 120 4 c#=1 + http://www.zytrax.com/books/dns/ch8/txt.html +*/ +bool MDNSResponder::_writeMDNSAnswer_TXT(MDNSResponder::stcMDNSService& p_rService, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT\n"));); + + bool bResult = false; + + stcMDNS_RRAttributes attributes(DNS_RRTYPE_TXT, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet + + if ((_collectServiceTxts(p_rService)) && + (_writeMDNSServiceDomain(p_rService, true, false, p_rSendParameter)) && // MyESP._http._tcp.local + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL + (_write16(p_rService.m_Txts.length(), p_rSendParameter))) // RDLength + { + + bResult = true; + // RData Txts + for (stcMDNSServiceTxt* pTxt = p_rService.m_Txts.m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) + { + unsigned char ucLengthByte = pTxt->length(); + bResult = ((_udpAppendBuffer((unsigned char*)&ucLengthByte, sizeof(ucLengthByte))) && // Length + (p_rSendParameter.shiftOffset(sizeof(ucLengthByte))) && + ((size_t)os_strlen(pTxt->m_pcKey) == m_pUDPContext->append(pTxt->m_pcKey, os_strlen(pTxt->m_pcKey))) && // Key + (p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcKey))) && + (1 == m_pUDPContext->append("=", 1)) && // = + (p_rSendParameter.shiftOffset(1)) && + ((!pTxt->m_pcValue) || + (((size_t)os_strlen(pTxt->m_pcValue) == m_pUDPContext->append(pTxt->m_pcValue, os_strlen(pTxt->m_pcValue))) && // Value + (p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcValue)))))); + + DEBUG_EX_ERR(if (!bResult) + { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT: FAILED to write %sTxt %s=%s!\n"), (pTxt->m_bTemp ? "temp. " : ""), (pTxt->m_pcKey ? : "?"), (pTxt->m_pcValue ? : "?")); + }); + } + } + _releaseTempServiceTxts(p_rService); + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT: FAILED!\n")); + }); + return bResult; +} + +#ifdef MDNS_IP6_SUPPORT +/* + MDNSResponder::_writeMDNSAnswer_AAAA + + Write a MDNS AAAA answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' + + eg. esp8266.local AAAA 0x8001 120 16 xxxx::xx + http://www.zytrax.com/books/dns/ch8/aaaa.html +*/ +bool MDNSResponder::_writeMDNSAnswer_AAAA(IPAddress p_IPAddress, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA\n"));); + + stcMDNS_RRAttributes attributes(DNS_RRTYPE_AAAA, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet + bool bResult = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) && // esp8266.local + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL + (_write16(MDNS_IP6_SIZE, p_rSendParameter)) && // RDLength + (false /*TODO: IP6 version of: _udpAppendBuffer((uint32_t)p_IPAddress, MDNS_IP4_SIZE)*/)); // RData + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA: FAILED!\n")); + }); + return bResult; +} + +/* + MDNSResponder::_writeMDNSAnswer_PTR_IP6 + + Write a MDNS reverse IP6 PTR answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' + + eg. xxxx::xx.in6.arpa PTR 0x8001 120 15 esp8266.local + Used while answering reverse IP6 questions +*/ +bool MDNSResponder::_writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP6\n"));); + + stcMDNS_RRDomain reverseIP6Domain; + stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet + bool bResult = ((_buildDomainForReverseIP6(p_IPAddress, reverseIP6Domain)) && // xxxx::xx.ip6.arpa + (_writeMDNSRRDomain(reverseIP6Domain, p_rSendParameter)) && + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL + (_writeMDNSHostDomain(m_pcHostname, true, p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local) + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP6: FAILED!\n")); + }); + return bResult; +} +#endif + +/* + MDNSResponder::_writeMDNSAnswer_SRV + + eg. MyESP._http.tcp.local SRV 0x8001 120 0 0 60068 esp8266.local + http://www.zytrax.com/books/dns/ch8/srv.html ???? Include instance name ???? +*/ +bool MDNSResponder::_writeMDNSAnswer_SRV(MDNSResponder::stcMDNSService& p_rService, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) +{ + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_SRV\n"));); + + uint16_t u16CachedDomainOffset = (p_rSendParameter.m_bLegacyQuery + ? 0 + : p_rSendParameter.findCachedDomainOffset((const void*)m_pcHostname, false)); + + stcMDNS_RRAttributes attributes(DNS_RRTYPE_SRV, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet + stcMDNS_RRDomain hostDomain; + bool bResult = ((_writeMDNSServiceDomain(p_rService, true, false, p_rSendParameter)) && // MyESP._http._tcp.local + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL + (!u16CachedDomainOffset + // No cache for domain name (or no compression allowed) + ? ((_buildDomainForHost(m_pcHostname, hostDomain)) && + (_write16((sizeof(uint16_t /*Prio*/) + // RDLength + sizeof(uint16_t /*Weight*/) + + sizeof(uint16_t /*Port*/) + + hostDomain.m_u16NameLength), p_rSendParameter)) && // Domain length + (_write16(MDNS_SRV_PRIORITY, p_rSendParameter)) && // Priority + (_write16(MDNS_SRV_WEIGHT, p_rSendParameter)) && // Weight + (_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port + (p_rSendParameter.addDomainCacheItem((const void*)m_pcHostname, false, p_rSendParameter.m_u16Offset)) && + (_writeMDNSRRDomain(hostDomain, p_rSendParameter))) // Host, eg. esp8266.local + // Cache available for domain + : ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset + (_write16((sizeof(uint16_t /*Prio*/) + // RDLength + sizeof(uint16_t /*Weight*/) + + sizeof(uint16_t /*Port*/) + + 2), p_rSendParameter)) && // Length of 'C0xx' + (_write16(MDNS_SRV_PRIORITY, p_rSendParameter)) && // Priority + (_write16(MDNS_SRV_WEIGHT, p_rSendParameter)) && // Weight + (_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port + (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) + (_write8((uint8_t)u16CachedDomainOffset, p_rSendParameter))))); // Offset + + DEBUG_EX_ERR(if (!bResult) +{ + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_SRV: FAILED!\n")); + }); + return bResult; +} + +} // namespace MDNSImplementation + +} // namespace esp8266 + + + + + + diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h b/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h index c58aebdc74..a3bcc4b370 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h @@ -1,26 +1,26 @@ /* - * LEAmDNS_Priv.h - * - * License (MIT license): - * 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. - * - */ + LEAmDNS_Priv.h + + License (MIT license): + 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. + +*/ #ifndef MDNS_LWIPDEFS_H #define MDNS_LWIPDEFS_H diff --git a/libraries/Ethernet/examples/udpntpclient_pedantic/RFC1305.h b/libraries/Ethernet/examples/udpntpclient_pedantic/RFC1305.h index c328290c2e..fa0b72fd7a 100644 --- a/libraries/Ethernet/examples/udpntpclient_pedantic/RFC1305.h +++ b/libraries/Ethernet/examples/udpntpclient_pedantic/RFC1305.h @@ -1,50 +1,51 @@ -#ifndef RFC1305_H -#define RFC1305_H -/* - * see https://www.eecis.udel.edu/~mills/database/rfc/rfc1305/rfc1305c.pdf - * https://tools.ietf.org/html/rfc1305 - */ - -#pragma pack(1) -struct sRFC1305 { - // NOTE all fields are BIG-ENDIAN so must be swapped on little endian machines - uint8_t MODE:3; - uint8_t VN:3; - uint8_t LI:2; - uint8_t stratum; - uint8_t poll; - uint8_t precision; - int16_t rootdelay_main; - uint16_t rootdelay_fraction; - int16_t rootdispersion_main; - uint16_t rootdispersion_fraction; - uint8_t identifier[4]; - // 64 bit timestamps contain 32 bit whole part + 32 bit fractional part - uint32_t referencetimestamp_main; - uint32_t referencetimestamp_fraction; - uint32_t origintimestamp_main; - uint32_t origintimestamp_fraction; - uint32_t receivetimestamp_main; - uint32_t receivetimestamp_fraction; - uint32_t transmittimestamp_main; - uint32_t transmittimestamp_fraction; -}; -#pragma pack(0) - -#define LI_NOWARNING 0 -#define LI_61_SEC 1 -#define LI_59_SEC 2 -#define LI_ALARM 3 - -#define VERN 4 - -#define MODE_SYMMETRIC_ACTIVE 1 -#define MODE_SYMMETRIC_PASSIVE 2 -#define MODE_CLIENT 3 -#define MODE_SERVER 4 -#define MODE_BROADCAST 5 - -#define ENDIAN_SWAP_32(l) ((l>>24) |((l>>16)<<8)&0xff00 | ((l>>8)<<16)&0xff0000 | (l << 24)) -#define ENDIAN_SWAP_16(l) ((l>>8) | (l << 8)) -#endif - +#ifndef RFC1305_H +#define RFC1305_H +/* + see https://www.eecis.udel.edu/~mills/database/rfc/rfc1305/rfc1305c.pdf + https://tools.ietf.org/html/rfc1305 +*/ + +#pragma pack(1) +struct sRFC1305 +{ + // NOTE all fields are BIG-ENDIAN so must be swapped on little endian machines + uint8_t MODE: 3; + uint8_t VN: 3; + uint8_t LI: 2; + uint8_t stratum; + uint8_t poll; + uint8_t precision; + int16_t rootdelay_main; + uint16_t rootdelay_fraction; + int16_t rootdispersion_main; + uint16_t rootdispersion_fraction; + uint8_t identifier[4]; + // 64 bit timestamps contain 32 bit whole part + 32 bit fractional part + uint32_t referencetimestamp_main; + uint32_t referencetimestamp_fraction; + uint32_t origintimestamp_main; + uint32_t origintimestamp_fraction; + uint32_t receivetimestamp_main; + uint32_t receivetimestamp_fraction; + uint32_t transmittimestamp_main; + uint32_t transmittimestamp_fraction; +}; +#pragma pack(0) + +#define LI_NOWARNING 0 +#define LI_61_SEC 1 +#define LI_59_SEC 2 +#define LI_ALARM 3 + +#define VERN 4 + +#define MODE_SYMMETRIC_ACTIVE 1 +#define MODE_SYMMETRIC_PASSIVE 2 +#define MODE_CLIENT 3 +#define MODE_SERVER 4 +#define MODE_BROADCAST 5 + +#define ENDIAN_SWAP_32(l) ((l>>24) |((l>>16)<<8)&0xff00 | ((l>>8)<<16)&0xff0000 | (l << 24)) +#define ENDIAN_SWAP_16(l) ((l>>8) | (l << 8)) +#endif + diff --git a/libraries/Ethernet/src/Dhcp.cpp b/libraries/Ethernet/src/Dhcp.cpp index 5f53db41bc..24355675ee 100644 --- a/libraries/Ethernet/src/Dhcp.cpp +++ b/libraries/Ethernet/src/Dhcp.cpp @@ -1,481 +1,508 @@ -// DHCP Library v0.3 - April 25, 2009 -// Author: Jordan Terrell - blog.jordanterrell.com - -#include "utility/w5100.h" - -#include -#include -#include "Dhcp.h" -#include "Arduino.h" -#include "utility/util.h" - -int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) -{ - _dhcpLeaseTime=0; - _dhcpT1=0; - _dhcpT2=0; - _lastCheck=0; - _timeout = timeout; - _responseTimeout = responseTimeout; - - // zero out _dhcpMacAddr - memset(_dhcpMacAddr, 0, 6); - reset_DHCP_lease(); - - memcpy((void*)_dhcpMacAddr, (void*)mac, 6); - _dhcp_state = STATE_DHCP_START; - return request_DHCP_lease(); -} - -void DhcpClass::reset_DHCP_lease(){ - // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp - memset(_dhcpLocalIp, 0, 20); -} - -//return:0 on error, 1 if request is sent and response is received -int DhcpClass::request_DHCP_lease(){ - - uint8_t messageType = 0; - - - - // Pick an initial transaction ID - _dhcpTransactionId = random(1UL, 2000UL); - _dhcpInitialTransactionId = _dhcpTransactionId; - - _dhcpUdpSocket.stop(); - if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) - { - // Couldn't get a socket - return 0; - } - - presend_DHCP(); - - int result = 0; - - unsigned long startTime = millis(); - - while(_dhcp_state != STATE_DHCP_LEASED) - { - if(_dhcp_state == STATE_DHCP_START) - { - _dhcpTransactionId++; - - send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000)); - _dhcp_state = STATE_DHCP_DISCOVER; - } - else if(_dhcp_state == STATE_DHCP_REREQUEST){ - _dhcpTransactionId++; - send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime)/1000)); - _dhcp_state = STATE_DHCP_REQUEST; - } - else if(_dhcp_state == STATE_DHCP_DISCOVER) - { - uint32_t respId; - messageType = parseDHCPResponse(_responseTimeout, respId); - if(messageType == DHCP_OFFER) - { - // We'll use the transaction ID that the offer came with, - // rather than the one we were up to - _dhcpTransactionId = respId; - send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000)); - _dhcp_state = STATE_DHCP_REQUEST; - } - } - else if(_dhcp_state == STATE_DHCP_REQUEST) - { - uint32_t respId; - messageType = parseDHCPResponse(_responseTimeout, respId); - if(messageType == DHCP_ACK) - { - _dhcp_state = STATE_DHCP_LEASED; - result = 1; - //use default lease time if we didn't get it - if(_dhcpLeaseTime == 0){ - _dhcpLeaseTime = DEFAULT_LEASE; - } - //calculate T1 & T2 if we didn't get it - if(_dhcpT1 == 0){ - //T1 should be 50% of _dhcpLeaseTime - _dhcpT1 = _dhcpLeaseTime >> 1; - } - if(_dhcpT2 == 0){ - //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime - _dhcpT2 = _dhcpT1 << 1; - } - _renewInSec = _dhcpT1; - _rebindInSec = _dhcpT2; - } - else if(messageType == DHCP_NAK) - _dhcp_state = STATE_DHCP_START; - } - - if(messageType == 255) - { - messageType = 0; - _dhcp_state = STATE_DHCP_START; - } - - if(result != 1 && ((millis() - startTime) > _timeout)) - break; - } - - // We're done with the socket now - _dhcpUdpSocket.stop(); - _dhcpTransactionId++; - - return result; -} - -void DhcpClass::presend_DHCP() -{ -} - -void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed) -{ - uint8_t buffer[32]; - memset(buffer, 0, 32); - IPAddress dest_addr( 255, 255, 255, 255 ); // Broadcast address - - if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT)) - { - // FIXME Need to return errors - return; - } - - buffer[0] = DHCP_BOOTREQUEST; // op - buffer[1] = DHCP_HTYPE10MB; // htype - buffer[2] = DHCP_HLENETHERNET; // hlen - buffer[3] = DHCP_HOPS; // hops - - // xid - unsigned long xid = htonl(_dhcpTransactionId); - memcpy(buffer + 4, &(xid), 4); - - // 8, 9 - seconds elapsed - buffer[8] = ((secondsElapsed & 0xff00) >> 8); - buffer[9] = (secondsElapsed & 0x00ff); - - // flags - unsigned short flags = htons(DHCP_FLAGSBROADCAST); - memcpy(buffer + 10, &(flags), 2); - - // ciaddr: already zeroed - // yiaddr: already zeroed - // siaddr: already zeroed - // giaddr: already zeroed - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 28); - - memset(buffer, 0, 32); // clear local buffer - - memcpy(buffer, _dhcpMacAddr, 6); // chaddr - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 16); - - memset(buffer, 0, 32); // clear local buffer - - // leave zeroed out for sname && file - // put in W5100 transmit buffer x 6 (192 bytes) - - for(int i = 0; i < 6; i++) { - _dhcpUdpSocket.write(buffer, 32); - } - - // OPT - Magic Cookie - buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24)& 0xFF); - buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16)& 0xFF); - buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8)& 0xFF); - buffer[3] = (uint8_t)(MAGIC_COOKIE& 0xFF); - - // OPT - message type - buffer[4] = dhcpMessageType; - buffer[5] = 0x01; - buffer[6] = messageType; //DHCP_REQUEST; - - // OPT - client identifier - buffer[7] = dhcpClientIdentifier; - buffer[8] = 0x07; - buffer[9] = 0x01; - memcpy(buffer + 10, _dhcpMacAddr, 6); - - // OPT - host name - buffer[16] = hostName; - buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address - strcpy((char*)&(buffer[18]), HOST_NAME); - - printByte((char*)&(buffer[24]), _dhcpMacAddr[3]); - printByte((char*)&(buffer[26]), _dhcpMacAddr[4]); - printByte((char*)&(buffer[28]), _dhcpMacAddr[5]); - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 30); - - if(messageType == DHCP_REQUEST) - { - buffer[0] = dhcpRequestedIPaddr; - buffer[1] = 0x04; - buffer[2] = _dhcpLocalIp[0]; - buffer[3] = _dhcpLocalIp[1]; - buffer[4] = _dhcpLocalIp[2]; - buffer[5] = _dhcpLocalIp[3]; - - buffer[6] = dhcpServerIdentifier; - buffer[7] = 0x04; - buffer[8] = _dhcpDhcpServerIp[0]; - buffer[9] = _dhcpDhcpServerIp[1]; - buffer[10] = _dhcpDhcpServerIp[2]; - buffer[11] = _dhcpDhcpServerIp[3]; - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 12); - } - - buffer[0] = dhcpParamRequest; - buffer[1] = 0x06; - buffer[2] = subnetMask; - buffer[3] = routersOnSubnet; - buffer[4] = dns; - buffer[5] = domainName; - buffer[6] = dhcpT1value; - buffer[7] = dhcpT2value; - buffer[8] = endOption; - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 9); - - _dhcpUdpSocket.endPacket(); -} - -uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId) -{ - uint8_t type = 0; - uint8_t opt_len = 0; - - unsigned long startTime = millis(); - - while(_dhcpUdpSocket.parsePacket() <= 0) - { - if((millis() - startTime) > responseTimeout) - { - return 255; - } - delay(50); - } - // start reading in the packet - RIP_MSG_FIXED fixedMsg; - _dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED)); - - if(fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) - { - transactionId = ntohl(fixedMsg.xid); - if(memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || (transactionId < _dhcpInitialTransactionId) || (transactionId > _dhcpTransactionId)) - { - // Need to read the rest of the packet here regardless - _dhcpUdpSocket.flush(); - return 0; - } - - memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4); - - // Skip to the option part - // Doing this a byte at a time so we don't have to put a big buffer - // on the stack (as we don't have lots of memory lying around) - for (int i =0; i < (240 - (int)sizeof(RIP_MSG_FIXED)); i++) - { - _dhcpUdpSocket.read(); // we don't care about the returned byte - } - - while (_dhcpUdpSocket.available() > 0) - { - switch (_dhcpUdpSocket.read()) - { - case endOption : - break; - - case padOption : - break; - - case dhcpMessageType : - opt_len = _dhcpUdpSocket.read(); - type = _dhcpUdpSocket.read(); - break; - - case subnetMask : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read(_dhcpSubnetMask, 4); - break; - - case routersOnSubnet : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read(_dhcpGatewayIp, 4); - for (int i = 0; i < opt_len-4; i++) - { - _dhcpUdpSocket.read(); - } - break; - - case dns : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); - for (int i = 0; i < opt_len-4; i++) - { - _dhcpUdpSocket.read(); - } - break; - - case dhcpServerIdentifier : - opt_len = _dhcpUdpSocket.read(); - if ((_dhcpDhcpServerIp[0] == 0 && _dhcpDhcpServerIp[1] == 0 && - _dhcpDhcpServerIp[2] == 0 && _dhcpDhcpServerIp[3] == 0) || - IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP()) - { - _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp)); - } - else - { - // Skip over the rest of this option - while (opt_len--) - { - _dhcpUdpSocket.read(); - } - } - break; - - case dhcpT1value : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1)); - _dhcpT1 = ntohl(_dhcpT1); - break; - - case dhcpT2value : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2)); - _dhcpT2 = ntohl(_dhcpT2); - break; - - case dhcpIPaddrLeaseTime : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime)); - _dhcpLeaseTime = ntohl(_dhcpLeaseTime); - _renewInSec = _dhcpLeaseTime; - break; - - default : - opt_len = _dhcpUdpSocket.read(); - // Skip over the rest of this option - while (opt_len--) - { - _dhcpUdpSocket.read(); - } - break; - } - } - } - - // Need to skip to end of the packet regardless here - _dhcpUdpSocket.flush(); - - return type; -} - - -/* - returns: - 0/DHCP_CHECK_NONE: nothing happened - 1/DHCP_CHECK_RENEW_FAIL: renew failed - 2/DHCP_CHECK_RENEW_OK: renew success - 3/DHCP_CHECK_REBIND_FAIL: rebind fail - 4/DHCP_CHECK_REBIND_OK: rebind success -*/ -int DhcpClass::checkLease(){ - //this uses a signed / unsigned trick to deal with millis overflow - unsigned long now = millis(); - signed long snow = (long)now; - int rc=DHCP_CHECK_NONE; - if (_lastCheck != 0){ - signed long factor; - //calc how many ms past the timeout we are - factor = snow - (long)_secTimeout; - //if on or passed the timeout, reduce the counters - if ( factor >= 0 ){ - //next timeout should be now plus 1000 ms minus parts of second in factor - _secTimeout = snow + 1000 - factor % 1000; - //how many seconds late are we, minimum 1 - factor = factor / 1000 +1; - - //reduce the counters by that mouch - //if we can assume that the cycle time (factor) is fairly constant - //and if the remainder is less than cycle time * 2 - //do it early instead of late - if(_renewInSec < factor*2 ) - _renewInSec = 0; - else - _renewInSec -= factor; - - if(_rebindInSec < factor*2 ) - _rebindInSec = 0; - else - _rebindInSec -= factor; - } - - //if we have a lease but should renew, do it - if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <=0){ - _dhcp_state = STATE_DHCP_REREQUEST; - rc = 1 + request_DHCP_lease(); - } - - //if we have a lease or is renewing but should bind, do it - if( (_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <=0){ - //this should basically restart completely - _dhcp_state = STATE_DHCP_START; - reset_DHCP_lease(); - rc = 3 + request_DHCP_lease(); - } - } - else{ - _secTimeout = snow + 1000; - } - - _lastCheck = now; - return rc; -} - -IPAddress DhcpClass::getLocalIp() -{ - return IPAddress(_dhcpLocalIp); -} - -IPAddress DhcpClass::getSubnetMask() -{ - return IPAddress(_dhcpSubnetMask); -} - -IPAddress DhcpClass::getGatewayIp() -{ - return IPAddress(_dhcpGatewayIp); -} - -IPAddress DhcpClass::getDhcpServerIp() -{ - return IPAddress(_dhcpDhcpServerIp); -} - -IPAddress DhcpClass::getDnsServerIp() -{ - return IPAddress(_dhcpDnsServerIp); -} - -void DhcpClass::printByte(char * buf, uint8_t n ) { - char *str = &buf[1]; - buf[0]='0'; - do { - unsigned long m = n; - n /= 16; - char c = m - 16 * n; - *str-- = c < 10 ? c + '0' : c + 'A' - 10; - } while(n); -} +// DHCP Library v0.3 - April 25, 2009 +// Author: Jordan Terrell - blog.jordanterrell.com + +#include "utility/w5100.h" + +#include +#include +#include "Dhcp.h" +#include "Arduino.h" +#include "utility/util.h" + +int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) +{ + _dhcpLeaseTime = 0; + _dhcpT1 = 0; + _dhcpT2 = 0; + _lastCheck = 0; + _timeout = timeout; + _responseTimeout = responseTimeout; + + // zero out _dhcpMacAddr + memset(_dhcpMacAddr, 0, 6); + reset_DHCP_lease(); + + memcpy((void*)_dhcpMacAddr, (void*)mac, 6); + _dhcp_state = STATE_DHCP_START; + return request_DHCP_lease(); +} + +void DhcpClass::reset_DHCP_lease() +{ + // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp + memset(_dhcpLocalIp, 0, 20); +} + +//return:0 on error, 1 if request is sent and response is received +int DhcpClass::request_DHCP_lease() +{ + + uint8_t messageType = 0; + + + + // Pick an initial transaction ID + _dhcpTransactionId = random(1UL, 2000UL); + _dhcpInitialTransactionId = _dhcpTransactionId; + + _dhcpUdpSocket.stop(); + if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) + { + // Couldn't get a socket + return 0; + } + + presend_DHCP(); + + int result = 0; + + unsigned long startTime = millis(); + + while (_dhcp_state != STATE_DHCP_LEASED) + { + if (_dhcp_state == STATE_DHCP_START) + { + _dhcpTransactionId++; + + send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000)); + _dhcp_state = STATE_DHCP_DISCOVER; + } + else if (_dhcp_state == STATE_DHCP_REREQUEST) + { + _dhcpTransactionId++; + send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000)); + _dhcp_state = STATE_DHCP_REQUEST; + } + else if (_dhcp_state == STATE_DHCP_DISCOVER) + { + uint32_t respId; + messageType = parseDHCPResponse(_responseTimeout, respId); + if (messageType == DHCP_OFFER) + { + // We'll use the transaction ID that the offer came with, + // rather than the one we were up to + _dhcpTransactionId = respId; + send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000)); + _dhcp_state = STATE_DHCP_REQUEST; + } + } + else if (_dhcp_state == STATE_DHCP_REQUEST) + { + uint32_t respId; + messageType = parseDHCPResponse(_responseTimeout, respId); + if (messageType == DHCP_ACK) + { + _dhcp_state = STATE_DHCP_LEASED; + result = 1; + //use default lease time if we didn't get it + if (_dhcpLeaseTime == 0) + { + _dhcpLeaseTime = DEFAULT_LEASE; + } + //calculate T1 & T2 if we didn't get it + if (_dhcpT1 == 0) + { + //T1 should be 50% of _dhcpLeaseTime + _dhcpT1 = _dhcpLeaseTime >> 1; + } + if (_dhcpT2 == 0) + { + //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime + _dhcpT2 = _dhcpT1 << 1; + } + _renewInSec = _dhcpT1; + _rebindInSec = _dhcpT2; + } + else if (messageType == DHCP_NAK) + { + _dhcp_state = STATE_DHCP_START; + } + } + + if (messageType == 255) + { + messageType = 0; + _dhcp_state = STATE_DHCP_START; + } + + if (result != 1 && ((millis() - startTime) > _timeout)) + { + break; + } + } + + // We're done with the socket now + _dhcpUdpSocket.stop(); + _dhcpTransactionId++; + + return result; +} + +void DhcpClass::presend_DHCP() +{ +} + +void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed) +{ + uint8_t buffer[32]; + memset(buffer, 0, 32); + IPAddress dest_addr(255, 255, 255, 255); // Broadcast address + + if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT)) + { + // FIXME Need to return errors + return; + } + + buffer[0] = DHCP_BOOTREQUEST; // op + buffer[1] = DHCP_HTYPE10MB; // htype + buffer[2] = DHCP_HLENETHERNET; // hlen + buffer[3] = DHCP_HOPS; // hops + + // xid + unsigned long xid = htonl(_dhcpTransactionId); + memcpy(buffer + 4, &(xid), 4); + + // 8, 9 - seconds elapsed + buffer[8] = ((secondsElapsed & 0xff00) >> 8); + buffer[9] = (secondsElapsed & 0x00ff); + + // flags + unsigned short flags = htons(DHCP_FLAGSBROADCAST); + memcpy(buffer + 10, &(flags), 2); + + // ciaddr: already zeroed + // yiaddr: already zeroed + // siaddr: already zeroed + // giaddr: already zeroed + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 28); + + memset(buffer, 0, 32); // clear local buffer + + memcpy(buffer, _dhcpMacAddr, 6); // chaddr + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 16); + + memset(buffer, 0, 32); // clear local buffer + + // leave zeroed out for sname && file + // put in W5100 transmit buffer x 6 (192 bytes) + + for (int i = 0; i < 6; i++) + { + _dhcpUdpSocket.write(buffer, 32); + } + + // OPT - Magic Cookie + buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24) & 0xFF); + buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16) & 0xFF); + buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8) & 0xFF); + buffer[3] = (uint8_t)(MAGIC_COOKIE & 0xFF); + + // OPT - message type + buffer[4] = dhcpMessageType; + buffer[5] = 0x01; + buffer[6] = messageType; //DHCP_REQUEST; + + // OPT - client identifier + buffer[7] = dhcpClientIdentifier; + buffer[8] = 0x07; + buffer[9] = 0x01; + memcpy(buffer + 10, _dhcpMacAddr, 6); + + // OPT - host name + buffer[16] = hostName; + buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address + strcpy((char*) & (buffer[18]), HOST_NAME); + + printByte((char*) & (buffer[24]), _dhcpMacAddr[3]); + printByte((char*) & (buffer[26]), _dhcpMacAddr[4]); + printByte((char*) & (buffer[28]), _dhcpMacAddr[5]); + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 30); + + if (messageType == DHCP_REQUEST) + { + buffer[0] = dhcpRequestedIPaddr; + buffer[1] = 0x04; + buffer[2] = _dhcpLocalIp[0]; + buffer[3] = _dhcpLocalIp[1]; + buffer[4] = _dhcpLocalIp[2]; + buffer[5] = _dhcpLocalIp[3]; + + buffer[6] = dhcpServerIdentifier; + buffer[7] = 0x04; + buffer[8] = _dhcpDhcpServerIp[0]; + buffer[9] = _dhcpDhcpServerIp[1]; + buffer[10] = _dhcpDhcpServerIp[2]; + buffer[11] = _dhcpDhcpServerIp[3]; + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 12); + } + + buffer[0] = dhcpParamRequest; + buffer[1] = 0x06; + buffer[2] = subnetMask; + buffer[3] = routersOnSubnet; + buffer[4] = dns; + buffer[5] = domainName; + buffer[6] = dhcpT1value; + buffer[7] = dhcpT2value; + buffer[8] = endOption; + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 9); + + _dhcpUdpSocket.endPacket(); +} + +uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId) +{ + uint8_t type = 0; + uint8_t opt_len = 0; + + unsigned long startTime = millis(); + + while (_dhcpUdpSocket.parsePacket() <= 0) + { + if ((millis() - startTime) > responseTimeout) + { + return 255; + } + delay(50); + } + // start reading in the packet + RIP_MSG_FIXED fixedMsg; + _dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED)); + + if (fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) + { + transactionId = ntohl(fixedMsg.xid); + if (memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || (transactionId < _dhcpInitialTransactionId) || (transactionId > _dhcpTransactionId)) + { + // Need to read the rest of the packet here regardless + _dhcpUdpSocket.flush(); + return 0; + } + + memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4); + + // Skip to the option part + // Doing this a byte at a time so we don't have to put a big buffer + // on the stack (as we don't have lots of memory lying around) + for (int i = 0; i < (240 - (int)sizeof(RIP_MSG_FIXED)); i++) + { + _dhcpUdpSocket.read(); // we don't care about the returned byte + } + + while (_dhcpUdpSocket.available() > 0) + { + switch (_dhcpUdpSocket.read()) + { + case endOption : + break; + + case padOption : + break; + + case dhcpMessageType : + opt_len = _dhcpUdpSocket.read(); + type = _dhcpUdpSocket.read(); + break; + + case subnetMask : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read(_dhcpSubnetMask, 4); + break; + + case routersOnSubnet : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read(_dhcpGatewayIp, 4); + for (int i = 0; i < opt_len - 4; i++) + { + _dhcpUdpSocket.read(); + } + break; + + case dns : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); + for (int i = 0; i < opt_len - 4; i++) + { + _dhcpUdpSocket.read(); + } + break; + + case dhcpServerIdentifier : + opt_len = _dhcpUdpSocket.read(); + if ((_dhcpDhcpServerIp[0] == 0 && _dhcpDhcpServerIp[1] == 0 && + _dhcpDhcpServerIp[2] == 0 && _dhcpDhcpServerIp[3] == 0) || + IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP()) + { + _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp)); + } + else + { + // Skip over the rest of this option + while (opt_len--) + { + _dhcpUdpSocket.read(); + } + } + break; + + case dhcpT1value : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1)); + _dhcpT1 = ntohl(_dhcpT1); + break; + + case dhcpT2value : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2)); + _dhcpT2 = ntohl(_dhcpT2); + break; + + case dhcpIPaddrLeaseTime : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime)); + _dhcpLeaseTime = ntohl(_dhcpLeaseTime); + _renewInSec = _dhcpLeaseTime; + break; + + default : + opt_len = _dhcpUdpSocket.read(); + // Skip over the rest of this option + while (opt_len--) + { + _dhcpUdpSocket.read(); + } + break; + } + } + } + + // Need to skip to end of the packet regardless here + _dhcpUdpSocket.flush(); + + return type; +} + + +/* + returns: + 0/DHCP_CHECK_NONE: nothing happened + 1/DHCP_CHECK_RENEW_FAIL: renew failed + 2/DHCP_CHECK_RENEW_OK: renew success + 3/DHCP_CHECK_REBIND_FAIL: rebind fail + 4/DHCP_CHECK_REBIND_OK: rebind success +*/ +int DhcpClass::checkLease() +{ + //this uses a signed / unsigned trick to deal with millis overflow + unsigned long now = millis(); + signed long snow = (long)now; + int rc = DHCP_CHECK_NONE; + if (_lastCheck != 0) + { + signed long factor; + //calc how many ms past the timeout we are + factor = snow - (long)_secTimeout; + //if on or passed the timeout, reduce the counters + if (factor >= 0) + { + //next timeout should be now plus 1000 ms minus parts of second in factor + _secTimeout = snow + 1000 - factor % 1000; + //how many seconds late are we, minimum 1 + factor = factor / 1000 + 1; + + //reduce the counters by that mouch + //if we can assume that the cycle time (factor) is fairly constant + //and if the remainder is less than cycle time * 2 + //do it early instead of late + if (_renewInSec < factor * 2) + { + _renewInSec = 0; + } + else + { + _renewInSec -= factor; + } + + if (_rebindInSec < factor * 2) + { + _rebindInSec = 0; + } + else + { + _rebindInSec -= factor; + } + } + + //if we have a lease but should renew, do it + if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <= 0) + { + _dhcp_state = STATE_DHCP_REREQUEST; + rc = 1 + request_DHCP_lease(); + } + + //if we have a lease or is renewing but should bind, do it + if ((_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <= 0) + { + //this should basically restart completely + _dhcp_state = STATE_DHCP_START; + reset_DHCP_lease(); + rc = 3 + request_DHCP_lease(); + } + } + else + { + _secTimeout = snow + 1000; + } + + _lastCheck = now; + return rc; +} + +IPAddress DhcpClass::getLocalIp() +{ + return IPAddress(_dhcpLocalIp); +} + +IPAddress DhcpClass::getSubnetMask() +{ + return IPAddress(_dhcpSubnetMask); +} + +IPAddress DhcpClass::getGatewayIp() +{ + return IPAddress(_dhcpGatewayIp); +} + +IPAddress DhcpClass::getDhcpServerIp() +{ + return IPAddress(_dhcpDhcpServerIp); +} + +IPAddress DhcpClass::getDnsServerIp() +{ + return IPAddress(_dhcpDnsServerIp); +} + +void DhcpClass::printByte(char * buf, uint8_t n) +{ + char *str = &buf[1]; + buf[0] = '0'; + do + { + unsigned long m = n; + n /= 16; + char c = m - 16 * n; + *str-- = c < 10 ? c + '0' : c + 'A' - 10; + } while (n); +} diff --git a/libraries/Ethernet/src/Dhcp.h b/libraries/Ethernet/src/Dhcp.h index 1a533ef000..58765cba55 100644 --- a/libraries/Ethernet/src/Dhcp.h +++ b/libraries/Ethernet/src/Dhcp.h @@ -1,178 +1,179 @@ -// DHCP Library v0.3 - April 25, 2009 -// Author: Jordan Terrell - blog.jordanterrell.com - -#ifndef Dhcp_h -#define Dhcp_h - -#include "EthernetUdp.h" - -/* DHCP state machine. */ -#define STATE_DHCP_START 0 -#define STATE_DHCP_DISCOVER 1 -#define STATE_DHCP_REQUEST 2 -#define STATE_DHCP_LEASED 3 -#define STATE_DHCP_REREQUEST 4 -#define STATE_DHCP_RELEASE 5 - -#define DHCP_FLAGSBROADCAST 0x8000 - -/* UDP port numbers for DHCP */ -#define DHCP_SERVER_PORT 67 /* from server to client */ -#define DHCP_CLIENT_PORT 68 /* from client to server */ - -/* DHCP message OP code */ -#define DHCP_BOOTREQUEST 1 -#define DHCP_BOOTREPLY 2 - -/* DHCP message type */ -#define DHCP_DISCOVER 1 -#define DHCP_OFFER 2 -#define DHCP_REQUEST 3 -#define DHCP_DECLINE 4 -#define DHCP_ACK 5 -#define DHCP_NAK 6 -#define DHCP_RELEASE 7 -#define DHCP_INFORM 8 - -#define DHCP_HTYPE10MB 1 -#define DHCP_HTYPE100MB 2 - -#define DHCP_HLENETHERNET 6 -#define DHCP_HOPS 0 -#define DHCP_SECS 0 - -#define MAGIC_COOKIE 0x63825363 -#define MAX_DHCP_OPT 16 - -#define HOST_NAME "WIZnet" -#define DEFAULT_LEASE (900) //default lease time in seconds - -#define DHCP_CHECK_NONE (0) -#define DHCP_CHECK_RENEW_FAIL (1) -#define DHCP_CHECK_RENEW_OK (2) -#define DHCP_CHECK_REBIND_FAIL (3) -#define DHCP_CHECK_REBIND_OK (4) - -enum -{ - padOption = 0, - subnetMask = 1, - timerOffset = 2, - routersOnSubnet = 3, - /* timeServer = 4, - nameServer = 5,*/ - dns = 6, - /*logServer = 7, - cookieServer = 8, - lprServer = 9, - impressServer = 10, - resourceLocationServer = 11,*/ - hostName = 12, - /*bootFileSize = 13, - meritDumpFile = 14,*/ - domainName = 15, - /*swapServer = 16, - rootPath = 17, - extentionsPath = 18, - IPforwarding = 19, - nonLocalSourceRouting = 20, - policyFilter = 21, - maxDgramReasmSize = 22, - defaultIPTTL = 23, - pathMTUagingTimeout = 24, - pathMTUplateauTable = 25, - ifMTU = 26, - allSubnetsLocal = 27, - broadcastAddr = 28, - performMaskDiscovery = 29, - maskSupplier = 30, - performRouterDiscovery = 31, - routerSolicitationAddr = 32, - staticRoute = 33, - trailerEncapsulation = 34, - arpCacheTimeout = 35, - ethernetEncapsulation = 36, - tcpDefaultTTL = 37, - tcpKeepaliveInterval = 38, - tcpKeepaliveGarbage = 39, - nisDomainName = 40, - nisServers = 41, - ntpServers = 42, - vendorSpecificInfo = 43, - netBIOSnameServer = 44, - netBIOSdgramDistServer = 45, - netBIOSnodeType = 46, - netBIOSscope = 47, - xFontServer = 48, - xDisplayManager = 49,*/ - dhcpRequestedIPaddr = 50, - dhcpIPaddrLeaseTime = 51, - /*dhcpOptionOverload = 52,*/ - dhcpMessageType = 53, - dhcpServerIdentifier = 54, - dhcpParamRequest = 55, - /*dhcpMsg = 56, - dhcpMaxMsgSize = 57,*/ - dhcpT1value = 58, - dhcpT2value = 59, - /*dhcpClassIdentifier = 60,*/ - dhcpClientIdentifier = 61, - endOption = 255 -}; - -typedef struct __attribute__((packed)) _RIP_MSG_FIXED -{ - uint8_t op; - uint8_t htype; - uint8_t hlen; - uint8_t hops; - uint32_t xid; - uint16_t secs; - uint16_t flags; - uint8_t ciaddr[4]; - uint8_t yiaddr[4]; - uint8_t siaddr[4]; - uint8_t giaddr[4]; - uint8_t chaddr[6]; -}RIP_MSG_FIXED; - -class DhcpClass { -private: - uint32_t _dhcpInitialTransactionId; - uint32_t _dhcpTransactionId; - uint8_t _dhcpMacAddr[6]; - uint8_t _dhcpLocalIp[4]; - uint8_t _dhcpSubnetMask[4]; - uint8_t _dhcpGatewayIp[4]; - uint8_t _dhcpDhcpServerIp[4]; - uint8_t _dhcpDnsServerIp[4]; - uint32_t _dhcpLeaseTime; - uint32_t _dhcpT1, _dhcpT2; - signed long _renewInSec; - signed long _rebindInSec; - signed long _lastCheck; - unsigned long _timeout; - unsigned long _responseTimeout; - unsigned long _secTimeout; - uint8_t _dhcp_state; - EthernetUDP _dhcpUdpSocket; - - int request_DHCP_lease(); - void reset_DHCP_lease(); - void presend_DHCP(); - void send_DHCP_MESSAGE(uint8_t, uint16_t); - void printByte(char *, uint8_t); - - uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId); -public: - IPAddress getLocalIp(); - IPAddress getSubnetMask(); - IPAddress getGatewayIp(); - IPAddress getDhcpServerIp(); - IPAddress getDnsServerIp(); - - int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); - int checkLease(); -}; - -#endif +// DHCP Library v0.3 - April 25, 2009 +// Author: Jordan Terrell - blog.jordanterrell.com + +#ifndef Dhcp_h +#define Dhcp_h + +#include "EthernetUdp.h" + +/* DHCP state machine. */ +#define STATE_DHCP_START 0 +#define STATE_DHCP_DISCOVER 1 +#define STATE_DHCP_REQUEST 2 +#define STATE_DHCP_LEASED 3 +#define STATE_DHCP_REREQUEST 4 +#define STATE_DHCP_RELEASE 5 + +#define DHCP_FLAGSBROADCAST 0x8000 + +/* UDP port numbers for DHCP */ +#define DHCP_SERVER_PORT 67 /* from server to client */ +#define DHCP_CLIENT_PORT 68 /* from client to server */ + +/* DHCP message OP code */ +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/* DHCP message type */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +#define DHCP_HTYPE10MB 1 +#define DHCP_HTYPE100MB 2 + +#define DHCP_HLENETHERNET 6 +#define DHCP_HOPS 0 +#define DHCP_SECS 0 + +#define MAGIC_COOKIE 0x63825363 +#define MAX_DHCP_OPT 16 + +#define HOST_NAME "WIZnet" +#define DEFAULT_LEASE (900) //default lease time in seconds + +#define DHCP_CHECK_NONE (0) +#define DHCP_CHECK_RENEW_FAIL (1) +#define DHCP_CHECK_RENEW_OK (2) +#define DHCP_CHECK_REBIND_FAIL (3) +#define DHCP_CHECK_REBIND_OK (4) + +enum +{ + padOption = 0, + subnetMask = 1, + timerOffset = 2, + routersOnSubnet = 3, + /* timeServer = 4, + nameServer = 5,*/ + dns = 6, + /* logServer = 7, + cookieServer = 8, + lprServer = 9, + impressServer = 10, + resourceLocationServer = 11,*/ + hostName = 12, + /* bootFileSize = 13, + meritDumpFile = 14,*/ + domainName = 15, + /* swapServer = 16, + rootPath = 17, + extentionsPath = 18, + IPforwarding = 19, + nonLocalSourceRouting = 20, + policyFilter = 21, + maxDgramReasmSize = 22, + defaultIPTTL = 23, + pathMTUagingTimeout = 24, + pathMTUplateauTable = 25, + ifMTU = 26, + allSubnetsLocal = 27, + broadcastAddr = 28, + performMaskDiscovery = 29, + maskSupplier = 30, + performRouterDiscovery = 31, + routerSolicitationAddr = 32, + staticRoute = 33, + trailerEncapsulation = 34, + arpCacheTimeout = 35, + ethernetEncapsulation = 36, + tcpDefaultTTL = 37, + tcpKeepaliveInterval = 38, + tcpKeepaliveGarbage = 39, + nisDomainName = 40, + nisServers = 41, + ntpServers = 42, + vendorSpecificInfo = 43, + netBIOSnameServer = 44, + netBIOSdgramDistServer = 45, + netBIOSnodeType = 46, + netBIOSscope = 47, + xFontServer = 48, + xDisplayManager = 49,*/ + dhcpRequestedIPaddr = 50, + dhcpIPaddrLeaseTime = 51, + /*dhcpOptionOverload = 52,*/ + dhcpMessageType = 53, + dhcpServerIdentifier = 54, + dhcpParamRequest = 55, + /* dhcpMsg = 56, + dhcpMaxMsgSize = 57,*/ + dhcpT1value = 58, + dhcpT2value = 59, + /*dhcpClassIdentifier = 60,*/ + dhcpClientIdentifier = 61, + endOption = 255 +}; + +typedef struct __attribute__((packed)) _RIP_MSG_FIXED +{ + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint8_t ciaddr[4]; + uint8_t yiaddr[4]; + uint8_t siaddr[4]; + uint8_t giaddr[4]; + uint8_t chaddr[6]; +} RIP_MSG_FIXED; + +class DhcpClass +{ +private: + uint32_t _dhcpInitialTransactionId; + uint32_t _dhcpTransactionId; + uint8_t _dhcpMacAddr[6]; + uint8_t _dhcpLocalIp[4]; + uint8_t _dhcpSubnetMask[4]; + uint8_t _dhcpGatewayIp[4]; + uint8_t _dhcpDhcpServerIp[4]; + uint8_t _dhcpDnsServerIp[4]; + uint32_t _dhcpLeaseTime; + uint32_t _dhcpT1, _dhcpT2; + signed long _renewInSec; + signed long _rebindInSec; + signed long _lastCheck; + unsigned long _timeout; + unsigned long _responseTimeout; + unsigned long _secTimeout; + uint8_t _dhcp_state; + EthernetUDP _dhcpUdpSocket; + + int request_DHCP_lease(); + void reset_DHCP_lease(); + void presend_DHCP(); + void send_DHCP_MESSAGE(uint8_t, uint16_t); + void printByte(char *, uint8_t); + + uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId); +public: + IPAddress getLocalIp(); + IPAddress getSubnetMask(); + IPAddress getGatewayIp(); + IPAddress getDhcpServerIp(); + IPAddress getDnsServerIp(); + + int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + int checkLease(); +}; + +#endif diff --git a/libraries/Ethernet/src/Dns.cpp b/libraries/Ethernet/src/Dns.cpp index ad4e167f25..98e2b3054e 100644 --- a/libraries/Ethernet/src/Dns.cpp +++ b/libraries/Ethernet/src/Dns.cpp @@ -58,9 +58,9 @@ void DNSClient::begin(const IPAddress& aDNSServer) int DNSClient::inet_aton_ethlib(const char* aIPAddrString, IPAddress& aResult) { // See if we've been given a valid IP address - const char* p =aIPAddrString; + const char* p = aIPAddrString; while (*p && - ( (*p == '.') || (*p >= '0') || (*p <= '9') )) + ((*p == '.') || (*p >= '0') || (*p <= '9'))) { p++; } @@ -69,8 +69,8 @@ int DNSClient::inet_aton_ethlib(const char* aIPAddrString, IPAddress& aResult) { // It's looking promising, we haven't found any invalid characters p = aIPAddrString; - int segment =0; - int segmentValue =0; + int segment = 0; + int segmentValue = 0; while (*p && (segment < 4)) { if (*p == '.') @@ -91,7 +91,7 @@ int DNSClient::inet_aton_ethlib(const char* aIPAddrString, IPAddress& aResult) else { // Next digit - segmentValue = (segmentValue*10)+(*p - '0'); + segmentValue = (segmentValue * 10) + (*p - '0'); } p++; } @@ -117,7 +117,7 @@ int DNSClient::inet_aton_ethlib(const char* aIPAddrString, IPAddress& aResult) int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) { - int ret =0; + int ret = 0; // See if it's a numeric IP address if (inet_aton_ethlib(aHostname, aResult)) @@ -131,13 +131,13 @@ int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) { return INVALID_SERVER; } - + // Find a socket to use - if (iUdp.begin(1024+(millis() & 0xF)) == 1) + if (iUdp.begin(1024 + (millis() & 0xF)) == 1) { // Try up to three times int retries = 0; -// while ((retries < 3) && (ret <= 0)) + // while ((retries < 3) && (ret <= 0)) { // Send DNS request ret = iUdp.beginPacket(iDNSServer, DNS_PORT); @@ -213,28 +213,28 @@ uint16_t DNSClient::BuildRequest(const char* aName) iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); // Build question - const char* start =aName; - const char* end =start; + const char* start = aName; + const char* end = start; uint8_t len; // Run through the name being requested while (*end) { // Find out how long this section of the name is end = start; - while (*end && (*end != '.') ) + while (*end && (*end != '.')) { end++; } - if (end-start > 0) + if (end - start > 0) { // Write out the size of this section - len = end-start; + len = end - start; iUdp.write(&len, sizeof(len)); // And then write out the section - iUdp.write((uint8_t*)start, end-start); + iUdp.write((uint8_t*)start, end - start); } - start = end+1; + start = end + 1; } // We've got to the end of the question name, so @@ -257,10 +257,12 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) uint32_t startTime = millis(); // Wait for a response packet - while(iUdp.parsePacket() <= 0) + while (iUdp.parsePacket() <= 0) { - if((millis() - startTime) > aTimeout) + if ((millis() - startTime) > aTimeout) + { return TIMED_OUT; + } delay(50); } @@ -268,8 +270,8 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) // Read the UDP header uint8_t header[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header // Check that it's a response from the right server and the right port - if ( (iDNSServer != iUdp.remoteIP()) || - (iUdp.remotePort() != DNS_PORT) ) + if ((iDNSServer != iUdp.remoteIP()) || + (iUdp.remotePort() != DNS_PORT)) { // It's not from who we expected return INVALID_SERVER; @@ -287,8 +289,8 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) uint16_t header_flags = htons(staging); memcpy(&staging, &header[0], sizeof(uint16_t)); // Check that it's a response to this request - if ( ( iRequestId != staging ) || - ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t)RESPONSE_FLAG) ) + if ((iRequestId != staging) || + ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t)RESPONSE_FLAG)) { // Mark the entire packet as read iUdp.flush(); @@ -296,7 +298,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) } // Check for any errors in the response (or in our request) // although we don't do anything to get round these - if ( (header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK) ) + if ((header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK)) { // Mark the entire packet as read iUdp.flush(); @@ -306,7 +308,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) // And make sure we've got (at least) one answer memcpy(&staging, &header[6], sizeof(uint16_t)); uint16_t answerCount = htons(staging); - if (answerCount == 0 ) + if (answerCount == 0) { // Mark the entire packet as read iUdp.flush(); @@ -315,7 +317,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) // Skip over any questions memcpy(&staging, &header[4], sizeof(uint16_t)); - for (uint16_t i =0; i < htons(staging); i++) + for (uint16_t i = 0; i < htons(staging); i++) { // Skip over the name uint8_t len; @@ -326,7 +328,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) { // Don't need to actually read the data out for the string, just // advance ptr to beyond it - while(len--) + while (len--) { iUdp.read(); // we don't care about the returned byte } @@ -334,7 +336,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) } while (len != 0); // Now jump over the type and class - for (int i =0; i < 4; i++) + for (int i = 0; i < 4; i++) { iUdp.read(); // we don't care about the returned byte } @@ -345,7 +347,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) // type A answer) and some authority and additional resource records but // we're going to ignore all of them. - for (uint16_t i =0; i < answerCount; i++) + for (uint16_t i = 0; i < answerCount; i++) { // Skip the name uint8_t len; @@ -360,7 +362,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) // And it's got a length // Don't need to actually read the data out for the string, // just advance ptr to beyond it - while(len--) + while (len--) { iUdp.read(); // we don't care about the returned byte } @@ -388,7 +390,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) iUdp.read((uint8_t*)&answerClass, sizeof(answerClass)); // Ignore the Time-To-Live as we don't do any caching - for (int i =0; i < TTL_SIZE; i++) + for (int i = 0; i < TTL_SIZE; i++) { iUdp.read(); // we don't care about the returned byte } @@ -397,7 +399,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) // Don't need header_flags anymore, so we can reuse it here iUdp.read((uint8_t*)&header_flags, sizeof(header_flags)); - if ( (htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN) ) + if ((htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN)) { if (htons(header_flags) != 4) { @@ -412,7 +414,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) else { // This isn't an answer type we're after, move onto the next one - for (uint16_t i =0; i < htons(header_flags); i++) + for (uint16_t i = 0; i < htons(header_flags); i++) { iUdp.read(); // we don't care about the returned byte } diff --git a/libraries/Ethernet/src/Dns.h b/libraries/Ethernet/src/Dns.h index aa7212f703..b75b3f1339 100644 --- a/libraries/Ethernet/src/Dns.h +++ b/libraries/Ethernet/src/Dns.h @@ -1,41 +1,41 @@ -// Arduino DNS client for WizNet5100-based Ethernet shield -// (c) Copyright 2009-2010 MCQN Ltd. -// Released under Apache License, version 2.0 - -#ifndef DNSClient_h -#define DNSClient_h - -#include - -class DNSClient -{ -public: - // ctor - void begin(const IPAddress& aDNSServer); - - /** Convert a numeric IP address string into a four-byte IP address. - @param aIPAddrString IP address to convert - @param aResult IPAddress structure to store the returned IP address - @result 1 if aIPAddrString was successfully converted to an IP address, - else error code - */ - int inet_aton_ethlib(const char *aIPAddrString, IPAddress& aResult); - - /** Resolve the given hostname to an IP address. - @param aHostname Name to be resolved - @param aResult IPAddress structure to store the returned IP address - @result 1 if aIPAddrString was successfully converted to an IP address, - else error code - */ - int getHostByName(const char* aHostname, IPAddress& aResult); - -protected: - uint16_t BuildRequest(const char* aName); - uint16_t ProcessResponse(uint16_t aTimeout, IPAddress& aAddress); - - IPAddress iDNSServer; - uint16_t iRequestId; - EthernetUDP iUdp; -}; - -#endif +// Arduino DNS client for WizNet5100-based Ethernet shield +// (c) Copyright 2009-2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#ifndef DNSClient_h +#define DNSClient_h + +#include + +class DNSClient +{ +public: + // ctor + void begin(const IPAddress& aDNSServer); + + /** Convert a numeric IP address string into a four-byte IP address. + @param aIPAddrString IP address to convert + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, + else error code + */ + int inet_aton_ethlib(const char *aIPAddrString, IPAddress& aResult); + + /** Resolve the given hostname to an IP address. + @param aHostname Name to be resolved + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, + else error code + */ + int getHostByName(const char* aHostname, IPAddress& aResult); + +protected: + uint16_t BuildRequest(const char* aName); + uint16_t ProcessResponse(uint16_t aTimeout, IPAddress& aAddress); + + IPAddress iDNSServer; + uint16_t iRequestId; + EthernetUDP iUdp; +}; + +#endif diff --git a/libraries/Ethernet/src/Ethernet.cpp b/libraries/Ethernet/src/Ethernet.cpp index 157d361a10..76252ea932 100644 --- a/libraries/Ethernet/src/Ethernet.cpp +++ b/libraries/Ethernet/src/Ethernet.cpp @@ -3,10 +3,14 @@ #include "Dhcp.h" // XXX: don't make assumptions about the value of MAX_SOCK_NUM. -uint8_t EthernetClass::_state[MAX_SOCK_NUM] = { - 0, 0, 0, 0 }; -uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = { - 0, 0, 0, 0 }; +uint8_t EthernetClass::_state[MAX_SOCK_NUM] = +{ + 0, 0, 0, 0 +}; +uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = +{ + 0, 0, 0, 0 +}; #ifdef ESP8266 static DhcpClass s_dhcp; @@ -15,128 +19,131 @@ static DhcpClass s_dhcp; int EthernetClass::begin(uint8_t *mac_address) { #ifndef ESP8266 - static DhcpClass s_dhcp; + static DhcpClass s_dhcp; #endif - _dhcp = &s_dhcp; - - - // Initialise the basic info - W5100.init(); - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.setMACAddress(mac_address); - W5100.setIPAddress(IPAddress(0,0,0,0).raw_address()); - SPI.endTransaction(); - - // Now try to get our config info from a DHCP server - int ret = _dhcp->beginWithDHCP(mac_address); - if(ret == 1) - { - // We've successfully found a DHCP server and got our configuration info, so set things - // accordingly + _dhcp = &s_dhcp; + + + // Initialise the basic info + W5100.init(); SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); - W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); - W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + W5100.setMACAddress(mac_address); + W5100.setIPAddress(IPAddress(0, 0, 0, 0).raw_address()); SPI.endTransaction(); - _dnsServerAddress = _dhcp->getDnsServerIp(); - } - return ret; + // Now try to get our config info from a DHCP server + int ret = _dhcp->beginWithDHCP(mac_address); + if (ret == 1) + { + // We've successfully found a DHCP server and got our configuration info, so set things + // accordingly + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); + W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + SPI.endTransaction(); + _dnsServerAddress = _dhcp->getDnsServerIp(); + } + + return ret; } void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip) { - // Assume the DNS server will be the machine on the same network as the local IP - // but with last octet being '1' - IPAddress dns_server = local_ip; - dns_server[3] = 1; - begin(mac_address, local_ip, dns_server); + // Assume the DNS server will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress dns_server = local_ip; + dns_server[3] = 1; + begin(mac_address, local_ip, dns_server); } void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server) { - // Assume the gateway will be the machine on the same network as the local IP - // but with last octet being '1' - IPAddress gateway = local_ip; - gateway[3] = 1; - begin(mac_address, local_ip, dns_server, gateway); + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = local_ip; + gateway[3] = 1; + begin(mac_address, local_ip, dns_server, gateway); } void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway) { - IPAddress subnet(255, 255, 255, 0); - begin(mac_address, local_ip, dns_server, gateway, subnet); + IPAddress subnet(255, 255, 255, 0); + begin(mac_address, local_ip, dns_server, gateway, subnet); } void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) { - W5100.init(); - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.setMACAddress(mac); - W5100.setIPAddress(local_ip.raw_address()); - W5100.setGatewayIp(gateway.raw_address()); - W5100.setSubnetMask(subnet.raw_address()); - SPI.endTransaction(); - _dnsServerAddress = dns_server; + W5100.init(); + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setMACAddress(mac); + W5100.setIPAddress(local_ip.raw_address()); + W5100.setGatewayIp(gateway.raw_address()); + W5100.setSubnetMask(subnet.raw_address()); + SPI.endTransaction(); + _dnsServerAddress = dns_server; } -int EthernetClass::maintain(){ - int rc = DHCP_CHECK_NONE; - if(_dhcp != NULL){ - //we have a pointer to dhcp, use it - rc = _dhcp->checkLease(); - switch ( rc ){ - case DHCP_CHECK_NONE: - //nothing done - break; - case DHCP_CHECK_RENEW_OK: - case DHCP_CHECK_REBIND_OK: - //we might have got a new IP. - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); - W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); - W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); - SPI.endTransaction(); - _dnsServerAddress = _dhcp->getDnsServerIp(); - break; - default: - //this is actually a error, it will retry though - break; +int EthernetClass::maintain() +{ + int rc = DHCP_CHECK_NONE; + if (_dhcp != NULL) + { + //we have a pointer to dhcp, use it + rc = _dhcp->checkLease(); + switch (rc) + { + case DHCP_CHECK_NONE: + //nothing done + break; + case DHCP_CHECK_RENEW_OK: + case DHCP_CHECK_REBIND_OK: + //we might have got a new IP. + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); + W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + SPI.endTransaction(); + _dnsServerAddress = _dhcp->getDnsServerIp(); + break; + default: + //this is actually a error, it will retry though + break; + } } - } - return rc; + return rc; } IPAddress EthernetClass::localIP() { - IPAddress ret; - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.getIPAddress(ret.raw_address()); - SPI.endTransaction(); - return ret; + IPAddress ret; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getIPAddress(ret.raw_address()); + SPI.endTransaction(); + return ret; } IPAddress EthernetClass::subnetMask() { - IPAddress ret; - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.getSubnetMask(ret.raw_address()); - SPI.endTransaction(); - return ret; + IPAddress ret; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getSubnetMask(ret.raw_address()); + SPI.endTransaction(); + return ret; } IPAddress EthernetClass::gatewayIP() { - IPAddress ret; - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.getGatewayIp(ret.raw_address()); - SPI.endTransaction(); - return ret; + IPAddress ret; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.getGatewayIp(ret.raw_address()); + SPI.endTransaction(); + return ret; } IPAddress EthernetClass::dnsServerIP() { - return _dnsServerAddress; + return _dnsServerAddress; } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ETHERNET) diff --git a/libraries/Ethernet/src/Ethernet.h b/libraries/Ethernet/src/Ethernet.h index 806a9ddd9c..4eb3f6e626 100644 --- a/libraries/Ethernet/src/Ethernet.h +++ b/libraries/Ethernet/src/Ethernet.h @@ -10,30 +10,31 @@ #define MAX_SOCK_NUM 4 -class EthernetClass { +class EthernetClass +{ private: - IPAddress _dnsServerAddress; - DhcpClass* _dhcp; + IPAddress _dnsServerAddress; + DhcpClass* _dhcp; public: - static uint8_t _state[MAX_SOCK_NUM]; - static uint16_t _server_port[MAX_SOCK_NUM]; - // Initialise the Ethernet shield to use the provided MAC address and gain the rest of the - // configuration through DHCP. - // Returns 0 if the DHCP configuration failed, and 1 if it succeeded - int begin(uint8_t *mac_address); - void begin(uint8_t *mac_address, IPAddress local_ip); - void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server); - void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); - void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); - int maintain(); + static uint8_t _state[MAX_SOCK_NUM]; + static uint16_t _server_port[MAX_SOCK_NUM]; + // Initialise the Ethernet shield to use the provided MAC address and gain the rest of the + // configuration through DHCP. + // Returns 0 if the DHCP configuration failed, and 1 if it succeeded + int begin(uint8_t *mac_address); + void begin(uint8_t *mac_address, IPAddress local_ip); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); + int maintain(); - IPAddress localIP(); - IPAddress subnetMask(); - IPAddress gatewayIP(); - IPAddress dnsServerIP(); + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP(); - friend class EthernetClient; - friend class EthernetServer; + friend class EthernetClient; + friend class EthernetServer; }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ETHERNET) diff --git a/libraries/Ethernet/src/EthernetClient.cpp b/libraries/Ethernet/src/EthernetClient.cpp index 942c4fca83..b4d837d3ff 100644 --- a/libraries/Ethernet/src/EthernetClient.cpp +++ b/libraries/Ethernet/src/EthernetClient.cpp @@ -2,7 +2,7 @@ #include "utility/socket.h" extern "C" { - #include "string.h" +#include "string.h" } #include "Arduino.h" @@ -14,161 +14,210 @@ extern "C" { uint16_t EthernetClient::_srcport = 49152; //Use IANA recommended ephemeral port range 49152-65535 -EthernetClient::EthernetClient() : _sock(MAX_SOCK_NUM) { +EthernetClient::EthernetClient() : _sock(MAX_SOCK_NUM) +{ } -EthernetClient::EthernetClient(uint8_t sock) : _sock(sock) { +EthernetClient::EthernetClient(uint8_t sock) : _sock(sock) +{ } -int EthernetClient::connect(const char* host, uint16_t port) { - // Look up the host first - int ret = 0; - DNSClient dns; - IPAddress remote_addr; +int EthernetClient::connect(const char* host, uint16_t port) +{ + // Look up the host first + int ret = 0; + DNSClient dns; + IPAddress remote_addr; - dns.begin(Ethernet.dnsServerIP()); - ret = dns.getHostByName(host, remote_addr); - if (ret == 1) { - return connect(remote_addr, port); - } else { - return ret; - } + dns.begin(Ethernet.dnsServerIP()); + ret = dns.getHostByName(host, remote_addr); + if (ret == 1) + { + return connect(remote_addr, port); + } + else + { + return ret; + } } -int EthernetClient::connect(IPAddress ip, uint16_t port) { - if (_sock != MAX_SOCK_NUM) - return 0; +int EthernetClient::connect(IPAddress ip, uint16_t port) +{ + if (_sock != MAX_SOCK_NUM) + { + return 0; + } - for (int i = 0; i < MAX_SOCK_NUM; i++) { - uint8_t s = socketStatus(i); - if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT || s == SnSR::CLOSE_WAIT) { - _sock = i; - break; + for (int i = 0; i < MAX_SOCK_NUM; i++) + { + uint8_t s = socketStatus(i); + if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT || s == SnSR::CLOSE_WAIT) + { + _sock = i; + break; + } } - } - if (_sock == MAX_SOCK_NUM) - return 0; + if (_sock == MAX_SOCK_NUM) + { + return 0; + } - _srcport++; - if (_srcport == 0) _srcport = 49152; //Use IANA recommended ephemeral port range 49152-65535 - socket(_sock, SnMR::TCP, _srcport, 0); + _srcport++; + if (_srcport == 0) + { + _srcport = 49152; //Use IANA recommended ephemeral port range 49152-65535 + } + socket(_sock, SnMR::TCP, _srcport, 0); - if (!::connect(_sock, rawIPAddress(ip), port)) { - _sock = MAX_SOCK_NUM; - return 0; - } + if (!::connect(_sock, rawIPAddress(ip), port)) + { + _sock = MAX_SOCK_NUM; + return 0; + } - while (status() != SnSR::ESTABLISHED) { - delay(1); - if (status() == SnSR::CLOSED) { - _sock = MAX_SOCK_NUM; - return 0; + while (status() != SnSR::ESTABLISHED) + { + delay(1); + if (status() == SnSR::CLOSED) + { + _sock = MAX_SOCK_NUM; + return 0; + } } - } - return 1; + return 1; } -size_t EthernetClient::write(uint8_t b) { - return write(&b, 1); +size_t EthernetClient::write(uint8_t b) +{ + return write(&b, 1); } -size_t EthernetClient::write(const uint8_t *buf, size_t size) { - if (_sock == MAX_SOCK_NUM) { - setWriteError(); - return 0; - } - if (!send(_sock, buf, size)) { - setWriteError(); - return 0; - } - return size; +size_t EthernetClient::write(const uint8_t *buf, size_t size) +{ + if (_sock == MAX_SOCK_NUM) + { + setWriteError(); + return 0; + } + if (!send(_sock, buf, size)) + { + setWriteError(); + return 0; + } + return size; } -int EthernetClient::available() { - if (_sock != MAX_SOCK_NUM) - return recvAvailable(_sock); - return 0; +int EthernetClient::available() +{ + if (_sock != MAX_SOCK_NUM) + { + return recvAvailable(_sock); + } + return 0; } -int EthernetClient::read() { - uint8_t b; - if ( recv(_sock, &b, 1) > 0 ) - { - // recv worked - return b; - } - else - { - // No data available - return -1; - } +int EthernetClient::read() +{ + uint8_t b; + if (recv(_sock, &b, 1) > 0) + { + // recv worked + return b; + } + else + { + // No data available + return -1; + } } -int EthernetClient::read(uint8_t *buf, size_t size) { - return recv(_sock, buf, size); +int EthernetClient::read(uint8_t *buf, size_t size) +{ + return recv(_sock, buf, size); } -int EthernetClient::peek() { - uint8_t b; - // Unlike recv, peek doesn't check to see if there's any data available, so we must - if (!available()) - return -1; - ::peek(_sock, &b); - return b; +int EthernetClient::peek() +{ + uint8_t b; + // Unlike recv, peek doesn't check to see if there's any data available, so we must + if (!available()) + { + return -1; + } + ::peek(_sock, &b); + return b; } -void EthernetClient::flush() { - ::flush(_sock); +void EthernetClient::flush() +{ + ::flush(_sock); } -void EthernetClient::stop() { - if (_sock == MAX_SOCK_NUM) - return; - - // attempt to close the connection gracefully (send a FIN to other side) - disconnect(_sock); - unsigned long start = millis(); - - // wait up to a second for the connection to close - uint8_t s; - do { - s = status(); - if (s == SnSR::CLOSED) - break; // exit the loop - delay(1); - } while (millis() - start < 1000); +void EthernetClient::stop() +{ + if (_sock == MAX_SOCK_NUM) + { + return; + } - // if it hasn't closed, close it forcefully - if (s != SnSR::CLOSED) { - close(_sock); - } + // attempt to close the connection gracefully (send a FIN to other side) + disconnect(_sock); + unsigned long start = millis(); + + // wait up to a second for the connection to close + uint8_t s; + do + { + s = status(); + if (s == SnSR::CLOSED) + { + break; // exit the loop + } + delay(1); + } while (millis() - start < 1000); + + // if it hasn't closed, close it forcefully + if (s != SnSR::CLOSED) + { + close(_sock); + } - EthernetClass::_server_port[_sock] = 0; - _sock = MAX_SOCK_NUM; + EthernetClass::_server_port[_sock] = 0; + _sock = MAX_SOCK_NUM; } -uint8_t EthernetClient::connected() { - if (_sock == MAX_SOCK_NUM) return 0; - - uint8_t s = status(); - return !(s == SnSR::LISTEN || s == SnSR::CLOSED || s == SnSR::FIN_WAIT || - (s == SnSR::CLOSE_WAIT && !available())); +uint8_t EthernetClient::connected() +{ + if (_sock == MAX_SOCK_NUM) + { + return 0; + } + + uint8_t s = status(); + return !(s == SnSR::LISTEN || s == SnSR::CLOSED || s == SnSR::FIN_WAIT || + (s == SnSR::CLOSE_WAIT && !available())); } -uint8_t EthernetClient::status() { - if (_sock == MAX_SOCK_NUM) return SnSR::CLOSED; - return socketStatus(_sock); +uint8_t EthernetClient::status() +{ + if (_sock == MAX_SOCK_NUM) + { + return SnSR::CLOSED; + } + return socketStatus(_sock); } // the next function allows us to use the client returned by // EthernetServer::available() as the condition in an if-statement. -EthernetClient::operator bool() { - return _sock != MAX_SOCK_NUM; +EthernetClient::operator bool() +{ + return _sock != MAX_SOCK_NUM; } -bool EthernetClient::operator==(const EthernetClient& rhs) { - return _sock == rhs._sock && _sock != MAX_SOCK_NUM && rhs._sock != MAX_SOCK_NUM; +bool EthernetClient::operator==(const EthernetClient& rhs) +{ + return _sock == rhs._sock && _sock != MAX_SOCK_NUM && rhs._sock != MAX_SOCK_NUM; } diff --git a/libraries/Ethernet/src/EthernetClient.h b/libraries/Ethernet/src/EthernetClient.h index 16e2500bc3..c071450d12 100644 --- a/libraries/Ethernet/src/EthernetClient.h +++ b/libraries/Ethernet/src/EthernetClient.h @@ -1,41 +1,51 @@ #ifndef ethernetclient_h #define ethernetclient_h -#include "Arduino.h" +#include "Arduino.h" #include "Print.h" #include "Client.h" #include "IPAddress.h" -class EthernetClient : public Client { +class EthernetClient : public Client +{ public: - EthernetClient(); - EthernetClient(uint8_t sock); + EthernetClient(); + EthernetClient(uint8_t sock); - uint8_t status(); - virtual int connect(IPAddress ip, uint16_t port); - virtual int connect(const char *host, uint16_t port); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - virtual int available(); - virtual int read(); - virtual int read(uint8_t *buf, size_t size); - virtual int peek(); - virtual void flush(); - virtual void stop(); - virtual uint8_t connected(); - virtual operator bool(); - virtual bool operator==(const bool value) { return bool() == value; } - virtual bool operator!=(const bool value) { return bool() != value; } - virtual bool operator==(const EthernetClient&); - virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; + uint8_t status(); + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, uint16_t port); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual int available(); + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual int peek(); + virtual void flush(); + virtual void stop(); + virtual uint8_t connected(); + virtual operator bool(); + virtual bool operator==(const bool value) + { + return bool() == value; + } + virtual bool operator!=(const bool value) + { + return bool() != value; + } + virtual bool operator==(const EthernetClient&); + virtual bool operator!=(const EthernetClient& rhs) + { + return !this->operator==(rhs); + }; - friend class EthernetServer; - - using Print::write; + friend class EthernetServer; + + using Print::write; private: - static uint16_t _srcport; - uint8_t _sock; + static uint16_t _srcport; + uint8_t _sock; }; #endif diff --git a/libraries/Ethernet/src/EthernetServer.cpp b/libraries/Ethernet/src/EthernetServer.cpp index cfa813eb7b..72eb0f46de 100644 --- a/libraries/Ethernet/src/EthernetServer.cpp +++ b/libraries/Ethernet/src/EthernetServer.cpp @@ -10,83 +10,96 @@ extern "C" { EthernetServer::EthernetServer(uint16_t port) { - _port = port; + _port = port; } void EthernetServer::begin() { - for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { - EthernetClient client(sock); - if (client.status() == SnSR::CLOSED) { - socket(sock, SnMR::TCP, _port, 0); - listen(sock); - EthernetClass::_server_port[sock] = _port; - break; + for (int sock = 0; sock < MAX_SOCK_NUM; sock++) + { + EthernetClient client(sock); + if (client.status() == SnSR::CLOSED) + { + socket(sock, SnMR::TCP, _port, 0); + listen(sock); + EthernetClass::_server_port[sock] = _port; + break; + } } - } } void EthernetServer::accept() { - int listening = 0; - - for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { - EthernetClient client(sock); - - if (EthernetClass::_server_port[sock] == _port) { - if (client.status() == SnSR::LISTEN) { - listening = 1; - } - else if (client.status() == SnSR::CLOSE_WAIT && !client.available()) { - client.stop(); - } - } - } - - if (!listening) { - begin(); - } + int listening = 0; + + for (int sock = 0; sock < MAX_SOCK_NUM; sock++) + { + EthernetClient client(sock); + + if (EthernetClass::_server_port[sock] == _port) + { + if (client.status() == SnSR::LISTEN) + { + listening = 1; + } + else if (client.status() == SnSR::CLOSE_WAIT && !client.available()) + { + client.stop(); + } + } + } + + if (!listening) + { + begin(); + } } EthernetClient EthernetServer::available() { - accept(); - - for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { - EthernetClient client(sock); - if (EthernetClass::_server_port[sock] == _port) { - uint8_t s = client.status(); - if (s == SnSR::ESTABLISHED || s == SnSR::CLOSE_WAIT) { - if (client.available()) { - // XXX: don't always pick the lowest numbered socket. - return client; + accept(); + + for (int sock = 0; sock < MAX_SOCK_NUM; sock++) + { + EthernetClient client(sock); + if (EthernetClass::_server_port[sock] == _port) + { + uint8_t s = client.status(); + if (s == SnSR::ESTABLISHED || s == SnSR::CLOSE_WAIT) + { + if (client.available()) + { + // XXX: don't always pick the lowest numbered socket. + return client; + } + } } - } } - } - return EthernetClient(MAX_SOCK_NUM); + return EthernetClient(MAX_SOCK_NUM); } -size_t EthernetServer::write(uint8_t b) +size_t EthernetServer::write(uint8_t b) { - return write(&b, 1); + return write(&b, 1); } -size_t EthernetServer::write(const uint8_t *buffer, size_t size) +size_t EthernetServer::write(const uint8_t *buffer, size_t size) { - size_t n = 0; - - accept(); + size_t n = 0; + + accept(); - for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { - EthernetClient client(sock); + for (int sock = 0; sock < MAX_SOCK_NUM; sock++) + { + EthernetClient client(sock); - if (EthernetClass::_server_port[sock] == _port && - client.status() == SnSR::ESTABLISHED) { - n += client.write(buffer, size); + if (EthernetClass::_server_port[sock] == _port && + client.status() == SnSR::ESTABLISHED) + { + n += client.write(buffer, size); + } } - } - - return n; + + return n; } diff --git a/libraries/Ethernet/src/EthernetServer.h b/libraries/Ethernet/src/EthernetServer.h index 86ccafe969..76dc876bd6 100644 --- a/libraries/Ethernet/src/EthernetServer.h +++ b/libraries/Ethernet/src/EthernetServer.h @@ -5,18 +5,19 @@ class EthernetClient; -class EthernetServer : -public Server { +class EthernetServer : + public Server +{ private: - uint16_t _port; - void accept(); + uint16_t _port; + void accept(); public: - EthernetServer(uint16_t); - EthernetClient available(); - virtual void begin(); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - using Print::write; + EthernetServer(uint16_t); + EthernetClient available(); + virtual void begin(); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + using Print::write; }; #endif diff --git a/libraries/Ethernet/src/EthernetUdp.cpp b/libraries/Ethernet/src/EthernetUdp.cpp index b9a2c867af..487c164460 100644 --- a/libraries/Ethernet/src/EthernetUdp.cpp +++ b/libraries/Ethernet/src/EthernetUdp.cpp @@ -1,30 +1,30 @@ /* - * Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. - * This version only offers minimal wrapping of socket.c/socket.h - * Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ - * - * MIT License: - * Copyright (c) 2008 Bjoern Hartmann - * 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. - * - * bjoern@cs.stanford.edu 12/30/2008 - */ + Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. + This version only offers minimal wrapping of socket.c/socket.h + Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ + + MIT License: + Copyright (c) 2008 Bjoern Hartmann + 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. + + bjoern@cs.stanford.edu 12/30/2008 +*/ #include "utility/w5100.h" #include "utility/socket.h" @@ -36,188 +36,203 @@ EthernetUDP::EthernetUDP() : _sock(MAX_SOCK_NUM) {} /* Start EthernetUDP socket, listening at local port PORT */ -uint8_t EthernetUDP::begin(uint16_t port) { - if (_sock != MAX_SOCK_NUM) - return 0; +uint8_t EthernetUDP::begin(uint16_t port) +{ + if (_sock != MAX_SOCK_NUM) + { + return 0; + } - for (int i = 0; i < MAX_SOCK_NUM; i++) { - uint8_t s = socketStatus(i); - if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) { - _sock = i; - break; + for (int i = 0; i < MAX_SOCK_NUM; i++) + { + uint8_t s = socketStatus(i); + if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) + { + _sock = i; + break; + } } - } - if (_sock == MAX_SOCK_NUM) - return 0; + if (_sock == MAX_SOCK_NUM) + { + return 0; + } - _port = port; - _remaining = 0; - socket(_sock, SnMR::UDP, _port, 0); + _port = port; + _remaining = 0; + socket(_sock, SnMR::UDP, _port, 0); - return 1; + return 1; } -/* return number of bytes available in the current packet, - will return zero if parsePacket hasn't been called yet */ -int EthernetUDP::available() { - return _remaining; +/* return number of bytes available in the current packet, + will return zero if parsePacket hasn't been called yet */ +int EthernetUDP::available() +{ + return _remaining; } /* Release any resources being used by this EthernetUDP instance */ void EthernetUDP::stop() { - if (_sock == MAX_SOCK_NUM) - return; + if (_sock == MAX_SOCK_NUM) + { + return; + } - close(_sock); + close(_sock); - EthernetClass::_server_port[_sock] = 0; - _sock = MAX_SOCK_NUM; + EthernetClass::_server_port[_sock] = 0; + _sock = MAX_SOCK_NUM; } int EthernetUDP::beginPacket(const char *host, uint16_t port) { - // Look up the host first - int ret = 0; - DNSClient dns; - IPAddress remote_addr; - - dns.begin(Ethernet.dnsServerIP()); - ret = dns.getHostByName(host, remote_addr); - if (ret == 1) { - return beginPacket(remote_addr, port); - } else { - return ret; - } + // Look up the host first + int ret = 0; + DNSClient dns; + IPAddress remote_addr; + + dns.begin(Ethernet.dnsServerIP()); + ret = dns.getHostByName(host, remote_addr); + if (ret == 1) + { + return beginPacket(remote_addr, port); + } + else + { + return ret; + } } int EthernetUDP::beginPacket(IPAddress ip, uint16_t port) { - _offset = 0; - return startUDP(_sock, rawIPAddress(ip), port); + _offset = 0; + return startUDP(_sock, rawIPAddress(ip), port); } int EthernetUDP::endPacket() { - return sendUDP(_sock); + return sendUDP(_sock); } size_t EthernetUDP::write(uint8_t byte) { - return write(&byte, 1); + return write(&byte, 1); } size_t EthernetUDP::write(const uint8_t *buffer, size_t size) { - uint16_t bytes_written = bufferData(_sock, _offset, buffer, size); - _offset += bytes_written; - return bytes_written; + uint16_t bytes_written = bufferData(_sock, _offset, buffer, size); + _offset += bytes_written; + return bytes_written; } int EthernetUDP::parsePacket() { - // discard any remaining bytes in the last packet - clear_remaining(); - - if (recvAvailable(_sock) > 0) - { - //HACK - hand-parse the UDP packet using TCP recv method - uint8_t tmpBuf[8]; - int ret =0; - //read 8 header bytes and get IP and port from it - ret = recv(_sock,tmpBuf,8); - if (ret > 0) + // discard any remaining bytes in the last packet + clear_remaining(); + + if (recvAvailable(_sock) > 0) { - _remoteIP = tmpBuf; - _remotePort = tmpBuf[4]; - _remotePort = (_remotePort << 8) + tmpBuf[5]; - _remaining = tmpBuf[6]; - _remaining = (_remaining << 8) + tmpBuf[7]; - - // When we get here, any remaining bytes are the data - ret = _remaining; + //HACK - hand-parse the UDP packet using TCP recv method + uint8_t tmpBuf[8]; + int ret = 0; + //read 8 header bytes and get IP and port from it + ret = recv(_sock, tmpBuf, 8); + if (ret > 0) + { + _remoteIP = tmpBuf; + _remotePort = tmpBuf[4]; + _remotePort = (_remotePort << 8) + tmpBuf[5]; + _remaining = tmpBuf[6]; + _remaining = (_remaining << 8) + tmpBuf[7]; + + // When we get here, any remaining bytes are the data + ret = _remaining; + } + return ret; } - return ret; - } - // There aren't any packets available - return 0; + // There aren't any packets available + return 0; } int EthernetUDP::read() { - uint8_t byte; + uint8_t byte; - if ((_remaining > 0) && (recv(_sock, &byte, 1) > 0)) - { - // We read things without any problems - _remaining--; - return byte; - } + if ((_remaining > 0) && (recv(_sock, &byte, 1) > 0)) + { + // We read things without any problems + _remaining--; + return byte; + } - // If we get here, there's no data available - return -1; + // If we get here, there's no data available + return -1; } int EthernetUDP::read(unsigned char* buffer, size_t len) { - if (_remaining > 0) - { - - int got; - - if (_remaining <= len) + if (_remaining > 0) { - // data should fit in the buffer - got = recv(_sock, buffer, _remaining); - } - else - { - // too much data for the buffer, - // grab as much as will fit - got = recv(_sock, buffer, len); - } - if (got > 0) - { - _remaining -= got; - return got; - } + int got; + + if (_remaining <= len) + { + // data should fit in the buffer + got = recv(_sock, buffer, _remaining); + } + else + { + // too much data for the buffer, + // grab as much as will fit + got = recv(_sock, buffer, len); + } + + if (got > 0) + { + _remaining -= got; + return got; + } - } + } - // If we get here, there's no data available or recv failed - return -1; + // If we get here, there's no data available or recv failed + return -1; } int EthernetUDP::peek() { - uint8_t b; - // Unlike recv, peek doesn't check to see if there's any data available, so we must. - // If the user hasn't called parsePacket yet then return nothing otherwise they - // may get the UDP header - if (!_remaining) - return -1; - ::peek(_sock, &b); - return b; + uint8_t b; + // Unlike recv, peek doesn't check to see if there's any data available, so we must. + // If the user hasn't called parsePacket yet then return nothing otherwise they + // may get the UDP header + if (!_remaining) + { + return -1; + } + ::peek(_sock, &b); + return b; } void EthernetUDP::clear_remaining() { - // could this fail (loop endlessly) if _remaining > 0 and recv in read fails? - // should only occur if recv fails after telling us the data is there, lets - // hope the w5100 always behaves :) - - while (_remaining) - { - read(); - } + // could this fail (loop endlessly) if _remaining > 0 and recv in read fails? + // should only occur if recv fails after telling us the data is there, lets + // hope the w5100 always behaves :) + + while (_remaining) + { + read(); + } } void EthernetUDP::flush() { - endPacket(); + endPacket(); } diff --git a/libraries/Ethernet/src/EthernetUdp.h b/libraries/Ethernet/src/EthernetUdp.h index 1e928d88a4..679d83170d 100644 --- a/libraries/Ethernet/src/EthernetUdp.h +++ b/libraries/Ethernet/src/EthernetUdp.h @@ -1,38 +1,38 @@ /* - * Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. - * This version only offers minimal wrapping of socket.c/socket.h - * Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ - * - * NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these) - * 1) UDP does not guarantee the order in which assembled UDP packets are received. This - * might not happen often in practice, but in larger network topologies, a UDP - * packet can be received out of sequence. - * 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being - * aware of it. Again, this may not be a concern in practice on small local networks. - * For more information, see http://www.cafeaulait.org/course/week12/35.html - * - * MIT License: - * Copyright (c) 2008 Bjoern Hartmann - * 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. - * - * bjoern@cs.stanford.edu 12/30/2008 - */ + Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. + This version only offers minimal wrapping of socket.c/socket.h + Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ + + NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these) + 1) UDP does not guarantee the order in which assembled UDP packets are received. This + might not happen often in practice, but in larger network topologies, a UDP + packet can be received out of sequence. + 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being + aware of it. Again, this may not be a concern in practice on small local networks. + For more information, see http://www.cafeaulait.org/course/week12/35.html + + MIT License: + Copyright (c) 2008 Bjoern Hartmann + 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. + + bjoern@cs.stanford.edu 12/30/2008 +*/ #ifndef ethernetudp_h #define ethernetudp_h @@ -41,62 +41,72 @@ #define UDP_TX_PACKET_MAX_SIZE 24 -class EthernetUDP : public UDP { +class EthernetUDP : public UDP +{ private: - uint8_t _sock; // socket ID for Wiz5100 - uint16_t _port; // local port to listen on - IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed - uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed - uint16_t _offset; // offset into the packet being sent - uint16_t _remaining; // remaining bytes of incoming packet yet to be processed - + uint8_t _sock; // socket ID for Wiz5100 + uint16_t _port; // local port to listen on + IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed + uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed + uint16_t _offset; // offset into the packet being sent + uint16_t _remaining; // remaining bytes of incoming packet yet to be processed + protected: - void clear_remaining(); + void clear_remaining(); public: - EthernetUDP(); // Constructor - virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use - virtual void stop(); // Finish with the UDP socket - - // Sending UDP packets - - // Start building up a packet to send to the remote host specific in ip and port - // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port - virtual int beginPacket(IPAddress ip, uint16_t port); - // Start building up a packet to send to the remote host specific in host and port - // Returns 1 if successful, 0 if there was a problem resolving the hostname or port - virtual int beginPacket(const char *host, uint16_t port); - // Finish off this packet and send it - // Returns 1 if the packet was sent successfully, 0 if there was an error - virtual int endPacket(); - // Write a single byte into the packet - virtual size_t write(uint8_t); - // Write size bytes from buffer into the packet - virtual size_t write(const uint8_t *buffer, size_t size); - - using Print::write; - - // Start processing the next available incoming packet - // Returns the size of the packet in bytes, or 0 if no packets are available - virtual int parsePacket(); - // Number of bytes remaining in the current packet - virtual int available(); - // Read a single byte from the current packet - virtual int read(); - // Read up to len bytes from the current packet and place them into buffer - // Returns the number of bytes read, or 0 if none are available - virtual int read(unsigned char* buffer, size_t len); - // Read up to len characters from the current packet and place them into buffer - // Returns the number of characters read, or 0 if none are available - virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); }; - // Return the next byte from the current packet without moving on to the next byte - virtual int peek(); - virtual void flush(); // Finish reading the current packet - - // Return the IP address of the host who sent the current incoming packet - virtual IPAddress remoteIP() { return _remoteIP; }; - // Return the port of the host who sent the current incoming packet - virtual uint16_t remotePort() { return _remotePort; }; + EthernetUDP(); // Constructor + virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual void stop(); // Finish with the UDP socket + + // Sending UDP packets + + // Start building up a packet to send to the remote host specific in ip and port + // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port + virtual int beginPacket(IPAddress ip, uint16_t port); + // Start building up a packet to send to the remote host specific in host and port + // Returns 1 if successful, 0 if there was a problem resolving the hostname or port + virtual int beginPacket(const char *host, uint16_t port); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + virtual int endPacket(); + // Write a single byte into the packet + virtual size_t write(uint8_t); + // Write size bytes from buffer into the packet + virtual size_t write(const uint8_t *buffer, size_t size); + + using Print::write; + + // Start processing the next available incoming packet + // Returns the size of the packet in bytes, or 0 if no packets are available + virtual int parsePacket(); + // Number of bytes remaining in the current packet + virtual int available(); + // Read a single byte from the current packet + virtual int read(); + // Read up to len bytes from the current packet and place them into buffer + // Returns the number of bytes read, or 0 if none are available + virtual int read(unsigned char* buffer, size_t len); + // Read up to len characters from the current packet and place them into buffer + // Returns the number of characters read, or 0 if none are available + virtual int read(char* buffer, size_t len) + { + return read((unsigned char*)buffer, len); + }; + // Return the next byte from the current packet without moving on to the next byte + virtual int peek(); + virtual void flush(); // Finish reading the current packet + + // Return the IP address of the host who sent the current incoming packet + virtual IPAddress remoteIP() + { + return _remoteIP; + }; + // Return the port of the host who sent the current incoming packet + virtual uint16_t remotePort() + { + return _remotePort; + }; }; #endif diff --git a/libraries/Ethernet/src/utility/socket.cpp b/libraries/Ethernet/src/utility/socket.cpp index 29c20ca2c2..8ac7f15e2d 100644 --- a/libraries/Ethernet/src/utility/socket.cpp +++ b/libraries/Ethernet/src/utility/socket.cpp @@ -4,467 +4,488 @@ static uint16_t local_port; /** - * @brief This Socket function initialize the channel in perticular mode, and set the port and wait for W5100 done it. - * @return 1 for success else 0. - */ + @brief This Socket function initialize the channel in perticular mode, and set the port and wait for W5100 done it. + @return 1 for success else 0. +*/ uint8_t socket(SOCKET s, uint8_t protocol, uint16_t port, uint8_t flag) { - if ((protocol == SnMR::TCP) || (protocol == SnMR::UDP) || (protocol == SnMR::IPRAW) || (protocol == SnMR::MACRAW) || (protocol == SnMR::PPPOE)) - { - close(s); - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.writeSnMR(s, protocol | flag); - if (port != 0) { - W5100.writeSnPORT(s, port); - } - else { - local_port++; // if don't set the source port, set local_port number. - W5100.writeSnPORT(s, local_port); + if ((protocol == SnMR::TCP) || (protocol == SnMR::UDP) || (protocol == SnMR::IPRAW) || (protocol == SnMR::MACRAW) || (protocol == SnMR::PPPOE)) + { + close(s); + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.writeSnMR(s, protocol | flag); + if (port != 0) + { + W5100.writeSnPORT(s, port); + } + else + { + local_port++; // if don't set the source port, set local_port number. + W5100.writeSnPORT(s, local_port); + } + + W5100.execCmdSn(s, Sock_OPEN); + SPI.endTransaction(); + return 1; } - W5100.execCmdSn(s, Sock_OPEN); - SPI.endTransaction(); - return 1; - } - - return 0; + return 0; } uint8_t socketStatus(SOCKET s) { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - uint8_t status = W5100.readSnSR(s); - SPI.endTransaction(); - return status; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + uint8_t status = W5100.readSnSR(s); + SPI.endTransaction(); + return status; } /** - * @brief This function close the socket and parameter is "s" which represent the socket number - */ + @brief This function close the socket and parameter is "s" which represent the socket number +*/ void close(SOCKET s) { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.execCmdSn(s, Sock_CLOSE); - W5100.writeSnIR(s, 0xFF); - SPI.endTransaction(); + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.execCmdSn(s, Sock_CLOSE); + W5100.writeSnIR(s, 0xFF); + SPI.endTransaction(); } /** - * @brief This function established the connection for the channel in passive (server) mode. This function waits for the request from the peer. - * @return 1 for success else 0. - */ + @brief This function established the connection for the channel in passive (server) mode. This function waits for the request from the peer. + @return 1 for success else 0. +*/ uint8_t listen(SOCKET s) { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - if (W5100.readSnSR(s) != SnSR::INIT) { + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + if (W5100.readSnSR(s) != SnSR::INIT) + { + SPI.endTransaction(); + return 0; + } + W5100.execCmdSn(s, Sock_LISTEN); SPI.endTransaction(); - return 0; - } - W5100.execCmdSn(s, Sock_LISTEN); - SPI.endTransaction(); - return 1; + return 1; } /** - * @brief This function established the connection for the channel in Active (client) mode. - * This function waits for the untill the connection is established. - * - * @return 1 for success else 0. - */ + @brief This function established the connection for the channel in Active (client) mode. + This function waits for the untill the connection is established. + + @return 1 for success else 0. +*/ uint8_t connect(SOCKET s, const uint8_t * addr, uint16_t port) { - if + if ( - ((addr[0] == 0xFF) && (addr[1] == 0xFF) && (addr[2] == 0xFF) && (addr[3] == 0xFF)) || - ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) || - (port == 0x00) - ) - return 0; + ((addr[0] == 0xFF) && (addr[1] == 0xFF) && (addr[2] == 0xFF) && (addr[3] == 0xFF)) || + ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) || + (port == 0x00) + ) + { + return 0; + } - // set destination IP - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.writeSnDIPR(s, addr); - W5100.writeSnDPORT(s, port); - W5100.execCmdSn(s, Sock_CONNECT); - SPI.endTransaction(); + // set destination IP + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.writeSnDIPR(s, addr); + W5100.writeSnDPORT(s, port); + W5100.execCmdSn(s, Sock_CONNECT); + SPI.endTransaction(); - return 1; + return 1; } /** - * @brief This function used for disconnect the socket and parameter is "s" which represent the socket number - * @return 1 for success else 0. - */ + @brief This function used for disconnect the socket and parameter is "s" which represent the socket number + @return 1 for success else 0. +*/ void disconnect(SOCKET s) { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.execCmdSn(s, Sock_DISCON); - SPI.endTransaction(); + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.execCmdSn(s, Sock_DISCON); + SPI.endTransaction(); } /** - * @brief This function used to send the data in TCP mode - * @return 1 for success else 0. - */ + @brief This function used to send the data in TCP mode + @return 1 for success else 0. +*/ uint16_t send(SOCKET s, const uint8_t * buf, uint16_t len) { - uint8_t status=0; - uint16_t ret=0; - uint16_t freesize=0; - - if (len > W5100.SSIZE) - ret = W5100.SSIZE; // check size not to exceed MAX size. - else - ret = len; - - // if freebuf is available, start. - do - { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - freesize = W5100.getTXFreeSize(s); - status = W5100.readSnSR(s); - SPI.endTransaction(); - if ((status != SnSR::ESTABLISHED) && (status != SnSR::CLOSE_WAIT)) + uint8_t status = 0; + uint16_t ret = 0; + uint16_t freesize = 0; + + if (len > W5100.SSIZE) { - ret = 0; - break; + ret = W5100.SSIZE; // check size not to exceed MAX size. } - yield(); - } - while (freesize < ret); - - // copy data - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.send_data_processing(s, (uint8_t *)buf, ret); - W5100.execCmdSn(s, Sock_SEND); - - /* +2008.01 bj */ - while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) - { - /* m2008.01 [bj] : reduce code */ - if ( W5100.readSnSR(s) == SnSR::CLOSED ) + else { - SPI.endTransaction(); - close(s); - return 0; + ret = len; } - SPI.endTransaction(); - yield(); + + // if freebuf is available, start. + do + { + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + freesize = W5100.getTXFreeSize(s); + status = W5100.readSnSR(s); + SPI.endTransaction(); + if ((status != SnSR::ESTABLISHED) && (status != SnSR::CLOSE_WAIT)) + { + ret = 0; + break; + } + yield(); + } while (freesize < ret); + + // copy data SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - } - /* +2008.01 bj */ - W5100.writeSnIR(s, SnIR::SEND_OK); - SPI.endTransaction(); - return ret; + W5100.send_data_processing(s, (uint8_t *)buf, ret); + W5100.execCmdSn(s, Sock_SEND); + + /* +2008.01 bj */ + while ((W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK) + { + /* m2008.01 [bj] : reduce code */ + if (W5100.readSnSR(s) == SnSR::CLOSED) + { + SPI.endTransaction(); + close(s); + return 0; + } + SPI.endTransaction(); + yield(); + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + } + /* +2008.01 bj */ + W5100.writeSnIR(s, SnIR::SEND_OK); + SPI.endTransaction(); + return ret; } /** - * @brief This function is an application I/F function which is used to receive the data in TCP mode. - * It continues to wait for data as much as the application wants to receive. - * - * @return received data size for success else -1. - */ + @brief This function is an application I/F function which is used to receive the data in TCP mode. + It continues to wait for data as much as the application wants to receive. + + @return received data size for success else -1. +*/ int16_t recv(SOCKET s, uint8_t *buf, int16_t len) { - // Check how much data is available - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - int16_t ret = W5100.getRXReceivedSize(s); - if ( ret == 0 ) - { - // No data available. - uint8_t status = W5100.readSnSR(s); - if ( status == SnSR::LISTEN || status == SnSR::CLOSED || status == SnSR::CLOSE_WAIT ) + // Check how much data is available + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + int16_t ret = W5100.getRXReceivedSize(s); + if (ret == 0) { - // The remote end has closed its side of the connection, so this is the eof state - ret = 0; + // No data available. + uint8_t status = W5100.readSnSR(s); + if (status == SnSR::LISTEN || status == SnSR::CLOSED || status == SnSR::CLOSE_WAIT) + { + // The remote end has closed its side of the connection, so this is the eof state + ret = 0; + } + else + { + // The connection is still up, but there's no data waiting to be read + ret = -1; + } } - else + else if (ret > len) { - // The connection is still up, but there's no data waiting to be read - ret = -1; + ret = len; } - } - else if (ret > len) - { - ret = len; - } - - if ( ret > 0 ) - { - W5100.recv_data_processing(s, buf, ret); - W5100.execCmdSn(s, Sock_RECV); - } - SPI.endTransaction(); - return ret; + + if (ret > 0) + { + W5100.recv_data_processing(s, buf, ret); + W5100.execCmdSn(s, Sock_RECV); + } + SPI.endTransaction(); + return ret; } int16_t recvAvailable(SOCKET s) { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - int16_t ret = W5100.getRXReceivedSize(s); - SPI.endTransaction(); - return ret; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + int16_t ret = W5100.getRXReceivedSize(s); + SPI.endTransaction(); + return ret; } /** - * @brief Returns the first byte in the receive queue (no checking) - * - * @return - */ + @brief Returns the first byte in the receive queue (no checking) + + @return +*/ uint16_t peek(SOCKET s, uint8_t *buf) { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.recv_data_processing(s, buf, 1, 1); - SPI.endTransaction(); - return 1; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.recv_data_processing(s, buf, 1, 1); + SPI.endTransaction(); + return 1; } /** - * @brief This function is an application I/F function which is used to send the data for other then TCP mode. - * Unlike TCP transmission, The peer's destination address and the port is needed. - * - * @return This function return send data size for success else -1. - */ + @brief This function is an application I/F function which is used to send the data for other then TCP mode. + Unlike TCP transmission, The peer's destination address and the port is needed. + + @return This function return send data size for success else -1. +*/ uint16_t sendto(SOCKET s, const uint8_t *buf, uint16_t len, uint8_t *addr, uint16_t port) { - uint16_t ret=0; + uint16_t ret = 0; - if (len > W5100.SSIZE) ret = W5100.SSIZE; // check size not to exceed MAX size. - else ret = len; + if (len > W5100.SSIZE) + { + ret = W5100.SSIZE; // check size not to exceed MAX size. + } + else + { + ret = len; + } - if + if ( - ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) || - ((port == 0x00)) ||(ret == 0) - ) - { - /* +2008.01 [bj] : added return value */ - ret = 0; - } - else - { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.writeSnDIPR(s, addr); - W5100.writeSnDPORT(s, port); - - // copy data - W5100.send_data_processing(s, (uint8_t *)buf, ret); - W5100.execCmdSn(s, Sock_SEND); - - /* +2008.01 bj */ - while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) + ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) || + ((port == 0x00)) || (ret == 0) + ) { - if (W5100.readSnIR(s) & SnIR::TIMEOUT) - { - /* +2008.01 [bj]: clear interrupt */ - W5100.writeSnIR(s, (SnIR::SEND_OK | SnIR::TIMEOUT)); /* clear SEND_OK & TIMEOUT */ + /* +2008.01 [bj] : added return value */ + ret = 0; + } + else + { + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.writeSnDIPR(s, addr); + W5100.writeSnDPORT(s, port); + + // copy data + W5100.send_data_processing(s, (uint8_t *)buf, ret); + W5100.execCmdSn(s, Sock_SEND); + + /* +2008.01 bj */ + while ((W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK) + { + if (W5100.readSnIR(s) & SnIR::TIMEOUT) + { + /* +2008.01 [bj]: clear interrupt */ + W5100.writeSnIR(s, (SnIR::SEND_OK | SnIR::TIMEOUT)); /* clear SEND_OK & TIMEOUT */ + SPI.endTransaction(); + return 0; + } + SPI.endTransaction(); + yield(); + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + } + + /* +2008.01 bj */ + W5100.writeSnIR(s, SnIR::SEND_OK); SPI.endTransaction(); - return 0; - } - SPI.endTransaction(); - yield(); - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); } - - /* +2008.01 bj */ - W5100.writeSnIR(s, SnIR::SEND_OK); - SPI.endTransaction(); - } - return ret; + return ret; } /** - * @brief This function is an application I/F function which is used to receive the data in other then - * TCP mode. This function is used to receive UDP, IP_RAW and MAC_RAW mode, and handle the header as well. - * - * @return This function return received data size for success else -1. - */ + @brief This function is an application I/F function which is used to receive the data in other then + TCP mode. This function is used to receive UDP, IP_RAW and MAC_RAW mode, and handle the header as well. + + @return This function return received data size for success else -1. +*/ uint16_t recvfrom(SOCKET s, uint8_t *buf, uint16_t len, uint8_t *addr, uint16_t *port) { - uint8_t head[8]; - uint16_t data_len=0; - uint16_t ptr=0; + uint8_t head[8]; + uint16_t data_len = 0; + uint16_t ptr = 0; - if ( len > 0 ) - { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - ptr = W5100.readSnRX_RD(s); - switch (W5100.readSnMR(s) & 0x07) + if (len > 0) { - case SnMR::UDP : - W5100.read_data(s, ptr, head, 0x08); - ptr += 8; - // read peer's IP address, port number. - addr[0] = head[0]; - addr[1] = head[1]; - addr[2] = head[2]; - addr[3] = head[3]; - *port = head[4]; - *port = (*port << 8) + head[5]; - data_len = head[6]; - data_len = (data_len << 8) + head[7]; - - W5100.read_data(s, ptr, buf, data_len); // data copy. - ptr += data_len; - - W5100.writeSnRX_RD(s, ptr); - break; - - case SnMR::IPRAW : - W5100.read_data(s, ptr, head, 0x06); - ptr += 6; - - addr[0] = head[0]; - addr[1] = head[1]; - addr[2] = head[2]; - addr[3] = head[3]; - data_len = head[4]; - data_len = (data_len << 8) + head[5]; - - W5100.read_data(s, ptr, buf, data_len); // data copy. - ptr += data_len; - - W5100.writeSnRX_RD(s, ptr); - break; - - case SnMR::MACRAW: - W5100.read_data(s, ptr, head, 2); - ptr+=2; - data_len = head[0]; - data_len = (data_len<<8) + head[1] - 2; - - W5100.read_data(s, ptr, buf, data_len); - ptr += data_len; - W5100.writeSnRX_RD(s, ptr); - break; - - default : - break; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + ptr = W5100.readSnRX_RD(s); + switch (W5100.readSnMR(s) & 0x07) + { + case SnMR::UDP : + W5100.read_data(s, ptr, head, 0x08); + ptr += 8; + // read peer's IP address, port number. + addr[0] = head[0]; + addr[1] = head[1]; + addr[2] = head[2]; + addr[3] = head[3]; + *port = head[4]; + *port = (*port << 8) + head[5]; + data_len = head[6]; + data_len = (data_len << 8) + head[7]; + + W5100.read_data(s, ptr, buf, data_len); // data copy. + ptr += data_len; + + W5100.writeSnRX_RD(s, ptr); + break; + + case SnMR::IPRAW : + W5100.read_data(s, ptr, head, 0x06); + ptr += 6; + + addr[0] = head[0]; + addr[1] = head[1]; + addr[2] = head[2]; + addr[3] = head[3]; + data_len = head[4]; + data_len = (data_len << 8) + head[5]; + + W5100.read_data(s, ptr, buf, data_len); // data copy. + ptr += data_len; + + W5100.writeSnRX_RD(s, ptr); + break; + + case SnMR::MACRAW: + W5100.read_data(s, ptr, head, 2); + ptr += 2; + data_len = head[0]; + data_len = (data_len << 8) + head[1] - 2; + + W5100.read_data(s, ptr, buf, data_len); + ptr += data_len; + W5100.writeSnRX_RD(s, ptr); + break; + + default : + break; + } + W5100.execCmdSn(s, Sock_RECV); + SPI.endTransaction(); } - W5100.execCmdSn(s, Sock_RECV); - SPI.endTransaction(); - } - return data_len; + return data_len; } /** - * @brief Wait for buffered transmission to complete. - */ -void flush(SOCKET s) { - // TODO - (void) s; + @brief Wait for buffered transmission to complete. +*/ +void flush(SOCKET s) +{ + // TODO + (void) s; } uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len) { - uint16_t ret=0; + uint16_t ret = 0; - if (len > W5100.SSIZE) - ret = W5100.SSIZE; // check size not to exceed MAX size. - else - ret = len; + if (len > W5100.SSIZE) + { + ret = W5100.SSIZE; // check size not to exceed MAX size. + } + else + { + ret = len; + } - if (ret == 0) - return 0; + if (ret == 0) + { + return 0; + } - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.send_data_processing(s, (uint8_t *)buf, ret); - W5100.execCmdSn(s, Sock_SEND); + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.send_data_processing(s, (uint8_t *)buf, ret); + W5100.execCmdSn(s, Sock_SEND); - while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) - { - if (W5100.readSnIR(s) & SnIR::TIMEOUT) + while ((W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK) { - /* in case of igmp, if send fails, then socket closed */ - /* if you want change, remove this code. */ - SPI.endTransaction(); - close(s); - return 0; + if (W5100.readSnIR(s) & SnIR::TIMEOUT) + { + /* in case of igmp, if send fails, then socket closed */ + /* if you want change, remove this code. */ + SPI.endTransaction(); + close(s); + return 0; + } + SPI.endTransaction(); + yield(); + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); } - SPI.endTransaction(); - yield(); - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - } - W5100.writeSnIR(s, SnIR::SEND_OK); - SPI.endTransaction(); - return ret; + W5100.writeSnIR(s, SnIR::SEND_OK); + SPI.endTransaction(); + return ret; } uint16_t bufferData(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len) { - uint16_t ret =0; - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - if (len > W5100.getTXFreeSize(s)) - { - ret = W5100.getTXFreeSize(s); // check size not to exceed MAX size. - } - else - { - ret = len; - } - W5100.send_data_processing_offset(s, offset, buf, ret); - SPI.endTransaction(); - return ret; + uint16_t ret = 0; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + if (len > W5100.getTXFreeSize(s)) + { + ret = W5100.getTXFreeSize(s); // check size not to exceed MAX size. + } + else + { + ret = len; + } + W5100.send_data_processing_offset(s, offset, buf, ret); + SPI.endTransaction(); + return ret; } int startUDP(SOCKET s, const uint8_t* addr, uint16_t port) { - if + if ( - ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) || - ((port == 0x00)) - ) - { - return 0; - } - else - { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.writeSnDIPR(s, addr); - W5100.writeSnDPORT(s, port); - SPI.endTransaction(); - return 1; - } + ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) || + ((port == 0x00)) + ) + { + return 0; + } + else + { + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.writeSnDIPR(s, addr); + W5100.writeSnDPORT(s, port); + SPI.endTransaction(); + return 1; + } } int sendUDP(SOCKET s) { - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.execCmdSn(s, Sock_SEND); - - /* +2008.01 bj */ - while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) - { - if (W5100.readSnIR(s) & SnIR::TIMEOUT) + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); + W5100.execCmdSn(s, Sock_SEND); + + /* +2008.01 bj */ + while ((W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK) { - /* +2008.01 [bj]: clear interrupt */ - W5100.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT)); - SPI.endTransaction(); - return 0; + if (W5100.readSnIR(s) & SnIR::TIMEOUT) + { + /* +2008.01 [bj]: clear interrupt */ + W5100.writeSnIR(s, (SnIR::SEND_OK | SnIR::TIMEOUT)); + SPI.endTransaction(); + return 0; + } + SPI.endTransaction(); + yield(); + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); } - SPI.endTransaction(); - yield(); - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - } - /* +2008.01 bj */ - W5100.writeSnIR(s, SnIR::SEND_OK); - SPI.endTransaction(); + /* +2008.01 bj */ + W5100.writeSnIR(s, SnIR::SEND_OK); + SPI.endTransaction(); - /* Sent ok */ - return 1; + /* Sent ok */ + return 1; } diff --git a/libraries/Ethernet/src/utility/socket.h b/libraries/Ethernet/src/utility/socket.h index 4b191e24e5..ffafb26271 100644 --- a/libraries/Ethernet/src/utility/socket.h +++ b/libraries/Ethernet/src/utility/socket.h @@ -22,21 +22,21 @@ extern uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len); // Functions to allow buffered UDP send (i.e. where the UDP datagram is built up over a // number of calls before being sent /* - @brief This function sets up a UDP datagram, the data for which will be provided by one - or more calls to bufferData and then finally sent with sendUDP. - @return 1 if the datagram was successfully set up, or 0 if there was an error + @brief This function sets up a UDP datagram, the data for which will be provided by one + or more calls to bufferData and then finally sent with sendUDP. + @return 1 if the datagram was successfully set up, or 0 if there was an error */ extern int startUDP(SOCKET s, const uint8_t* addr, uint16_t port); /* - @brief This function copies up to len bytes of data from buf into a UDP datagram to be - sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls. - @return Number of bytes successfully buffered + @brief This function copies up to len bytes of data from buf into a UDP datagram to be + sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls. + @return Number of bytes successfully buffered */ uint16_t bufferData(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len); /* - @brief Send a UDP datagram built up from a sequence of startUDP followed by one or more - calls to bufferData. - @return 1 if the datagram was successfully sent, or 0 if there was an error + @brief Send a UDP datagram built up from a sequence of startUDP followed by one or more + calls to bufferData. + @return 1 if the datagram was successfully sent, or 0 if there was an error */ int sendUDP(SOCKET s); diff --git a/libraries/Ethernet/src/utility/w5100.cpp b/libraries/Ethernet/src/utility/w5100.cpp index 05b22d187a..478a638cca 100644 --- a/libraries/Ethernet/src/utility/w5100.cpp +++ b/libraries/Ethernet/src/utility/w5100.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2010 by Arduino LLC. All rights reserved. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of either the GNU General Public License version 2 - * or the GNU Lesser General Public License version 2.1, both as - * published by the Free Software Foundation. - */ + Copyright (c) 2010 by Arduino LLC. All rights reserved. + + This file is free software; you can redistribute it and/or modify + it under the terms of either the GNU General Public License version 2 + or the GNU Lesser General Public License version 2.1, both as + published by the Free Software Foundation. +*/ #include #include @@ -24,202 +24,211 @@ W5100Class W5100; void W5100Class::init(void) { - delay(300); + delay(300); #if defined(ARDUINO_ARCH_AVR) || defined(ESP8266) - SPI.begin(); - initSS(); + SPI.begin(); + initSS(); #else - SPI.begin(SPI_CS); - // Set clock to 4Mhz (W5100 should support up to about 14Mhz) - SPI.setClockDivider(SPI_CS, 21); - SPI.setDataMode(SPI_CS, SPI_MODE0); + SPI.begin(SPI_CS); + // Set clock to 4Mhz (W5100 should support up to about 14Mhz) + SPI.setClockDivider(SPI_CS, 21); + SPI.setDataMode(SPI_CS, SPI_MODE0); #endif - SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - writeMR(1< SSIZE) - { - // Wrap around circular buffer - uint16_t size = SSIZE - offset; - write(dstAddr, data, size); - write(SBASE[s], data + size, len - size); - } - else { - write(dstAddr, data, len); - } - - ptr += len; - writeSnTX_WR(s, ptr); + uint16_t ptr = readSnTX_WR(s); + ptr += data_offset; + uint16_t offset = ptr & SMASK; + uint16_t dstAddr = offset + SBASE[s]; + + if (offset + len > SSIZE) + { + // Wrap around circular buffer + uint16_t size = SSIZE - offset; + write(dstAddr, data, size); + write(SBASE[s], data + size, len - size); + } + else + { + write(dstAddr, data, len); + } + + ptr += len; + writeSnTX_WR(s, ptr); } void W5100Class::recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek) { - uint16_t ptr; - ptr = readSnRX_RD(s); - read_data(s, ptr, data, len); - if (!peek) - { - ptr += len; - writeSnRX_RD(s, ptr); - } + uint16_t ptr; + ptr = readSnRX_RD(s); + read_data(s, ptr, data, len); + if (!peek) + { + ptr += len; + writeSnRX_RD(s, ptr); + } } void W5100Class::read_data(SOCKET s, volatile uint16_t src, volatile uint8_t *dst, uint16_t len) { - uint16_t size; - uint16_t src_mask; - uint16_t src_ptr; - - src_mask = src & RMASK; - src_ptr = RBASE[s] + src_mask; - - if( (src_mask + len) > RSIZE ) - { - size = RSIZE - src_mask; - read(src_ptr, (uint8_t *)dst, size); - dst += size; - read(RBASE[s], (uint8_t *) dst, len - size); - } - else - read(src_ptr, (uint8_t *) dst, len); + uint16_t size; + uint16_t src_mask; + uint16_t src_ptr; + + src_mask = src & RMASK; + src_ptr = RBASE[s] + src_mask; + + if ((src_mask + len) > RSIZE) + { + size = RSIZE - src_mask; + read(src_ptr, (uint8_t *)dst, size); + dst += size; + read(RBASE[s], (uint8_t *) dst, len - size); + } + else + { + read(src_ptr, (uint8_t *) dst, len); + } } uint8_t W5100Class::write(uint16_t _addr, uint8_t _data) { #if defined(ARDUINO_ARCH_AVR) || defined(ESP8266) - setSS(); - SPI.transfer(0xF0); - SPI.transfer(_addr >> 8); - SPI.transfer(_addr & 0xFF); - SPI.transfer(_data); - resetSS(); -#else - SPI.transfer(SPI_CS, 0xF0, SPI_CONTINUE); - SPI.transfer(SPI_CS, _addr >> 8, SPI_CONTINUE); - SPI.transfer(SPI_CS, _addr & 0xFF, SPI_CONTINUE); - SPI.transfer(SPI_CS, _data); -#endif - return 1; -} - -uint16_t W5100Class::write(uint16_t _addr, const uint8_t *_buf, uint16_t _len) -{ - for (uint16_t i=0; i<_len; i++) - { -#if defined(ARDUINO_ARCH_AVR) || defined(ESP8266) - setSS(); + setSS(); SPI.transfer(0xF0); SPI.transfer(_addr >> 8); SPI.transfer(_addr & 0xFF); - _addr++; - SPI.transfer(_buf[i]); + SPI.transfer(_data); resetSS(); #else - SPI.transfer(SPI_CS, 0xF0, SPI_CONTINUE); - SPI.transfer(SPI_CS, _addr >> 8, SPI_CONTINUE); - SPI.transfer(SPI_CS, _addr & 0xFF, SPI_CONTINUE); - SPI.transfer(SPI_CS, _buf[i]); - _addr++; + SPI.transfer(SPI_CS, 0xF0, SPI_CONTINUE); + SPI.transfer(SPI_CS, _addr >> 8, SPI_CONTINUE); + SPI.transfer(SPI_CS, _addr & 0xFF, SPI_CONTINUE); + SPI.transfer(SPI_CS, _data); #endif - } - return _len; + return 1; } -uint8_t W5100Class::read(uint16_t _addr) +uint16_t W5100Class::write(uint16_t _addr, const uint8_t *_buf, uint16_t _len) { + for (uint16_t i = 0; i < _len; i++) + { #if defined(ARDUINO_ARCH_AVR) || defined(ESP8266) - setSS(); - SPI.transfer(0x0F); - SPI.transfer(_addr >> 8); - SPI.transfer(_addr & 0xFF); - uint8_t _data = SPI.transfer(0); - resetSS(); + setSS(); + SPI.transfer(0xF0); + SPI.transfer(_addr >> 8); + SPI.transfer(_addr & 0xFF); + _addr++; + SPI.transfer(_buf[i]); + resetSS(); #else - SPI.transfer(SPI_CS, 0x0F, SPI_CONTINUE); - SPI.transfer(SPI_CS, _addr >> 8, SPI_CONTINUE); - SPI.transfer(SPI_CS, _addr & 0xFF, SPI_CONTINUE); - uint8_t _data = SPI.transfer(SPI_CS, 0); + SPI.transfer(SPI_CS, 0xF0, SPI_CONTINUE); + SPI.transfer(SPI_CS, _addr >> 8, SPI_CONTINUE); + SPI.transfer(SPI_CS, _addr & 0xFF, SPI_CONTINUE); + SPI.transfer(SPI_CS, _buf[i]); + _addr++; #endif - return _data; + } + return _len; } -uint16_t W5100Class::read(uint16_t _addr, uint8_t *_buf, uint16_t _len) +uint8_t W5100Class::read(uint16_t _addr) { - for (uint16_t i=0; i<_len; i++) - { #if defined(ARDUINO_ARCH_AVR) || defined(ESP8266) setSS(); SPI.transfer(0x0F); SPI.transfer(_addr >> 8); SPI.transfer(_addr & 0xFF); - _addr++; - _buf[i] = SPI.transfer(0); + uint8_t _data = SPI.transfer(0); resetSS(); #else - SPI.transfer(SPI_CS, 0x0F, SPI_CONTINUE); - SPI.transfer(SPI_CS, _addr >> 8, SPI_CONTINUE); - SPI.transfer(SPI_CS, _addr & 0xFF, SPI_CONTINUE); - _buf[i] = SPI.transfer(SPI_CS, 0); - _addr++; + SPI.transfer(SPI_CS, 0x0F, SPI_CONTINUE); + SPI.transfer(SPI_CS, _addr >> 8, SPI_CONTINUE); + SPI.transfer(SPI_CS, _addr & 0xFF, SPI_CONTINUE); + uint8_t _data = SPI.transfer(SPI_CS, 0); +#endif + return _data; +} + +uint16_t W5100Class::read(uint16_t _addr, uint8_t *_buf, uint16_t _len) +{ + for (uint16_t i = 0; i < _len; i++) + { +#if defined(ARDUINO_ARCH_AVR) || defined(ESP8266) + setSS(); + SPI.transfer(0x0F); + SPI.transfer(_addr >> 8); + SPI.transfer(_addr & 0xFF); + _addr++; + _buf[i] = SPI.transfer(0); + resetSS(); +#else + SPI.transfer(SPI_CS, 0x0F, SPI_CONTINUE); + SPI.transfer(SPI_CS, _addr >> 8, SPI_CONTINUE); + SPI.transfer(SPI_CS, _addr & 0xFF, SPI_CONTINUE); + _buf[i] = SPI.transfer(SPI_CS, 0); + _addr++; #endif - } - return _len; + } + return _len; } -void W5100Class::execCmdSn(SOCKET s, SockCMD _cmd) { - // Send command to socket - writeSnCR(s, _cmd); - // Wait for command to complete - while (readSnCR(s)) - ; +void W5100Class::execCmdSn(SOCKET s, SockCMD _cmd) +{ + // Send command to socket + writeSnCR(s, _cmd); + // Wait for command to complete + while (readSnCR(s)) + ; } \ No newline at end of file diff --git a/libraries/Ethernet/src/utility/w5100.h b/libraries/Ethernet/src/utility/w5100.h index 6d3fa6d07f..b824513a83 100644 --- a/libraries/Ethernet/src/utility/w5100.h +++ b/libraries/Ethernet/src/utility/w5100.h @@ -1,11 +1,11 @@ /* - * Copyright (c) 2010 by Arduino LLC. All rights reserved. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of either the GNU General Public License version 2 - * or the GNU Lesser General Public License version 2.1, both as - * published by the Free Software Foundation. - */ + Copyright (c) 2010 by Arduino LLC. All rights reserved. + + This file is free software; you can redistribute it and/or modify + it under the terms of either the GNU General Public License version 2 + or the GNU Lesser General Public License version 2.1, both as + published by the Free Software Foundation. +*/ #ifndef W5100_H_INCLUDED #define W5100_H_INCLUDED @@ -31,182 +31,188 @@ typedef uint8_t SOCKET; #define IDM_AR1 0x8002 #define IDM_DR 0x8003 /* -class MR { -public: - static const uint8_t RST = 0x80; - static const uint8_t PB = 0x10; - static const uint8_t PPPOE = 0x08; - static const uint8_t LB = 0x04; - static const uint8_t AI = 0x02; - static const uint8_t IND = 0x01; -}; + class MR { + public: + static const uint8_t RST = 0x80; + static const uint8_t PB = 0x10; + static const uint8_t PPPOE = 0x08; + static const uint8_t LB = 0x04; + static const uint8_t AI = 0x02; + static const uint8_t IND = 0x01; + }; */ /* -class IR { -public: - static const uint8_t CONFLICT = 0x80; - static const uint8_t UNREACH = 0x40; - static const uint8_t PPPoE = 0x20; - static const uint8_t SOCK0 = 0x01; - static const uint8_t SOCK1 = 0x02; - static const uint8_t SOCK2 = 0x04; - static const uint8_t SOCK3 = 0x08; - static inline uint8_t SOCK(SOCKET ch) { return (0x01 << ch); }; -}; + class IR { + public: + static const uint8_t CONFLICT = 0x80; + static const uint8_t UNREACH = 0x40; + static const uint8_t PPPoE = 0x20; + static const uint8_t SOCK0 = 0x01; + static const uint8_t SOCK1 = 0x02; + static const uint8_t SOCK2 = 0x04; + static const uint8_t SOCK3 = 0x08; + static inline uint8_t SOCK(SOCKET ch) { return (0x01 << ch); }; + }; */ -class SnMR { +class SnMR +{ public: - static const uint8_t CLOSE = 0x00; - static const uint8_t TCP = 0x01; - static const uint8_t UDP = 0x02; - static const uint8_t IPRAW = 0x03; - static const uint8_t MACRAW = 0x04; - static const uint8_t PPPOE = 0x05; - static const uint8_t ND = 0x20; - static const uint8_t MULTI = 0x80; + static const uint8_t CLOSE = 0x00; + static const uint8_t TCP = 0x01; + static const uint8_t UDP = 0x02; + static const uint8_t IPRAW = 0x03; + static const uint8_t MACRAW = 0x04; + static const uint8_t PPPOE = 0x05; + static const uint8_t ND = 0x20; + static const uint8_t MULTI = 0x80; }; -enum SockCMD { - Sock_OPEN = 0x01, - Sock_LISTEN = 0x02, - Sock_CONNECT = 0x04, - Sock_DISCON = 0x08, - Sock_CLOSE = 0x10, - Sock_SEND = 0x20, - Sock_SEND_MAC = 0x21, - Sock_SEND_KEEP = 0x22, - Sock_RECV = 0x40 +enum SockCMD +{ + Sock_OPEN = 0x01, + Sock_LISTEN = 0x02, + Sock_CONNECT = 0x04, + Sock_DISCON = 0x08, + Sock_CLOSE = 0x10, + Sock_SEND = 0x20, + Sock_SEND_MAC = 0x21, + Sock_SEND_KEEP = 0x22, + Sock_RECV = 0x40 }; -/*class SnCmd { -public: - static const uint8_t OPEN = 0x01; - static const uint8_t LISTEN = 0x02; - static const uint8_t CONNECT = 0x04; - static const uint8_t DISCON = 0x08; - static const uint8_t CLOSE = 0x10; - static const uint8_t SEND = 0x20; - static const uint8_t SEND_MAC = 0x21; - static const uint8_t SEND_KEEP = 0x22; - static const uint8_t RECV = 0x40; -}; +/* class SnCmd { + public: + static const uint8_t OPEN = 0x01; + static const uint8_t LISTEN = 0x02; + static const uint8_t CONNECT = 0x04; + static const uint8_t DISCON = 0x08; + static const uint8_t CLOSE = 0x10; + static const uint8_t SEND = 0x20; + static const uint8_t SEND_MAC = 0x21; + static const uint8_t SEND_KEEP = 0x22; + static const uint8_t RECV = 0x40; + }; */ -class SnIR { +class SnIR +{ public: - static const uint8_t SEND_OK = 0x10; - static const uint8_t TIMEOUT = 0x08; - static const uint8_t RECV = 0x04; - static const uint8_t DISCON = 0x02; - static const uint8_t CON = 0x01; + static const uint8_t SEND_OK = 0x10; + static const uint8_t TIMEOUT = 0x08; + static const uint8_t RECV = 0x04; + static const uint8_t DISCON = 0x02; + static const uint8_t CON = 0x01; }; -class SnSR { +class SnSR +{ public: - static const uint8_t CLOSED = 0x00; - static const uint8_t INIT = 0x13; - static const uint8_t LISTEN = 0x14; - static const uint8_t SYNSENT = 0x15; - static const uint8_t SYNRECV = 0x16; - static const uint8_t ESTABLISHED = 0x17; - static const uint8_t FIN_WAIT = 0x18; - static const uint8_t CLOSING = 0x1A; - static const uint8_t TIME_WAIT = 0x1B; - static const uint8_t CLOSE_WAIT = 0x1C; - static const uint8_t LAST_ACK = 0x1D; - static const uint8_t UDP = 0x22; - static const uint8_t IPRAW = 0x32; - static const uint8_t MACRAW = 0x42; - static const uint8_t PPPOE = 0x5F; + static const uint8_t CLOSED = 0x00; + static const uint8_t INIT = 0x13; + static const uint8_t LISTEN = 0x14; + static const uint8_t SYNSENT = 0x15; + static const uint8_t SYNRECV = 0x16; + static const uint8_t ESTABLISHED = 0x17; + static const uint8_t FIN_WAIT = 0x18; + static const uint8_t CLOSING = 0x1A; + static const uint8_t TIME_WAIT = 0x1B; + static const uint8_t CLOSE_WAIT = 0x1C; + static const uint8_t LAST_ACK = 0x1D; + static const uint8_t UDP = 0x22; + static const uint8_t IPRAW = 0x32; + static const uint8_t MACRAW = 0x42; + static const uint8_t PPPOE = 0x5F; }; -class IPPROTO { +class IPPROTO +{ public: - static const uint8_t IP = 0; - static const uint8_t ICMP = 1; - static const uint8_t IGMP = 2; - static const uint8_t GGP = 3; - static const uint8_t TCP = 6; - static const uint8_t PUP = 12; - static const uint8_t UDP = 17; - static const uint8_t IDP = 22; - static const uint8_t ND = 77; - static const uint8_t RAW = 255; + static const uint8_t IP = 0; + static const uint8_t ICMP = 1; + static const uint8_t IGMP = 2; + static const uint8_t GGP = 3; + static const uint8_t TCP = 6; + static const uint8_t PUP = 12; + static const uint8_t UDP = 17; + static const uint8_t IDP = 22; + static const uint8_t ND = 77; + static const uint8_t RAW = 255; }; -class W5100Class { +class W5100Class +{ public: - void init(); - - /** - * @brief This function is being used for copy the data form Receive buffer of the chip to application buffer. - * - * It calculate the actual physical address where one has to read - * the data from Receive buffer. Here also take care of the condition while it exceed - * the Rx memory uper-bound of socket. - */ - void read_data(SOCKET s, volatile uint16_t src, volatile uint8_t * dst, uint16_t len); - - /** - * @brief This function is being called by send() and sendto() function also. - * - * This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer - * register. User should read upper byte first and lower byte later to get proper value. - */ - void send_data_processing(SOCKET s, const uint8_t *data, uint16_t len); - /** - * @brief A copy of send_data_processing that uses the provided ptr for the - * write offset. Only needed for the "streaming" UDP API, where - * a single UDP packet is built up over a number of calls to - * send_data_processing_ptr, because TX_WR doesn't seem to get updated - * correctly in those scenarios - * @param ptr value to use in place of TX_WR. If 0, then the value is read - * in from TX_WR - * @return New value for ptr, to be used in the next call - */ -// FIXME Update documentation - void send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len); - - /** - * @brief This function is being called by recv() also. - * - * This function read the Rx read pointer register - * and after copy the data from receive buffer update the Rx write pointer register. - * User should read upper byte first and lower byte later to get proper value. - */ - void recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek = 0); - - inline void setGatewayIp(uint8_t *_addr); - inline void getGatewayIp(uint8_t *_addr); - - inline void setSubnetMask(uint8_t *_addr); - inline void getSubnetMask(uint8_t *_addr); - - inline void setMACAddress(uint8_t * addr); - inline void getMACAddress(uint8_t * addr); - - inline void setIPAddress(uint8_t * addr); - inline void getIPAddress(uint8_t * addr); - - inline void setRetransmissionTime(uint16_t timeout); - inline void setRetransmissionCount(uint8_t _retry); - - void execCmdSn(SOCKET s, SockCMD _cmd); - - uint16_t getTXFreeSize(SOCKET s); - uint16_t getRXReceivedSize(SOCKET s); - - - // W5100 Registers - // --------------- + void init(); + + /** + @brief This function is being used for copy the data form Receive buffer of the chip to application buffer. + + It calculate the actual physical address where one has to read + the data from Receive buffer. Here also take care of the condition while it exceed + the Rx memory uper-bound of socket. + */ + void read_data(SOCKET s, volatile uint16_t src, volatile uint8_t * dst, uint16_t len); + + /** + @brief This function is being called by send() and sendto() function also. + + This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer + register. User should read upper byte first and lower byte later to get proper value. + */ + void send_data_processing(SOCKET s, const uint8_t *data, uint16_t len); + /** + @brief A copy of send_data_processing that uses the provided ptr for the + write offset. Only needed for the "streaming" UDP API, where + a single UDP packet is built up over a number of calls to + send_data_processing_ptr, because TX_WR doesn't seem to get updated + correctly in those scenarios + @param ptr value to use in place of TX_WR. If 0, then the value is read + in from TX_WR + @return New value for ptr, to be used in the next call + */ + // FIXME Update documentation + void send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len); + + /** + @brief This function is being called by recv() also. + + This function read the Rx read pointer register + and after copy the data from receive buffer update the Rx write pointer register. + User should read upper byte first and lower byte later to get proper value. + */ + void recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek = 0); + + inline void setGatewayIp(uint8_t *_addr); + inline void getGatewayIp(uint8_t *_addr); + + inline void setSubnetMask(uint8_t *_addr); + inline void getSubnetMask(uint8_t *_addr); + + inline void setMACAddress(uint8_t * addr); + inline void getMACAddress(uint8_t * addr); + + inline void setIPAddress(uint8_t * addr); + inline void getIPAddress(uint8_t * addr); + + inline void setRetransmissionTime(uint16_t timeout); + inline void setRetransmissionCount(uint8_t _retry); + + void execCmdSn(SOCKET s, SockCMD _cmd); + + uint16_t getTXFreeSize(SOCKET s); + uint16_t getRXReceivedSize(SOCKET s); + + + // W5100 Registers + // --------------- private: - static uint8_t write(uint16_t _addr, uint8_t _data); - static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len); - static uint8_t read(uint16_t addr); - static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len); - + static uint8_t write(uint16_t _addr, uint8_t _data); + static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len); + static uint8_t read(uint16_t addr); + static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len); + #define __GP_REGISTER8(name, address) \ static inline void write##name(uint8_t _data) { \ write(address, _data); \ @@ -233,37 +239,37 @@ class W5100Class { } public: - __GP_REGISTER8 (MR, 0x0000); // Mode - __GP_REGISTER_N(GAR, 0x0001, 4); // Gateway IP address - __GP_REGISTER_N(SUBR, 0x0005, 4); // Subnet mask address - __GP_REGISTER_N(SHAR, 0x0009, 6); // Source MAC address - __GP_REGISTER_N(SIPR, 0x000F, 4); // Source IP address - __GP_REGISTER8 (IR, 0x0015); // Interrupt - __GP_REGISTER8 (IMR, 0x0016); // Interrupt Mask - __GP_REGISTER16(RTR, 0x0017); // Timeout address - __GP_REGISTER8 (RCR, 0x0019); // Retry count - __GP_REGISTER8 (RMSR, 0x001A); // Receive memory size - __GP_REGISTER8 (TMSR, 0x001B); // Transmit memory size - __GP_REGISTER8 (PATR, 0x001C); // Authentication type address in PPPoE mode - __GP_REGISTER8 (PTIMER, 0x0028); // PPP LCP Request Timer - __GP_REGISTER8 (PMAGIC, 0x0029); // PPP LCP Magic Number - __GP_REGISTER_N(UIPR, 0x002A, 4); // Unreachable IP address in UDP mode - __GP_REGISTER16(UPORT, 0x002E); // Unreachable Port address in UDP mode - + __GP_REGISTER8(MR, 0x0000); // Mode + __GP_REGISTER_N(GAR, 0x0001, 4); // Gateway IP address + __GP_REGISTER_N(SUBR, 0x0005, 4); // Subnet mask address + __GP_REGISTER_N(SHAR, 0x0009, 6); // Source MAC address + __GP_REGISTER_N(SIPR, 0x000F, 4); // Source IP address + __GP_REGISTER8(IR, 0x0015); // Interrupt + __GP_REGISTER8(IMR, 0x0016); // Interrupt Mask + __GP_REGISTER16(RTR, 0x0017); // Timeout address + __GP_REGISTER8(RCR, 0x0019); // Retry count + __GP_REGISTER8(RMSR, 0x001A); // Receive memory size + __GP_REGISTER8(TMSR, 0x001B); // Transmit memory size + __GP_REGISTER8(PATR, 0x001C); // Authentication type address in PPPoE mode + __GP_REGISTER8(PTIMER, 0x0028); // PPP LCP Request Timer + __GP_REGISTER8(PMAGIC, 0x0029); // PPP LCP Magic Number + __GP_REGISTER_N(UIPR, 0x002A, 4); // Unreachable IP address in UDP mode + __GP_REGISTER16(UPORT, 0x002E); // Unreachable Port address in UDP mode + #undef __GP_REGISTER8 #undef __GP_REGISTER16 #undef __GP_REGISTER_N - // W5100 Socket registers - // ---------------------- + // W5100 Socket registers + // ---------------------- private: - static inline uint8_t readSn(SOCKET _s, uint16_t _addr); - static inline uint8_t writeSn(SOCKET _s, uint16_t _addr, uint8_t _data); - static inline uint16_t readSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len); - static inline uint16_t writeSn(SOCKET _s, uint16_t _addr, const uint8_t *_buf, uint16_t len); + static inline uint8_t readSn(SOCKET _s, uint16_t _addr); + static inline uint8_t writeSn(SOCKET _s, uint16_t _addr, uint8_t _data); + static inline uint16_t readSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len); + static inline uint16_t writeSn(SOCKET _s, uint16_t _addr, const uint8_t *_buf, uint16_t len); - static const uint16_t CH_BASE = 0x0400; - static const uint16_t CH_SIZE = 0x0100; + static const uint16_t CH_BASE = 0x0400; + static const uint16_t CH_SIZE = 0x0100; #define __SOCKET_REGISTER8(name, address) \ static inline void write##name(SOCKET _s, uint8_t _data) { \ @@ -292,127 +298,186 @@ class W5100Class { static uint16_t read##name(SOCKET _s, uint8_t *_buff) { \ return readSn(_s, address, _buff, size); \ } - + public: - __SOCKET_REGISTER8(SnMR, 0x0000) // Mode - __SOCKET_REGISTER8(SnCR, 0x0001) // Command - __SOCKET_REGISTER8(SnIR, 0x0002) // Interrupt - __SOCKET_REGISTER8(SnSR, 0x0003) // Status - __SOCKET_REGISTER16(SnPORT, 0x0004) // Source Port - __SOCKET_REGISTER_N(SnDHAR, 0x0006, 6) // Destination Hardw Addr - __SOCKET_REGISTER_N(SnDIPR, 0x000C, 4) // Destination IP Addr - __SOCKET_REGISTER16(SnDPORT, 0x0010) // Destination Port - __SOCKET_REGISTER16(SnMSSR, 0x0012) // Max Segment Size - __SOCKET_REGISTER8(SnPROTO, 0x0014) // Protocol in IP RAW Mode - __SOCKET_REGISTER8(SnTOS, 0x0015) // IP TOS - __SOCKET_REGISTER8(SnTTL, 0x0016) // IP TTL - __SOCKET_REGISTER16(SnTX_FSR, 0x0020) // TX Free Size - __SOCKET_REGISTER16(SnTX_RD, 0x0022) // TX Read Pointer - __SOCKET_REGISTER16(SnTX_WR, 0x0024) // TX Write Pointer - __SOCKET_REGISTER16(SnRX_RSR, 0x0026) // RX Free Size - __SOCKET_REGISTER16(SnRX_RD, 0x0028) // RX Read Pointer - __SOCKET_REGISTER16(SnRX_WR, 0x002A) // RX Write Pointer (supported?) - + __SOCKET_REGISTER8(SnMR, 0x0000) // Mode + __SOCKET_REGISTER8(SnCR, 0x0001) // Command + __SOCKET_REGISTER8(SnIR, 0x0002) // Interrupt + __SOCKET_REGISTER8(SnSR, 0x0003) // Status + __SOCKET_REGISTER16(SnPORT, 0x0004) // Source Port + __SOCKET_REGISTER_N(SnDHAR, 0x0006, 6) // Destination Hardw Addr + __SOCKET_REGISTER_N(SnDIPR, 0x000C, 4) // Destination IP Addr + __SOCKET_REGISTER16(SnDPORT, 0x0010) // Destination Port + __SOCKET_REGISTER16(SnMSSR, 0x0012) // Max Segment Size + __SOCKET_REGISTER8(SnPROTO, 0x0014) // Protocol in IP RAW Mode + __SOCKET_REGISTER8(SnTOS, 0x0015) // IP TOS + __SOCKET_REGISTER8(SnTTL, 0x0016) // IP TTL + __SOCKET_REGISTER16(SnTX_FSR, 0x0020) // TX Free Size + __SOCKET_REGISTER16(SnTX_RD, 0x0022) // TX Read Pointer + __SOCKET_REGISTER16(SnTX_WR, 0x0024) // TX Write Pointer + __SOCKET_REGISTER16(SnRX_RSR, 0x0026) // RX Free Size + __SOCKET_REGISTER16(SnRX_RD, 0x0028) // RX Read Pointer + __SOCKET_REGISTER16(SnRX_WR, 0x002A) // RX Write Pointer (supported?) + #undef __SOCKET_REGISTER8 #undef __SOCKET_REGISTER16 #undef __SOCKET_REGISTER_N private: - static const uint8_t RST = 7; // Reset BIT + static const uint8_t RST = 7; // Reset BIT - static const int SOCKETS = 4; - static const uint16_t SMASK = 0x07FF; // Tx buffer MASK - static const uint16_t RMASK = 0x07FF; // Rx buffer MASK + static const int SOCKETS = 4; + static const uint16_t SMASK = 0x07FF; // Tx buffer MASK + static const uint16_t RMASK = 0x07FF; // Rx buffer MASK public: - static const uint16_t SSIZE = 2048; // Max Tx buffer size + static const uint16_t SSIZE = 2048; // Max Tx buffer size private: - static const uint16_t RSIZE = 2048; // Max Rx buffer size - uint16_t SBASE[SOCKETS]; // Tx buffer base address - uint16_t RBASE[SOCKETS]; // Rx buffer base address + static const uint16_t RSIZE = 2048; // Max Rx buffer size + uint16_t SBASE[SOCKETS]; // Tx buffer base address + uint16_t RBASE[SOCKETS]; // Rx buffer base address private: #if defined(ARDUINO_ARCH_AVR) #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) - inline static void initSS() { DDRB |= _BV(4); }; - inline static void setSS() { PORTB &= ~_BV(4); }; - inline static void resetSS() { PORTB |= _BV(4); }; + inline static void initSS() + { + DDRB |= _BV(4); + }; + inline static void setSS() + { + PORTB &= ~_BV(4); + }; + inline static void resetSS() + { + PORTB |= _BV(4); + }; #elif defined(__AVR_ATmega32U4__) - inline static void initSS() { DDRB |= _BV(6); }; - inline static void setSS() { PORTB &= ~_BV(6); }; - inline static void resetSS() { PORTB |= _BV(6); }; + inline static void initSS() + { + DDRB |= _BV(6); + }; + inline static void setSS() + { + PORTB &= ~_BV(6); + }; + inline static void resetSS() + { + PORTB |= _BV(6); + }; #elif defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB162__) - inline static void initSS() { DDRB |= _BV(0); }; - inline static void setSS() { PORTB &= ~_BV(0); }; - inline static void resetSS() { PORTB |= _BV(0); }; + inline static void initSS() + { + DDRB |= _BV(0); + }; + inline static void setSS() + { + PORTB &= ~_BV(0); + }; + inline static void resetSS() + { + PORTB |= _BV(0); + }; #else - inline static void initSS() { DDRB |= _BV(2); }; - inline static void setSS() { PORTB &= ~_BV(2); }; - inline static void resetSS() { PORTB |= _BV(2); }; + inline static void initSS() + { + DDRB |= _BV(2); + }; + inline static void setSS() + { + PORTB &= ~_BV(2); + }; + inline static void resetSS() + { + PORTB |= _BV(2); + }; #endif #elif defined(ESP8266) - inline static void initSS() { pinMode(SS, OUTPUT); }; - inline static void setSS() { GPOC = digitalPinToBitMask(SS); }; - inline static void resetSS() { GPOS = digitalPinToBitMask(SS); }; + inline static void initSS() + { + pinMode(SS, OUTPUT); + }; + inline static void setSS() + { + GPOC = digitalPinToBitMask(SS); + }; + inline static void resetSS() + { + GPOS = digitalPinToBitMask(SS); + }; #endif // ARDUINO_ARCH_AVR }; extern W5100Class W5100; -uint8_t W5100Class::readSn(SOCKET _s, uint16_t _addr) { - return read(CH_BASE + _s * CH_SIZE + _addr); +uint8_t W5100Class::readSn(SOCKET _s, uint16_t _addr) +{ + return read(CH_BASE + _s * CH_SIZE + _addr); } -uint8_t W5100Class::writeSn(SOCKET _s, uint16_t _addr, uint8_t _data) { - return write(CH_BASE + _s * CH_SIZE + _addr, _data); +uint8_t W5100Class::writeSn(SOCKET _s, uint16_t _addr, uint8_t _data) +{ + return write(CH_BASE + _s * CH_SIZE + _addr, _data); } -uint16_t W5100Class::readSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t _len) { - return read(CH_BASE + _s * CH_SIZE + _addr, _buf, _len); +uint16_t W5100Class::readSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t _len) +{ + return read(CH_BASE + _s * CH_SIZE + _addr, _buf, _len); } -uint16_t W5100Class::writeSn(SOCKET _s, uint16_t _addr, const uint8_t *_buf, uint16_t _len) { - return write(CH_BASE + _s * CH_SIZE + _addr, _buf, _len); +uint16_t W5100Class::writeSn(SOCKET _s, uint16_t _addr, const uint8_t *_buf, uint16_t _len) +{ + return write(CH_BASE + _s * CH_SIZE + _addr, _buf, _len); } -void W5100Class::getGatewayIp(uint8_t *_addr) { - readGAR(_addr); +void W5100Class::getGatewayIp(uint8_t *_addr) +{ + readGAR(_addr); } -void W5100Class::setGatewayIp(uint8_t *_addr) { - writeGAR(_addr); +void W5100Class::setGatewayIp(uint8_t *_addr) +{ + writeGAR(_addr); } -void W5100Class::getSubnetMask(uint8_t *_addr) { - readSUBR(_addr); +void W5100Class::getSubnetMask(uint8_t *_addr) +{ + readSUBR(_addr); } -void W5100Class::setSubnetMask(uint8_t *_addr) { - writeSUBR(_addr); +void W5100Class::setSubnetMask(uint8_t *_addr) +{ + writeSUBR(_addr); } -void W5100Class::getMACAddress(uint8_t *_addr) { - readSHAR(_addr); +void W5100Class::getMACAddress(uint8_t *_addr) +{ + readSHAR(_addr); } -void W5100Class::setMACAddress(uint8_t *_addr) { - writeSHAR(_addr); +void W5100Class::setMACAddress(uint8_t *_addr) +{ + writeSHAR(_addr); } -void W5100Class::getIPAddress(uint8_t *_addr) { - readSIPR(_addr); +void W5100Class::getIPAddress(uint8_t *_addr) +{ + readSIPR(_addr); } -void W5100Class::setIPAddress(uint8_t *_addr) { - writeSIPR(_addr); +void W5100Class::setIPAddress(uint8_t *_addr) +{ + writeSIPR(_addr); } -void W5100Class::setRetransmissionTime(uint16_t _timeout) { - writeRTR(_timeout); +void W5100Class::setRetransmissionTime(uint16_t _timeout) +{ + writeRTR(_timeout); } -void W5100Class::setRetransmissionCount(uint8_t _retry) { - writeRCR(_retry); +void W5100Class::setRetransmissionCount(uint8_t _retry) +{ + writeRCR(_retry); } #endif \ No newline at end of file diff --git a/libraries/GDBStub/src/internal/gdbstub-cfg.h b/libraries/GDBStub/src/internal/gdbstub-cfg.h index be40ab98f6..ad2cd914df 100644 --- a/libraries/GDBStub/src/internal/gdbstub-cfg.h +++ b/libraries/GDBStub/src/internal/gdbstub-cfg.h @@ -2,66 +2,66 @@ #define GDBSTUB_CFG_H /* -Enable this define if you're using the RTOS SDK. It will use a custom exception handler instead of the HAL -and do some other magic to make everything work and compile under FreeRTOS. + Enable this define if you're using the RTOS SDK. It will use a custom exception handler instead of the HAL + and do some other magic to make everything work and compile under FreeRTOS. */ #ifndef GDBSTUB_FREERTOS #define GDBSTUB_FREERTOS 0 #endif /* -Enable this to make the exception and debugging handlers switch to a private stack. This will use -up 1K of RAM, but may be useful if you're debugging stack or stack pointer corruption problems. It's -normally disabled because not many situations need it. If for some reason the GDB communication -stops when you run into an error in your code, try enabling this. + Enable this to make the exception and debugging handlers switch to a private stack. This will use + up 1K of RAM, but may be useful if you're debugging stack or stack pointer corruption problems. It's + normally disabled because not many situations need it. If for some reason the GDB communication + stops when you run into an error in your code, try enabling this. */ #ifndef GDBSTUB_USE_OWN_STACK #define GDBSTUB_USE_OWN_STACK 0 #endif /* -Enable this to cause the program to pause and wait for gdb to be connected when an exception is -encountered. + Enable this to cause the program to pause and wait for gdb to be connected when an exception is + encountered. */ #ifndef GDBSTUB_BREAK_ON_EXCEPTION #define GDBSTUB_BREAK_ON_EXCEPTION 1 #endif /* -If this is defined, gdbstub will break the program when you press Ctrl-C in gdb. it does this by -hooking the UART interrupt. Unfortunately, this means receiving stuff over the serial port won't -work for your program anymore. This will fail if your program sets an UART interrupt handler after -the gdbstub_init call. + If this is defined, gdbstub will break the program when you press Ctrl-C in gdb. it does this by + hooking the UART interrupt. Unfortunately, this means receiving stuff over the serial port won't + work for your program anymore. This will fail if your program sets an UART interrupt handler after + the gdbstub_init call. */ #ifndef GDBSTUB_CTRLC_BREAK #define GDBSTUB_CTRLC_BREAK 1 #endif /* -Enabling this will redirect console output to GDB. This basically means that printf/os_printf output -will show up in your gdb session, which is useful if you use gdb to do stuff. It also means that if -you use a normal terminal, you can't read the printfs anymore. + Enabling this will redirect console output to GDB. This basically means that printf/os_printf output + will show up in your gdb session, which is useful if you use gdb to do stuff. It also means that if + you use a normal terminal, you can't read the printfs anymore. */ #ifndef GDBSTUB_REDIRECT_CONSOLE_OUTPUT #define GDBSTUB_REDIRECT_CONSOLE_OUTPUT 1 #endif /* -Enable this if you want the GDB stub to wait for you to attach GDB before running. It does this by -breaking in the init routine; use the gdb 'c' command (continue) to start the program. + Enable this if you want the GDB stub to wait for you to attach GDB before running. It does this by + breaking in the init routine; use the gdb 'c' command (continue) to start the program. */ #ifndef GDBSTUB_BREAK_ON_INIT #define GDBSTUB_BREAK_ON_INIT 0 #endif /* -Function attributes for function types. -Gdbstub functions are placed in flash or IRAM using attributes, as defined here. The gdbinit function -(and related) can always be in flash, because it's called in the normal code flow. The rest of the -gdbstub functions can be in flash too, but only if there's no chance of them being called when the -flash somehow is disabled (eg during SPI operations or flash write/erase operations). If the routines -are called when the flash is disabled (eg due to a Ctrl-C at the wrong time), the ESP8266 will most -likely crash. + Function attributes for function types. + Gdbstub functions are placed in flash or IRAM using attributes, as defined here. The gdbinit function + (and related) can always be in flash, because it's called in the normal code flow. The rest of the + gdbstub functions can be in flash too, but only if there's no chance of them being called when the + flash somehow is disabled (eg during SPI operations or flash write/erase operations). If the routines + are called when the flash is disabled (eg due to a Ctrl-C at the wrong time), the ESP8266 will most + likely crash. */ #ifndef ATTR_GDBINIT #define ATTR_GDBINIT ICACHE_FLASH_ATTR diff --git a/libraries/GDBStub/src/internal/gdbstub.c b/libraries/GDBStub/src/internal/gdbstub.c index cd707d278c..2d4531b234 100644 --- a/libraries/GDBStub/src/internal/gdbstub.c +++ b/libraries/GDBStub/src/internal/gdbstub.c @@ -1,10 +1,10 @@ /****************************************************************************** - * Copyright 2015 Espressif Systems - * - * Description: A stub to make the ESP8266 debuggable by GDB over the serial - * port. - * - * License: ESPRESSIF MIT License + Copyright 2015 Espressif Systems + + Description: A stub to make the ESP8266 debuggable by GDB over the serial + port. + + License: ESPRESSIF MIT License *******************************************************************************/ #include @@ -22,34 +22,36 @@ //From xtruntime-frames.h -struct XTensa_exception_frame_s { - uint32_t pc; - uint32_t ps; - uint32_t sar; - uint32_t vpri; - uint32_t a[16]; //a0..a15 -//These are added manually by the exception code; the HAL doesn't set these on an exception. - uint32_t litbase; - uint32_t sr176; - uint32_t sr208; - //'reason' is abused for both the debug and the exception vector: if bit 7 is set, - //this contains an exception reason, otherwise it contains a debug vector bitmap. - uint32_t reason; +struct XTensa_exception_frame_s +{ + uint32_t pc; + uint32_t ps; + uint32_t sar; + uint32_t vpri; + uint32_t a[16]; //a0..a15 + //These are added manually by the exception code; the HAL doesn't set these on an exception. + uint32_t litbase; + uint32_t sr176; + uint32_t sr208; + //'reason' is abused for both the debug and the exception vector: if bit 7 is set, + //this contains an exception reason, otherwise it contains a debug vector bitmap. + uint32_t reason; }; #if GDBSTUB_FREERTOS -struct XTensa_rtos_int_frame_s { - uint32_t exitPtr; - uint32_t pc; - uint32_t ps; - uint32_t a[16]; - uint32_t sar; +struct XTensa_rtos_int_frame_s +{ + uint32_t exitPtr; + uint32_t pc; + uint32_t ps; + uint32_t a[16]; + uint32_t sar; }; /* -Definitions for FreeRTOS. This redefines some os_* functions to use their non-os* counterparts. It -also sets up some function pointers for ROM functions that aren't in the FreeRTOS ld files. + Definitions for FreeRTOS. This redefines some os_* functions to use their non-os* counterparts. It + also sets up some function pointers for ROM functions that aren't in the FreeRTOS ld files. */ #include #include @@ -63,8 +65,8 @@ static wdtfntype *ets_wdt_enable = (wdtfntype *)0x40002fa0; #else /* -OS-less SDK defines. Defines some headers for things that aren't in the include files, plus -the xthal stack frame struct. + OS-less SDK defines. Defines some headers for things that aren't in the include files, plus + the xthal stack frame struct. */ #include "osapi.h" #include "user_interface.h" @@ -101,29 +103,32 @@ static void (*uart_putc1_callback)(char) = NULL; static int32_t singleStepPs = -1; //Uart libs can reference these to see if gdb is attaching to them -bool gdbstub_has_putc1_control() { +bool gdbstub_has_putc1_control() +{ #if GDBSTUB_REDIRECT_CONSOLE_OUTPUT - return true; + return true; #else - return false; + return false; #endif } -bool gdbstub_has_uart_isr_control() { +bool gdbstub_has_uart_isr_control() +{ #if GDBSTUB_CTRLC_BREAK - return true; + return true; #else - return false; + return false; #endif } //Small function to feed the hardware watchdog. Needed to stop the ESP from resetting //due to a watchdog timeout while reading a command. -static void ATTR_GDBFN keepWDTalive() { - uint64_t *wdtval = (uint64_t*)0x3ff21048; - uint64_t *wdtovf = (uint64_t*)0x3ff210cc; - int *wdtctl = (int*)0x3ff210c8; - *wdtovf = *wdtval + 1600000; - *wdtctl |= 1 << 31; +static void ATTR_GDBFN keepWDTalive() +{ + uint64_t *wdtval = (uint64_t*)0x3ff21048; + uint64_t *wdtovf = (uint64_t*)0x3ff210cc; + int *wdtctl = (int*)0x3ff210c8; + *wdtovf = *wdtval + 1600000; + *wdtctl |= 1 << 31; } @@ -138,257 +143,354 @@ static void ATTR_GDBFN keepWDTalive() { //of the hex string, as far as the routine has read into it. Bits/4 indicates //the max amount of hex chars it gobbles up. Bits can be -1 to eat up as much //hex chars as possible. -static long gdbGetHexVal(unsigned char **ptr, int bits) { - int i; - int no; - unsigned int v = 0; - char c; - no = bits / 4; - if (bits == -1) - no = 64; - for (i = 0; i < no; i++) { - c = **ptr; - (*ptr)++; - if (c >= '0' && c <= '9') { - v <<= 4; - v |= (c-'0'); - } else if (c >= 'A' && c <= 'F') { - v <<= 4; - v |= (c-'A') + 10; - } else if (c >= 'a' && c <= 'f') { - v <<= 4; - v |= (c-'a') + 10; - } else if (c == '#') { - if (bits == -1) { - (*ptr)--; - return v; - } - return ST_ENDPACKET; - } else { - if (bits == -1) { - (*ptr)--; - return v; - } - return ST_ERR; - } - } - return v; +static long gdbGetHexVal(unsigned char **ptr, int bits) +{ + int i; + int no; + unsigned int v = 0; + char c; + no = bits / 4; + if (bits == -1) + { + no = 64; + } + for (i = 0; i < no; i++) + { + c = **ptr; + (*ptr)++; + if (c >= '0' && c <= '9') + { + v <<= 4; + v |= (c - '0'); + } + else if (c >= 'A' && c <= 'F') + { + v <<= 4; + v |= (c - 'A') + 10; + } + else if (c >= 'a' && c <= 'f') + { + v <<= 4; + v |= (c - 'a') + 10; + } + else if (c == '#') + { + if (bits == -1) + { + (*ptr)--; + return v; + } + return ST_ENDPACKET; + } + else + { + if (bits == -1) + { + (*ptr)--; + return v; + } + return ST_ERR; + } + } + return v; } //Swap an int into the form gdb wants it -static int iswap(int i) { - return ((i >> 24) & 0xff) - | (((i >> 16) & 0xff) << 8) - | (((i >> 8) & 0xff) << 16) - | (((i >> 0) & 0xff) << 24); +static int iswap(int i) +{ + return ((i >> 24) & 0xff) + | (((i >> 16) & 0xff) << 8) + | (((i >> 8) & 0xff) << 16) + | (((i >> 0) & 0xff) << 24); } //Read a byte from the ESP8266 memory. -static unsigned char readbyte(unsigned int p) { - if (p < 0x20000000 || p >= 0x60000000) return -1; - int *i = (int*)(p & ~3); - return *i >> ((p & 3) * 8); +static unsigned char readbyte(unsigned int p) +{ + if (p < 0x20000000 || p >= 0x60000000) + { + return -1; + } + int *i = (int*)(p & ~3); + return *i >> ((p & 3) * 8); } //Write a byte to the ESP8266 memory. -static void writeByte(unsigned int p, unsigned char d) { - if (p < 0x20000000 || p >= 0x60000000) return; - int *i = (int*)(p & ~3); - if ((p & 3) == 0) *i = (*i & 0xffffff00) | (d << 0); - else if ((p & 3) == 1) *i = (*i & 0xffff00ff) | (d << 8); - else if ((p & 3) == 2) *i = (*i & 0xff00ffff) | (d << 16); - else if ((p & 3) == 3) *i = (*i & 0x00ffffff) | (d << 24); +static void writeByte(unsigned int p, unsigned char d) +{ + if (p < 0x20000000 || p >= 0x60000000) + { + return; + } + int *i = (int*)(p & ~3); + if ((p & 3) == 0) + { + *i = (*i & 0xffffff00) | (d << 0); + } + else if ((p & 3) == 1) + { + *i = (*i & 0xffff00ff) | (d << 8); + } + else if ((p & 3) == 2) + { + *i = (*i & 0xff00ffff) | (d << 16); + } + else if ((p & 3) == 3) + { + *i = (*i & 0x00ffffff) | (d << 24); + } } //Returns 1 if it makes sense to write to addr p -static int validWrAddr(int p) { - return (p >= 0x3ff00000 && p < 0x40000000) - || (p >= 0x40100000 && p < 0x40140000) - || (p >= 0x60000000 && p < 0x60002000); +static int validWrAddr(int p) +{ + return (p >= 0x3ff00000 && p < 0x40000000) + || (p >= 0x40100000 && p < 0x40140000) + || (p >= 0x60000000 && p < 0x60002000); } -static inline bool ATTR_GDBFN gdbRxFifoIsEmpty() { - return ((READ_PERI_REG(UART_STATUS(0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT) == 0; +static inline bool ATTR_GDBFN gdbRxFifoIsEmpty() +{ + return ((READ_PERI_REG(UART_STATUS(0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT) == 0; } -static inline bool ATTR_GDBFN gdbTxFifoIsFull() { - return ((READ_PERI_REG(UART_STATUS(0)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) >= 126; +static inline bool ATTR_GDBFN gdbTxFifoIsFull() +{ + return ((READ_PERI_REG(UART_STATUS(0)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) >= 126; } //Receive a char from the uart. Uses polling and feeds the watchdog. -static inline int gdbRecvChar() { - while (gdbRxFifoIsEmpty()) { - keepWDTalive(); - } - return READ_PERI_REG(UART_FIFO(0)); +static inline int gdbRecvChar() +{ + while (gdbRxFifoIsEmpty()) + { + keepWDTalive(); + } + return READ_PERI_REG(UART_FIFO(0)); } //Send a char to the uart. -static void gdbSendChar(char c) { - while (gdbTxFifoIsFull()) - ; - WRITE_PERI_REG(UART_FIFO(0), c); +static void gdbSendChar(char c) +{ + while (gdbTxFifoIsFull()) + ; + WRITE_PERI_REG(UART_FIFO(0), c); } //Send the start of a packet; reset checksum calculation. -static void gdbPacketStart() { - chsum = 0; - gdbSendChar('$'); +static void gdbPacketStart() +{ + chsum = 0; + gdbSendChar('$'); } //Send a char as part of a packet -static void gdbPacketChar(char c) { - if (c == '#' || c == '$' || c == '}' || c == '*') { - gdbSendChar('}'); - chsum += '}'; - c ^= 0x20; - } - gdbSendChar(c); - chsum += c; +static void gdbPacketChar(char c) +{ + if (c == '#' || c == '$' || c == '}' || c == '*') + { + gdbSendChar('}'); + chsum += '}'; + c ^= 0x20; + } + gdbSendChar(c); + chsum += c; } //Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. -static void gdbPacketHex(int val, int bits) { - static const char hexChars[] = "0123456789abcdef"; - int i; - for (i = bits; i > 0; i -= 4) { - gdbPacketChar(hexChars[(val >> (i - 4)) & 0xf]); - } +static void gdbPacketHex(int val, int bits) +{ + static const char hexChars[] = "0123456789abcdef"; + int i; + for (i = bits; i > 0; i -= 4) + { + gdbPacketChar(hexChars[(val >> (i - 4)) & 0xf]); + } } //Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. -static void gdbPacketSwappedHexInt(int val) { - gdbPacketHex(iswap(val), 32); +static void gdbPacketSwappedHexInt(int val) +{ + gdbPacketHex(iswap(val), 32); } -static void gdbPacketXXXXInt() { - for (int i=0; i<8; i++) gdbPacketChar('x'); +static void gdbPacketXXXXInt() +{ + for (int i = 0; i < 8; i++) + { + gdbPacketChar('x'); + } } //Finish sending a packet. -static void gdbPacketEnd() { - gdbSendChar('#'); - //Ok to use packet version here since hex char can never be an - //excape-requiring character - gdbPacketHex(chsum, 8); +static void gdbPacketEnd() +{ + gdbSendChar('#'); + //Ok to use packet version here since hex char can never be an + //excape-requiring character + gdbPacketHex(chsum, 8); } // Send a complete packet containing str -static void gdbSendPacketStr(const char *c) { - gdbPacketStart(); - while (*c != 0) { - gdbPacketChar(*c); - c++; - } - gdbPacketEnd(); +static void gdbSendPacketStr(const char *c) +{ + gdbPacketStart(); + while (*c != 0) + { + gdbPacketChar(*c); + c++; + } + gdbPacketEnd(); } // Send a complete packet containing str as an output message -static inline void ATTR_GDBEXTERNFN gdbSendOutputPacketStr(const unsigned char* buf, size_t size) { - size_t i; - gdbPacketStart(); - gdbPacketChar('O'); - for (i = 0; i < size; i++) - gdbPacketHex(buf[i], 8); - gdbPacketEnd(); +static inline void ATTR_GDBEXTERNFN gdbSendOutputPacketStr(const unsigned char* buf, size_t size) +{ + size_t i; + gdbPacketStart(); + gdbPacketChar('O'); + for (i = 0; i < size; i++) + { + gdbPacketHex(buf[i], 8); + } + gdbPacketEnd(); } // Send a complete packet containing c as an output message -static inline void ATTR_GDBEXTERNFN gdbSendOutputPacketChar(unsigned char c) { - gdbPacketStart(); - gdbPacketChar('O'); - gdbPacketHex(c, 8); - gdbPacketEnd(); +static inline void ATTR_GDBEXTERNFN gdbSendOutputPacketChar(unsigned char c) +{ + gdbPacketStart(); + gdbPacketChar('O'); + gdbPacketHex(c, 8); + gdbPacketEnd(); } -static long gdbGetSwappedHexInt(unsigned char **ptr) { - return iswap(gdbGetHexVal(ptr, 32)); +static long gdbGetSwappedHexInt(unsigned char **ptr) +{ + return iswap(gdbGetHexVal(ptr, 32)); } //Send the reason execution is stopped to GDB. -static void sendReason() { - static const char exceptionSignal[] = {4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7}; +static void sendReason() +{ + static const char exceptionSignal[] = {4, 31, 11, 11, 2, 6, 8, 0, 6, 7, 0, 0, 7, 7, 7, 7}; #if 0 - char *reason=""; //default + char *reason = ""; //default #endif - //exception-to-signal mapping - size_t i; - gdbPacketStart(); - gdbPacketChar('T'); - if (gdbstub_savedRegs.reason == 0xff) { - gdbPacketHex(2, 8); //sigint - } else if (gdbstub_savedRegs.reason & 0x80) { - //We stopped because of an exception. Convert exception code to a signal number and send it. - i = gdbstub_savedRegs.reason & 0x7f; - if (i < sizeof(exceptionSignal)) - gdbPacketHex(exceptionSignal[i], 8); - else - gdbPacketHex(11, 8); - } else { - //We stopped because of a debugging exception. - gdbPacketHex(5, 8); //sigtrap -//Current Xtensa GDB versions don't seem to request this, so let's leave it off. + //exception-to-signal mapping + size_t i; + gdbPacketStart(); + gdbPacketChar('T'); + if (gdbstub_savedRegs.reason == 0xff) + { + gdbPacketHex(2, 8); //sigint + } + else if (gdbstub_savedRegs.reason & 0x80) + { + //We stopped because of an exception. Convert exception code to a signal number and send it. + i = gdbstub_savedRegs.reason & 0x7f; + if (i < sizeof(exceptionSignal)) + { + gdbPacketHex(exceptionSignal[i], 8); + } + else + { + gdbPacketHex(11, 8); + } + } + else + { + //We stopped because of a debugging exception. + gdbPacketHex(5, 8); //sigtrap + //Current Xtensa GDB versions don't seem to request this, so let's leave it off. #if 0 - if (gdbstub_savedRegs.reason&(1<<0)) reason="break"; - if (gdbstub_savedRegs.reason&(1<<1)) reason="hwbreak"; - if (gdbstub_savedRegs.reason&(1<<2)) reason="watch"; - if (gdbstub_savedRegs.reason&(1<<3)) reason="swbreak"; - if (gdbstub_savedRegs.reason&(1<<4)) reason="swbreak"; - - gdbPacketStr(reason); - gdbPacketChar(':'); - //ToDo: watch: send address + if (gdbstub_savedRegs.reason & (1 << 0)) + { + reason = "break"; + } + if (gdbstub_savedRegs.reason & (1 << 1)) + { + reason = "hwbreak"; + } + if (gdbstub_savedRegs.reason & (1 << 2)) + { + reason = "watch"; + } + if (gdbstub_savedRegs.reason & (1 << 3)) + { + reason = "swbreak"; + } + if (gdbstub_savedRegs.reason & (1 << 4)) + { + reason = "swbreak"; + } + + gdbPacketStr(reason); + gdbPacketChar(':'); + //ToDo: watch: send address #endif - } - gdbPacketEnd(); + } + gdbPacketEnd(); } -static inline void ATTR_GDBFN gdbSendPacketOK() { - gdbSendPacketStr("OK"); +static inline void ATTR_GDBFN gdbSendPacketOK() +{ + gdbSendPacketStr("OK"); } -static inline void ATTR_GDBFN gdbSendPacketE01() { - gdbSendPacketStr("E01"); +static inline void ATTR_GDBFN gdbSendPacketE01() +{ + gdbSendPacketStr("E01"); } -static inline void ATTR_GDBFN gdbSendEmptyPacket() { - gdbPacketStart(); - gdbPacketEnd(); +static inline void ATTR_GDBFN gdbSendEmptyPacket() +{ + gdbPacketStart(); + gdbPacketEnd(); } -void ATTR_GDBEXTERNFN gdbstub_write_char(char c) { - if (gdb_attached) { - ETS_UART_INTR_DISABLE(); - gdbSendOutputPacketChar(c); - ETS_UART_INTR_ENABLE(); - } else { - gdbSendChar(c); - } +void ATTR_GDBEXTERNFN gdbstub_write_char(char c) +{ + if (gdb_attached) + { + ETS_UART_INTR_DISABLE(); + gdbSendOutputPacketChar(c); + ETS_UART_INTR_ENABLE(); + } + else + { + gdbSendChar(c); + } } -void ATTR_GDBEXTERNFN gdbstub_write(const char* buf, size_t size) { - size_t i; - if (gdb_attached) { - ETS_UART_INTR_DISABLE(); - gdbSendOutputPacketStr((const unsigned char *)buf, size); - ETS_UART_INTR_ENABLE(); - } else { - for (i = 0; i < size; i++) { - gdbSendChar(buf[i]); - } - } +void ATTR_GDBEXTERNFN gdbstub_write(const char* buf, size_t size) +{ + size_t i; + if (gdb_attached) + { + ETS_UART_INTR_DISABLE(); + gdbSendOutputPacketStr((const unsigned char *)buf, size); + ETS_UART_INTR_ENABLE(); + } + else + { + for (i = 0; i < size; i++) + { + gdbSendChar(buf[i]); + } + } } /* -Register file in the format lx106 gdb port expects it. -Inspired by gdb/regformats/reg-xtensa.dat from -https://github.com/jcmvbkbc/crosstool-NG/blob/lx106-g%2B%2B/overlays/xtensa_lx106.tar -As decoded by Cesanta. + Register file in the format lx106 gdb port expects it. + Inspired by gdb/regformats/reg-xtensa.dat from + https://github.com/jcmvbkbc/crosstool-NG/blob/lx106-g%2B%2B/overlays/xtensa_lx106.tar + As decoded by Cesanta. -struct regfile { + struct regfile { uint32_t a[16]; uint32_t pc; uint32_t sar; @@ -396,132 +498,236 @@ struct regfile { uint32_t sr176; uint32_t sr208; uint32_t ps; -}; + }; */ //Handle a command as received from GDB. -static inline int gdbHandleCommand() { - //Handle a command - int i, j, k; - unsigned char *data = cmd + 1; - if (cmd[0]=='g') { //send all registers to gdb - gdbPacketStart(); - gdbPacketSwappedHexInt(gdbstub_savedRegs.pc); - for (int i=1; i<=35; i++) gdbPacketXXXXInt(); - gdbPacketSwappedHexInt(gdbstub_savedRegs.sar); - gdbPacketSwappedHexInt(gdbstub_savedRegs.litbase); - for (int i=38; i<=39; i++) gdbPacketXXXXInt(); - gdbPacketSwappedHexInt(gdbstub_savedRegs.sr176); - for (int i=41; i<=41; i++) gdbPacketXXXXInt(); - gdbPacketSwappedHexInt(gdbstub_savedRegs.ps); - for (int i=43; i<=96; i++) gdbPacketXXXXInt(); - for (i=0; i<16; i++) gdbPacketSwappedHexInt(gdbstub_savedRegs.a[i]); - gdbPacketEnd(); - } else if (cmd[0]=='G') { //receive content for all registers from gdb - gdbstub_savedRegs.pc=gdbGetSwappedHexInt(&data); - for (int i=1; i<=35; i++) gdbGetHexVal(&data, 32); - gdbstub_savedRegs.sar=gdbGetSwappedHexInt(&data); - gdbstub_savedRegs.litbase=gdbGetSwappedHexInt(&data); - for (int i=38; i<=39; i++) gdbGetHexVal(&data, 32); - gdbstub_savedRegs.sr176=gdbGetSwappedHexInt(&data); - for (int i=41; i<=41; i++) gdbGetHexVal(&data, 32); - gdbstub_savedRegs.ps=gdbGetSwappedHexInt(&data); - for (int i=43; i<=96; i++) gdbGetHexVal(&data, 32); - for (i=0; i<16; i++) gdbstub_savedRegs.a[i]=gdbGetSwappedHexInt(&data); - gdbSendPacketOK(); - } else if ((cmd[0] | 0x20) == 'm') { //read/write memory to gdb - i = gdbGetHexVal(&data, -1); //addr - data++; - j = gdbGetHexVal(&data, -1); //length - if (cmd[0] == 'm') { //read memory to gdb - gdbPacketStart(); - for (k = 0; k < j; k++) { - gdbPacketHex(readbyte(i++), 8); - } - gdbPacketEnd(); - } else { //write memory from gdb - if (validWrAddr(i) && validWrAddr(i + j)) { - data++; //skip : - for (k = 0; k < j; k++, i++) { - writeByte(i, gdbGetHexVal(&data, 8)); - } - //Make sure caches are up-to-date. Procedure according to Xtensa ISA document, ISYNC inst desc. - asm volatile("ISYNC\nISYNC\n"); - gdbSendPacketOK(); - } else { - //Trying to do a software breakpoint on a flash proc, perhaps? - gdbSendPacketE01(); - } - } - } else if (cmd[0] == '?') { //Reply with stop reason - sendReason(); - } else if (cmd[0] == 'c') { //continue execution - return ST_CONT; - } else if (cmd[0] == 's') { //single-step instruction - //Single-stepping can go wrong if an interrupt is pending, especially when it is e.g. a task switch: - //the ICOUNT register will overflow in the task switch code. That is why we disable interupts when - //doing single-instruction stepping. - singleStepPs=gdbstub_savedRegs.ps; - gdbstub_savedRegs.ps=(gdbstub_savedRegs.ps & ~0xf) | (XCHAL_DEBUGLEVEL - 1); - gdbstub_icount_ena_single_step(); - return ST_CONT; - } else if (cmd[0] == 'D') { //detach - gdbSendPacketOK(); - return ST_DETACH; - } else if (cmd[0] == 'k') { //kill - system_restart_core(); - } else if (cmd[0] == 'q') { //Extended query - if (os_strncmp((char*)&cmd[1], "Supported", 9) == 0) { //Capabilities query - gdbSendPacketStr("swbreak+;hwbreak+;PacketSize=FF"); //PacketSize is in hex - } else if (os_strncmp((char*)&cmd[1], "Attached", 8) == 0) { - //Let gdb know that it is attaching to a running program - //In general that just means it detaches instead of killing when it exits - gdbSendPacketStr("1"); - } else { - //We don't support other queries. - gdbSendEmptyPacket(); - } - // case insensitive compare matches 'Z' or 'z' - } else if ((cmd[0] | 0x20) == 'z' && cmd[1] >= '1' && cmd[2] <= '4') { //hardware break/watchpoint - int result; - data += 2; //skip 'x,' - i = gdbGetHexVal(&data, -1); - data++; //skip ',' - j = gdbGetHexVal(&data, -1); - if (cmd[0] == 'Z') { //Set hardware break/watchpoint - if (cmd[1] == '1') { //Set breakpoint - result = gdbstub_set_hw_breakpoint(i, j); - } else { //Set watchpoint - int access; - unsigned int mask = 0; - if (cmd[1] == '2') access = 2; //write - if (cmd[1] == '3') access = 1; //read - if (cmd[1] == '4') access = 3; //access - if (j == 1) mask = 0x3F; - if (j == 2) mask = 0x3E; - if (j == 4) mask = 0x3C; - if (j == 8) mask = 0x38; - if (j == 16) mask = 0x30; - if (j == 32) mask = 0x20; - result = mask != 0 && gdbstub_set_hw_watchpoint(i, mask, access); - } - } else { //Clear hardware break/watchpoint - if (cmd[1] == '1') { //hardware breakpoint - result = gdbstub_del_hw_breakpoint(i); - } else { //hardware watchpoint - result = gdbstub_del_hw_watchpoint(i); - } - } - if (result) { - gdbSendPacketOK(); - } else { - gdbSendPacketE01(); - } - } else { - //We don't recognize or support whatever GDB just sent us. - gdbSendEmptyPacket(); - } - return ST_OK; +static inline int gdbHandleCommand() +{ + //Handle a command + int i, j, k; + unsigned char *data = cmd + 1; + if (cmd[0] == 'g') //send all registers to gdb + { + gdbPacketStart(); + gdbPacketSwappedHexInt(gdbstub_savedRegs.pc); + for (int i = 1; i <= 35; i++) + { + gdbPacketXXXXInt(); + } + gdbPacketSwappedHexInt(gdbstub_savedRegs.sar); + gdbPacketSwappedHexInt(gdbstub_savedRegs.litbase); + for (int i = 38; i <= 39; i++) + { + gdbPacketXXXXInt(); + } + gdbPacketSwappedHexInt(gdbstub_savedRegs.sr176); + for (int i = 41; i <= 41; i++) + { + gdbPacketXXXXInt(); + } + gdbPacketSwappedHexInt(gdbstub_savedRegs.ps); + for (int i = 43; i <= 96; i++) + { + gdbPacketXXXXInt(); + } + for (i = 0; i < 16; i++) + { + gdbPacketSwappedHexInt(gdbstub_savedRegs.a[i]); + } + gdbPacketEnd(); + } + else if (cmd[0] == 'G') //receive content for all registers from gdb + { + gdbstub_savedRegs.pc = gdbGetSwappedHexInt(&data); + for (int i = 1; i <= 35; i++) + { + gdbGetHexVal(&data, 32); + } + gdbstub_savedRegs.sar = gdbGetSwappedHexInt(&data); + gdbstub_savedRegs.litbase = gdbGetSwappedHexInt(&data); + for (int i = 38; i <= 39; i++) + { + gdbGetHexVal(&data, 32); + } + gdbstub_savedRegs.sr176 = gdbGetSwappedHexInt(&data); + for (int i = 41; i <= 41; i++) + { + gdbGetHexVal(&data, 32); + } + gdbstub_savedRegs.ps = gdbGetSwappedHexInt(&data); + for (int i = 43; i <= 96; i++) + { + gdbGetHexVal(&data, 32); + } + for (i = 0; i < 16; i++) + { + gdbstub_savedRegs.a[i] = gdbGetSwappedHexInt(&data); + } + gdbSendPacketOK(); + } + else if ((cmd[0] | 0x20) == 'm') //read/write memory to gdb + { + i = gdbGetHexVal(&data, -1); //addr + data++; + j = gdbGetHexVal(&data, -1); //length + if (cmd[0] == 'm') //read memory to gdb + { + gdbPacketStart(); + for (k = 0; k < j; k++) + { + gdbPacketHex(readbyte(i++), 8); + } + gdbPacketEnd(); + } + else //write memory from gdb + { + if (validWrAddr(i) && validWrAddr(i + j)) + { + data++; //skip : + for (k = 0; k < j; k++, i++) + { + writeByte(i, gdbGetHexVal(&data, 8)); + } + //Make sure caches are up-to-date. Procedure according to Xtensa ISA document, ISYNC inst desc. + asm volatile("ISYNC\nISYNC\n"); + gdbSendPacketOK(); + } + else + { + //Trying to do a software breakpoint on a flash proc, perhaps? + gdbSendPacketE01(); + } + } + } + else if (cmd[0] == '?') //Reply with stop reason + { + sendReason(); + } + else if (cmd[0] == 'c') //continue execution + { + return ST_CONT; + } + else if (cmd[0] == 's') //single-step instruction + { + //Single-stepping can go wrong if an interrupt is pending, especially when it is e.g. a task switch: + //the ICOUNT register will overflow in the task switch code. That is why we disable interupts when + //doing single-instruction stepping. + singleStepPs = gdbstub_savedRegs.ps; + gdbstub_savedRegs.ps = (gdbstub_savedRegs.ps & ~0xf) | (XCHAL_DEBUGLEVEL - 1); + gdbstub_icount_ena_single_step(); + return ST_CONT; + } + else if (cmd[0] == 'D') //detach + { + gdbSendPacketOK(); + return ST_DETACH; + } + else if (cmd[0] == 'k') //kill + { + system_restart_core(); + } + else if (cmd[0] == 'q') //Extended query + { + if (os_strncmp((char*)&cmd[1], "Supported", 9) == 0) //Capabilities query + { + gdbSendPacketStr("swbreak+;hwbreak+;PacketSize=FF"); //PacketSize is in hex + } + else if (os_strncmp((char*)&cmd[1], "Attached", 8) == 0) + { + //Let gdb know that it is attaching to a running program + //In general that just means it detaches instead of killing when it exits + gdbSendPacketStr("1"); + } + else + { + //We don't support other queries. + gdbSendEmptyPacket(); + } + // case insensitive compare matches 'Z' or 'z' + } + else if ((cmd[0] | 0x20) == 'z' && cmd[1] >= '1' && cmd[2] <= '4') //hardware break/watchpoint + { + int result; + data += 2; //skip 'x,' + i = gdbGetHexVal(&data, -1); + data++; //skip ',' + j = gdbGetHexVal(&data, -1); + if (cmd[0] == 'Z') //Set hardware break/watchpoint + { + if (cmd[1] == '1') //Set breakpoint + { + result = gdbstub_set_hw_breakpoint(i, j); + } + else //Set watchpoint + { + int access; + unsigned int mask = 0; + if (cmd[1] == '2') + { + access = 2; //write + } + if (cmd[1] == '3') + { + access = 1; //read + } + if (cmd[1] == '4') + { + access = 3; //access + } + if (j == 1) + { + mask = 0x3F; + } + if (j == 2) + { + mask = 0x3E; + } + if (j == 4) + { + mask = 0x3C; + } + if (j == 8) + { + mask = 0x38; + } + if (j == 16) + { + mask = 0x30; + } + if (j == 32) + { + mask = 0x20; + } + result = mask != 0 && gdbstub_set_hw_watchpoint(i, mask, access); + } + } + else //Clear hardware break/watchpoint + { + if (cmd[1] == '1') //hardware breakpoint + { + result = gdbstub_del_hw_breakpoint(i); + } + else //hardware watchpoint + { + result = gdbstub_del_hw_watchpoint(i); + } + } + if (result) + { + gdbSendPacketOK(); + } + else + { + gdbSendPacketE01(); + } + } + else + { + //We don't recognize or support whatever GDB just sent us. + gdbSendEmptyPacket(); + } + return ST_OK; } //Lower layer: grab a command packet and check the checksum @@ -537,144 +743,174 @@ static inline int gdbHandleCommand() { //It is not necessary for gdb to be attached for it to be paused //For example, during an exception break, the program is // paused but gdb might not be attached yet -static int gdbReadCommand() { - unsigned char chsum; - unsigned char sentchs[2]; - size_t p; - unsigned char c; - unsigned char *ptr; - int result; - ETS_UART_INTR_DISABLE(); - ets_wdt_disable(); - sendReason(); - while (true) { +static int gdbReadCommand() +{ + unsigned char chsum; + unsigned char sentchs[2]; + size_t p; + unsigned char c; + unsigned char *ptr; + int result; + ETS_UART_INTR_DISABLE(); + ets_wdt_disable(); + sendReason(); + while (true) + { gdbReadCommand_start: - while (gdbRecvChar() != '$') - ; + while (gdbRecvChar() != '$') + ; gdbReadCommand_packetBegin: - chsum = 0; - p = 0; - while ((c = gdbRecvChar()) != '#') { //end of packet, checksum follows - if (c == '$') { - //Wut, restart packet? - goto gdbReadCommand_packetBegin; - } - if (c == '}') { //escape the next char - c = gdbRecvChar() ^ 0x20; - } - chsum += c; - cmd[p++] = c; - if (p >= PBUFLEN) { - //Received more than the size of the command buffer - goto gdbReadCommand_start; - } - } - cmd[p] = 0; - sentchs[0] = gdbRecvChar(); - sentchs[1] = gdbRecvChar(); - ptr = &sentchs[0]; - if (gdbGetHexVal(&ptr, 8) == chsum) { - gdb_attached = true; - gdbSendChar('+'); - result = gdbHandleCommand(); - if (result != ST_OK) { - break; - } - } else { - gdbSendChar('-'); - } - } - if (result == ST_DETACH) { - gdb_attached = false; - } - ets_wdt_enable(); - ETS_UART_INTR_ENABLE(); - return result; + chsum = 0; + p = 0; + while ((c = gdbRecvChar()) != '#') //end of packet, checksum follows + { + if (c == '$') + { + //Wut, restart packet? + goto gdbReadCommand_packetBegin; + } + if (c == '}') //escape the next char + { + c = gdbRecvChar() ^ 0x20; + } + chsum += c; + cmd[p++] = c; + if (p >= PBUFLEN) + { + //Received more than the size of the command buffer + goto gdbReadCommand_start; + } + } + cmd[p] = 0; + sentchs[0] = gdbRecvChar(); + sentchs[1] = gdbRecvChar(); + ptr = &sentchs[0]; + if (gdbGetHexVal(&ptr, 8) == chsum) + { + gdb_attached = true; + gdbSendChar('+'); + result = gdbHandleCommand(); + if (result != ST_OK) + { + break; + } + } + else + { + gdbSendChar('-'); + } + } + if (result == ST_DETACH) + { + gdb_attached = false; + } + ets_wdt_enable(); + ETS_UART_INTR_ENABLE(); + return result; } //Get the value of one of the A registers -static unsigned int ATTR_GDBFN getaregval(int reg) { - return gdbstub_savedRegs.a[reg]; +static unsigned int ATTR_GDBFN getaregval(int reg) +{ + return gdbstub_savedRegs.a[reg]; } //Set the value of one of the A registers -static inline void ATTR_GDBFN setaregval(int reg, unsigned int val) { - // os_printf("%x -> %x\n", val, reg); - gdbstub_savedRegs.a[reg] = val; +static inline void ATTR_GDBFN setaregval(int reg, unsigned int val) +{ + // os_printf("%x -> %x\n", val, reg); + gdbstub_savedRegs.a[reg] = val; } //Emulate the l32i/s32i instruction we're stopped at. -static inline void emulLdSt() { - unsigned char i0 = readbyte(gdbstub_savedRegs.pc); - unsigned char i1 = readbyte(gdbstub_savedRegs.pc + 1); - unsigned char i2; - int *p; - - if ((i0 & 0xf) == 2 && (i1 & 0xb0) == 0x20) { - //l32i or s32i - i2 = readbyte(gdbstub_savedRegs.pc + 2); - p = (int*)getaregval(i1 & 0xf) + (i2 * 4); - i0 >>= 4; - if ((i1 & 0xf0) == 0x20) { //l32i - setaregval(i0, *p); - } else { //s32i - *p = getaregval(i0); - } - gdbstub_savedRegs.pc += 3; - } else if ((i0 & 0xe) == 0x8) { - //l32i.n or s32i.n - p = (int*)getaregval(i1 & 0xf) + ((i1 >> 4) * 4); - if ((i0 & 0xf) == 0x8) { //l32i.n - setaregval(i0 >> 4, *p); - } else { - *p = getaregval(i0 >> 4); - } - gdbstub_savedRegs.pc += 2; - // } else { - // os_printf("GDBSTUB: No l32i/s32i instruction: %x %x. Huh?", i1, i0); - } +static inline void emulLdSt() +{ + unsigned char i0 = readbyte(gdbstub_savedRegs.pc); + unsigned char i1 = readbyte(gdbstub_savedRegs.pc + 1); + unsigned char i2; + int *p; + + if ((i0 & 0xf) == 2 && (i1 & 0xb0) == 0x20) + { + //l32i or s32i + i2 = readbyte(gdbstub_savedRegs.pc + 2); + p = (int*)getaregval(i1 & 0xf) + (i2 * 4); + i0 >>= 4; + if ((i1 & 0xf0) == 0x20) //l32i + { + setaregval(i0, *p); + } + else //s32i + { + *p = getaregval(i0); + } + gdbstub_savedRegs.pc += 3; + } + else if ((i0 & 0xe) == 0x8) + { + //l32i.n or s32i.n + p = (int*)getaregval(i1 & 0xf) + ((i1 >> 4) * 4); + if ((i0 & 0xf) == 0x8) //l32i.n + { + setaregval(i0 >> 4, *p); + } + else + { + *p = getaregval(i0 >> 4); + } + gdbstub_savedRegs.pc += 2; + // } else { + // os_printf("GDBSTUB: No l32i/s32i instruction: %x %x. Huh?", i1, i0); + } } //We just caught a debug exception and need to handle it. This is called from an assembly //routine in gdbstub-entry.S static void gdbstub_handle_debug_exception_flash(); -void ATTR_GDBFN gdbstub_handle_debug_exception() { - Cache_Read_Enable_New(); - gdbstub_handle_debug_exception_flash(); -} - -static void __attribute__((noinline)) gdbstub_handle_debug_exception_flash() { - if (singleStepPs != -1) { - //We come here after single-stepping an instruction. Interrupts are disabled - //for the single step. Re-enable them here. - gdbstub_savedRegs.ps = (gdbstub_savedRegs.ps & ~0xf) | (singleStepPs & 0xf); - singleStepPs =- 1; - } - - gdbReadCommand(); - if ((gdbstub_savedRegs.reason & 0x80) == 0) { //Watchpoint/BREAK/BREAK.N - if ((gdbstub_savedRegs.reason & 0x4) != 0) { - //We stopped due to a watchpoint. We can't re-execute the current instruction - //because it will happily re-trigger the same watchpoint, so we emulate it - //while we're still in debugger space. - emulLdSt(); - } else if ((((gdbstub_savedRegs.reason & 0x8) != 0) - //We stopped due to a BREAK instruction. Skip over it. - //Check the instruction first; gdb may have replaced it with the original instruction - //if it's one of the breakpoints it set. - && readbyte(gdbstub_savedRegs.pc + 2) == 0 - && (readbyte(gdbstub_savedRegs.pc + 1) & 0xf0) == 0x40 - && (readbyte(gdbstub_savedRegs.pc) & 0x0f) == 0x00) - || (((gdbstub_savedRegs.reason & 0x10) != 0) - //We stopped due to a BREAK.N instruction. Skip over it, after making sure the instruction - //actually is a BREAK.N - && (readbyte(gdbstub_savedRegs.pc + 1) & 0xf0) == 0xf0 - && readbyte(gdbstub_savedRegs.pc) == 0x2d)) { - gdbstub_savedRegs.pc += 3; - } - } +void ATTR_GDBFN gdbstub_handle_debug_exception() +{ + Cache_Read_Enable_New(); + gdbstub_handle_debug_exception_flash(); +} + +static void __attribute__((noinline)) gdbstub_handle_debug_exception_flash() +{ + if (singleStepPs != -1) + { + //We come here after single-stepping an instruction. Interrupts are disabled + //for the single step. Re-enable them here. + gdbstub_savedRegs.ps = (gdbstub_savedRegs.ps & ~0xf) | (singleStepPs & 0xf); + singleStepPs = - 1; + } + + gdbReadCommand(); + if ((gdbstub_savedRegs.reason & 0x80) == 0) //Watchpoint/BREAK/BREAK.N + { + if ((gdbstub_savedRegs.reason & 0x4) != 0) + { + //We stopped due to a watchpoint. We can't re-execute the current instruction + //because it will happily re-trigger the same watchpoint, so we emulate it + //while we're still in debugger space. + emulLdSt(); + } + else if ((((gdbstub_savedRegs.reason & 0x8) != 0) + //We stopped due to a BREAK instruction. Skip over it. + //Check the instruction first; gdb may have replaced it with the original instruction + //if it's one of the breakpoints it set. + && readbyte(gdbstub_savedRegs.pc + 2) == 0 + && (readbyte(gdbstub_savedRegs.pc + 1) & 0xf0) == 0x40 + && (readbyte(gdbstub_savedRegs.pc) & 0x0f) == 0x00) + || (((gdbstub_savedRegs.reason & 0x10) != 0) + //We stopped due to a BREAK.N instruction. Skip over it, after making sure the instruction + //actually is a BREAK.N + && (readbyte(gdbstub_savedRegs.pc + 1) & 0xf0) == 0xf0 + && readbyte(gdbstub_savedRegs.pc) == 0x2d)) + { + gdbstub_savedRegs.pc += 3; + } + } } @@ -682,21 +918,22 @@ static void __attribute__((noinline)) gdbstub_handle_debug_exception_flash() { #if GDBSTUB_BREAK_ON_EXCEPTION || GDBSTUB_CTRLC_BREAK #if !GDBSTUB_FREERTOS -static inline int gdbReadCommandWithFrame(void* frame) { - //Copy registers the Xtensa HAL did save to gdbstub_savedRegs - os_memcpy(&gdbstub_savedRegs, frame, 5 * 4); - os_memcpy(&gdbstub_savedRegs.a[2], ((uint32_t*)frame) + 5, 14 * 4); - //Credits go to Cesanta for this trick. A1 seems to be destroyed, but because it - //has a fixed offset from the address of the passed frame, we can recover it. - gdbstub_savedRegs.a[1] = (uint32_t)frame + EXCEPTION_GDB_SP_OFFSET; - - int result = gdbReadCommand(); - - //Copy any changed registers back to the frame the Xtensa HAL uses. - os_memcpy(frame, &gdbstub_savedRegs, 5 * 4); - os_memcpy(((uint32_t*)frame) + 5, &gdbstub_savedRegs.a[2], 14 * 4); - - return result; +static inline int gdbReadCommandWithFrame(void* frame) +{ + //Copy registers the Xtensa HAL did save to gdbstub_savedRegs + os_memcpy(&gdbstub_savedRegs, frame, 5 * 4); + os_memcpy(&gdbstub_savedRegs.a[2], ((uint32_t*)frame) + 5, 14 * 4); + //Credits go to Cesanta for this trick. A1 seems to be destroyed, but because it + //has a fixed offset from the address of the passed frame, we can recover it. + gdbstub_savedRegs.a[1] = (uint32_t)frame + EXCEPTION_GDB_SP_OFFSET; + + int result = gdbReadCommand(); + + //Copy any changed registers back to the frame the Xtensa HAL uses. + os_memcpy(frame, &gdbstub_savedRegs, 5 * 4); + os_memcpy(((uint32_t*)frame) + 5, &gdbstub_savedRegs.a[2], 14 * 4); + + return result; } #endif @@ -706,10 +943,11 @@ static inline int gdbReadCommandWithFrame(void* frame) { #if GDBSTUB_FREERTOS //Freertos exception. This routine is called by an assembly routine in gdbstub-entry.S -void ATTR_GDBFN gdbstub_handle_user_exception() { - gdbstub_savedRegs.reason |= 0x80; //mark as an exception reason - while (gdbReadCommand() != ST_CONT) - ; +void ATTR_GDBFN gdbstub_handle_user_exception() +{ + gdbstub_savedRegs.reason |= 0x80; //mark as an exception reason + while (gdbReadCommand() != ST_CONT) + ; } //FreeRTOS doesn't use the Xtensa HAL for exceptions, but uses its own fatal exception handler. @@ -718,39 +956,45 @@ void ATTR_GDBFN gdbstub_handle_user_exception() { extern void user_fatal_exception_handler(); extern void gdbstub_user_exception_entry(); -static void ATTR_GDBINIT install_exceptions() { - //Replace the user_fatal_exception_handler by a jump to our own code - int *ufe = (int*)user_fatal_exception_handler; - //This mess encodes as a relative jump instruction to user_fatal_exception_handler - *ufe = ((((int)gdbstub_user_exception_entry - (int)user_fatal_exception_handler) - 4) << 6) | 6; +static void ATTR_GDBINIT install_exceptions() +{ + //Replace the user_fatal_exception_handler by a jump to our own code + int *ufe = (int*)user_fatal_exception_handler; + //This mess encodes as a relative jump instruction to user_fatal_exception_handler + *ufe = ((((int)gdbstub_user_exception_entry - (int)user_fatal_exception_handler) - 4) << 6) | 6; } #else //Non-OS exception handler. Gets called by the Xtensa HAL. static void gdbstub_exception_handler_flash(struct XTensa_exception_frame_s *frame); -static void ATTR_GDBFN gdbstub_exception_handler(struct XTensa_exception_frame_s *frame) { - //Save the extra registers the Xtensa HAL doesn't save - gdbstub_save_extra_sfrs_for_exception(); - Cache_Read_Enable_New(); - gdbstub_exception_handler_flash(frame); +static void ATTR_GDBFN gdbstub_exception_handler(struct XTensa_exception_frame_s *frame) +{ + //Save the extra registers the Xtensa HAL doesn't save + gdbstub_save_extra_sfrs_for_exception(); + Cache_Read_Enable_New(); + gdbstub_exception_handler_flash(frame); } -static void __attribute__((noinline)) gdbstub_exception_handler_flash(struct XTensa_exception_frame_s *frame) { - gdbstub_savedRegs.reason |= 0x80; //mark as an exception reason - while (gdbReadCommandWithFrame((void*)frame) != ST_CONT) - ; +static void __attribute__((noinline)) gdbstub_exception_handler_flash(struct XTensa_exception_frame_s *frame) +{ + gdbstub_savedRegs.reason |= 0x80; //mark as an exception reason + while (gdbReadCommandWithFrame((void*)frame) != ST_CONT) + ; } //The OS-less SDK uses the Xtensa HAL to handle exceptions. We can use those functions to catch any //fatal exceptions and invoke the debugger when this happens. -static void ATTR_GDBINIT install_exceptions() { - static int exno[] = {EXCCAUSE_ILLEGAL, EXCCAUSE_SYSCALL, EXCCAUSE_INSTR_ERROR, EXCCAUSE_LOAD_STORE_ERROR, - EXCCAUSE_DIVIDE_BY_ZERO, EXCCAUSE_UNALIGNED, EXCCAUSE_INSTR_DATA_ERROR, EXCCAUSE_LOAD_STORE_DATA_ERROR, - EXCCAUSE_INSTR_ADDR_ERROR, EXCCAUSE_LOAD_STORE_ADDR_ERROR, EXCCAUSE_INSTR_PROHIBITED, - EXCCAUSE_LOAD_PROHIBITED, EXCCAUSE_STORE_PROHIBITED}; - unsigned int i; - for (i = 0; i < (sizeof(exno) / sizeof(exno[0])); i++) { - _xtos_set_exception_handler(exno[i], gdbstub_exception_handler); - } +static void ATTR_GDBINIT install_exceptions() +{ + static int exno[] = {EXCCAUSE_ILLEGAL, EXCCAUSE_SYSCALL, EXCCAUSE_INSTR_ERROR, EXCCAUSE_LOAD_STORE_ERROR, + EXCCAUSE_DIVIDE_BY_ZERO, EXCCAUSE_UNALIGNED, EXCCAUSE_INSTR_DATA_ERROR, EXCCAUSE_LOAD_STORE_DATA_ERROR, + EXCCAUSE_INSTR_ADDR_ERROR, EXCCAUSE_LOAD_STORE_ADDR_ERROR, EXCCAUSE_INSTR_PROHIBITED, + EXCCAUSE_LOAD_PROHIBITED, EXCCAUSE_STORE_PROHIBITED + }; + unsigned int i; + for (i = 0; i < (sizeof(exno) / sizeof(exno[0])); i++) + { + _xtos_set_exception_handler(exno[i], gdbstub_exception_handler); + } } #endif @@ -761,16 +1005,21 @@ static void ATTR_GDBINIT install_exceptions() { #if GDBSTUB_REDIRECT_CONSOLE_OUTPUT //Replacement putchar1 routine. Instead of spitting out the character directly, it will buffer up to //OBUFLEN characters (or up to a \n, whichever comes earlier) and send it out as a gdb stdout packet. -static void ATTR_GDBEXTERNFN gdbstub_semihost_putchar1(char c) { - if (!gdb_attached && uart_putc1_callback != NULL) { - uart_putc1_callback(c); - } else { - gdbstub_write_char(c); - } +static void ATTR_GDBEXTERNFN gdbstub_semihost_putchar1(char c) +{ + if (!gdb_attached && uart_putc1_callback != NULL) + { + uart_putc1_callback(c); + } + else + { + gdbstub_write_char(c); + } } -void ATTR_GDBINIT gdbstub_set_putc1_callback(void (*func)(char)) { - uart_putc1_callback = func; +void ATTR_GDBINIT gdbstub_set_putc1_callback(void (*func)(char)) +{ + uart_putc1_callback = func; } #endif @@ -779,18 +1028,19 @@ void ATTR_GDBINIT gdbstub_set_putc1_callback(void (*func)(char)) { #if GDBSTUB_FREERTOS static void ATTR_GDBINIT configure_uart() {} #else -static void ATTR_GDBINIT configure_uart() { +static void ATTR_GDBINIT configure_uart() +{ #ifdef ARDUINO - // Set the UART input/output pins to TX=1, RX=3 - pinMode(3, SPECIAL); - pinMode(1, FUNCTION_0); + // Set the UART input/output pins to TX=1, RX=3 + pinMode(3, SPECIAL); + pinMode(1, FUNCTION_0); #endif - WRITE_PERI_REG(UART_CONF0(0), 0b00011100); //8N1 + WRITE_PERI_REG(UART_CONF0(0), 0b00011100); //8N1 - SET_PERI_REG_MASK(UART_CONF0(0), UART_RXFIFO_RST | UART_TXFIFO_RST); //RESET FIFO - CLEAR_PERI_REG_MASK(UART_CONF0(0), UART_RXFIFO_RST | UART_TXFIFO_RST); + SET_PERI_REG_MASK(UART_CONF0(0), UART_RXFIFO_RST | UART_TXFIFO_RST); //RESET FIFO + CLEAR_PERI_REG_MASK(UART_CONF0(0), UART_RXFIFO_RST | UART_TXFIFO_RST); } #endif @@ -798,99 +1048,116 @@ static void ATTR_GDBINIT configure_uart() { #if GDBSTUB_FREERTOS -void ATTR_GDBFN gdbstub_handle_uart_int(struct XTensa_rtos_int_frame_s *frame) { - int doDebug = 0, fifolen, x; - - fifolen = (READ_PERI_REG(UART_STATUS(0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; - while (fifolen != 0) { - //Check if any of the chars is control-C. Throw away rest. - if ((READ_PERI_REG(UART_FIFO(0)) & 0xFF) == 0x3) - doDebug = 1; - fifolen--; - } - WRITE_PERI_REG(UART_INT_CLR(0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR); - - if (doDebug) { - //Copy registers the Xtensa HAL did save to gdbstub_savedRegs - gdbstub_savedRegs.pc = frame->pc; - gdbstub_savedRegs.ps = frame->ps; - gdbstub_savedRegs.sar = frame->sar; - for (x = 0; x < 16; x++) - gdbstub_savedRegs.a[x] = frame->a[x]; -// gdbstub_savedRegs.a1=(uint32_t)frame+EXCEPTION_GDB_SP_OFFSET; - gdbstub_savedRegs.reason = 0xff; //mark as user break reason - - gdbReadCommand(); - //Copy any changed registers back to the frame the Xtensa HAL uses. - frame->pc = gdbstub_savedRegs.pc; - frame->ps = gdbstub_savedRegs.ps; - frame->sar = gdbstub_savedRegs.sar; - for (x = 0; x < 16; x++) - frame->a[x] = gdbstub_savedRegs.a[x]; - } -} - -static void ATTR_GDBINIT install_uart_hdlr() { - os_isr_attach(ETS_UART_INUM, gdbstub_uart_entry); - SET_PERI_REG_MASK(UART_INT_ENA(0), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA); - ETS_UART_INTR_ENABLE(); +void ATTR_GDBFN gdbstub_handle_uart_int(struct XTensa_rtos_int_frame_s *frame) +{ + int doDebug = 0, fifolen, x; + + fifolen = (READ_PERI_REG(UART_STATUS(0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; + while (fifolen != 0) + { + //Check if any of the chars is control-C. Throw away rest. + if ((READ_PERI_REG(UART_FIFO(0)) & 0xFF) == 0x3) + { + doDebug = 1; + } + fifolen--; + } + WRITE_PERI_REG(UART_INT_CLR(0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR); + + if (doDebug) + { + //Copy registers the Xtensa HAL did save to gdbstub_savedRegs + gdbstub_savedRegs.pc = frame->pc; + gdbstub_savedRegs.ps = frame->ps; + gdbstub_savedRegs.sar = frame->sar; + for (x = 0; x < 16; x++) + { + gdbstub_savedRegs.a[x] = frame->a[x]; + } + // gdbstub_savedRegs.a1=(uint32_t)frame+EXCEPTION_GDB_SP_OFFSET; + gdbstub_savedRegs.reason = 0xff; //mark as user break reason + + gdbReadCommand(); + //Copy any changed registers back to the frame the Xtensa HAL uses. + frame->pc = gdbstub_savedRegs.pc; + frame->ps = gdbstub_savedRegs.ps; + frame->sar = gdbstub_savedRegs.sar; + for (x = 0; x < 16; x++) + { + frame->a[x] = gdbstub_savedRegs.a[x]; + } + } +} + +static void ATTR_GDBINIT install_uart_hdlr() +{ + os_isr_attach(ETS_UART_INUM, gdbstub_uart_entry); + SET_PERI_REG_MASK(UART_INT_ENA(0), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA); + ETS_UART_INTR_ENABLE(); } #else -static void ATTR_GDBFN gdbstub_uart_hdlr(void* arg, void* frame) { - (void) arg; - unsigned char c; - //Save the extra registers the Xtensa HAL doesn't save - gdbstub_save_extra_sfrs_for_exception(); - - ETS_UART_INTR_DISABLE(); - WRITE_PERI_REG(UART_INT_CLR(0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR); - - int fifolen = (READ_PERI_REG(UART_STATUS(0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; - while (true) { - if (fifolen == 0) { - ETS_UART_INTR_ENABLE(); - return; - } - c = READ_PERI_REG(UART_FIFO(0)) & 0xFF; - //Check if any of the chars is control-C - if (c == 0x3) { - break; - } +static void ATTR_GDBFN gdbstub_uart_hdlr(void* arg, void* frame) +{ + (void) arg; + unsigned char c; + //Save the extra registers the Xtensa HAL doesn't save + gdbstub_save_extra_sfrs_for_exception(); + + ETS_UART_INTR_DISABLE(); + WRITE_PERI_REG(UART_INT_CLR(0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR); + + int fifolen = (READ_PERI_REG(UART_STATUS(0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; + while (true) + { + if (fifolen == 0) + { + ETS_UART_INTR_ENABLE(); + return; + } + c = READ_PERI_REG(UART_FIFO(0)) & 0xFF; + //Check if any of the chars is control-C + if (c == 0x3) + { + break; + } #if GDBSTUB_CTRLC_BREAK - if (!gdb_attached && uart_isr_callback != NULL) { - uart_isr_callback(uart_isr_arg, c); - } + if (!gdb_attached && uart_isr_callback != NULL) + { + uart_isr_callback(uart_isr_arg, c); + } #endif - fifolen--; - } + fifolen--; + } - gdbstub_savedRegs.reason = 0xff; //mark as user break reason - gdbReadCommandWithFrame(frame); + gdbstub_savedRegs.reason = 0xff; //mark as user break reason + gdbReadCommandWithFrame(frame); } -static void ATTR_GDBINIT install_uart_hdlr() { - ETS_UART_INTR_DISABLE(); - ETS_UART_INTR_ATTACH(gdbstub_uart_hdlr, NULL); +static void ATTR_GDBINIT install_uart_hdlr() +{ + ETS_UART_INTR_DISABLE(); + ETS_UART_INTR_ATTACH(gdbstub_uart_hdlr, NULL); - configure_uart(); + configure_uart(); - WRITE_PERI_REG(UART_CONF1(0), - ((16 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) | - ((0x02 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S) | - UART_RX_TOUT_EN); + WRITE_PERI_REG(UART_CONF1(0), + ((16 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) | + ((0x02 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S) | + UART_RX_TOUT_EN); - WRITE_PERI_REG(UART_INT_CLR(0), 0xffff); - SET_PERI_REG_MASK(UART_INT_ENA(0), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA); - ETS_UART_INTR_ENABLE(); + WRITE_PERI_REG(UART_INT_CLR(0), 0xffff); + SET_PERI_REG_MASK(UART_INT_ENA(0), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA); + ETS_UART_INTR_ENABLE(); } -void ATTR_GDBINIT gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg) { - ETS_UART_INTR_DISABLE(); - uart_isr_callback = func; - uart_isr_arg = arg; - ETS_UART_INTR_ENABLE(); +void ATTR_GDBINIT gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg) +{ + ETS_UART_INTR_DISABLE(); + uart_isr_callback = func; + uart_isr_arg = arg; + ETS_UART_INTR_ENABLE(); } #endif @@ -900,27 +1167,32 @@ void ATTR_GDBINIT gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), vo //gdbstub initialization routine. -void ATTR_GDBINIT gdbstub_init() { +void ATTR_GDBINIT gdbstub_init() +{ #if GDBSTUB_REDIRECT_CONSOLE_OUTPUT - os_install_putc1(gdbstub_semihost_putchar1); + os_install_putc1(gdbstub_semihost_putchar1); #endif #if GDBSTUB_CTRLC_BREAK - install_uart_hdlr(); + install_uart_hdlr(); #else - configure_uart(); + configure_uart(); #endif #if GDBSTUB_BREAK_ON_EXCEPTION - install_exceptions(); + install_exceptions(); #endif - gdbstub_init_debug_entry(); + gdbstub_init_debug_entry(); #if GDBSTUB_BREAK_ON_INIT - gdbstub_do_break(); + gdbstub_do_break(); #endif } -bool ATTR_GDBEXTERNFN gdb_present() { - return true; +bool ATTR_GDBEXTERNFN gdb_present() +{ + return true; } -void ATTR_GDBFN gdb_do_break() { gdbstub_do_break(); } +void ATTR_GDBFN gdb_do_break() +{ + gdbstub_do_break(); +} void ATTR_GDBINIT gdb_init() __attribute__((alias("gdbstub_init"))); diff --git a/libraries/GDBStub/src/xtensa/config/core-isa.h b/libraries/GDBStub/src/xtensa/config/core-isa.h index 1128a0c779..1e45367cad 100644 --- a/libraries/GDBStub/src/xtensa/config/core-isa.h +++ b/libraries/GDBStub/src/xtensa/config/core-isa.h @@ -1,32 +1,32 @@ -/* - * xtensa/config/core-isa.h -- HAL definitions that are dependent on Xtensa - * processor CORE configuration - * - * See , which includes this file, for more details. - */ - -/* Xtensa processor core configuration information. - - Customer ID=7011; Build=0x2b6f6; Copyright (c) 1999-2010 Tensilica Inc. - - 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. */ +/* + xtensa/config/core-isa.h -- HAL definitions that are dependent on Xtensa + processor CORE configuration + + See , which includes this file, for more details. +*/ + +/* Xtensa processor core configuration information. + + Customer ID=7011; Build=0x2b6f6; Copyright (c) 1999-2010 Tensilica Inc. + + 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. */ #ifndef _XTENSA_CORE_CONFIGURATION_H #define _XTENSA_CORE_CONFIGURATION_H @@ -37,14 +37,14 @@ ****************************************************************************/ /* - * Note: Macros of the form XCHAL_HAVE_*** have a value of 1 if the option is - * configured, and a value of 0 otherwise. These macros are always defined. - */ + Note: Macros of the form XCHAL_HAVE_*** have a value of 1 if the option is + configured, and a value of 0 otherwise. These macros are always defined. +*/ -/*---------------------------------------------------------------------- +/* ---------------------------------------------------------------------- ISA - ----------------------------------------------------------------------*/ + ----------------------------------------------------------------------*/ #define XCHAL_HAVE_BE 0 /* big-endian byte ordering */ #define XCHAL_HAVE_WINDOWED 0 /* windowed registers option */ @@ -99,9 +99,9 @@ #define XCHAL_HAVE_CONNXD2 0 /* ConnX D2 pkg */ -/*---------------------------------------------------------------------- +/* ---------------------------------------------------------------------- MISC - ----------------------------------------------------------------------*/ + ----------------------------------------------------------------------*/ #define XCHAL_NUM_WRITEBUFFER_ENTRIES 1 /* size of write buffer */ #define XCHAL_INST_FETCH_WIDTH 4 /* instr-fetch width in bytes */ @@ -121,8 +121,8 @@ #define XCHAL_BUILD_UNIQUE_ID 0x0002B6F6 /* 22-bit sw build ID */ /* - * These definitions describe the hardware targeted by this software. - */ + These definitions describe the hardware targeted by this software. +*/ #define XCHAL_HW_CONFIGID0 0xC28CDAFA /* ConfigID hi 32 bits*/ #define XCHAL_HW_CONFIGID1 0x1082B6F6 /* ConfigID lo 32 bits*/ #define XCHAL_HW_VERSION_NAME "LX3.0.1" /* full version name */ @@ -142,9 +142,9 @@ #define XCHAL_HW_MAX_VERSION 230001 /* latest targeted hw */ -/*---------------------------------------------------------------------- +/* ---------------------------------------------------------------------- CACHE - ----------------------------------------------------------------------*/ + ----------------------------------------------------------------------*/ #define XCHAL_ICACHE_LINESIZE 4 /* I-cache line size in bytes */ #define XCHAL_DCACHE_LINESIZE 4 /* D-cache line size in bytes */ @@ -169,9 +169,9 @@ #ifndef XTENSA_HAL_NON_PRIVILEGED_ONLY -/*---------------------------------------------------------------------- +/* ---------------------------------------------------------------------- CACHE - ----------------------------------------------------------------------*/ + ----------------------------------------------------------------------*/ #define XCHAL_HAVE_PIF 1 /* any outbound PIF present */ @@ -199,9 +199,9 @@ #define XCHAL_CA_BITS 4 -/*---------------------------------------------------------------------- +/* ---------------------------------------------------------------------- INTERNAL I/D RAM/ROMs and XLMI - ----------------------------------------------------------------------*/ + ----------------------------------------------------------------------*/ #define XCHAL_NUM_INSTROM 1 /* number of core instr. ROMs */ #define XCHAL_NUM_INSTRAM 2 /* number of core instr. RAMs */ @@ -253,9 +253,9 @@ #define XCHAL_XLMI0_ECC_PARITY 0 -/*---------------------------------------------------------------------- +/* ---------------------------------------------------------------------- INTERRUPTS and TIMERS - ----------------------------------------------------------------------*/ + ----------------------------------------------------------------------*/ #define XCHAL_HAVE_INTERRUPTS 1 /* interrupt option */ #define XCHAL_HAVE_HIGHPRI_INTERRUPTS 1 /* med/high-pri. interrupts */ @@ -268,7 +268,7 @@ #define XCHAL_NUM_INTLEVELS 2 /* number of interrupt levels (not including level zero) */ #define XCHAL_EXCM_LEVEL 1 /* level masked by PS.EXCM */ - /* (always 1 in XEA1; levels 2 .. EXCM_LEVEL are "medium priority") */ +/* (always 1 in XEA1; levels 2 .. EXCM_LEVEL are "medium priority") */ /* Masks of interrupts at each interrupt level: */ #define XCHAL_INTLEVEL1_MASK 0x00003FFF @@ -348,13 +348,13 @@ /* - * External interrupt vectors/levels. - * These macros describe how Xtensa processor interrupt numbers - * (as numbered internally, eg. in INTERRUPT and INTENABLE registers) - * map to external BInterrupt pins, for those interrupts - * configured as external (level-triggered, edge-triggered, or NMI). - * See the Xtensa processor databook for more details. - */ + External interrupt vectors/levels. + These macros describe how Xtensa processor interrupt numbers + (as numbered internally, eg. in INTERRUPT and INTENABLE registers) + map to external BInterrupt pins, for those interrupts + configured as external (level-triggered, edge-triggered, or NMI). + See the Xtensa processor databook for more details. +*/ /* Core interrupt numbers mapped to each EXTERNAL interrupt number: */ #define XCHAL_EXTINT0_NUM 0 /* (intlevel 1) */ @@ -372,9 +372,9 @@ #define XCHAL_EXTINT12_NUM 14 /* (intlevel 3) */ -/*---------------------------------------------------------------------- +/* ---------------------------------------------------------------------- EXCEPTIONS and VECTORS - ----------------------------------------------------------------------*/ + ----------------------------------------------------------------------*/ #define XCHAL_XEA_VERSION 2 /* Xtensa Exception Architecture number: 1 == XEA1 (old) @@ -420,9 +420,9 @@ #define XCHAL_INTLEVEL3_VECTOR_PADDR XCHAL_NMI_VECTOR_PADDR -/*---------------------------------------------------------------------- +/* ---------------------------------------------------------------------- DEBUG - ----------------------------------------------------------------------*/ + ----------------------------------------------------------------------*/ #define XCHAL_HAVE_OCD 1 /* OnChipDebug option */ #define XCHAL_NUM_IBREAK 1 /* number of IBREAKn regs */ @@ -430,9 +430,9 @@ #define XCHAL_HAVE_OCD_DIR_ARRAY 0 /* faster OCD option */ -/*---------------------------------------------------------------------- +/* ---------------------------------------------------------------------- MMU - ----------------------------------------------------------------------*/ + ----------------------------------------------------------------------*/ /* See core-matmap.h header file for more details. */ diff --git a/libraries/GDBStub/src/xtensa/config/specreg.h b/libraries/GDBStub/src/xtensa/config/specreg.h index 724b490d95..83663e6f53 100644 --- a/libraries/GDBStub/src/xtensa/config/specreg.h +++ b/libraries/GDBStub/src/xtensa/config/specreg.h @@ -1,29 +1,29 @@ /* - * Xtensa Special Register symbolic names - */ + Xtensa Special Register symbolic names +*/ /* $Id: //depot/rel/Boreal/Xtensa/SWConfig/hal/specreg.h.tpp#2 $ */ -/* Customer ID=7011; Build=0x2b6f6; Copyright (c) 1998-2002 Tensilica Inc. +/* Customer ID=7011; Build=0x2b6f6; Copyright (c) 1998-2002 Tensilica Inc. - 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: + 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 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. */ + 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. */ #ifndef XTENSA_SPECREG_H #define XTENSA_SPECREG_H diff --git a/libraries/GDBStub/src/xtensa/corebits.h b/libraries/GDBStub/src/xtensa/corebits.h index 762bc60911..99f7bfda45 100644 --- a/libraries/GDBStub/src/xtensa/corebits.h +++ b/libraries/GDBStub/src/xtensa/corebits.h @@ -1,34 +1,34 @@ /* - * xtensa/corebits.h - Xtensa Special Register field positions, masks, values. - * - * (In previous releases, these were defined in specreg.h, a generated file. - * This file is not generated, ie. it is processor configuration independent.) - */ + xtensa/corebits.h - Xtensa Special Register field positions, masks, values. + + (In previous releases, these were defined in specreg.h, a generated file. + This file is not generated, ie. it is processor configuration independent.) +*/ /* $Id: //depot/rel/Boreal/Xtensa/OS/include/xtensa/corebits.h#2 $ */ /* - * Copyright (c) 2005-2007 Tensilica Inc. - * - * 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. - */ + Copyright (c) 2005-2007 Tensilica Inc. + + 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. +*/ #ifndef XTENSA_COREBITS_H #define XTENSA_COREBITS_H @@ -38,10 +38,10 @@ #define EXCCAUSE_EXCCAUSE_MASK 0x3F /* EXCCAUSE register values: */ /* - * General Exception Causes - * (values of EXCCAUSE special register set by general exceptions, - * which vector to the user, kernel, or double-exception vectors). - */ + General Exception Causes + (values of EXCCAUSE special register set by general exceptions, + which vector to the user, kernel, or double-exception vectors). +*/ #define EXCCAUSE_ILLEGAL 0 /* Illegal Instruction */ #define EXCCAUSE_SYSCALL 1 /* System Call (SYSCALL instruction) */ #define EXCCAUSE_INSTR_ERROR 2 /* Instruction Fetch Error */ @@ -143,7 +143,7 @@ #define MESR_DME_SHIFT 1 #define MESR_RCE 0x00000010 /* recorded memory error */ #define MESR_RCE_SHIFT 4 -#define MESR_LCE +#define MESR_LCE #define MESR_LCE_SHIFT ? #define MESR_LCE_L #define MESR_ERRENAB 0x00000100 diff --git a/libraries/Hash/src/Hash.cpp b/libraries/Hash/src/Hash.cpp index 5a58c961d2..22e68a0443 100644 --- a/libraries/Hash/src/Hash.cpp +++ b/libraries/Hash/src/Hash.cpp @@ -1,117 +1,132 @@ -/** - * @file Hash.cpp - * @date 20.05.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the esp8266 core for Arduino environment. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include - -#include "Hash.h" - -extern "C" { -#include "sha1/sha1.h" -} - -/** - * create a sha1 hash from data - * @param data uint8_t * - * @param size uint32_t - * @param hash uint8_t[20] - */ -void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]) { - - SHA1_CTX ctx; - -#ifdef DEBUG_SHA1 - os_printf("DATA:"); - for(uint16_t i = 0; i < size; i++) { - os_printf("%02X", data[i]); - } - os_printf("\n"); - os_printf("DATA:"); - for(uint16_t i = 0; i < size; i++) { - os_printf("%c", data[i]); - } - os_printf("\n"); -#endif - - SHA1Init(&ctx); - SHA1Update(&ctx, data, size); - SHA1Final(hash, &ctx); - -#ifdef DEBUG_SHA1 - os_printf("SHA1:"); - for(uint16_t i = 0; i < 20; i++) { - os_printf("%02X", hash[i]); - } - os_printf("\n\n"); -#endif -} - -void sha1(char * data, uint32_t size, uint8_t hash[20]) { - sha1((uint8_t *) data, size, hash); -} - -void sha1(const uint8_t * data, uint32_t size, 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); -} - -String sha1(uint8_t* data, uint32_t size) { - uint8_t hash[20]; - String hashStr = ""; - - sha1(&data[0], size, &hash[0]); - - for(uint16_t i = 0; i < 20; i++) { - String hex = String(hash[i], HEX); - if(hex.length() < 2) { - hex = "0" + hex; - } - hashStr += hex; - } - - 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) { - return sha1((uint8_t*) data, size); -} - -String sha1(String data) { - return sha1(data.c_str(), data.length()); -} - +/** + @file Hash.cpp + @date 20.05.2015 + @author Markus Sattler + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include + +#include "Hash.h" + +extern "C" { +#include "sha1/sha1.h" +} + +/** + create a sha1 hash from data + @param data uint8_t + @param size uint32_t + @param hash uint8_t[20] +*/ +void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]) +{ + + SHA1_CTX ctx; + +#ifdef DEBUG_SHA1 + os_printf("DATA:"); + for (uint16_t i = 0; i < size; i++) + { + os_printf("%02X", data[i]); + } + os_printf("\n"); + os_printf("DATA:"); + for (uint16_t i = 0; i < size; i++) + { + os_printf("%c", data[i]); + } + os_printf("\n"); +#endif + + SHA1Init(&ctx); + SHA1Update(&ctx, data, size); + SHA1Final(hash, &ctx); + +#ifdef DEBUG_SHA1 + os_printf("SHA1:"); + for (uint16_t i = 0; i < 20; i++) + { + os_printf("%02X", hash[i]); + } + os_printf("\n\n"); +#endif +} + +void sha1(char * data, uint32_t size, uint8_t hash[20]) +{ + sha1((uint8_t *) data, size, hash); +} + +void sha1(const uint8_t * data, uint32_t size, 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); +} + +String sha1(uint8_t* data, uint32_t size) +{ + uint8_t hash[20]; + String hashStr = ""; + + sha1(&data[0], size, &hash[0]); + + for (uint16_t i = 0; i < 20; i++) + { + String hex = String(hash[i], HEX); + if (hex.length() < 2) + { + hex = "0" + hex; + } + hashStr += hex; + } + + 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) +{ + return sha1((uint8_t*) data, size); +} + +String sha1(String data) +{ + return sha1(data.c_str(), data.length()); +} + diff --git a/libraries/Hash/src/Hash.h b/libraries/Hash/src/Hash.h index 774b8aad12..07af461205 100644 --- a/libraries/Hash/src/Hash.h +++ b/libraries/Hash/src/Hash.h @@ -1,42 +1,42 @@ -/** - * @file Hash.h - * @date 20.05.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the esp8266 core for Arduino environment. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef HASH_H_ -#define HASH_H_ - -//#define DEBUG_SHA1 - -void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]); -void sha1(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 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 char* data, uint32_t size); -String sha1(String data); - -#endif /* HASH_H_ */ +/** + @file Hash.h + @date 20.05.2015 + @author Markus Sattler + + Copyright (c) 2015 Markus Sattler. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HASH_H_ +#define HASH_H_ + +//#define DEBUG_SHA1 + +void sha1(uint8_t * data, uint32_t size, uint8_t hash[20]); +void sha1(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 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 char* data, uint32_t size); +String sha1(String data); + +#endif /* HASH_H_ */ diff --git a/libraries/Hash/src/sha1/sha1.c b/libraries/Hash/src/sha1/sha1.c index fae926462d..1fdf6eefbc 100644 --- a/libraries/Hash/src/sha1/sha1.c +++ b/libraries/Hash/src/sha1/sha1.c @@ -1,208 +1,221 @@ -/** - * @file sha1.c - * @date 20.05.2015 - * @author Steve Reid - * - * 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 - 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 -#include -#include -#include - -#include "sha1.h" - -//#include - -#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 ================ */ +/** + @file sha1.c + @date 20.05.2015 + @author Steve Reid + + 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 + 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 +#include +#include +#include + +#include "sha1.h" + +//#include + +#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 ================ */ diff --git a/libraries/Hash/src/sha1/sha1.h b/libraries/Hash/src/sha1/sha1.h index 158bd76b36..507a21b1e4 100644 --- a/libraries/Hash/src/sha1/sha1.h +++ b/libraries/Hash/src/sha1/sha1.h @@ -1,32 +1,33 @@ -/** - * @file sha1.h - * @date 20.05.2015 - * @author Steve Reid - * - * from: http://www.virtualbox.org/svn/vbox/trunk/src/recompiler/tests/sha1.c - */ - -/* ================ sha1.h ================ */ -/* - SHA-1 in C - By Steve Reid - 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 ================ */ +/** + @file sha1.h + @date 20.05.2015 + @author Steve Reid + + from: http://www.virtualbox.org/svn/vbox/trunk/src/recompiler/tests/sha1.c +*/ + +/* ================ sha1.h ================ */ +/* + SHA-1 in C + By Steve Reid + 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 ================ */ diff --git a/libraries/SD/src/SD.h b/libraries/SD/src/SD.h index 71d0102bd1..a5c0bb672f 100644 --- a/libraries/SD/src/SD.h +++ b/libraries/SD/src/SD.h @@ -1,20 +1,20 @@ /* - SD.h - A thin shim for Arduino ESP8266 Filesystems - Copyright (c) 2019 Earle F. Philhower, III. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + SD.h - A thin shim for Arduino ESP8266 Filesystems + Copyright (c) 2019 Earle F. Philhower, III. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __SD_H__ @@ -29,103 +29,144 @@ #undef FILE_WRITE #define FILE_WRITE (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT) -class SDClass { +class SDClass +{ public: - boolean begin(uint8_t csPin, SPISettings cfg = SPI_HALF_SPEED) { - SDFS.setConfig(SDFSConfig(csPin, cfg)); + boolean begin(uint8_t csPin, SPISettings cfg = SPI_HALF_SPEED) + { + SDFS.setConfig(SDFSConfig(csPin, cfg)); return (boolean)SDFS.begin(); } - void end(bool endSPI = true) { + void end(bool endSPI = true) + { SDFS.end(); - if (endSPI) { + if (endSPI) + { SPI.end(); } } - File open(const char *filename, uint8_t mode = FILE_READ) { + File open(const char *filename, uint8_t mode = FILE_READ) + { return SDFS.open(filename, getMode(mode)); } - File open(const String &filename, uint8_t mode = FILE_READ) { + File open(const String &filename, uint8_t mode = FILE_READ) + { return open(filename.c_str(), mode); } - boolean exists(const char *filepath) { + boolean exists(const char *filepath) + { return (boolean)SDFS.exists(filepath); } - boolean exists(const String &filepath) { + boolean exists(const String &filepath) + { return (boolean)SDFS.exists(filepath.c_str()); } - boolean mkdir(const char *filepath) { + boolean mkdir(const char *filepath) + { return (boolean)SDFS.mkdir(filepath); } - boolean mkdir(const String &filepath) { + boolean mkdir(const String &filepath) + { return (boolean)SDFS.mkdir(filepath.c_str()); } - - boolean remove(const char *filepath) { + + boolean remove(const char *filepath) + { return (boolean)SDFS.remove(filepath); } - boolean remove(const String &filepath) { + boolean remove(const String &filepath) + { return remove(filepath.c_str()); } - - boolean rmdir(const char *filepath) { + + boolean rmdir(const char *filepath) + { return (boolean)SDFS.rmdir(filepath); } - boolean rmdir(const String &filepath) { + boolean rmdir(const String &filepath) + { return rmdir(filepath.c_str()); } - uint8_t type() { + uint8_t type() + { return 0;//card.type(); } - uint8_t fatType() { + uint8_t fatType() + { return 0;//volume.fatType(); } - size_t blocksPerCluster() { + size_t blocksPerCluster() + { return 0;//volume.blocksPerCluster(); } - size_t totalClusters() { + size_t totalClusters() + { return 0;//volume.clusterCount(); } - size_t blockSize() { + size_t blockSize() + { return 512; } - size_t totalBlocks() { + size_t totalBlocks() + { return 0;//(totalClusters() / blocksPerCluster()); } - size_t clusterSize() { + size_t clusterSize() + { return 0;//blocksPerCluster() * blockSize(); } - size_t size() { + size_t size() + { return 0;//(clusterSize() * totalClusters()); } private: - const char *getMode(uint8_t mode) { + const char *getMode(uint8_t mode) + { bool read = (mode & sdfat::O_READ) ? true : false; bool write = (mode & sdfat::O_WRITE) ? true : false; bool append = (mode & sdfat::O_APPEND) ? true : false; - if ( read & !write ) { return "r"; } - else if ( !read & write & !append ) { return "w+"; } - else if ( !read & write & append ) { return "a"; } - else if ( read & write & !append ) { return "w+"; } // may be a bug in FS::mode interpretation, "r+" seems proper - else if ( read & write & append ) { return "a+"; } - else { return "r"; } + if (read & !write) + { + return "r"; + } + else if (!read & write & !append) + { + return "w+"; + } + else if (!read & write & append) + { + return "a"; + } + else if (read & write & !append) + { + return "w+"; // may be a bug in FS::mode interpretation, "r+" seems proper + } + else if (read & write & append) + { + return "a+"; + } + else + { + return "r"; + } } }; diff --git a/libraries/SDFS/src/SDFS.cpp b/libraries/SDFS/src/SDFS.cpp index d862ff5d89..7b71f69f3a 100644 --- a/libraries/SDFS/src/SDFS.cpp +++ b/libraries/SDFS/src/SDFS.cpp @@ -1,29 +1,29 @@ /* - SDFS.cpp - file system wrapper for SdFat - Copyright (c) 2019 Earle F. Philhower, III. All rights reserved. + SDFS.cpp - file system wrapper for SdFat + Copyright (c) 2019 Earle F. Philhower, III. All rights reserved. - Based on spiffs_api.cpp which is: - | Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + Based on spiffs_api.cpp which is: + | Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This code was influenced by NodeMCU and Sming libraries, and first version of - Arduino wrapper written by Hristo Gochkov. + This code was influenced by NodeMCU and Sming libraries, and first version of + Arduino wrapper written by Hristo Gochkov. - This file is part of the esp8266 core for Arduino environment. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include "SDFS.h" #include "SDFSFormatter.h" #include @@ -35,28 +35,34 @@ using namespace fs; FS SDFS = FS(FSImplPtr(new sdfs::SDFSImpl())); #endif -namespace sdfs { +namespace sdfs +{ FileImplPtr SDFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode) { - if (!_mounted) { + if (!_mounted) + { DEBUGV("SDFSImpl::open() called on unmounted FS\n"); return FileImplPtr(); } - if (!path || !path[0]) { + if (!path || !path[0]) + { DEBUGV("SDFSImpl::open() called with invalid filename\n"); return FileImplPtr(); } int flags = _getFlags(openMode, accessMode); - if ((openMode && OM_CREATE) && strchr(path, '/')) { + if ((openMode && OM_CREATE) && strchr(path, '/')) + { // For file creation, silently make subdirs as needed. If any fail, // it will be caught by the real file open later on char *pathStr = strdup(path); - if (pathStr) { + if (pathStr) + { // Make dirs up to the final fnamepart char *ptr = strrchr(pathStr, '/'); - if (ptr && ptr != pathStr) { // Don't try to make root dir! + if (ptr && ptr != pathStr) // Don't try to make root dir! + { *ptr = 0; _fs.mkdir(pathStr, true); } @@ -64,7 +70,8 @@ FileImplPtr SDFSImpl::open(const char* path, OpenMode openMode, AccessMode acces free(pathStr); } sdfat::File fd = _fs.open(path, flags); - if (!fd) { + if (!fd) + { DEBUGV("SDFSImpl::openFile: fd=%p path=`%s` openMode=%d accessMode=%d", &fd, path, openMode, accessMode); return FileImplPtr(); @@ -75,62 +82,80 @@ FileImplPtr SDFSImpl::open(const char* path, OpenMode openMode, AccessMode acces DirImplPtr SDFSImpl::openDir(const char* path) { - if (!_mounted) { + if (!_mounted) + { return DirImplPtr(); } char *pathStr = strdup(path); // Allow edits on our scratch copy - if (!pathStr) { + if (!pathStr) + { // OOM return DirImplPtr(); } // Get rid of any trailing slashes - while (strlen(pathStr) && (pathStr[strlen(pathStr)-1]=='/')) { - pathStr[strlen(pathStr)-1] = 0; + while (strlen(pathStr) && (pathStr[strlen(pathStr) - 1] == '/')) + { + pathStr[strlen(pathStr) - 1] = 0; } // At this point we have a name of "/blah/blah/blah" or "blah" or "" // If that references a directory, just open it and we're done. sdfat::File dirFile; const char *filter = ""; - if (!pathStr[0]) { + if (!pathStr[0]) + { // openDir("") === openDir("/") dirFile = _fs.open("/", sdfat::O_RDONLY); filter = ""; - } else if (_fs.exists(pathStr)) { + } + else if (_fs.exists(pathStr)) + { dirFile = _fs.open(pathStr, sdfat::O_RDONLY); - if (dirFile.isDir()) { + if (dirFile.isDir()) + { // Easy peasy, path specifies an existing dir! filter = ""; - } else { + } + else + { dirFile.close(); // This is a file, so open the containing dir char *ptr = strrchr(pathStr, '/'); - if (!ptr) { + if (!ptr) + { // No slashes, open the root dir dirFile = _fs.open("/", sdfat::O_RDONLY); filter = pathStr; - } else { + } + else + { // We've got slashes, open the dir one up *ptr = 0; // Remove slash, truncare string dirFile = _fs.open(pathStr, sdfat::O_RDONLY); filter = ptr + 1; } } - } else { + } + else + { // Name doesn't exist, so use the parent dir of whatever was sent in // This is a file, so open the containing dir char *ptr = strrchr(pathStr, '/'); - if (!ptr) { + if (!ptr) + { // No slashes, open the root dir dirFile = _fs.open("/", sdfat::O_RDONLY); filter = pathStr; - } else { + } + else + { // We've got slashes, open the dir one up *ptr = 0; // Remove slash, truncare string dirFile = _fs.open(pathStr, sdfat::O_RDONLY); filter = ptr + 1; } } - if (!dirFile) { + if (!dirFile) + { DEBUGV("SDFSImpl::openDir: path=`%s`\n", path); return DirImplPtr(); } @@ -140,8 +165,10 @@ DirImplPtr SDFSImpl::openDir(const char* path) return ret; } -bool SDFSImpl::format() { - if (_mounted) { +bool SDFSImpl::format() +{ + if (_mounted) + { return false; } SDFSFormatter formatter; diff --git a/libraries/SDFS/src/SDFS.h b/libraries/SDFS/src/SDFS.h index 44b017a1bb..5412040fff 100644 --- a/libraries/SDFS/src/SDFS.h +++ b/libraries/SDFS/src/SDFS.h @@ -2,31 +2,31 @@ #define SDFS_H /* - SDFS.h - file system wrapper for SdLib - Copyright (c) 2019 Earle F. Philhower, III. All rights reserved. + SDFS.h - file system wrapper for SdLib + Copyright (c) 2019 Earle F. Philhower, III. All rights reserved. - Based on spiffs_api.h, which is: - | Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + Based on spiffs_api.h, which is: + | Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. - This code was influenced by NodeMCU and Sming libraries, and first version of - Arduino wrapper written by Hristo Gochkov. + This code was influenced by NodeMCU and Sming libraries, and first version of + Arduino wrapper written by Hristo Gochkov. - This file is part of the esp8266 core for Arduino environment. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include #include #include "FS.h" @@ -38,43 +38,50 @@ using namespace fs; -namespace sdfs { +namespace sdfs +{ class SDFSFileImpl; class SDFSDirImpl; class SDFSConfig : public FSConfig { public: - SDFSConfig() { + SDFSConfig() + { _type = SDFSConfig::fsid::FSId; _autoFormat = false; _csPin = 4; _spiSettings = SD_SCK_MHZ(10); - _part = 0; + _part = 0; } - SDFSConfig(uint8_t csPin, SPISettings spi) { + SDFSConfig(uint8_t csPin, SPISettings spi) + { _type = SDFSConfig::fsid::FSId; _autoFormat = false; _csPin = csPin; _spiSettings = spi; - _part = 0; + _part = 0; } enum fsid { FSId = 0x53444653 }; - SDFSConfig setAutoFormat(bool val = true) { + SDFSConfig setAutoFormat(bool val = true) + { _autoFormat = val; return *this; } - SDFSConfig setCSPin(uint8_t pin) { + SDFSConfig setCSPin(uint8_t pin) + { _csPin = pin; return *this; } - SDFSConfig setSPI(SPISettings spi) { + SDFSConfig setSPI(SPISettings spi) + { _spiSettings = spi; return *this; } - SDFSConfig setPart(uint8_t part) { + SDFSConfig setPart(uint8_t part) + { _part = part; return *this; } @@ -94,18 +101,22 @@ class SDFSImpl : public FSImpl FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) override; - bool exists(const char* path) { + bool exists(const char* path) + { return _mounted ? _fs.exists(path) : false; } DirImplPtr openDir(const char* path) override; - bool rename(const char* pathFrom, const char* pathTo) override { + bool rename(const char* pathFrom, const char* pathTo) override + { return _mounted ? _fs.rename(pathFrom, pathTo) : false; } - bool info(FSInfo& info) override { - if (!_mounted) { + bool info(FSInfo& info) override + { + if (!_mounted) + { DEBUGV("SDFS::info: FS not mounted\n"); return false; } @@ -113,46 +124,54 @@ class SDFSImpl : public FSImpl info.blockSize = _fs.vol()->blocksPerCluster() * 512; info.pageSize = 0; // TODO ? info.maxPathLength = 255; // TODO ? - info.totalBytes =_fs.vol()->volumeBlockCount() * 512; + info.totalBytes = _fs.vol()->volumeBlockCount() * 512; info.usedBytes = info.totalBytes - (_fs.vol()->freeClusterCount() * _fs.vol()->blocksPerCluster() * 512); return true; } - bool remove(const char* path) override { + bool remove(const char* path) override + { return _mounted ? _fs.remove(path) : false; } - bool mkdir(const char* path) override { + bool mkdir(const char* path) override + { return _mounted ? _fs.mkdir(path) : false; } - bool rmdir(const char* path) override { - return _mounted ?_fs.rmdir(path) : false; + bool rmdir(const char* path) override + { + return _mounted ? _fs.rmdir(path) : false; } bool setConfig(const FSConfig &cfg) override { - if ((cfg._type != SDFSConfig::fsid::FSId) || _mounted) { + if ((cfg._type != SDFSConfig::fsid::FSId) || _mounted) + { DEBUGV("SDFS::setConfig: invalid config or already mounted\n"); return false; } - _cfg = *static_cast(&cfg); + _cfg = *static_cast(&cfg); return true; } - bool begin() override { - if (_mounted) { + bool begin() override + { + if (_mounted) + { end(); } _mounted = _fs.begin(_cfg._csPin, _cfg._spiSettings); - if (!_mounted && _cfg._autoFormat) { + if (!_mounted && _cfg._autoFormat) + { format(); _mounted = _fs.begin(_cfg._csPin, _cfg._spiSettings); } return _mounted; } - void end() override { + void end() override + { _mounted = false; // TODO } @@ -168,21 +187,27 @@ class SDFSImpl : public FSImpl return &_fs; } - static uint8_t _getFlags(OpenMode openMode, AccessMode accessMode) { + static uint8_t _getFlags(OpenMode openMode, AccessMode accessMode) + { uint8_t mode = 0; - if (openMode & OM_CREATE) { + if (openMode & OM_CREATE) + { mode |= sdfat::O_CREAT; } - if (openMode & OM_APPEND) { + if (openMode & OM_APPEND) + { mode |= sdfat::O_AT_END; } - if (openMode & OM_TRUNCATE) { + if (openMode & OM_TRUNCATE) + { mode |= sdfat::O_TRUNC; } - if (accessMode & AM_READ) { + if (accessMode & AM_READ) + { mode |= sdfat::O_READ; } - if (accessMode & AM_WRITE) { + if (accessMode & AM_WRITE) + { mode |= sdfat::O_WRITE; } return mode; @@ -222,7 +247,8 @@ class SDFSFileImpl : public FileImpl void flush() override { - if (_opened) { + if (_opened) + { _fd->flush(); _fd->sync(); } @@ -230,21 +256,23 @@ class SDFSFileImpl : public FileImpl bool seek(uint32_t pos, SeekMode mode) override { - if (!_opened) { + if (!_opened) + { return false; } - switch (mode) { - case SeekSet: - return _fd->seekSet(pos); - case SeekEnd: - return _fd->seekEnd(-pos); // TODO again, odd from POSIX - case SeekCur: - return _fd->seekCur(pos); - default: - // Should not be hit, we've got an invalid seek mode - DEBUGV("SDFSFileImpl::seek: invalid seek mode %d\n", mode); - assert((mode==SeekSet) || (mode==SeekEnd) || (mode==SeekCur)); // Will fail and give meaningful assert message - return false; + switch (mode) + { + case SeekSet: + return _fd->seekSet(pos); + case SeekEnd: + return _fd->seekEnd(-pos); // TODO again, odd from POSIX + case SeekCur: + return _fd->seekCur(pos); + default: + // Should not be hit, we've got an invalid seek mode + DEBUGV("SDFSFileImpl::seek: invalid seek mode %d\n", mode); + assert((mode == SeekSet) || (mode == SeekEnd) || (mode == SeekCur)); // Will fail and give meaningful assert message + return false; } } @@ -260,7 +288,8 @@ class SDFSFileImpl : public FileImpl bool truncate(uint32_t size) override { - if (!_opened) { + if (!_opened) + { DEBUGV("SDFSFileImpl::truncate: file not opened\n"); return false; } @@ -269,7 +298,8 @@ class SDFSFileImpl : public FileImpl void close() override { - if (_opened) { + if (_opened) + { _fd->close(); _opened = false; } @@ -277,10 +307,13 @@ class SDFSFileImpl : public FileImpl const char* name() const override { - if (!_opened) { + if (!_opened) + { DEBUGV("SDFSFileImpl::name: file not opened\n"); return nullptr; - } else { + } + else + { const char *p = _name.get(); const char *slash = strrchr(p, '/'); // For names w/o any path elements, return directly @@ -320,7 +353,8 @@ class SDFSDirImpl : public DirImpl SDFSDirImpl(const String& pattern, SDFSImpl* fs, std::shared_ptr dir, const char *dirPath = nullptr) : _pattern(pattern), _fs(fs), _dir(dir), _valid(false), _dirPath(nullptr) { - if (dirPath) { + if (dirPath) + { _dirPath = std::shared_ptr(new char[strlen(dirPath) + 1], std::default_delete()); strcpy(_dirPath.get(), dirPath); } @@ -333,18 +367,20 @@ class SDFSDirImpl : public DirImpl FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) override { - if (!_valid) { + if (!_valid) + { return FileImplPtr(); } // MAX_PATH on FAT32 is potentially 260 bytes per most implementations char tmpName[260]; - snprintf(tmpName, sizeof(tmpName), "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _lfn); + snprintf(tmpName, sizeof(tmpName), "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get() && _dirPath.get()[0] ? "/" : "", _lfn); return _fs->open((const char *)tmpName, openMode, accessMode); } const char* fileName() override { - if (!_valid) { + if (!_valid) + { DEBUGV("SDFSDirImpl::fileName: directory not valid\n"); return nullptr; } @@ -353,7 +389,8 @@ class SDFSDirImpl : public DirImpl size_t fileSize() override { - if (!_valid) { + if (!_valid) + { return 0; } @@ -373,20 +410,24 @@ class SDFSDirImpl : public DirImpl bool next() override { const int n = _pattern.length(); - do { + do + { sdfat::File file; file.openNext(_dir.get(), sdfat::O_READ); - if (file) { + if (file) + { _valid = 1; _size = file.fileSize(); _isFile = file.isFile(); _isDirectory = file.isDirectory(); file.getName(_lfn, sizeof(_lfn)); file.close(); - } else { + } + else + { _valid = 0; } - } while(_valid && strncmp((const char*) _lfn, _pattern.c_str(), n) != 0); + } while (_valid && strncmp((const char*) _lfn, _pattern.c_str(), n) != 0); return _valid; } diff --git a/libraries/SDFS/src/SDFSFormatter.h b/libraries/SDFS/src/SDFSFormatter.h index d1fe6c94a7..e2807cd849 100644 --- a/libraries/SDFS/src/SDFSFormatter.h +++ b/libraries/SDFS/src/SDFSFormatter.h @@ -1,24 +1,24 @@ /* - SDFSFormatter.cpp - Formatter for SdFat SD cards - Copyright (c) 2019 Earle F. Philhower, III. All rights reserved. + SDFSFormatter.cpp - Formatter for SdFat SD cards + Copyright (c) 2019 Earle F. Philhower, III. All rights reserved. - A C++ implementation of the SdFat/examples/SdFormatter sketch: - | Copyright (c) 2011-2018 Bill Greiman + A C++ implementation of the SdFat/examples/SdFormatter sketch: + | Copyright (c) 2011-2018 Bill Greiman - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef _SDFSFORMATTER_H #define _SDFSFORMATTER_H @@ -27,9 +27,11 @@ #include #include -namespace sdfs { +namespace sdfs +{ -class SDFSFormatter { +class SDFSFormatter +{ private: // Taken from main FS object sdfat::Sd2Card *card; @@ -55,59 +57,73 @@ class SDFSFormatter { uint32_t fatSize; uint32_t dataStart; - uint8_t writeCache(uint32_t lbn) { + uint8_t writeCache(uint32_t lbn) + { return card->writeBlock(lbn, cache->data); } - void clearCache(uint8_t addSig) { + void clearCache(uint8_t addSig) + { memset(cache, 0, sizeof(*cache)); - if (addSig) { + if (addSig) + { cache->mbr.mbrSig0 = sdfat::BOOTSIG0; cache->mbr.mbrSig1 = sdfat::BOOTSIG1; } } - bool clearFatDir(uint32_t bgn, uint32_t count) { + bool clearFatDir(uint32_t bgn, uint32_t count) + { clearCache(false); - if (!card->writeStart(bgn, count)) { + if (!card->writeStart(bgn, count)) + { DEBUGV("SDFS: Clear FAT/DIR writeStart failed"); return false; } - esp8266::polledTimeout::periodicFastMs timeToYield(5); // Yield every 5ms of runtime - for (uint32_t i = 0; i < count; i++) { - if (timeToYield) { + esp8266::polledTimeout::periodicFastMs timeToYield(5); // Yield every 5ms of runtime + for (uint32_t i = 0; i < count; i++) + { + if (timeToYield) + { delay(0); // WDT feed } - if (!card->writeData(cache->data)) { + if (!card->writeData(cache->data)) + { DEBUGV("SDFS: Clear FAT/DIR writeData failed"); return false; } } - if (!card->writeStop()) { + if (!card->writeStop()) + { DEBUGV("SDFS: Clear FAT/DIR writeStop failed"); return false; } return true; } - uint16_t lbnToCylinder(uint32_t lbn) { + uint16_t lbnToCylinder(uint32_t lbn) + { return lbn / (numberOfHeads * sectorsPerTrack); } - uint8_t lbnToHead(uint32_t lbn) { + uint8_t lbnToHead(uint32_t lbn) + { return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack; } - uint8_t lbnToSector(uint32_t lbn) { + uint8_t lbnToSector(uint32_t lbn) + { return (lbn % sectorsPerTrack) + 1; } - bool writeMbr() { + bool writeMbr() + { clearCache(true); sdfat::part_t* p = cache->mbr.part; p->boot = 0; uint16_t c = lbnToCylinder(relSector); - if (c > 1023) { + if (c > 1023) + { DEBUGV("SDFS: MBR CHS"); return false; } @@ -118,12 +134,15 @@ class SDFSFormatter { p->type = partType; uint32_t endLbn = relSector + partSize - 1; c = lbnToCylinder(endLbn); - if (c <= 1023) { + if (c <= 1023) + { p->endCylinderHigh = c >> 8; p->endCylinderLow = c & 0XFF; p->endHead = lbnToHead(endLbn); p->endSector = lbnToSector(endLbn); - } else { + } + else + { // Too big flag, c = 1023, h = 254, s = 63 p->endCylinderHigh = 3; p->endCylinderLow = 255; @@ -132,46 +151,58 @@ class SDFSFormatter { } p->firstSector = relSector; p->totalSectors = partSize; - if (!writeCache(0)) { + if (!writeCache(0)) + { DEBUGV("SDFS: write MBR"); - return false; + return false; } return true; } - uint32_t volSerialNumber() { + uint32_t volSerialNumber() + { return (cardSizeBlocks << 8) + micros(); } - bool makeFat16() { + bool makeFat16() + { uint16_t const BU16 = 128; uint32_t nc; - for (dataStart = 2 * BU16;; dataStart += BU16) { - nc = (cardSizeBlocks - dataStart)/sectorsPerCluster; - fatSize = (nc + 2 + 255)/256; + for (dataStart = 2 * BU16;; dataStart += BU16) + { + nc = (cardSizeBlocks - dataStart) / sectorsPerCluster; + fatSize = (nc + 2 + 255) / 256; uint32_t r = BU16 + 1 + 2 * fatSize + 32; - if (dataStart < r) { + if (dataStart < r) + { continue; } relSector = dataStart - r + BU16; break; } // check valid cluster count for FAT16 volume - if (nc < 4085 || nc >= 65525) { + if (nc < 4085 || nc >= 65525) + { DEBUGV("SDFS: Bad cluster count"); } reservedSectors = 1; fatStart = relSector + reservedSectors; partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32; - if (partSize < 32680) { + if (partSize < 32680) + { partType = 0X01; - } else if (partSize < 65536) { + } + else if (partSize < 65536) + { partType = 0X04; - } else { + } + else + { partType = 0X06; } // write MBR - if (!writeMbr()) { + if (!writeMbr()) + { DEBUGV("SDFS: writembr failed"); return false; } @@ -181,7 +212,8 @@ class SDFSFormatter { pb->jump[0] = 0XEB; pb->jump[1] = 0X00; pb->jump[2] = 0X90; - for (uint8_t i = 0; i < sizeof(pb->oemId); i++) { + for (uint8_t i = 0; i < sizeof(pb->oemId); i++) + { pb->oemId[i] = ' '; } pb->bytesPerSector = 512; @@ -201,12 +233,14 @@ class SDFSFormatter { memcpy_P(pb->volumeLabel, PSTR("NO NAME "), sizeof(pb->volumeLabel)); memcpy_P(pb->fileSystemType, PSTR("FAT16 "), sizeof(pb->fileSystemType)); // write partition boot sector - if (!writeCache(relSector)) { + if (!writeCache(relSector)) + { DEBUGV("SDFS: FAT16 write PBS failed"); return false; } // clear FAT and root directory - if (!clearFatDir(fatStart, dataStart - fatStart)) { + if (!clearFatDir(fatStart, dataStart - fatStart)) + { DEBUGV("SDFS: FAT16 clear root failed\n"); return false; } @@ -214,27 +248,32 @@ class SDFSFormatter { cache->fat16[0] = 0XFFF8; cache->fat16[1] = 0XFFFF; // write first block of FAT and backup for reserved clusters - if (!writeCache(fatStart) || !writeCache(fatStart + fatSize)) { + if (!writeCache(fatStart) || !writeCache(fatStart + fatSize)) + { DEBUGV("FAT16 reserve failed"); return false; } return true; } - bool makeFat32() { + bool makeFat32() + { uint16_t const BU32 = 8192; uint32_t nc; relSector = BU32; - for (dataStart = 2 * BU32;; dataStart += BU32) { - nc = (cardSizeBlocks - dataStart)/sectorsPerCluster; - fatSize = (nc + 2 + 127)/128; + for (dataStart = 2 * BU32;; dataStart += BU32) + { + nc = (cardSizeBlocks - dataStart) / sectorsPerCluster; + fatSize = (nc + 2 + 127) / 128; uint32_t r = relSector + 9 + 2 * fatSize; - if (dataStart >= r) { + if (dataStart >= r) + { break; } } // error if too few clusters in FAT32 volume - if (nc < 65525) { + if (nc < 65525) + { DEBUGV("SDFS: Bad cluster count"); return false; } @@ -243,14 +282,18 @@ class SDFSFormatter { partSize = nc * sectorsPerCluster + dataStart - relSector; // type depends on address of end sector // max CHS has lbn = 16450560 = 1024*255*63 - if ((relSector + partSize) <= 16450560) { + if ((relSector + partSize) <= 16450560) + { // FAT32 partType = 0X0B; - } else { + } + else + { // FAT32 with INT 13 partType = 0X0C; } - if (!writeMbr()) { + if (!writeMbr()) + { DEBUGV("SDFS: writembr failed"); return false; } @@ -261,7 +304,8 @@ class SDFSFormatter { pb->jump[0] = 0XEB; pb->jump[1] = 0X00; pb->jump[2] = 0X90; - for (uint8_t i = 0; i < sizeof(pb->oemId); i++) { + for (uint8_t i = 0; i < sizeof(pb->oemId); i++) + { pb->oemId[i] = ' '; } pb->bytesPerSector = 512; @@ -283,13 +327,15 @@ class SDFSFormatter { memcpy_P(pb->volumeLabel, PSTR("NO NAME "), sizeof(pb->volumeLabel)); memcpy_P(pb->fileSystemType, PSTR("FAT32 "), sizeof(pb->fileSystemType)); // write partition boot sector and backup - if (!writeCache(relSector) || !writeCache(relSector + 6)) { + if (!writeCache(relSector) || !writeCache(relSector + 6)) + { DEBUGV("SDFS: FAT32 write PBS failed"); return false; } clearCache(true); // write extra boot area and backup - if (!writeCache(relSector + 2) || !writeCache(relSector + 8)) { + if (!writeCache(relSector + 2) || !writeCache(relSector + 8)) + { DEBUGV("SDFS: FAT32 PBS ext failed"); return false; } @@ -299,7 +345,8 @@ class SDFSFormatter { pf->freeCount = 0XFFFFFFFF; pf->nextFree = 0XFFFFFFFF; // write FSINFO sector and backup - if (!writeCache(relSector + 1) || !writeCache(relSector + 7)) { + if (!writeCache(relSector + 1) || !writeCache(relSector + 7)) + { DEBUGV("SDFS: FAT32 FSINFO failed"); return false; } @@ -309,43 +356,62 @@ class SDFSFormatter { cache->fat32[1] = 0x0FFFFFFF; cache->fat32[2] = 0x0FFFFFFF; // write first block of FAT and backup for reserved clusters - if (!writeCache(fatStart) || !writeCache(fatStart + fatSize)) { + if (!writeCache(fatStart) || !writeCache(fatStart + fatSize)) + { DEBUGV("SDFS: FAT32 reserve failed"); return false; } - return true; + return true; } public: - bool format(sdfat::SdFat *_fs, int8_t _csPin, SPISettings _spiSettings) { + bool format(sdfat::SdFat *_fs, int8_t _csPin, SPISettings _spiSettings) + { card = static_cast(_fs->card()); cache = _fs->cacheClear(); - if (!card->begin(_csPin, _spiSettings)) { + if (!card->begin(_csPin, _spiSettings)) + { return false; } cardSizeBlocks = card->cardSize(); - if (cardSizeBlocks == 0) { + if (cardSizeBlocks == 0) + { return false; } - cardCapacityMB = (cardSizeBlocks + 2047)/2048; + cardCapacityMB = (cardSizeBlocks + 2047) / 2048; - if (cardCapacityMB <= 6) { + if (cardCapacityMB <= 6) + { return false; // Card is too small - } else if (cardCapacityMB <= 16) { + } + else if (cardCapacityMB <= 16) + { sectorsPerCluster = 2; - } else if (cardCapacityMB <= 32) { + } + else if (cardCapacityMB <= 32) + { sectorsPerCluster = 4; - } else if (cardCapacityMB <= 64) { + } + else if (cardCapacityMB <= 64) + { sectorsPerCluster = 8; - } else if (cardCapacityMB <= 128) { + } + else if (cardCapacityMB <= 128) + { sectorsPerCluster = 16; - } else if (cardCapacityMB <= 1024) { + } + else if (cardCapacityMB <= 1024) + { sectorsPerCluster = 32; - } else if (cardCapacityMB <= 32768) { + } + else if (cardCapacityMB <= 32768) + { sectorsPerCluster = 64; - } else { + } + else + { // SDXC cards sectorsPerCluster = 128; } @@ -353,21 +419,36 @@ class SDFSFormatter { // set fake disk geometry sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63; - if (cardCapacityMB <= 16) { + if (cardCapacityMB <= 16) + { numberOfHeads = 2; - } else if (cardCapacityMB <= 32) { + } + else if (cardCapacityMB <= 32) + { numberOfHeads = 4; - } else if (cardCapacityMB <= 128) { + } + else if (cardCapacityMB <= 128) + { numberOfHeads = 8; - } else if (cardCapacityMB <= 504) { + } + else if (cardCapacityMB <= 504) + { numberOfHeads = 16; - } else if (cardCapacityMB <= 1008) { + } + else if (cardCapacityMB <= 1008) + { numberOfHeads = 32; - } else if (cardCapacityMB <= 2016) { + } + else if (cardCapacityMB <= 2016) + { numberOfHeads = 64; - } else if (cardCapacityMB <= 4032) { + } + else if (cardCapacityMB <= 4032) + { numberOfHeads = 128; - } else { + } + else + { numberOfHeads = 255; } @@ -375,25 +456,32 @@ class SDFSFormatter { uint32_t const ERASE_SIZE = 262144L; uint32_t firstBlock = 0; uint32_t lastBlock; - do { + do + { lastBlock = firstBlock + ERASE_SIZE - 1; - if (lastBlock >= cardSizeBlocks) { + if (lastBlock >= cardSizeBlocks) + { lastBlock = cardSizeBlocks - 1; } - if (!card->erase(firstBlock, lastBlock)) { + if (!card->erase(firstBlock, lastBlock)) + { return false; // Erase fail } delay(0); // yield to the OS to avoid WDT firstBlock += ERASE_SIZE; } while (firstBlock < cardSizeBlocks); - if (!card->readBlock(0, cache->data)) { + if (!card->readBlock(0, cache->data)) + { return false; } - if (card->type() != sdfat::SD_CARD_TYPE_SDHC) { + if (card->type() != sdfat::SD_CARD_TYPE_SDHC) + { return makeFat16(); - } else { + } + else + { return makeFat32(); } } diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 09336266c4..3fd4a761cd 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -1,23 +1,23 @@ -/* - SPI.cpp - SPI library for esp8266 - - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ +/* + SPI.cpp - SPI library for esp8266 + + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include "SPI.h" #include "HardwareSerial.h" @@ -28,18 +28,21 @@ #define SPI_OVERLAP_SS 0 -typedef union { - uint32_t regValue; - struct { - unsigned regL :6; - unsigned regH :6; - unsigned regN :6; - unsigned regPre :13; - unsigned regEQU :1; - }; +typedef union +{ + uint32_t regValue; + struct + { + unsigned regL : 6; + unsigned regH : 6; + unsigned regN : 6; + unsigned regPre : 13; + unsigned regEQU : 1; + }; } spiClk_t; -SPIClass::SPIClass() { +SPIClass::SPIClass() +{ useHwCs = false; pinSet = SPI_PINS_HSPI; } @@ -47,23 +50,30 @@ SPIClass::SPIClass() { bool SPIClass::pins(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) { if (sck == 6 && - miso == 7 && - mosi == 8 && - ss == 0) { + miso == 7 && + mosi == 8 && + ss == 0) + { pinSet = SPI_PINS_HSPI_OVERLAP; - } else if (sck == 14 && - miso == 12 && - mosi == 13) { + } + else if (sck == 14 && + miso == 12 && + mosi == 13) + { pinSet = SPI_PINS_HSPI; - } else { + } + else + { return false; } return true; } -void SPIClass::begin() { - switch (pinSet) { +void SPIClass::begin() +{ + switch (pinSet) + { case SPI_PINS_HSPI_OVERLAP: IOSWAP |= (1 << IOSWAP2CS); //SPI0E3 |= 0x1; This is in the MP3_DECODER example, but makes the WD kick in here. @@ -86,19 +96,23 @@ void SPIClass::begin() { SPI1C1 = 0; } -void SPIClass::end() { - switch (pinSet) { +void SPIClass::end() +{ + switch (pinSet) + { case SPI_PINS_HSPI: pinMode(SCK, INPUT); pinMode(MISO, INPUT); pinMode(MOSI, INPUT); - if (useHwCs) { + if (useHwCs) + { pinMode(SS, INPUT); } break; case SPI_PINS_HSPI_OVERLAP: IOSWAP &= ~(1 << IOSWAP2CS); - if (useHwCs) { + if (useHwCs) + { SPI1P |= SPIPCS1DIS | SPIPCS0DIS | SPIPCS2DIS; pinMode(SPI_OVERLAP_SS, INPUT); } @@ -106,28 +120,37 @@ void SPIClass::end() { } } -void SPIClass::setHwCs(bool use) { - switch (pinSet) { +void SPIClass::setHwCs(bool use) +{ + switch (pinSet) + { case SPI_PINS_HSPI: - if (use) { + if (use) + { pinMode(SS, SPECIAL); ///< GPIO15 SPI1U |= (SPIUCSSETUP | SPIUCSHOLD); - } else { - if (useHwCs) { + } + else + { + if (useHwCs) + { pinMode(SS, INPUT); - SPI1U &= ~(SPIUCSSETUP | SPIUCSHOLD); + SPI1U &= ~(SPIUCSSETUP | SPIUCSHOLD); } } break; case SPI_PINS_HSPI_OVERLAP: - if (use) { + if (use) + { pinMode(SPI_OVERLAP_SS, FUNCTION_1); // GPI0 to SPICS2 mode SPI1P &= ~SPIPCS2DIS; SPI1P |= SPIPCS1DIS | SPIPCS0DIS; SPI1U |= (SPIUCSSETUP | SPIUCSHOLD); } - else { - if (useHwCs) { + else + { + if (useHwCs) + { pinMode(SPI_OVERLAP_SS, INPUT); SPI1P |= SPIPCS1DIS | SPIPCS0DIS | SPIPCS2DIS; SPI1U &= ~(SPIUCSSETUP | SPIUCSHOLD); @@ -139,82 +162,102 @@ void SPIClass::setHwCs(bool use) { useHwCs = use; } -void SPIClass::beginTransaction(SPISettings settings) { - while(SPI1CMD & SPIBUSY) {} +void SPIClass::beginTransaction(SPISettings settings) +{ + while (SPI1CMD & SPIBUSY) {} setFrequency(settings._clock); setBitOrder(settings._bitOrder); setDataMode(settings._dataMode); } -void SPIClass::endTransaction() { +void SPIClass::endTransaction() +{ } -void SPIClass::setDataMode(uint8_t dataMode) { +void SPIClass::setDataMode(uint8_t dataMode) +{ /** - SPI_MODE0 0x00 - CPOL: 0 CPHA: 0 - SPI_MODE1 0x01 - CPOL: 0 CPHA: 1 - SPI_MODE2 0x10 - CPOL: 1 CPHA: 0 - SPI_MODE3 0x11 - CPOL: 1 CPHA: 1 - */ + SPI_MODE0 0x00 - CPOL: 0 CPHA: 0 + SPI_MODE1 0x01 - CPOL: 0 CPHA: 1 + SPI_MODE2 0x10 - CPOL: 1 CPHA: 0 + SPI_MODE3 0x11 - CPOL: 1 CPHA: 1 + */ bool CPOL = (dataMode & 0x10); ///< CPOL (Clock Polarity) bool CPHA = (dataMode & 0x01); ///< CPHA (Clock Phase) // https://github.com/esp8266/Arduino/issues/2416 // https://github.com/esp8266/Arduino/pull/2418 - if(CPOL) // Ensure same behavior as + if (CPOL) // Ensure same behavior as + { CPHA ^= 1; // SAM, AVR and Intel Boards + } - if(CPHA) { + if (CPHA) + { SPI1U |= (SPIUSME); - } else { + } + else + { SPI1U &= ~(SPIUSME); } - if(CPOL) { - SPI1P |= 1<<29; - } else { - SPI1P &= ~(1<<29); + if (CPOL) + { + SPI1P |= 1 << 29; + } + else + { + SPI1P &= ~(1 << 29); //todo test whether it is correct to set CPOL like this. } } -void SPIClass::setBitOrder(uint8_t bitOrder) { - if(bitOrder == MSBFIRST) { +void SPIClass::setBitOrder(uint8_t bitOrder) +{ + if (bitOrder == MSBFIRST) + { SPI1C &= ~(SPICWBO | SPICRBO); - } else { + } + else + { SPI1C |= (SPICWBO | SPICRBO); } } /** - * calculate the Frequency based on the register value - * @param reg - * @return - */ -static uint32_t ClkRegToFreq(spiClk_t * reg) { + calculate the Frequency based on the register value + @param reg + @return +*/ +static uint32_t ClkRegToFreq(spiClk_t * reg) +{ return (ESP8266_CLOCK / ((reg->regPre + 1) * (reg->regN + 1))); } -void SPIClass::setFrequency(uint32_t freq) { +void SPIClass::setFrequency(uint32_t freq) +{ static uint32_t lastSetFrequency = 0; static uint32_t lastSetRegister = 0; - if(freq >= ESP8266_CLOCK) { + if (freq >= ESP8266_CLOCK) + { setClockDivider(0x80000000); return; } - if(lastSetFrequency == freq && lastSetRegister == SPI1CLK) { + if (lastSetFrequency == freq && lastSetRegister == SPI1CLK) + { // do nothing (speed optimization) return; } const spiClk_t minFreqReg = { 0x7FFFF000 }; uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg); - if(freq < minFreq) { + if (freq < minFreq) + { // use minimum possible clock setClockDivider(minFreqReg.regValue); lastSetRegister = SPI1CLK; @@ -228,7 +271,8 @@ void SPIClass::setFrequency(uint32_t freq) { int32_t bestFreq = 0; // find the best match - while(calN <= 0x3F) { // 0x3F max for N + while (calN <= 0x3F) // 0x3F max for N + { spiClk_t reg = { 0 }; int32_t calFreq; @@ -237,13 +281,19 @@ void SPIClass::setFrequency(uint32_t freq) { reg.regN = calN; - while(calPreVari++ <= 1) { // test different variants for Pre (we calculate in int so we miss the decimals, testing is the easyest and fastest way) + while (calPreVari++ <= 1) // test different variants for Pre (we calculate in int so we miss the decimals, testing is the easyest and fastest way) + { calPre = (((ESP8266_CLOCK / (reg.regN + 1)) / freq) - 1) + calPreVari; - if(calPre > 0x1FFF) { + if (calPre > 0x1FFF) + { reg.regPre = 0x1FFF; // 8191 - } else if(calPre <= 0) { + } + else if (calPre <= 0) + { reg.regPre = 0; - } else { + } + else + { reg.regPre = calPre; } @@ -254,19 +304,24 @@ void SPIClass::setFrequency(uint32_t freq) { calFreq = ClkRegToFreq(®); //os_printf("-----[0x%08X][%d]\t EQU: %d\t Pre: %d\t N: %d\t H: %d\t L: %d = %d\n", reg.regValue, freq, reg.regEQU, reg.regPre, reg.regN, reg.regH, reg.regL, calFreq); - if(calFreq == (int32_t) freq) { + if (calFreq == (int32_t) freq) + { // accurate match use it! memcpy(&bestReg, ®, sizeof(bestReg)); break; - } else if(calFreq < (int32_t) freq) { + } + else if (calFreq < (int32_t) freq) + { // never go over the requested frequency - if(abs(freq - calFreq) < abs(freq - bestFreq)) { + if (abs(freq - calFreq) < abs(freq - bestFreq)) + { bestFreq = calFreq; memcpy(&bestReg, ®, sizeof(bestReg)); } } } - if(calFreq == (int32_t) freq) { + if (calFreq == (int32_t) freq) + { // accurate match use it! break; } @@ -281,46 +336,58 @@ void SPIClass::setFrequency(uint32_t freq) { } -void SPIClass::setClockDivider(uint32_t clockDiv) { - if(clockDiv == 0x80000000) { +void SPIClass::setClockDivider(uint32_t clockDiv) +{ + if (clockDiv == 0x80000000) + { GPMUX |= (1 << 9); // Set bit 9 if sysclock required - } else { + } + else + { GPMUX &= ~(1 << 9); } SPI1CLK = clockDiv; } -inline void SPIClass::setDataBits(uint16_t bits) { +inline void SPIClass::setDataBits(uint16_t bits) +{ const uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); bits--; SPI1U1 = ((SPI1U1 & mask) | ((bits << SPILMOSI) | (bits << SPILMISO))); } -uint8_t SPIClass::transfer(uint8_t data) { - while(SPI1CMD & SPIBUSY) {} +uint8_t SPIClass::transfer(uint8_t data) +{ + while (SPI1CMD & SPIBUSY) {} // reset to 8Bit mode setDataBits(8); SPI1W0 = data; SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} - return (uint8_t) (SPI1W0 & 0xff); + while (SPI1CMD & SPIBUSY) {} + return (uint8_t)(SPI1W0 & 0xff); } -uint16_t SPIClass::transfer16(uint16_t data) { - union { - uint16_t val; - struct { - uint8_t lsb; - uint8_t msb; - }; +uint16_t SPIClass::transfer16(uint16_t data) +{ + union + { + uint16_t val; + struct + { + uint8_t lsb; + uint8_t msb; + }; } in, out; in.val = data; - if((SPI1C & (SPICWBO | SPICRBO))) { + if ((SPI1C & (SPICWBO | SPICRBO))) + { //LSBFIRST out.lsb = transfer(in.lsb); out.msb = transfer(in.msb); - } else { + } + else + { //MSBFIRST out.msb = transfer(in.msb); out.lsb = transfer(in.lsb); @@ -328,12 +395,15 @@ uint16_t SPIClass::transfer16(uint16_t data) { return out.val; } -void SPIClass::transfer(void *buf, uint16_t count) { +void SPIClass::transfer(void *buf, uint16_t count) +{ uint8_t *cbuf = reinterpret_cast(buf); // cbuf may not be 32bits-aligned for (; (((unsigned long)cbuf) & 3) && count; cbuf++, count--) + { *cbuf = transfer(*cbuf); + } // cbuf is now aligned // count may not be a multiple of 4 @@ -344,49 +414,61 @@ void SPIClass::transfer(void *buf, uint16_t count) { cbuf += count4; count -= count4; for (; count; cbuf++, count--) + { *cbuf = transfer(*cbuf); + } } -void SPIClass::write(uint8_t data) { - while(SPI1CMD & SPIBUSY) {} +void SPIClass::write(uint8_t data) +{ + while (SPI1CMD & SPIBUSY) {} // reset to 8Bit mode setDataBits(8); SPI1W0 = data; SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} + while (SPI1CMD & SPIBUSY) {} } -void SPIClass::write16(uint16_t data) { +void SPIClass::write16(uint16_t data) +{ write16(data, !(SPI1C & (SPICWBO | SPICRBO))); } -void SPIClass::write16(uint16_t data, bool msb) { - while(SPI1CMD & SPIBUSY) {} +void SPIClass::write16(uint16_t data, bool msb) +{ + while (SPI1CMD & SPIBUSY) {} // Set to 16Bits transfer setDataBits(16); - if(msb) { + if (msb) + { // MSBFIRST Byte first SPI1W0 = (data >> 8) | (data << 8); - } else { + } + else + { // LSBFIRST Byte first SPI1W0 = data; } SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} + while (SPI1CMD & SPIBUSY) {} } -void SPIClass::write32(uint32_t data) { +void SPIClass::write32(uint32_t data) +{ write32(data, !(SPI1C & (SPICWBO | SPICRBO))); } -void SPIClass::write32(uint32_t data, bool msb) { - while(SPI1CMD & SPIBUSY) {} +void SPIClass::write32(uint32_t data, bool msb) +{ + while (SPI1CMD & SPIBUSY) {} // Set to 32Bits transfer setDataBits(32); - if(msb) { - union { - uint32_t l; - uint8_t b[4]; + if (msb) + { + union + { + uint32_t l; + uint8_t b[4]; } data_; data_.l = data; // MSBFIRST Byte first @@ -394,31 +476,37 @@ void SPIClass::write32(uint32_t data, bool msb) { } SPI1W0 = data; SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} + while (SPI1CMD & SPIBUSY) {} } /** - * Note: - * data need to be aligned to 32Bit - * or you get an Fatal exception (9) - * @param data uint8_t * - * @param size uint32_t - */ -void SPIClass::writeBytes(const uint8_t * data, uint32_t size) { - while(size) { - if(size > 64) { + Note: + data need to be aligned to 32Bit + or you get an Fatal exception (9) + @param data uint8_t + @param size uint32_t +*/ +void SPIClass::writeBytes(const uint8_t * data, uint32_t size) +{ + while (size) + { + if (size > 64) + { writeBytes_(data, 64); size -= 64; data += 64; - } else { + } + else + { writeBytes_(data, size); size = 0; } } } -void SPIClass::writeBytes_(const uint8_t * data, uint8_t size) { - while(SPI1CMD & SPIBUSY) {} +void SPIClass::writeBytes_(const uint8_t * data, uint8_t size) +{ + while (SPI1CMD & SPIBUSY) {} // Set Bits to transfer setDataBits(size * 8); @@ -426,7 +514,8 @@ void SPIClass::writeBytes_(const uint8_t * data, uint8_t size) { const uint32_t * dataPtr = (uint32_t*) data; uint32_t dataSize = ((size + 3) / 4); - while(dataSize--) { + while (dataSize--) + { *fifoPtr = *dataPtr; dataPtr++; fifoPtr++; @@ -434,33 +523,40 @@ void SPIClass::writeBytes_(const uint8_t * data, uint8_t size) { __sync_synchronize(); SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} + while (SPI1CMD & SPIBUSY) {} } /** - * @param data uint8_t * - * @param size uint8_t max for size is 64Byte - * @param repeat uint32_t - */ -void SPIClass::writePattern(const uint8_t * data, uint8_t size, uint32_t repeat) { - if(size > 64) return; //max Hardware FIFO + @param data uint8_t + @param size uint8_t max for size is 64Byte + @param repeat uint32_t +*/ +void SPIClass::writePattern(const uint8_t * data, uint8_t size, uint32_t repeat) +{ + if (size > 64) + { + return; //max Hardware FIFO + } - while(SPI1CMD & SPIBUSY) {} + while (SPI1CMD & SPIBUSY) {} uint32_t buffer[16]; - uint8_t *bufferPtr=(uint8_t *)&buffer; + uint8_t *bufferPtr = (uint8_t *)&buffer; const uint8_t *dataPtr = data; volatile uint32_t * fifoPtr = &SPI1W0; uint8_t r; uint32_t repeatRem; uint8_t i; - if((repeat * size) <= 64){ + if ((repeat * size) <= 64) + { repeatRem = repeat * size; r = repeat; - while(r--){ + while (r--) + { dataPtr = data; - for(i=0; i 64) { + @param out uint8_t + @param in uint8_t + @param size uint32_t +*/ +void SPIClass::transferBytes(const uint8_t * out, uint8_t * in, uint32_t size) +{ + while (size) + { + if (size > 64) + { transferBytes_(out, in, 64); size -= 64; - if(out) out += 64; - if(in) in += 64; - } else { + if (out) + { + out += 64; + } + if (in) + { + in += 64; + } + } + else + { transferBytes_(out, in, size); size = 0; } @@ -533,74 +653,91 @@ void SPIClass::transferBytes(const uint8_t * out, uint8_t * in, uint32_t size) { } /** - * Note: - * in and out need to be aligned to 32Bit - * or you get an Fatal exception (9) - * @param out uint8_t * - * @param in uint8_t * - * @param size uint8_t (max 64) - */ - -void SPIClass::transferBytesAligned_(const uint8_t * out, uint8_t * in, uint8_t size) { + Note: + in and out need to be aligned to 32Bit + or you get an Fatal exception (9) + @param out uint8_t + @param in uint8_t + @param size uint8_t (max 64) +*/ + +void SPIClass::transferBytesAligned_(const uint8_t * out, uint8_t * in, uint8_t size) +{ if (!size) + { return; + } - while(SPI1CMD & SPIBUSY) {} + while (SPI1CMD & SPIBUSY) {} // Set in/out Bits to transfer setDataBits(size * 8); volatile uint32_t *fifoPtr = &SPI1W0; - if (out) { + if (out) + { uint8_t outSize = ((size + 3) / 4); uint32_t *dataPtr = (uint32_t*) out; - while (outSize--) { + while (outSize--) + { *(fifoPtr++) = *(dataPtr++); } - } else { + } + else + { uint8_t outSize = ((size + 3) / 4); // no out data only read fill with dummy data! - while (outSize--) { + while (outSize--) + { *(fifoPtr++) = 0xFFFFFFFF; } } SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} + while (SPI1CMD & SPIBUSY) {} - if (in) { + if (in) + { uint32_t *dataPtr = (uint32_t*) in; fifoPtr = &SPI1W0; int inSize = size; // Unlike outSize above, inSize tracks *bytes* since we must transfer only the requested bytes to the app to avoid overwriting other vars. - while (inSize >= 4) { + while (inSize >= 4) + { *(dataPtr++) = *(fifoPtr++); inSize -= 4; in += 4; } volatile uint8_t *fifoPtrB = (volatile uint8_t *)fifoPtr; - while (inSize--) { + while (inSize--) + { *(in++) = *(fifoPtrB++); } } } -void SPIClass::transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size) { - if (!((uint32_t)out & 3) && !((uint32_t)in & 3)) { +void SPIClass::transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size) +{ + if (!((uint32_t)out & 3) && !((uint32_t)in & 3)) + { // Input and output are both 32b aligned or NULL transferBytesAligned_(out, in, size); - } else { + } + else + { // HW FIFO has 64b limit and ::transferBytes breaks up large xfers into 64byte chunks before calling this function // We know at this point at least one direction is misaligned, so use temporary buffer to align everything // No need for separate out and in aligned copies, we can overwrite our out copy with the input data safely uint8_t aligned[64]; // Stack vars will be 32b aligned - if (out) { + if (out) + { memcpy(aligned, out, size); } transferBytesAligned_(out ? aligned : nullptr, in ? aligned : nullptr, size); - if (in) { + if (in) + { memcpy(in, aligned, size); } } diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index e8fb8a3902..3c31e075b8 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -1,22 +1,22 @@ -/* - SPI.h - SPI library for esp8266 +/* + SPI.h - SPI library for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SPI_H_INCLUDED #define _SPI_H_INCLUDED @@ -41,46 +41,48 @@ const uint8_t SPI_MODE1 = 0x01; ///< CPOL: 0 CPHA: 1 const uint8_t SPI_MODE2 = 0x10; ///< CPOL: 1 CPHA: 0 const uint8_t SPI_MODE3 = 0x11; ///< CPOL: 1 CPHA: 1 -class SPISettings { +class SPISettings +{ public: - SPISettings() :_clock(1000000), _bitOrder(LSBFIRST), _dataMode(SPI_MODE0){} - SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) :_clock(clock), _bitOrder(bitOrder), _dataMode(dataMode){} - uint32_t _clock; - uint8_t _bitOrder; - uint8_t _dataMode; + SPISettings() : _clock(1000000), _bitOrder(LSBFIRST), _dataMode(SPI_MODE0) {} + SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) : _clock(clock), _bitOrder(bitOrder), _dataMode(dataMode) {} + uint32_t _clock; + uint8_t _bitOrder; + uint8_t _dataMode; }; -class SPIClass { +class SPIClass +{ public: - SPIClass(); - bool pins(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); - void begin(); - void end(); - void setHwCs(bool use); - void setBitOrder(uint8_t bitOrder); - void setDataMode(uint8_t dataMode); - void setFrequency(uint32_t freq); - void setClockDivider(uint32_t clockDiv); - void beginTransaction(SPISettings settings); - uint8_t transfer(uint8_t data); - uint16_t transfer16(uint16_t data); - void transfer(void *buf, uint16_t count); - void write(uint8_t data); - void write16(uint16_t data); - void write16(uint16_t data, bool msb); - void write32(uint32_t data); - void write32(uint32_t data, bool msb); - void writeBytes(const uint8_t * data, uint32_t size); - void writePattern(const uint8_t * data, uint8_t size, uint32_t repeat); - void transferBytes(const uint8_t * out, uint8_t * in, uint32_t size); - void endTransaction(void); + SPIClass(); + bool pins(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); + void begin(); + void end(); + void setHwCs(bool use); + void setBitOrder(uint8_t bitOrder); + void setDataMode(uint8_t dataMode); + void setFrequency(uint32_t freq); + void setClockDivider(uint32_t clockDiv); + void beginTransaction(SPISettings settings); + uint8_t transfer(uint8_t data); + uint16_t transfer16(uint16_t data); + void transfer(void *buf, uint16_t count); + void write(uint8_t data); + void write16(uint16_t data); + void write16(uint16_t data, bool msb); + void write32(uint32_t data); + void write32(uint32_t data, bool msb); + void writeBytes(const uint8_t * data, uint32_t size); + void writePattern(const uint8_t * data, uint8_t size, uint32_t repeat); + void transferBytes(const uint8_t * out, uint8_t * in, uint32_t size); + void endTransaction(void); private: - bool useHwCs; - uint8_t pinSet; - void writeBytes_(const uint8_t * data, uint8_t size); - void transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size); - void transferBytesAligned_(const uint8_t * out, uint8_t * in, uint8_t size); - inline void setDataBits(uint16_t bits); + bool useHwCs; + uint8_t pinSet; + void writeBytes_(const uint8_t * data, uint8_t size); + void transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size); + void transferBytesAligned_(const uint8_t * out, uint8_t * in, uint8_t size); + inline void setDataBits(uint16_t bits); }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPI) diff --git a/libraries/SPISlave/src/SPISlave.cpp b/libraries/SPISlave/src/SPISlave.cpp index a88915b518..dd8a48190a 100644 --- a/libraries/SPISlave/src/SPISlave.cpp +++ b/libraries/SPISlave/src/SPISlave.cpp @@ -1,22 +1,22 @@ /* - SPISlave library for esp8266 + SPISlave library for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "SPISlave.h" extern "C" { @@ -25,31 +25,35 @@ extern "C" { void SPISlaveClass::_data_rx(uint8_t * data, uint8_t len) { - if(_data_cb) { + if (_data_cb) + { _data_cb(data, len); } } void SPISlaveClass::_status_rx(uint32_t data) { - if(_status_cb) { + if (_status_cb) + { _status_cb(data); } } void SPISlaveClass::_data_tx(void) { - if(_data_sent_cb) { + if (_data_sent_cb) + { _data_sent_cb(); } } void SPISlaveClass::_status_tx(void) { - if(_status_sent_cb) { + if (_status_sent_cb) + { _status_sent_cb(); } } void SPISlaveClass::_s_data_rx(void *arg, uint8_t * data, uint8_t len) { - reinterpret_cast(arg)->_data_rx(data,len); + reinterpret_cast(arg)->_data_rx(data, len); } void SPISlaveClass::_s_status_rx(void *arg, uint32_t data) { @@ -82,7 +86,8 @@ void SPISlaveClass::end() } void SPISlaveClass::setData(uint8_t * data, size_t len) { - if(len > 32) { + if (len > 32) + { len = 32; } hspi_slave_setData(data, len); diff --git a/libraries/SPISlave/src/SPISlave.h b/libraries/SPISlave/src/SPISlave.h index a52495cf9c..140758d3c9 100644 --- a/libraries/SPISlave/src/SPISlave.h +++ b/libraries/SPISlave/src/SPISlave.h @@ -1,22 +1,22 @@ /* - SPISlave library for esp8266 + SPISlave library for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SPISLAVE_H_INCLUDED #define _SPISLAVE_H_INCLUDED diff --git a/libraries/SPISlave/src/hspi_slave.c b/libraries/SPISlave/src/hspi_slave.c index a2cbf9d466..9295b0b646 100644 --- a/libraries/SPISlave/src/hspi_slave.c +++ b/libraries/SPISlave/src/hspi_slave.c @@ -1,22 +1,22 @@ /* - SPISlave library for esp8266 + SPISlave library for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hspi_slave.h" #include "esp8266_peri.h" @@ -35,48 +35,59 @@ void ICACHE_RAM_ATTR _hspi_slave_isr_handler(void *arg) istatus = SPIIR; - if(istatus & (1 << SPII1)) { //SPI1 ISR + if (istatus & (1 << SPII1)) //SPI1 ISR + { status = SPI1S; SPI1S &= ~(0x3E0);//disable interrupts SPI1S |= SPISSRES;//reset SPI1S &= ~(0x1F);//clear interrupts SPI1S |= (0x3E0);//enable interrupts - if((status & SPISRBIS) != 0 && (_hspi_slave_tx_data_cb)) { + if ((status & SPISRBIS) != 0 && (_hspi_slave_tx_data_cb)) + { _hspi_slave_tx_data_cb(arg); } - if((status & SPISRSIS) != 0 && (_hspi_slave_tx_status_cb)) { + if ((status & SPISRSIS) != 0 && (_hspi_slave_tx_status_cb)) + { _hspi_slave_tx_status_cb(arg); } - if((status & SPISWSIS) != 0 && (_hspi_slave_rx_status_cb)) { + if ((status & SPISWSIS) != 0 && (_hspi_slave_rx_status_cb)) + { uint32_t s = SPI1WS; _hspi_slave_rx_status_cb(arg, s); } - if((status & SPISWBIS) != 0 && (_hspi_slave_rx_data_cb)) { + if ((status & SPISWBIS) != 0 && (_hspi_slave_rx_data_cb)) + { uint8_t i; uint32_t data; _hspi_slave_buffer[32] = 0; - for(i=0; i<8; i++) { - data=SPI1W(i); - _hspi_slave_buffer[i<<2] = data & 0xff; - _hspi_slave_buffer[(i<<2)+1] = (data >> 8) & 0xff; - _hspi_slave_buffer[(i<<2)+2] = (data >> 16) & 0xff; - _hspi_slave_buffer[(i<<2)+3] = (data >> 24) & 0xff; + for (i = 0; i < 8; i++) + { + data = SPI1W(i); + _hspi_slave_buffer[i << 2] = data & 0xff; + _hspi_slave_buffer[(i << 2) + 1] = (data >> 8) & 0xff; + _hspi_slave_buffer[(i << 2) + 2] = (data >> 16) & 0xff; + _hspi_slave_buffer[(i << 2) + 3] = (data >> 24) & 0xff; } _hspi_slave_rx_data_cb(arg, &_hspi_slave_buffer[0], 32); } - } else if(istatus & (1 << SPII0)) { //SPI0 ISR + } + else if (istatus & (1 << SPII0)) //SPI0 ISR + { SPI0S &= ~(0x3ff);//clear SPI ISR - } else if(istatus & (1 << SPII2)) {} //I2S ISR + } + else if (istatus & (1 << SPII2)) {} //I2S ISR } void hspi_slave_begin(uint8_t status_len, void * arg) { status_len &= 7; - if(status_len > 4) { + if (status_len > 4) + { status_len = 4; //max 32 bits } - if(status_len == 0) { + if (status_len == 0) + { status_len = 1; //min 8 bits } @@ -97,25 +108,25 @@ void hspi_slave_begin(uint8_t status_len, void * arg) // Setting SPIC2MOSIDN_S is probably not critical, all tests run fine with this setting SPI1C2 = (0x2 << SPIC2MOSIDN_S) | (0x1 << SPIC2MISODM_S); - ETS_SPI_INTR_ATTACH(_hspi_slave_isr_handler,arg); + ETS_SPI_INTR_ATTACH(_hspi_slave_isr_handler, arg); ETS_SPI_INTR_ENABLE(); } void hspi_slave_end() { - ETS_SPI_INTR_DISABLE(); - ETS_SPI_INTR_ATTACH(NULL, NULL); - - pinMode(SS, INPUT); - pinMode(SCK, INPUT); - pinMode(MISO, INPUT); - pinMode(MOSI, INPUT); - - // defaults - SPI1S = 0; - SPI1U = SPIUSSE | SPIUCOMMAND; - SPI1S1 = 0; - SPI1P = B110; + ETS_SPI_INTR_DISABLE(); + ETS_SPI_INTR_ATTACH(NULL, NULL); + + pinMode(SS, INPUT); + pinMode(SCK, INPUT); + pinMode(MISO, INPUT); + pinMode(MOSI, INPUT); + + // defaults + SPI1S = 0; + SPI1U = SPIUSSE | SPIUCOMMAND; + SPI1S1 = 0; + SPI1P = B110; } void ICACHE_RAM_ATTR hspi_slave_setStatus(uint32_t status) @@ -130,11 +141,13 @@ void hspi_slave_setData(uint8_t *data, uint8_t len) uint8_t bi = 0; uint8_t wi = 8; - for(i=0; i<32; i++) { - out |= (i -void TFT::TFTinit (void) +void TFT::TFTinit(void) { pinMode(2, OUTPUT); pinMode(4, OUTPUT); pinMode(15, OUTPUT); SPI.begin(); SPI.setClockDivider(SPI_CLOCK_DIV2); - + TFT_CS_HIGH; TFT_DC_HIGH; - INT8U i=0; - for(i=0;i<3;i++) + INT8U i = 0; + for (i = 0; i < 3; i++) { readID(); } @@ -149,38 +149,38 @@ void TFT::TFTinit (void) INT8U TFT::readID(void) { - INT8U i=0; + INT8U i = 0; INT8U data[3] ; INT8U ID[3] = {0x00, 0x93, 0x41}; - INT8U ToF=1; - for(i=0;i<3;i++) + INT8U ToF = 1; + for (i = 0; i < 3; i++) { - data[i]=Read_Register(0xd3,i+1); - if(data[i] != ID[i]) + data[i] = Read_Register(0xd3, i + 1); + if (data[i] != ID[i]) { - ToF=0; + ToF = 0; } } - if(!ToF) /* data!=ID */ + if (!ToF) /* data!=ID */ { Serial.print("Read TFT ID failed, ID should be 0x09341, but read ID = 0x"); - for(i=0;i<3;i++) + for (i = 0; i < 3; i++) { - Serial.print(data[i],HEX); + Serial.print(data[i], HEX); } Serial.println(); } return ToF; } -void TFT::setCol(INT16U StartCol,INT16U EndCol) +void TFT::setCol(INT16U StartCol, INT16U EndCol) { sendCMD(0x2A); /* Column Command address */ sendData(StartCol); sendData(EndCol); } -void TFT::setPage(INT16U StartPage,INT16U EndPage) +void TFT::setPage(INT16U StartPage, INT16U EndPage) { sendCMD(0x2B); /* Column Command address */ sendData(StartPage); @@ -189,39 +189,39 @@ void TFT::setPage(INT16U StartPage,INT16U EndPage) void TFT::fillScreen(INT16U XL, INT16U XR, INT16U YU, INT16U YD, INT16U color) { - unsigned long XY=0; - unsigned long i=0; + unsigned long XY = 0; + unsigned long i = 0; - if(XL > XR) + if (XL > XR) { - XL = XL^XR; - XR = XL^XR; - XL = XL^XR; + XL = XL ^ XR; + XR = XL ^ XR; + XL = XL ^ XR; } - if(YU > YD) + if (YU > YD) { - YU = YU^YD; - YD = YU^YD; - YU = YU^YD; + YU = YU ^ YD; + YD = YU ^ YD; + YU = YU ^ YD; } XL = constrain((int)XL, (int)MIN_X, (int)MAX_X); XR = constrain((int)XR, (int)MIN_X, (int)MAX_X); YU = constrain((int)YU, (int)MIN_Y, (int)MAX_Y); YD = constrain((int)YD, (int)MIN_Y, (int)MAX_Y); - XY = (XR-XL+1); - XY = XY*(YD-YU+1); + XY = (XR - XL + 1); + XY = XY * (YD - YU + 1); - Tft.setCol(XL,XR); + Tft.setCol(XL, XR); Tft.setPage(YU, YD); - Tft.sendCMD(0x2c); - + Tft.sendCMD(0x2c); + TFT_DC_HIGH; TFT_CS_LOW; - INT8U Hcolor = color>>8; - INT8U Lcolor = color&0xff; - for(i=0; i < XY; i++) + INT8U Hcolor = color >> 8; + INT8U Lcolor = color & 0xff; + for (i = 0; i < XY; i++) { SPI.transfer(Hcolor); SPI.transfer(Lcolor); @@ -238,7 +238,7 @@ void TFT::fillScreen(void) TFT_DC_HIGH; TFT_CS_LOW; - for(INT16U i=0; i<38400; i++) + for (INT16U i = 0; i < 38400; i++) { SPI.transfer(0); SPI.transfer(0); @@ -256,7 +256,7 @@ void TFT::setXY(INT16U poX, INT16U poY) sendCMD(0x2c); } -void TFT::setPixel(INT16U poX, INT16U poY,INT16U color) +void TFT::setPixel(INT16U poX, INT16U poY, INT16U color) { sendCMD(0x2A); /* Column Command address */ @@ -266,45 +266,46 @@ void TFT::setPixel(INT16U poX, INT16U poY,INT16U color) sendCMD(0x2B); /* Column Command address */ sendData(poY); sendData(poY); - + sendCMD(0x2c); sendData(color); } -void TFT::drawChar( INT8U ascii, INT16U poX, INT16U poY,INT16U size, INT16U fgcolor) +void TFT::drawChar(INT8U ascii, INT16U poX, INT16U poY, INT16U size, INT16U fgcolor) { - if((ascii>=32)&&(ascii<=127)) + if ((ascii >= 32) && (ascii <= 127)) { ; } else { - ascii = '?'-32; + ascii = '?' - 32; } - for (int i =0; i>f)&0x01) + if ((temp >> f) & 0x01) { - fillRectangle(poX+i*size, poY+f*size, size, size, fgcolor); + fillRectangle(poX + i * size, poY + f * size, size, size, fgcolor); } } } } -void TFT::drawString(const char *string,INT16U poX, INT16U poY, INT16U size,INT16U fgcolor) +void TFT::drawString(const char *string, INT16U poX, INT16U poY, INT16U size, INT16U fgcolor) { - while(*string) + while (*string) { drawChar(*string, poX, poY, size, fgcolor); string++; - if(poX < MAX_X) + if (poX < MAX_X) { - poX += FONT_SPACE*size; /* Move cursor right */ + poX += FONT_SPACE * size; /* Move cursor right */ } } } @@ -312,102 +313,131 @@ void TFT::drawString(const char *string,INT16U poX, INT16U poY, INT16U size,INT1 //fillRectangle(poX+i*size, poY+f*size, size, size, fgcolor); void TFT::fillRectangle(INT16U poX, INT16U poY, INT16U length, INT16U width, INT16U color) { - fillScreen(poX, poX+length, poY, poY+width, color); + fillScreen(poX, poX + length, poY, poY + width, color); } -void TFT::drawHorizontalLine( INT16U poX, INT16U poY, -INT16U length,INT16U color) +void TFT::drawHorizontalLine(INT16U poX, INT16U poY, + INT16U length, INT16U color) { - setCol(poX,poX + length); - setPage(poY,poY); + setCol(poX, poX + length); + setPage(poY, poY); sendCMD(0x2c); - for(INT16U i=0; i= dy) { /* e_xy+e_x > 0 */ - if (x0 == x1) break; + { + /* loop */ + setPixel(x0, y0, color); + e2 = 2 * err; + if (e2 >= dy) /* e_xy+e_x > 0 */ + { + if (x0 == x1) + { + break; + } err += dy; x0 += sx; } - if (e2 <= dx) { /* e_xy+e_y < 0 */ - if (y0 == y1) break; + if (e2 <= dx) /* e_xy+e_y < 0 */ + { + if (y0 == y1) + { + break; + } err += dx; y0 += sy; } } } -void TFT::drawVerticalLine( INT16U poX, INT16U poY, INT16U length,INT16U color) +void TFT::drawVerticalLine(INT16U poX, INT16U poY, INT16U length, INT16U color) { - setCol(poX,poX); - setPage(poY,poY+length); + setCol(poX, poX); + setPage(poY, poY + length); sendCMD(0x2c); - for(INT16U i=0; i x) + { + err += ++x * 2 + 1; } - if (e2 > x) err += ++x*2+1; } while (x <= 0); } -void TFT::fillCircle(int poX, int poY, int r,INT16U color) +void TFT::fillCircle(int poX, int poY, int r, INT16U color) { - int x = -r, y = 0, err = 2-2*r, e2; - do { + int x = -r, y = 0, err = 2 - 2 * r, e2; + do + { - drawVerticalLine(poX-x, poY-y, 2*y, color); - drawVerticalLine(poX+x, poY-y, 2*y, color); + drawVerticalLine(poX - x, poY - y, 2 * y, color); + drawVerticalLine(poX + x, poY - y, 2 * y, color); e2 = err; - if (e2 <= y) { - err += ++y*2+1; - if (-x == y && e2 <= x) e2 = 0; + if (e2 <= y) + { + err += ++y * 2 + 1; + if (-x == y && e2 <= x) + { + e2 = 0; + } + } + if (e2 > x) + { + err += ++x * 2 + 1; } - if (e2 > x) err += ++x*2+1; } while (x <= 0); } -void TFT::drawTraingle( int poX1, int poY1, int poX2, int poY2, int poX3, int poY3, INT16U color) +void TFT::drawTraingle(int poX1, int poY1, int poX2, int poY2, int poX3, int poY3, INT16U color) { - drawLine(poX1, poY1, poX2, poY2,color); - drawLine(poX1, poY1, poX3, poY3,color); - drawLine(poX2, poY2, poX3, poY3,color); + drawLine(poX1, poY1, poX2, poY2, color); + drawLine(poX1, poY1, poX3, poY3, color); + drawLine(poX2, poY2, poX3, poY3, color); } -INT8U TFT::drawNumber(long long_num,INT16U poX, INT16U poY,INT16U size,INT16U fgcolor) +INT8U TFT::drawNumber(long long_num, INT16U poX, INT16U poY, INT16U size, INT16U fgcolor) { INT8U char_buffer[10] = ""; INT8U i = 0; @@ -415,22 +445,22 @@ INT8U TFT::drawNumber(long long_num,INT16U poX, INT16U poY,INT16U size,INT16U fg if (long_num < 0) { - f=1; - drawChar('-',poX, poY, size, fgcolor); + f = 1; + drawChar('-', poX, poY, size, fgcolor); long_num = -long_num; - if(poX < MAX_X) + if (poX < MAX_X) { - poX += FONT_SPACE*size; /* Move cursor right */ + poX += FONT_SPACE * size; /* Move cursor right */ } } else if (long_num == 0) { - f=1; - drawChar('0',poX, poY, size, fgcolor); + f = 1; + drawChar('0', poX, poY, size, fgcolor); return f; - if(poX < MAX_X) + if (poX < MAX_X) { - poX += FONT_SPACE*size; /* Move cursor right */ + poX += FONT_SPACE * size; /* Move cursor right */ } } @@ -441,125 +471,125 @@ INT8U TFT::drawNumber(long long_num,INT16U poX, INT16U poY,INT16U size,INT16U fg long_num /= 10; } - f = f+i; - for(; i > 0; i--) + f = f + i; + for (; i > 0; i--) { - drawChar('0'+ char_buffer[i - 1],poX, poY, size, fgcolor); - if(poX < MAX_X) + drawChar('0' + char_buffer[i - 1], poX, poY, size, fgcolor); + if (poX < MAX_X) { - poX+=FONT_SPACE*size; /* Move cursor right */ + poX += FONT_SPACE * size; /* Move cursor right */ } } return f; } -INT8U TFT::drawFloat(float floatNumber,INT8U decimal,INT16U poX, INT16U poY,INT16U size,INT16U fgcolor) +INT8U TFT::drawFloat(float floatNumber, INT8U decimal, INT16U poX, INT16U poY, INT16U size, INT16U fgcolor) { - INT16U temp=0; - float decy=0.0; + INT16U temp = 0; + float decy = 0.0; float rounding = 0.5; - INT8U f=0; - if(floatNumber<0.0) + INT8U f = 0; + if (floatNumber < 0.0) { - drawChar('-',poX, poY, size, fgcolor); + drawChar('-', poX, poY, size, fgcolor); floatNumber = -floatNumber; - if(poX < MAX_X) + if (poX < MAX_X) { - poX+=FONT_SPACE*size; /* Move cursor right */ + poX += FONT_SPACE * size; /* Move cursor right */ } - f =1; + f = 1; } - for (INT8U i=0; i0) + if (decimal > 0) { - drawChar('.',poX, poY, size, fgcolor); - if(poX < MAX_X) + drawChar('.', poX, poY, size, fgcolor); + if (poX < MAX_X) { - poX+=FONT_SPACE*size; /* Move cursor right */ + poX += FONT_SPACE * size; /* Move cursor right */ } - f +=1; + f += 1; } - decy = floatNumber-temp; /* decimal part, 4 */ - for(INT8U i=0;i0) + if (decimal > 0) { - drawChar('.',poX, poY, size, fgcolor); - if(poX < MAX_X) + drawChar('.', poX, poY, size, fgcolor); + if (poX < MAX_X) { - poX += FONT_SPACE*size; /* Move cursor right */ + poX += FONT_SPACE * size; /* Move cursor right */ } - f +=1; + f += 1; } - decy = floatNumber-temp; /* decimal part, */ - for(INT8U i=0;i>8; - INT8U data2 = data&0xff; + INT8U data1 = data >> 8; + INT8U data2 = data & 0xff; TFT_DC_HIGH; TFT_CS_LOW; SPI.transfer(data1); @@ -157,11 +157,11 @@ class TFT TFT_DC_HIGH; TFT_CS_LOW; - INT8U count=0; - for(count=0;count>8; - data2 = data[count]&0xff; + data1 = data[count] >> 8; + data2 = data[count] & 0xff; SPI.transfer(data1); SPI.transfer(data2); } @@ -170,9 +170,9 @@ class TFT INT8U Read_Register(INT8U Addr, INT8U xParameter) { - INT8U data=0; + INT8U data = 0; sendCMD(0xd9); /* ext command */ - WRITE_DATA(0x10+xParameter); /* 0x11 is the first Parameter */ + WRITE_DATA(0x10 + xParameter); /* 0x11 is the first Parameter */ TFT_DC_LOW; TFT_CS_LOW; SPI.transfer(Addr); @@ -182,34 +182,34 @@ class TFT return data; } - - void TFTinit (void); - void setCol(INT16U StartCol,INT16U EndCol); - void setPage(INT16U StartPage,INT16U EndPage); - void setXY(INT16U poX, INT16U poY); - void setPixel(INT16U poX, INT16U poY,INT16U color); - - void fillScreen(INT16U XL,INT16U XR,INT16U YU,INT16U YD,INT16U color); - void fillScreen(void); - INT8U readID(void); - - void drawChar(INT8U ascii,INT16U poX, INT16U poY,INT16U size, INT16U fgcolor); - void drawString(const char *string,INT16U poX, INT16U poY,INT16U size,INT16U fgcolor); - void fillRectangle(INT16U poX, INT16U poY, INT16U length, INT16U width, INT16U color); - - void drawLine(INT16U x0,INT16U y0,INT16U x1,INT16U y1,INT16U color); - void drawVerticalLine(INT16U poX, INT16U poY,INT16U length,INT16U color); - void drawHorizontalLine(INT16U poX, INT16U poY,INT16U length,INT16U color); - void drawRectangle(INT16U poX, INT16U poY, INT16U length,INT16U width,INT16U color); - - void drawCircle(int poX, int poY, int r,INT16U color); - void fillCircle(int poX, int poY, int r,INT16U color); - - void drawTraingle(int poX1, int poY1, int poX2, int poY2, int poX3, int poY3, INT16U color); - - INT8U drawNumber(long long_num,INT16U poX, INT16U poY,INT16U size,INT16U fgcolor); - INT8U drawFloat(float floatNumber,INT8U decimal,INT16U poX, INT16U poY,INT16U size,INT16U fgcolor); - INT8U drawFloat(float floatNumber,INT16U poX, INT16U poY,INT16U size,INT16U fgcolor); + + void TFTinit(void); + void setCol(INT16U StartCol, INT16U EndCol); + void setPage(INT16U StartPage, INT16U EndPage); + void setXY(INT16U poX, INT16U poY); + void setPixel(INT16U poX, INT16U poY, INT16U color); + + void fillScreen(INT16U XL, INT16U XR, INT16U YU, INT16U YD, INT16U color); + void fillScreen(void); + INT8U readID(void); + + void drawChar(INT8U ascii, INT16U poX, INT16U poY, INT16U size, INT16U fgcolor); + void drawString(const char *string, INT16U poX, INT16U poY, INT16U size, INT16U fgcolor); + void fillRectangle(INT16U poX, INT16U poY, INT16U length, INT16U width, INT16U color); + + void drawLine(INT16U x0, INT16U y0, INT16U x1, INT16U y1, INT16U color); + void drawVerticalLine(INT16U poX, INT16U poY, INT16U length, INT16U color); + void drawHorizontalLine(INT16U poX, INT16U poY, INT16U length, INT16U color); + void drawRectangle(INT16U poX, INT16U poY, INT16U length, INT16U width, INT16U color); + + void drawCircle(int poX, int poY, int r, INT16U color); + void fillCircle(int poX, int poY, int r, INT16U color); + + void drawTraingle(int poX1, int poY1, int poX2, int poY2, int poX3, int poY3, INT16U color); + + INT8U drawNumber(long long_num, INT16U poX, INT16U poY, INT16U size, INT16U fgcolor); + INT8U drawFloat(float floatNumber, INT8U decimal, INT16U poX, INT16U poY, INT16U size, INT16U fgcolor); + INT8U drawFloat(float floatNumber, INT16U poX, INT16U poY, INT16U size, INT16U fgcolor); }; @@ -220,5 +220,5 @@ extern TFT Tft; #endif /********************************************************************************************************* - END FILE + END FILE *********************************************************************************************************/ diff --git a/libraries/TFT_Touch_Shield_V2/font.c b/libraries/TFT_Touch_Shield_V2/font.c index 3a67494a22..f58558b0c3 100644 --- a/libraries/TFT_Touch_Shield_V2/font.c +++ b/libraries/TFT_Touch_Shield_V2/font.c @@ -1,107 +1,107 @@ /* - 2012 Copyright (c) Seeed Technology Inc. + 2012 Copyright (c) Seeed Technology Inc. */ #include -const unsigned char simpleFont[][8] PROGMEM= +const unsigned char simpleFont[][8] PROGMEM = { - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x07,0x00,0x07,0x00,0x00,0x00}, - {0x00,0x14,0x7F,0x14,0x7F,0x14,0x00,0x00}, - {0x00,0x24,0x2A,0x7F,0x2A,0x12,0x00,0x00}, - {0x00,0x23,0x13,0x08,0x64,0x62,0x00,0x00}, - {0x00,0x36,0x49,0x55,0x22,0x50,0x00,0x00}, - {0x00,0x00,0x05,0x03,0x00,0x00,0x00,0x00}, - {0x00,0x1C,0x22,0x41,0x00,0x00,0x00,0x00}, - {0x00,0x41,0x22,0x1C,0x00,0x00,0x00,0x00}, - {0x00,0x08,0x2A,0x1C,0x2A,0x08,0x00,0x00}, - {0x00,0x08,0x08,0x3E,0x08,0x08,0x00,0x00}, - {0x00,0xA0,0x60,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00}, - {0x00,0x60,0x60,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x20,0x10,0x08,0x04,0x02,0x00,0x00}, - {0x00,0x3E,0x51,0x49,0x45,0x3E,0x00,0x00}, - {0x00,0x00,0x42,0x7F,0x40,0x00,0x00,0x00}, - {0x00,0x62,0x51,0x49,0x49,0x46,0x00,0x00}, - {0x00,0x22,0x41,0x49,0x49,0x36,0x00,0x00}, - {0x00,0x18,0x14,0x12,0x7F,0x10,0x00,0x00}, - {0x00,0x27,0x45,0x45,0x45,0x39,0x00,0x00}, - {0x00,0x3C,0x4A,0x49,0x49,0x30,0x00,0x00}, - {0x00,0x01,0x71,0x09,0x05,0x03,0x00,0x00}, - {0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x00}, - {0x00,0x06,0x49,0x49,0x29,0x1E,0x00,0x00}, - {0x00,0x00,0x36,0x36,0x00,0x00,0x00,0x00}, - {0x00,0x00,0xAC,0x6C,0x00,0x00,0x00,0x00}, - {0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00}, - {0x00,0x14,0x14,0x14,0x14,0x14,0x00,0x00}, - {0x00,0x41,0x22,0x14,0x08,0x00,0x00,0x00}, - {0x00,0x02,0x01,0x51,0x09,0x06,0x00,0x00}, - {0x00,0x32,0x49,0x79,0x41,0x3E,0x00,0x00}, - {0x00,0x7E,0x09,0x09,0x09,0x7E,0x00,0x00}, - {0x00,0x7F,0x49,0x49,0x49,0x36,0x00,0x00}, - {0x00,0x3E,0x41,0x41,0x41,0x22,0x00,0x00}, - {0x00,0x7F,0x41,0x41,0x22,0x1C,0x00,0x00}, - {0x00,0x7F,0x49,0x49,0x49,0x41,0x00,0x00}, - {0x00,0x7F,0x09,0x09,0x09,0x01,0x00,0x00}, - {0x00,0x3E,0x41,0x41,0x51,0x72,0x00,0x00}, - {0x00,0x7F,0x08,0x08,0x08,0x7F,0x00,0x00}, - {0x00,0x41,0x7F,0x41,0x00,0x00,0x00,0x00}, - {0x00,0x20,0x40,0x41,0x3F,0x01,0x00,0x00}, - {0x00,0x7F,0x08,0x14,0x22,0x41,0x00,0x00}, - {0x00,0x7F,0x40,0x40,0x40,0x40,0x00,0x00}, - {0x00,0x7F,0x02,0x0C,0x02,0x7F,0x00,0x00}, - {0x00,0x7F,0x04,0x08,0x10,0x7F,0x00,0x00}, - {0x00,0x3E,0x41,0x41,0x41,0x3E,0x00,0x00}, - {0x00,0x7F,0x09,0x09,0x09,0x06,0x00,0x00}, - {0x00,0x3E,0x41,0x51,0x21,0x5E,0x00,0x00}, - {0x00,0x7F,0x09,0x19,0x29,0x46,0x00,0x00}, - {0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x00}, - {0x00,0x01,0x01,0x7F,0x01,0x01,0x00,0x00}, - {0x00,0x3F,0x40,0x40,0x40,0x3F,0x00,0x00}, - {0x00,0x1F,0x20,0x40,0x20,0x1F,0x00,0x00}, - {0x00,0x3F,0x40,0x38,0x40,0x3F,0x00,0x00}, - {0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x00}, - {0x00,0x03,0x04,0x78,0x04,0x03,0x00,0x00}, - {0x00,0x61,0x51,0x49,0x45,0x43,0x00,0x00}, - {0x00,0x7F,0x41,0x41,0x00,0x00,0x00,0x00}, - {0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00}, - {0x00,0x41,0x41,0x7F,0x00,0x00,0x00,0x00}, - {0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x00}, - {0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00}, - {0x00,0x01,0x02,0x04,0x00,0x00,0x00,0x00}, - {0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x00}, - {0x00,0x7F,0x48,0x44,0x44,0x38,0x00,0x00}, - {0x00,0x38,0x44,0x44,0x28,0x00,0x00,0x00}, - {0x00,0x38,0x44,0x44,0x48,0x7F,0x00,0x00}, - {0x00,0x38,0x54,0x54,0x54,0x18,0x00,0x00}, - {0x00,0x08,0x7E,0x09,0x02,0x00,0x00,0x00}, - {0x00,0x18,0xA4,0xA4,0xA4,0x7C,0x00,0x00}, - {0x00,0x7F,0x08,0x04,0x04,0x78,0x00,0x00}, - {0x00,0x00,0x7D,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x80,0x84,0x7D,0x00,0x00,0x00,0x00}, - {0x00,0x7F,0x10,0x28,0x44,0x00,0x00,0x00}, - {0x00,0x41,0x7F,0x40,0x00,0x00,0x00,0x00}, - {0x00,0x7C,0x04,0x18,0x04,0x78,0x00,0x00}, - {0x00,0x7C,0x08,0x04,0x7C,0x00,0x00,0x00}, - {0x00,0x38,0x44,0x44,0x38,0x00,0x00,0x00}, - {0x00,0xFC,0x24,0x24,0x18,0x00,0x00,0x00}, - {0x00,0x18,0x24,0x24,0xFC,0x00,0x00,0x00}, - {0x00,0x00,0x7C,0x08,0x04,0x00,0x00,0x00}, - {0x00,0x48,0x54,0x54,0x24,0x00,0x00,0x00}, - {0x00,0x04,0x7F,0x44,0x00,0x00,0x00,0x00}, - {0x00,0x3C,0x40,0x40,0x7C,0x00,0x00,0x00}, - {0x00,0x1C,0x20,0x40,0x20,0x1C,0x00,0x00}, - {0x00,0x3C,0x40,0x30,0x40,0x3C,0x00,0x00}, - {0x00,0x44,0x28,0x10,0x28,0x44,0x00,0x00}, - {0x00,0x1C,0xA0,0xA0,0x7C,0x00,0x00,0x00}, - {0x00,0x44,0x64,0x54,0x4C,0x44,0x00,0x00}, - {0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x41,0x36,0x08,0x00,0x00,0x00,0x00}, - {0x00,0x02,0x01,0x01,0x02,0x01,0x00,0x00}, - {0x00,0x02,0x05,0x05,0x02,0x00,0x00,0x00} + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00}, + {0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, 0x00}, + {0x00, 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00, 0x00}, + {0x00, 0x23, 0x13, 0x08, 0x64, 0x62, 0x00, 0x00}, + {0x00, 0x36, 0x49, 0x55, 0x22, 0x50, 0x00, 0x00}, + {0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x41, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x08, 0x2A, 0x1C, 0x2A, 0x08, 0x00, 0x00}, + {0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00}, + {0x00, 0xA0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00}, + {0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00}, + {0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x00}, + {0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x00, 0x00}, + {0x00, 0x62, 0x51, 0x49, 0x49, 0x46, 0x00, 0x00}, + {0x00, 0x22, 0x41, 0x49, 0x49, 0x36, 0x00, 0x00}, + {0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, 0x00, 0x00}, + {0x00, 0x27, 0x45, 0x45, 0x45, 0x39, 0x00, 0x00}, + {0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, 0x00, 0x00}, + {0x00, 0x01, 0x71, 0x09, 0x05, 0x03, 0x00, 0x00}, + {0x00, 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00}, + {0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, 0x00, 0x00}, + {0x00, 0x00, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xAC, 0x6C, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00, 0x00}, + {0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00}, + {0x00, 0x41, 0x22, 0x14, 0x08, 0x00, 0x00, 0x00}, + {0x00, 0x02, 0x01, 0x51, 0x09, 0x06, 0x00, 0x00}, + {0x00, 0x32, 0x49, 0x79, 0x41, 0x3E, 0x00, 0x00}, + {0x00, 0x7E, 0x09, 0x09, 0x09, 0x7E, 0x00, 0x00}, + {0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00}, + {0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00, 0x00}, + {0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, 0x00, 0x00}, + {0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00, 0x00}, + {0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x00, 0x00}, + {0x00, 0x3E, 0x41, 0x41, 0x51, 0x72, 0x00, 0x00}, + {0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x00}, + {0x00, 0x41, 0x7F, 0x41, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00, 0x00}, + {0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00}, + {0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00}, + {0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, 0x00, 0x00}, + {0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00, 0x00}, + {0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x00}, + {0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00, 0x00}, + {0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00, 0x00}, + {0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00, 0x00}, + {0x00, 0x26, 0x49, 0x49, 0x49, 0x32, 0x00, 0x00}, + {0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, 0x00, 0x00}, + {0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00, 0x00}, + {0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00, 0x00}, + {0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00, 0x00}, + {0x00, 0x63, 0x14, 0x08, 0x14, 0x63, 0x00, 0x00}, + {0x00, 0x03, 0x04, 0x78, 0x04, 0x03, 0x00, 0x00}, + {0x00, 0x61, 0x51, 0x49, 0x45, 0x43, 0x00, 0x00}, + {0x00, 0x7F, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00}, + {0x00, 0x41, 0x41, 0x7F, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x04, 0x02, 0x01, 0x02, 0x04, 0x00, 0x00}, + {0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00}, + {0x00, 0x01, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x20, 0x54, 0x54, 0x54, 0x78, 0x00, 0x00}, + {0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, 0x00, 0x00}, + {0x00, 0x38, 0x44, 0x44, 0x28, 0x00, 0x00, 0x00}, + {0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, 0x00, 0x00}, + {0x00, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x00}, + {0x00, 0x08, 0x7E, 0x09, 0x02, 0x00, 0x00, 0x00}, + {0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, 0x00, 0x00}, + {0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x00}, + {0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x80, 0x84, 0x7D, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00}, + {0x00, 0x41, 0x7F, 0x40, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, 0x00, 0x00}, + {0x00, 0x7C, 0x08, 0x04, 0x7C, 0x00, 0x00, 0x00}, + {0x00, 0x38, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00}, + {0x00, 0xFC, 0x24, 0x24, 0x18, 0x00, 0x00, 0x00}, + {0x00, 0x18, 0x24, 0x24, 0xFC, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x7C, 0x08, 0x04, 0x00, 0x00, 0x00}, + {0x00, 0x48, 0x54, 0x54, 0x24, 0x00, 0x00, 0x00}, + {0x00, 0x04, 0x7F, 0x44, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x3C, 0x40, 0x40, 0x7C, 0x00, 0x00, 0x00}, + {0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00, 0x00}, + {0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00, 0x00}, + {0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00}, + {0x00, 0x1C, 0xA0, 0xA0, 0x7C, 0x00, 0x00, 0x00}, + {0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, 0x00}, + {0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x41, 0x36, 0x08, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x00}, + {0x00, 0x02, 0x05, 0x05, 0x02, 0x00, 0x00, 0x00} }; diff --git a/libraries/Ticker/Ticker.cpp b/libraries/Ticker/Ticker.cpp index b53b429dab..8ea5df352e 100644 --- a/libraries/Ticker/Ticker.cpp +++ b/libraries/Ticker/Ticker.cpp @@ -1,22 +1,22 @@ -/* - Ticker.cpp - esp8266 library that calls functions periodically - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +/* + Ticker.cpp - esp8266 library that calls functions periodically + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include @@ -33,55 +33,57 @@ static const int REPEAT = 1; #include "Ticker.h" Ticker::Ticker() -: _timer(nullptr) + : _timer(nullptr) { } Ticker::~Ticker() { - detach(); + detach(); } void Ticker::_attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, uint32_t arg) { - if (_timer) - { - os_timer_disarm(_timer); - } - else - { - _timer = new ETSTimer; - } - - os_timer_setfn(_timer, reinterpret_cast(callback), reinterpret_cast(arg)); - os_timer_arm(_timer, milliseconds, (repeat)?REPEAT:ONCE); + if (_timer) + { + os_timer_disarm(_timer); + } + else + { + _timer = new ETSTimer; + } + + os_timer_setfn(_timer, reinterpret_cast(callback), reinterpret_cast(arg)); + os_timer_arm(_timer, milliseconds, (repeat) ? REPEAT : ONCE); } void Ticker::detach() { - if (!_timer) - return; + if (!_timer) + { + return; + } - os_timer_disarm(_timer); - delete _timer; - _timer = nullptr; - _callback_function = nullptr; + os_timer_disarm(_timer); + delete _timer; + _timer = nullptr; + _callback_function = nullptr; } bool Ticker::active() const { - return (bool)_timer; + return (bool)_timer; } void Ticker::_static_callback(void* arg) { - Ticker* _this = (Ticker*)arg; - if (_this == nullptr) - { - return; - } - if (_this->_callback_function) - { - _this->_callback_function(); - } + Ticker* _this = (Ticker*)arg; + if (_this == nullptr) + { + return; + } + if (_this->_callback_function) + { + _this->_callback_function(); + } } diff --git a/libraries/Ticker/Ticker.h b/libraries/Ticker/Ticker.h index 15474594f0..5a4b1d4c01 100644 --- a/libraries/Ticker/Ticker.h +++ b/libraries/Ticker/Ticker.h @@ -1,22 +1,22 @@ -/* - Ticker.h - esp8266 library that calls functions periodically - - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +/* + Ticker.h - esp8266 library that calls functions periodically + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TICKER_H @@ -29,107 +29,107 @@ #include extern "C" { - typedef struct _ETSTIMER_ ETSTimer; + typedef struct _ETSTIMER_ ETSTimer; } class Ticker { public: - Ticker(); - ~Ticker(); - typedef void (*callback_t)(void); - typedef void (*callback_with_arg_t)(void*); - typedef std::function callback_function_t; - - void attach_scheduled(float seconds, callback_function_t callback) - { - attach(seconds,std::bind(schedule_function, callback)); - } - - void attach(float seconds, callback_function_t callback) - { - _callback_function = callback; - attach(seconds, _static_callback, (void*)this); - } - - void attach_ms_scheduled(uint32_t milliseconds, callback_function_t callback) - { - attach_ms(milliseconds, std::bind(schedule_function, callback)); - } - - void attach_ms(uint32_t milliseconds, callback_function_t callback) - { - _callback_function = callback; - attach_ms(milliseconds, _static_callback, (void*)this); - } - - template - void attach(float seconds, void (*callback)(TArg), TArg arg) - { - static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach() callback argument size must be <= 4 bytes"); - // C-cast serves two purposes: - // static_cast for smaller integer types, - // reinterpret_cast + const_cast for pointer types - uint32_t arg32 = (uint32_t)arg; - _attach_ms(seconds * 1000, true, reinterpret_cast(callback), arg32); - } - - template - void attach_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg) - { - static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach_ms() callback argument size must be <= 4 bytes"); - uint32_t arg32 = (uint32_t)arg; - _attach_ms(milliseconds, true, reinterpret_cast(callback), arg32); - } - - void once_scheduled(float seconds, callback_function_t callback) - { - once(seconds, std::bind(schedule_function, callback)); - } - - void once(float seconds, callback_function_t callback) - { - _callback_function = callback; - once(seconds, _static_callback, (void*)this); - } - - void once_ms_scheduled(uint32_t milliseconds, callback_function_t callback) - { - once_ms(milliseconds, std::bind(schedule_function, callback)); - } - - void once_ms(uint32_t milliseconds, callback_function_t callback) - { - _callback_function = callback; - once_ms(milliseconds, _static_callback, (void*)this); - } - - template - void once(float seconds, void (*callback)(TArg), TArg arg) - { - static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach() callback argument size must be <= 4 bytes"); - uint32_t arg32 = (uint32_t)(arg); - _attach_ms(seconds * 1000, false, reinterpret_cast(callback), arg32); - } - - template - void once_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg) - { - static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach_ms() callback argument size must be <= 4 bytes"); - uint32_t arg32 = (uint32_t)(arg); - _attach_ms(milliseconds, false, reinterpret_cast(callback), arg32); - } - - void detach(); - bool active() const; - -protected: - void _attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, uint32_t arg); - static void _static_callback (void* arg); + Ticker(); + ~Ticker(); + typedef void (*callback_t)(void); + typedef void (*callback_with_arg_t)(void*); + typedef std::function callback_function_t; + + void attach_scheduled(float seconds, callback_function_t callback) + { + attach(seconds, std::bind(schedule_function, callback)); + } + + void attach(float seconds, callback_function_t callback) + { + _callback_function = callback; + attach(seconds, _static_callback, (void*)this); + } + + void attach_ms_scheduled(uint32_t milliseconds, callback_function_t callback) + { + attach_ms(milliseconds, std::bind(schedule_function, callback)); + } + + void attach_ms(uint32_t milliseconds, callback_function_t callback) + { + _callback_function = callback; + attach_ms(milliseconds, _static_callback, (void*)this); + } + + template + void attach(float seconds, void (*callback)(TArg), TArg arg) + { + static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach() callback argument size must be <= 4 bytes"); + // C-cast serves two purposes: + // static_cast for smaller integer types, + // reinterpret_cast + const_cast for pointer types + uint32_t arg32 = (uint32_t)arg; + _attach_ms(seconds * 1000, true, reinterpret_cast(callback), arg32); + } + + template + void attach_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg) + { + static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach_ms() callback argument size must be <= 4 bytes"); + uint32_t arg32 = (uint32_t)arg; + _attach_ms(milliseconds, true, reinterpret_cast(callback), arg32); + } + + void once_scheduled(float seconds, callback_function_t callback) + { + once(seconds, std::bind(schedule_function, callback)); + } + + void once(float seconds, callback_function_t callback) + { + _callback_function = callback; + once(seconds, _static_callback, (void*)this); + } + + void once_ms_scheduled(uint32_t milliseconds, callback_function_t callback) + { + once_ms(milliseconds, std::bind(schedule_function, callback)); + } + + void once_ms(uint32_t milliseconds, callback_function_t callback) + { + _callback_function = callback; + once_ms(milliseconds, _static_callback, (void*)this); + } + + template + void once(float seconds, void (*callback)(TArg), TArg arg) + { + static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach() callback argument size must be <= 4 bytes"); + uint32_t arg32 = (uint32_t)(arg); + _attach_ms(seconds * 1000, false, reinterpret_cast(callback), arg32); + } + + template + void once_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg) + { + static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach_ms() callback argument size must be <= 4 bytes"); + uint32_t arg32 = (uint32_t)(arg); + _attach_ms(milliseconds, false, reinterpret_cast(callback), arg32); + } + + void detach(); + bool active() const; + +protected: + void _attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, uint32_t arg); + static void _static_callback(void* arg); protected: - ETSTimer* _timer; - callback_function_t _callback_function = nullptr; + ETSTimer* _timer; + callback_function_t _callback_function = nullptr; }; diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 88103650d1..d2b0861c2f 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -1,31 +1,31 @@ /* - TwoWire.cpp - TWI/I2C library for Arduino & Wiring - Copyright (c) 2006 Nicholas Zambetti. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts - Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support - Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support - Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support + TwoWire.cpp - TWI/I2C library for Arduino & Wiring + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts + Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support + Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support + Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support */ extern "C" { - #include - #include - #include +#include +#include +#include } #include "twi.h" @@ -58,226 +58,271 @@ static int default_scl_pin = SCL; // Constructors //////////////////////////////////////////////////////////////// -TwoWire::TwoWire(){} +TwoWire::TwoWire() {} // Public Methods ////////////////////////////////////////////////////////////// -void TwoWire::begin(int sda, int scl){ - default_sda_pin = sda; - default_scl_pin = scl; - twi_init(sda, scl); - flush(); +void TwoWire::begin(int sda, int scl) +{ + default_sda_pin = sda; + default_scl_pin = scl; + twi_init(sda, scl); + flush(); } -void TwoWire::begin(int sda, int scl, uint8_t address){ - default_sda_pin = sda; - default_scl_pin = scl; - twi_setAddress(address); - twi_init(sda, scl); - twi_attachSlaveTxEvent(onRequestService); - twi_attachSlaveRxEvent(onReceiveService); - flush(); +void TwoWire::begin(int sda, int scl, uint8_t address) +{ + default_sda_pin = sda; + default_scl_pin = scl; + twi_setAddress(address); + twi_init(sda, scl); + twi_attachSlaveTxEvent(onRequestService); + twi_attachSlaveRxEvent(onReceiveService); + flush(); } -void TwoWire::pins(int sda, int scl){ - default_sda_pin = sda; - default_scl_pin = scl; +void TwoWire::pins(int sda, int scl) +{ + default_sda_pin = sda; + default_scl_pin = scl; } -void TwoWire::begin(void){ - begin(default_sda_pin, default_scl_pin); +void TwoWire::begin(void) +{ + begin(default_sda_pin, default_scl_pin); } -void TwoWire::begin(uint8_t address){ - twi_setAddress(address); - twi_attachSlaveTxEvent(onRequestService); - twi_attachSlaveRxEvent(onReceiveService); - begin(); +void TwoWire::begin(uint8_t address) +{ + twi_setAddress(address); + twi_attachSlaveTxEvent(onRequestService); + twi_attachSlaveRxEvent(onReceiveService); + begin(); } -uint8_t TwoWire::status(){ - return twi_status(); +uint8_t TwoWire::status() +{ + return twi_status(); } -void TwoWire::begin(int address){ - begin((uint8_t)address); +void TwoWire::begin(int address) +{ + begin((uint8_t)address); } -void TwoWire::setClock(uint32_t frequency){ - twi_setClock(frequency); +void TwoWire::setClock(uint32_t frequency) +{ + twi_setClock(frequency); } -void TwoWire::setClockStretchLimit(uint32_t limit){ - twi_setClockStretchLimit(limit); +void TwoWire::setClockStretchLimit(uint32_t limit) +{ + twi_setClockStretchLimit(limit); } -size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop){ - if(size > BUFFER_LENGTH){ - size = BUFFER_LENGTH; - } - size_t read = (twi_readFrom(address, rxBuffer, size, sendStop) == 0)?size:0; - rxBufferIndex = 0; - rxBufferLength = read; - return read; +size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop) +{ + if (size > BUFFER_LENGTH) + { + size = BUFFER_LENGTH; + } + size_t read = (twi_readFrom(address, rxBuffer, size, sendStop) == 0) ? size : 0; + rxBufferIndex = 0; + rxBufferLength = read; + return read; } -uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop){ - return requestFrom(address, static_cast(quantity), static_cast(sendStop)); +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) +{ + return requestFrom(address, static_cast(quantity), static_cast(sendStop)); } -uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity){ - return requestFrom(address, static_cast(quantity), true); +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) +{ + return requestFrom(address, static_cast(quantity), true); } -uint8_t TwoWire::requestFrom(int address, int quantity){ - return requestFrom(static_cast(address), static_cast(quantity), true); +uint8_t TwoWire::requestFrom(int address, int quantity) +{ + return requestFrom(static_cast(address), static_cast(quantity), true); } -uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop){ - return requestFrom(static_cast(address), static_cast(quantity), static_cast(sendStop)); +uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) +{ + return requestFrom(static_cast(address), static_cast(quantity), static_cast(sendStop)); } -void TwoWire::beginTransmission(uint8_t address){ - transmitting = 1; - txAddress = address; - txBufferIndex = 0; - txBufferLength = 0; +void TwoWire::beginTransmission(uint8_t address) +{ + transmitting = 1; + txAddress = address; + txBufferIndex = 0; + txBufferLength = 0; } -void TwoWire::beginTransmission(int address){ - beginTransmission((uint8_t)address); +void TwoWire::beginTransmission(int address) +{ + beginTransmission((uint8_t)address); } -uint8_t TwoWire::endTransmission(uint8_t sendStop){ - int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, sendStop); - txBufferIndex = 0; - txBufferLength = 0; - transmitting = 0; - return ret; +uint8_t TwoWire::endTransmission(uint8_t sendStop) +{ + int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, sendStop); + txBufferIndex = 0; + txBufferLength = 0; + transmitting = 0; + return ret; } -uint8_t TwoWire::endTransmission(void){ - return endTransmission(true); +uint8_t TwoWire::endTransmission(void) +{ + return endTransmission(true); } -size_t TwoWire::write(uint8_t data){ - if(transmitting){ - if(txBufferLength >= BUFFER_LENGTH){ - setWriteError(); - return 0; +size_t TwoWire::write(uint8_t data) +{ + if (transmitting) + { + if (txBufferLength >= BUFFER_LENGTH) + { + setWriteError(); + return 0; + } + txBuffer[txBufferIndex] = data; + ++txBufferIndex; + txBufferLength = txBufferIndex; } - txBuffer[txBufferIndex] = data; - ++txBufferIndex; - txBufferLength = txBufferIndex; - } else { - twi_transmit(&data, 1); - } - return 1; + else + { + twi_transmit(&data, 1); + } + return 1; } -size_t TwoWire::write(const uint8_t *data, size_t quantity){ - if(transmitting){ - for(size_t i = 0; i < quantity; ++i){ - if(!write(data[i])) return i; +size_t TwoWire::write(const uint8_t *data, size_t quantity) +{ + if (transmitting) + { + for (size_t i = 0; i < quantity; ++i) + { + if (!write(data[i])) + { + return i; + } + } + } + else + { + twi_transmit(data, quantity); } - }else{ - twi_transmit(data, quantity); - } - return quantity; + return quantity; } -int TwoWire::available(void){ - int result = rxBufferLength - rxBufferIndex; +int TwoWire::available(void) +{ + int result = rxBufferLength - rxBufferIndex; - if (!result) { - // yielding here will not make more data "available", - // but it will prevent the system from going into WDT reset - optimistic_yield(1000); - } + if (!result) + { + // yielding here will not make more data "available", + // but it will prevent the system from going into WDT reset + optimistic_yield(1000); + } - return result; + return result; } -int TwoWire::read(void){ - int value = -1; - if(rxBufferIndex < rxBufferLength){ - value = rxBuffer[rxBufferIndex]; - ++rxBufferIndex; - } - return value; +int TwoWire::read(void) +{ + int value = -1; + if (rxBufferIndex < rxBufferLength) + { + value = rxBuffer[rxBufferIndex]; + ++rxBufferIndex; + } + return value; } -int TwoWire::peek(void){ - int value = -1; - if(rxBufferIndex < rxBufferLength){ - value = rxBuffer[rxBufferIndex]; - } - return value; +int TwoWire::peek(void) +{ + int value = -1; + if (rxBufferIndex < rxBufferLength) + { + value = rxBuffer[rxBufferIndex]; + } + return value; } -void TwoWire::flush(void){ - rxBufferIndex = 0; - rxBufferLength = 0; - txBufferIndex = 0; - txBufferLength = 0; +void TwoWire::flush(void) +{ + rxBufferIndex = 0; + rxBufferLength = 0; + txBufferIndex = 0; + txBufferLength = 0; } void TwoWire::onReceiveService(uint8_t* inBytes, size_t numBytes) { - // don't bother if user hasn't registered a callback - if (!user_onReceive) { - return; - } - // // don't bother if rx buffer is in use by a master requestFrom() op - // // i know this drops data, but it allows for slight stupidity - // // meaning, they may not have read all the master requestFrom() data yet - // if(rxBufferIndex < rxBufferLength){ - // return; - // } - - // copy twi rx buffer into local read buffer - // this enables new reads to happen in parallel - for (uint8_t i = 0; i < numBytes; ++i) { - rxBuffer[i] = inBytes[i]; - } - - // set rx iterator vars - rxBufferIndex = 0; - rxBufferLength = numBytes; - - // alert user program - user_onReceive(numBytes); + // don't bother if user hasn't registered a callback + if (!user_onReceive) + { + return; + } + // // don't bother if rx buffer is in use by a master requestFrom() op + // // i know this drops data, but it allows for slight stupidity + // // meaning, they may not have read all the master requestFrom() data yet + // if(rxBufferIndex < rxBufferLength){ + // return; + // } + + // copy twi rx buffer into local read buffer + // this enables new reads to happen in parallel + for (uint8_t i = 0; i < numBytes; ++i) + { + rxBuffer[i] = inBytes[i]; + } + + // set rx iterator vars + rxBufferIndex = 0; + rxBufferLength = numBytes; + + // alert user program + user_onReceive(numBytes); } void TwoWire::onRequestService(void) { - // don't bother if user hasn't registered a callback - if (!user_onRequest) { - return; - } - - // reset tx buffer iterator vars - // !!! this will kill any pending pre-master sendTo() activity - txBufferIndex = 0; - txBufferLength = 0; - - // alert user program - user_onRequest(); + // don't bother if user hasn't registered a callback + if (!user_onRequest) + { + return; + } + + // reset tx buffer iterator vars + // !!! this will kill any pending pre-master sendTo() activity + txBufferIndex = 0; + txBufferLength = 0; + + // alert user program + user_onRequest(); } -void TwoWire::onReceive( void (*function)(int) ) { - // arduino api compatibility fixer: - // really hope size parameter will not exceed 2^31 :) - static_assert(sizeof(int) == sizeof(size_t), "something is wrong in Arduino kingdom"); - user_onReceive = reinterpret_cast(function); +void TwoWire::onReceive(void (*function)(int)) +{ + // arduino api compatibility fixer: + // really hope size parameter will not exceed 2^31 :) + static_assert(sizeof(int) == sizeof(size_t), "something is wrong in Arduino kingdom"); + user_onReceive = reinterpret_cast(function); } -void TwoWire::onReceive( void (*function)(size_t) ) { - user_onReceive = function; +void TwoWire::onReceive(void (*function)(size_t)) +{ + user_onReceive = function; } -void TwoWire::onRequest( void (*function)(void) ){ - user_onRequest = function; +void TwoWire::onRequest(void (*function)(void)) +{ + user_onRequest = function; } // Preinstantiate Objects ////////////////////////////////////////////////////// diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 8f2a373943..4622d5248b 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -1,24 +1,24 @@ /* - TwoWire.h - TWI/I2C library for Arduino & Wiring - Copyright (c) 2006 Nicholas Zambetti. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts - Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support - Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support + TwoWire.h - TWI/I2C library for Arduino & Wiring + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts + Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support + Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support */ #ifndef TwoWire_h @@ -33,7 +33,7 @@ class TwoWire : public Stream { - private: +private: static uint8_t rxBuffer[]; static uint8_t rxBufferIndex; static uint8_t rxBufferLength; @@ -48,10 +48,10 @@ class TwoWire : public Stream static void (*user_onReceive)(size_t); static void onRequestService(void); static void onReceiveService(uint8_t*, size_t); - public: +public: TwoWire(); void begin(int sda, int scl); - void begin(int sda, int scl, uint8_t address); + void begin(int sda, int scl, uint8_t address); void pins(int sda, int scl) __attribute__((deprecated)); // use begin(sda, scl) in new code void begin(); void begin(uint8_t); @@ -63,27 +63,39 @@ class TwoWire : public Stream uint8_t endTransmission(void); uint8_t endTransmission(uint8_t); size_t requestFrom(uint8_t address, size_t size, bool sendStop); - uint8_t status(); + uint8_t status(); uint8_t requestFrom(uint8_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t, uint8_t); uint8_t requestFrom(int, int); uint8_t requestFrom(int, int, int); - + virtual size_t write(uint8_t); virtual size_t write(const uint8_t *, size_t); virtual int available(void); virtual int read(void); virtual int peek(void); virtual void flush(void); - void onReceive( void (*)(int) ); // arduino api - void onReceive( void (*)(size_t) ); // legacy esp8266 backward compatibility - void onRequest( void (*)(void) ); - - inline size_t write(unsigned long n) { return write((uint8_t)n); } - inline size_t write(long n) { return write((uint8_t)n); } - inline size_t write(unsigned int n) { return write((uint8_t)n); } - inline size_t write(int n) { return write((uint8_t)n); } + void onReceive(void (*)(int)); // arduino api + void onReceive(void (*)(size_t)); // legacy esp8266 backward compatibility + void onRequest(void (*)(void)); + + inline size_t write(unsigned long n) + { + return write((uint8_t)n); + } + inline size_t write(long n) + { + return write((uint8_t)n); + } + inline size_t write(unsigned int n) + { + return write((uint8_t)n); + } + inline size_t write(int n) + { + return write((uint8_t)n); + } using Print::write; }; diff --git a/tests/restyle-all.sh b/tests/restyle-all.sh deleted file mode 100755 index e751f476de..0000000000 --- a/tests/restyle-all.sh +++ /dev/null @@ -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 diff --git a/tests/restyle-old.sh b/tests/restyle-old.sh new file mode 100755 index 0000000000..513a891900 --- /dev/null +++ b/tests/restyle-old.sh @@ -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 diff --git a/tests/restyle.sh b/tests/restyle.sh index 513a891900..acca13d874 100755 --- a/tests/restyle.sh +++ b/tests/restyle.sh @@ -8,12 +8,18 @@ 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 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 {} \; + find $d -name "*.ino" -exec \ + astyle \ + --suffix=none \ + --options=${org}/astyle_examples.conf {} \; done