diff --git a/cores/esp8266/AddrList.h b/cores/esp8266/AddrList.h index fd1ff45730..40889500dd 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,24 +110,60 @@ 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 netmask () const { return _netif->netmask; } - IPAddress gw () const { return _netif->gw; } + 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; @@ -149,8 +185,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_ @@ -159,13 +195,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) { @@ -187,7 +239,9 @@ class AddressListIterator } if (!ip_addr_isany(netIf.ipFromNetifNum())) // found an initialized address + { break; + } } return *this; } @@ -199,15 +253,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 d7bb99f63d..d389330c4d 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 cbdd6ae0b0..fda3355b7f 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,26 +23,28 @@ #include "Stream.h" #include "IPAddress.h" -class Client: public Stream { +class Client: public Stream +{ - public: - virtual int connect(CONST 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 bool flush(unsigned int maxWaitMs = 0) = 0; - virtual bool stop(unsigned int maxWaitMs = 0) = 0; - virtual uint8_t connected() = 0; - virtual operator bool() = 0; - protected: - CONST uint8_t* rawIPAddress(CONST IPAddress& addr) { - return addr.raw_address(); - } - ; +public: + virtual int connect(CONST 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 bool flush(unsigned int maxWaitMs = 0) = 0; + virtual bool stop(unsigned int maxWaitMs = 0) = 0; + virtual uint8_t connected() = 0; + virtual operator bool() = 0; +protected: + 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..6feef9b8c9 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 21dff08523..bb99e4d01e 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,93 +124,95 @@ 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(); - inline uint32_t getCycleCount(); + inline uint32_t getCycleCount(); }; 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; } diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp index 90de4f3c4c..d73eebc331 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,48 +94,66 @@ 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; } -const char* File::name() const { +const char* File::name() const +{ if (!_p) + { return nullptr; + } return _p->name(); } @@ -125,25 +162,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(); } @@ -151,69 +191,87 @@ File Dir::openFile(const char* mode) { return File(_impl->openFile(om, am)); } -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::next() { - if (!_impl) { +bool Dir::next() +{ + if (!_impl) + { return false; } return _impl->next(); } -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::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(); } @@ -221,76 +279,91 @@ File FS::open(const char* path, const char* mode) { return File(_impl->open(path, om, am)); } -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(); } return Dir(_impl->openDir(path)); } -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::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; } @@ -299,7 +372,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); @@ -313,7 +386,8 @@ bool mount(FS& fs, const char* mountPoint); */ -struct MountEntry { +struct MountEntry +{ FSImplPtr fs; String path; MountEntry* next; @@ -322,9 +396,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; } @@ -343,31 +419,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 3cf34bb746..107b4dea47 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; @@ -39,7 +40,8 @@ typedef std::shared_ptr DirImplPtr; template bool mount(Tfs& fs, const char* mountPoint); -enum SeekMode { +enum SeekMode +{ SeekSet = 0, SeekCur = 1, SeekEnd = 2 @@ -59,12 +61,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; @@ -73,12 +77,13 @@ class File : public Stream operator bool() const; const char* name() const; String readString() override; - + protected: FileImplPtr _p; }; -class Dir { +class Dir +{ public: Dir(DirImplPtr impl = DirImplPtr()): _impl(impl) { } @@ -91,7 +96,8 @@ class Dir { DirImplPtr _impl; }; -struct FSInfo { +struct FSInfo +{ size_t totalBytes; size_t usedBytes; size_t blockSize; @@ -107,7 +113,7 @@ class FS bool begin(); void end(); - + bool format(); bool info(FSInfo& info); diff --git a/cores/esp8266/FSImpl.h b/cores/esp8266/FSImpl.h index cf84be6a6e..eeac304ec5 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; @@ -38,20 +40,23 @@ class FileImpl { virtual const char* name() 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; @@ -60,9 +65,10 @@ class DirImpl { virtual bool next() = 0; }; -class FSImpl { +class FSImpl +{ public: - virtual ~FSImpl () { } + virtual ~FSImpl() { } virtual bool begin() = 0; virtual void end() = 0; virtual bool format() = 0; diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp index 2b16fb899f..471de34c29 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 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 ebb23ce0ca..7b9a3520b3 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::oneShot 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::oneShot 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 9d50be55e5..f16795f7e4 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); @@ -174,7 +176,7 @@ class HardwareSerial: public Stream } size_t write(const char *buffer) { - return buffer? uart_write(_uart, buffer, strlen(buffer)): 0; + return buffer ? uart_write(_uart, buffer, strlen(buffer)) : 0; } operator bool() const { 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 d13bae200d..1ac676fd7f 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 @@ -37,82 +37,87 @@ namespace YieldPolicy struct DoNothing { - static void execute() {} + static void execute() {} }; struct YieldOrSkip { - static void execute() {delay(0);} + static void execute() + { + delay(0); + } }; } //YieldPolicy -template +template class timeoutTemplate { public: - using timeType = decltype(millis()); - - timeoutTemplate(timeType timeout) - : _timeout(timeout), _start(millis()) - {} - - bool expired() - { - YieldPolicyT::execute(); //in case of DoNothing: gets optimized away - if(PeriodicT) //in case of false: gets optimized away - return expiredRetrigger(); - return expiredOneShot(); - } - - operator bool() - { - return expired(); - } - - void reset(const timeType newTimeout) - { - _timeout = newTimeout; - reset(); - } - - void reset() - { - _start = millis(); - } - - timeType getTimeout() const - { - return _timeout; - } - - bool checkExpired(const timeType t) const - { - return (t - _start) >= _timeout; - } - + using timeType = decltype(millis()); + + timeoutTemplate(timeType timeout) + : _timeout(timeout), _start(millis()) + {} + + bool expired() + { + YieldPolicyT::execute(); //in case of DoNothing: gets optimized away + if (PeriodicT) //in case of false: gets optimized away + { + return expiredRetrigger(); + } + return expiredOneShot(); + } + + operator bool() + { + return expired(); + } + + void reset(const timeType newTimeout) + { + _timeout = newTimeout; + reset(); + } + + void reset() + { + _start = millis(); + } + + timeType getTimeout() const + { + return _timeout; + } + + bool checkExpired(const timeType t) const + { + return (t - _start) >= _timeout; + } + protected: - - bool expiredRetrigger() - { - timeType current = millis(); - if(checkExpired(current)) + + bool expiredRetrigger() + { + timeType current = millis(); + 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; + } + + 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; + return checkExpired(millis()); } - return false; - } - - bool expiredOneShot() const - { - return checkExpired(millis()); - } - - timeType _timeout; - timeType _start; + + timeType _timeout; + timeType _start; }; using oneShot = polledTimeout::timeoutTemplate; @@ -121,11 +126,11 @@ using periodic = polledTimeout::timeoutTemplate; } //polledTimeout -/* A 1-shot timeout that auto-yields when in CONT can be built as follows: - * using oneShotYield = 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. - */ +/* A 1-shot timeout that auto-yields when in CONT can be built as follows: + using oneShotYield = 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 73a955b4dd..3d59ed8ef8 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,66 +31,75 @@ #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; } + 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); + } - 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.c b/cores/esp8266/StackThunk.c index 23f4374082..8faa1a616b 100644 --- a/cores/esp8266/StackThunk.c +++ b/cores/esp8266/StackThunk.c @@ -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 @@ -39,84 +39,98 @@ uint32_t stack_thunk_refcnt = 0; /* 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(); - } + 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; - } + 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; - } + 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_refcnt() +{ + return stack_thunk_refcnt; } -uint32_t stack_thunk_get_stack_top() { - return (uint32_t)stack_thunk_top; +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_stack_bot() +{ + return (uint32_t)stack_thunk_ptr; } -uint32_t stack_thunk_get_cont_sp() { - return (uint32_t)stack_thunk_save; +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); + 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", 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", 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 7ebf11d0e2..933815f02f 100644 --- a/cores/esp8266/StreamString.cpp +++ b/cores/esp8266/StreamString.cpp @@ -1,66 +1,76 @@ -/** - StreamString.cpp - - 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 "StreamString.h" - -size_t StreamString::write(const uint8_t *data, size_t size) { - if(size && data) { - if(reserve(length() + size + 1)) { - memcpy((void *) (buffer + len), (const void *) data, size); - len += size; - *(buffer + len) = 0x00; // add null for string end - return size; - } - } - return 0; -} - -size_t StreamString::write(uint8_t data) { - return concat((char) data); -} - -int StreamString::available() { - return length(); -} - -int StreamString::read() { - if(length()) { - char c = charAt(0); - remove(0, 1); - return c; - - } - return -1; -} - -int StreamString::peek() { - if(length()) { - char c = charAt(0); - return c; - } - return -1; -} - -void StreamString::flush() { -} - +/** + StreamString.cpp + + 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 "StreamString.h" + +size_t StreamString::write(const uint8_t *data, size_t size) +{ + if (size && data) + { + if (reserve(length() + size + 1)) + { + memcpy((void *)(buffer + len), (const void *) data, size); + len += size; + *(buffer + len) = 0x00; // add null for string end + return size; + } + } + return 0; +} + +size_t StreamString::write(uint8_t data) +{ + return concat((char) data); +} + +int StreamString::available() +{ + return length(); +} + +int StreamString::read() +{ + if (length()) + { + char c = charAt(0); + remove(0, 1); + return c; + + } + return -1; +} + +int StreamString::peek() +{ + if (length()) + { + char c = charAt(0); + return c; + } + return -1; +} + +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 ba2d5e3715..080f269c46 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,54 +38,56 @@ #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() const =0; - // Return the port of the host who sent the current incoming packet - virtual uint16_t remotePort() const =0; - protected: - CONST uint8_t* rawIPAddress(CONST IPAddress& addr) { - return addr.raw_address(); - } + // Return the IP address of the host who sent the current incoming packet + virtual IPAddress remoteIP() const = 0; + // Return the port of the host who sent the current incoming packet + virtual uint16_t remotePort() const = 0; +protected: + 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 de57ef3c66..3295e0546a 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -8,109 +8,120 @@ #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) + : _async(false) + , _error(0) + , _buffer(0) + , _bufferLen(0) + , _size(0) + , _startAddress(0) + , _currentAddress(0) + , _command(U_FLASH) + , _hash(nullptr) + , _verify(nullptr) { #if ARDUINO_SIGNING - installSignature(&hash, &sign); + installSignature(&hash, &sign); #endif } -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); @@ -118,322 +129,396 @@ 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); + 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(); + // 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 - 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"); + 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 - uint8_t *sig = (uint8_t*)malloc(sigLen); - if (!sig) { - _setError(UPDATE_ERROR_SIGN); - _reset(); - return false; - } - ESP.flashRead(_startAddress + binSize, (uint32_t *)sig, sigLen); + 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 - 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; + } +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.printf_P(PSTR("[Updater] Signature matches\n")); #endif - if (!_verify->verify(_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 @@ -441,79 +526,117 @@ size_t UpdaterClass::writeStream(Stream &data) { return 0; } - 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; yield(); } 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 e9eea05ef7..40c4984702 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -30,8 +30,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; @@ -40,137 +41,190 @@ 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: 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); + } //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 51c91238e2..7a45a3e8e3 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,33 +150,45 @@ String::~String() { // /* Memory Management */ // /*********************************************/ -inline void String::init(void) { +inline void String::init(void) +{ buffer = NULL; capacity = 0; len = 0; } -void String::invalidate(void) { - if(buffer) +void String::invalidate(void) +{ + if (buffer) + { free(buffer); + } 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) + { buffer[0] = 0; + } return 1; } return 0; } -unsigned char String::changeBuffer(unsigned int maxStrLen) { +unsigned char String::changeBuffer(unsigned int maxStrLen) +{ size_t newSize = (maxStrLen + 16) & (~0xf); char *newbuffer = (char *) realloc(buffer, newSize); - if(newbuffer) { + if (newbuffer) + { size_t oldSize = capacity + 1; // include NULL. if (newSize > oldSize) { @@ -171,8 +205,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; } @@ -181,8 +217,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; } @@ -192,14 +230,19 @@ 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(buffer, rhs.buffer); len = rhs.len; rhs.len = 0; return; - } else { + } + else + { free(buffer); } } @@ -212,45 +255,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; } @@ -259,100 +326,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(buffer + len, buffer, len); len = newlen; buffer[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(buffer + len, cstr); len = 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(buffer + len, (PGM_P)str); len = newlen; return 1; @@ -362,80 +467,113 @@ 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; } StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs) { StringSumHelper &a = const_cast(lhs); - if (!a.concat(rhs)) a.invalidate(); + if (!a.concat(rhs)) + { + a.invalidate(); + } return a; } @@ -443,79 +581,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; } @@ -525,21 +699,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 > len - s2.len || !buffer || !s2.buffer) +unsigned char String::startsWith(const String &s2, unsigned int offset) const +{ + if (offset > 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; } @@ -547,40 +730,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) + { buffer[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 buffer[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; } @@ -589,79 +787,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]; buffer[fromIndex + 1] = '\0'; char* temp = strrchr(buffer, ch); buffer[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 = buffer; p <= buffer + fromIndex; p++) { + for (char *p = buffer; p <= buffer + fromIndex; p++) + { p = strstr(p, s2.buffer); - if(!p) + if (!p) + { break; - if((unsigned int) (p - buffer) <= fromIndex) + } + if ((unsigned int)(p - buffer) <= 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 buffer[right] = '\0'; out = buffer + left; // pointer arithmetic @@ -673,29 +906,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 = buffer; *p; p++) { - if(*p == find) + } + for (char *p = buffer; *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 = buffer; char *foundAt; - if(diff == 0) { - while((foundAt = strstr(readFrom, find.buffer)) != NULL) { + if (diff == 0) + { + while ((foundAt = strstr(readFrom, find.buffer)) != NULL) + { memcpy(foundAt, replace.buffer, replace.len); readFrom = foundAt + replace.len; } - } else if(diff < 0) { + } + else if (diff < 0) + { char *writeTo = buffer; - while((foundAt = strstr(readFrom, find.buffer)) != NULL) { + while ((foundAt = strstr(readFrom, find.buffer)) != NULL) + { unsigned int n = foundAt - readFrom; memcpy(writeTo, readFrom, n); writeTo += n; @@ -705,18 +952,26 @@ void String::replace(const String& find, const String& replace) { len += diff; } strcpy(writeTo, readFrom); - } else { + } + 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 = buffer + index + find.len; memmove(readFrom + diff, readFrom, len - (readFrom - buffer)); len += diff; @@ -727,21 +982,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 = buffer + index; @@ -750,34 +1010,51 @@ void String::remove(unsigned int index, unsigned int count) { buffer[len] = 0; } -void String::toLowerCase(void) { - if(!buffer) +void String::toLowerCase(void) +{ + if (!buffer) + { return; - for(char *p = buffer; *p; p++) { + } + for (char *p = buffer; *p; p++) + { *p = tolower(*p); } } -void String::toUpperCase(void) { - if(!buffer) +void String::toUpperCase(void) +{ + if (!buffer) + { return; - for(char *p = buffer; *p; p++) { + } + for (char *p = buffer; *p; p++) + { *p = toupper(*p); } } -void String::trim(void) { - if(!buffer || len == 0) +void String::trim(void) +{ + if (!buffer || len == 0) + { return; + } char *begin = buffer; - while(isspace(*begin)) + while (isspace(*begin)) + { begin++; + } char *end = buffer + len - 1; - while(isspace(*end) && end >= begin) + while (isspace(*end) && end >= begin) + { end--; + } len = end + 1 - begin; - if(begin > buffer) + if (begin > buffer) + { memmove(buffer, begin, len); + } buffer[len] = 0; } @@ -785,15 +1062,21 @@ void String::trim(void) { // /* Parsing / Conversion */ // /*********************************************/ -long String::toInt(void) const { - if(buffer) +long String::toInt(void) const +{ + if (buffer) + { return atol(buffer); + } return 0; } -float String::toFloat(void) const { - if(buffer) +float String::toFloat(void) const +{ + if (buffer) + { return atof(buffer); + } return 0; } diff --git a/cores/esp8266/WString.h b/cores/esp8266/WString.h index bbf8ee40ad..9cf7689ccc 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,259 +39,309 @@ 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 unsucessful. - 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 unsucessful. + 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 acccess - 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 buffer; } - char* end() { return buffer + length(); } - const char* begin() const { return c_str(); } - const char* end() const { return c_str() + length(); } + // character acccess + 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 buffer; + } + char* end() + { + return buffer + 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; + // parsing/conversion + long toInt(void) const; + float toFloat(void) const; - protected: - char *buffer; // the actual char array - unsigned int capacity; // the array length minus one (for the '\0') - unsigned int len; // the String length (not counting the '\0') - protected: - void init(void); - void invalidate(void); - unsigned char changeBuffer(unsigned int maxStrLen); - unsigned char concat(const char *cstr, unsigned int length); +protected: + char *buffer; // the actual char array + unsigned int capacity; // the array length minus one (for the '\0') + unsigned int len; // the String length (not counting the '\0') +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.c b/cores/esp8266/cont_util.c index 2d5d2b1eac..c0e27da664 100644 --- a/cores/esp8266/cont_util.c +++ b/cores/esp8266/cont_util.c @@ -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 @@ -26,42 +26,49 @@ #define CONT_STACKGUARD 0xfeefeffe -void cont_init(cont_t* cont) { +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++) + 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; } // No need for this to be in IRAM, not expected to be IRQ called -int cont_get_free_stack(cont_t* cont) { +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) { +bool ICACHE_RAM_ATTR cont_can_yield(cont_t* cont) +{ return !ETS_INTR_WITHINISR() && cont->pc_ret != 0 && cont->pc_yield == 0; } @@ -72,10 +79,10 @@ 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. - uint32_t sp_safe = CONT_STACKSIZE/4 - ((sp - &cont->stack[0] - 64)/4); + uint32_t sp_safe = CONT_STACKSIZE / 4 - ((sp - &cont->stack[0] - 64) / 4); // Fill stack with magic values - for(uint32_t pos = 0; pos < sp_safe; pos++) + for (uint32_t pos = 0; pos < sp_safe; pos++) { cont->stack[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.c b/cores/esp8266/core_esp8266_eboot_command.c index ee3ccb4809..50072f9c08 100644 --- a/cores/esp8266/core_esp8266_eboot_command.c +++ b/cores/esp8266/core_esp8266_eboot_command.c @@ -1,23 +1,23 @@ -/* - core_esp8266_eboot_command.c - interface to the eboot bootloader +/* + 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. + 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 #include @@ -28,16 +28,20 @@ uint32_t crc_update(uint32_t crc, const uint8_t *data, size_t length) uint32_t i; bool bit; uint8_t c; - - while (length--) { + + while (length--) + { c = *data++; - for (i = 0x80; i > 0; i >>= 1) { + for (i = 0x80; i > 0; i >>= 1) + { bit = crc & 0x80000000; - if (c & i) { + if (c & i) + { bit = !bit; } crc <<= 1; - if (bit) { + if (bit) + { crc ^= 0x04c11db7; } } @@ -47,7 +51,7 @@ uint32_t crc_update(uint32_t crc, const uint8_t *data, size_t length) uint32_t eboot_command_calculate_crc32(const struct eboot_command* cmd) { - return crc_update(0xffffffff, (const uint8_t*) cmd, + return crc_update(0xffffffff, (const uint8_t*) cmd, offsetof(struct eboot_command, crc32)); } @@ -55,13 +59,15 @@ 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) { + for (uint32_t i = 0; i < dw_count; ++i) + { dst[i] = RTC_MEM[i]; } uint32_t crc32 = eboot_command_calculate_crc32(cmd); if ((cmd->magic & EBOOT_MAGIC_MASK) != EBOOT_MAGIC || - cmd->crc32 != crc32) { + cmd->crc32 != crc32) + { return 1; } @@ -75,7 +81,8 @@ void eboot_command_write(struct eboot_command* 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) { + for (uint32_t i = 0; i < dw_count; ++i) + { RTC_MEM[i] = src[i]; } } 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.c b/cores/esp8266/core_esp8266_flash_utils.c index 0713998a6a..580bff6374 100644 --- a/cores/esp8266/core_esp8266_flash_utils.c +++ b/cores/esp8266/core_esp8266_flash_utils.c @@ -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 @@ -28,7 +28,8 @@ int SPIEraseAreaEx(const uint32_t start, const uint32_t size) { - if ((start & (FLASH_SECTOR_SIZE - 1)) != 0) { + if ((start & (FLASH_SECTOR_SIZE - 1)) != 0) + { return 1; } @@ -37,24 +38,30 @@ int SPIEraseAreaEx(const uint32_t start, const uint32_t 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)) { + 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)) { + 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)) { + for (; current_sector < end; + ++current_sector, --sector_count) + { + if (SPIEraseSector(current_sector)) + { return 4; } } diff --git a/cores/esp8266/core_esp8266_i2s.c b/cores/esp8266/core_esp8266_i2s.c index 7c8b4b73ac..0a5a652485 100644 --- a/cores/esp8266/core_esp8266_i2s.c +++ b/cores/esp8266/core_esp8266_i2s.c @@ -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" @@ -42,28 +42,30 @@ // 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; +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. +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) @@ -83,493 +85,606 @@ 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); +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_is_full() +{ + return _i2s_is_full(tx); } -bool i2s_rx_is_full() { - return _i2s_is_full( rx ); +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); +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_is_empty() +{ + return _i2s_is_empty(tx); } -bool i2s_rx_is_empty() { - return _i2s_is_empty( rx ); +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; +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_available() +{ + return _i2s_available(tx); } -uint16_t i2s_rx_available(){ - return _i2s_available( rx ); +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; +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; +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; + } } - 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 +static bool _i2s_write_sample(uint32_t sample, bool nb) +{ + if (!tx) + { return false; - } - while (1) { - if (tx->slc_queue_len > 0) { - break; - } else { - optimistic_yield(10000); + } + + 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; } - 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; + 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(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_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); +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; +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) + { - 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) { + 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 (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); + 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; - } + 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;icurr_slc_buf[tx->curr_slc_buf_pos++] = (v << 16) | v; - } + tx->curr_slc_buf[tx->curr_slc_buf_pos++] = (v << 16) | v; + } } else - { - for(uint16_t i=0;icurr_slc_buf[tx->curr_slc_buf_pos++] = (v1 << 16) | v2; } - } - + } + 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_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_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_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); } +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) { +bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking) +{ + if (!rx) + { return false; - } - while (1) { - if (rx->slc_queue_len > 0){ - break; - } else { - optimistic_yield(10000); + } + 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; } - 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; - } } 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.c b/cores/esp8266/core_esp8266_noniso.c index c499c1bcf3..bb508803e8 100644 --- a/cores/esp8266/core_esp8266_noniso.c +++ b/cores/esp8266/core_esp8266_noniso.c @@ -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 @@ -29,22 +29,27 @@ #include #include "stdlib_noniso.h" -char* ltoa(long value, char* result, int base) { +char* ltoa(long value, char* result, int base) +{ return itoa((int)value, result, base); } -char* ultoa(unsigned long value, char* result, int 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) { +char * dtostrf(double number, signed char width, unsigned char prec, char *s) +{ bool negative = false; - if (isnan(number)) { + if (isnan(number)) + { strcpy(s, "nan"); return s; } - if (isinf(number)) { + if (isinf(number)) + { strcpy(s, "inf"); return s; } @@ -52,12 +57,14 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { char* out = s; int fillme = width; // how many cells to fill for the integer part - if (prec > 0) { - fillme -= (prec+1); + if (prec > 0) + { + fillme -= (prec + 1); } // Handle negative numbers - if (number < 0.0) { + if (number < 0.0) + { negative = true; fillme--; number = -number; @@ -67,7 +74,9 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { // 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; @@ -75,7 +84,8 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { // Figure out how big our number really is double tenpow = 1.0; int digitcount = 1; - while (number >= 10.0 * tenpow) { + while (number >= 10.0 * tenpow) + { tenpow *= 10.0; digitcount++; } @@ -84,21 +94,30 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { fillme -= digitcount; // Pad unused cells with spaces - while (fillme-- > 0) { + while (fillme-- > 0) + { *out++ = ' '; } // Handle negative sign - if (negative) *out++ = '-'; + if (negative) + { + *out++ = '-'; + } // Print the digits, and if necessary, the decimal point digitcount += prec; int8_t digit = 0; - while (digitcount-- > 0) { + while (digitcount-- > 0) + { digit = (int8_t)number; - if (digit > 9) digit = 9; // insurance + if (digit > 9) + { + digit = 9; // insurance + } *out++ = (char)('0' | digit); - if ((digitcount == prec) && (prec > 0)) { + if ((digitcount == prec) && (prec > 0)) + { *out++ = '.'; } number -= digit; diff --git a/cores/esp8266/core_esp8266_phy.c b/cores/esp8266/core_esp8266_phy.c index d15b6830b1..7f628c7c8b 100644 --- a/cores/esp8266/core_esp8266_phy.c +++ b/cores/esp8266/core_esp8266_phy.c @@ -1,32 +1,32 @@ /* - 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 - #include - #include - #include "c_types.h" - #include "ets_sys.h" - #include "spi_flash.h" +#include +#include +#include +#include +#include "c_types.h" +#include "ets_sys.h" +#include "spi_flash.h" static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] = { @@ -88,11 +88,11 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] = // 0: 40MHz // 1: 26MHz // 2: 24MHz - #if F_CRYSTAL == 40000000 - [48] = 0, - #else - [48] = 1, - #endif +#if F_CRYSTAL == 40000000 + [48] = 0, +#else + [48] = 1, +#endif @@ -263,7 +263,8 @@ extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, s extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size) { - if (!spoof_init_data || size != 128) { + if (!spoof_init_data || size != 128) + { return __real_spi_flash_read(addr, dst, size); } @@ -293,7 +294,7 @@ extern void __run_user_rf_pre_init(void) uint32_t user_rf_cal_sector_set(void) { spoof_init_data = true; - return flashchip->chip_size/SPI_FLASH_SEC_SIZE - 4; + return flashchip->chip_size / SPI_FLASH_SEC_SIZE - 4; } void user_rf_pre_init() @@ -305,7 +306,8 @@ void user_rf_pre_init() system_set_os_print(0); int rf_mode = __get_rf_mode(); - if (rf_mode >= 0) { + if (rf_mode >= 0) + { system_phy_set_rfoption(rf_mode); } __run_user_rf_pre_init(); diff --git a/cores/esp8266/core_esp8266_postmortem.c b/cores/esp8266/core_esp8266_postmortem.c index 67ceac0127..4286554706 100644 --- a/cores/esp8266/core_esp8266_postmortem.c +++ b/cores/esp8266/core_esp8266_postmortem.c @@ -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 @@ -54,76 +54,87 @@ 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 ) { +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"))); +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, ...) { +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) { + while (*c) + { ets_putc(*(c++)); } } -void __wrap_system_restart_local() { +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. */ + 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 = {0}; 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) + rst_info.reason != REASON_EXCEPTION_RST && + rst_info.reason != REASON_WDT_RST) { return; } ets_install_putc1(&uart_write_char_d); - if (s_panic_line) { + if (s_panic_line) + { ets_printf_P(PSTR("\nPanic %S:%d %S"), s_panic_file, s_panic_line, s_panic_func); - if (s_panic_what) { + if (s_panic_what) + { ets_printf_P(PSTR(": Assertion '%S' failed."), s_panic_what); } ets_putc('\n'); } - else if (s_unhandled_exception) { + else if (s_unhandled_exception) + { ets_printf_P(PSTR("\nUnhandled C++ exception: %S\n"), s_unhandled_exception); } - else if (s_abort_called) { + else if (s_abort_called) + { ets_printf_P(PSTR("\nAbort called\n")); } - else if (rst_info.reason == REASON_EXCEPTION_RST) { + 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); + 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) { + 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_start = (uint32_t) & (g_pcont->stack); uint32_t cont_stack_end = (uint32_t) g_pcont->stack_end; uint32_t stack_end; @@ -131,19 +142,23 @@ void __wrap_system_restart_local() { // and everything up to __wrap_system_restart_local // (determined empirically, might break) uint32_t offset = 0; - if (rst_info.reason == REASON_SOFT_WDT_RST) { + if (rst_info.reason == REASON_SOFT_WDT_RST) + { offset = 0x1b0; } - else if (rst_info.reason == REASON_EXCEPTION_RST) { + else if (rst_info.reason == REASON_EXCEPTION_RST) + { offset = 0x1a0; } - else if (rst_info.reason == REASON_WDT_RST) { + else if (rst_info.reason == REASON_WDT_RST) + { offset = 0x10; } ets_printf_P(PSTR("\n>>>stack>>>\n")); - if (sp_dump > stack_thunk_get_stack_bot() && sp_dump <= stack_thunk_get_stack_top()) { + 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()); @@ -151,11 +166,13 @@ void __wrap_system_restart_local() { sp_dump = stack_thunk_get_cont_sp(); } - if (sp_dump > cont_stack_start && sp_dump < cont_stack_end) { + if (sp_dump > cont_stack_start && sp_dump < cont_stack_end) + { ets_printf_P(PSTR("\nctx: cont\n")); stack_end = cont_stack_end; } - else { + else + { ets_printf_P(PSTR("\nctx: sys\n")); stack_end = 0x3fffffb0; // it's actually 0x3ffffff0, but the stuff below ets_run @@ -169,68 +186,80 @@ void __wrap_system_restart_local() { ets_printf_P(PSTR("<<> USTXC) & 0xff)) { } - if (c == '\n') { + if (c == '\n') + { USF(0) = '\r'; } USF(0) = c; } -static void uart1_write_char_d(char c) { +static void uart1_write_char_d(char c) +{ while (((USS(1) >> USTXC) & 0xff) >= 0x7e) { } - if (c == '\n') { + if (c == '\n') + { USF(1) = '\r'; } USF(1) = c; } -static void raise_exception() { - __asm__ __volatile__ ("syscall"); +static void raise_exception() +{ + __asm__ __volatile__("syscall"); while (1); // never reached, needed to satisfy "noreturn" attribute } -void abort() { +void abort() +{ s_abort_called = true; raise_exception(); } -void __unhandled_exception(const char *str) { +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) { +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; @@ -239,7 +268,8 @@ void __assert_func(const char *file, int line, const char *func, const char *wha raise_exception(); } -void __panic_func(const char* file, int line, const char* func) { +void __panic_func(const char* file, int line, const char* func) +{ s_panic_file = file; s_panic_line = line; s_panic_func = func; diff --git a/cores/esp8266/core_esp8266_si2c.c b/cores/esp8266/core_esp8266_si2c.c index 549273a1ad..1b6ee38cc7 100644 --- a/cores/esp8266/core_esp8266_si2c.c +++ b/cores/esp8266/core_esp8266_si2c.c @@ -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" @@ -118,666 +118,863 @@ static void onTimer(); #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_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); - } + // 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; + // 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; +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; - } + unsigned int reg; + for (i = 0; i < v; i++) + { + reg = GPI; + } #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_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_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_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_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 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; +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 + 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 (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. - + { + 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_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; + uint8_t i; - // ensure data will fit into buffer - if (length > TWI_BUFFER_LENGTH) { - return 1; - } + // 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; - } + // 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]; - } + // set length and copy data into tx buffer + twi_txBufferLength = length; + for (i = 0; i < length; ++i) + { + twi_txBuffer[i] = data[i]; + } - return 0; + return 0; } -void twi_attachSlaveRxEvent( void (*function)(uint8_t*, size_t) ) +void twi_attachSlaveRxEvent(void (*function)(uint8_t*, size_t)) { - twi_onSlaveReceive = function; + twi_onSlaveReceive = function; } -void twi_attachSlaveTxEvent( void (*function)(void) ) +void twi_attachSlaveTxEvent(void (*function)(void)) { - twi_onSlaveTransmit = function; + 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) - } + // 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; + // 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; + // 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) { + 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; + // 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; + // 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; + // 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; + // 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; + // 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; + // leave slave receiver state + twi_releaseBus(); + break; // All case TW_NO_INFO: // no state information - break; + break; case TW_BUS_ERROR: // bus error, illegal stop/start - twi_error = TW_BUS_ERROR; - twi_stop(); - break; - } + twi_error = TW_BUS_ERROR; + twi_stop(); + break; + } } void ICACHE_RAM_ATTR onTimer() { - twi_releaseBus(); - twip_status = TW_BUS_ERROR; - twi_onTwipEvent(twip_status); - twip_mode = TWIPM_WAIT; - twip_state = TWIP_BUS_ERR; + 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; - } + if (e == NULL) + { + return; + } - switch (e->sig) - { - case TWI_SIG_TX: - twi_onSlaveTransmit(); + 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; - } + // 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); + // Initiate transmission + twi_onTwipEvent(TW_ST_DATA_ACK); - break; + break; - case TWI_SIG_RX: - // ack future responses and leave slave receiver state - twi_releaseBus(); - twi_onSlaveReceive(twi_rxBuffer, e->par); - 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; - } + 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; - } + 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.c b/cores/esp8266/core_esp8266_sigma_delta.c index d101401c2e..d1d867de33 100644 --- a/cores/esp8266/core_esp8266_sigma_delta.c +++ b/cores/esp8266/core_esp8266_sigma_delta.c @@ -1,22 +1,22 @@ /* - 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 @@ -30,145 +30,151 @@ void sigmaDeltaSetPrescaler(uint8_t prescaler); // avoids compiler warning /****************************************************************************** - * FunctionName : sigmaDeltaEnable - * Description : enable the internal sigma delta source - * Parameters : none - * Returns : none + 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) + 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 + 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) + 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 + 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 - } + (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 + 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 - } + 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 + 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; + 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 + 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); + (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 + 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; + uint32_t reg = GPSD; + (void) channel; + + reg = (reg & ~(0xFF << GPSDT)) | ((duty & 0xFF) << GPSDT); + GPSD = reg; - 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 + 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); + (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 + 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; + uint32_t reg = GPSD; - reg = (reg & ~(0xFF << GPSDP)) | ((prescaler & 0xFF) << GPSDP); - GPSD = reg; + 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 + 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); + return (uint8_t)((GPSD >> GPSDP) & 0xFF); } diff --git a/cores/esp8266/core_esp8266_timer.c b/cores/esp8266/core_esp8266_timer.c index 1b18861ae4..f4c4596819 100644 --- a/cores/esp8266/core_esp8266_timer.c +++ b/cores/esp8266/core_esp8266_timer.c @@ -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" @@ -29,11 +29,16 @@ static volatile timercallback timer1_user_cb = NULL; -void ICACHE_RAM_ATTR timer1_isr_handler(void *para){ +void ICACHE_RAM_ATTR timer1_isr_handler(void *para) +{ (void) para; - if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) TEIE &= ~TEIE1;//edge int disable + if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) + { + TEIE &= ~TEIE1; //edge int disable + } T1I = 0; - if (timer1_user_cb) { + 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 @@ -42,32 +47,41 @@ void ICACHE_RAM_ATTR timer1_isr_handler(void *para){ } } -void ICACHE_RAM_ATTR timer1_isr_init(){ +void ICACHE_RAM_ATTR timer1_isr_init() +{ ETS_FRC_TIMER1_INTR_ATTACH(timer1_isr_handler, NULL); } -void timer1_attachInterrupt(timercallback userFunc) { +void timer1_attachInterrupt(timercallback userFunc) +{ timer1_user_cb = userFunc; ETS_FRC1_INTR_ENABLE(); } -void ICACHE_RAM_ATTR timer1_detachInterrupt() { +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){ +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_write(uint32_t ticks) +{ + T1L = ((ticks) & 0x7FFFFF); + if ((T1C & (1 << TCIT)) == 0) + { + TEIE |= TEIE1; //edge int enable + } } -void ICACHE_RAM_ATTR timer1_disable(){ +void ICACHE_RAM_ATTR timer1_disable() +{ T1C = 0; T1I = 0; } @@ -77,9 +91,11 @@ void ICACHE_RAM_ATTR timer1_disable(){ static volatile timercallback timer0_user_cb = NULL; -void ICACHE_RAM_ATTR timer0_isr_handler(void* para){ +void ICACHE_RAM_ATTR timer0_isr_handler(void* para) +{ (void) para; - if (timer0_user_cb) { + 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 @@ -88,16 +104,19 @@ void ICACHE_RAM_ATTR timer0_isr_handler(void* para){ } } -void timer0_isr_init(){ +void timer0_isr_init() +{ ETS_CCOMPARE0_INTR_ATTACH(timer0_isr_handler, NULL); } -void timer0_attachInterrupt(timercallback userFunc) { +void timer0_attachInterrupt(timercallback userFunc) +{ timer0_user_cb = userFunc; ETS_CCOMPARE0_ENABLE(); } -void ICACHE_RAM_ATTR timer0_detachInterrupt() { +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 28d4af6095..15e65cb53f 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 @@ -31,134 +31,138 @@ 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 - -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 + + 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.c b/cores/esp8266/core_esp8266_waveform.c index e443102db7..3bb12c7c14 100644 --- a/cores/esp8266/core_esp8266_waveform.c +++ b/cores/esp8266/core_esp8266_waveform.c @@ -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 @@ -49,11 +49,12 @@ #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 +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 @@ -70,78 +71,94 @@ static uint32_t (*timer1CB)() = NULL; // 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; +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 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; +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(); - } +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)); - } +int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) +{ + if ((pin > 16) || isFlashInterfacePin(pin)) + { + return false; } - while (waveformToEnable) { - delay(0); // Wait for waveform to update + 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 } - } - return true; + 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 @@ -150,156 +167,190 @@ int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t // 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 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; +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; +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); + } } - } - // 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()); - } - - 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 + // 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 #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.c b/cores/esp8266/core_esp8266_wiring.c index 3d91043d34..ed07a5839b 100644 --- a/cores/esp8266/core_esp8266_wiring.c +++ b/cores/esp8266/core_esp8266_wiring.c @@ -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" @@ -35,29 +35,38 @@ static uint32_t micros_overflow_count = 0; #define ONCE 0 #define REPEAT 1 -void delay_end(void* arg) { +void delay_end(void* arg) +{ (void) arg; esp_schedule(); } -void delay(unsigned long ms) { - if(ms) { +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 { + } + else + { esp_schedule(); } esp_yield(); - if(ms) { + if (ms) + { os_timer_disarm(&delay_timer); } } -void micros_overflow_tick(void* arg) { +void micros_overflow_tick(void* arg) +{ (void) arg; uint32_t m = system_get_time(); - if(m < micros_at_last_overflow_tick) + if (m < micros_at_last_overflow_tick) + { ++micros_overflow_count; + } micros_at_last_overflow_tick = m; } @@ -87,7 +96,7 @@ void micros_overflow_tick(void* arg) { // 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 +// 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. @@ -160,51 +169,56 @@ void micros_overflow_tick(void* arg) { 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); + 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 ); + // (a) Init. low-acc with high-word of 1st product. The right-shift + // falls on a byte boundary, hence is relatively quick. - // (b) Offset sum, low-acc - acc.q += ( m * (uint64_t)MAGIC_1E3_wHI ); + acc.q = ((uint64_t)(m * (uint64_t)MAGIC_1E3_wLO) >> 32); - // (c) Offset sum, low-acc - acc.q += ( c * (uint64_t)MAGIC_1E3_wLO ); + // (b) Offset sum, low-acc + acc.q += (m * (uint64_t)MAGIC_1E3_wHI); - // (d) Truncated sum, high-acc - acc.a[1] += (uint32_t)( c * (uint64_t)MAGIC_1E3_wHI ); + // (c) Offset sum, low-acc + acc.q += (c * (uint64_t)MAGIC_1E3_wLO); - return ( acc.a[1] ); // Extract result, high-acc + // (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() { +unsigned long ICACHE_RAM_ATTR micros() +{ return system_get_time(); } -uint64_t ICACHE_RAM_ATTR micros64() { +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) { +void ICACHE_RAM_ATTR delayMicroseconds(unsigned int us) +{ os_delay_us(us); } -void init() { +void init() +{ initPins(); timer1_isr_init(); os_timer_setfn(µs_overflow_timer, (os_timer_func_t*) µs_overflow_tick, 0); diff --git a/cores/esp8266/core_esp8266_wiring_analog.c b/cores/esp8266/core_esp8266_wiring_analog.c index 1ff1d264de..e95936883f 100644 --- a/cores/esp8266/core_esp8266_wiring_analog.c +++ b/cores/esp8266/core_esp8266_wiring_analog.c @@ -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" @@ -29,10 +29,11 @@ extern int __analogRead(uint8_t pin) { // accept both A0 constant and ADC channel number - if(pin == 17 || pin == 0) { + if (pin == 17 || pin == 0) + { return system_adc_read(); } 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.c b/cores/esp8266/core_esp8266_wiring_digital.c index 1954b3a1a2..a94153c914 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.c +++ b/cores/esp8266/core_esp8266_wiring_digital.c @@ -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" @@ -28,207 +28,278 @@ 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) - } +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; +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 + GPIO INTERRUPTS */ typedef void (*voidFuncPtr)(void); typedef void (*voidFuncPtrArg)(void*); -typedef struct { - uint8_t mode; - void (*fn)(void); - void * arg; +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; +typedef struct InterruptInfo +{ + uint8_t pin; + uint8_t value; + uint32_t micro; } InterruptInfo; -typedef struct { - InterruptInfo* interruptInfo; - void* functionInfo; +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); +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(); + ETS_GPIO_INTR_ENABLE(); } extern void cleanupFunctional(void* arg); -extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void *arg, int mode) { - 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 __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void *arg, int mode) +{ + 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 ) +extern void ICACHE_RAM_ATTR __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode) { - __attachInterruptArg (pin, userFunc, 0, 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(); - } +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); - } +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 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.c b/cores/esp8266/core_esp8266_wiring_pulse.c index ddcd8b9cd5..5b2f5baf5a 100644 --- a/cores/esp8266/core_esp8266_wiring_pulse.c +++ b/cores/esp8266/core_esp8266_wiring_pulse.c @@ -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" @@ -37,7 +37,8 @@ extern uint32_t xthal_get_ccount(); 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) { + if (timeout > max_timeout_us) + { timeout = max_timeout_us; } const uint32_t timeout_cycles = microsecondsToClockCycles(timeout); diff --git a/cores/esp8266/core_esp8266_wiring_pwm.c b/cores/esp8266/core_esp8266_wiring_pwm.c index 744a3e4c84..892e58f449 100644 --- a/cores/esp8266/core_esp8266_wiring_pwm.c +++ b/cores/esp8266/core_esp8266_wiring_pwm.c @@ -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 @@ -29,48 +29,67 @@ 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"))); diff --git a/cores/esp8266/core_esp8266_wiring_shift.c b/cores/esp8266/core_esp8266_wiring_shift.c index 673db90641..e4f11af566 100644 --- a/cores/esp8266/core_esp8266_wiring_shift.c +++ b/cores/esp8266/core_esp8266_wiring_shift.c @@ -1,55 +1,67 @@ /* - 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" -uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) { +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) { + for (i = 0; i < 8; ++i) + { digitalWrite(clockPin, HIGH); - if(bitOrder == LSBFIRST) + if (bitOrder == LSBFIRST) + { value |= digitalRead(dataPin) << i; + } else + { value |= digitalRead(dataPin) << (7 - i); + } digitalWrite(clockPin, LOW); } return value; } -void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val) { +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) + 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); 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 6a99510fe6..671ab37608 100644 --- a/cores/esp8266/debug.cpp +++ b/cores/esp8266/debug.cpp @@ -1,37 +1,40 @@ -/* - 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 01f2a417cd..6cbd16a6a5 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 @@ -841,8 +841,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.c b/cores/esp8266/gdb_hooks.c index 26aa6b0dfb..a3723730ed 100644 --- a/cores/esp8266/gdb_hooks.c +++ b/cores/esp8266/gdb_hooks.c @@ -1,42 +1,42 @@ /* - 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. */ 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"))); +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.c b/cores/esp8266/heap.c index d11525671c..c7278f6e62 100644 --- a/cores/esp8266/heap.c +++ b/cores/esp8266/heap.c @@ -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" @@ -16,7 +16,8 @@ void* _malloc_r(struct _reent* unused, size_t size) { (void) unused; void *ret = malloc(size); - if (0 != size && 0 == ret) { + if (0 != size && 0 == ret) + { umm_last_fail_alloc_addr = __builtin_return_address(0); umm_last_fail_alloc_size = size; } @@ -33,7 +34,8 @@ void* _realloc_r(struct _reent* unused, void* ptr, size_t size) { (void) unused; void *ret = realloc(ptr, size); - if (0 != size && 0 == ret) { + if (0 != size && 0 == ret) + { umm_last_fail_alloc_addr = __builtin_return_address(0); umm_last_fail_alloc_size = size; } @@ -44,7 +46,8 @@ 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) { + if (0 != (count * size) && 0 == ret) + { umm_last_fail_alloc_addr = __builtin_return_address(0); umm_last_fail_alloc_size = count * size; } @@ -88,58 +91,70 @@ 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* 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* 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* 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) +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); + 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* 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* 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* 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; } 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..2c336470e3 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; } @@ -50,13 +54,13 @@ class InterruptLock { //} // #define AutoInterruptLock(intrLevel) \ -class _AutoDisableIntr { \ -public: \ - _AutoDisableIntr() { _savedPS = xt_rsil(intrLevel); } \ - ~_AutoDisableIntr() { xt_wsr_ps(_savedPS); } \ -private: \ - uint32_t _savedPS; \ + class _AutoDisableIntr { \ + public: \ + _AutoDisableIntr() { _savedPS = xt_rsil(intrLevel); } \ + ~_AutoDisableIntr() { xt_wsr_ps(_savedPS); } \ + private: \ + uint32_t _savedPS; \ }; \ -_AutoDisableIntr _autoDisableIntr + _AutoDisableIntr _autoDisableIntr #endif //INTERRUPTS_H diff --git a/cores/esp8266/libb64/cdecode.c b/cores/esp8266/libb64/cdecode.c index aa84ef60a8..97e8177484 100755 --- a/cores/esp8266/libb64/cdecode.c +++ b/cores/esp8266/libb64/cdecode.c @@ -1,99 +1,122 @@ /* -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 "cdecode.h" #include -static int base64_decode_value_signed(int8_t value_in){ - static const int8_t decoding[] = {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 decoding[(int)value_in]; +static int base64_decode_value_signed(int8_t value_in) +{ + static const int8_t decoding[] = {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 decoding[(int)value_in]; } -void base64_init_decodestate(base64_decodestate* state_in){ - state_in->step = step_a; - state_in->plainchar = 0; +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; - - *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); +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); + } } - } - /* 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.c b/cores/esp8266/libb64/cencode.c index 4b902997de..e32952431f 100755 --- a/cores/esp8266/libb64/cencode.c +++ b/cores/esp8266/libb64/cencode.c @@ -1,109 +1,125 @@ /* -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 "cencode.h" -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(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; +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 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - if (value_in > 63) return '='; - return encoding[(int)value_in]; +char base64_encode_value(char value_in) +{ + static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) + { + return '='; + } + return 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; - } +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; + /* 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_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..4615899776 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 @@ -12,22 +12,24 @@ For details, see http://sourceforge.net/projects/libb64 #define base64_encode_expected_len_nonewlines(n) ((((4 * (n)) / 3) + 3) & ~3) #define base64_encode_expected_len(n) \ - (base64_encode_expected_len_nonewlines(n) + ((n / ((BASE64_CHARS_PER_LINE * 3) / 4)) + 1)) + (base64_encode_expected_len_nonewlines(n) + ((n / ((BASE64_CHARS_PER_LINE * 3) / 4)) + 1)) #ifdef __cplusplus 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.c b/cores/esp8266/libc_replacements.c index be55e49d3d..6c347f46b8 100644 --- a/cores/esp8266/libc_replacements.c +++ b/cores/esp8266/libc_replacements.c @@ -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 @@ -46,27 +46,31 @@ #include "debug.h" -int ICACHE_RAM_ATTR _open_r (struct _reent* unused, const char *ptr, int mode) { +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) { +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) { +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) { +int ICACHE_RAM_ATTR _lseek_r(struct _reent* unused, int file, int ptr, int dir) +{ (void)unused; (void)file; (void)ptr; @@ -74,7 +78,8 @@ int ICACHE_RAM_ATTR _lseek_r(struct _reent* unused, int file, int ptr, int dir) return 0; } -int ICACHE_RAM_ATTR _read_r(struct _reent* unused, int file, char *ptr, int len) { +int ICACHE_RAM_ATTR _read_r(struct _reent* unused, int file, char *ptr, int len) +{ (void)unused; (void)file; (void)ptr; @@ -82,11 +87,14 @@ int ICACHE_RAM_ATTR _read_r(struct _reent* unused, int file, char *ptr, int len) return 0; } -int ICACHE_RAM_ATTR _write_r(struct _reent* r, int file, char *ptr, int len) { +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--) { + if (file == STDOUT_FILENO) + { + while (pos--) + { ets_putc(*ptr); ++ptr; } @@ -96,17 +104,21 @@ int ICACHE_RAM_ATTR _write_r(struct _reent* r, int file, char *ptr, int 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) { +int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) +{ (void) r; - if (file->_file == STDOUT_FILENO) { + if (file->_file == STDOUT_FILENO) + { return ets_putc(c); } return EOF; } -int ICACHE_RAM_ATTR puts(const char * str) { +int ICACHE_RAM_ATTR puts(const char * str) +{ char c; - while((c = *str) != 0) { + while ((c = *str) != 0) + { ets_putc(c); ++str; } @@ -115,17 +127,20 @@ int ICACHE_RAM_ATTR puts(const char * str) { } #undef putchar -int ICACHE_RAM_ATTR putchar(int c) { +int ICACHE_RAM_ATTR putchar(int c) +{ ets_putc(c); return c; } -void _exit(int status) { +void _exit(int status) +{ (void) status; abort(); } -int atexit(void (*func)()) { +int atexit(void (*func)()) +{ (void) func; return 0; } \ No newline at end of file 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,7 +45,7 @@ static void (*_settimeofday_cb)(void) = NULL; -void settimeofday_cb (void (*cb)(void)) +void settimeofday_cb(void (*cb)(void)) { _settimeofday_cb = cb; } @@ -71,7 +71,7 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz) // reset time subsystem timeshift64_is_set = false; - + return -1; } return 0; @@ -109,291 +109,319 @@ LOCAL os_timer_t sntp_timer; 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 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 +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 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 { - char ch; - int m; - int n; - int d; - int s; - time_t change; - int offset; + 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) +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) + 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; + rem += SECSPERDAY; + --days; } - while (rem >= SECSPERDAY) + while (rem >= SECSPERDAY) { - rem -= SECSPERDAY; - ++days; + 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 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 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) + /* 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]; - } + for (;;) + { + yleap = isleap(y); + if (days < year_lengths[yleap]) + { + break; + } + y++; + days -= year_lengths[yleap]; + } } - else + else { - do - { - --y; - yleap = isleap(y); - days += year_lengths[yleap]; - } while (days < 0); + 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; + 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) + 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 { - 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; + res->tm_isdst = 0; } - 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); + // 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) +sntp_localtime_r(const time_t * tim_p, + struct tm *res) { - return sntp_mktm_r (tim_p, res, 0); + 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); + 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; + int days, year_days, years; + int i, j; - 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 (year < EPOCH_YEAR) { - 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; + return 0; + } - 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; + __tzyear = year; - while (m_day >= ip[j-1]) - m_day -= DAYSPERWEEK; + years = (year - EPOCH_YEAR); - days += m_day; - } + 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; - /* 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; + 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); + __tznorth = (sntp__tzrule[0].change < sntp__tzrule[1].change); - return 1; + return 1; } -char* sntp_asctime_r(struct tm *tim_p ,char *result) +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; + 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); + return sntp_asctime_r(tim_p, reult); } uint32 ICACHE_RAM_ATTR sntp_get_current_timestamp(void) @@ -403,7 +431,7 @@ uint32 ICACHE_RAM_ATTR sntp_get_current_timestamp(void) char* sntp_get_real_time(time_t t) { - return sntp_asctime(sntp_localtime (&t)); + return sntp_asctime(sntp_localtime(&t)); } sint8 sntp_get_timezone(void) @@ -413,10 +441,13 @@ sint8 sntp_get_timezone(void) bool sntp_set_timezone(sint8 timezone) { - if(timezone >= -11 || timezone <= 13) { + if (timezone >= -11 || timezone <= 13) + { time_zone = timezone; return true; - } else { + } + else + { return false; } } @@ -426,12 +457,12 @@ void sntp_set_daylight(int daylight) dst = daylight; } -void ICACHE_RAM_ATTR sntp_time_inc (void) +void ICACHE_RAM_ATTR sntp_time_inc(void) { realtime_stamp++; } -static void sntp_set_system_time (uint32_t t) +static void sntp_set_system_time(uint32_t t) { realtime_stamp = t + time_zone * 60 * 60 + dst; os_timer_disarm(&sntp_timer); @@ -455,7 +486,9 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz) sntp_set_system_time(tv->tv_sec); if (_settimeofday_cb) + { _settimeofday_cb(); + } } return 0; } 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.c b/cores/esp8266/spiffs/spiffs_cache.c index e7cd4b7376..d09875c182 100644 --- a/cores/esp8266/spiffs/spiffs_cache.c +++ b/cores/esp8266/spiffs/spiffs_cache.c @@ -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" @@ -11,110 +11,132 @@ #if SPIFFS_CACHE // 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; +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; + //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); - } +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); + { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); + } + cache->cpage_use_map &= ~(1 << ix); + cp->flags = 0; } - cache->cpage_use_map &= ~(1 << ix); - cp->flags = 0; - } - return res; + 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; +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; } - } - if (cand_ix >= 0) { - res = spiffs_cache_page_free(fs, cand_ix, 1); - } + // 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; + 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; +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; + // 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); - } +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); + } } // ------------------------------ @@ -126,58 +148,68 @@ s32_t spiffs_phys_rd( 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 + 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 @@ -187,133 +219,162 @@ s32_t spiffs_phys_wr( 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 *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; + } } - - 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); +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; + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) + { + // all cpages free, no cpage cannot be assigned to obj_id + return 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) && + cp->obj_id == fd->obj_id) + { + return cp; + } + } + + 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; - } +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; + 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; +void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) +{ + if (cp == 0) + { + return; } - } - spiffs_cache_page_free(fs, cp->ix, 0); + 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; + 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; - } +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.c b/cores/esp8266/spiffs/spiffs_check.c index dde85eff3c..b295caf5b8 100644 --- a/cores/esp8266/spiffs/spiffs_check.c +++ b/cores/esp8266/spiffs/spiffs_check.c @@ -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" @@ -27,14 +27,14 @@ #if SPIFFS_HAL_CALLBACK_EXTRA #define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ - do { \ - if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \ - } while (0) + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \ + } while (0) #else #define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ - do { \ - if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \ - } while (0) + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \ + } while (0) #endif //--------------------------------------- @@ -44,453 +44,535 @@ // 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; + 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; +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; +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; +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 { - 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); + 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 { - // 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 + { + // 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); + } } - } 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); + 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 { - // 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; + 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 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; + // 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); - 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"); + 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 (((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); - 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); + 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 (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; + 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; + 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; +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); + 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); + 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_VIS_END) + { + res = SPIFFS_OK; + } - if (res != SPIFFS_OK) { - CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); - } + 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); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); - return res; + return res; } //--------------------------------------- @@ -506,356 +588,414 @@ s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { // * 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); +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; + 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; + 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)); } - fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1)); - } + 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++; } - } // 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 - } + // 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 + } } - } - } 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; + 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; } // 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; +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; } //--------------------------------------- @@ -863,133 +1003,159 @@ s32_t spiffs_page_consistency_check(spiffs *fs) { // 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; +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; + 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; - } + 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); - 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; - } + // 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) { - // 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); + 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; } - (*log_ix)++; - if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { - *log_ix = 0; + + 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; } - } else { - // in temporary index, check reachable flag - if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) { - // registered as unreachable - delete = 1; + + 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 + 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; + 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; +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; } #endif // !SPIFFS_READ_ONLY 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.c b/cores/esp8266/spiffs/spiffs_gc.c index db1af4ccf6..d24366946c 100644 --- a/cores/esp8266/spiffs/spiffs_gc.c +++ b/cores/esp8266/spiffs/spiffs_gc.c @@ -8,228 +8,263 @@ // is dropped. static s32_t spiffs_gc_erase_block( spiffs *fs, - spiffs_block_ix bix) { - s32_t res; + 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); + 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; + 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"); + 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 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; + } - 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; + 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++; - } // 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 + 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; + 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; + 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))); + } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || + (s32_t)len > free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); - free_pages = + 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; - } + 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); + 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; + 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; + 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 @@ -237,128 +272,151 @@ 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; + 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; + } - int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + // 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); - // check each block - while (res == SPIFFS_OK && blocks--) { - u16_t deleted_pages_in_block = 0; - u16_t used_pages_in_block = 0; + 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); + } - 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; + 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)++; } - cand_ix++; - } - (*candidate_count)++; - } - cur_entry = 0; - cur_block++; - cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); - } // per block + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block - return res; + return res; } -typedef enum { - FIND_OBJ_DATA, - MOVE_OBJ_DATA, - MOVE_OBJ_IX, - FINISHED +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; +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 @@ -374,233 +432,267 @@ typedef struct { // 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) { +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 + 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: - // 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); + 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); - // 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); + 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); - } 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_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); - 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)); - } + // 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; } - } - 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); - } + 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); } - 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; } #endif // !SPIFFS_READ_ONLY diff --git a/cores/esp8266/spiffs/spiffs_hydrogen.c b/cores/esp8266/spiffs/spiffs_hydrogen.c index 18c8bb4148..111a0a2f41 100644 --- a/cores/esp8266/spiffs/spiffs_hydrogen.c +++ b/cores/esp8266/spiffs/spiffs_hydrogen.c @@ -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" @@ -13,1009 +13,1132 @@ 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_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_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)); - } + 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); + spiffs_fd_return(fs, cur_fd->file_nbr); + } } - } - fs->mounted = 0; + 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); + 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); } - 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 (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 ((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_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 } - 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); + else + { + if (res < SPIFFS_OK) + { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } - 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); + 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); - } - 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); + 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); } - 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); -#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); + 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); + } #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); + 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); + 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); } - 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); - - spiffs_fd *fd; - s32_t res; +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); - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + spiffs_fd *fd; + s32_t res; - if ((fd->flags & SPIFFS_O_RDONLY) == 0) { - res = SPIFFS_ERR_NOT_READABLE; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); 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->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 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_fd *fd; - s32_t res; - u32_t offset; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + spiffs_fd *fd; + s32_t res; + u32_t offset; - if ((fd->flags & SPIFFS_O_WRONLY) == 0) { - res = SPIFFS_ERR_NOT_WRITABLE; + 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_APPEND)) { - fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; - } - offset = fd->fdoffset; + if ((fd->flags & SPIFFS_O_WRONLY) == 0) + { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } -#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); - } -#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)) + { + fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; } + offset = fd->fdoffset; + #if SPIFFS_CACHE_WR - if (fd->cache_page) { - offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size); + 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 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 + if (fd->flags & SPIFFS_O_APPEND) + { + if (fd->size == SPIFFS_UNDEFINED_LEN) { - // 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; + offset = 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); + else + { + offset = fd->size; } - } - - 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 SPIFFS_CACHE_WR + if (fd->cache_page) { - 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(); - } + offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size); } #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 - } } - } + +#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 + { + 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 - res = spiffs_hydro_write(fs, fd, buf, offset, len); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - fd->fdoffset += len; + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(fs); - return res; + return res; #endif // SPIFFS_READ_ONLY } -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); +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); #if SPIFFS_CACHE_WR - spiffs_fflush_cache(fs, fh); + spiffs_fflush_cache(fs, fh); #endif - 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); + 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); - fd->cursor_objix_spix = objix_spix; - fd->cursor_objix_pix = pix; - } - fd->fdoffset = offs; - SPIFFS_UNLOCK(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_UNLOCK(fs); - return offs; + 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); + 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); - 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); + spiffs_fd *fd; + spiffs_page_ix pix; + s32_t 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); + 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_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); - - if ((fd->flags & SPIFFS_O_WRONLY) == 0) { - res = SPIFFS_ERR_NOT_WRITABLE; + 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 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. #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); - - 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); - } - } + 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); + } + } #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); + 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_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path, - 0, 0, &pix_dummy); + 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); - - s32_t res; - spiffs_fd *fd; - spiffs_page_ix pix_dummy; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); - fh = SPIFFS_FH_UNOFFS(fs, fh); - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + s32_t res; + spiffs_fd *fd; + spiffs_page_ix pix_dummy; - if ((fd->flags & SPIFFS_O_WRONLY) == 0) { - res = SPIFFS_ERR_NOT_WRITABLE; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); 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); + 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); - 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; +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_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; - } + 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; + d->fs = fs; + d->block = 0; + d->entry = 0; + return d; } static s32_t spiffs_read_dir_v( @@ -1024,429 +1147,484 @@ static s32_t spiffs_read_dir_v( 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; + 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; + 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; +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_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__); +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_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_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; + 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); +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); + 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 (fd->ix_map == 0) { - SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); - } + if (fd->ix_map == 0) + { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } - fd->ix_map = 0; + fd->ix_map = 0; - SPIFFS_UNLOCK(fs); - return res; + 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; +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; } -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)); +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); } - cur_entry++; - if ((cur_entry & 0x3f) == 0) { - spiffs_printf("\n"); + else + { + spiffs_printf("\tera_cnt: N/A\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.c b/cores/esp8266/spiffs/spiffs_nucleus.c index 27ecdff2a6..a75742a3e1 100644 --- a/cores/esp8266/spiffs/spiffs_nucleus.c +++ b/cores/esp8266/spiffs/spiffs_nucleus.c @@ -1,61 +1,69 @@ #include "spiffs.h" #include "spiffs_nucleus.h" -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 @@ -65,16 +73,18 @@ s32_t spiffs_phys_rd( spiffs *fs, u32_t addr, u32_t len, - u8_t *dst) { - return SPIFFS_HAL_READ(fs, addr, len, dst); + 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); + u8_t *src) +{ + return SPIFFS_HAL_WRITE(fs, addr, len, src); } #endif @@ -85,21 +95,23 @@ s32_t spiffs_phys_cpy( 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; + 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 @@ -131,188 +143,226 @@ s32_t spiffs_obj_lu_find_entry_visitor( 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) { - 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; + 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; } - } 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 - - SPIFFS_CHECK_RES(res); - - return SPIFFS_VIS_END; + } + + // 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) + { + 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; + } + } + } // per block + + SPIFFS_CHECK_RES(res); + + 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); + 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); + 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 @@ -323,22 +373,29 @@ static s32_t spiffs_obj_lu_scan_v( 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++; - } + 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; + return SPIFFS_VIS_COUNTINUE; } @@ -346,101 +403,115 @@ static s32_t spiffs_obj_lu_scan_v( // 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; + 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_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_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); + 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++; } - 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; - - 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; - } + // 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; + } - SPIFFS_CHECK_RES(res); + SPIFFS_CHECK_RES(res); - return res; + return res; } #if !SPIFFS_READ_ONLY @@ -451,32 +522,39 @@ s32_t spiffs_obj_lu_find_free( 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; + 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; + } } - 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; + 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; } #endif // !SPIFFS_READ_ONLY @@ -488,13 +566,15 @@ s32_t spiffs_obj_lu_find_id( 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; + 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; } @@ -504,22 +584,26 @@ static s32_t spiffs_obj_lu_find_id_and_span_v( 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; - } + 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 @@ -529,36 +613,39 @@ s32_t spiffs_obj_lu_find_id_and_span( 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; + 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 @@ -568,98 +655,109 @@ s32_t spiffs_obj_lu_find_id_and_span_by_phdr( 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; + 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; } #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) { + 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)]; - } - - 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++; - } + 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++; + } } -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; +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( @@ -668,79 +766,85 @@ static s32_t spiffs_populate_ix_map_v( 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 + 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), - SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix), - (u8_t *)objix + sizeof(spiffs_page_object_ix)); + 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); + 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); - } + 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; + 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; +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 @@ -758,51 +862,55 @@ s32_t spiffs_page_allocate_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_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); - } - // finalize header if necessary - if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) { - ph->flags &= ~SPIFFS_PH_FLAG_FINAL; + // 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) + offsetof(spiffs_page_header, flags), - sizeof(u8_t), - (u8_t *)&ph->flags); + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t*)ph); SPIFFS_CHECK_RES(res); - } - // return written page - if (pix) { - *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); - } + // 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); + } - return 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 @@ -816,58 +924,66 @@ s32_t spiffs_page_move( 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_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 source deleted - res = spiffs_page_delete(fs, src_pix); - return 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 @@ -875,30 +991,31 @@ s32_t spiffs_page_move( // 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; + 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 @@ -910,59 +1027,64 @@ s32_t spiffs_object_create( 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); + 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 @@ -980,56 +1102,68 @@ s32_t spiffs_object_update_index_hdr( 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_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); + 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); - } + // 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 + } } - // 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; + return res; } #endif // !SPIFFS_READ_ONLY @@ -1040,109 +1174,149 @@ void spiffs_cb_object_event( spiffs_obj_id obj_id_raw, spiffs_span_ix spix, spiffs_page_ix new_pix, - u32_t new_size) { + 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); - } + // 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 @@ -1151,16 +1325,17 @@ s32_t spiffs_object_open_by_id( spiffs_obj_id obj_id, spiffs_fd *fd, spiffs_flags flags, - spiffs_mode mode) { - s32_t res = SPIFFS_OK; - spiffs_page_ix pix; + 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_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); + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); - return res; + return res; } // Open object by page index @@ -1169,487 +1344,572 @@ s32_t spiffs_object_open_by_page( 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; + 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); + 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); + 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); + 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; + 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_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); + SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", SPIFFS_FH_OFFS(fs, fd->file_nbr), fd->obj_id); - return res; + 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); +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; + } + 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; } - 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); + + // 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); - } - 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; + + 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); } - 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; + + 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_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); +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); + 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; } - } - // 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); + // 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]; } - 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; + 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; + } - res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); - SPIFFS_CHECK_RES(res2); + // 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; + } + } - 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; + 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; - 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; + + // 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 @@ -1659,62 +1919,69 @@ static s32_t spiffs_object_find_object_index_header_by_name_v( 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) { + 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; + } + } + 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; - } - } - - 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; + 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 @@ -1722,239 +1989,292 @@ s32_t spiffs_object_find_object_index_header_by_name( 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; + 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; + } - 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); + } - // 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); - } + 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); + // 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); + // 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_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); + 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); - } - fd->size = cur_size; + 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; } - } - // 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; - } - - 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; + + 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 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) { + 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; + } + + 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 - ((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); + 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); - res = spiffs_page_delete(fs, objix_pix); + // 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 *)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)); + 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, - objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); + fd->objix_hdr_pix, 0, 0, 0, cur_size, &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; + } + fd->size = cur_size; - return res; + return res; } // spiffs_object_truncate #endif // !SPIFFS_READ_ONLY @@ -1962,398 +2282,483 @@ 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) { + 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); - } + // 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]; } - 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); + 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; + fd->offset = cur_offset; + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; - prev_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 (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; - } - 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++; - } + // 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; + 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; +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; + 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<> 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; } 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; + 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; + } - 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]++; - } - } - } - return SPIFFS_VIS_COUNTINUE; + 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]++; + } + } + } + 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; +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; } - } - } - - 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 + 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; } - 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; + 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 } - } - // 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; + 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; +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--; + 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; + } } - } - } - } - - // 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; + } + + 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; + } } - } 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; - } + 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; + 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; +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; - } - } + 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 dd1c414bc7..3fd2419f02 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_ @@ -144,15 +144,15 @@ #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 @@ -160,10 +160,10 @@ #if SPIFFS_USE_MAGIC #if !SPIFFS_USE_MAGIC_LENGTH #define SPIFFS_MAGIC(fs, bix) \ - ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) #else // SPIFFS_USE_MAGIC_LENGTH #define SPIFFS_MAGIC(fs, bix) \ - ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) #endif // SPIFFS_USE_MAGIC_LENGTH #endif // SPIFFS_USE_MAGIC @@ -171,92 +171,92 @@ #if SPIFFS_SINGLETON == 0 #define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ - ((fs)->cfg.log_page_size) + ((fs)->cfg.log_page_size) #define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \ - ((fs)->cfg.log_block_size) + ((fs)->cfg.log_block_size) #define SPIFFS_CFG_PHYS_SZ(fs) \ - ((fs)->cfg.phys_size) + ((fs)->cfg.phys_size) #define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \ - ((fs)->cfg.phys_erase_block) + ((fs)->cfg.phys_erase_block) #define SPIFFS_CFG_PHYS_ADDR(fs) \ - ((fs)->cfg.phys_addr) + ((fs)->cfg.phys_addr) #endif // total number of pages #define SPIFFS_MAX_PAGES(fs) \ - ( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) + ( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) // total number of pages per block, including object lookup pages #define SPIFFS_PAGES_PER_BLOCK(fs) \ - ( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) + ( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) // number of object lookup pages per block #define SPIFFS_OBJ_LOOKUP_PAGES(fs) \ - (MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) ) + (MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) ) // checks if page index belongs to object lookup #define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \ - (((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) + (((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) // number of object lookup entries in all object lookup pages #define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \ - (SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) + (SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) // converts a block to physical address #define SPIFFS_BLOCK_TO_PADDR(fs, block) \ - ( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) ) + ( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) ) // converts a object lookup entry to page index #define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \ - ((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry)) + ((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry)) // converts a object lookup entry to physical address of corresponding page #define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \ - (SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) + (SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) // converts a page to physical address #define SPIFFS_PAGE_TO_PADDR(fs, page) \ - ( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) + ( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) // converts a physical address to page #define SPIFFS_PADDR_TO_PAGE(fs, addr) \ - ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) ) + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) ) // gives index in page for a physical address #define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \ - ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) ) + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) ) // returns containing block for given page #define SPIFFS_BLOCK_FOR_PAGE(fs, page) \ - ( (page) / SPIFFS_PAGES_PER_BLOCK(fs) ) + ( (page) / SPIFFS_PAGES_PER_BLOCK(fs) ) // returns starting page for block #define SPIFFS_PAGE_FOR_BLOCK(fs, block) \ - ( (block) * SPIFFS_PAGES_PER_BLOCK(fs) ) + ( (block) * SPIFFS_PAGES_PER_BLOCK(fs) ) // converts page to entry in object lookup page #define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \ - ( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) ) + ( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) ) // returns data size in a data page #define SPIFFS_DATA_PAGE_SIZE(fs) \ ( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) // returns physical address for block's erase count, // always in the physical last entry of the last object lookup page #define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \ - ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) + ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) // returns physical address for block's magic, // always in the physical second last entry of the last object lookup page #define SPIFFS_MAGIC_PADDR(fs, bix) \ - ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 ) + ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 ) // checks if there is any room for magic in the object luts #define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \ - ( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \ - <= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) ) + ( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \ + <= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) ) // define helpers object // entries in an object header page index #define SPIFFS_OBJ_HDR_IX_LEN(fs) \ - ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) // entries in an object page index #define SPIFFS_OBJ_IX_LEN(fs) \ - ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) // object index entry for given data span index #define SPIFFS_OBJ_IX_ENTRY(fs, spix) \ - ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs))) + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs))) // object index span index number for given data span index or entry #define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \ - ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) // get data span index for object index span index #define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \ - ( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) ) + ( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) ) #if SPIFFS_FILEHDL_OFFSET #define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0) @@ -296,40 +296,40 @@ #define SPIFFS_CHECK_MOUNT(fs) \ - ((fs)->mounted != 0) + ((fs)->mounted != 0) #define SPIFFS_CHECK_CFG(fs) \ - ((fs)->config_magic == SPIFFS_CONFIG_MAGIC) + ((fs)->config_magic == SPIFFS_CONFIG_MAGIC) #define SPIFFS_CHECK_RES(res) \ - do { \ - if ((res) < SPIFFS_OK) return (res); \ - } while (0); + do { \ + if ((res) < SPIFFS_OK) return (res); \ + } while (0); #define SPIFFS_API_CHECK_MOUNT(fs) \ - if (!SPIFFS_CHECK_MOUNT((fs))) { \ - (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ - return SPIFFS_ERR_NOT_MOUNTED; \ - } + if (!SPIFFS_CHECK_MOUNT((fs))) { \ + (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ + return SPIFFS_ERR_NOT_MOUNTED; \ + } #define SPIFFS_API_CHECK_CFG(fs) \ - if (!SPIFFS_CHECK_CFG((fs))) { \ - (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ - return SPIFFS_ERR_NOT_CONFIGURED; \ - } + if (!SPIFFS_CHECK_CFG((fs))) { \ + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ + return SPIFFS_ERR_NOT_CONFIGURED; \ + } #define SPIFFS_API_CHECK_RES(fs, res) \ - if ((res) < SPIFFS_OK) { \ - (fs)->err_code = (res); \ - return (res); \ - } + if ((res) < SPIFFS_OK) { \ + (fs)->err_code = (res); \ + return (res); \ + } #define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ - if ((res) < SPIFFS_OK) { \ - (fs)->err_code = (res); \ - SPIFFS_UNLOCK(fs); \ - return (res); \ - } + if ((res) < SPIFFS_OK) { \ + (fs)->err_code = (res); \ + SPIFFS_UNLOCK(fs); \ + return (res); \ + } #define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ @@ -338,7 +338,7 @@ 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; \ @@ -359,20 +359,20 @@ #if SPIFFS_HAL_CALLBACK_EXTRA #define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ - (_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src)) + (_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src)) #define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ - (_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst)) + (_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst)) #define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ - (_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len)) + (_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len)) #else // SPIFFS_HAL_CALLBACK_EXTRA #define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ - (_fs)->cfg.hal_write_f((_paddr), (_len), (_src)) + (_fs)->cfg.hal_write_f((_paddr), (_len), (_src)) #define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ - (_fs)->cfg.hal_read_f((_paddr), (_len), (_dst)) + (_fs)->cfg.hal_read_f((_paddr), (_len), (_dst)) #define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ - (_fs)->cfg.hal_erase_f((_paddr), (_len)) + (_fs)->cfg.hal_erase_f((_paddr), (_len)) #endif // SPIFFS_HAL_CALLBACK_EXTRA @@ -386,91 +386,97 @@ #define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7) #define SPIFFS_CACHE_PAGE_SIZE(fs) \ - (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)) + (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)) #define spiffs_get_cache(fs) \ - ((spiffs_cache *)((fs)->cache)) + ((spiffs_cache *)((fs)->cache)) #define spiffs_get_cache_page_hdr(fs, c, ix) \ - ((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)]))) + ((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)]))) #define spiffs_get_cache_page(fs, c, ix) \ - ((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page)) + ((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; @@ -480,46 +486,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 @@ -823,16 +831,16 @@ s32_t spiffs_object_index_consistency_check( // checked in test builds, otherwise plain memcpy (unless already defined) #ifdef _SPIFFS_TEST #define _SPIFFS_MEMCPY(__d, __s, __l) do { \ - intptr_t __a1 = (intptr_t)((u8_t*)(__s)); \ - intptr_t __a2 = (intptr_t)((u8_t*)(__s)+(__l)); \ - intptr_t __b1 = (intptr_t)((u8_t*)(__d)); \ - intptr_t __b2 = (intptr_t)((u8_t*)(__d)+(__l)); \ - if (__a1 <= __b2 && __b1 <= __a2) { \ - printf("FATAL OVERLAP: memcpy from %lx..%lx to %lx..%lx\n", __a1, __a2, __b1, __b2); \ - ERREXIT(); \ - } \ - memcpy((__d),(__s),(__l)); \ -} while (0) + intptr_t __a1 = (intptr_t)((u8_t*)(__s)); \ + intptr_t __a2 = (intptr_t)((u8_t*)(__s)+(__l)); \ + intptr_t __b1 = (intptr_t)((u8_t*)(__d)); \ + intptr_t __b2 = (intptr_t)((u8_t*)(__d)+(__l)); \ + if (__a1 <= __b2 && __b1 <= __a2) { \ + printf("FATAL OVERLAP: memcpy from %lx..%lx to %lx..%lx\n", __a1, __a2, __b1, __b2); \ + ERREXIT(); \ + } \ + memcpy((__d),(__s),(__l)); \ + } while (0) #else #ifndef _SPIFFS_MEMCPY #define _SPIFFS_MEMCPY(__d, __s, __l) do{memcpy((__d),(__s),(__l));}while(0) 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 4a32b8ec09..a2fdc0e6aa 100644 --- a/cores/esp8266/spiffs_api.h +++ b/cores/esp8266/spiffs_api.h @@ -2,28 +2,28 @@ #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 @@ -62,10 +62,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)); } @@ -76,16 +76,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; @@ -101,7 +104,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; } @@ -112,12 +116,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; } @@ -128,20 +134,26 @@ class SPIFFSImpl : public FSImpl { #if defined(ARDUINO) && !defined(CORE_MOCK) if (&_SPIFFS_end <= &_SPIFFS_start) + { return false; + } #endif - 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; } 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; } @@ -150,7 +162,8 @@ class SPIFFSImpl : public FSImpl void end() override { - if (SPIFFS_mounted(&_fs) == 0) { + if (SPIFFS_mounted(&_fs) == 0) + { return; } SPIFFS_unmount(&_fs); @@ -161,23 +174,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(); } @@ -208,22 +225,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(); } @@ -238,7 +259,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); @@ -266,7 +288,7 @@ class SPIFFSImpl : public FSImpl (void) report; (void) arg1; (void) arg2; - + // TODO: spiffs doesn't pass any context pointer along with _check_cb, // so we can't do anything useful here other than perhaps // feeding the watchdog @@ -293,7 +315,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(); @@ -309,7 +331,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; } @@ -321,7 +344,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; } @@ -334,7 +358,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; @@ -345,11 +370,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; } @@ -362,7 +389,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; } @@ -373,7 +401,8 @@ class SPIFFSFileImpl : public FileImpl size_t size() const override { CHECKFD(); - if (_written) { + if (_written) + { _getStat(); } return _stat.size; @@ -399,7 +428,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)); } @@ -430,13 +460,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(); @@ -446,7 +478,8 @@ class SPIFFSDirImpl : public DirImpl const char* fileName() override { - if (!_valid) { + if (!_valid) + { return nullptr; } @@ -455,7 +488,8 @@ class SPIFFSDirImpl : public DirImpl size_t fileSize() override { - if (!_valid) { + if (!_valid) + { return 0; } @@ -465,10 +499,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.c b/cores/esp8266/sqrt32.c index af6ffa495f..2e9a7b6638 100644 --- a/cores/esp8266/sqrt32.c +++ b/cores/esp8266/sqrt32.c @@ -2,7 +2,7 @@ #include #include -uint32_t sqrt32 (uint32_t n) +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 @@ -13,27 +13,31 @@ uint32_t sqrt32 (uint32_t n) unsigned int c = 0x8000; unsigned int g = 0x8000; - for(;;) + for (;;) { - if (g*g > n) + if (g * g > n) + { g ^= c; + } c >>= 1; if (!c) + { return g; + } g |= c; } } /* - * tested with: - * + tested with: -#include -#include -#include -int main (void) -{ + #include + #include + #include + + int main (void) + { for (uint32_t i = 0; ++i; ) { uint32_t sr = sqrt32(i); @@ -50,7 +54,7 @@ int main (void) } 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.c b/cores/esp8266/time.c index 19b0dee522..2d16c05589 100644 --- a/cores/esp8266/time.c +++ b/cores/esp8266/time.c @@ -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 @@ -24,9 +24,10 @@ #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 @@ -40,10 +41,10 @@ extern uint64_t micros64(); bool timeshift64_is_set = false; static uint64_t timeshift64 = 0; -void tune_timeshift64 (uint64_t now_us) +void tune_timeshift64(uint64_t now_us) { - timeshift64 = now_us - micros64(); - timeshift64_is_set = true; + timeshift64 = now_us - micros64(); + timeshift64_is_set = true; } static void setServer(int id, const char* name_or_ip) @@ -63,7 +64,7 @@ void configTime(int timezone, int daylightOffset_sec, const char* server1, const setServer(1, server2); setServer(2, server3); - sntp_set_timezone(timezone/3600); + sntp_set_timezone(timezone / 3600); sntp_set_daylight(daylightOffset_sec); sntp_init(); } @@ -94,7 +95,9 @@ int _gettimeofday_r(struct _reent* unused, struct timeval *tp, 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; 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..37e428b0ca 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)) + _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.c b/cores/esp8266/uart.c index 88048cd363..4d2db04970 100644 --- a/cores/esp8266/uart.c +++ b/cores/esp8266/uart.c @@ -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 "uart.h" @@ -48,26 +48,26 @@ #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. */ static int s_uart_debug_nr = UART0; @@ -81,7 +81,7 @@ struct uart_rx_buffer_ uint8_t * buffer; }; -struct uart_ +struct uart_ { int uart_nr; int baud_rate; @@ -96,21 +96,21 @@ struct uart_ /* - 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 + 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. + 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) +uart_rx_fifo_available(const int uart_nr) { return (USS(uart_nr) >> USRXC) & 0xFF; } @@ -119,12 +119,14 @@ uart_rx_fifo_available(const int uart_nr) /**********************************************************/ /************ UNSAFE FUNCTIONS ****************************/ /**********************************************************/ -inline size_t -uart_rx_buffer_available_unsafe(const struct uart_rx_buffer_ * rx_buffer) +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; - + if (rx_buffer->wpos < rx_buffer->rpos) + { + return (rx_buffer->wpos + rx_buffer->size) - rx_buffer->rpos; + } + return rx_buffer->wpos - rx_buffer->rpos; } @@ -139,14 +141,14 @@ uart_rx_available_unsafe(uart_t* 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) +uart_rx_copy_fifo_to_buffer_unsafe(uart_t* uart) { struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer; - while(uart_rx_fifo_available(uart->uart_nr)) + while (uart_rx_fifo_available(uart->uart_nr)) { size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size; - if(nextPos == rx_buffer->rpos) + if (nextPos == rx_buffer->rpos) { if (!uart->rx_overrun) { @@ -164,7 +166,9 @@ uart_rx_copy_fifo_to_buffer_unsafe(uart_t* uart) #else // discard oldest data if (++rx_buffer->rpos == rx_buffer->size) + { rx_buffer->rpos = 0; + } #endif } uint8_t data = USF(uart->uart_nr); @@ -173,17 +177,21 @@ uart_rx_copy_fifo_to_buffer_unsafe(uart_t* uart) } } -inline int +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]; } @@ -202,12 +210,14 @@ uart_read_char_unsafe(uart_t* uart) return -1; } -size_t +size_t uart_rx_available(uart_t* uart) { - if(uart == NULL || !uart->rx_enabled) + 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(); @@ -215,31 +225,35 @@ uart_rx_available(uart_t* uart) return uartrxbufferavailable + uart_rx_fifo_available(uart->uart_nr); } -int +int uart_peek_char(uart_t* uart) { - if(uart == NULL || !uart->rx_enabled) + 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 +int uart_read_char(uart_t* uart) { uint8_t ret; - return uart_read(uart, (char*)&ret, 1)? ret: -1; + 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) + if (uart == NULL || !uart->rx_enabled) + { return 0; + } size_t ret = 0; ETS_UART_INTR_DISABLE(); @@ -250,19 +264,23 @@ uart_read(uart_t* uart, char* userbuffer, size_t usersize) { // 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 + // 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; + 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; @@ -279,16 +297,17 @@ uart_read(uart_t* uart, char* userbuffer, size_t usersize) 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) { + if (uart == NULL || !uart->rx_enabled) + { return; } -// Copy all the rx fifo bytes that fit into the rx buffer -// called by ISR + // 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) + if (nextPos == rx_buffer->rpos) { uart->rx_overrun = true; //os_printf_plus(overrun_str); @@ -302,7 +321,9 @@ static void ICACHE_RAM_ATTR uart_isr_handle_data(void* arg, uint8_t data) #else // discard oldest data if (++rx_buffer->rpos == rx_buffer->size) + { rx_buffer->rpos = 0; + } #endif } rx_buffer->buffer[rx_buffer->wpos] = data; @@ -311,35 +332,49 @@ static void ICACHE_RAM_ATTR uart_isr_handle_data(void* arg, uint8_t data) // Check the UART flags and note hardware overflow/etc. uint32_t usis = USIS(uart->uart_nr); - if(usis & (1 << UIOF)) + if (usis & (1 << UIOF)) + { uart->rx_overrun = true; + } if (usis & ((1 << UIFR) | (1 << UIPE) | (1 << UITO))) + { uart->rx_error = true; + } USIC(uart->uart_nr) = usis; } -size_t +size_t uart_resize_rx_buffer(uart_t* uart, size_t new_size) { - if(uart == NULL || !uart->rx_enabled) + if (uart == NULL || !uart->rx_enabled) + { return 0; + } - if(uart->rx_buffer->size == new_size) + if (uart->rx_buffer->size == new_size) + { return uart->rx_buffer->size; + } uint8_t * new_buf = (uint8_t*)malloc(new_size); - if(!new_buf) + 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 + 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; @@ -353,46 +388,53 @@ uart_resize_rx_buffer(uart_t* uart, size_t new_size) size_t uart_get_rx_buffer_size(uart_t* uart) { - return uart && uart->rx_enabled? uart->rx_buffer->size: 0; + return uart && uart->rx_enabled ? uart->rx_buffer->size : 0; } // The default ISR handler called when GDB is not enabled -void ICACHE_RAM_ATTR +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) + if (uart == NULL || !uart->rx_enabled) { USIC(uart->uart_nr) = usis; ETS_UART_INTR_DISABLE(); return; } - if(usis & (1 << UIFF)) + if (usis & (1 << UIFF)) + { uart_rx_copy_fifo_to_buffer_unsafe(uart); + } - if(usis & (1 << UIOF)) + 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 +static void uart_start_isr(uart_t* uart) { - if(uart == NULL || !uart->rx_enabled) + if (uart == NULL || !uart->rx_enabled) + { return; + } - if(gdbstub_has_uart_isr_control()) { - gdbstub_set_uart_isr_callback(uart_isr_handle_data, (void *)uart); + if (gdbstub_has_uart_isr_control()) + { + gdbstub_set_uart_isr_callback(uart_isr_handle_data, (void *)uart); return; } @@ -404,7 +446,7 @@ uart_start_isr(uart_t* uart) // - 4..120 give > 2300Kibits/s // - 1, 2, 3 are below // was 100, use 16 to stay away from overrun - #define INTRIGG 16 +#define INTRIGG 16 //was:USC1(uart->uart_nr) = (INTRIGG << UCFFT) | (0x02 << UCTOT) | (1 <uart_nr) = (INTRIGG << UCFFT); @@ -416,17 +458,20 @@ uart_start_isr(uart_t* uart) // 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_ATTACH(uart_isr, (void *)uart); ETS_UART_INTR_ENABLE(); } -static void +static void uart_stop_isr(uart_t* uart) { - if(uart == NULL || !uart->rx_enabled) + if (uart == NULL || !uart->rx_enabled) + { return; + } - if(gdbstub_has_uart_isr_control()) { + if (gdbstub_has_uart_isr_control()) + { gdbstub_set_uart_isr_callback(NULL, NULL); return; } @@ -439,11 +484,11 @@ uart_stop_isr(uart_t* uart) } /* - 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 - */ + 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) { @@ -457,21 +502,24 @@ uart_tx_fifo_full(const int uart_nr) } -static void +static void uart_do_write_char(const int uart_nr, char c) { - while(uart_tx_fifo_full(uart_nr)); + while (uart_tx_fifo_full(uart_nr)); USF(uart_nr) = c; } -size_t +size_t uart_write_char(uart_t* uart, char c) { - if(uart == NULL || !uart->tx_enabled) + if (uart == NULL || !uart->tx_enabled) + { return 0; + } - if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) { + if (gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) + { gdbstub_write_char(c); return 1; } @@ -479,13 +527,16 @@ uart_write_char(uart_t* uart, char c) return 1; } -size_t +size_t uart_write(uart_t* uart, const char* buf, size_t size) { - if(uart == NULL || !uart->tx_enabled) + if (uart == NULL || !uart->tx_enabled) + { return 0; + } - if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) { + if (gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) + { gdbstub_write(buf, size); return 0; } @@ -493,40 +544,50 @@ uart_write(uart_t* uart, const char* buf, size_t size) size_t ret = size; const int uart_nr = uart->uart_nr; while (size--) + { uart_do_write_char(uart_nr, *buf++); + } return ret; } -size_t +size_t uart_tx_free(uart_t* uart) { - if(uart == NULL || !uart->tx_enabled) + if (uart == NULL || !uart->tx_enabled) + { return 0; + } return UART_TX_FIFO_SIZE - uart_tx_fifo_available(uart->uart_nr); } -void +void uart_wait_tx_empty(uart_t* uart) { - if(uart == NULL || !uart->tx_enabled) + if (uart == NULL || !uart->tx_enabled) + { return; + } - while(uart_tx_fifo_available(uart->uart_nr) > 0) + while (uart_tx_fifo_available(uart->uart_nr) > 0) + { delay(0); + } } -void +void uart_flush(uart_t* uart) { - if(uart == NULL) + if (uart == NULL) + { return; + } uint32_t tmp = 0x00000000; - if(uart->rx_enabled) + if (uart->rx_enabled) { tmp |= (1 << UCRXRST); ETS_UART_INTR_DISABLE(); @@ -535,90 +596,100 @@ uart_flush(uart_t* uart) ETS_UART_INTR_ENABLE(); } - if(uart->tx_enabled) + if (uart->tx_enabled) + { tmp |= (1 << UCTXRST); + } - if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) { + if (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) + { USC0(uart->uart_nr) |= (tmp); USC0(uart->uart_nr) &= ~(tmp); } } -void +void uart_set_baudrate(uart_t* uart, int baud_rate) { - if(uart == NULL) + if (uart == NULL) + { return; + } uart->baud_rate = baud_rate; USD(uart->uart_nr) = (ESP8266_CLOCK / uart->baud_rate); } -int +int uart_get_baudrate(uart_t* uart) { - if(uart == NULL) + if (uart == NULL) + { return 0; + } return uart->baud_rate; } -uart_t* +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) + if (uart == NULL) + { return NULL; + } uart->uart_nr = uart_nr; uart->rx_overrun = false; uart->rx_error = false; - switch(uart->uart_nr) + switch (uart->uart_nr) { case UART0: ETS_UART_INTR_DISABLE(); - if(!gdbstub_has_uart_isr_control()) { + if (!gdbstub_has_uart_isr_control()) + { ETS_UART_INTR_ATTACH(NULL, 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) + uart->rx_pin = (uart->rx_enabled) ? 3 : 255; + if (uart->rx_enabled) { struct uart_rx_buffer_ * rx_buffer = (struct uart_rx_buffer_ *)malloc(sizeof(struct uart_rx_buffer_)); - if(rx_buffer == NULL) + if (rx_buffer == NULL) { - free(uart); - return 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) + if (rx_buffer->buffer == NULL) { - free(rx_buffer); - free(uart); - return NULL; + free(rx_buffer); + free(uart); + return NULL; } uart->rx_buffer = rx_buffer; pinMode(uart->rx_pin, SPECIAL); } - if(uart->tx_enabled) + if (uart->tx_enabled) { - if (tx_pin == 2) + if (tx_pin == 2) { uart->tx_pin = 2; pinMode(uart->tx_pin, FUNCTION_4); - } - else + } + else { uart->tx_pin = 1; pinMode(uart->tx_pin, FUNCTION_0); } - } - else + } + else { uart->tx_pin = 255; } @@ -630,9 +701,11 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx 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) + 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; @@ -646,17 +719,21 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx uart_set_baudrate(uart, baudrate); USC0(uart->uart_nr) = config; - if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) { + 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) { + if (uart->uart_nr == UART0) + { + if (uart->rx_enabled) + { uart_start_isr(uart); } - if(gdbstub_has_uart_isr_control()) { + if (gdbstub_has_uart_isr_control()) + { ETS_UART_INTR_ENABLE(); // Undo the disable in the switch() above } } @@ -664,16 +741,19 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx return uart; } -void +void uart_uninit(uart_t* uart) { - if(uart == NULL) + if (uart == NULL) + { return; + } uart_stop_isr(uart); - if(uart->tx_enabled && (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0)) { - switch(uart->tx_pin) + if (uart->tx_enabled && (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0)) + { + switch (uart->tx_pin) { case 1: pinMode(1, INPUT); @@ -687,11 +767,13 @@ uart_uninit(uart_t* uart) } } - if(uart->rx_enabled) { + if (uart->rx_enabled) + { free(uart->rx_buffer->buffer); free(uart->rx_buffer); - if(!gdbstub_has_uart_isr_control()) { - switch(uart->rx_pin) + if (!gdbstub_has_uart_isr_control()) + { + switch (uart->rx_pin) { case 3: pinMode(3, INPUT); @@ -705,52 +787,62 @@ uart_uninit(uart_t* uart) free(uart); } -void +void uart_swap(uart_t* uart, int tx_pin) { - if(uart == NULL) + if (uart == NULL) + { return; + } - switch(uart->uart_nr) + 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_pin == 1 || uart->tx_pin == 2) && uart->tx_enabled) || (uart->rx_pin == 3 && uart->rx_enabled)) { - if(uart->tx_enabled) //TX + if (uart->tx_enabled) //TX { pinMode(uart->tx_pin, INPUT); uart->tx_pin = 15; } - if(uart->rx_enabled) //RX + if (uart->rx_enabled) //RX { pinMode(uart->rx_pin, INPUT); uart->rx_pin = 13; } - if(uart->tx_enabled) + if (uart->tx_enabled) + { pinMode(uart->tx_pin, FUNCTION_4); //TX + } - if(uart->rx_enabled) + if (uart->rx_enabled) + { pinMode(uart->rx_pin, FUNCTION_4); //RX - + } + IOSWAP |= (1 << IOSWAPU0); - } - else + } + else { - if(uart->tx_enabled) //TX + if (uart->tx_enabled) //TX { pinMode(uart->tx_pin, INPUT); - uart->tx_pin = (tx_pin == 2)?2:1; + uart->tx_pin = (tx_pin == 2) ? 2 : 1; } - if(uart->rx_enabled) //RX + 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->tx_enabled) + { + pinMode(uart->tx_pin, (tx_pin == 2) ? FUNCTION_4 : SPECIAL); //TX + } - if(uart->rx_enabled) + if (uart->rx_enabled) + { pinMode(3, SPECIAL); //RX + } IOSWAP &= ~(1 << IOSWAPU0); } @@ -763,24 +855,26 @@ uart_swap(uart_t* uart, int tx_pin) } } -void +void uart_set_tx(uart_t* uart, int tx_pin) { - if(uart == NULL) + if (uart == NULL) + { return; + } - switch(uart->uart_nr) + switch (uart->uart_nr) { case UART0: - if(uart->tx_enabled) + if (uart->tx_enabled) { - if (uart->tx_pin == 1 && tx_pin == 2) + 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) + } + else if (uart->tx_pin == 2 && tx_pin != 2) { pinMode(uart->tx_pin, INPUT); uart->tx_pin = 1; @@ -797,57 +891,71 @@ uart_set_tx(uart_t* uart, int tx_pin) } } -void +void uart_set_pins(uart_t* uart, int tx, int rx) { - if(uart == NULL) + if (uart == NULL) + { return; + } - if(uart->uart_nr == UART0) // Only UART0 allows pin changes + if (uart->uart_nr == UART0) // Only UART0 allows pin changes { - if(uart->tx_enabled && uart->tx_pin != tx) + if (uart->tx_enabled && uart->tx_pin != tx) { - if( rx == 13 && tx == 15) + if (rx == 13 && tx == 15) { uart_swap(uart, 15); - } - else if (rx == 3 && (tx == 1 || tx == 2)) + } + 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 && uart->rx_pin != rx && rx == 13 && tx == 15) + if (uart->rx_enabled && uart->rx_pin != rx && rx == 13 && tx == 15) + { uart_swap(uart, 15); + } } } -bool +bool uart_tx_enabled(uart_t* uart) { - if(uart == NULL) + if (uart == NULL) + { return false; + } return uart->tx_enabled; } -bool +bool uart_rx_enabled(uart_t* uart) { - if(uart == NULL) + if (uart == NULL) + { return false; + } return uart->rx_enabled; } -bool -uart_has_overrun (uart_t* uart) +bool +uart_has_overrun(uart_t* uart) { if (uart == NULL || !uart->rx_overrun) + { return false; + } // clear flag uart->rx_overrun = false; @@ -855,17 +963,19 @@ uart_has_overrun (uart_t* uart) } bool -uart_has_rx_error (uart_t* uart) +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 +static void uart_ignore_char(char c) { (void) c; @@ -874,31 +984,33 @@ uart_ignore_char(char c) inline void uart_write_char_delay(const int uart_nr, char c) { - while(uart_tx_fifo_full(uart_nr)) + while (uart_tx_fifo_full(uart_nr)) + { delay(0); + } USF(uart_nr) = c; } -static void +static void uart0_write_char(char c) { uart_write_char_delay(0, c); } -static void +static void uart1_write_char(char c) { uart_write_char_delay(1, c); } -void +void uart_set_debug(int uart_nr) { s_uart_debug_nr = uart_nr; void (*func)(char) = NULL; - switch(s_uart_debug_nr) + switch (s_uart_debug_nr) { case UART0: func = &uart0_write_char; @@ -911,26 +1023,32 @@ uart_set_debug(int uart_nr) func = &uart_ignore_char; break; } - if(gdbstub_has_putc1_control()) { + if (gdbstub_has_putc1_control()) + { gdbstub_set_putc1_callback(func); - } else { - if (uart_nr == UART0 || uart_nr == UART1) { + } + else + { + if (uart_nr == UART0 || uart_nr == UART1) + { system_set_os_print(1); - } else { + } + else + { system_set_os_print(0); } ets_install_putc1((void *) func); } } -int +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(). + 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) @@ -951,7 +1069,8 @@ uart_detect_baudrate(int uart_nr) } int32_t divisor = uart_baudrate_detect(uart_nr, 1); - if (!divisor) { + if (!divisor) + { return 0; } @@ -965,7 +1084,8 @@ uart_detect_baudrate(int uart_nr) { if (baudrate <= default_rates[i]) { - if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) { + if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) + { i--; } break; 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.c b/cores/esp8266/umm_malloc/umm_malloc.c index 46b8578927..d171f84f7f 100644 --- a/cores/esp8266/umm_malloc/umm_malloc.c +++ b/cores/esp8266/umm_malloc/umm_malloc.c @@ -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 @@ -522,32 +522,32 @@ extern int umm_last_fail_alloc_size; /* -- dbglog {{{ */ -/* ---------------------------------------------------------------------------- - * A set of macros that cleans up code that needs to produce debug - * or log information. - * - * See copyright notice in LICENSE.TXT - * ---------------------------------------------------------------------------- - * - * There are macros to handle the following decreasing levels of detail: - * - * 6 = TRACE - * 5 = DEBUG - * 4 = CRITICAL - * 3 = ERROR - * 2 = WARNING - * 1 = INFO - * 0 = FORCE - The printf is always compiled in and is called only when - * the first parameter to the macro is non-0 - * - * ---------------------------------------------------------------------------- - * - * The following #define should be set up before this file is included so - * that we can be sure that the correct macros are defined. - * - * #define DBG_LOG_LEVEL x - * ---------------------------------------------------------------------------- - */ +/* ---------------------------------------------------------------------------- + A set of macros that cleans up code that needs to produce debug + or log information. + + See copyright notice in LICENSE.TXT + ---------------------------------------------------------------------------- + + There are macros to handle the following decreasing levels of detail: + + 6 = TRACE + 5 = DEBUG + 4 = CRITICAL + 3 = ERROR + 2 = WARNING + 1 = INFO + 0 = FORCE - The printf is always compiled in and is called only when + the first parameter to the macro is non-0 + + ---------------------------------------------------------------------------- + + The following #define should be set up before this file is included so + that we can be sure that the correct macros are defined. + + #define DBG_LOG_LEVEL x + ---------------------------------------------------------------------------- +*/ #undef DBG_LOG_TRACE #undef DBG_LOG_DEBUG @@ -601,20 +601,24 @@ extern int umm_last_fail_alloc_size; /* ------------------------------------------------------------------------- */ -UMM_H_ATTPACKPRE typedef struct umm_ptr_t { - unsigned short int next; - unsigned short int prev; +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_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) @@ -647,126 +651,137 @@ unsigned short int umm_numblocks = 0; /* 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; + 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(); } - /* make sure the free mark is appropriate, and unmark it */ - if ((UMM_NBLOCK(cur) & UMM_FREELIST_MASK) - != (UMM_PBLOCK(cur) & UMM_FREELIST_MASK)) + /* Iterate through all free blocks */ + prev = 0; + while (1) { - 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; + 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; } - /* 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; + /* 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; } - 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 /* }}} */ @@ -776,173 +791,197 @@ static int integrity_check(void) { #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 + \ - sizeof(UMM_POISONED_BLOCK_LEN_TYPE) \ - ) : 0 \ - ) + (s) ? \ + (UMM_POISON_SIZE_BEFORE + UMM_POISON_SIZE_AFTER + \ + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) \ + ) : 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++)); - } + 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); + 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; + 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"); - } + 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; + 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; + 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); } - - 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; + 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; + 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; - } + 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; - } - return ok; + 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( unsigned char *ptr, size_t size_w_poison ) { - 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 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(unsigned char *ptr, size_t size_w_poison) +{ + 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( unsigned char *ptr ) { - if (ptr != NULL) { - unsigned short int c; + 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(unsigned char *ptr) +{ + if (ptr != NULL) + { + unsigned short int c; - ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE); + 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); + /* 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)); - } + check_poison_block(&UMM_BLOCK(c)); + } - return ptr; + return ptr; } #define CHECK_POISON_ALL_BLOCKS() check_poison_all_blocks() @@ -951,8 +990,8 @@ static void *get_unpoisoned( unsigned char *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) @@ -960,819 +999,889 @@ static void *get_unpoisoned( unsigned char *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. - * ---------------------------------------------------------------------------- - */ +/* ---------------------------------------------------------------------------- + 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. -UMM_HEAP_INFO ummHeapInfo; + 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. -void ICACHE_FLASH_ATTR *umm_info( void *ptr, int force ) { + The umm_info function does all of that and makes the results available + in the ummHeapInfo structure. + ---------------------------------------------------------------------------- +*/ - unsigned short int blockNo = 0; +UMM_HEAP_INFO ummHeapInfo; - if (umm_heap == NULL) { - umm_init(); - } +void ICACHE_FLASH_ATTR *umm_info(void *ptr, int force) +{ - /* Protect the critical section... */ - UMM_CRITICAL_ENTRY(); + unsigned short int blockNo = 0; - /* - * Clear out all of the entries in the ummHeapInfo structure before doing - * any calculations.. - */ - memset( &ummHeapInfo, 0, sizeof( ummHeapInfo ) ); + if (umm_heap == NULL) + { + umm_init(); + } - DBG_LOG_FORCE( force, "\n\nDumping the umm_heap...\n" ); + /* Protect the critical section... */ + UMM_CRITICAL_ENTRY(); - 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) ); + /* + 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... - */ + /* + 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; + blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; - while( UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK ) { - size_t curBlocks = (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo; + 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; + } - ++ummHeapInfo.totalEntries; - ummHeapInfo.totalBlocks += curBlocks; + /* + Update the accounting totals with information from the last block, the + rest must be free! + */ - /* Is this a free block? */ + { + size_t curBlocks = UMM_NUMBLOCKS - blockNo; + ummHeapInfo.freeBlocks += curBlocks; + ummHeapInfo.totalBlocks += curBlocks; + + if (ummHeapInfo.maxFreeContiguousBlocks < curBlocks) + { + ummHeapInfo.maxFreeContiguousBlocks = curBlocks; + } + } - 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); + 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); - if (ummHeapInfo.maxFreeContiguousBlocks < curBlocks) { - ummHeapInfo.maxFreeContiguousBlocks = curBlocks; - } + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); - 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) ); + return (NULL); +} - /* Does this block address match the ptr we may be trying to free? */ +/* ------------------------------------------------------------------------ */ - if( ptr == &UMM_BLOCK(blockNo) ) { +static unsigned short int umm_blocks(size_t size) +{ - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); + /* + The calculation of the block size is not too difficult, but there are + a few little things that we need to be mindful of. - 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 ); - } + 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. + */ - blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; - } + if (size <= (sizeof(((umm_block *)0)->body))) + { + return (1); + } - /* - * Update the accounting totals with information from the last block, the - * rest must be free! - */ + /* + 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_t curBlocks = UMM_NUMBLOCKS-blockNo; - ummHeapInfo.freeBlocks += curBlocks; - ummHeapInfo.totalBlocks += curBlocks; + size -= (1 + (sizeof(((umm_block *)0)->body))); - 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 ); - - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); - - return( NULL ); + return (2 + size / (sizeof(umm_block))); } /* ------------------------------------------------------------------------ */ -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. - */ +/* + Split the block `c` into two blocks: `c` and `c + blocks`. - if( size <= (sizeof(((umm_block *)0)->body)) ) - return( 1 ); + - `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. - /* - * 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. - */ + 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) +{ - size -= ( 1 + (sizeof(((umm_block *)0)->body)) ); + UMM_NBLOCK(c + blocks) = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) | new_freemask; + UMM_PBLOCK(c + blocks) = c; - return( 2 + size/(sizeof(umm_block)) ); + UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) = (c + blocks); + UMM_NBLOCK(c) = (c + blocks) | cur_freemask; } /* ------------------------------------------------------------------------ */ -/* - * 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; -} +static void umm_disconnect_from_free_list(unsigned short int c) +{ + /* Disconnect this block from the FREE list */ -/* ------------------------------------------------------------------------ */ + UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); + UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); -static void umm_disconnect_from_free_list( unsigned short int c ) { - /* Disconnect this block from the FREE list */ + /* And clear the free block indicator */ - UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); - UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); - - /* And clear the free block indicator */ - - UMM_NBLOCK(c) &= (~UMM_FREELIST_MASK); + UMM_NBLOCK(c) &= (~UMM_FREELIST_MASK); } /* ------------------------------------------------------------------------ */ -static void umm_assimilate_up( unsigned short int c ) { +static void umm_assimilate_up(unsigned short int 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 - */ + 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 + */ - DBG_LOG_DEBUG( "Assimilate up to next block, which is FREE\n" ); + DBG_LOG_DEBUG("Assimilate up to next block, which is FREE\n"); - /* Disconnect the next block from the FREE list */ + /* Disconnect the next block from the FREE list */ - umm_disconnect_from_free_list( UMM_NBLOCK(c) ); + umm_disconnect_from_free_list(UMM_NBLOCK(c)); - /* Assimilate the next block with this one */ + /* Assimilate the next block with this one */ - UMM_PBLOCK(UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) = c; - UMM_NBLOCK(c) = UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK; - } + UMM_PBLOCK(UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) = c; + UMM_NBLOCK(c) = UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK; + } } /* ------------------------------------------------------------------------ */ -static unsigned short int umm_assimilate_down( unsigned short int c, unsigned short int freemask ) { +static unsigned short int umm_assimilate_down(unsigned short int c, unsigned short int freemask) +{ - UMM_NBLOCK(UMM_PBLOCK(c)) = UMM_NBLOCK(c) | freemask; - UMM_PBLOCK(UMM_NBLOCK(c)) = UMM_PBLOCK(c); + UMM_NBLOCK(UMM_PBLOCK(c)) = UMM_NBLOCK(c) | freemask; + UMM_PBLOCK(UMM_NBLOCK(c)) = UMM_PBLOCK(c); - return( UMM_PBLOCK(c) ); + return (UMM_PBLOCK(c)); } /* ------------------------------------------------------------------------- */ /* 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; - } +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; + } } /* ------------------------------------------------------------------------ */ -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); - } + /* + 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)) + { - UMM_NFREE(c) = 0; - UMM_NBLOCK(c) = 0; - } + 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. - * - * This part may be customized to be a best-fit, worst-fit, or first-fit - * algorithm - */ + /* + 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. - cf = UMM_NFREE(0); + This part may be customized to be a best-fit, worst-fit, or first-fit + algorithm + */ - bestBlock = UMM_NFREE(0); - bestSize = 0x7FFF; + cf = UMM_NFREE(0); - while( cf ) { - blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf; + bestBlock = UMM_NFREE(0); + bestSize = 0x7FFF; - DBG_LOG_TRACE( "Looking at block %6d size %6d\n", cf, blockSize ); + while (cf) + { + blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf; + + 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 (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); + 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 */ + else + { + /* Out of memory */ - DBG_LOG_DEBUG( "Can't allocate %5d blocks\n", blocks ); + DBG_LOG_DEBUG("Can't allocate %5d blocks\n", blocks); - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); - return( (void *)NULL ); - } + return ((void *)NULL); + } - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); - return( (void *)&UMM_DATA(cf) ); + return ((void *)&UMM_DATA(cf)); } /* ------------------------------------------------------------------------ */ -static void *_umm_realloc( void *ptr, size_t size ) { +static void *_umm_realloc(void *ptr, size_t size) +{ - unsigned short int blocks; - unsigned short int blockSize; + unsigned short int blocks; + unsigned short int blockSize; - unsigned short int c; + unsigned short int c; - size_t curSize; + size_t curSize; - if (umm_heap == NULL) { - umm_init(); - } + if (umm_heap == NULL) + { + umm_init(); + } - /* - * 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. - */ + /* + 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. + */ + + if (((void *)NULL == ptr)) + { + DBG_LOG_DEBUG("realloc the NULL pointer - call malloc()\n"); - if( ((void *)NULL == ptr) ) { - DBG_LOG_DEBUG( "realloc the NULL pointer - call malloc()\n" ); + return (_umm_malloc(size)); + } - return( _umm_malloc(size) ); - } + /* + 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. + */ - /* - * 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 (0 == size) + { + DBG_LOG_DEBUG("realloc to 0 size, just free the block\n"); - if( 0 == size ) { - DBG_LOG_DEBUG( "realloc to 0 size, just free the block\n" ); + _umm_free(ptr); - _umm_free( ptr ); + return ((void *)NULL); + } - return( (void *)NULL ); - } + /* Protect the critical section... */ + UMM_CRITICAL_ENTRY(); - /* Protect the critical section... */ - UMM_CRITICAL_ENTRY(); + /* + 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. - /* - * 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. - */ + 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. + */ - blocks = umm_blocks( size ); + blocks = umm_blocks(size); - /* 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); - /* Figure out how big this block is... */ + /* Figure out how big this block is... */ - blockSize = (UMM_NBLOCK(c) - c); + blockSize = (UMM_NBLOCK(c) - c); - /* Figure out how many bytes are in this block */ + /* Figure out how many bytes are in this block */ - curSize = (blockSize*sizeof(umm_block))-(sizeof(((umm_block *)0)->header)); + curSize = (blockSize * sizeof(umm_block)) - (sizeof(((umm_block *)0)->header)); - /* - * 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... - */ + /* + 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... + */ - if( blockSize == blocks ) { - /* This space intentionally left blank - return the original pointer! */ + if (blockSize == blocks) + { + /* This space intentionally left blank - return the original pointer! */ - DBG_LOG_DEBUG( "realloc the same size block - %d, do nothing\n", blocks ); + DBG_LOG_DEBUG("realloc the same size block - %d, do nothing\n", blocks); - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); - return( ptr ); - } + return (ptr); + } - /* - * 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 :-) - */ + /* + 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... - umm_assimilate_up( c ); + If it's still too small, we have to free it anyways and it will save the + assimilation step later in free :-) + */ - /* - * 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. - */ + umm_assimilate_up(c); - if( (UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK) && - (blocks <= (UMM_NBLOCK(c)-UMM_PBLOCK(c))) ) { + /* + 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. + */ + + if ((UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK) && + (blocks <= (UMM_NBLOCK(c) - UMM_PBLOCK(c)))) + { - /* Check if the resulting block would be big enough... */ + /* Check if the resulting block would be big enough... */ - DBG_LOG_DEBUG( "realloc() could assimilate down %d blocks - fits!\n\r", c-UMM_PBLOCK(c) ); + DBG_LOG_DEBUG("realloc() could assimilate down %d blocks - fits!\n\r", c - UMM_PBLOCK(c)); - /* Disconnect the previous block from the FREE list */ + /* Disconnect the previous block from the FREE list */ - umm_disconnect_from_free_list( UMM_PBLOCK(c) ); + umm_disconnect_from_free_list(UMM_PBLOCK(c)); - /* - * Connect the previous block to the next block ... and then - * realign the current block pointer - */ + /* + Connect the previous block to the next block ... and then + realign the current block pointer + */ - c = umm_assimilate_down(c, 0); + c = umm_assimilate_down(c, 0); - /* - * Move the bytes down to the new block we just created, but be sure to move - * only the original bytes. - */ + /* + Move the bytes down to the new block we just created, but be sure to move + only the original bytes. + */ - memmove( (void *)&UMM_DATA(c), ptr, curSize ); + memmove((void *)&UMM_DATA(c), ptr, curSize); - /* And don't forget to adjust the pointer to the new block location! */ + /* And don't forget to adjust the pointer to the new block location! */ - ptr = (void *)&UMM_DATA(c); - } + ptr = (void *)&UMM_DATA(c); + } - /* Now calculate the block size again...and we'll have three cases */ + /* Now calculate the block size again...and we'll have three cases */ - blockSize = (UMM_NBLOCK(c) - c); + blockSize = (UMM_NBLOCK(c) - c); - if( blockSize == blocks ) { - /* This space intentionally left blank - return the original pointer! */ + if (blockSize == blocks) + { + /* This space intentionally left blank - return the original pointer! */ - DBG_LOG_DEBUG( "realloc the same size block - %d, do nothing\n", blocks ); + DBG_LOG_DEBUG("realloc the same size block - %d, do nothing\n", blocks); - } 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... - */ + } + 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... + */ - 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 smaller block %d, shrink and free the leftover bits\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... */ + umm_make_new_block(c, blocks, 0, 0); + _umm_free((void *)&UMM_DATA(c + blocks)); + } + else + { + /* New block is bigger than the old block... */ - void *oldptr = ptr; + void *oldptr = ptr; - DBG_LOG_DEBUG( "realloc %d to a bigger block %d, make new, copy, and free the old\n", blockSize, blocks ); + 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! - */ + /* + 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! + */ - if( (ptr = _umm_malloc( size )) ) { - memcpy( ptr, oldptr, curSize ); - _umm_free( oldptr ); - } + if ((ptr = _umm_malloc(size))) + { + memcpy(ptr, oldptr, curSize); + _umm_free(oldptr); + } - } + } - /* Release the critical section... */ - UMM_CRITICAL_EXIT(); + /* Release the critical section... */ + UMM_CRITICAL_EXIT(); - return( ptr ); + return (ptr); } /* ------------------------------------------------------------------------ */ -void *umm_malloc( size_t size ) { - void *ret; +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 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); + 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 = _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); + ret = GET_POISONED(ret, size); - return ret; + 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_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 e2125732be..80ebf62b20 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" @@ -29,344 +29,431 @@ 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; - } +void ArduinoOTAClass::begin(bool useMDNS) +{ + if (_initialized) + { + return; + } - if(_udp_ota){ - _udp_ota->unref(); - _udp_ota = 0; - } + _useMDNS = useMDNS; - _udp_ota = new UdpContext; - _udp_ota->ref(); + if (!_hostname.length()) + { + char tmp[15]; + sprintf(tmp, "esp8266-%06x", ESP.getChipId()); + _hostname = tmp; + } + if (!_port) + { + _port = 8266; + } - if(!_udp_ota->listen(IP_ADDR_ANY, _port)) - return; - _udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this)); + if (_udp_ota) + { + _udp_ota->unref(); + _udp_ota = 0; + } - if(_useMDNS) { - MDNS.begin(_hostname.c_str()); + _udp_ota = new UdpContext; + _udp_ota->ref(); - if (_password.length()) { - MDNS.enableArduino(_port, true); - } else { - MDNS.enableArduino(_port); + if (!_udp_ota->listen(IP_ADDR_ANY, _port)) + { + return; + } + _udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this)); + + if (_useMDNS) + { + MDNS.begin(_hostname.c_str()); + + if (_password.length()) + { + MDNS.enableArduino(_port, true); + } + else + { + MDNS.enableArduino(_port); + } } - } - _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(); + } + 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(); } - data[index] = _udp_ota->read(); - } - return 0; + 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(_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 + } } -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 693f0f5635..6ed8b51cc0 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; } } @@ -143,15 +145,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(); @@ -162,13 +166,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; } @@ -179,18 +185,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(); @@ -199,7 +206,7 @@ bool HTTPClient::begin(WiFiClient &client, String host, uint16_t port, String ur _client = &client; - clear(); + clear(); _host = host; _port = port; _uri = uri; @@ -211,17 +218,20 @@ 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(); } _port = 443; - 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,19 +242,22 @@ 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(); } _port = 443; - 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"); @@ -253,19 +266,21 @@ 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(); } _port = 80; - if (!beginInternal(url, "http")) { + if (!beginInternal(url, "http")) + { return false; } _transportTraits = TransportTraitsPtr(new TransportTraits()); @@ -280,7 +295,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; } @@ -294,7 +310,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 @ @@ -303,16 +320,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 (_protocol != expectedProtocol) { + if (_protocol != expectedProtocol) + { DEBUG_HTTPCLIENT("[HTTP-Client][begin] unexpected protocol: %s, expected %s\n", _protocol.c_str(), expectedProtocol); return false; } @@ -323,7 +344,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(); @@ -342,9 +364,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); } } @@ -352,7 +377,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(); @@ -363,7 +389,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)); @@ -373,7 +400,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(); @@ -386,7 +414,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"); @@ -395,9 +424,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(); @@ -405,78 +434,90 @@ void HTTPClient::end(void) } /** - * disconnect - * close the TCP socket - */ + disconnect + close the TCP socket +*/ void HTTPClient::disconnect() { - 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(); _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; @@ -485,52 +526,54 @@ 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); } } /** - * 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); @@ -542,70 +585,79 @@ 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) { // 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); } } @@ -615,30 +667,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); } @@ -647,36 +703,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; } @@ -688,11 +751,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 @@ -705,10 +770,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); @@ -717,34 +783,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] too less ram! need %d\n", HTTP_TCP_BUFFER_SIZE); return returnError(HTTPC_ERROR_TOO_LESS_RAM); } @@ -754,21 +829,22 @@ 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; } /** - * 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; } @@ -778,12 +854,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; } @@ -792,18 +869,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); } @@ -811,22 +890,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); } @@ -838,22 +924,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; @@ -862,13 +954,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); } @@ -877,20 +972,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; } @@ -901,13 +999,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: @@ -936,25 +1035,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); } @@ -962,9 +1064,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; } } @@ -974,19 +1079,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; } } @@ -995,7 +1104,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(); @@ -1003,7 +1113,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(); @@ -1016,8 +1127,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; } } @@ -1025,34 +1138,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; } @@ -1060,7 +1178,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; @@ -1075,21 +1194,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"; } @@ -1102,18 +1225,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; @@ -1128,13 +1256,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; } @@ -1144,9 +1273,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 @@ -1154,31 +1285,42 @@ 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; } - 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 @@ -1186,34 +1328,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); @@ -1224,11 +1380,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; @@ -1236,31 +1392,37 @@ 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) + { // read all data from server - while(connected() && (len > 0 || len == -1)) { + while (connected() && (len > 0 || len == -1)) + { // get available data size size_t sizeAvailable = _client->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; } @@ -1272,11 +1434,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 @@ -1292,7 +1456,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); @@ -1301,19 +1466,23 @@ 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 -= readBytes; } delay(0); - } else { + } + else + { delay(1); } } @@ -1322,12 +1491,15 @@ 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; } - } else { + } + else + { DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] too less ram! need %d\n", HTTP_TCP_BUFFER_SIZE); return HTTPC_ERROR_TOO_LESS_RAM; } @@ -1336,15 +1508,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 dad3267712..87a261f305 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); @@ -209,7 +211,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..43782c8d58 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 7f5e3ceef1..f8e257feb8 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,452 +59,540 @@ 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: uuid:%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: uuid:%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" - "uuid:%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" + "uuid:%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), - _respondToPort(0), - _pending(false), - _delay(0), - _process_time(0), - _notify_time(0) + _server(0), + _timer(0), + _port(80), + _ttl(SSDP_MULTICAST_TTL), + _respondToPort(0), + _pending(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; - if (strcmp(_uuid,"") == 0) { - uint32_t chipId = ESP.getChipId(); - sprintf(_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; + if (strcmp(_uuid, "") == 0) + { + uint32_t chipId = ESP.getChipId(); + sprintf(_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 ) { - DEBUGV("SSDP failed to join igmp group"); - return false; - } + if (igmp_joingroup(local, mcast) != ERR_OK) + { + DEBUGV("SSDP failed to join igmp group"); + 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", - _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", + _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 + } + // if the search type matches our type, we should respond instead of ABORT + if (strcasecmp(buffer, _deviceType) == 0) + { + _pending = 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 the search type matches our type, we should respond instead of ABORT - if (strcasecmp(buffer, _deviceType) == 0) { - _pending = 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(); - _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(); + _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) { - strlcpy(_uuid, uuid, sizeof(_uuid)); +void SSDPClass::setUUID(const char *uuid) +{ + strlcpy(_uuid, uuid, sizeof(_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 e320f058aa..99c659142b 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 char *uuid); - - void setName(const String& name) { setName(name.c_str()); } + + /*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 char *uuid); + + 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 d04ddb2ba1..65e27d31c0 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,235 +477,307 @@ 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()); 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(int code) { - switch (code) { +const String ESP8266WebServer::_responseCodeToString(int code) +{ + switch (code) + { case 100: return F("Continue"); case 101: return F("Switching Protocols"); case 200: return F("OK"); @@ -666,5 +819,5 @@ const String ESP8266WebServer::_responseCodeToString(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 a4f3bf2840..3d6068c3e1 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,152 +51,174 @@ 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); - } + 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); + } 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); - static const String _responseCodeToString(int code); - 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); + static const String _responseCodeToString(int code); + 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 d0cc2066d9..3de955811a 100644 --- a/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp @@ -1,194 +1,221 @@ /* - 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" #include -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; - br_sha256_init(sha256); - br_x509_decoder_init(ctx, dn_append, sha256, nullptr, nullptr); - br_x509_decoder_push(ctx, (const void*)raw, length); + // 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; + 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; - - _index = index; - _data = data; +int CertStore::initCertStore(CertStoreFile *index, CertStoreFile *data) +{ + int count = 0; + uint32_t offset = 0; - if (!_index || !data) { - return 0; - } + _index = index; + _data = data; - if (!_index->open(true)) { - return 0; - } - - if (!_data->open(false)) { - _index->close(); - return 0; - } - - 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; } - } - _data->close(); - _index->close(); - return count; + 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; } -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; - } - - if (!cs->_index->open(false)) { - 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; - 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); + } - 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); + + 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 3bd1472069..827777ec1c 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 dd448f7a46..da7d36e378 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 - * @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 = 1; - 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 + @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 = 1; + 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 e1a3583ff8..5e31e1fccd 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -1,608 +1,688 @@ -/* - 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 - It should be called following the order: - wifi_set_sleep_level(MAX_SLEEP_T) - wifi_set_listen_interval - wifi_set_sleep_type - 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/) - */ - -#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; - } - } - } - 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 () { - return wifi_get_listen_interval(); -} - -/** - * Get sleep level of modem sleep and light sleep - * @return true if max level - */ -bool ESP8266WiFiGenericClass::isSleepLevelMax () { - return wifi_get_sleep_level() == MAX_SLEEP_T; -} - - -// ----------------------------------------------------------------------------------------------------------------------- -// ------------------------------------------------ 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 + It should be called following the order: + wifi_set_sleep_level(MAX_SLEEP_T) + wifi_set_listen_interval + wifi_set_sleep_type + 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/) + */ + +#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; + } + } + } + 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() +{ + return wifi_get_listen_interval(); +} + +/** + Get sleep level of modem sleep and light sleep + @return true if max level +*/ +bool ESP8266WiFiGenericClass::isSleepLevelMax() +{ + return wifi_get_sleep_level() == MAX_SLEEP_T; +} + + +// ----------------------------------------------------------------------------------------------------------------------- +// ------------------------------------------------ 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 af3fce3e16..d16fc542d8 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( (PGM_P)PSTR(fmt), ##__VA_ARGS__ ) -#endif -#endif - -#ifndef DEBUG_WIFI_GENERIC -#define DEBUG_WIFI_GENERIC(...) -#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( (PGM_P)PSTR(fmt), ##__VA_ARGS__ ) +#endif +#endif + +#ifndef DEBUG_WIFI_GENERIC +#define DEBUG_WIFI_GENERIC(...) +#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 5c616da269..4c2b73ecf1 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( (PGM_P)PSTR(fmt), ##__VA_ARGS__ ) -#endif -#endif - -#ifndef DEBUG_WIFI_MULTI -#define DEBUG_WIFI_MULTI(...) -#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( (PGM_P)PSTR(fmt), ##__VA_ARGS__ ) +#endif +#endif + +#ifndef DEBUG_WIFI_MULTI +#define DEBUG_WIFI_MULTI(...) +#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 2c362a06b9..0d16a9bdd5 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp @@ -1,713 +1,816 @@ -/* - 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) { - 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; - } - } - - 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; - conf.open_and_wep_mode_disable = !(_useInsecureWEP || *conf.password == 0); - - 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 - */ -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); - } - - 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) +{ + 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; + } + } + + 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; + conf.open_and_wep_mode_disable = !(_useInsecureWEP || *conf.password == 0); + + 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 +*/ +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); + } + + 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 d1d507a759..146c8aeb77 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp @@ -1,340 +1,383 @@ -/* - 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; - } - - ssid = (const char*) it->ssid; - 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; + } + + ssid = (const char*) it->ssid; + 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 4e100a032d..0e4a1fab89 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(CONST IPAddress& ip, uint16_t port) { - if (_client) { + if (_client) + { stop(); _client->unref(); _client = nullptr; @@ -150,7 +159,8 @@ int WiFiClient::connect(CONST 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(CONST 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(CONST 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(CONST 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 cd83fcbcbd..127a30b348 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,92 +42,100 @@ 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(CONST IPAddress& ip, uint16_t port); - virtual int connect(const char *host, uint16_t port); - virtual int connect(const String& host, uint16_t port); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - 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(); - virtual int read(); - virtual int read(uint8_t *buf, size_t size); - virtual int peek(); - 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 bool flush(unsigned int maxWaitMs = 0); - virtual bool stop(unsigned int maxWaitMs = 0); - virtual uint8_t connected(); - virtual operator bool(); - - 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(CONST IPAddress& ip, uint16_t port); + virtual int connect(const char *host, uint16_t port); + virtual int connect(const String& host, uint16_t port); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + 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(); + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual int peek(); + 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 bool flush(unsigned int maxWaitMs = 0); + virtual bool stop(unsigned int maxWaitMs = 0); + virtual uint8_t connected(); + virtual operator bool(); + + 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 84be20b53a..40e2f75a6f 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(CONST IPAddress& ip, uint16_t port) { - if (!WiFiClient::connect(ip, port)) { + if (!WiFiClient::connect(ip, port)) + { return 0; } @@ -107,10 +116,12 @@ int WiFiClientSecure::connect(CONST 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 b1ae65d57a..0843c985bc 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,66 +26,71 @@ #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(CONST 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; - bool stop(unsigned int maxWaitMs = 0) override; - - 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(CONST 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; + bool stop(unsigned int maxWaitMs = 0) override; + + 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 ac13adde49..d9b908a358 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 @@ -58,984 +58,1157 @@ extern "C" { #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(CONST IPAddress& ip, uint16_t port) { - if (!WiFiClient::connect(ip, port)) { - return 0; - } - return _connectSSL(nullptr); +int WiFiClientSecure::connect(CONST 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)) { - return 0; - } - if (!WiFiClient::connect(remote_addr, port)) { - return 0; - } - return _connectSSL(name); +int WiFiClientSecure::connect(const char* name, uint16_t port) +{ + IPAddress remote_addr; + if (!WiFi.hostByName(name, remote_addr)) + { + return 0; + } + if (!WiFiClient::connect(remote_addr, port)) + { + 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) { - 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; + if (!connected() || !_handshake_done) + { + 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; } -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) { - 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) + { + 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; + } - return conn ? 0 : -1; // If we're connected, no error but no read. OTW error + return conn ? 0 : -1; // If we're connected, no error but no read. OTW error } -int WiFiClientSecure::read() { - uint8_t c; - if (1 == read(&c, 1)) { - return c; - } - return -1; +int WiFiClientSecure::read() +{ + uint8_t c; + if (1 == read(&c, 1)) + { + return c; + } + 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()) { +int WiFiClientSecure::peek() +{ + if (!ctx_present() || !available()) + { + return -1; + } + if (_recvapp_buf && _recvapp_len) + { + return _recvapp_buf[0]; + } return -1; - } - if (_recvapp_buf && _recvapp_len) { - return _recvapp_buf[0]; - } - return -1; } -size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) { - size_t to_copy = 0; - if (!ctx_present()) { - return 0; - } +size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) +{ + size_t to_copy = 0; + if (!ctx_present()) + { + 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()) { - 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()) + { + 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) { - 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) + { + 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) { - 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) + { + 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) return false; // Only half of the last hex digit, error - c = htoi(c); - d = htoi(d); - if ((c>15) || (d>15)) { - 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)) { - 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) + { + return false; // Only half of the last hex digit, error + } + c = htoi(c); + d = htoi(d); + if ((c > 15) || (d > 15)) + { + 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)) + { + 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) { - 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))) { - 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))) { - 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 = { - 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_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_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 - }; - - // 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); - br_ssl_engine_set_default_ecdsa(&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); - 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); - } + // 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) + { + 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))) + { + 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))) + { + 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 = + { + 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_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_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 + }; + + // 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); + br_ssl_engine_set_default_ecdsa(&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); + 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); + } } // 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()) { - 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()) + { + 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) { - 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) { - return false; - } - if (_knownkey->isRSA()) { - br_x509_knownkey_init_rsa(_x509_knownkey.get(), _knownkey->getRSA(), _knownkey_usages); - } else if (_knownkey->isEC()) { - br_x509_knownkey_init_ec(_x509_knownkey.get(), _knownkey->getEC(), _knownkey_usages); - } - 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) { - 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)); - br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng)); - 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); - } - return true; +bool WiFiClientSecure::_installClientX509Validator() +{ + if (_use_insecure || _use_fingerprint || _use_self_signed) + { + // Use common insecure x509 authenticator + _x509_insecure = std::make_shared(); + if (!_x509_insecure) + { + 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) + { + return false; + } + if (_knownkey->isRSA()) + { + br_x509_knownkey_init_rsa(_x509_knownkey.get(), _knownkey->getRSA(), _knownkey_usages); + } + else if (_knownkey->isEC()) + { + br_x509_knownkey_init_ec(_x509_knownkey.get(), _knownkey->getEC(), _knownkey_usages); + } + 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) + { + 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)); + br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng)); + 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); + } + 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) { - _freeSSL(); - _oom_err = false; +bool WiFiClientSecure::_connectSSL(const char* hostName) +{ + _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) { - DEBUGV("BSSL: Connection *will* fail, no authentication method is setup"); - } + // 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) + { + DEBUGV("BSSL: Connection *will* fail, no authentication method is setup"); + } #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; - 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; - 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()) { - 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()); - } - - // 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(); - return false; - } + if (!_sc || !_iobuf_in || !_iobuf_out) + { + _freeSSL(); // Frees _sc, _iobuf* + _oom_err = true; + 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; + 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()) + { + 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()); + } - return _wait_for_handshake(); + // 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(); + return false; + } + + return _wait_for_handshake(); } // 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; - return false; - } - br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta->getTrustAnchors(), _ta->getCount()); - br_ssl_engine_set_default_rsavrfy(_eng); - br_ssl_engine_set_default_ecdsa(_eng); - br_x509_minimal_set_rsa(_x509_minimal.get(), br_ssl_engine_get_rsavrfy(_eng)); - br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng)); - 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; +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; + return false; + } + br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta->getTrustAnchors(), _ta->getCount()); + br_ssl_engine_set_default_rsavrfy(_eng); + br_ssl_engine_set_default_ecdsa(_eng); + br_x509_minimal_set_rsa(_x509_minimal.get(), br_ssl_engine_get_rsavrfy(_eng)); + br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng)); + 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; - 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; + 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)) { - return false; - } - if (!br_ssl_server_reset(_sc_svr.get())) { - _freeSSL(); - 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)) + { + return false; + } + if (!br_ssl_server_reset(_sc_svr.get())) + { + _freeSSL(); + 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) { - _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) { + unsigned cert_issuer_key_type, const PrivateKey *sk, + const X509List *client_CA_ta) +{ _freeSSL(); - _oom_err = true; - 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; + 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)) { - return false; - } - if (!br_ssl_server_reset(_sc_svr.get())) { - _freeSSL(); - 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)) + { + return false; + } + if (!br_ssl_server_reset(_sc_svr.get())) + { + _freeSSL(); + return false; + } - return _wait_for_handshake(); + return _wait_for_handshake(); } // 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; @@ -1096,48 +1269,56 @@ 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)) { - 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)) + { + 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 @@ -1149,232 +1330,264 @@ 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, 0x05, // Extension length - 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, 0x05, // Extension length + 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) { - 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; - return false; - } + } + int ttlLen = sizeof(clientHelloHead_P) + (2 + sizeof(suites_P)) + (sizeof(clientHelloTail_P) + 1); + uint8_t *clientHello = new uint8_t[ttlLen]; + if (!clientHello) + { + 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; + return false; + } - int ret = probe.write(clientHello, ttlLen); - delete[] clientHello; // We're done w/the hello message - if (!probe.connected() || (ret != ttlLen)) { - 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; - - 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)) + { + 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; + + 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(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 + ret = probe.readBytes(&comp, 1); + handLen -= ret; + if ((ret != 1) || comp != 0) + { + // short read or invalid compression + return _SendAbort(probe, supportsLen); + } + if (handLen > 0) + { + // At this point, having an extension present means that the extension we + // sent was accepted. + supportsLen = true; + } return _SendAbort(probe, supportsLen); - } - if (handLen > 0) { - // At this point, having an extension present means that the extension we - // sent was accepted. - supportsLen = true; - } - 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 0ac9003c78..5a1bb2d0ba 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; @@ -60,40 +64,49 @@ class WiFiClientSecure : public WiFiClient { bool stop(unsigned int maxWaitMs = 0) override; // 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); @@ -107,8 +120,9 @@ class WiFiClientSecure : public WiFiClient { 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) @@ -125,10 +139,10 @@ class WiFiClientSecure : public WiFiClient { //////////////////////////////////////////////////// // AxTLS API deprecated warnings to help upgrading - #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"))) +#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"))) bool setCACert(const uint8_t* pk, size_t size) AXTLS_DEPRECATED; bool setCertificate(const uint8_t* pk, size_t size) AXTLS_DEPRECATED; @@ -141,57 +155,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; @@ -249,9 +272,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 8de4cc4ff4..c4f44d08a3 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() const { if (!_ctx) + { return INADDR_ANY; + } return _ctx->getRemoteAddress(); } @@ -242,7 +274,9 @@ IPAddress WiFiUDP::remoteIP() const uint16_t WiFiUDP::remotePort() const { if (!_ctx) + { return 0; + } return _ctx->getRemotePort(); } @@ -250,7 +284,9 @@ uint16_t WiFiUDP::remotePort() const 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 fb205513c5..41ade70070 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() const override; - // Return the port of the host who sent the current incoming packet - uint16_t remotePort() const 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() const override; + // Return the port of the host who sent the current incoming packet + uint16_t remotePort() const 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 af67ae140a..72cc34231b 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 } @@ -39,14 +39,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 @@ -80,9 +80,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; } } @@ -127,14 +129,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; } @@ -150,7 +155,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); } @@ -162,7 +168,9 @@ class UdpContext uint16_t getRemotePort() const { if (!_rx_buf) + { return 0; + } udp_hdr* udphdr = GET_UDP_HDR(_rx_buf); return lwip_ntohs(udphdr->src); @@ -176,14 +184,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) { @@ -206,7 +218,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); @@ -216,7 +230,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; @@ -231,7 +247,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]; } @@ -240,7 +258,9 @@ class UdpContext { //XXX this does not follow Arduino's flush definition if (!_rx_buf) + { return; + } _consume(_rx_buf->len - _rx_buf_offset); } @@ -258,7 +278,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); @@ -281,12 +301,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; @@ -294,27 +317,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 @@ -342,11 +371,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) @@ -355,7 +386,9 @@ class UdpContext } pbuf_cat(_tx_buf_head, pb); if (grow_size < pbuf_unit_size) + { return; + } grow_size -= pbuf_unit_size; } } @@ -363,13 +396,14 @@ 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 *addr, u16_t port) + const ip_addr_t *addr, u16_t port) { (void) upcb; (void) addr; @@ -406,15 +440,16 @@ class UdpContext _dst_addr = ip_data.current_iphdr_dest; #endif - if (_on_rx) { + if (_on_rx) + { _on_rx(); } } static void _s_recv(void *arg, - udp_pcb *upcb, pbuf *p, - CONST ip_addr_t *addr, u16_t port) + udp_pcb *upcb, pbuf *p, + CONST ip_addr_t *addr, u16_t port) { reinterpret_cast(arg)->_recv(upcb, p, addr, port); } 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 b23ad4631c..6b3e815ddd 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), _ledPin(-1) + : _httpClientTimeout(8000), _ledPin(-1) { } ESP8266HTTPUpdate::ESP8266HTTPUpdate(int httpClientTimeout) - : _httpClientTimeout(httpClientTimeout), _ledPin(-1) + : _httpClientTimeout(httpClientTimeout), _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) { @@ -270,13 +277,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); } @@ -290,7 +301,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(); @@ -303,7 +315,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()); } @@ -311,31 +324,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(); @@ -346,17 +370,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(); @@ -364,7 +393,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(); @@ -375,28 +405,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"); @@ -427,18 +464,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 @@ -446,15 +484,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 @@ -462,7 +503,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 2e5b140f7f..be5ef23289 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 1b45b9950f..9f10fe1062 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, *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, *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, *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, *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 d35df1fbd3..9a2369d3a6 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::oneShot 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::oneShot m_TTLTimeout; - timeoutLevel_t m_timeoutLevel; - - stcTTL(void); - bool set(uint32_t p_u32TTL); - - bool flagged(void) const; - 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::oneShot 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::oneShot 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::oneShot m_TTLTimeout; + timeoutLevel_t m_timeoutLevel; + + stcTTL(void); + bool set(uint32_t p_u32TTL); + + bool flagged(void) const; + 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::oneShot 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 c95c8d8234..3abe279d6f 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp @@ -1,1832 +1,2134 @@ -/* - * 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 -#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()) && // Has connection? - (_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);); - Serial.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.checkExpired(millis()))) { // 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.reset(std::numeric_limits::max()); - 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.checkExpired(std::numeric_limits::max()))) { - - 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.reset(std::numeric_limits::max()); - 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.checkExpired(millis()))) { // 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.reset(std::numeric_limits::max()); - 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.checkExpired(millis()))) { - - 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.reset(std::numeric_limits::max()); - 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.checkExpired(millis()))) { - - 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)) - : std::numeric_limits::max()); - } - 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 +/* + 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 +#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()) && // Has connection? + (_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) && (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"));); + Serial.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);); + Serial.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.checkExpired(millis()))) // 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.reset(std::numeric_limits::max()); + 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.checkExpired(std::numeric_limits::max()))) + { + + 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.reset(std::numeric_limits::max()); + 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.checkExpired(millis()))) // 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.reset(std::numeric_limits::max()); + 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.checkExpired(millis()))) + { + + 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.reset(std::numeric_limits::max()); + 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.checkExpired(millis()))) + { + + 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)) + : std::numeric_limits::max()); + } + 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..04f81cbe6c 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 41c38fe6b1..caddd0bb53 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp @@ -1,2218 +1,2477 @@ -/* - * 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(std::numeric_limits::max()), - 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.reset(std::numeric_limits::max()); - 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(std::numeric_limits::max()), - 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.reset(std::numeric_limits::max()); - } - return true; -} - -/* - * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged - */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) const { - - return ((m_u32TTL) && - (TIMEOUTLEVEL_UNSET != m_timeoutLevel) && - (m_TTLTimeout.checkExpired(millis()))); -} - -/* - * 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.reset(std::numeric_limits::max()); - 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 = std::numeric_limits::max(); - - 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(std::numeric_limits::max()), - 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.reset(std::numeric_limits::max()); - 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(std::numeric_limits::max()), + 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.reset(std::numeric_limits::max()); + 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(std::numeric_limits::max()), + 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.reset(std::numeric_limits::max()); + } + return true; +} + +/* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged +*/ +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) const +{ + + return ((m_u32TTL) && + (TIMEOUTLEVEL_UNSET != m_timeoutLevel) && + (m_TTLTimeout.checkExpired(millis()))); +} + +/* + 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.reset(std::numeric_limits::max()); + 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 = std::numeric_limits::max(); + + 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(std::numeric_limits::max()), + 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.reset(std::numeric_limits::max()); + 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..596294f6a7 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 b03e2c495a..845fd92520 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,170 +14,221 @@ 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(CONST 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; +int EthernetClient::connect(CONST IPAddress& ip, uint16_t port) +{ + if (_sock != MAX_SOCK_NUM) + { + return 0; } - } - 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; + } + } - _srcport++; - if (_srcport == 0) _srcport = 49152; //Use IANA recommended ephemeral port range 49152-65535 - socket(_sock, SnMR::TCP, _srcport, 0); + if (_sock == MAX_SOCK_NUM) + { + return 0; + } - if (!::connect(_sock, rawIPAddress(ip), port)) { - _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); - while (status() != SnSR::ESTABLISHED) { - delay(1); - if (status() == SnSR::CLOSED) { - _sock = MAX_SOCK_NUM; - return 0; + if (!::connect(_sock, rawIPAddress(ip), port)) + { + _sock = MAX_SOCK_NUM; + return 0; } - } - return 1; -} + while (status() != SnSR::ESTABLISHED) + { + delay(1); + if (status() == SnSR::CLOSED) + { + _sock = MAX_SOCK_NUM; + return 0; + } + } -size_t EthernetClient::write(uint8_t b) { - return write(&b, 1); + return 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(uint8_t b) +{ + return write(&b, 1); } -int EthernetClient::available() { - if (_sock != MAX_SOCK_NUM) - return recvAvailable(_sock); - return 0; +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::read() { - uint8_t b; - if ( recv(_sock, &b, 1) > 0 ) - { - // recv worked - return b; - } - else - { - // No data available - return -1; - } +int EthernetClient::available() +{ + if (_sock != MAX_SOCK_NUM) + { + return recvAvailable(_sock); + } + return 0; } -int EthernetClient::read(uint8_t *buf, size_t size) { - return recv(_sock, buf, size); +int EthernetClient::read() +{ + uint8_t b; + if (recv(_sock, &b, 1) > 0) + { + // recv worked + return b; + } + else + { + // No data available + return -1; + } } -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::read(uint8_t *buf, size_t size) +{ + return recv(_sock, buf, size); } -bool EthernetClient::flush(unsigned int maxWaitMs) { - (void)maxWaitMs; - ::flush(_sock); - return true; +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; } -bool EthernetClient::stop(unsigned int maxWaitMs) { - if (_sock == MAX_SOCK_NUM) +bool EthernetClient::flush(unsigned int maxWaitMs) +{ + (void)maxWaitMs; + ::flush(_sock); return true; +} - // 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; - if (maxWaitMs == 0) - maxWaitMs = 1000; - do { - s = status(); - if (s == SnSR::CLOSED) - break; // exit the loop - delay(1); - } while (millis() - start < maxWaitMs); +bool EthernetClient::stop(unsigned int maxWaitMs) +{ + if (_sock == MAX_SOCK_NUM) + { + return true; + } - bool ret = true; + // attempt to close the connection gracefully (send a FIN to other side) + disconnect(_sock); + unsigned long start = millis(); - // if it hasn't closed, close it forcefully - if (s != SnSR::CLOSED) { - ret = false; - close(_sock); - } + // wait up to a second for the connection to close + uint8_t s; + if (maxWaitMs == 0) + { + maxWaitMs = 1000; + } + do + { + s = status(); + if (s == SnSR::CLOSED) + { + break; // exit the loop + } + delay(1); + } while (millis() - start < maxWaitMs); + + bool ret = true; + + // if it hasn't closed, close it forcefully + if (s != SnSR::CLOSED) + { + ret = false; + close(_sock); + } - EthernetClass::_server_port[_sock] = 0; - _sock = MAX_SOCK_NUM; + EthernetClass::_server_port[_sock] = 0; + _sock = MAX_SOCK_NUM; - return ret; + return ret; } -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 0ea3f6ca0c..4dd2e1f400 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(CONST 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 bool flush(unsigned int maxWaitMs = 0); - virtual bool stop(unsigned int maxWaitMs = 0); - 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(CONST 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 bool flush(unsigned int maxWaitMs = 0); + virtual bool stop(unsigned int maxWaitMs = 0); + 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 d4feb41afa..890e7b07df 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() const { return _remoteIP; }; - // Return the port of the host who sent the current incoming packet - virtual uint16_t remotePort() const { 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() const + { + return _remoteIP; + }; + // Return the port of the host who sent the current incoming packet + virtual uint16_t remotePort() const + { + 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..b85f1bfe0f 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,388 +31,453 @@ 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); \ - } \ - static inline uint8_t read##name() { \ - return read(address); \ - } + static inline void write##name(uint8_t _data) { \ + write(address, _data); \ + } \ + static inline uint8_t read##name() { \ + return read(address); \ + } #define __GP_REGISTER16(name, address) \ - static void write##name(uint16_t _data) { \ - write(address, _data >> 8); \ - write(address+1, _data & 0xFF); \ - } \ - static uint16_t read##name() { \ - uint16_t res = read(address); \ - res = (res << 8) + read(address + 1); \ - return res; \ - } + static void write##name(uint16_t _data) { \ + write(address, _data >> 8); \ + write(address+1, _data & 0xFF); \ + } \ + static uint16_t read##name() { \ + uint16_t res = read(address); \ + res = (res << 8) + read(address + 1); \ + return res; \ + } #define __GP_REGISTER_N(name, address, size) \ - static uint16_t write##name(uint8_t *_buff) { \ - return write(address, _buff, size); \ - } \ - static uint16_t read##name(uint8_t *_buff) { \ - return read(address, _buff, size); \ - } + static uint16_t write##name(uint8_t *_buff) { \ + return write(address, _buff, size); \ + } \ + static uint16_t read##name(uint8_t *_buff) { \ + return read(address, _buff, size); \ + } 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) { \ - writeSn(_s, address, _data); \ - } \ - static inline uint8_t read##name(SOCKET _s) { \ - return readSn(_s, address); \ - } + static inline void write##name(SOCKET _s, uint8_t _data) { \ + writeSn(_s, address, _data); \ + } \ + static inline uint8_t read##name(SOCKET _s) { \ + return readSn(_s, address); \ + } #define __SOCKET_REGISTER16(name, address) \ - static void write##name(SOCKET _s, uint16_t _data) { \ - writeSn(_s, address, _data >> 8); \ - writeSn(_s, address+1, _data & 0xFF); \ - } \ - static uint16_t read##name(SOCKET _s) { \ - uint16_t res = readSn(_s, address); \ - uint16_t res2 = readSn(_s,address + 1); \ - res = res << 8; \ - res2 = res2 & 0xFF; \ - res = res | res2; \ - return res; \ - } + static void write##name(SOCKET _s, uint16_t _data) { \ + writeSn(_s, address, _data >> 8); \ + writeSn(_s, address+1, _data & 0xFF); \ + } \ + static uint16_t read##name(SOCKET _s) { \ + uint16_t res = readSn(_s, address); \ + uint16_t res2 = readSn(_s,address + 1); \ + res = res << 8; \ + res2 = res2 & 0xFF; \ + res = res | res2; \ + return res; \ + } #define __SOCKET_REGISTER_N(name, address, size) \ - static uint16_t write##name(SOCKET _s, const uint8_t *_buff) { \ - return writeSn(_s, address, _buff, size); \ - } \ - static uint16_t read##name(SOCKET _s, uint8_t *_buff) { \ - return readSn(_s, address, _buff, size); \ - } - + static uint16_t write##name(SOCKET _s, const uint8_t *_buff) { \ + return writeSn(_s, address, _buff, size); \ + } \ + 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..be32079b9b 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..9f73d3cef2 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/File.cpp b/libraries/SD/src/File.cpp index 2002f85129..a323fc27e7 100644 --- a/libraries/SD/src/File.cpp +++ b/libraries/SD/src/File.cpp @@ -1,149 +1,195 @@ /* - SD - a slightly more friendly wrapper for sdfatlib + SD - a slightly more friendly wrapper for sdfatlib - This library aims to expose a subset of SD card functionality - in the form of a higher level "wrapper" object. + This library aims to expose a subset of SD card functionality + in the form of a higher level "wrapper" object. - License: GNU General Public License V3 + License: GNU General Public License V3 (Because sdfatlib is licensed with this.) - (C) Copyright 2010 SparkFun Electronics + (C) Copyright 2010 SparkFun Electronics - */ +*/ #include -/* for debugging file open/close leaks - uint8_t nfilecount=0; +/* for debugging file open/close leaks + uint8_t nfilecount=0; */ -File::File(SdFile f, const char *n) { - // oh man you are kidding me, new() doesnt exist? Ok we do it by hand! - _file = (SdFile *)malloc(sizeof(SdFile)); - if (_file) { - memcpy(_file, &f, sizeof(SdFile)); - - strncpy(_name, n, 12); - _name[12] = 0; - - /* for debugging file open/close leaks - nfilecount++; - Serial.print("Created \""); - Serial.print(n); - Serial.print("\": "); - Serial.println(nfilecount, DEC); - */ - } -} - -File::File(void) { - _file = 0; - _name[0] = 0; - //Serial.print("Created empty file object"); +File::File(SdFile f, const char *n) +{ + // oh man you are kidding me, new() doesnt exist? Ok we do it by hand! + _file = (SdFile *)malloc(sizeof(SdFile)); + if (_file) + { + memcpy(_file, &f, sizeof(SdFile)); + + strncpy(_name, n, 12); + _name[12] = 0; + + /* for debugging file open/close leaks + nfilecount++; + Serial.print("Created \""); + Serial.print(n); + Serial.print("\": "); + Serial.println(nfilecount, DEC); + */ + } +} + +File::File(void) +{ + _file = 0; + _name[0] = 0; + //Serial.print("Created empty file object"); } // returns a pointer to the file name -char *File::name(void) { - return _name; +char *File::name(void) +{ + return _name; } // a directory is a special type of file -boolean File::isDirectory(void) { - return (_file && _file->isDir()); -} - - -size_t File::write(uint8_t val) { - return write(&val, 1); -} - -size_t File::write(const uint8_t *buf, size_t size) { - size_t t; - if (!_file) { - setWriteError(); - return 0; - } - _file->clearWriteError(); - t = _file->write(buf, size); - if (_file->getWriteError()) { - setWriteError(); - return 0; - } - return t; -} - -int File::peek() { - if (! _file) - return 0; - - int c = _file->read(); - if (c != -1) _file->seekCur(-1); - return c; -} - -int File::read() { - if (_file) - return _file->read(); - return -1; +boolean File::isDirectory(void) +{ + return (_file && _file->isDir()); +} + + +size_t File::write(uint8_t val) +{ + return write(&val, 1); +} + +size_t File::write(const uint8_t *buf, size_t size) +{ + size_t t; + if (!_file) + { + setWriteError(); + return 0; + } + _file->clearWriteError(); + t = _file->write(buf, size); + if (_file->getWriteError()) + { + setWriteError(); + return 0; + } + return t; +} + +int File::peek() +{ + if (! _file) + { + return 0; + } + + int c = _file->read(); + if (c != -1) + { + _file->seekCur(-1); + } + return c; +} + +int File::read() +{ + if (_file) + { + return _file->read(); + } + return -1; } // buffered read for more efficient, high speed reading -int File::read(void *buf, uint16_t nbyte) { - if (_file) - return _file->read(buf, nbyte); - return 0; +int File::read(void *buf, uint16_t nbyte) +{ + if (_file) + { + return _file->read(buf, nbyte); + } + return 0; } -size_t File::readBytes(char *buffer, size_t length) { - int result = read(buffer, (uint16_t)length); - return result < 0 ? 0 : (size_t)result; +size_t File::readBytes(char *buffer, size_t length) +{ + int result = read(buffer, (uint16_t)length); + return result < 0 ? 0 : (size_t)result; } -int File::available() { - if (! _file) return 0; +int File::available() +{ + if (! _file) + { + return 0; + } - return size() - position(); + return size() - position(); } -void File::flush() { - if (_file) - _file->sync(); +void File::flush() +{ + if (_file) + { + _file->sync(); + } } -boolean File::seek(uint32_t pos) { - if (! _file) return false; +boolean File::seek(uint32_t pos) +{ + if (! _file) + { + return false; + } - return _file->seekSet(pos); + return _file->seekSet(pos); } -uint32_t File::position() { - if (! _file) return -1; - return _file->curPosition(); +uint32_t File::position() +{ + if (! _file) + { + return -1; + } + return _file->curPosition(); } -uint32_t File::size() { - if (! _file) return 0; - return _file->fileSize(); +uint32_t File::size() +{ + if (! _file) + { + return 0; + } + return _file->fileSize(); } -void File::close() { - if (_file) { - _file->close(); - free(_file); - _file = 0; +void File::close() +{ + if (_file) + { + _file->close(); + free(_file); + _file = 0; - /* for debugging file open/close leaks - nfilecount--; - Serial.print("Deleted "); - Serial.println(nfilecount, DEC); - */ - } + /* for debugging file open/close leaks + nfilecount--; + Serial.print("Deleted "); + Serial.println(nfilecount, DEC); + */ + } } -File::operator bool() { - if (_file) - return _file->isOpen(); - return false; +File::operator bool() +{ + if (_file) + { + return _file->isOpen(); + } + return false; } diff --git a/libraries/SD/src/SD.cpp b/libraries/SD/src/SD.cpp index 0c3a1b0935..9316fdda9f 100644 --- a/libraries/SD/src/SD.cpp +++ b/libraries/SD/src/SD.cpp @@ -1,54 +1,54 @@ /* - SD - a slightly more friendly wrapper for sdfatlib + SD - a slightly more friendly wrapper for sdfatlib - This library aims to expose a subset of SD card functionality - in the form of a higher level "wrapper" object. + This library aims to expose a subset of SD card functionality + in the form of a higher level "wrapper" object. - License: GNU General Public License V3 + License: GNU General Public License V3 (Because sdfatlib is licensed with this.) - (C) Copyright 2010 SparkFun Electronics + (C) Copyright 2010 SparkFun Electronics - This library provides four key benefits: + This library provides four key benefits: - * Including `SD.h` automatically creates a global + Including `SD.h` automatically creates a global `SD` object which can be interacted with in a similar manner to other standard global objects like `Serial` and `Ethernet`. - * Boilerplate initialisation code is contained in one method named + Boilerplate initialisation code is contained in one method named `begin` and no further objects need to be created in order to access the SD card. - * Calls to `open` can supply a full path name including parent + Calls to `open` can supply a full path name including parent directories which simplifies interacting with files in subdirectories. - * Utility methods are provided to determine whether a file exists + Utility methods are provided to determine whether a file exists and to create a directory heirarchy. - Note however that not all functionality provided by the underlying - sdfatlib library is exposed. + Note however that not all functionality provided by the underlying + sdfatlib library is exposed. - */ +*/ /* - Implementation Notes + Implementation Notes - In order to handle multi-directory path traversal, functionality that - requires this ability is implemented as callback functions. + In order to handle multi-directory path traversal, functionality that + requires this ability is implemented as callback functions. - Individual methods call the `walkPath` function which performs the actual - directory traversal (swapping between two different directory/file handles - along the way) and at each level calls the supplied callback function. + Individual methods call the `walkPath` function which performs the actual + directory traversal (swapping between two different directory/file handles + along the way) and at each level calls the supplied callback function. - Some types of functionality will take an action at each level (e.g. exists - or make directory) which others will only take an action at the bottom - level (e.g. open). + Some types of functionality will take an action at each level (e.g. exists + or make directory) which others will only take an action at the bottom + level (e.g. open). - */ +*/ #include "SD.h" @@ -57,230 +57,249 @@ #define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1 bool getNextPathComponent(const char *path, unsigned int *p_offset, - char *buffer) { - /* + char *buffer) +{ + /* - Parse individual path components from a path. + Parse individual path components from a path. - e.g. after repeated calls '/foo/bar/baz' will be split - into 'foo', 'bar', 'baz'. + e.g. after repeated calls '/foo/bar/baz' will be split + into 'foo', 'bar', 'baz'. - This is similar to `strtok()` but copies the component into the - supplied buffer rather than modifying the original string. + This is similar to `strtok()` but copies the component into the + supplied buffer rather than modifying the original string. - `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size. + `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size. - `p_offset` needs to point to an integer of the offset at - which the previous path component finished. + `p_offset` needs to point to an integer of the offset at + which the previous path component finished. - Returns `true` if more components remain. + Returns `true` if more components remain. - Returns `false` if this is the last component. - (This means path ended with 'foo' or 'foo/'.) + Returns `false` if this is the last component. + (This means path ended with 'foo' or 'foo/'.) - */ + */ - // TODO: Have buffer local to this function, so we know it's the - // correct length? + // TODO: Have buffer local to this function, so we know it's the + // correct length? - int bufferOffset = 0; + int bufferOffset = 0; - int offset = *p_offset; + int offset = *p_offset; - // Skip root or other separator - if (path[offset] == '/') { - offset++; - } - - // Copy the next next path segment - while (bufferOffset < MAX_COMPONENT_LEN - && (path[offset] != '/') - && (path[offset] != '\0')) { - buffer[bufferOffset++] = path[offset++]; - } + // Skip root or other separator + if (path[offset] == '/') + { + offset++; + } - buffer[bufferOffset] = '\0'; + // Copy the next next path segment + while (bufferOffset < MAX_COMPONENT_LEN + && (path[offset] != '/') + && (path[offset] != '\0')) + { + buffer[bufferOffset++] = path[offset++]; + } - // Skip trailing separator so we can determine if this - // is the last component in the path or not. - if (path[offset] == '/') { - offset++; - } + buffer[bufferOffset] = '\0'; - *p_offset = offset; + // Skip trailing separator so we can determine if this + // is the last component in the path or not. + if (path[offset] == '/') + { + offset++; + } - return (path[offset] != '\0'); + *p_offset = offset; + + return (path[offset] != '\0'); } boolean walkPath(const char *filepath, SdFile& parentDir, - boolean (*callback)(SdFile& parentDir, - char *filePathComponent, - boolean isLastComponent, - void *object), - void *object = NULL) { - /* - - When given a file path (and parent directory--normally root), - this function traverses the directories in the path and at each - level calls the supplied callback function while also providing - the supplied object for context if required. + boolean(*callback)(SdFile& parentDir, + char *filePathComponent, + boolean isLastComponent, + void *object), + void *object = NULL) +{ + /* - e.g. given the path '/foo/bar/baz' - the callback would be called at the equivalent of - '/foo', '/foo/bar' and '/foo/bar/baz'. + When given a file path (and parent directory--normally root), + this function traverses the directories in the path and at each + level calls the supplied callback function while also providing + the supplied object for context if required. - The implementation swaps between two different directory/file - handles as it traverses the directories and does not use recursion - in an attempt to use memory efficiently. + e.g. given the path '/foo/bar/baz' + the callback would be called at the equivalent of + '/foo', '/foo/bar' and '/foo/bar/baz'. - If a callback wishes to stop the directory traversal it should - return false--in this case the function will stop the traversal, - tidy up and return false. + The implementation swaps between two different directory/file + handles as it traverses the directories and does not use recursion + in an attempt to use memory efficiently. - If a directory path doesn't exist at some point this function will - also return false and not subsequently call the callback. + If a callback wishes to stop the directory traversal it should + return false--in this case the function will stop the traversal, + tidy up and return false. - If a directory path specified is complete, valid and the callback - did not indicate the traversal should be interrupted then this - function will return true. + If a directory path doesn't exist at some point this function will + also return false and not subsequently call the callback. - */ + If a directory path specified is complete, valid and the callback + did not indicate the traversal should be interrupted then this + function will return true. + */ - SdFile subfile1; - SdFile subfile2; - char buffer[PATH_COMPONENT_BUFFER_LEN]; + SdFile subfile1; + SdFile subfile2; - unsigned int offset = 0; + char buffer[PATH_COMPONENT_BUFFER_LEN]; - SdFile *p_parent; - SdFile *p_child; + unsigned int offset = 0; - SdFile *p_tmp_sdfile; - - p_child = &subfile1; - - p_parent = &parentDir; + SdFile *p_parent; + SdFile *p_child; - while (true) { + SdFile *p_tmp_sdfile; - boolean moreComponents = getNextPathComponent(filepath, &offset, buffer); + p_child = &subfile1; - boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object); + p_parent = &parentDir; - if (!shouldContinue) { - // TODO: Don't repeat this code? - // If it's one we've created then we - // don't need the parent handle anymore. - if (p_parent != &parentDir) { - (*p_parent).close(); - } - return false; - } - - if (!moreComponents) { - break; - } - - boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY); + while (true) + { + + boolean moreComponents = getNextPathComponent(filepath, &offset, buffer); - // If it's one we've created then we - // don't need the parent handle anymore. - if (p_parent != &parentDir) { - (*p_parent).close(); + boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object); + + if (!shouldContinue) + { + // TODO: Don't repeat this code? + // If it's one we've created then we + // don't need the parent handle anymore. + if (p_parent != &parentDir) + { + (*p_parent).close(); + } + return false; + } + + if (!moreComponents) + { + break; + } + + boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY); + + // If it's one we've created then we + // don't need the parent handle anymore. + if (p_parent != &parentDir) + { + (*p_parent).close(); + } + + // Handle case when it doesn't exist and we can't continue... + if (exists) + { + // We alternate between two file handles as we go down + // the path. + if (p_parent == &parentDir) + { + p_parent = &subfile2; + } + + p_tmp_sdfile = p_parent; + p_parent = p_child; + p_child = p_tmp_sdfile; + } + else + { + return false; + } } - - // Handle case when it doesn't exist and we can't continue... - if (exists) { - // We alternate between two file handles as we go down - // the path. - if (p_parent == &parentDir) { - p_parent = &subfile2; - } - - p_tmp_sdfile = p_parent; - p_parent = p_child; - p_child = p_tmp_sdfile; - } else { - return false; + + if (p_parent != &parentDir) + { + (*p_parent).close(); // TODO: Return/ handle different? } - } - - if (p_parent != &parentDir) { - (*p_parent).close(); // TODO: Return/ handle different? - } - return true; + return true; } /* - The callbacks used to implement various functionality follow. + The callbacks used to implement various functionality follow. + + Each callback is supplied with a parent directory handle, + character string with the name of the current file path component, + a flag indicating if this component is the last in the path and + a pointer to an arbitrary object used for context. + +*/ - Each callback is supplied with a parent directory handle, - character string with the name of the current file path component, - a flag indicating if this component is the last in the path and - a pointer to an arbitrary object used for context. +boolean callback_pathExists(SdFile& parentDir, char *filePathComponent, + boolean isLastComponent, void *object) +{ + /* - */ + Callback used to determine if a file/directory exists in parent + directory. -boolean callback_pathExists(SdFile& parentDir, char *filePathComponent, - boolean isLastComponent, void *object) { - /* + Returns true if file path exists. - Callback used to determine if a file/directory exists in parent - directory. + */ + SdFile child; + (void) isLastComponent; + (void) object; - Returns true if file path exists. + boolean exists = child.open(parentDir, filePathComponent, O_RDONLY); - */ - SdFile child; - (void) isLastComponent; - (void) object; + if (exists) + { + child.close(); + } - boolean exists = child.open(parentDir, filePathComponent, O_RDONLY); - - if (exists) { - child.close(); - } - - return exists; + return exists; } -boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent, - boolean isLastComponent, void *object) { - /* +boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent, + boolean isLastComponent, void *object) +{ + /* + + Callback used to create a directory in the parent directory if + it does not already exist. + + Returns true if a directory was created or it already existed. - Callback used to create a directory in the parent directory if - it does not already exist. + */ + boolean result = false; + SdFile child; - Returns true if a directory was created or it already existed. + result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object); + if (!result) + { + result = child.makeDir(parentDir, filePathComponent); + } - */ - boolean result = false; - SdFile child; - - result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object); - if (!result) { - result = child.makeDir(parentDir, filePathComponent); - } - - return result; + return result; } - /* +/* -boolean callback_openPath(SdFile& parentDir, char *filePathComponent, - boolean isLastComponent, void *object) { + boolean callback_openPath(SdFile& parentDir, char *filePathComponent, + boolean isLastComponent, void *object) { Callback used to open a file specified by a filepath that may specify one or more directories above it. @@ -295,40 +314,47 @@ boolean callback_openPath(SdFile& parentDir, char *filePathComponent, Returns false once the file has been opened--to prevent the traversal from descending further. (This may be unnecessary.) - if (isLastComponent) { + if (isLastComponent) { SDClass *p_SD = static_cast(object); p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode); if (p_SD->fileOpenMode == FILE_WRITE) { - p_SD->file.seekSet(p_SD->file.fileSize()); + p_SD->file.seekSet(p_SD->file.fileSize()); } // TODO: Return file open result? return false; - } - return true; -} - */ + } + return true; + } +*/ -boolean callback_remove(SdFile& parentDir, char *filePathComponent, - boolean isLastComponent, void *object) { - (void) object; +boolean callback_remove(SdFile& parentDir, char *filePathComponent, + boolean isLastComponent, void *object) +{ + (void) object; - if (isLastComponent) { - return SdFile::remove(parentDir, filePathComponent); - } - return true; + if (isLastComponent) + { + return SdFile::remove(parentDir, filePathComponent); + } + return true; } -boolean callback_rmdir(SdFile& parentDir, char *filePathComponent, - boolean isLastComponent, void *object) { - (void) object; - if (isLastComponent) { - SdFile f; - if (!f.open(parentDir, filePathComponent, O_READ)) return false; - return f.rmDir(); - } - return true; +boolean callback_rmdir(SdFile& parentDir, char *filePathComponent, + boolean isLastComponent, void *object) +{ + (void) object; + if (isLastComponent) + { + SdFile f; + if (!f.open(parentDir, filePathComponent, O_READ)) + { + return false; + } + return f.rmDir(); + } + return true; } @@ -337,155 +363,175 @@ boolean callback_rmdir(SdFile& parentDir, char *filePathComponent, -boolean SDClass::begin(uint8_t csPin, uint32_t speed) { - /* +boolean SDClass::begin(uint8_t csPin, uint32_t speed) +{ + /* - Performs the initialisation required by the sdfatlib library. + Performs the initialisation required by the sdfatlib library. - Return true if initialization succeeds, false otherwise. + Return true if initialization succeeds, false otherwise. - */ - return card.init(speed, csPin) && - volume.init(card) && - root.openRoot(volume); + */ + return card.init(speed, csPin) && + volume.init(card) && + root.openRoot(volume); } //Warning: see comment in SD.h about possible card corruption. void SDClass::end(bool endSPI) { - if(card.errorCode() == 0 && root.isOpen()) { - root.close(); //Warning: this calls sync(), see above comment about corruption. - } - - card.end(endSPI); + if (card.errorCode() == 0 && root.isOpen()) + { + root.close(); //Warning: this calls sync(), see above comment about corruption. + } + + card.end(endSPI); } // this little helper is used to traverse paths -SdFile SDClass::getParentDir(const char *filepath, int *index) { - // get parent directory - SdFile d1 = root; // start with the mostparent, root! - SdFile d2; - - // we'll use the pointers to swap between the two objects - SdFile *parent = &d1; - SdFile *subdir = &d2; - - const char *origpath = filepath; - - while (strchr(filepath, '/')) { - - // get rid of leading /'s - if (filepath[0] == '/') { - filepath++; - continue; - } - - if (! strchr(filepath, '/')) { - // it was in the root directory, so leave now - break; - } - - // extract just the name of the next subdirectory - uint8_t idx = strchr(filepath, '/') - filepath; - if (idx > 12) - idx = 12; // dont let them specify long names - char subdirname[13]; - strncpy(subdirname, filepath, idx); - subdirname[idx] = 0; - - // close the subdir (we reuse them) if open - subdir->close(); - if (! subdir->open(parent, subdirname, O_READ)) { - // failed to open one of the subdirectories - return SdFile(); +SdFile SDClass::getParentDir(const char *filepath, int *index) +{ + // get parent directory + SdFile d1 = root; // start with the mostparent, root! + SdFile d2; + + // we'll use the pointers to swap between the two objects + SdFile *parent = &d1; + SdFile *subdir = &d2; + + const char *origpath = filepath; + + while (strchr(filepath, '/')) + { + + // get rid of leading /'s + if (filepath[0] == '/') + { + filepath++; + continue; + } + + if (! strchr(filepath, '/')) + { + // it was in the root directory, so leave now + break; + } + + // extract just the name of the next subdirectory + uint8_t idx = strchr(filepath, '/') - filepath; + if (idx > 12) + { + idx = 12; // dont let them specify long names + } + char subdirname[13]; + strncpy(subdirname, filepath, idx); + subdirname[idx] = 0; + + // close the subdir (we reuse them) if open + subdir->close(); + if (! subdir->open(parent, subdirname, O_READ)) + { + // failed to open one of the subdirectories + return SdFile(); + } + // move forward to the next subdirectory + filepath += idx; + + // we reuse the objects, close it. + parent->close(); + + // swap the pointers + SdFile *t = parent; + parent = subdir; + subdir = t; } - // move forward to the next subdirectory - filepath += idx; - - // we reuse the objects, close it. - parent->close(); - - // swap the pointers - SdFile *t = parent; - parent = subdir; - subdir = t; - } - *index = (int)(filepath - origpath); - // parent is now the parent diretory of the file! - return *parent; + *index = (int)(filepath - origpath); + // parent is now the parent diretory of the file! + return *parent; } -File SDClass::open(const char *filepath, uint8_t mode) { - /* +File SDClass::open(const char *filepath, uint8_t mode) +{ + /* - Open the supplied file path for reading or writing. + Open the supplied file path for reading or writing. - The file content can be accessed via the `file` property of - the `SDClass` object--this property is currently - a standard `SdFile` object from `sdfatlib`. + The file content can be accessed via the `file` property of + the `SDClass` object--this property is currently + a standard `SdFile` object from `sdfatlib`. - Defaults to read only. + Defaults to read only. - If `write` is true, default action (when `append` is true) is to - append data to the end of the file. + If `write` is true, default action (when `append` is true) is to + append data to the end of the file. - If `append` is false then the file will be truncated first. + If `append` is false then the file will be truncated first. - If the file does not exist and it is opened for writing the file - will be created. + If the file does not exist and it is opened for writing the file + will be created. - An attempt to open a file for reading that does not exist is an - error. + An attempt to open a file for reading that does not exist is an + error. - */ + */ - int pathidx; + int pathidx; - // do the interative search - SdFile parentdir = getParentDir(filepath, &pathidx); - // no more subdirs! + // do the interative search + SdFile parentdir = getParentDir(filepath, &pathidx); + // no more subdirs! - filepath += pathidx; + filepath += pathidx; - if (! filepath[0]) { - // it was the directory itself! - return File(parentdir, "/"); - } + if (! filepath[0]) + { + // it was the directory itself! + return File(parentdir, "/"); + } - // Open the file itself - SdFile file; + // Open the file itself + SdFile file; - // failed to open a subdir! - if (!parentdir.isOpen()) - return File(); + // failed to open a subdir! + if (!parentdir.isOpen()) + { + return File(); + } - // there is a special case for the Root directory since its a static dir - if (parentdir.isRoot()) { - if ( ! file.open(root, filepath, mode)) { - // failed to open the file :( - return File(); + // there is a special case for the Root directory since its a static dir + if (parentdir.isRoot()) + { + if (! file.open(root, filepath, mode)) + { + // failed to open the file :( + return File(); + } + // dont close the root! } - // dont close the root! - } else { - if ( ! file.open(parentdir, filepath, mode)) { - return File(); + else + { + if (! file.open(parentdir, filepath, mode)) + { + return File(); + } + // close the parent + parentdir.close(); } - // close the parent - parentdir.close(); - } - if (mode & (O_APPEND | O_WRITE)) - file.seekSet(file.fileSize()); - return File(file, filepath); + if (mode & (O_APPEND | O_WRITE)) + { + file.seekSet(file.fileSize()); + } + return File(file, filepath); } /* -File SDClass::open(char *filepath, uint8_t mode) { - // + File SDClass::open(char *filepath, uint8_t mode) { + // Open the supplied file path for reading or writing. @@ -506,16 +552,16 @@ File SDClass::open(char *filepath, uint8_t mode) { An attempt to open a file for reading that does not exist is an error. - // + // - // TODO: Allow for read&write? (Possibly not, as it requires seek.) + // TODO: Allow for read&write? (Possibly not, as it requires seek.) - fileOpenMode = mode; - walkPath(filepath, root, callback_openPath, this); + fileOpenMode = mode; + walkPath(filepath, root, callback_openPath, this); - return File(); + return File(); -} + } */ @@ -529,13 +575,14 @@ File SDClass::open(char *filepath, uint8_t mode) { //} -boolean SDClass::exists(const char *filepath) { - /* +boolean SDClass::exists(const char *filepath) +{ + /* - Returns true if the supplied file path exists. + Returns true if the supplied file path exists. - */ - return walkPath(filepath, root, callback_pathExists); + */ + return walkPath(filepath, root, callback_pathExists); } @@ -550,81 +597,95 @@ boolean SDClass::exists(const char *filepath) { //} -boolean SDClass::mkdir(const char *filepath) { - /* - - Makes a single directory or a heirarchy of directories. - - A rough equivalent to `mkdir -p`. - - */ - return walkPath(filepath, root, callback_makeDirPath); -} +boolean SDClass::mkdir(const char *filepath) +{ + /* -boolean SDClass::rmdir(const char *filepath) { - /* - - Remove a single directory or a heirarchy of directories. + Makes a single directory or a heirarchy of directories. - A rough equivalent to `rm -rf`. - - */ - return walkPath(filepath, root, callback_rmdir); -} + A rough equivalent to `mkdir -p`. -boolean SDClass::remove(const char *filepath) { - return walkPath(filepath, root, callback_remove); + */ + return walkPath(filepath, root, callback_makeDirPath); } +boolean SDClass::rmdir(const char *filepath) +{ + /* -// allows you to recurse into a directory -File File::openNextFile(uint8_t mode) { - dir_t p; + Remove a single directory or a heirarchy of directories. - //Serial.print("\t\treading dir..."); - while (_file->readDir(&p) > 0) { + A rough equivalent to `rm -rf`. - // done if past last used entry - if (p.name[0] == DIR_NAME_FREE) { - //Serial.println("end"); - return File(); - } + */ + return walkPath(filepath, root, callback_rmdir); +} - // skip deleted entry and entries for . and .. - if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') { - //Serial.println("dots"); - continue; - } +boolean SDClass::remove(const char *filepath) +{ + return walkPath(filepath, root, callback_remove); +} - // only list subdirectories and files - if (!DIR_IS_FILE_OR_SUBDIR(&p)) { - //Serial.println("notafile"); - continue; - } - // print file name with possible blank fill - SdFile f; - char name[13]; - _file->dirName(p, name); - //Serial.print("try to open file "); - //Serial.println(name); - - if (f.open(_file, name, mode)) { - //Serial.println("OK!"); - return File(f, name); - } else { - //Serial.println("ugh"); - return File(); +// allows you to recurse into a directory +File File::openNextFile(uint8_t mode) +{ + dir_t p; + + //Serial.print("\t\treading dir..."); + while (_file->readDir(&p) > 0) + { + + // done if past last used entry + if (p.name[0] == DIR_NAME_FREE) + { + //Serial.println("end"); + return File(); + } + + // skip deleted entry and entries for . and .. + if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') + { + //Serial.println("dots"); + continue; + } + + // only list subdirectories and files + if (!DIR_IS_FILE_OR_SUBDIR(&p)) + { + //Serial.println("notafile"); + continue; + } + + // print file name with possible blank fill + SdFile f; + char name[13]; + _file->dirName(p, name); + //Serial.print("try to open file "); + //Serial.println(name); + + if (f.open(_file, name, mode)) + { + //Serial.println("OK!"); + return File(f, name); + } + else + { + //Serial.println("ugh"); + return File(); + } } - } - //Serial.println("nothing"); - return File(); + //Serial.println("nothing"); + return File(); } -void File::rewindDirectory(void) { - if (isDirectory()) - _file->rewind(); +void File::rewindDirectory(void) +{ + if (isDirectory()) + { + _file->rewind(); + } } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD) diff --git a/libraries/SD/src/SD.h b/libraries/SD/src/SD.h index c998c8bbe6..17833891f2 100644 --- a/libraries/SD/src/SD.h +++ b/libraries/SD/src/SD.h @@ -1,16 +1,16 @@ /* - SD - a slightly more friendly wrapper for sdfatlib + SD - a slightly more friendly wrapper for sdfatlib - This library aims to expose a subset of SD card functionality - in the form of a higher level "wrapper" object. + This library aims to expose a subset of SD card functionality + in the form of a higher level "wrapper" object. - License: GNU General Public License V3 + License: GNU General Public License V3 (Because sdfatlib is licensed with this.) - (C) Copyright 2010 SparkFun Electronics + (C) Copyright 2010 SparkFun Electronics - */ +*/ #ifndef __SD_H__ #define __SD_H__ @@ -23,132 +23,176 @@ #define FILE_READ O_READ #define FILE_WRITE (O_READ | O_WRITE | O_CREAT) -class File : public Stream { - private: - char _name[13]; // our name - SdFile *_file; // underlying file pointer +class File : public Stream +{ +private: + char _name[13]; // our name + SdFile *_file; // underlying file pointer public: - File(SdFile f, const char *name); // wraps an underlying SdFile - File(void); // 'empty' constructor - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - virtual int read(); - virtual size_t readBytes(char *buffer, size_t length); - virtual int peek(); - virtual int available(); - virtual void flush(); - int read(void *buf, uint16_t nbyte); - boolean seek(uint32_t pos); - uint32_t position(); - uint32_t size(); - void close(); - operator bool(); - char * name(); - - boolean isDirectory(void); - File openNextFile(uint8_t mode = O_RDONLY); - void rewindDirectory(void); - - template size_t write(T &src){ - uint8_t obuf[512]; - size_t doneLen = 0; - size_t sentLen; - int i; - - while (src.available() > 512){ - src.read(obuf, 512); - sentLen = write(obuf, 512); - doneLen = doneLen + sentLen; - if(sentLen != 512){ + File(SdFile f, const char *name); // wraps an underlying SdFile + File(void); // 'empty' constructor + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual int read(); + virtual size_t readBytes(char *buffer, size_t length); + virtual int peek(); + virtual int available(); + virtual void flush(); + int read(void *buf, uint16_t nbyte); + boolean seek(uint32_t pos); + uint32_t position(); + uint32_t size(); + void close(); + operator bool(); + char * name(); + + boolean isDirectory(void); + File openNextFile(uint8_t mode = O_RDONLY); + void rewindDirectory(void); + + template size_t write(T &src) + { + uint8_t obuf[512]; + size_t doneLen = 0; + size_t sentLen; + int i; + + while (src.available() > 512) + { + src.read(obuf, 512); + sentLen = write(obuf, 512); + doneLen = doneLen + sentLen; + if (sentLen != 512) + { + 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; + + using Print::write; }; -class SDClass { +class SDClass +{ private: - // These are required for initialisation and use of sdfatlib - Sd2Card card; - SdVolume volume; - SdFile root; - - // my quick&dirty iterator, should be replaced - SdFile getParentDir(const char *filepath, int *indx); + // These are required for initialisation and use of sdfatlib + Sd2Card card; + SdVolume volume; + SdFile root; + + // my quick&dirty iterator, should be replaced + SdFile getParentDir(const char *filepath, int *indx); public: - // This needs to be called to set up the connection to the SD card - // before other methods are used. - boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN, uint32_t speed = SPI_HALF_SPEED); + // This needs to be called to set up the connection to the SD card + // before other methods are used. + boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN, uint32_t speed = SPI_HALF_SPEED); + + /* + In the following sequence: + //Insert SD Card A + SD.begin() + //do operations + //remove card A + //insert SD card B + SD.end() + + It is possible that card A becomes corrupted due to removal before calling SD.end(). + It is possible that card B becomes corrupted if there were ops pending for card A at the time SD.end() is called. + + Call SD.end() or SD.end(true) to shut everything down. + Call SD.end(false) to shut everything but the SPI object down. + */ + void end(bool endSPI = true); + + + // Open the specified file/directory with the supplied mode (e.g. read or + // write, etc). Returns a File object for interacting with the file. + // Note that currently only one file can be open at a time. + File open(const char *filename, uint8_t mode = FILE_READ); + File open(const String &filename, uint8_t mode = FILE_READ) + { + return open(filename.c_str(), mode); + } -/* - In the following sequence: - //Insert SD Card A - SD.begin() - //do operations - //remove card A - //insert SD card B - SD.end() - - It is possible that card A becomes corrupted due to removal before calling SD.end(). - It is possible that card B becomes corrupted if there were ops pending for card A at the time SD.end() is called. - - Call SD.end() or SD.end(true) to shut everything down. - Call SD.end(false) to shut everything but the SPI object down. - */ - void end(bool endSPI = true); - - - // Open the specified file/directory with the supplied mode (e.g. read or - // write, etc). Returns a File object for interacting with the file. - // Note that currently only one file can be open at a time. - File open(const char *filename, uint8_t mode = FILE_READ); - File open(const String &filename, uint8_t mode = FILE_READ) { return open( filename.c_str(), mode ); } - - // Methods to determine if the requested file path exists. - boolean exists(const char *filepath); - boolean exists(const String &filepath) { return exists(filepath.c_str()); } - - // Create the requested directory heirarchy--if intermediate directories - // do not exist they will be created. - boolean mkdir(const char *filepath); - boolean mkdir(const String &filepath) { return mkdir(filepath.c_str()); } - - // Delete the file. - boolean remove(const char *filepath); - boolean remove(const String &filepath) { return remove(filepath.c_str()); } - - boolean rmdir(const char *filepath); - boolean rmdir(const String &filepath) { return rmdir(filepath.c_str()); } - - uint8_t type(){ return card.type(); } - uint8_t fatType(){ return volume.fatType(); } - size_t blocksPerCluster(){ return volume.blocksPerCluster(); } - size_t totalClusters(){ return volume.clusterCount(); } - size_t blockSize(){ return (size_t)0x200; } - size_t totalBlocks(){ return (totalClusters() / blocksPerCluster()); } - size_t clusterSize(){ return blocksPerCluster() * blockSize(); } - size_t size(){ return (clusterSize() * totalClusters()); } + // Methods to determine if the requested file path exists. + boolean exists(const char *filepath); + boolean exists(const String &filepath) + { + return exists(filepath.c_str()); + } + + // Create the requested directory heirarchy--if intermediate directories + // do not exist they will be created. + boolean mkdir(const char *filepath); + boolean mkdir(const String &filepath) + { + return mkdir(filepath.c_str()); + } + + // Delete the file. + boolean remove(const char *filepath); + boolean remove(const String &filepath) + { + return remove(filepath.c_str()); + } + + boolean rmdir(const char *filepath); + boolean rmdir(const String &filepath) + { + return rmdir(filepath.c_str()); + } + + uint8_t type() + { + return card.type(); + } + uint8_t fatType() + { + return volume.fatType(); + } + size_t blocksPerCluster() + { + return volume.blocksPerCluster(); + } + size_t totalClusters() + { + return volume.clusterCount(); + } + size_t blockSize() + { + return (size_t)0x200; + } + size_t totalBlocks() + { + return (totalClusters() / blocksPerCluster()); + } + size_t clusterSize() + { + return blocksPerCluster() * blockSize(); + } + size_t size() + { + return (clusterSize() * totalClusters()); + } private: - // This is used to determine the mode used to open a file - // it's here because it's the easiest place to pass the - // information through the directory walking function. But - // it's probably not the best place for it. - // It shouldn't be set directly--it is set via the parameters to `open`. - int fileOpenMode; - - friend class File; - friend boolean callback_openPath(SdFile&, char *, boolean, void *); + // This is used to determine the mode used to open a file + // it's here because it's the easiest place to pass the + // information through the directory walking function. But + // it's probably not the best place for it. + // It shouldn't be set directly--it is set via the parameters to `open`. + int fileOpenMode; + + friend class File; + friend boolean callback_openPath(SdFile&, char *, boolean, void *); }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD) diff --git a/libraries/SD/src/utility/FatStructs.h b/libraries/SD/src/utility/FatStructs.h index 8a2d9ebcc1..de10f0ede5 100644 --- a/libraries/SD/src/utility/FatStructs.h +++ b/libraries/SD/src/utility/FatStructs.h @@ -1,418 +1,427 @@ -/* Arduino SdFat Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ -#ifndef FatStructs_h -#define FatStructs_h -/** - * \file - * FAT file structures - */ -/* - * mostly from Microsoft document fatgen103.doc - * http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx - */ -//------------------------------------------------------------------------------ -/** Value for byte 510 of boot block or MBR */ -uint8_t const BOOTSIG0 = 0X55; -/** Value for byte 511 of boot block or MBR */ -uint8_t const BOOTSIG1 = 0XAA; -//------------------------------------------------------------------------------ -/** - * \struct partitionTable - * \brief MBR partition table entry - * - * A partition table entry for a MBR formatted storage device. - * The MBR partition table has four entries. - */ -struct partitionTable { - /** - * Boot Indicator . Indicates whether the volume is the active - * partition. Legal values include: 0X00. Do not use for booting. - * 0X80 Active partition. - */ - uint8_t boot; - /** - * Head part of Cylinder-head-sector address of the first block in - * the partition. Legal values are 0-255. Only used in old PC BIOS. - */ - uint8_t beginHead; - /** - * Sector part of Cylinder-head-sector address of the first block in - * the partition. Legal values are 1-63. Only used in old PC BIOS. - */ - unsigned beginSector : 6; - /** High bits cylinder for first block in partition. */ - unsigned beginCylinderHigh : 2; - /** - * Combine beginCylinderLow with beginCylinderHigh. Legal values - * are 0-1023. Only used in old PC BIOS. - */ - uint8_t beginCylinderLow; - /** - * Partition type. See defines that begin with PART_TYPE_ for - * some Microsoft partition types. - */ - uint8_t type; - /** - * head part of cylinder-head-sector address of the last sector in the - * partition. Legal values are 0-255. Only used in old PC BIOS. - */ - uint8_t endHead; - /** - * Sector part of cylinder-head-sector address of the last sector in - * the partition. Legal values are 1-63. Only used in old PC BIOS. - */ - unsigned endSector : 6; - /** High bits of end cylinder */ - unsigned endCylinderHigh : 2; - /** - * Combine endCylinderLow with endCylinderHigh. Legal values - * are 0-1023. Only used in old PC BIOS. - */ - uint8_t endCylinderLow; - /** Logical block address of the first block in the partition. */ - uint32_t firstSector; - /** Length of the partition, in blocks. */ - uint32_t totalSectors; -} __attribute__((packed)); -/** Type name for partitionTable */ -typedef struct partitionTable part_t; -//------------------------------------------------------------------------------ -/** - * \struct masterBootRecord - * - * \brief Master Boot Record - * - * The first block of a storage device that is formatted with a MBR. - */ -struct masterBootRecord { - /** Code Area for master boot program. */ - uint8_t codeArea[440]; - /** Optional WindowsNT disk signature. May contain more boot code. */ - uint32_t diskSignature; - /** Usually zero but may be more boot code. */ - uint16_t usuallyZero; - /** Partition tables. */ - part_t part[4]; - /** First MBR signature byte. Must be 0X55 */ - uint8_t mbrSig0; - /** Second MBR signature byte. Must be 0XAA */ - uint8_t mbrSig1; -} __attribute__((packed)); -/** Type name for masterBootRecord */ -typedef struct masterBootRecord mbr_t; -//------------------------------------------------------------------------------ -/** - * \struct biosParmBlock - * - * \brief BIOS parameter block - * - * The BIOS parameter block describes the physical layout of a FAT volume. - */ -struct biosParmBlock { - /** - * Count of bytes per sector. This value may take on only the - * following values: 512, 1024, 2048 or 4096 - */ - uint16_t bytesPerSector; - /** - * Number of sectors per allocation unit. This value must be a - * power of 2 that is greater than 0. The legal values are - * 1, 2, 4, 8, 16, 32, 64, and 128. - */ - uint8_t sectorsPerCluster; - /** - * Number of sectors before the first FAT. - * This value must not be zero. - */ - uint16_t reservedSectorCount; - /** The count of FAT data structures on the volume. This field should - * always contain the value 2 for any FAT volume of any type. - */ - uint8_t fatCount; - /** - * For FAT12 and FAT16 volumes, this field contains the count of - * 32-byte directory entries in the root directory. For FAT32 volumes, - * this field must be set to 0. For FAT12 and FAT16 volumes, this - * value should always specify a count that when multiplied by 32 - * results in a multiple of bytesPerSector. FAT16 volumes should - * use the value 512. - */ - uint16_t rootDirEntryCount; - /** - * This field is the old 16-bit total count of sectors on the volume. - * This count includes the count of all sectors in all four regions - * of the volume. This field can be 0; if it is 0, then totalSectors32 - * must be non-zero. For FAT32 volumes, this field must be 0. For - * FAT12 and FAT16 volumes, this field contains the sector count, and - * totalSectors32 is 0 if the total sector count fits - * (is less than 0x10000). - */ - uint16_t totalSectors16; - /** - * This dates back to the old MS-DOS 1.x media determination and is - * no longer usually used for anything. 0xF8 is the standard value - * for fixed (non-removable) media. For removable media, 0xF0 is - * frequently used. Legal values are 0xF0 or 0xF8-0xFF. - */ - uint8_t mediaType; - /** - * Count of sectors occupied by one FAT on FAT12/FAT16 volumes. - * On FAT32 volumes this field must be 0, and sectorsPerFat32 - * contains the FAT size count. - */ - uint16_t sectorsPerFat16; - /** Sectors per track for interrupt 0x13. Not used otherwise. */ - uint16_t sectorsPerTrtack; - /** Number of heads for interrupt 0x13. Not used otherwise. */ - uint16_t headCount; - /** - * Count of hidden sectors preceding the partition that contains this - * FAT volume. This field is generally only relevant for media - * visible on interrupt 0x13. - */ - uint32_t hidddenSectors; - /** - * This field is the new 32-bit total count of sectors on the volume. - * This count includes the count of all sectors in all four regions - * of the volume. This field can be 0; if it is 0, then - * totalSectors16 must be non-zero. - */ - uint32_t totalSectors32; - /** - * Count of sectors occupied by one FAT on FAT32 volumes. - */ - uint32_t sectorsPerFat32; - /** - * This field is only defined for FAT32 media and does not exist on - * FAT12 and FAT16 media. - * Bits 0-3 -- Zero-based number of active FAT. - * Only valid if mirroring is disabled. - * Bits 4-6 -- Reserved. - * Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. - * -- 1 means only one FAT is active; it is the one referenced in bits 0-3. - * Bits 8-15 -- Reserved. - */ - uint16_t fat32Flags; - /** - * FAT32 version. High byte is major revision number. - * Low byte is minor revision number. Only 0.0 define. - */ - uint16_t fat32Version; - /** - * Cluster number of the first cluster of the root directory for FAT32. - * This usually 2 but not required to be 2. - */ - uint32_t fat32RootCluster; - /** - * Sector number of FSINFO structure in the reserved area of the - * FAT32 volume. Usually 1. - */ - uint16_t fat32FSInfo; - /** - * If non-zero, indicates the sector number in the reserved area - * of the volume of a copy of the boot record. Usually 6. - * No value other than 6 is recommended. - */ - uint16_t fat32BackBootBlock; - /** - * Reserved for future expansion. Code that formats FAT32 volumes - * should always set all of the bytes of this field to 0. - */ - uint8_t fat32Reserved[12]; -} __attribute__((packed)); -/** Type name for biosParmBlock */ -typedef struct biosParmBlock bpb_t; -//------------------------------------------------------------------------------ -/** - * \struct fat32BootSector - * - * \brief Boot sector for a FAT16 or FAT32 volume. - * - */ -struct fat32BootSector { - /** X86 jmp to boot program */ - uint8_t jmpToBootCode[3]; - /** informational only - don't depend on it */ - char oemName[8]; - /** BIOS Parameter Block */ - bpb_t bpb; - /** for int0x13 use value 0X80 for hard drive */ - uint8_t driveNumber; - /** used by Windows NT - should be zero for FAT */ - uint8_t reserved1; - /** 0X29 if next three fields are valid */ - uint8_t bootSignature; - /** usually generated by combining date and time */ - uint32_t volumeSerialNumber; - /** should match volume label in root dir */ - char volumeLabel[11]; - /** informational only - don't depend on it */ - char fileSystemType[8]; - /** X86 boot code */ - uint8_t bootCode[420]; - /** must be 0X55 */ - uint8_t bootSectorSig0; - /** must be 0XAA */ - uint8_t bootSectorSig1; -} __attribute__((packed)); -//------------------------------------------------------------------------------ -// End Of Chain values for FAT entries -/** FAT16 end of chain value used by Microsoft. */ -uint16_t const FAT16EOC = 0XFFFF; -/** Minimum value for FAT16 EOC. Use to test for EOC. */ -uint16_t const FAT16EOC_MIN = 0XFFF8; -/** FAT32 end of chain value used by Microsoft. */ -uint32_t const FAT32EOC = 0X0FFFFFFF; -/** Minimum value for FAT32 EOC. Use to test for EOC. */ -uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; -/** Mask a for FAT32 entry. Entries are 28 bits. */ -uint32_t const FAT32MASK = 0X0FFFFFFF; - -/** Type name for fat32BootSector */ -typedef struct fat32BootSector fbs_t; -//------------------------------------------------------------------------------ -/** - * \struct directoryEntry - * \brief FAT short directory entry - * - * Short means short 8.3 name, not the entry size. - * - * Date Format. A FAT directory entry date stamp is a 16-bit field that is - * basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the - * format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the - * 16-bit word): - * - * Bits 9-15: Count of years from 1980, valid value range 0-127 - * inclusive (1980-2107). - * - * Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive. - * - * Bits 0-4: Day of month, valid value range 1-31 inclusive. - * - * Time Format. A FAT directory entry time stamp is a 16-bit field that has - * a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the - * 16-bit word, bit 15 is the MSB of the 16-bit word). - * - * Bits 11-15: Hours, valid value range 0-23 inclusive. - * - * Bits 5-10: Minutes, valid value range 0-59 inclusive. - * - * Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds). - * - * The valid time range is from Midnight 00:00:00 to 23:59:58. - */ -struct directoryEntry { - /** - * Short 8.3 name. - * The first eight bytes contain the file name with blank fill. - * The last three bytes contain the file extension with blank fill. - */ - uint8_t name[11]; - /** Entry attributes. - * - * The upper two bits of the attribute byte are reserved and should - * always be set to 0 when a file is created and never modified or - * looked at after that. See defines that begin with DIR_ATT_. - */ - uint8_t attributes; - /** - * Reserved for use by Windows NT. Set value to 0 when a file is - * created and never modify or look at it after that. - */ - uint8_t reservedNT; - /** - * The granularity of the seconds part of creationTime is 2 seconds - * so this field is a count of tenths of a second and its valid - * value range is 0-199 inclusive. (WHG note - seems to be hundredths) - */ - uint8_t creationTimeTenths; - /** Time file was created. */ - uint16_t creationTime; - /** Date file was created. */ - uint16_t creationDate; - /** - * Last access date. Note that there is no last access time, only - * a date. This is the date of last read or write. In the case of - * a write, this should be set to the same date as lastWriteDate. - */ - uint16_t lastAccessDate; - /** - * High word of this entry's first cluster number (always 0 for a - * FAT12 or FAT16 volume). - */ - uint16_t firstClusterHigh; - /** Time of last write. File creation is considered a write. */ - uint16_t lastWriteTime; - /** Date of last write. File creation is considered a write. */ - uint16_t lastWriteDate; - /** Low word of this entry's first cluster number. */ - uint16_t firstClusterLow; - /** 32-bit unsigned holding this file's size in bytes. */ - uint32_t fileSize; -} __attribute__((packed)); -//------------------------------------------------------------------------------ -// Definitions for directory entries -// -/** Type name for directoryEntry */ -typedef struct directoryEntry dir_t; -/** escape for name[0] = 0XE5 */ -uint8_t const DIR_NAME_0XE5 = 0X05; -/** name[0] value for entry that is free after being "deleted" */ -uint8_t const DIR_NAME_DELETED = 0XE5; -/** name[0] value for entry that is free and no allocated entries follow */ -uint8_t const DIR_NAME_FREE = 0X00; -/** file is read-only */ -uint8_t const DIR_ATT_READ_ONLY = 0X01; -/** File should hidden in directory listings */ -uint8_t const DIR_ATT_HIDDEN = 0X02; -/** Entry is for a system file */ -uint8_t const DIR_ATT_SYSTEM = 0X04; -/** Directory entry contains the volume label */ -uint8_t const DIR_ATT_VOLUME_ID = 0X08; -/** Entry is for a directory */ -uint8_t const DIR_ATT_DIRECTORY = 0X10; -/** Old DOS archive bit for backup support */ -uint8_t const DIR_ATT_ARCHIVE = 0X20; -/** Test value for long name entry. Test is - (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ -uint8_t const DIR_ATT_LONG_NAME = 0X0F; -/** Test mask for long name entry */ -uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; -/** defined attribute bits */ -uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; -/** Directory entry is part of a long name */ -static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { - return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME; -} -/** Mask for file/subdirectory tests */ -uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); -/** Directory entry is for a file */ -static inline uint8_t DIR_IS_FILE(const dir_t* dir) { - return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0; -} -/** Directory entry is for a subdirectory */ -static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) { - return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; -} -/** Directory entry is for a file or subdirectory */ -static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { - return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; -} -#endif // FatStructs_h +/* Arduino SdFat Library + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ +#ifndef FatStructs_h +#define FatStructs_h +/** + \file + FAT file structures +*/ +/* + mostly from Microsoft document fatgen103.doc + http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx +*/ +//------------------------------------------------------------------------------ +/** Value for byte 510 of boot block or MBR */ +uint8_t const BOOTSIG0 = 0X55; +/** Value for byte 511 of boot block or MBR */ +uint8_t const BOOTSIG1 = 0XAA; +//------------------------------------------------------------------------------ +/** + \struct partitionTable + \brief MBR partition table entry + + A partition table entry for a MBR formatted storage device. + The MBR partition table has four entries. +*/ +struct partitionTable +{ + /** + Boot Indicator . Indicates whether the volume is the active + partition. Legal values include: 0X00. Do not use for booting. + 0X80 Active partition. + */ + uint8_t boot; + /** + Head part of Cylinder-head-sector address of the first block in + the partition. Legal values are 0-255. Only used in old PC BIOS. + */ + uint8_t beginHead; + /** + Sector part of Cylinder-head-sector address of the first block in + the partition. Legal values are 1-63. Only used in old PC BIOS. + */ + unsigned beginSector : 6; + /** High bits cylinder for first block in partition. */ + unsigned beginCylinderHigh : 2; + /** + Combine beginCylinderLow with beginCylinderHigh. Legal values + are 0-1023. Only used in old PC BIOS. + */ + uint8_t beginCylinderLow; + /** + Partition type. See defines that begin with PART_TYPE_ for + some Microsoft partition types. + */ + uint8_t type; + /** + head part of cylinder-head-sector address of the last sector in the + partition. Legal values are 0-255. Only used in old PC BIOS. + */ + uint8_t endHead; + /** + Sector part of cylinder-head-sector address of the last sector in + the partition. Legal values are 1-63. Only used in old PC BIOS. + */ + unsigned endSector : 6; + /** High bits of end cylinder */ + unsigned endCylinderHigh : 2; + /** + Combine endCylinderLow with endCylinderHigh. Legal values + are 0-1023. Only used in old PC BIOS. + */ + uint8_t endCylinderLow; + /** Logical block address of the first block in the partition. */ + uint32_t firstSector; + /** Length of the partition, in blocks. */ + uint32_t totalSectors; +} __attribute__((packed)); +/** Type name for partitionTable */ +typedef struct partitionTable part_t; +//------------------------------------------------------------------------------ +/** + \struct masterBootRecord + + \brief Master Boot Record + + The first block of a storage device that is formatted with a MBR. +*/ +struct masterBootRecord +{ + /** Code Area for master boot program. */ + uint8_t codeArea[440]; + /** Optional WindowsNT disk signature. May contain more boot code. */ + uint32_t diskSignature; + /** Usually zero but may be more boot code. */ + uint16_t usuallyZero; + /** Partition tables. */ + part_t part[4]; + /** First MBR signature byte. Must be 0X55 */ + uint8_t mbrSig0; + /** Second MBR signature byte. Must be 0XAA */ + uint8_t mbrSig1; +} __attribute__((packed)); +/** Type name for masterBootRecord */ +typedef struct masterBootRecord mbr_t; +//------------------------------------------------------------------------------ +/** + \struct biosParmBlock + + \brief BIOS parameter block + + The BIOS parameter block describes the physical layout of a FAT volume. +*/ +struct biosParmBlock +{ + /** + Count of bytes per sector. This value may take on only the + following values: 512, 1024, 2048 or 4096 + */ + uint16_t bytesPerSector; + /** + Number of sectors per allocation unit. This value must be a + power of 2 that is greater than 0. The legal values are + 1, 2, 4, 8, 16, 32, 64, and 128. + */ + uint8_t sectorsPerCluster; + /** + Number of sectors before the first FAT. + This value must not be zero. + */ + uint16_t reservedSectorCount; + /** The count of FAT data structures on the volume. This field should + always contain the value 2 for any FAT volume of any type. + */ + uint8_t fatCount; + /** + For FAT12 and FAT16 volumes, this field contains the count of + 32-byte directory entries in the root directory. For FAT32 volumes, + this field must be set to 0. For FAT12 and FAT16 volumes, this + value should always specify a count that when multiplied by 32 + results in a multiple of bytesPerSector. FAT16 volumes should + use the value 512. + */ + uint16_t rootDirEntryCount; + /** + This field is the old 16-bit total count of sectors on the volume. + This count includes the count of all sectors in all four regions + of the volume. This field can be 0; if it is 0, then totalSectors32 + must be non-zero. For FAT32 volumes, this field must be 0. For + FAT12 and FAT16 volumes, this field contains the sector count, and + totalSectors32 is 0 if the total sector count fits + (is less than 0x10000). + */ + uint16_t totalSectors16; + /** + This dates back to the old MS-DOS 1.x media determination and is + no longer usually used for anything. 0xF8 is the standard value + for fixed (non-removable) media. For removable media, 0xF0 is + frequently used. Legal values are 0xF0 or 0xF8-0xFF. + */ + uint8_t mediaType; + /** + Count of sectors occupied by one FAT on FAT12/FAT16 volumes. + On FAT32 volumes this field must be 0, and sectorsPerFat32 + contains the FAT size count. + */ + uint16_t sectorsPerFat16; + /** Sectors per track for interrupt 0x13. Not used otherwise. */ + uint16_t sectorsPerTrtack; + /** Number of heads for interrupt 0x13. Not used otherwise. */ + uint16_t headCount; + /** + Count of hidden sectors preceding the partition that contains this + FAT volume. This field is generally only relevant for media + visible on interrupt 0x13. + */ + uint32_t hidddenSectors; + /** + This field is the new 32-bit total count of sectors on the volume. + This count includes the count of all sectors in all four regions + of the volume. This field can be 0; if it is 0, then + totalSectors16 must be non-zero. + */ + uint32_t totalSectors32; + /** + Count of sectors occupied by one FAT on FAT32 volumes. + */ + uint32_t sectorsPerFat32; + /** + This field is only defined for FAT32 media and does not exist on + FAT12 and FAT16 media. + Bits 0-3 -- Zero-based number of active FAT. + Only valid if mirroring is disabled. + Bits 4-6 -- Reserved. + Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. + -- 1 means only one FAT is active; it is the one referenced in bits 0-3. + Bits 8-15 -- Reserved. + */ + uint16_t fat32Flags; + /** + FAT32 version. High byte is major revision number. + Low byte is minor revision number. Only 0.0 define. + */ + uint16_t fat32Version; + /** + Cluster number of the first cluster of the root directory for FAT32. + This usually 2 but not required to be 2. + */ + uint32_t fat32RootCluster; + /** + Sector number of FSINFO structure in the reserved area of the + FAT32 volume. Usually 1. + */ + uint16_t fat32FSInfo; + /** + If non-zero, indicates the sector number in the reserved area + of the volume of a copy of the boot record. Usually 6. + No value other than 6 is recommended. + */ + uint16_t fat32BackBootBlock; + /** + Reserved for future expansion. Code that formats FAT32 volumes + should always set all of the bytes of this field to 0. + */ + uint8_t fat32Reserved[12]; +} __attribute__((packed)); +/** Type name for biosParmBlock */ +typedef struct biosParmBlock bpb_t; +//------------------------------------------------------------------------------ +/** + \struct fat32BootSector + + \brief Boot sector for a FAT16 or FAT32 volume. + +*/ +struct fat32BootSector +{ + /** X86 jmp to boot program */ + uint8_t jmpToBootCode[3]; + /** informational only - don't depend on it */ + char oemName[8]; + /** BIOS Parameter Block */ + bpb_t bpb; + /** for int0x13 use value 0X80 for hard drive */ + uint8_t driveNumber; + /** used by Windows NT - should be zero for FAT */ + uint8_t reserved1; + /** 0X29 if next three fields are valid */ + uint8_t bootSignature; + /** usually generated by combining date and time */ + uint32_t volumeSerialNumber; + /** should match volume label in root dir */ + char volumeLabel[11]; + /** informational only - don't depend on it */ + char fileSystemType[8]; + /** X86 boot code */ + uint8_t bootCode[420]; + /** must be 0X55 */ + uint8_t bootSectorSig0; + /** must be 0XAA */ + uint8_t bootSectorSig1; +} __attribute__((packed)); +//------------------------------------------------------------------------------ +// End Of Chain values for FAT entries +/** FAT16 end of chain value used by Microsoft. */ +uint16_t const FAT16EOC = 0XFFFF; +/** Minimum value for FAT16 EOC. Use to test for EOC. */ +uint16_t const FAT16EOC_MIN = 0XFFF8; +/** FAT32 end of chain value used by Microsoft. */ +uint32_t const FAT32EOC = 0X0FFFFFFF; +/** Minimum value for FAT32 EOC. Use to test for EOC. */ +uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; +/** Mask a for FAT32 entry. Entries are 28 bits. */ +uint32_t const FAT32MASK = 0X0FFFFFFF; + +/** Type name for fat32BootSector */ +typedef struct fat32BootSector fbs_t; +//------------------------------------------------------------------------------ +/** + \struct directoryEntry + \brief FAT short directory entry + + Short means short 8.3 name, not the entry size. + + Date Format. A FAT directory entry date stamp is a 16-bit field that is + basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the + format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the + 16-bit word): + + Bits 9-15: Count of years from 1980, valid value range 0-127 + inclusive (1980-2107). + + Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive. + + Bits 0-4: Day of month, valid value range 1-31 inclusive. + + Time Format. A FAT directory entry time stamp is a 16-bit field that has + a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the + 16-bit word, bit 15 is the MSB of the 16-bit word). + + Bits 11-15: Hours, valid value range 0-23 inclusive. + + Bits 5-10: Minutes, valid value range 0-59 inclusive. + + Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds). + + The valid time range is from Midnight 00:00:00 to 23:59:58. +*/ +struct directoryEntry +{ + /** + Short 8.3 name. + The first eight bytes contain the file name with blank fill. + The last three bytes contain the file extension with blank fill. + */ + uint8_t name[11]; + /** Entry attributes. + + The upper two bits of the attribute byte are reserved and should + always be set to 0 when a file is created and never modified or + looked at after that. See defines that begin with DIR_ATT_. + */ + uint8_t attributes; + /** + Reserved for use by Windows NT. Set value to 0 when a file is + created and never modify or look at it after that. + */ + uint8_t reservedNT; + /** + The granularity of the seconds part of creationTime is 2 seconds + so this field is a count of tenths of a second and its valid + value range is 0-199 inclusive. (WHG note - seems to be hundredths) + */ + uint8_t creationTimeTenths; + /** Time file was created. */ + uint16_t creationTime; + /** Date file was created. */ + uint16_t creationDate; + /** + Last access date. Note that there is no last access time, only + a date. This is the date of last read or write. In the case of + a write, this should be set to the same date as lastWriteDate. + */ + uint16_t lastAccessDate; + /** + High word of this entry's first cluster number (always 0 for a + FAT12 or FAT16 volume). + */ + uint16_t firstClusterHigh; + /** Time of last write. File creation is considered a write. */ + uint16_t lastWriteTime; + /** Date of last write. File creation is considered a write. */ + uint16_t lastWriteDate; + /** Low word of this entry's first cluster number. */ + uint16_t firstClusterLow; + /** 32-bit unsigned holding this file's size in bytes. */ + uint32_t fileSize; +} __attribute__((packed)); +//------------------------------------------------------------------------------ +// Definitions for directory entries +// +/** Type name for directoryEntry */ +typedef struct directoryEntry dir_t; +/** escape for name[0] = 0XE5 */ +uint8_t const DIR_NAME_0XE5 = 0X05; +/** name[0] value for entry that is free after being "deleted" */ +uint8_t const DIR_NAME_DELETED = 0XE5; +/** name[0] value for entry that is free and no allocated entries follow */ +uint8_t const DIR_NAME_FREE = 0X00; +/** file is read-only */ +uint8_t const DIR_ATT_READ_ONLY = 0X01; +/** File should hidden in directory listings */ +uint8_t const DIR_ATT_HIDDEN = 0X02; +/** Entry is for a system file */ +uint8_t const DIR_ATT_SYSTEM = 0X04; +/** Directory entry contains the volume label */ +uint8_t const DIR_ATT_VOLUME_ID = 0X08; +/** Entry is for a directory */ +uint8_t const DIR_ATT_DIRECTORY = 0X10; +/** Old DOS archive bit for backup support */ +uint8_t const DIR_ATT_ARCHIVE = 0X20; +/** Test value for long name entry. Test is + (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ +uint8_t const DIR_ATT_LONG_NAME = 0X0F; +/** Test mask for long name entry */ +uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; +/** defined attribute bits */ +uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; +/** Directory entry is part of a long name */ +static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) +{ + return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME; +} +/** Mask for file/subdirectory tests */ +uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); +/** Directory entry is for a file */ +static inline uint8_t DIR_IS_FILE(const dir_t* dir) +{ + return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0; +} +/** Directory entry is for a subdirectory */ +static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) +{ + return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; +} +/** Directory entry is for a file or subdirectory */ +static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) +{ + return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; +} +#endif // FatStructs_h diff --git a/libraries/SD/src/utility/Sd2Card.cpp b/libraries/SD/src/utility/Sd2Card.cpp index dd2db4bf63..e1a792e7fd 100644 --- a/libraries/SD/src/utility/Sd2Card.cpp +++ b/libraries/SD/src/utility/Sd2Card.cpp @@ -1,780 +1,922 @@ -/* Arduino Sd2Card Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino Sd2Card Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino Sd2Card Library. If not, see - * . - */ -#define USE_SPI_LIB -#include -#include "Sd2Card.h" -//------------------------------------------------------------------------------ -#ifndef SOFTWARE_SPI -#ifdef USE_SPI_LIB -#include -static SPISettings settings; -#endif -// functions for hardware SPI -/** Send a byte to the card */ -static void spiSend(uint8_t b) { -#ifndef USE_SPI_LIB - SPDR = b; - while (!(SPSR & (1 << SPIF))) - ; -#else -#ifdef ESP8266 - SPI.write(b); -#else - SPI.transfer(b); -#endif -#endif -} -/** Receive a byte from the card */ -static uint8_t spiRec(void) { -#ifndef USE_SPI_LIB - spiSend(0XFF); - return SPDR; -#else - return SPI.transfer(0xFF); -#endif -} -#else // SOFTWARE_SPI -//------------------------------------------------------------------------------ -/** nop to tune soft SPI timing */ -#define nop asm volatile ("nop\n\t") -//------------------------------------------------------------------------------ -/** Soft SPI receive */ -uint8_t spiRec(void) { - uint8_t data = 0; - // no interrupts during byte receive - about 8 us - cli(); - // output pin high - like sending 0XFF - fastDigitalWrite(SPI_MOSI_PIN, HIGH); - - for (uint8_t i = 0; i < 8; i++) { - fastDigitalWrite(SPI_SCK_PIN, HIGH); - - // adjust so SCK is nice - nop; - nop; - - data <<= 1; - - if (fastDigitalRead(SPI_MISO_PIN)) data |= 1; - - fastDigitalWrite(SPI_SCK_PIN, LOW); - } - // enable interrupts - sei(); - return data; -} -//------------------------------------------------------------------------------ -/** Soft SPI send */ -void spiSend(uint8_t data) { - // no interrupts during byte send - about 8 us - cli(); - for (uint8_t i = 0; i < 8; i++) { - fastDigitalWrite(SPI_SCK_PIN, LOW); - - fastDigitalWrite(SPI_MOSI_PIN, data & 0X80); - - data <<= 1; - - fastDigitalWrite(SPI_SCK_PIN, HIGH); - } - // hold SCK high for a few ns - nop; - nop; - nop; - nop; - - fastDigitalWrite(SPI_SCK_PIN, LOW); - // enable interrupts - sei(); -} -#endif // SOFTWARE_SPI -//------------------------------------------------------------------------------ -// send command and return error code. Return zero for OK -uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { - // end read if in partialBlockRead mode - readEnd(); - - // select card - chipSelectLow(); - - // wait up to 300 ms if busy - waitNotBusy(300); - - // send command - spiSend(cmd | 0x40); - -#ifdef ESP8266 - // send argument - SPI.write32(arg, true); -#else - // send argument - for (int8_t s = 24; s >= 0; s -= 8) spiSend(arg >> s); -#endif - - - // send CRC - uint8_t crc = 0xFF; - if (cmd == CMD0) crc = 0x95; // correct crc for CMD0 with arg 0 - if (cmd == CMD8) crc = 0x87; // correct crc for CMD8 with arg 0X1AA - spiSend(crc); - - // wait for response - for (uint8_t i = 0; ((status_ = spiRec()) & 0x80) && i != 0xFF; i++) - ; - #ifdef ESP8266 - optimistic_yield(10000); - #endif - return status_; -} -//------------------------------------------------------------------------------ -/** - * Determine the size of an SD flash memory card. - * - * \return The number of 512 byte data blocks in the card - * or zero if an error occurs. - */ -uint32_t Sd2Card::cardSize(void) { - csd_t csd; - if (!readCSD(&csd)) return 0; - if (csd.v1.csd_ver == 0) { - uint8_t read_bl_len = csd.v1.read_bl_len; - uint16_t c_size = (csd.v1.c_size_high << 10) - | (csd.v1.c_size_mid << 2) | csd.v1.c_size_low; - uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1) - | csd.v1.c_size_mult_low; - return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7); - } else if (csd.v2.csd_ver == 1) { - uint32_t c_size = ((uint32_t)csd.v2.c_size_high << 16) - | (csd.v2.c_size_mid << 8) | csd.v2.c_size_low; - return (c_size + 1) << 10; - } else { - error(SD_CARD_ERROR_BAD_CSD); - return 0; - } -} -//------------------------------------------------------------------------------ -static uint8_t chip_select_asserted = 0; - -void Sd2Card::chipSelectHigh(void) { - digitalWrite(chipSelectPin_, HIGH); -#ifdef USE_SPI_LIB - if (chip_select_asserted) { - chip_select_asserted = 0; - SPI.endTransaction(); - } -#endif -} -//------------------------------------------------------------------------------ -void Sd2Card::chipSelectLow(void) { -#ifdef USE_SPI_LIB - if (!chip_select_asserted) { - chip_select_asserted = 1; - SPI.beginTransaction(settings); - } -#endif - digitalWrite(chipSelectPin_, LOW); -} -//------------------------------------------------------------------------------ -/** Erase a range of blocks. - * - * \param[in] firstBlock The address of the first block in the range. - * \param[in] lastBlock The address of the last block in the range. - * - * \note This function requests the SD card to do a flash erase for a - * range of blocks. The data on the card after an erase operation is - * either 0 or 1, depends on the card vendor. The card must support - * single block erase. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ -uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { - if (!eraseSingleBlockEnable()) { - error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); - goto fail; - } - if (type_ != SD_CARD_TYPE_SDHC) { - firstBlock <<= 9; - lastBlock <<= 9; - } - if (cardCommand(CMD32, firstBlock) - || cardCommand(CMD33, lastBlock) - || cardCommand(CMD38, 0)) { - error(SD_CARD_ERROR_ERASE); - goto fail; - } - if (!waitNotBusy(SD_ERASE_TIMEOUT)) { - error(SD_CARD_ERROR_ERASE_TIMEOUT); - goto fail; - } - chipSelectHigh(); - return true; - - fail: - chipSelectHigh(); - return false; -} -//------------------------------------------------------------------------------ -/** Determine if card supports single block erase. - * - * \return The value one, true, is returned if single block erase is supported. - * The value zero, false, is returned if single block erase is not supported. - */ -uint8_t Sd2Card::eraseSingleBlockEnable(void) { - csd_t csd; - return readCSD(&csd) ? csd.v1.erase_blk_en : 0; -} -//------------------------------------------------------------------------------ -/** - * Initialize an SD flash memory card. - * - * \param[in] sckRateID SPI clock rate selector. See setSckRate(). - * \param[in] chipSelectPin SD chip select pin number. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. The reason for failure - * can be determined by calling errorCode() and errorData(). - */ -#ifdef ESP8266 -uint8_t Sd2Card::init(uint32_t sckRateID, uint8_t chipSelectPin) { -#else -uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { -#endif - errorCode_ = inBlock_ = partialBlockRead_ = type_ = 0; - chipSelectPin_ = chipSelectPin; - // 16-bit init start time allows over a minute - uint16_t t0 = (uint16_t)millis(); - uint32_t arg; - - // set pin modes - pinMode(chipSelectPin_, OUTPUT); - digitalWrite(chipSelectPin_, HIGH); -#ifndef USE_SPI_LIB - pinMode(SPI_MISO_PIN, INPUT); - pinMode(SPI_MOSI_PIN, OUTPUT); - pinMode(SPI_SCK_PIN, OUTPUT); -#endif - -#ifndef SOFTWARE_SPI -#ifndef USE_SPI_LIB - // SS must be in output mode even it is not chip select - pinMode(SS_PIN, OUTPUT); - digitalWrite(SS_PIN, HIGH); // disable any SPI device using hardware SS pin - // Enable SPI, Master, clock rate f_osc/128 - SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); - // clear double speed - SPSR &= ~(1 << SPI2X); -#else // USE_SPI_LIB - SPI.begin(); - settings = SPISettings(250000, MSBFIRST, SPI_MODE0); -#endif // USE_SPI_LIB -#endif // SOFTWARE_SPI - - // must supply min of 74 clock cycles with CS high. -#ifdef USE_SPI_LIB - SPI.beginTransaction(settings); -#endif - for (uint8_t i = 0; i < 10; i++) spiSend(0XFF); -#ifdef USE_SPI_LIB - SPI.endTransaction(); -#endif - - chipSelectLow(); - - // command to go idle in SPI mode - while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) { - if (((uint16_t)(millis() - t0)) > SD_INIT_TIMEOUT) { - error(SD_CARD_ERROR_CMD0); - goto fail; - } - } - // check SD version - if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) { - type(SD_CARD_TYPE_SD1); - } else { - // only need last byte of r7 response - for (uint8_t i = 0; i < 4; i++) status_ = spiRec(); - if (status_ != 0XAA) { - error(SD_CARD_ERROR_CMD8); - goto fail; - } - type(SD_CARD_TYPE_SD2); - } - // initialize card and send host supports SDHC if SD2 - arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0; - - while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) { - // check for timeout - if (((uint16_t)(millis() - t0)) > SD_INIT_TIMEOUT) { - error(SD_CARD_ERROR_ACMD41); - goto fail; - } - } - // if SD2 read OCR register to check for SDHC card - if (type() == SD_CARD_TYPE_SD2) { - if (cardCommand(CMD58, 0)) { - error(SD_CARD_ERROR_CMD58); - goto fail; - } - if ((spiRec() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC); - // discard rest of ocr - contains allowed voltage range - for (uint8_t i = 0; i < 3; i++) spiRec(); - } - chipSelectHigh(); - -#ifndef SOFTWARE_SPI - return setSckRate(sckRateID); -#else // SOFTWARE_SPI - return true; -#endif // SOFTWARE_SPI - - fail: - chipSelectHigh(); - return false; -} - -//------------------------------------------------------------------------------ -/** - * Shut down Sd2Card, which at this point means end SPI. - * - * \param[in] endSPI The value true (non-zero) or FALSE (zero). - - Call card.end() or card.end(true) to shut everything down. - Call card.end(false) to shut everything but the SPI object down. - */ -void Sd2Card::end(bool endSPI) -{ - if(endSPI) - SPI.end(); -} - - - -//------------------------------------------------------------------------------ -/** - * Enable or disable partial block reads. - * - * Enabling partial block reads improves performance by allowing a block - * to be read over the SPI bus as several sub-blocks. Errors may occur - * if the time between reads is too long since the SD card may timeout. - * The SPI SS line will be held low until the entire block is read or - * readEnd() is called. - * - * Use this for applications like the Adafruit Wave Shield. - * - * \param[in] value The value TRUE (non-zero) or FALSE (zero).) - */ -void Sd2Card::partialBlockRead(uint8_t value) { - readEnd(); - partialBlockRead_ = value; -} -//------------------------------------------------------------------------------ -/** - * Read a 512 byte block from an SD card device. - * - * \param[in] block Logical block to be read. - * \param[out] dst Pointer to the location that will receive the data. - - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ -uint8_t Sd2Card::readBlock(uint32_t block, uint8_t* dst) { - return readData(block, 0, 512, dst); -} -//------------------------------------------------------------------------------ -/** - * Read part of a 512 byte block from an SD card. - * - * \param[in] block Logical block to be read. - * \param[in] offset Number of bytes to skip at start of block - * \param[out] dst Pointer to the location that will receive the data. - * \param[in] count Number of bytes to read - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ -uint8_t Sd2Card::readData(uint32_t block, - uint16_t offset, uint16_t count, uint8_t* dst) { - if (count == 0) return true; - if ((count + offset) > 512) { - goto fail; - } - if (!inBlock_ || block != block_ || offset < offset_) { - block_ = block; - // use address if not SDHC card - if (type()!= SD_CARD_TYPE_SDHC) block <<= 9; - if (cardCommand(CMD17, block)) { - error(SD_CARD_ERROR_CMD17); - goto fail; - } - if (!waitStartBlock()) { - goto fail; - } - offset_ = 0; - inBlock_ = 1; - } - -#ifdef OPTIMIZE_HARDWARE_SPI - uint16_t n; - - // start first spi transfer - SPDR = 0XFF; - - // skip data before offset - for (;offset_ < offset; offset_++) { - while (!(SPSR & (1 << SPIF))) - ; - SPDR = 0XFF; - } - // transfer data - n = count - 1; - for (uint16_t i = 0; i < n; i++) { - while (!(SPSR & (1 << SPIF))) - ; - dst[i] = SPDR; - SPDR = 0XFF; - } - // wait for last byte - while (!(SPSR & (1 << SPIF))) - ; - dst[n] = SPDR; - -#else // OPTIMIZE_HARDWARE_SPI -#ifdef ESP8266 - // skip data before offset - SPI.transferBytes(NULL, NULL, offset_); - - // transfer data - SPI.transferBytes(NULL, dst, count); - -#else - // skip data before offset - for (;offset_ < offset; offset_++) { - spiRec(); - } - // transfer data - for (uint16_t i = 0; i < count; i++) { - dst[i] = spiRec(); - } -#endif -#endif // OPTIMIZE_HARDWARE_SPI - - offset_ += count; - if (!partialBlockRead_ || offset_ >= 512) { - // read rest of data, checksum and set chip select high - readEnd(); - } - return true; - - fail: - chipSelectHigh(); - return false; -} -//------------------------------------------------------------------------------ -/** Skip remaining data in a block when in partial block read mode. */ -void Sd2Card::readEnd(void) { - if (inBlock_) { - // skip data and crc -#ifdef OPTIMIZE_HARDWARE_SPI - // optimize skip for hardware - SPDR = 0XFF; - while (offset_++ < 513) { - while (!(SPSR & (1 << SPIF))) - ; - SPDR = 0XFF; - } - // wait for last crc byte - while (!(SPSR & (1 << SPIF))) - ; -#else // OPTIMIZE_HARDWARE_SPI -#ifdef ESP8266 - SPI.transferBytes(NULL, NULL, (514-offset_)); -#else - while (offset_++ < 514) spiRec(); -#endif -#endif // OPTIMIZE_HARDWARE_SPI - chipSelectHigh(); - inBlock_ = 0; - } -} -//------------------------------------------------------------------------------ -/** read CID or CSR register */ -uint8_t Sd2Card::readRegister(uint8_t cmd, void* buf) { - uint8_t* dst = reinterpret_cast(buf); - if (cardCommand(cmd, 0)) { - error(SD_CARD_ERROR_READ_REG); - goto fail; - } - if (!waitStartBlock()) goto fail; - // transfer data -#ifdef ESP8266 - SPI.transferBytes(NULL, dst, 16); -#else - for (uint16_t i = 0; i < 16; i++) dst[i] = spiRec(); -#endif - spiRec(); // get first crc byte - spiRec(); // get second crc byte - chipSelectHigh(); - return true; - - fail: - chipSelectHigh(); - return false; -} -//------------------------------------------------------------------------------ -/** - * Set the SPI clock rate. - * - * \param[in] sckRateID A value in the range [0, 6]. - * - * The SPI clock will be set to F_CPU/pow(2, 1 + sckRateID). The maximum - * SPI rate is F_CPU/2 for \a sckRateID = 0 and the minimum rate is F_CPU/128 - * for \a scsRateID = 6. - * - * \return The value one, true, is returned for success and the value zero, - * false, is returned for an invalid value of \a sckRateID. - */ -#ifdef ESP8266 -uint8_t Sd2Card::setSckRate(uint32_t sckRateID) { -#else -uint8_t Sd2Card::setSckRate(uint8_t sckRateID) { - if (sckRateID > 6) { - error(SD_CARD_ERROR_SCK_RATE); - return false; - } -#endif -#ifndef USE_SPI_LIB - // see avr processor datasheet for SPI register bit definitions - if ((sckRateID & 1) || sckRateID == 6) { - SPSR &= ~(1 << SPI2X); - } else { - SPSR |= (1 << SPI2X); - } - SPCR &= ~((1 < SD_READ_TIMEOUT) { - error(SD_CARD_ERROR_READ_TIMEOUT); - goto fail; - } - } - if (status_ != DATA_START_BLOCK) { - error(SD_CARD_ERROR_READ); - goto fail; - } - return true; - - fail: - chipSelectHigh(); - return false; -} -//------------------------------------------------------------------------------ -/** - * Writes a 512 byte block to an SD card. - * - * \param[in] blockNumber Logical block to be written. - * \param[in] src Pointer to the location of the data to be written. - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ -uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) { -#if SD_PROTECT_BLOCK_ZERO - // don't allow write to first block - if (blockNumber == 0) { - error(SD_CARD_ERROR_WRITE_BLOCK_ZERO); - goto fail; - } -#endif // SD_PROTECT_BLOCK_ZERO - - // use address if not SDHC card - if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; - if (cardCommand(CMD24, blockNumber)) { - error(SD_CARD_ERROR_CMD24); - goto fail; - } - if (!writeData(DATA_START_BLOCK, src)) goto fail; - - // wait for flash programming to complete - if (!waitNotBusy(SD_WRITE_TIMEOUT)) { - error(SD_CARD_ERROR_WRITE_TIMEOUT); - goto fail; - } - // response is r2 so get and check two bytes for nonzero - if (cardCommand(CMD13, 0) || spiRec()) { - error(SD_CARD_ERROR_WRITE_PROGRAMMING); - goto fail; - } - chipSelectHigh(); - return true; - - fail: - chipSelectHigh(); - return false; -} -//------------------------------------------------------------------------------ -/** Write one data block in a multiple block write sequence */ -uint8_t Sd2Card::writeData(const uint8_t* src) { - // wait for previous write to finish - if (!waitNotBusy(SD_WRITE_TIMEOUT)) { - error(SD_CARD_ERROR_WRITE_MULTIPLE); - chipSelectHigh(); - return false; - } - return writeData(WRITE_MULTIPLE_TOKEN, src); -} -//------------------------------------------------------------------------------ -// send one block of data for write block or write multiple blocks -uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) { -#ifdef OPTIMIZE_HARDWARE_SPI - - // send data - optimized loop - SPDR = token; - - // send two byte per iteration - for (uint16_t i = 0; i < 512; i += 2) { - while (!(SPSR & (1 << SPIF))) - ; - SPDR = src[i]; - while (!(SPSR & (1 << SPIF))) - ; - SPDR = src[i+1]; - } - - // wait for last data byte - while (!(SPSR & (1 << SPIF))) - ; - -#else // OPTIMIZE_HARDWARE_SPI - spiSend(token); -#ifdef ESP8266 - // send argument - SPI.writeBytes((uint8_t *)src, 512); -#else - for (uint16_t i = 0; i < 512; i++) { - spiSend(src[i]); - } -#endif -#endif // OPTIMIZE_HARDWARE_SPI -#ifdef ESP8266 - SPI.write16(0xFFFF, true); -#else - spiSend(0xff); // dummy crc - spiSend(0xff); // dummy crc -#endif - status_ = spiRec(); - if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) { - error(SD_CARD_ERROR_WRITE); - chipSelectHigh(); - return false; - } - return true; -} -//------------------------------------------------------------------------------ -/** Start a write multiple blocks sequence. - * - * \param[in] blockNumber Address of first block in sequence. - * \param[in] eraseCount The number of blocks to be pre-erased. - * - * \note This function is used with writeData() and writeStop() - * for optimized multiple block writes. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ -uint8_t Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) { -#if SD_PROTECT_BLOCK_ZERO - // don't allow write to first block - if (blockNumber == 0) { - error(SD_CARD_ERROR_WRITE_BLOCK_ZERO); - goto fail; - } -#endif // SD_PROTECT_BLOCK_ZERO - // send pre-erase count - if (cardAcmd(ACMD23, eraseCount)) { - error(SD_CARD_ERROR_ACMD23); - goto fail; - } - // use address if not SDHC card - if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; - if (cardCommand(CMD25, blockNumber)) { - error(SD_CARD_ERROR_CMD25); - goto fail; - } - return true; - - fail: - chipSelectHigh(); - return false; -} -//------------------------------------------------------------------------------ -/** End a write multiple blocks sequence. - * -* \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ -uint8_t Sd2Card::writeStop(void) { - if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; - spiSend(STOP_TRAN_TOKEN); - if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; - chipSelectHigh(); - return true; - - fail: - error(SD_CARD_ERROR_STOP_TRAN); - chipSelectHigh(); - return false; -} +/* Arduino Sd2Card Library + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino Sd2Card Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino Sd2Card Library. If not, see + . +*/ +#define USE_SPI_LIB +#include +#include "Sd2Card.h" +//------------------------------------------------------------------------------ +#ifndef SOFTWARE_SPI +#ifdef USE_SPI_LIB +#include +static SPISettings settings; +#endif +// functions for hardware SPI +/** Send a byte to the card */ +static void spiSend(uint8_t b) +{ +#ifndef USE_SPI_LIB + SPDR = b; + while (!(SPSR & (1 << SPIF))) + ; +#else +#ifdef ESP8266 + SPI.write(b); +#else + SPI.transfer(b); +#endif +#endif +} +/** Receive a byte from the card */ +static uint8_t spiRec(void) +{ +#ifndef USE_SPI_LIB + spiSend(0XFF); + return SPDR; +#else + return SPI.transfer(0xFF); +#endif +} +#else // SOFTWARE_SPI +//------------------------------------------------------------------------------ +/** nop to tune soft SPI timing */ +#define nop asm volatile ("nop\n\t") +//------------------------------------------------------------------------------ +/** Soft SPI receive */ +uint8_t spiRec(void) +{ + uint8_t data = 0; + // no interrupts during byte receive - about 8 us + cli(); + // output pin high - like sending 0XFF + fastDigitalWrite(SPI_MOSI_PIN, HIGH); + + for (uint8_t i = 0; i < 8; i++) + { + fastDigitalWrite(SPI_SCK_PIN, HIGH); + + // adjust so SCK is nice + nop; + nop; + + data <<= 1; + + if (fastDigitalRead(SPI_MISO_PIN)) + { + data |= 1; + } + + fastDigitalWrite(SPI_SCK_PIN, LOW); + } + // enable interrupts + sei(); + return data; +} +//------------------------------------------------------------------------------ +/** Soft SPI send */ +void spiSend(uint8_t data) +{ + // no interrupts during byte send - about 8 us + cli(); + for (uint8_t i = 0; i < 8; i++) + { + fastDigitalWrite(SPI_SCK_PIN, LOW); + + fastDigitalWrite(SPI_MOSI_PIN, data & 0X80); + + data <<= 1; + + fastDigitalWrite(SPI_SCK_PIN, HIGH); + } + // hold SCK high for a few ns + nop; + nop; + nop; + nop; + + fastDigitalWrite(SPI_SCK_PIN, LOW); + // enable interrupts + sei(); +} +#endif // SOFTWARE_SPI +//------------------------------------------------------------------------------ +// send command and return error code. Return zero for OK +uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) +{ + // end read if in partialBlockRead mode + readEnd(); + + // select card + chipSelectLow(); + + // wait up to 300 ms if busy + waitNotBusy(300); + + // send command + spiSend(cmd | 0x40); + +#ifdef ESP8266 + // send argument + SPI.write32(arg, true); +#else + // send argument + for (int8_t s = 24; s >= 0; s -= 8) + { + spiSend(arg >> s); + } +#endif + + + // send CRC + uint8_t crc = 0xFF; + if (cmd == CMD0) + { + crc = 0x95; // correct crc for CMD0 with arg 0 + } + if (cmd == CMD8) + { + crc = 0x87; // correct crc for CMD8 with arg 0X1AA + } + spiSend(crc); + + // wait for response + for (uint8_t i = 0; ((status_ = spiRec()) & 0x80) && i != 0xFF; i++) + ; +#ifdef ESP8266 + optimistic_yield(10000); +#endif + return status_; +} +//------------------------------------------------------------------------------ +/** + Determine the size of an SD flash memory card. + + \return The number of 512 byte data blocks in the card + or zero if an error occurs. +*/ +uint32_t Sd2Card::cardSize(void) +{ + csd_t csd; + if (!readCSD(&csd)) + { + return 0; + } + if (csd.v1.csd_ver == 0) + { + uint8_t read_bl_len = csd.v1.read_bl_len; + uint16_t c_size = (csd.v1.c_size_high << 10) + | (csd.v1.c_size_mid << 2) | csd.v1.c_size_low; + uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1) + | csd.v1.c_size_mult_low; + return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7); + } + else if (csd.v2.csd_ver == 1) + { + uint32_t c_size = ((uint32_t)csd.v2.c_size_high << 16) + | (csd.v2.c_size_mid << 8) | csd.v2.c_size_low; + return (c_size + 1) << 10; + } + else + { + error(SD_CARD_ERROR_BAD_CSD); + return 0; + } +} +//------------------------------------------------------------------------------ +static uint8_t chip_select_asserted = 0; + +void Sd2Card::chipSelectHigh(void) +{ + digitalWrite(chipSelectPin_, HIGH); +#ifdef USE_SPI_LIB + if (chip_select_asserted) + { + chip_select_asserted = 0; + SPI.endTransaction(); + } +#endif +} +//------------------------------------------------------------------------------ +void Sd2Card::chipSelectLow(void) +{ +#ifdef USE_SPI_LIB + if (!chip_select_asserted) + { + chip_select_asserted = 1; + SPI.beginTransaction(settings); + } +#endif + digitalWrite(chipSelectPin_, LOW); +} +//------------------------------------------------------------------------------ +/** Erase a range of blocks. + + \param[in] firstBlock The address of the first block in the range. + \param[in] lastBlock The address of the last block in the range. + + \note This function requests the SD card to do a flash erase for a + range of blocks. The data on the card after an erase operation is + either 0 or 1, depends on the card vendor. The card must support + single block erase. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ +uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) +{ + if (!eraseSingleBlockEnable()) + { + error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); + goto fail; + } + if (type_ != SD_CARD_TYPE_SDHC) + { + firstBlock <<= 9; + lastBlock <<= 9; + } + if (cardCommand(CMD32, firstBlock) + || cardCommand(CMD33, lastBlock) + || cardCommand(CMD38, 0)) + { + error(SD_CARD_ERROR_ERASE); + goto fail; + } + if (!waitNotBusy(SD_ERASE_TIMEOUT)) + { + error(SD_CARD_ERROR_ERASE_TIMEOUT); + goto fail; + } + chipSelectHigh(); + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Determine if card supports single block erase. + + \return The value one, true, is returned if single block erase is supported. + The value zero, false, is returned if single block erase is not supported. +*/ +uint8_t Sd2Card::eraseSingleBlockEnable(void) +{ + csd_t csd; + return readCSD(&csd) ? csd.v1.erase_blk_en : 0; +} +//------------------------------------------------------------------------------ +/** + Initialize an SD flash memory card. + + \param[in] sckRateID SPI clock rate selector. See setSckRate(). + \param[in] chipSelectPin SD chip select pin number. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. The reason for failure + can be determined by calling errorCode() and errorData(). +*/ +#ifdef ESP8266 +uint8_t Sd2Card::init(uint32_t sckRateID, uint8_t chipSelectPin) +{ +#else +uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) +{ +#endif + errorCode_ = inBlock_ = partialBlockRead_ = type_ = 0; + chipSelectPin_ = chipSelectPin; + // 16-bit init start time allows over a minute + uint16_t t0 = (uint16_t)millis(); + uint32_t arg; + + // set pin modes + pinMode(chipSelectPin_, OUTPUT); + digitalWrite(chipSelectPin_, HIGH); +#ifndef USE_SPI_LIB + pinMode(SPI_MISO_PIN, INPUT); + pinMode(SPI_MOSI_PIN, OUTPUT); + pinMode(SPI_SCK_PIN, OUTPUT); +#endif + +#ifndef SOFTWARE_SPI +#ifndef USE_SPI_LIB + // SS must be in output mode even it is not chip select + pinMode(SS_PIN, OUTPUT); + digitalWrite(SS_PIN, HIGH); // disable any SPI device using hardware SS pin + // Enable SPI, Master, clock rate f_osc/128 + SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); + // clear double speed + SPSR &= ~(1 << SPI2X); +#else // USE_SPI_LIB + SPI.begin(); + settings = SPISettings(250000, MSBFIRST, SPI_MODE0); +#endif // USE_SPI_LIB +#endif // SOFTWARE_SPI + + // must supply min of 74 clock cycles with CS high. +#ifdef USE_SPI_LIB + SPI.beginTransaction(settings); +#endif + for (uint8_t i = 0; i < 10; i++) + { + spiSend(0XFF); + } +#ifdef USE_SPI_LIB + SPI.endTransaction(); +#endif + + chipSelectLow(); + + // command to go idle in SPI mode + while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) + { + if (((uint16_t)(millis() - t0)) > SD_INIT_TIMEOUT) + { + error(SD_CARD_ERROR_CMD0); + goto fail; + } + } + // check SD version + if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) + { + type(SD_CARD_TYPE_SD1); + } + else + { + // only need last byte of r7 response + for (uint8_t i = 0; i < 4; i++) + { + status_ = spiRec(); + } + if (status_ != 0XAA) + { + error(SD_CARD_ERROR_CMD8); + goto fail; + } + type(SD_CARD_TYPE_SD2); + } + // initialize card and send host supports SDHC if SD2 + arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0; + + while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) + { + // check for timeout + if (((uint16_t)(millis() - t0)) > SD_INIT_TIMEOUT) + { + error(SD_CARD_ERROR_ACMD41); + goto fail; + } + } + // if SD2 read OCR register to check for SDHC card + if (type() == SD_CARD_TYPE_SD2) + { + if (cardCommand(CMD58, 0)) + { + error(SD_CARD_ERROR_CMD58); + goto fail; + } + if ((spiRec() & 0XC0) == 0XC0) + { + type(SD_CARD_TYPE_SDHC); + } + // discard rest of ocr - contains allowed voltage range + for (uint8_t i = 0; i < 3; i++) + { + spiRec(); + } + } + chipSelectHigh(); + +#ifndef SOFTWARE_SPI + return setSckRate(sckRateID); +#else // SOFTWARE_SPI + return true; +#endif // SOFTWARE_SPI + +fail: + chipSelectHigh(); + return false; +} + +//------------------------------------------------------------------------------ +/** + Shut down Sd2Card, which at this point means end SPI. + + \param[in] endSPI The value true (non-zero) or FALSE (zero). + + Call card.end() or card.end(true) to shut everything down. + Call card.end(false) to shut everything but the SPI object down. +*/ +void Sd2Card::end(bool endSPI) +{ + if (endSPI) + { + SPI.end(); + } +} + + + +//------------------------------------------------------------------------------ +/** + Enable or disable partial block reads. + + Enabling partial block reads improves performance by allowing a block + to be read over the SPI bus as several sub-blocks. Errors may occur + if the time between reads is too long since the SD card may timeout. + The SPI SS line will be held low until the entire block is read or + readEnd() is called. + + Use this for applications like the Adafruit Wave Shield. + + \param[in] value The value TRUE (non-zero) or FALSE (zero).) +*/ +void Sd2Card::partialBlockRead(uint8_t value) +{ + readEnd(); + partialBlockRead_ = value; +} +//------------------------------------------------------------------------------ +/** + Read a 512 byte block from an SD card device. + + \param[in] block Logical block to be read. + \param[out] dst Pointer to the location that will receive the data. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ +uint8_t Sd2Card::readBlock(uint32_t block, uint8_t* dst) +{ + return readData(block, 0, 512, dst); +} +//------------------------------------------------------------------------------ +/** + Read part of a 512 byte block from an SD card. + + \param[in] block Logical block to be read. + \param[in] offset Number of bytes to skip at start of block + \param[out] dst Pointer to the location that will receive the data. + \param[in] count Number of bytes to read + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ +uint8_t Sd2Card::readData(uint32_t block, + uint16_t offset, uint16_t count, uint8_t* dst) +{ + if (count == 0) + { + return true; + } + if ((count + offset) > 512) + { + goto fail; + } + if (!inBlock_ || block != block_ || offset < offset_) + { + block_ = block; + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) + { + block <<= 9; + } + if (cardCommand(CMD17, block)) + { + error(SD_CARD_ERROR_CMD17); + goto fail; + } + if (!waitStartBlock()) + { + goto fail; + } + offset_ = 0; + inBlock_ = 1; + } + +#ifdef OPTIMIZE_HARDWARE_SPI + uint16_t n; + + // start first spi transfer + SPDR = 0XFF; + + // skip data before offset + for (; offset_ < offset; offset_++) + { + while (!(SPSR & (1 << SPIF))) + ; + SPDR = 0XFF; + } + // transfer data + n = count - 1; + for (uint16_t i = 0; i < n; i++) + { + while (!(SPSR & (1 << SPIF))) + ; + dst[i] = SPDR; + SPDR = 0XFF; + } + // wait for last byte + while (!(SPSR & (1 << SPIF))) + ; + dst[n] = SPDR; + +#else // OPTIMIZE_HARDWARE_SPI +#ifdef ESP8266 + // skip data before offset + SPI.transferBytes(NULL, NULL, offset_); + + // transfer data + SPI.transferBytes(NULL, dst, count); + +#else + // skip data before offset + for (; offset_ < offset; offset_++) + { + spiRec(); + } + // transfer data + for (uint16_t i = 0; i < count; i++) + { + dst[i] = spiRec(); + } +#endif +#endif // OPTIMIZE_HARDWARE_SPI + + offset_ += count; + if (!partialBlockRead_ || offset_ >= 512) + { + // read rest of data, checksum and set chip select high + readEnd(); + } + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Skip remaining data in a block when in partial block read mode. */ +void Sd2Card::readEnd(void) +{ + if (inBlock_) + { + // skip data and crc +#ifdef OPTIMIZE_HARDWARE_SPI + // optimize skip for hardware + SPDR = 0XFF; + while (offset_++ < 513) + { + while (!(SPSR & (1 << SPIF))) + ; + SPDR = 0XFF; + } + // wait for last crc byte + while (!(SPSR & (1 << SPIF))) + ; +#else // OPTIMIZE_HARDWARE_SPI +#ifdef ESP8266 + SPI.transferBytes(NULL, NULL, (514 - offset_)); +#else + while (offset_++ < 514) + { + spiRec(); + } +#endif +#endif // OPTIMIZE_HARDWARE_SPI + chipSelectHigh(); + inBlock_ = 0; + } +} +//------------------------------------------------------------------------------ +/** read CID or CSR register */ +uint8_t Sd2Card::readRegister(uint8_t cmd, void* buf) +{ + uint8_t* dst = reinterpret_cast(buf); + if (cardCommand(cmd, 0)) + { + error(SD_CARD_ERROR_READ_REG); + goto fail; + } + if (!waitStartBlock()) + { + goto fail; + } + // transfer data +#ifdef ESP8266 + SPI.transferBytes(NULL, dst, 16); +#else + for (uint16_t i = 0; i < 16; i++) + { + dst[i] = spiRec(); + } +#endif + spiRec(); // get first crc byte + spiRec(); // get second crc byte + chipSelectHigh(); + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** + Set the SPI clock rate. + + \param[in] sckRateID A value in the range [0, 6]. + + The SPI clock will be set to F_CPU/pow(2, 1 + sckRateID). The maximum + SPI rate is F_CPU/2 for \a sckRateID = 0 and the minimum rate is F_CPU/128 + for \a scsRateID = 6. + + \return The value one, true, is returned for success and the value zero, + false, is returned for an invalid value of \a sckRateID. +*/ +#ifdef ESP8266 +uint8_t Sd2Card::setSckRate(uint32_t sckRateID) +{ +#else +uint8_t Sd2Card::setSckRate(uint8_t sckRateID) +{ + if (sckRateID > 6) + { + error(SD_CARD_ERROR_SCK_RATE); + return false; + } +#endif +#ifndef USE_SPI_LIB + // see avr processor datasheet for SPI register bit definitions + if ((sckRateID & 1) || sckRateID == 6) + { + SPSR &= ~(1 << SPI2X); + } + else + { + SPSR |= (1 << SPI2X); + } + SPCR &= ~((1 << SPR1) | (1 << SPR0)); + SPCR |= (sckRateID & 4 ? (1 << SPR1) : 0) + | (sckRateID & 2 ? (1 << SPR0) : 0); +#else // USE_SPI_LIB +#ifdef ESP8266 + settings = SPISettings(sckRateID, MSBFIRST, SPI_MODE0); +#else + switch (sckRateID) + { + case 0: settings = SPISettings(25000000, MSBFIRST, SPI_MODE0); break; + case 1: settings = SPISettings(4000000, MSBFIRST, SPI_MODE0); break; + case 2: settings = SPISettings(2000000, MSBFIRST, SPI_MODE0); break; + case 3: settings = SPISettings(1000000, MSBFIRST, SPI_MODE0); break; + case 4: settings = SPISettings(500000, MSBFIRST, SPI_MODE0); break; + case 5: settings = SPISettings(250000, MSBFIRST, SPI_MODE0); break; + default: settings = SPISettings(125000, MSBFIRST, SPI_MODE0); + } +#endif +#endif // USE_SPI_LIB + return true; +} +//------------------------------------------------------------------------------ +// wait for card to go not busy +uint8_t Sd2Card::waitNotBusy(uint16_t timeoutMillis) +{ + uint16_t t0 = millis(); + do + { +#ifdef ESP8266 + optimistic_yield(10000); +#endif + if (spiRec() == 0XFF) + { + return true; + } + } while (((uint16_t)millis() - t0) < timeoutMillis); + return false; +} +//------------------------------------------------------------------------------ +/** Wait for start block token */ +uint8_t Sd2Card::waitStartBlock(void) +{ + uint16_t t0 = millis(); + while ((status_ = spiRec()) == 0XFF) + { +#ifdef ESP8266 + optimistic_yield(10000); +#endif + if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) + { + error(SD_CARD_ERROR_READ_TIMEOUT); + goto fail; + } + } + if (status_ != DATA_START_BLOCK) + { + error(SD_CARD_ERROR_READ); + goto fail; + } + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** + Writes a 512 byte block to an SD card. + + \param[in] blockNumber Logical block to be written. + \param[in] src Pointer to the location of the data to be written. + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ +uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) +{ +#if SD_PROTECT_BLOCK_ZERO + // don't allow write to first block + if (blockNumber == 0) + { + error(SD_CARD_ERROR_WRITE_BLOCK_ZERO); + goto fail; + } +#endif // SD_PROTECT_BLOCK_ZERO + + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) + { + blockNumber <<= 9; + } + if (cardCommand(CMD24, blockNumber)) + { + error(SD_CARD_ERROR_CMD24); + goto fail; + } + if (!writeData(DATA_START_BLOCK, src)) + { + goto fail; + } + + // wait for flash programming to complete + if (!waitNotBusy(SD_WRITE_TIMEOUT)) + { + error(SD_CARD_ERROR_WRITE_TIMEOUT); + goto fail; + } + // response is r2 so get and check two bytes for nonzero + if (cardCommand(CMD13, 0) || spiRec()) + { + error(SD_CARD_ERROR_WRITE_PROGRAMMING); + goto fail; + } + chipSelectHigh(); + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** Write one data block in a multiple block write sequence */ +uint8_t Sd2Card::writeData(const uint8_t* src) +{ + // wait for previous write to finish + if (!waitNotBusy(SD_WRITE_TIMEOUT)) + { + error(SD_CARD_ERROR_WRITE_MULTIPLE); + chipSelectHigh(); + return false; + } + return writeData(WRITE_MULTIPLE_TOKEN, src); +} +//------------------------------------------------------------------------------ +// send one block of data for write block or write multiple blocks +uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) +{ +#ifdef OPTIMIZE_HARDWARE_SPI + + // send data - optimized loop + SPDR = token; + + // send two byte per iteration + for (uint16_t i = 0; i < 512; i += 2) + { + while (!(SPSR & (1 << SPIF))) + ; + SPDR = src[i]; + while (!(SPSR & (1 << SPIF))) + ; + SPDR = src[i + 1]; + } + + // wait for last data byte + while (!(SPSR & (1 << SPIF))) + ; + +#else // OPTIMIZE_HARDWARE_SPI + spiSend(token); +#ifdef ESP8266 + // send argument + SPI.writeBytes((uint8_t *)src, 512); +#else + for (uint16_t i = 0; i < 512; i++) + { + spiSend(src[i]); + } +#endif +#endif // OPTIMIZE_HARDWARE_SPI +#ifdef ESP8266 + SPI.write16(0xFFFF, true); +#else + spiSend(0xff); // dummy crc + spiSend(0xff); // dummy crc +#endif + status_ = spiRec(); + if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) + { + error(SD_CARD_ERROR_WRITE); + chipSelectHigh(); + return false; + } + return true; +} +//------------------------------------------------------------------------------ +/** Start a write multiple blocks sequence. + + \param[in] blockNumber Address of first block in sequence. + \param[in] eraseCount The number of blocks to be pre-erased. + + \note This function is used with writeData() and writeStop() + for optimized multiple block writes. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ +uint8_t Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) +{ +#if SD_PROTECT_BLOCK_ZERO + // don't allow write to first block + if (blockNumber == 0) + { + error(SD_CARD_ERROR_WRITE_BLOCK_ZERO); + goto fail; + } +#endif // SD_PROTECT_BLOCK_ZERO + // send pre-erase count + if (cardAcmd(ACMD23, eraseCount)) + { + error(SD_CARD_ERROR_ACMD23); + goto fail; + } + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) + { + blockNumber <<= 9; + } + if (cardCommand(CMD25, blockNumber)) + { + error(SD_CARD_ERROR_CMD25); + goto fail; + } + return true; + +fail: + chipSelectHigh(); + return false; +} +//------------------------------------------------------------------------------ +/** End a write multiple blocks sequence. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ +uint8_t Sd2Card::writeStop(void) +{ + if (!waitNotBusy(SD_WRITE_TIMEOUT)) + { + goto fail; + } + spiSend(STOP_TRAN_TOKEN); + if (!waitNotBusy(SD_WRITE_TIMEOUT)) + { + goto fail; + } + chipSelectHigh(); + return true; + +fail: + error(SD_CARD_ERROR_STOP_TRAN); + chipSelectHigh(); + return false; +} diff --git a/libraries/SD/src/utility/Sd2Card.h b/libraries/SD/src/utility/Sd2Card.h index 6b1d164905..f68acc56d9 100644 --- a/libraries/SD/src/utility/Sd2Card.h +++ b/libraries/SD/src/utility/Sd2Card.h @@ -1,262 +1,287 @@ -/* Arduino Sd2Card Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino Sd2Card Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino Sd2Card Library. If not, see - * . - */ -#ifndef Sd2Card_h -#define Sd2Card_h -/** - * \file - * Sd2Card class - */ -#include "Sd2PinMap.h" -#include "SdInfo.h" - -#ifdef ESP8266 -#include "SPI.h" -uint32_t const SPI_FULL_SPEED = 8000000; -uint32_t const SPI_HALF_SPEED = 4000000; -uint32_t const SPI_QUARTER_SPEED = 2000000; -#else -/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */ -uint8_t const SPI_FULL_SPEED = 0; -/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */ -uint8_t const SPI_HALF_SPEED = 1; -/** Set SCK rate to F_CPU/8. Sd2Card::setSckRate(). */ -uint8_t const SPI_QUARTER_SPEED = 2; -#endif -/** - * USE_SPI_LIB: if set, use the SPI library bundled with Arduino IDE, otherwise - * run with a standalone driver for AVR. - */ -#define USE_SPI_LIB -/** - * Define MEGA_SOFT_SPI non-zero to use software SPI on Mega Arduinos. - * Pins used are SS 10, MOSI 11, MISO 12, and SCK 13. - * - * MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used - * on Mega Arduinos. Software SPI works well with GPS Shield V1.1 - * but many SD cards will fail with GPS Shield V1.0. - */ -#define MEGA_SOFT_SPI 0 -//------------------------------------------------------------------------------ -#if MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__)) -#define SOFTWARE_SPI -#endif // MEGA_SOFT_SPI -//------------------------------------------------------------------------------ -// SPI pin definitions -// -#ifndef SOFTWARE_SPI -// hardware pin defs -/** - * SD Chip Select pin - * - * Warning if this pin is redefined the hardware SS will pin will be enabled - * as an output by init(). An avr processor will not function as an SPI - * master unless SS is set to output mode. - */ -/** The default chip select pin for the SD card is SS. */ -uint8_t const SD_CHIP_SELECT_PIN = SS_PIN; -// The following three pins must not be redefined for hardware SPI. -/** SPI Master Out Slave In pin */ -uint8_t const SPI_MOSI_PIN = MOSI_PIN; -/** SPI Master In Slave Out pin */ -uint8_t const SPI_MISO_PIN = MISO_PIN; -/** SPI Clock pin */ -uint8_t const SPI_SCK_PIN = SCK_PIN; -/** optimize loops for hardware SPI */ -#ifndef USE_SPI_LIB -#define OPTIMIZE_HARDWARE_SPI -#endif - -#else // SOFTWARE_SPI -// define software SPI pins so Mega can use unmodified GPS Shield -/** SPI chip select pin */ -uint8_t const SD_CHIP_SELECT_PIN = 10; -/** SPI Master Out Slave In pin */ -uint8_t const SPI_MOSI_PIN = 11; -/** SPI Master In Slave Out pin */ -uint8_t const SPI_MISO_PIN = 12; -/** SPI Clock pin */ -uint8_t const SPI_SCK_PIN = 13; -#endif // SOFTWARE_SPI -//------------------------------------------------------------------------------ -/** Protect block zero from write if nonzero */ -#define SD_PROTECT_BLOCK_ZERO 1 -/** init timeout ms */ -uint16_t const SD_INIT_TIMEOUT = 2000; -/** erase timeout ms */ -uint16_t const SD_ERASE_TIMEOUT = 10000; -/** read timeout ms */ -uint16_t const SD_READ_TIMEOUT = 300; -/** write time out ms */ -uint16_t const SD_WRITE_TIMEOUT = 600; -//------------------------------------------------------------------------------ -// SD card errors -/** timeout error for command CMD0 */ -uint8_t const SD_CARD_ERROR_CMD0 = 0X1; -/** CMD8 was not accepted - not a valid SD card*/ -uint8_t const SD_CARD_ERROR_CMD8 = 0X2; -/** card returned an error response for CMD17 (read block) */ -uint8_t const SD_CARD_ERROR_CMD17 = 0X3; -/** card returned an error response for CMD24 (write block) */ -uint8_t const SD_CARD_ERROR_CMD24 = 0X4; -/** WRITE_MULTIPLE_BLOCKS command failed */ -uint8_t const SD_CARD_ERROR_CMD25 = 0X05; -/** card returned an error response for CMD58 (read OCR) */ -uint8_t const SD_CARD_ERROR_CMD58 = 0X06; -/** SET_WR_BLK_ERASE_COUNT failed */ -uint8_t const SD_CARD_ERROR_ACMD23 = 0X07; -/** card's ACMD41 initialization process timeout */ -uint8_t const SD_CARD_ERROR_ACMD41 = 0X08; -/** card returned a bad CSR version field */ -uint8_t const SD_CARD_ERROR_BAD_CSD = 0X09; -/** erase block group command failed */ -uint8_t const SD_CARD_ERROR_ERASE = 0X0A; -/** card not capable of single block erase */ -uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0X0B; -/** Erase sequence timed out */ -uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0X0C; -/** card returned an error token instead of read data */ -uint8_t const SD_CARD_ERROR_READ = 0X0D; -/** read CID or CSD failed */ -uint8_t const SD_CARD_ERROR_READ_REG = 0X0E; -/** timeout while waiting for start of read data */ -uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X0F; -/** card did not accept STOP_TRAN_TOKEN */ -uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X10; -/** card returned an error token as a response to a write operation */ -uint8_t const SD_CARD_ERROR_WRITE = 0X11; -/** attempt to write protected block zero */ -uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X12; -/** card did not go ready for a multiple block write */ -uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X13; -/** card returned an error to a CMD13 status check after a write */ -uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X14; -/** timeout occurred during write programming */ -uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X15; -/** incorrect rate selected */ -uint8_t const SD_CARD_ERROR_SCK_RATE = 0X16; -//------------------------------------------------------------------------------ -// card types -/** Standard capacity V1 SD card */ -uint8_t const SD_CARD_TYPE_SD1 = 1; -/** Standard capacity V2 SD card */ -uint8_t const SD_CARD_TYPE_SD2 = 2; -/** High Capacity SD card */ -uint8_t const SD_CARD_TYPE_SDHC = 3; -//------------------------------------------------------------------------------ -/** - * \class Sd2Card - * \brief Raw access to SD and SDHC flash memory cards. - */ -class Sd2Card { - public: - /** Construct an instance of Sd2Card. */ - Sd2Card(void) : errorCode_(0), inBlock_(0), partialBlockRead_(0), type_(0) {} - uint32_t cardSize(void); - uint8_t erase(uint32_t firstBlock, uint32_t lastBlock); - uint8_t eraseSingleBlockEnable(void); - /** - * \return error code for last error. See Sd2Card.h for a list of error codes. - */ - uint8_t errorCode(void) const {return errorCode_;} - /** \return error data for last error. */ - uint8_t errorData(void) const {return status_;} - /** - * Initialize an SD flash memory card with default clock rate and chip - * select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). - */ - uint8_t init(void) { - return init(SPI_FULL_SPEED, SD_CHIP_SELECT_PIN); - } - /** - * Initialize an SD flash memory card with the selected SPI clock rate - * and the default SD chip select pin. - * See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). - */ - #ifdef ESP8266 - uint8_t init(uint32_t sckRateID) { - return init(sckRateID, SD_CHIP_SELECT_PIN); - } - uint8_t init(uint32_t sckRateID, uint8_t chipSelectPin); - #else - uint8_t init(uint8_t sckRateID) { - return init(sckRateID, SD_CHIP_SELECT_PIN); - } - uint8_t init(uint8_t sckRateID, uint8_t chipSelectPin); - #endif - - void end(bool endSPI = true); - - void partialBlockRead(uint8_t value); - /** Returns the current value, true or false, for partial block read. */ - uint8_t partialBlockRead(void) const {return partialBlockRead_;} - uint8_t readBlock(uint32_t block, uint8_t* dst); - uint8_t readData(uint32_t block, - uint16_t offset, uint16_t count, uint8_t* dst); - /** - * Read a cards CID register. The CID contains card identification - * information such as Manufacturer ID, Product name, Product serial - * number and Manufacturing date. */ - uint8_t readCID(cid_t* cid) { - return readRegister(CMD10, cid); - } - /** - * Read a cards CSD register. The CSD contains Card-Specific Data that - * provides information regarding access to the card's contents. */ - uint8_t readCSD(csd_t* csd) { - return readRegister(CMD9, csd); - } - void readEnd(void); - #ifdef ESP8266 - uint8_t setSckRate(uint32_t sckRateID); - #else - uint8_t setSckRate(uint8_t sckRateID); - #endif - /** Return the card type: SD V1, SD V2 or SDHC */ - uint8_t type(void) const {return type_;} - uint8_t writeBlock(uint32_t blockNumber, const uint8_t* src); - uint8_t writeData(const uint8_t* src); - uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount); - uint8_t writeStop(void); - private: - uint32_t block_; - uint8_t chipSelectPin_; - uint8_t errorCode_; - uint8_t inBlock_; - uint16_t offset_; - uint8_t partialBlockRead_; - uint8_t status_; - uint8_t type_; - // private functions - uint8_t cardAcmd(uint8_t cmd, uint32_t arg) { - cardCommand(CMD55, 0); - return cardCommand(cmd, arg); - } - uint8_t cardCommand(uint8_t cmd, uint32_t arg); - void error(uint8_t code) {errorCode_ = code;} - uint8_t readRegister(uint8_t cmd, void* buf); - uint8_t sendWriteCommand(uint32_t blockNumber, uint32_t eraseCount); - void chipSelectHigh(void); - void chipSelectLow(void); - void type(uint8_t value) {type_ = value;} - uint8_t waitNotBusy(uint16_t timeoutMillis); - uint8_t writeData(uint8_t token, const uint8_t* src); - uint8_t waitStartBlock(void); -}; -#endif // Sd2Card_h +/* Arduino Sd2Card Library + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino Sd2Card Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino Sd2Card Library. If not, see + . +*/ +#ifndef Sd2Card_h +#define Sd2Card_h +/** + \file + Sd2Card class +*/ +#include "Sd2PinMap.h" +#include "SdInfo.h" + +#ifdef ESP8266 +#include "SPI.h" +uint32_t const SPI_FULL_SPEED = 8000000; +uint32_t const SPI_HALF_SPEED = 4000000; +uint32_t const SPI_QUARTER_SPEED = 2000000; +#else +/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */ +uint8_t const SPI_FULL_SPEED = 0; +/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */ +uint8_t const SPI_HALF_SPEED = 1; +/** Set SCK rate to F_CPU/8. Sd2Card::setSckRate(). */ +uint8_t const SPI_QUARTER_SPEED = 2; +#endif +/** + USE_SPI_LIB: if set, use the SPI library bundled with Arduino IDE, otherwise + run with a standalone driver for AVR. +*/ +#define USE_SPI_LIB +/** + Define MEGA_SOFT_SPI non-zero to use software SPI on Mega Arduinos. + Pins used are SS 10, MOSI 11, MISO 12, and SCK 13. + + MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used + on Mega Arduinos. Software SPI works well with GPS Shield V1.1 + but many SD cards will fail with GPS Shield V1.0. +*/ +#define MEGA_SOFT_SPI 0 +//------------------------------------------------------------------------------ +#if MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__)) +#define SOFTWARE_SPI +#endif // MEGA_SOFT_SPI +//------------------------------------------------------------------------------ +// SPI pin definitions +// +#ifndef SOFTWARE_SPI +// hardware pin defs +/** + SD Chip Select pin + + Warning if this pin is redefined the hardware SS will pin will be enabled + as an output by init(). An avr processor will not function as an SPI + master unless SS is set to output mode. +*/ +/** The default chip select pin for the SD card is SS. */ +uint8_t const SD_CHIP_SELECT_PIN = SS_PIN; +// The following three pins must not be redefined for hardware SPI. +/** SPI Master Out Slave In pin */ +uint8_t const SPI_MOSI_PIN = MOSI_PIN; +/** SPI Master In Slave Out pin */ +uint8_t const SPI_MISO_PIN = MISO_PIN; +/** SPI Clock pin */ +uint8_t const SPI_SCK_PIN = SCK_PIN; +/** optimize loops for hardware SPI */ +#ifndef USE_SPI_LIB +#define OPTIMIZE_HARDWARE_SPI +#endif + +#else // SOFTWARE_SPI +// define software SPI pins so Mega can use unmodified GPS Shield +/** SPI chip select pin */ +uint8_t const SD_CHIP_SELECT_PIN = 10; +/** SPI Master Out Slave In pin */ +uint8_t const SPI_MOSI_PIN = 11; +/** SPI Master In Slave Out pin */ +uint8_t const SPI_MISO_PIN = 12; +/** SPI Clock pin */ +uint8_t const SPI_SCK_PIN = 13; +#endif // SOFTWARE_SPI +//------------------------------------------------------------------------------ +/** Protect block zero from write if nonzero */ +#define SD_PROTECT_BLOCK_ZERO 1 +/** init timeout ms */ +uint16_t const SD_INIT_TIMEOUT = 2000; +/** erase timeout ms */ +uint16_t const SD_ERASE_TIMEOUT = 10000; +/** read timeout ms */ +uint16_t const SD_READ_TIMEOUT = 300; +/** write time out ms */ +uint16_t const SD_WRITE_TIMEOUT = 600; +//------------------------------------------------------------------------------ +// SD card errors +/** timeout error for command CMD0 */ +uint8_t const SD_CARD_ERROR_CMD0 = 0X1; +/** CMD8 was not accepted - not a valid SD card*/ +uint8_t const SD_CARD_ERROR_CMD8 = 0X2; +/** card returned an error response for CMD17 (read block) */ +uint8_t const SD_CARD_ERROR_CMD17 = 0X3; +/** card returned an error response for CMD24 (write block) */ +uint8_t const SD_CARD_ERROR_CMD24 = 0X4; +/** WRITE_MULTIPLE_BLOCKS command failed */ +uint8_t const SD_CARD_ERROR_CMD25 = 0X05; +/** card returned an error response for CMD58 (read OCR) */ +uint8_t const SD_CARD_ERROR_CMD58 = 0X06; +/** SET_WR_BLK_ERASE_COUNT failed */ +uint8_t const SD_CARD_ERROR_ACMD23 = 0X07; +/** card's ACMD41 initialization process timeout */ +uint8_t const SD_CARD_ERROR_ACMD41 = 0X08; +/** card returned a bad CSR version field */ +uint8_t const SD_CARD_ERROR_BAD_CSD = 0X09; +/** erase block group command failed */ +uint8_t const SD_CARD_ERROR_ERASE = 0X0A; +/** card not capable of single block erase */ +uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0X0B; +/** Erase sequence timed out */ +uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0X0C; +/** card returned an error token instead of read data */ +uint8_t const SD_CARD_ERROR_READ = 0X0D; +/** read CID or CSD failed */ +uint8_t const SD_CARD_ERROR_READ_REG = 0X0E; +/** timeout while waiting for start of read data */ +uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X0F; +/** card did not accept STOP_TRAN_TOKEN */ +uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X10; +/** card returned an error token as a response to a write operation */ +uint8_t const SD_CARD_ERROR_WRITE = 0X11; +/** attempt to write protected block zero */ +uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X12; +/** card did not go ready for a multiple block write */ +uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X13; +/** card returned an error to a CMD13 status check after a write */ +uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X14; +/** timeout occurred during write programming */ +uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X15; +/** incorrect rate selected */ +uint8_t const SD_CARD_ERROR_SCK_RATE = 0X16; +//------------------------------------------------------------------------------ +// card types +/** Standard capacity V1 SD card */ +uint8_t const SD_CARD_TYPE_SD1 = 1; +/** Standard capacity V2 SD card */ +uint8_t const SD_CARD_TYPE_SD2 = 2; +/** High Capacity SD card */ +uint8_t const SD_CARD_TYPE_SDHC = 3; +//------------------------------------------------------------------------------ +/** + \class Sd2Card + \brief Raw access to SD and SDHC flash memory cards. +*/ +class Sd2Card +{ +public: + /** Construct an instance of Sd2Card. */ + Sd2Card(void) : errorCode_(0), inBlock_(0), partialBlockRead_(0), type_(0) {} + uint32_t cardSize(void); + uint8_t erase(uint32_t firstBlock, uint32_t lastBlock); + uint8_t eraseSingleBlockEnable(void); + /** + \return error code for last error. See Sd2Card.h for a list of error codes. + */ + uint8_t errorCode(void) const + { + return errorCode_; + } + /** \return error data for last error. */ + uint8_t errorData(void) const + { + return status_; + } + /** + Initialize an SD flash memory card with default clock rate and chip + select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). + */ + uint8_t init(void) + { + return init(SPI_FULL_SPEED, SD_CHIP_SELECT_PIN); + } + /** + Initialize an SD flash memory card with the selected SPI clock rate + and the default SD chip select pin. + See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). + */ +#ifdef ESP8266 + uint8_t init(uint32_t sckRateID) + { + return init(sckRateID, SD_CHIP_SELECT_PIN); + } + uint8_t init(uint32_t sckRateID, uint8_t chipSelectPin); +#else + uint8_t init(uint8_t sckRateID) + { + return init(sckRateID, SD_CHIP_SELECT_PIN); + } + uint8_t init(uint8_t sckRateID, uint8_t chipSelectPin); +#endif + + void end(bool endSPI = true); + + void partialBlockRead(uint8_t value); + /** Returns the current value, true or false, for partial block read. */ + uint8_t partialBlockRead(void) const + { + return partialBlockRead_; + } + uint8_t readBlock(uint32_t block, uint8_t* dst); + uint8_t readData(uint32_t block, + uint16_t offset, uint16_t count, uint8_t* dst); + /** + Read a cards CID register. The CID contains card identification + information such as Manufacturer ID, Product name, Product serial + number and Manufacturing date. */ + uint8_t readCID(cid_t* cid) + { + return readRegister(CMD10, cid); + } + /** + Read a cards CSD register. The CSD contains Card-Specific Data that + provides information regarding access to the card's contents. */ + uint8_t readCSD(csd_t* csd) + { + return readRegister(CMD9, csd); + } + void readEnd(void); +#ifdef ESP8266 + uint8_t setSckRate(uint32_t sckRateID); +#else + uint8_t setSckRate(uint8_t sckRateID); +#endif + /** Return the card type: SD V1, SD V2 or SDHC */ + uint8_t type(void) const + { + return type_; + } + uint8_t writeBlock(uint32_t blockNumber, const uint8_t* src); + uint8_t writeData(const uint8_t* src); + uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount); + uint8_t writeStop(void); +private: + uint32_t block_; + uint8_t chipSelectPin_; + uint8_t errorCode_; + uint8_t inBlock_; + uint16_t offset_; + uint8_t partialBlockRead_; + uint8_t status_; + uint8_t type_; + // private functions + uint8_t cardAcmd(uint8_t cmd, uint32_t arg) + { + cardCommand(CMD55, 0); + return cardCommand(cmd, arg); + } + uint8_t cardCommand(uint8_t cmd, uint32_t arg); + void error(uint8_t code) + { + errorCode_ = code; + } + uint8_t readRegister(uint8_t cmd, void* buf); + uint8_t sendWriteCommand(uint32_t blockNumber, uint32_t eraseCount); + void chipSelectHigh(void); + void chipSelectLow(void); + void type(uint8_t value) + { + type_ = value; + } + uint8_t waitNotBusy(uint16_t timeoutMillis); + uint8_t writeData(uint8_t token, const uint8_t* src); + uint8_t waitStartBlock(void); +}; +#endif // Sd2Card_h diff --git a/libraries/SD/src/utility/Sd2PinMap.h b/libraries/SD/src/utility/Sd2PinMap.h index 7f4d9dd604..d32bce524c 100644 --- a/libraries/SD/src/utility/Sd2PinMap.h +++ b/libraries/SD/src/utility/Sd2PinMap.h @@ -1,385 +1,413 @@ -/* Arduino SdFat Library - * Copyright (C) 2010 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ -#if defined(__arm__) // Arduino Due Board follows - -#ifndef Sd2PinMap_h -#define Sd2PinMap_h - -#include - -uint8_t const SS_PIN = SS; -uint8_t const MOSI_PIN = MOSI; -uint8_t const MISO_PIN = MISO; -uint8_t const SCK_PIN = SCK; - -#endif // Sd2PinMap_h - -#elif defined(ESP8266) // Other AVR based Boards follows -#ifndef Sd2PinMap_h -#define Sd2PinMap_h - -#include - -uint8_t const SS_PIN = SS; -uint8_t const MOSI_PIN = MOSI; -uint8_t const MISO_PIN = MISO; -uint8_t const SCK_PIN = SCK; - -#endif // Sd2PinMap_h - -#elif defined(__AVR__) // Other AVR based Boards follows - -// Warning this file was generated by a program. -#ifndef Sd2PinMap_h -#define Sd2PinMap_h -#include - -//------------------------------------------------------------------------------ -/** struct for mapping digital pins */ -struct pin_map_t { - volatile uint8_t* ddr; - volatile uint8_t* pin; - volatile uint8_t* port; - uint8_t bit; -}; -//------------------------------------------------------------------------------ -#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -// Mega - -// Two Wire (aka I2C) ports -uint8_t const SDA_PIN = 20; -uint8_t const SCL_PIN = 21; - -// SPI port -uint8_t const SS_PIN = 53; -uint8_t const MOSI_PIN = 51; -uint8_t const MISO_PIN = 50; -uint8_t const SCK_PIN = 52; - -static const pin_map_t digitalPinMap[] = { - {&DDRE, &PINE, &PORTE, 0}, // E0 0 - {&DDRE, &PINE, &PORTE, 1}, // E1 1 - {&DDRE, &PINE, &PORTE, 4}, // E4 2 - {&DDRE, &PINE, &PORTE, 5}, // E5 3 - {&DDRG, &PING, &PORTG, 5}, // G5 4 - {&DDRE, &PINE, &PORTE, 3}, // E3 5 - {&DDRH, &PINH, &PORTH, 3}, // H3 6 - {&DDRH, &PINH, &PORTH, 4}, // H4 7 - {&DDRH, &PINH, &PORTH, 5}, // H5 8 - {&DDRH, &PINH, &PORTH, 6}, // H6 9 - {&DDRB, &PINB, &PORTB, 4}, // B4 10 - {&DDRB, &PINB, &PORTB, 5}, // B5 11 - {&DDRB, &PINB, &PORTB, 6}, // B6 12 - {&DDRB, &PINB, &PORTB, 7}, // B7 13 - {&DDRJ, &PINJ, &PORTJ, 1}, // J1 14 - {&DDRJ, &PINJ, &PORTJ, 0}, // J0 15 - {&DDRH, &PINH, &PORTH, 1}, // H1 16 - {&DDRH, &PINH, &PORTH, 0}, // H0 17 - {&DDRD, &PIND, &PORTD, 3}, // D3 18 - {&DDRD, &PIND, &PORTD, 2}, // D2 19 - {&DDRD, &PIND, &PORTD, 1}, // D1 20 - {&DDRD, &PIND, &PORTD, 0}, // D0 21 - {&DDRA, &PINA, &PORTA, 0}, // A0 22 - {&DDRA, &PINA, &PORTA, 1}, // A1 23 - {&DDRA, &PINA, &PORTA, 2}, // A2 24 - {&DDRA, &PINA, &PORTA, 3}, // A3 25 - {&DDRA, &PINA, &PORTA, 4}, // A4 26 - {&DDRA, &PINA, &PORTA, 5}, // A5 27 - {&DDRA, &PINA, &PORTA, 6}, // A6 28 - {&DDRA, &PINA, &PORTA, 7}, // A7 29 - {&DDRC, &PINC, &PORTC, 7}, // C7 30 - {&DDRC, &PINC, &PORTC, 6}, // C6 31 - {&DDRC, &PINC, &PORTC, 5}, // C5 32 - {&DDRC, &PINC, &PORTC, 4}, // C4 33 - {&DDRC, &PINC, &PORTC, 3}, // C3 34 - {&DDRC, &PINC, &PORTC, 2}, // C2 35 - {&DDRC, &PINC, &PORTC, 1}, // C1 36 - {&DDRC, &PINC, &PORTC, 0}, // C0 37 - {&DDRD, &PIND, &PORTD, 7}, // D7 38 - {&DDRG, &PING, &PORTG, 2}, // G2 39 - {&DDRG, &PING, &PORTG, 1}, // G1 40 - {&DDRG, &PING, &PORTG, 0}, // G0 41 - {&DDRL, &PINL, &PORTL, 7}, // L7 42 - {&DDRL, &PINL, &PORTL, 6}, // L6 43 - {&DDRL, &PINL, &PORTL, 5}, // L5 44 - {&DDRL, &PINL, &PORTL, 4}, // L4 45 - {&DDRL, &PINL, &PORTL, 3}, // L3 46 - {&DDRL, &PINL, &PORTL, 2}, // L2 47 - {&DDRL, &PINL, &PORTL, 1}, // L1 48 - {&DDRL, &PINL, &PORTL, 0}, // L0 49 - {&DDRB, &PINB, &PORTB, 3}, // B3 50 - {&DDRB, &PINB, &PORTB, 2}, // B2 51 - {&DDRB, &PINB, &PORTB, 1}, // B1 52 - {&DDRB, &PINB, &PORTB, 0}, // B0 53 - {&DDRF, &PINF, &PORTF, 0}, // F0 54 - {&DDRF, &PINF, &PORTF, 1}, // F1 55 - {&DDRF, &PINF, &PORTF, 2}, // F2 56 - {&DDRF, &PINF, &PORTF, 3}, // F3 57 - {&DDRF, &PINF, &PORTF, 4}, // F4 58 - {&DDRF, &PINF, &PORTF, 5}, // F5 59 - {&DDRF, &PINF, &PORTF, 6}, // F6 60 - {&DDRF, &PINF, &PORTF, 7}, // F7 61 - {&DDRK, &PINK, &PORTK, 0}, // K0 62 - {&DDRK, &PINK, &PORTK, 1}, // K1 63 - {&DDRK, &PINK, &PORTK, 2}, // K2 64 - {&DDRK, &PINK, &PORTK, 3}, // K3 65 - {&DDRK, &PINK, &PORTK, 4}, // K4 66 - {&DDRK, &PINK, &PORTK, 5}, // K5 67 - {&DDRK, &PINK, &PORTK, 6}, // K6 68 - {&DDRK, &PINK, &PORTK, 7} // K7 69 -}; -//------------------------------------------------------------------------------ -#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) -// Sanguino - -// Two Wire (aka I2C) ports -uint8_t const SDA_PIN = 17; -uint8_t const SCL_PIN = 18; - -// SPI port -uint8_t const SS_PIN = 4; -uint8_t const MOSI_PIN = 5; -uint8_t const MISO_PIN = 6; -uint8_t const SCK_PIN = 7; - -static const pin_map_t digitalPinMap[] = { - {&DDRB, &PINB, &PORTB, 0}, // B0 0 - {&DDRB, &PINB, &PORTB, 1}, // B1 1 - {&DDRB, &PINB, &PORTB, 2}, // B2 2 - {&DDRB, &PINB, &PORTB, 3}, // B3 3 - {&DDRB, &PINB, &PORTB, 4}, // B4 4 - {&DDRB, &PINB, &PORTB, 5}, // B5 5 - {&DDRB, &PINB, &PORTB, 6}, // B6 6 - {&DDRB, &PINB, &PORTB, 7}, // B7 7 - {&DDRD, &PIND, &PORTD, 0}, // D0 8 - {&DDRD, &PIND, &PORTD, 1}, // D1 9 - {&DDRD, &PIND, &PORTD, 2}, // D2 10 - {&DDRD, &PIND, &PORTD, 3}, // D3 11 - {&DDRD, &PIND, &PORTD, 4}, // D4 12 - {&DDRD, &PIND, &PORTD, 5}, // D5 13 - {&DDRD, &PIND, &PORTD, 6}, // D6 14 - {&DDRD, &PIND, &PORTD, 7}, // D7 15 - {&DDRC, &PINC, &PORTC, 0}, // C0 16 - {&DDRC, &PINC, &PORTC, 1}, // C1 17 - {&DDRC, &PINC, &PORTC, 2}, // C2 18 - {&DDRC, &PINC, &PORTC, 3}, // C3 19 - {&DDRC, &PINC, &PORTC, 4}, // C4 20 - {&DDRC, &PINC, &PORTC, 5}, // C5 21 - {&DDRC, &PINC, &PORTC, 6}, // C6 22 - {&DDRC, &PINC, &PORTC, 7}, // C7 23 - {&DDRA, &PINA, &PORTA, 7}, // A7 24 - {&DDRA, &PINA, &PORTA, 6}, // A6 25 - {&DDRA, &PINA, &PORTA, 5}, // A5 26 - {&DDRA, &PINA, &PORTA, 4}, // A4 27 - {&DDRA, &PINA, &PORTA, 3}, // A3 28 - {&DDRA, &PINA, &PORTA, 2}, // A2 29 - {&DDRA, &PINA, &PORTA, 1}, // A1 30 - {&DDRA, &PINA, &PORTA, 0} // A0 31 -}; -//------------------------------------------------------------------------------ -#elif defined(__AVR_ATmega32U4__) -// Leonardo - -// Two Wire (aka I2C) ports -uint8_t const SDA_PIN = 2; -uint8_t const SCL_PIN = 3; - -// SPI port -uint8_t const SS_PIN = 17; -uint8_t const MOSI_PIN = 16; -uint8_t const MISO_PIN = 14; -uint8_t const SCK_PIN = 15; - -static const pin_map_t digitalPinMap[] = { - {&DDRD, &PIND, &PORTD, 2}, // D2 0 - {&DDRD, &PIND, &PORTD, 3}, // D3 1 - {&DDRD, &PIND, &PORTD, 1}, // D1 2 - {&DDRD, &PIND, &PORTD, 0}, // D0 3 - {&DDRD, &PIND, &PORTD, 4}, // D4 4 - {&DDRC, &PINC, &PORTC, 6}, // C6 5 - {&DDRD, &PIND, &PORTD, 7}, // D7 6 - {&DDRE, &PINE, &PORTE, 6}, // E6 7 - {&DDRB, &PINB, &PORTB, 4}, // B4 8 - {&DDRB, &PINB, &PORTB, 5}, // B5 9 - {&DDRB, &PINB, &PORTB, 6}, // B6 10 - {&DDRB, &PINB, &PORTB, 7}, // B7 11 - {&DDRD, &PIND, &PORTD, 6}, // D6 12 - {&DDRC, &PINC, &PORTC, 7}, // C7 13 - {&DDRB, &PINB, &PORTB, 3}, // B3 14 - {&DDRB, &PINB, &PORTB, 1}, // B1 15 - {&DDRB, &PINB, &PORTB, 2}, // B2 16 - {&DDRB, &PINB, &PORTB, 0}, // B0 17 - {&DDRF, &PINF, &PORTF, 7}, // F7 18 - {&DDRF, &PINF, &PORTF, 6}, // F6 19 - {&DDRF, &PINF, &PORTF, 5}, // F5 20 - {&DDRF, &PINF, &PORTF, 4}, // F4 21 - {&DDRF, &PINF, &PORTF, 1}, // F1 22 - {&DDRF, &PINF, &PORTF, 0}, // F0 23 -}; -//------------------------------------------------------------------------------ -#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) -// Teensy++ 1.0 & 2.0 - -// Two Wire (aka I2C) ports -uint8_t const SDA_PIN = 1; -uint8_t const SCL_PIN = 0; - -// SPI port -uint8_t const SS_PIN = 20; -uint8_t const MOSI_PIN = 22; -uint8_t const MISO_PIN = 23; -uint8_t const SCK_PIN = 21; - -static const pin_map_t digitalPinMap[] = { - {&DDRD, &PIND, &PORTD, 0}, // D0 0 - {&DDRD, &PIND, &PORTD, 1}, // D1 1 - {&DDRD, &PIND, &PORTD, 2}, // D2 2 - {&DDRD, &PIND, &PORTD, 3}, // D3 3 - {&DDRD, &PIND, &PORTD, 4}, // D4 4 - {&DDRD, &PIND, &PORTD, 5}, // D5 5 - {&DDRD, &PIND, &PORTD, 6}, // D6 6 - {&DDRD, &PIND, &PORTD, 7}, // D7 7 - {&DDRE, &PINE, &PORTE, 0}, // E0 8 - {&DDRE, &PINE, &PORTE, 1}, // E1 9 - {&DDRC, &PINC, &PORTC, 0}, // C0 10 - {&DDRC, &PINC, &PORTC, 1}, // C1 11 - {&DDRC, &PINC, &PORTC, 2}, // C2 12 - {&DDRC, &PINC, &PORTC, 3}, // C3 13 - {&DDRC, &PINC, &PORTC, 4}, // C4 14 - {&DDRC, &PINC, &PORTC, 5}, // C5 15 - {&DDRC, &PINC, &PORTC, 6}, // C6 16 - {&DDRC, &PINC, &PORTC, 7}, // C7 17 - {&DDRE, &PINE, &PORTE, 6}, // E6 18 - {&DDRE, &PINE, &PORTE, 7}, // E7 19 - {&DDRB, &PINB, &PORTB, 0}, // B0 20 - {&DDRB, &PINB, &PORTB, 1}, // B1 21 - {&DDRB, &PINB, &PORTB, 2}, // B2 22 - {&DDRB, &PINB, &PORTB, 3}, // B3 23 - {&DDRB, &PINB, &PORTB, 4}, // B4 24 - {&DDRB, &PINB, &PORTB, 5}, // B5 25 - {&DDRB, &PINB, &PORTB, 6}, // B6 26 - {&DDRB, &PINB, &PORTB, 7}, // B7 27 - {&DDRA, &PINA, &PORTA, 0}, // A0 28 - {&DDRA, &PINA, &PORTA, 1}, // A1 29 - {&DDRA, &PINA, &PORTA, 2}, // A2 30 - {&DDRA, &PINA, &PORTA, 3}, // A3 31 - {&DDRA, &PINA, &PORTA, 4}, // A4 32 - {&DDRA, &PINA, &PORTA, 5}, // A5 33 - {&DDRA, &PINA, &PORTA, 6}, // A6 34 - {&DDRA, &PINA, &PORTA, 7}, // A7 35 - {&DDRE, &PINE, &PORTE, 4}, // E4 36 - {&DDRE, &PINE, &PORTE, 5}, // E5 37 - {&DDRF, &PINF, &PORTF, 0}, // F0 38 - {&DDRF, &PINF, &PORTF, 1}, // F1 39 - {&DDRF, &PINF, &PORTF, 2}, // F2 40 - {&DDRF, &PINF, &PORTF, 3}, // F3 41 - {&DDRF, &PINF, &PORTF, 4}, // F4 42 - {&DDRF, &PINF, &PORTF, 5}, // F5 43 - {&DDRF, &PINF, &PORTF, 6}, // F6 44 - {&DDRF, &PINF, &PORTF, 7} // F7 45 -}; -//------------------------------------------------------------------------------ -#else // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -// 168 and 328 Arduinos - -// Two Wire (aka I2C) ports -uint8_t const SDA_PIN = 18; -uint8_t const SCL_PIN = 19; - -// SPI port -uint8_t const SS_PIN = 10; -uint8_t const MOSI_PIN = 11; -uint8_t const MISO_PIN = 12; -uint8_t const SCK_PIN = 13; - -static const pin_map_t digitalPinMap[] = { - {&DDRD, &PIND, &PORTD, 0}, // D0 0 - {&DDRD, &PIND, &PORTD, 1}, // D1 1 - {&DDRD, &PIND, &PORTD, 2}, // D2 2 - {&DDRD, &PIND, &PORTD, 3}, // D3 3 - {&DDRD, &PIND, &PORTD, 4}, // D4 4 - {&DDRD, &PIND, &PORTD, 5}, // D5 5 - {&DDRD, &PIND, &PORTD, 6}, // D6 6 - {&DDRD, &PIND, &PORTD, 7}, // D7 7 - {&DDRB, &PINB, &PORTB, 0}, // B0 8 - {&DDRB, &PINB, &PORTB, 1}, // B1 9 - {&DDRB, &PINB, &PORTB, 2}, // B2 10 - {&DDRB, &PINB, &PORTB, 3}, // B3 11 - {&DDRB, &PINB, &PORTB, 4}, // B4 12 - {&DDRB, &PINB, &PORTB, 5}, // B5 13 - {&DDRC, &PINC, &PORTC, 0}, // C0 14 - {&DDRC, &PINC, &PORTC, 1}, // C1 15 - {&DDRC, &PINC, &PORTC, 2}, // C2 16 - {&DDRC, &PINC, &PORTC, 3}, // C3 17 - {&DDRC, &PINC, &PORTC, 4}, // C4 18 - {&DDRC, &PINC, &PORTC, 5} // C5 19 -}; -#endif // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -//------------------------------------------------------------------------------ -static const uint8_t digitalPinCount = sizeof(digitalPinMap)/sizeof(pin_map_t); - -uint8_t badPinNumber(void) - __attribute__((error("Pin number is too large or not a constant"))); - -static inline __attribute__((always_inline)) - uint8_t getPinMode(uint8_t pin) { - if (__builtin_constant_p(pin) && pin < digitalPinCount) { - return (*digitalPinMap[pin].ddr >> digitalPinMap[pin].bit) & 1; - } else { - return badPinNumber(); - } -} -static inline __attribute__((always_inline)) - void setPinMode(uint8_t pin, uint8_t mode) { - if (__builtin_constant_p(pin) && pin < digitalPinCount) { - if (mode) { - *digitalPinMap[pin].ddr |= 1 << digitalPinMap[pin].bit; - } else { - *digitalPinMap[pin].ddr &= ~(1 << digitalPinMap[pin].bit); - } - } else { - badPinNumber(); - } -} -static inline __attribute__((always_inline)) - uint8_t fastDigitalRead(uint8_t pin) { - if (__builtin_constant_p(pin) && pin < digitalPinCount) { - return (*digitalPinMap[pin].pin >> digitalPinMap[pin].bit) & 1; - } else { - return badPinNumber(); - } -} -static inline __attribute__((always_inline)) - void fastDigitalWrite(uint8_t pin, uint8_t value) { - if (__builtin_constant_p(pin) && pin < digitalPinCount) { - if (value) { - *digitalPinMap[pin].port |= 1 << digitalPinMap[pin].bit; - } else { - *digitalPinMap[pin].port &= ~(1 << digitalPinMap[pin].bit); - } - } else { - badPinNumber(); - } -} -#endif // Sd2PinMap_h - -#else -#error Architecture or board not supported. -#endif +/* Arduino SdFat Library + Copyright (C) 2010 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ +#if defined(__arm__) // Arduino Due Board follows + +#ifndef Sd2PinMap_h +#define Sd2PinMap_h + +#include + +uint8_t const SS_PIN = SS; +uint8_t const MOSI_PIN = MOSI; +uint8_t const MISO_PIN = MISO; +uint8_t const SCK_PIN = SCK; + +#endif // Sd2PinMap_h + +#elif defined(ESP8266) // Other AVR based Boards follows +#ifndef Sd2PinMap_h +#define Sd2PinMap_h + +#include + +uint8_t const SS_PIN = SS; +uint8_t const MOSI_PIN = MOSI; +uint8_t const MISO_PIN = MISO; +uint8_t const SCK_PIN = SCK; + +#endif // Sd2PinMap_h + +#elif defined(__AVR__) // Other AVR based Boards follows + +// Warning this file was generated by a program. +#ifndef Sd2PinMap_h +#define Sd2PinMap_h +#include + +//------------------------------------------------------------------------------ +/** struct for mapping digital pins */ +struct pin_map_t +{ + volatile uint8_t* ddr; + volatile uint8_t* pin; + volatile uint8_t* port; + uint8_t bit; +}; +//------------------------------------------------------------------------------ +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +// Mega + +// Two Wire (aka I2C) ports +uint8_t const SDA_PIN = 20; +uint8_t const SCL_PIN = 21; + +// SPI port +uint8_t const SS_PIN = 53; +uint8_t const MOSI_PIN = 51; +uint8_t const MISO_PIN = 50; +uint8_t const SCK_PIN = 52; + +static const pin_map_t digitalPinMap[] = +{ + {&DDRE, &PINE, &PORTE, 0}, // E0 0 + {&DDRE, &PINE, &PORTE, 1}, // E1 1 + {&DDRE, &PINE, &PORTE, 4}, // E4 2 + {&DDRE, &PINE, &PORTE, 5}, // E5 3 + {&DDRG, &PING, &PORTG, 5}, // G5 4 + {&DDRE, &PINE, &PORTE, 3}, // E3 5 + {&DDRH, &PINH, &PORTH, 3}, // H3 6 + {&DDRH, &PINH, &PORTH, 4}, // H4 7 + {&DDRH, &PINH, &PORTH, 5}, // H5 8 + {&DDRH, &PINH, &PORTH, 6}, // H6 9 + {&DDRB, &PINB, &PORTB, 4}, // B4 10 + {&DDRB, &PINB, &PORTB, 5}, // B5 11 + {&DDRB, &PINB, &PORTB, 6}, // B6 12 + {&DDRB, &PINB, &PORTB, 7}, // B7 13 + {&DDRJ, &PINJ, &PORTJ, 1}, // J1 14 + {&DDRJ, &PINJ, &PORTJ, 0}, // J0 15 + {&DDRH, &PINH, &PORTH, 1}, // H1 16 + {&DDRH, &PINH, &PORTH, 0}, // H0 17 + {&DDRD, &PIND, &PORTD, 3}, // D3 18 + {&DDRD, &PIND, &PORTD, 2}, // D2 19 + {&DDRD, &PIND, &PORTD, 1}, // D1 20 + {&DDRD, &PIND, &PORTD, 0}, // D0 21 + {&DDRA, &PINA, &PORTA, 0}, // A0 22 + {&DDRA, &PINA, &PORTA, 1}, // A1 23 + {&DDRA, &PINA, &PORTA, 2}, // A2 24 + {&DDRA, &PINA, &PORTA, 3}, // A3 25 + {&DDRA, &PINA, &PORTA, 4}, // A4 26 + {&DDRA, &PINA, &PORTA, 5}, // A5 27 + {&DDRA, &PINA, &PORTA, 6}, // A6 28 + {&DDRA, &PINA, &PORTA, 7}, // A7 29 + {&DDRC, &PINC, &PORTC, 7}, // C7 30 + {&DDRC, &PINC, &PORTC, 6}, // C6 31 + {&DDRC, &PINC, &PORTC, 5}, // C5 32 + {&DDRC, &PINC, &PORTC, 4}, // C4 33 + {&DDRC, &PINC, &PORTC, 3}, // C3 34 + {&DDRC, &PINC, &PORTC, 2}, // C2 35 + {&DDRC, &PINC, &PORTC, 1}, // C1 36 + {&DDRC, &PINC, &PORTC, 0}, // C0 37 + {&DDRD, &PIND, &PORTD, 7}, // D7 38 + {&DDRG, &PING, &PORTG, 2}, // G2 39 + {&DDRG, &PING, &PORTG, 1}, // G1 40 + {&DDRG, &PING, &PORTG, 0}, // G0 41 + {&DDRL, &PINL, &PORTL, 7}, // L7 42 + {&DDRL, &PINL, &PORTL, 6}, // L6 43 + {&DDRL, &PINL, &PORTL, 5}, // L5 44 + {&DDRL, &PINL, &PORTL, 4}, // L4 45 + {&DDRL, &PINL, &PORTL, 3}, // L3 46 + {&DDRL, &PINL, &PORTL, 2}, // L2 47 + {&DDRL, &PINL, &PORTL, 1}, // L1 48 + {&DDRL, &PINL, &PORTL, 0}, // L0 49 + {&DDRB, &PINB, &PORTB, 3}, // B3 50 + {&DDRB, &PINB, &PORTB, 2}, // B2 51 + {&DDRB, &PINB, &PORTB, 1}, // B1 52 + {&DDRB, &PINB, &PORTB, 0}, // B0 53 + {&DDRF, &PINF, &PORTF, 0}, // F0 54 + {&DDRF, &PINF, &PORTF, 1}, // F1 55 + {&DDRF, &PINF, &PORTF, 2}, // F2 56 + {&DDRF, &PINF, &PORTF, 3}, // F3 57 + {&DDRF, &PINF, &PORTF, 4}, // F4 58 + {&DDRF, &PINF, &PORTF, 5}, // F5 59 + {&DDRF, &PINF, &PORTF, 6}, // F6 60 + {&DDRF, &PINF, &PORTF, 7}, // F7 61 + {&DDRK, &PINK, &PORTK, 0}, // K0 62 + {&DDRK, &PINK, &PORTK, 1}, // K1 63 + {&DDRK, &PINK, &PORTK, 2}, // K2 64 + {&DDRK, &PINK, &PORTK, 3}, // K3 65 + {&DDRK, &PINK, &PORTK, 4}, // K4 66 + {&DDRK, &PINK, &PORTK, 5}, // K5 67 + {&DDRK, &PINK, &PORTK, 6}, // K6 68 + {&DDRK, &PINK, &PORTK, 7} // K7 69 +}; +//------------------------------------------------------------------------------ +#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) +// Sanguino + +// Two Wire (aka I2C) ports +uint8_t const SDA_PIN = 17; +uint8_t const SCL_PIN = 18; + +// SPI port +uint8_t const SS_PIN = 4; +uint8_t const MOSI_PIN = 5; +uint8_t const MISO_PIN = 6; +uint8_t const SCK_PIN = 7; + +static const pin_map_t digitalPinMap[] = +{ + {&DDRB, &PINB, &PORTB, 0}, // B0 0 + {&DDRB, &PINB, &PORTB, 1}, // B1 1 + {&DDRB, &PINB, &PORTB, 2}, // B2 2 + {&DDRB, &PINB, &PORTB, 3}, // B3 3 + {&DDRB, &PINB, &PORTB, 4}, // B4 4 + {&DDRB, &PINB, &PORTB, 5}, // B5 5 + {&DDRB, &PINB, &PORTB, 6}, // B6 6 + {&DDRB, &PINB, &PORTB, 7}, // B7 7 + {&DDRD, &PIND, &PORTD, 0}, // D0 8 + {&DDRD, &PIND, &PORTD, 1}, // D1 9 + {&DDRD, &PIND, &PORTD, 2}, // D2 10 + {&DDRD, &PIND, &PORTD, 3}, // D3 11 + {&DDRD, &PIND, &PORTD, 4}, // D4 12 + {&DDRD, &PIND, &PORTD, 5}, // D5 13 + {&DDRD, &PIND, &PORTD, 6}, // D6 14 + {&DDRD, &PIND, &PORTD, 7}, // D7 15 + {&DDRC, &PINC, &PORTC, 0}, // C0 16 + {&DDRC, &PINC, &PORTC, 1}, // C1 17 + {&DDRC, &PINC, &PORTC, 2}, // C2 18 + {&DDRC, &PINC, &PORTC, 3}, // C3 19 + {&DDRC, &PINC, &PORTC, 4}, // C4 20 + {&DDRC, &PINC, &PORTC, 5}, // C5 21 + {&DDRC, &PINC, &PORTC, 6}, // C6 22 + {&DDRC, &PINC, &PORTC, 7}, // C7 23 + {&DDRA, &PINA, &PORTA, 7}, // A7 24 + {&DDRA, &PINA, &PORTA, 6}, // A6 25 + {&DDRA, &PINA, &PORTA, 5}, // A5 26 + {&DDRA, &PINA, &PORTA, 4}, // A4 27 + {&DDRA, &PINA, &PORTA, 3}, // A3 28 + {&DDRA, &PINA, &PORTA, 2}, // A2 29 + {&DDRA, &PINA, &PORTA, 1}, // A1 30 + {&DDRA, &PINA, &PORTA, 0} // A0 31 +}; +//------------------------------------------------------------------------------ +#elif defined(__AVR_ATmega32U4__) +// Leonardo + +// Two Wire (aka I2C) ports +uint8_t const SDA_PIN = 2; +uint8_t const SCL_PIN = 3; + +// SPI port +uint8_t const SS_PIN = 17; +uint8_t const MOSI_PIN = 16; +uint8_t const MISO_PIN = 14; +uint8_t const SCK_PIN = 15; + +static const pin_map_t digitalPinMap[] = +{ + {&DDRD, &PIND, &PORTD, 2}, // D2 0 + {&DDRD, &PIND, &PORTD, 3}, // D3 1 + {&DDRD, &PIND, &PORTD, 1}, // D1 2 + {&DDRD, &PIND, &PORTD, 0}, // D0 3 + {&DDRD, &PIND, &PORTD, 4}, // D4 4 + {&DDRC, &PINC, &PORTC, 6}, // C6 5 + {&DDRD, &PIND, &PORTD, 7}, // D7 6 + {&DDRE, &PINE, &PORTE, 6}, // E6 7 + {&DDRB, &PINB, &PORTB, 4}, // B4 8 + {&DDRB, &PINB, &PORTB, 5}, // B5 9 + {&DDRB, &PINB, &PORTB, 6}, // B6 10 + {&DDRB, &PINB, &PORTB, 7}, // B7 11 + {&DDRD, &PIND, &PORTD, 6}, // D6 12 + {&DDRC, &PINC, &PORTC, 7}, // C7 13 + {&DDRB, &PINB, &PORTB, 3}, // B3 14 + {&DDRB, &PINB, &PORTB, 1}, // B1 15 + {&DDRB, &PINB, &PORTB, 2}, // B2 16 + {&DDRB, &PINB, &PORTB, 0}, // B0 17 + {&DDRF, &PINF, &PORTF, 7}, // F7 18 + {&DDRF, &PINF, &PORTF, 6}, // F6 19 + {&DDRF, &PINF, &PORTF, 5}, // F5 20 + {&DDRF, &PINF, &PORTF, 4}, // F4 21 + {&DDRF, &PINF, &PORTF, 1}, // F1 22 + {&DDRF, &PINF, &PORTF, 0}, // F0 23 +}; +//------------------------------------------------------------------------------ +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) +// Teensy++ 1.0 & 2.0 + +// Two Wire (aka I2C) ports +uint8_t const SDA_PIN = 1; +uint8_t const SCL_PIN = 0; + +// SPI port +uint8_t const SS_PIN = 20; +uint8_t const MOSI_PIN = 22; +uint8_t const MISO_PIN = 23; +uint8_t const SCK_PIN = 21; + +static const pin_map_t digitalPinMap[] = +{ + {&DDRD, &PIND, &PORTD, 0}, // D0 0 + {&DDRD, &PIND, &PORTD, 1}, // D1 1 + {&DDRD, &PIND, &PORTD, 2}, // D2 2 + {&DDRD, &PIND, &PORTD, 3}, // D3 3 + {&DDRD, &PIND, &PORTD, 4}, // D4 4 + {&DDRD, &PIND, &PORTD, 5}, // D5 5 + {&DDRD, &PIND, &PORTD, 6}, // D6 6 + {&DDRD, &PIND, &PORTD, 7}, // D7 7 + {&DDRE, &PINE, &PORTE, 0}, // E0 8 + {&DDRE, &PINE, &PORTE, 1}, // E1 9 + {&DDRC, &PINC, &PORTC, 0}, // C0 10 + {&DDRC, &PINC, &PORTC, 1}, // C1 11 + {&DDRC, &PINC, &PORTC, 2}, // C2 12 + {&DDRC, &PINC, &PORTC, 3}, // C3 13 + {&DDRC, &PINC, &PORTC, 4}, // C4 14 + {&DDRC, &PINC, &PORTC, 5}, // C5 15 + {&DDRC, &PINC, &PORTC, 6}, // C6 16 + {&DDRC, &PINC, &PORTC, 7}, // C7 17 + {&DDRE, &PINE, &PORTE, 6}, // E6 18 + {&DDRE, &PINE, &PORTE, 7}, // E7 19 + {&DDRB, &PINB, &PORTB, 0}, // B0 20 + {&DDRB, &PINB, &PORTB, 1}, // B1 21 + {&DDRB, &PINB, &PORTB, 2}, // B2 22 + {&DDRB, &PINB, &PORTB, 3}, // B3 23 + {&DDRB, &PINB, &PORTB, 4}, // B4 24 + {&DDRB, &PINB, &PORTB, 5}, // B5 25 + {&DDRB, &PINB, &PORTB, 6}, // B6 26 + {&DDRB, &PINB, &PORTB, 7}, // B7 27 + {&DDRA, &PINA, &PORTA, 0}, // A0 28 + {&DDRA, &PINA, &PORTA, 1}, // A1 29 + {&DDRA, &PINA, &PORTA, 2}, // A2 30 + {&DDRA, &PINA, &PORTA, 3}, // A3 31 + {&DDRA, &PINA, &PORTA, 4}, // A4 32 + {&DDRA, &PINA, &PORTA, 5}, // A5 33 + {&DDRA, &PINA, &PORTA, 6}, // A6 34 + {&DDRA, &PINA, &PORTA, 7}, // A7 35 + {&DDRE, &PINE, &PORTE, 4}, // E4 36 + {&DDRE, &PINE, &PORTE, 5}, // E5 37 + {&DDRF, &PINF, &PORTF, 0}, // F0 38 + {&DDRF, &PINF, &PORTF, 1}, // F1 39 + {&DDRF, &PINF, &PORTF, 2}, // F2 40 + {&DDRF, &PINF, &PORTF, 3}, // F3 41 + {&DDRF, &PINF, &PORTF, 4}, // F4 42 + {&DDRF, &PINF, &PORTF, 5}, // F5 43 + {&DDRF, &PINF, &PORTF, 6}, // F6 44 + {&DDRF, &PINF, &PORTF, 7} // F7 45 +}; +//------------------------------------------------------------------------------ +#else // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +// 168 and 328 Arduinos + +// Two Wire (aka I2C) ports +uint8_t const SDA_PIN = 18; +uint8_t const SCL_PIN = 19; + +// SPI port +uint8_t const SS_PIN = 10; +uint8_t const MOSI_PIN = 11; +uint8_t const MISO_PIN = 12; +uint8_t const SCK_PIN = 13; + +static const pin_map_t digitalPinMap[] = +{ + {&DDRD, &PIND, &PORTD, 0}, // D0 0 + {&DDRD, &PIND, &PORTD, 1}, // D1 1 + {&DDRD, &PIND, &PORTD, 2}, // D2 2 + {&DDRD, &PIND, &PORTD, 3}, // D3 3 + {&DDRD, &PIND, &PORTD, 4}, // D4 4 + {&DDRD, &PIND, &PORTD, 5}, // D5 5 + {&DDRD, &PIND, &PORTD, 6}, // D6 6 + {&DDRD, &PIND, &PORTD, 7}, // D7 7 + {&DDRB, &PINB, &PORTB, 0}, // B0 8 + {&DDRB, &PINB, &PORTB, 1}, // B1 9 + {&DDRB, &PINB, &PORTB, 2}, // B2 10 + {&DDRB, &PINB, &PORTB, 3}, // B3 11 + {&DDRB, &PINB, &PORTB, 4}, // B4 12 + {&DDRB, &PINB, &PORTB, 5}, // B5 13 + {&DDRC, &PINC, &PORTC, 0}, // C0 14 + {&DDRC, &PINC, &PORTC, 1}, // C1 15 + {&DDRC, &PINC, &PORTC, 2}, // C2 16 + {&DDRC, &PINC, &PORTC, 3}, // C3 17 + {&DDRC, &PINC, &PORTC, 4}, // C4 18 + {&DDRC, &PINC, &PORTC, 5} // C5 19 +}; +#endif // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +//------------------------------------------------------------------------------ +static const uint8_t digitalPinCount = sizeof(digitalPinMap) / sizeof(pin_map_t); + +uint8_t badPinNumber(void) +__attribute__((error("Pin number is too large or not a constant"))); + +static inline __attribute__((always_inline)) +uint8_t getPinMode(uint8_t pin) +{ + if (__builtin_constant_p(pin) && pin < digitalPinCount) + { + return (*digitalPinMap[pin].ddr >> digitalPinMap[pin].bit) & 1; + } + else + { + return badPinNumber(); + } +} +static inline __attribute__((always_inline)) +void setPinMode(uint8_t pin, uint8_t mode) +{ + if (__builtin_constant_p(pin) && pin < digitalPinCount) + { + if (mode) + { + *digitalPinMap[pin].ddr |= 1 << digitalPinMap[pin].bit; + } + else + { + *digitalPinMap[pin].ddr &= ~(1 << digitalPinMap[pin].bit); + } + } + else + { + badPinNumber(); + } +} +static inline __attribute__((always_inline)) +uint8_t fastDigitalRead(uint8_t pin) +{ + if (__builtin_constant_p(pin) && pin < digitalPinCount) + { + return (*digitalPinMap[pin].pin >> digitalPinMap[pin].bit) & 1; + } + else + { + return badPinNumber(); + } +} +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, uint8_t value) +{ + if (__builtin_constant_p(pin) && pin < digitalPinCount) + { + if (value) + { + *digitalPinMap[pin].port |= 1 << digitalPinMap[pin].bit; + } + else + { + *digitalPinMap[pin].port &= ~(1 << digitalPinMap[pin].bit); + } + } + else + { + badPinNumber(); + } +} +#endif // Sd2PinMap_h + +#else +#error Architecture or board not supported. +#endif diff --git a/libraries/SD/src/utility/SdFat.h b/libraries/SD/src/utility/SdFat.h index 89c244418a..1e4b5e8694 100644 --- a/libraries/SD/src/utility/SdFat.h +++ b/libraries/SD/src/utility/SdFat.h @@ -1,551 +1,687 @@ -/* Arduino SdFat Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ -#ifndef SdFat_h -#define SdFat_h -/** - * \file - * SdFile and SdVolume classes - */ -#ifdef __AVR__ -#include -#endif -#include "Sd2Card.h" -#include "FatStructs.h" -#include "Print.h" -//------------------------------------------------------------------------------ -/** - * Allow use of deprecated functions if non-zero - */ -#define ALLOW_DEPRECATED_FUNCTIONS 1 -//------------------------------------------------------------------------------ -// forward declaration since SdVolume is used in SdFile -class SdVolume; -//============================================================================== -// SdFile class - -// flags for ls() -/** ls() flag to print modify date */ -uint8_t const LS_DATE = 1; -/** ls() flag to print file size */ -uint8_t const LS_SIZE = 2; -/** ls() flag for recursive list of subdirectories */ -uint8_t const LS_R = 4; - -// use the gnu style oflag in open() -/** open() oflag for reading */ -uint8_t const O_READ = 0X01; -/** open() oflag - same as O_READ */ -uint8_t const O_RDONLY = O_READ; -/** open() oflag for write */ -uint8_t const O_WRITE = 0X02; -/** open() oflag - same as O_WRITE */ -uint8_t const O_WRONLY = O_WRITE; -/** open() oflag for reading and writing */ -uint8_t const O_RDWR = (O_READ | O_WRITE); -/** open() oflag mask for access modes */ -uint8_t const O_ACCMODE = (O_READ | O_WRITE); -/** The file offset shall be set to the end of the file prior to each write. */ -uint8_t const O_APPEND = 0X04; -/** synchronous writes - call sync() after each write */ -uint8_t const O_SYNC = 0X08; -/** create the file if nonexistent */ -uint8_t const O_CREAT = 0X10; -/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ -uint8_t const O_EXCL = 0X20; -/** truncate the file to zero length */ -uint8_t const O_TRUNC = 0X40; - -// flags for timestamp -/** set the file's last access date */ -uint8_t const T_ACCESS = 1; -/** set the file's creation date and time */ -uint8_t const T_CREATE = 2; -/** Set the file's write date and time */ -uint8_t const T_WRITE = 4; -// values for type_ -/** This SdFile has not been opened. */ -uint8_t const FAT_FILE_TYPE_CLOSED = 0; -/** SdFile for a file */ -uint8_t const FAT_FILE_TYPE_NORMAL = 1; -/** SdFile for a FAT16 root directory */ -uint8_t const FAT_FILE_TYPE_ROOT16 = 2; -/** SdFile for a FAT32 root directory */ -uint8_t const FAT_FILE_TYPE_ROOT32 = 3; -/** SdFile for a subdirectory */ -uint8_t const FAT_FILE_TYPE_SUBDIR = 4; -/** Test value for directory type */ -uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT16; - -/** date field for FAT directory entry */ -static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { - return (year - 1980) << 9 | month << 5 | day; -} -/** year part of FAT directory date field */ -static inline uint16_t FAT_YEAR(uint16_t fatDate) { - return 1980 + (fatDate >> 9); -} -/** month part of FAT directory date field */ -static inline uint8_t FAT_MONTH(uint16_t fatDate) { - return (fatDate >> 5) & 0XF; -} -/** day part of FAT directory date field */ -static inline uint8_t FAT_DAY(uint16_t fatDate) { - return fatDate & 0X1F; -} -/** time field for FAT directory entry */ -static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { - return hour << 11 | minute << 5 | second >> 1; -} -/** hour part of FAT directory time field */ -static inline uint8_t FAT_HOUR(uint16_t fatTime) { - return fatTime >> 11; -} -/** minute part of FAT directory time field */ -static inline uint8_t FAT_MINUTE(uint16_t fatTime) { - return(fatTime >> 5) & 0X3F; -} -/** second part of FAT directory time field */ -static inline uint8_t FAT_SECOND(uint16_t fatTime) { - return 2*(fatTime & 0X1F); -} -/** Default date for file timestamps is 1 Jan 2000 */ -uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; -/** Default time for file timestamp is 1 am */ -uint16_t const FAT_DEFAULT_TIME = (1 << 11); -//------------------------------------------------------------------------------ -/** - * \class SdFile - * \brief Access FAT16 and FAT32 files on SD and SDHC cards. - */ -class SdFile : public Print { - public: - /** Create an instance of SdFile. */ - SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {} - /** - * writeError is set to true if an error occurs during a write(). - * Set writeError to false before calling print() and/or write() and check - * for true after calls to print() and/or write(). - */ - //bool writeError; - /** - * Cancel unbuffered reads for this file. - * See setUnbufferedRead() - */ - void clearUnbufferedRead(void) { - flags_ &= ~F_FILE_UNBUFFERED_READ; - } - uint8_t close(void); - uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); - uint8_t createContiguous(SdFile* dirFile, - const char* fileName, uint32_t size); - /** \return The current cluster number for a file or directory. */ - uint32_t curCluster(void) const {return curCluster_;} - /** \return The current position for a file or directory. */ - uint32_t curPosition(void) const {return curPosition_;} - /** - * Set the date/time callback function - * - * \param[in] dateTime The user's call back function. The callback - * function is of the form: - * - * \code - * void dateTime(uint16_t* date, uint16_t* time) { - * uint16_t year; - * uint8_t month, day, hour, minute, second; - * - * // User gets date and time from GPS or real-time clock here - * - * // return date using FAT_DATE macro to format fields - * *date = FAT_DATE(year, month, day); - * - * // return time using FAT_TIME macro to format fields - * *time = FAT_TIME(hour, minute, second); - * } - * \endcode - * - * Sets the function that is called when a file is created or when - * a file's directory entry is modified by sync(). All timestamps, - * access, creation, and modify, are set when a file is created. - * sync() maintains the last access date and last modify date/time. - * - * See the timestamp() function. - */ - static void dateTimeCallback( - void (*dateTime)(uint16_t* date, uint16_t* time)) { - dateTime_ = dateTime; - } - /** - * Cancel the date/time callback function. - */ - static void dateTimeCallbackCancel(void) { - // use explicit zero since NULL is not defined for Sanguino - dateTime_ = 0; - } - /** \return Address of the block that contains this file's directory. */ - uint32_t dirBlock(void) const {return dirBlock_;} - uint8_t dirEntry(dir_t* dir); - /** \return Index of this file's directory in the block dirBlock. */ - uint8_t dirIndex(void) const {return dirIndex_;} - static void dirName(const dir_t& dir, char* name); - /** \return The total number of bytes in a file or directory. */ - uint32_t fileSize(void) const {return fileSize_;} - /** \return The first cluster number for a file or directory. */ - uint32_t firstCluster(void) const {return firstCluster_;} - /** \return True if this is a SdFile for a directory else false. */ - uint8_t isDir(void) const {return type_ >= FAT_FILE_TYPE_MIN_DIR;} - /** \return True if this is a SdFile for a file else false. */ - uint8_t isFile(void) const {return type_ == FAT_FILE_TYPE_NORMAL;} - /** \return True if this is a SdFile for an open file/directory else false. */ - uint8_t isOpen(void) const {return type_ != FAT_FILE_TYPE_CLOSED;} - /** \return True if this is a SdFile for a subdirectory else false. */ - uint8_t isSubDir(void) const {return type_ == FAT_FILE_TYPE_SUBDIR;} - /** \return True if this is a SdFile for the root directory. */ - uint8_t isRoot(void) const { - return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32; - } - void ls(uint8_t flags = 0, uint8_t indent = 0); - uint8_t makeDir(SdFile* dir, const char* dirName); - uint8_t open(SdFile* dirFile, uint16_t index, uint8_t oflag); - uint8_t open(SdFile* dirFile, const char* fileName, uint8_t oflag); - - uint8_t openRoot(SdVolume* vol); - static void printDirName(const dir_t& dir, uint8_t width); - static void printFatDate(uint16_t fatDate); - static void printFatTime(uint16_t fatTime); - static void printTwoDigits(uint8_t v); - /** - * Read the next byte from a file. - * - * \return For success read returns the next byte in the file as an int. - * If an error occurs or end of file is reached -1 is returned. - */ - int16_t read(void) { - uint8_t b; - return read(&b, 1) == 1 ? b : -1; - } - int16_t read(void* buf, uint16_t nbyte); - int8_t readDir(dir_t* dir); - static uint8_t remove(SdFile* dirFile, const char* fileName); - uint8_t remove(void); - /** Set the file's current position to zero. */ - void rewind(void) { - curPosition_ = curCluster_ = 0; - } - uint8_t rmDir(void); - uint8_t rmRfStar(void); - /** Set the files position to current position + \a pos. See seekSet(). */ - uint8_t seekCur(uint32_t pos) { - return seekSet(curPosition_ + pos); - } - /** - * Set the files current position to end of file. Useful to position - * a file for append. See seekSet(). - */ - uint8_t seekEnd(void) {return seekSet(fileSize_);} - uint8_t seekSet(uint32_t pos); - /** - * Use unbuffered reads to access this file. Used with Wave - * Shield ISR. Used with Sd2Card::partialBlockRead() in WaveRP. - * - * Not recommended for normal applications. - */ - void setUnbufferedRead(void) { - if (isFile()) flags_ |= F_FILE_UNBUFFERED_READ; - } - uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, - uint8_t hour, uint8_t minute, uint8_t second); - uint8_t sync(void); - /** Type of this SdFile. You should use isFile() or isDir() instead of type() - * if possible. - * - * \return The file or directory type. - */ - uint8_t type(void) const {return type_;} - uint8_t truncate(uint32_t size); - /** \return Unbuffered read flag. */ - uint8_t unbufferedRead(void) const { - return flags_ & F_FILE_UNBUFFERED_READ; - } - /** \return SdVolume that contains this file. */ - SdVolume* volume(void) const {return vol_;} - size_t write(uint8_t b); - size_t write(const void* buf, uint16_t nbyte); - size_t write(const char* str); -#ifdef __AVR__ - void write_P(PGM_P str); - void writeln_P(PGM_P str); -#endif -//------------------------------------------------------------------------------ -#if ALLOW_DEPRECATED_FUNCTIONS -// Deprecated functions - suppress cpplint warnings with NOLINT comment - /** \deprecated Use: - * uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); - */ - uint8_t contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { // NOLINT - return contiguousRange(&bgnBlock, &endBlock); - } - /** \deprecated Use: - * uint8_t SdFile::createContiguous(SdFile* dirFile, - * const char* fileName, uint32_t size) - */ - uint8_t createContiguous(SdFile& dirFile, // NOLINT - const char* fileName, uint32_t size) { - return createContiguous(&dirFile, fileName, size); - } - - /** - * \deprecated Use: - * static void SdFile::dateTimeCallback( - * void (*dateTime)(uint16_t* date, uint16_t* time)); - */ - static void dateTimeCallback( - void (*dateTime)(uint16_t& date, uint16_t& time)) { // NOLINT - oldDateTime_ = dateTime; - dateTime_ = dateTime ? oldToNew : 0; - } - /** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */ - uint8_t dirEntry(dir_t& dir) {return dirEntry(&dir);} // NOLINT - /** \deprecated Use: - * uint8_t SdFile::makeDir(SdFile* dir, const char* dirName); - */ - uint8_t makeDir(SdFile& dir, const char* dirName) { // NOLINT - return makeDir(&dir, dirName); - } - /** \deprecated Use: - * uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag); - */ - uint8_t open(SdFile& dirFile, // NOLINT - const char* fileName, uint8_t oflag) { - return open(&dirFile, fileName, oflag); - } - /** \deprecated Do not use in new apps */ - uint8_t open(SdFile& dirFile, const char* fileName) { // NOLINT - return open(dirFile, fileName, O_RDWR); - } - /** \deprecated Use: - * uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag); - */ - uint8_t open(SdFile& dirFile, uint16_t index, uint8_t oflag) { // NOLINT - return open(&dirFile, index, oflag); - } - /** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */ - uint8_t openRoot(SdVolume& vol) {return openRoot(&vol);} // NOLINT - - /** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */ - int8_t readDir(dir_t& dir) {return readDir(&dir);} // NOLINT - /** \deprecated Use: - * static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName); - */ - static uint8_t remove(SdFile& dirFile, const char* fileName) { // NOLINT - return remove(&dirFile, fileName); - } -//------------------------------------------------------------------------------ -// rest are private - private: - static void (*oldDateTime_)(uint16_t& date, uint16_t& time); // NOLINT - static void oldToNew(uint16_t* date, uint16_t* time) { - uint16_t d; - uint16_t t; - oldDateTime_(d, t); - *date = d; - *time = t; - } -#endif // ALLOW_DEPRECATED_FUNCTIONS - private: - // bits defined in flags_ - // should be 0XF - static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); - // available bits - static uint8_t const F_UNUSED = 0X30; - // use unbuffered SD read - static uint8_t const F_FILE_UNBUFFERED_READ = 0X40; - // sync of directory entry required - static uint8_t const F_FILE_DIR_DIRTY = 0X80; - -// make sure F_OFLAG is ok -#if ((F_UNUSED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & F_OFLAG) -#error flags_ bits conflict -#endif // flags_ bits - - // private data - uint8_t flags_; // See above for definition of flags_ bits - uint8_t type_; // type of file see above for values - uint32_t curCluster_; // cluster for current file position - uint32_t curPosition_; // current file position in bytes from beginning - uint32_t dirBlock_; // SD block that contains directory entry for file - uint8_t dirIndex_; // index of entry in dirBlock 0 <= dirIndex_ <= 0XF - uint32_t fileSize_; // file size in bytes - uint32_t firstCluster_; // first cluster of file - SdVolume* vol_; // volume where file is located - - // private functions - uint8_t addCluster(void); - uint8_t addDirCluster(void); - dir_t* cacheDirEntry(uint8_t action); - static void (*dateTime_)(uint16_t* date, uint16_t* time); - static uint8_t make83Name(const char* str, uint8_t* name); - uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags); - dir_t* readDirCache(void); -}; -//============================================================================== -// SdVolume class -/** - * \brief Cache for an SD data block - */ -union cache_t { - /** Used to access cached file data blocks. */ - uint8_t data[512]; - /** Used to access cached FAT16 entries. */ - uint16_t fat16[256]; - /** Used to access cached FAT32 entries. */ - uint32_t fat32[128]; - /** Used to access cached directory entries. */ - dir_t dir[16]; - /** Used to access a cached MasterBoot Record. */ - mbr_t mbr; - /** Used to access to a cached FAT boot sector. */ - fbs_t fbs; -}; -//------------------------------------------------------------------------------ -/** - * \class SdVolume - * \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. - */ -class SdVolume { - public: - /** Create an instance of SdVolume */ - SdVolume(void) :allocSearchStart_(2), fatType_(0) {} - /** Clear the cache and returns a pointer to the cache. Used by the WaveRP - * recorder to do raw write to the SD card. Not for normal apps. - */ - static uint8_t* cacheClear(void) { - cacheFlush(); - cacheBlockNumber_ = 0XFFFFFFFF; - return cacheBuffer_.data; - } - /** - * Initialize a FAT volume. Try partition one first then try super - * floppy format. - * - * \param[in] dev The Sd2Card where the volume is located. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. Reasons for - * failure include not finding a valid partition, not finding a valid - * FAT file system or an I/O error. - */ - uint8_t init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0);} - uint8_t init(Sd2Card* dev, uint8_t part); - - // inline functions that return volume info - /** \return The volume's cluster size in blocks. */ - uint8_t blocksPerCluster(void) const {return blocksPerCluster_;} - /** \return The number of blocks in one FAT. */ - uint32_t blocksPerFat(void) const {return blocksPerFat_;} - /** \return The total number of clusters in the volume. */ - uint32_t clusterCount(void) const {return clusterCount_;} - /** \return The shift count required to multiply by blocksPerCluster. */ - uint8_t clusterSizeShift(void) const {return clusterSizeShift_;} - /** \return The logical block number for the start of file data. */ - uint32_t dataStartBlock(void) const {return dataStartBlock_;} - /** \return The number of FAT structures on the volume. */ - uint8_t fatCount(void) const {return fatCount_;} - /** \return The logical block number for the start of the first FAT. */ - uint32_t fatStartBlock(void) const {return fatStartBlock_;} - /** \return The FAT type of the volume. Values are 12, 16 or 32. */ - uint8_t fatType(void) const {return fatType_;} - /** \return The number of entries in the root directory for FAT16 volumes. */ - uint32_t rootDirEntryCount(void) const {return rootDirEntryCount_;} - /** \return The logical block number for the start of the root directory - on FAT16 volumes or the first cluster number on FAT32 volumes. */ - uint32_t rootDirStart(void) const {return rootDirStart_;} - /** return a pointer to the Sd2Card object for this volume */ - static Sd2Card* sdCard(void) {return sdCard_;} -//------------------------------------------------------------------------------ -#if ALLOW_DEPRECATED_FUNCTIONS - // Deprecated functions - suppress cpplint warnings with NOLINT comment - /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */ - uint8_t init(Sd2Card& dev) {return init(&dev);} // NOLINT - - /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */ - uint8_t init(Sd2Card& dev, uint8_t part) { // NOLINT - return init(&dev, part); - } -#endif // ALLOW_DEPRECATED_FUNCTIONS -//------------------------------------------------------------------------------ - private: - // Allow SdFile access to SdVolume private data. - friend class SdFile; - - // value for action argument in cacheRawBlock to indicate read from cache - static uint8_t const CACHE_FOR_READ = 0; - // value for action argument in cacheRawBlock to indicate cache dirty - static uint8_t const CACHE_FOR_WRITE = 1; - - static cache_t cacheBuffer_; // 512 byte cache for device blocks - static uint32_t cacheBlockNumber_; // Logical number of block in the cache - static Sd2Card* sdCard_; // Sd2Card object for cache - static uint8_t cacheDirty_; // cacheFlush() will write block if true - static uint32_t cacheMirrorBlock_; // block number for mirror FAT -// - uint32_t allocSearchStart_; // start cluster for alloc search - uint8_t blocksPerCluster_; // cluster size in blocks - uint32_t blocksPerFat_; // FAT size in blocks - uint32_t clusterCount_; // clusters in one FAT - uint8_t clusterSizeShift_; // shift to convert cluster count to block count - uint32_t dataStartBlock_; // first data block number - uint8_t fatCount_; // number of FATs on volume - uint32_t fatStartBlock_; // start block for first FAT - uint8_t fatType_; // volume type (12, 16, OR 32) - uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir - uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32 - //---------------------------------------------------------------------------- - uint8_t allocContiguous(uint32_t count, uint32_t* curCluster); - uint8_t blockOfCluster(uint32_t position) const { - return (position >> 9) & (blocksPerCluster_ - 1);} - uint32_t clusterStartBlock(uint32_t cluster) const { - return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_);} - uint32_t blockNumber(uint32_t cluster, uint32_t position) const { - return clusterStartBlock(cluster) + blockOfCluster(position);} - static uint8_t cacheFlush(void); - static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action); - static void cacheSetDirty(void) {cacheDirty_ |= CACHE_FOR_WRITE;} - static uint8_t cacheZeroBlock(uint32_t blockNumber); - uint8_t chainSize(uint32_t beginCluster, uint32_t* size) const; - uint8_t fatGet(uint32_t cluster, uint32_t* value) const; - uint8_t fatPut(uint32_t cluster, uint32_t value); - uint8_t fatPutEOC(uint32_t cluster) { - return fatPut(cluster, 0x0FFFFFFF); - } - uint8_t freeChain(uint32_t cluster); - uint8_t isEOC(uint32_t cluster) const { - return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN); - } - uint8_t readBlock(uint32_t block, uint8_t* dst) { - return sdCard_->readBlock(block, dst);} - uint8_t readData(uint32_t block, uint16_t offset, - uint16_t count, uint8_t* dst) { - return sdCard_->readData(block, offset, count, dst); - } - uint8_t writeBlock(uint32_t block, const uint8_t* dst) { - return sdCard_->writeBlock(block, dst); - } -}; -#endif // SdFat_h +/* Arduino SdFat Library + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ +#ifndef SdFat_h +#define SdFat_h +/** + \file + SdFile and SdVolume classes +*/ +#ifdef __AVR__ +#include +#endif +#include "Sd2Card.h" +#include "FatStructs.h" +#include "Print.h" +//------------------------------------------------------------------------------ +/** + Allow use of deprecated functions if non-zero +*/ +#define ALLOW_DEPRECATED_FUNCTIONS 1 +//------------------------------------------------------------------------------ +// forward declaration since SdVolume is used in SdFile +class SdVolume; +//============================================================================== +// SdFile class + +// flags for ls() +/** ls() flag to print modify date */ +uint8_t const LS_DATE = 1; +/** ls() flag to print file size */ +uint8_t const LS_SIZE = 2; +/** ls() flag for recursive list of subdirectories */ +uint8_t const LS_R = 4; + +// use the gnu style oflag in open() +/** open() oflag for reading */ +uint8_t const O_READ = 0X01; +/** open() oflag - same as O_READ */ +uint8_t const O_RDONLY = O_READ; +/** open() oflag for write */ +uint8_t const O_WRITE = 0X02; +/** open() oflag - same as O_WRITE */ +uint8_t const O_WRONLY = O_WRITE; +/** open() oflag for reading and writing */ +uint8_t const O_RDWR = (O_READ | O_WRITE); +/** open() oflag mask for access modes */ +uint8_t const O_ACCMODE = (O_READ | O_WRITE); +/** The file offset shall be set to the end of the file prior to each write. */ +uint8_t const O_APPEND = 0X04; +/** synchronous writes - call sync() after each write */ +uint8_t const O_SYNC = 0X08; +/** create the file if nonexistent */ +uint8_t const O_CREAT = 0X10; +/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ +uint8_t const O_EXCL = 0X20; +/** truncate the file to zero length */ +uint8_t const O_TRUNC = 0X40; + +// flags for timestamp +/** set the file's last access date */ +uint8_t const T_ACCESS = 1; +/** set the file's creation date and time */ +uint8_t const T_CREATE = 2; +/** Set the file's write date and time */ +uint8_t const T_WRITE = 4; +// values for type_ +/** This SdFile has not been opened. */ +uint8_t const FAT_FILE_TYPE_CLOSED = 0; +/** SdFile for a file */ +uint8_t const FAT_FILE_TYPE_NORMAL = 1; +/** SdFile for a FAT16 root directory */ +uint8_t const FAT_FILE_TYPE_ROOT16 = 2; +/** SdFile for a FAT32 root directory */ +uint8_t const FAT_FILE_TYPE_ROOT32 = 3; +/** SdFile for a subdirectory */ +uint8_t const FAT_FILE_TYPE_SUBDIR = 4; +/** Test value for directory type */ +uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT16; + +/** date field for FAT directory entry */ +static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) +{ + return (year - 1980) << 9 | month << 5 | day; +} +/** year part of FAT directory date field */ +static inline uint16_t FAT_YEAR(uint16_t fatDate) +{ + return 1980 + (fatDate >> 9); +} +/** month part of FAT directory date field */ +static inline uint8_t FAT_MONTH(uint16_t fatDate) +{ + return (fatDate >> 5) & 0XF; +} +/** day part of FAT directory date field */ +static inline uint8_t FAT_DAY(uint16_t fatDate) +{ + return fatDate & 0X1F; +} +/** time field for FAT directory entry */ +static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) +{ + return hour << 11 | minute << 5 | second >> 1; +} +/** hour part of FAT directory time field */ +static inline uint8_t FAT_HOUR(uint16_t fatTime) +{ + return fatTime >> 11; +} +/** minute part of FAT directory time field */ +static inline uint8_t FAT_MINUTE(uint16_t fatTime) +{ + return (fatTime >> 5) & 0X3F; +} +/** second part of FAT directory time field */ +static inline uint8_t FAT_SECOND(uint16_t fatTime) +{ + return 2 * (fatTime & 0X1F); +} +/** Default date for file timestamps is 1 Jan 2000 */ +uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; +/** Default time for file timestamp is 1 am */ +uint16_t const FAT_DEFAULT_TIME = (1 << 11); +//------------------------------------------------------------------------------ +/** + \class SdFile + \brief Access FAT16 and FAT32 files on SD and SDHC cards. +*/ +class SdFile : public Print +{ +public: + /** Create an instance of SdFile. */ + SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {} + /** + writeError is set to true if an error occurs during a write(). + Set writeError to false before calling print() and/or write() and check + for true after calls to print() and/or write(). + */ + //bool writeError; + /** + Cancel unbuffered reads for this file. + See setUnbufferedRead() + */ + void clearUnbufferedRead(void) + { + flags_ &= ~F_FILE_UNBUFFERED_READ; + } + uint8_t close(void); + uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + uint8_t createContiguous(SdFile* dirFile, + const char* fileName, uint32_t size); + /** \return The current cluster number for a file or directory. */ + uint32_t curCluster(void) const + { + return curCluster_; + } + /** \return The current position for a file or directory. */ + uint32_t curPosition(void) const + { + return curPosition_; + } + /** + Set the date/time callback function + + \param[in] dateTime The user's call back function. The callback + function is of the form: + + \code + void dateTime(uint16_t* date, uint16_t* time) { + uint16_t year; + uint8_t month, day, hour, minute, second; + + // User gets date and time from GPS or real-time clock here + + // return date using FAT_DATE macro to format fields + * *date = FAT_DATE(year, month, day); + + // return time using FAT_TIME macro to format fields + * *time = FAT_TIME(hour, minute, second); + } + \endcode + + Sets the function that is called when a file is created or when + a file's directory entry is modified by sync(). All timestamps, + access, creation, and modify, are set when a file is created. + sync() maintains the last access date and last modify date/time. + + See the timestamp() function. + */ + static void dateTimeCallback( + void (*dateTime)(uint16_t* date, uint16_t* time)) + { + dateTime_ = dateTime; + } + /** + Cancel the date/time callback function. + */ + static void dateTimeCallbackCancel(void) + { + // use explicit zero since NULL is not defined for Sanguino + dateTime_ = 0; + } + /** \return Address of the block that contains this file's directory. */ + uint32_t dirBlock(void) const + { + return dirBlock_; + } + uint8_t dirEntry(dir_t* dir); + /** \return Index of this file's directory in the block dirBlock. */ + uint8_t dirIndex(void) const + { + return dirIndex_; + } + static void dirName(const dir_t& dir, char* name); + /** \return The total number of bytes in a file or directory. */ + uint32_t fileSize(void) const + { + return fileSize_; + } + /** \return The first cluster number for a file or directory. */ + uint32_t firstCluster(void) const + { + return firstCluster_; + } + /** \return True if this is a SdFile for a directory else false. */ + uint8_t isDir(void) const + { + return type_ >= FAT_FILE_TYPE_MIN_DIR; + } + /** \return True if this is a SdFile for a file else false. */ + uint8_t isFile(void) const + { + return type_ == FAT_FILE_TYPE_NORMAL; + } + /** \return True if this is a SdFile for an open file/directory else false. */ + uint8_t isOpen(void) const + { + return type_ != FAT_FILE_TYPE_CLOSED; + } + /** \return True if this is a SdFile for a subdirectory else false. */ + uint8_t isSubDir(void) const + { + return type_ == FAT_FILE_TYPE_SUBDIR; + } + /** \return True if this is a SdFile for the root directory. */ + uint8_t isRoot(void) const + { + return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32; + } + void ls(uint8_t flags = 0, uint8_t indent = 0); + uint8_t makeDir(SdFile* dir, const char* dirName); + uint8_t open(SdFile* dirFile, uint16_t index, uint8_t oflag); + uint8_t open(SdFile* dirFile, const char* fileName, uint8_t oflag); + + uint8_t openRoot(SdVolume* vol); + static void printDirName(const dir_t& dir, uint8_t width); + static void printFatDate(uint16_t fatDate); + static void printFatTime(uint16_t fatTime); + static void printTwoDigits(uint8_t v); + /** + Read the next byte from a file. + + \return For success read returns the next byte in the file as an int. + If an error occurs or end of file is reached -1 is returned. + */ + int16_t read(void) + { + uint8_t b; + return read(&b, 1) == 1 ? b : -1; + } + int16_t read(void* buf, uint16_t nbyte); + int8_t readDir(dir_t* dir); + static uint8_t remove(SdFile* dirFile, const char* fileName); + uint8_t remove(void); + /** Set the file's current position to zero. */ + void rewind(void) + { + curPosition_ = curCluster_ = 0; + } + uint8_t rmDir(void); + uint8_t rmRfStar(void); + /** Set the files position to current position + \a pos. See seekSet(). */ + uint8_t seekCur(uint32_t pos) + { + return seekSet(curPosition_ + pos); + } + /** + Set the files current position to end of file. Useful to position + a file for append. See seekSet(). + */ + uint8_t seekEnd(void) + { + return seekSet(fileSize_); + } + uint8_t seekSet(uint32_t pos); + /** + Use unbuffered reads to access this file. Used with Wave + Shield ISR. Used with Sd2Card::partialBlockRead() in WaveRP. + + Not recommended for normal applications. + */ + void setUnbufferedRead(void) + { + if (isFile()) + { + flags_ |= F_FILE_UNBUFFERED_READ; + } + } + uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second); + uint8_t sync(void); + /** Type of this SdFile. You should use isFile() or isDir() instead of type() + if possible. + + \return The file or directory type. + */ + uint8_t type(void) const + { + return type_; + } + uint8_t truncate(uint32_t size); + /** \return Unbuffered read flag. */ + uint8_t unbufferedRead(void) const + { + return flags_ & F_FILE_UNBUFFERED_READ; + } + /** \return SdVolume that contains this file. */ + SdVolume* volume(void) const + { + return vol_; + } + size_t write(uint8_t b); + size_t write(const void* buf, uint16_t nbyte); + size_t write(const char* str); +#ifdef __AVR__ + void write_P(PGM_P str); + void writeln_P(PGM_P str); +#endif + //------------------------------------------------------------------------------ +#if ALLOW_DEPRECATED_FUNCTIONS + // Deprecated functions - suppress cpplint warnings with NOLINT comment + /** \deprecated Use: + uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + */ + uint8_t contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) // NOLINT + { + return contiguousRange(&bgnBlock, &endBlock); + } + /** \deprecated Use: + uint8_t SdFile::createContiguous(SdFile* dirFile, + const char* fileName, uint32_t size) + */ + uint8_t createContiguous(SdFile& dirFile, // NOLINT + const char* fileName, uint32_t size) + { + return createContiguous(&dirFile, fileName, size); + } + + /** + \deprecated Use: + static void SdFile::dateTimeCallback( + void (*dateTime)(uint16_t* date, uint16_t* time)); + */ + static void dateTimeCallback( + void (*dateTime)(uint16_t& date, uint16_t& time)) // NOLINT + { + oldDateTime_ = dateTime; + dateTime_ = dateTime ? oldToNew : 0; + } + /** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */ + uint8_t dirEntry(dir_t& dir) + { + return dirEntry(&dir); // NOLINT + } + /** \deprecated Use: + uint8_t SdFile::makeDir(SdFile* dir, const char* dirName); + */ + uint8_t makeDir(SdFile& dir, const char* dirName) // NOLINT + { + return makeDir(&dir, dirName); + } + /** \deprecated Use: + uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag); + */ + uint8_t open(SdFile& dirFile, // NOLINT + const char* fileName, uint8_t oflag) + { + return open(&dirFile, fileName, oflag); + } + /** \deprecated Do not use in new apps */ + uint8_t open(SdFile& dirFile, const char* fileName) // NOLINT + { + return open(dirFile, fileName, O_RDWR); + } + /** \deprecated Use: + uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag); + */ + uint8_t open(SdFile& dirFile, uint16_t index, uint8_t oflag) // NOLINT + { + return open(&dirFile, index, oflag); + } + /** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */ + uint8_t openRoot(SdVolume& vol) + { + return openRoot(&vol); // NOLINT + } + + /** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */ + int8_t readDir(dir_t& dir) + { + return readDir(&dir); // NOLINT + } + /** \deprecated Use: + static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName); + */ + static uint8_t remove(SdFile& dirFile, const char* fileName) // NOLINT + { + return remove(&dirFile, fileName); + } + //------------------------------------------------------------------------------ + // rest are private +private: + static void (*oldDateTime_)(uint16_t& date, uint16_t& time); // NOLINT + static void oldToNew(uint16_t* date, uint16_t* time) + { + uint16_t d; + uint16_t t; + oldDateTime_(d, t); + *date = d; + *time = t; + } +#endif // ALLOW_DEPRECATED_FUNCTIONS +private: + // bits defined in flags_ + // should be 0XF + static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); + // available bits + static uint8_t const F_UNUSED = 0X30; + // use unbuffered SD read + static uint8_t const F_FILE_UNBUFFERED_READ = 0X40; + // sync of directory entry required + static uint8_t const F_FILE_DIR_DIRTY = 0X80; + + // make sure F_OFLAG is ok +#if ((F_UNUSED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & F_OFLAG) +#error flags_ bits conflict +#endif // flags_ bits + + // private data + uint8_t flags_; // See above for definition of flags_ bits + uint8_t type_; // type of file see above for values + uint32_t curCluster_; // cluster for current file position + uint32_t curPosition_; // current file position in bytes from beginning + uint32_t dirBlock_; // SD block that contains directory entry for file + uint8_t dirIndex_; // index of entry in dirBlock 0 <= dirIndex_ <= 0XF + uint32_t fileSize_; // file size in bytes + uint32_t firstCluster_; // first cluster of file + SdVolume* vol_; // volume where file is located + + // private functions + uint8_t addCluster(void); + uint8_t addDirCluster(void); + dir_t* cacheDirEntry(uint8_t action); + static void (*dateTime_)(uint16_t* date, uint16_t* time); + static uint8_t make83Name(const char* str, uint8_t* name); + uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags); + dir_t* readDirCache(void); +}; +//============================================================================== +// SdVolume class +/** + \brief Cache for an SD data block +*/ +union cache_t +{ + /** Used to access cached file data blocks. */ + uint8_t data[512]; + /** Used to access cached FAT16 entries. */ + uint16_t fat16[256]; + /** Used to access cached FAT32 entries. */ + uint32_t fat32[128]; + /** Used to access cached directory entries. */ + dir_t dir[16]; + /** Used to access a cached MasterBoot Record. */ + mbr_t mbr; + /** Used to access to a cached FAT boot sector. */ + fbs_t fbs; +}; +//------------------------------------------------------------------------------ +/** + \class SdVolume + \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. +*/ +class SdVolume +{ +public: + /** Create an instance of SdVolume */ + SdVolume(void) : allocSearchStart_(2), fatType_(0) {} + /** Clear the cache and returns a pointer to the cache. Used by the WaveRP + recorder to do raw write to the SD card. Not for normal apps. + */ + static uint8_t* cacheClear(void) + { + cacheFlush(); + cacheBlockNumber_ = 0XFFFFFFFF; + return cacheBuffer_.data; + } + /** + Initialize a FAT volume. Try partition one first then try super + floppy format. + + \param[in] dev The Sd2Card where the volume is located. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. Reasons for + failure include not finding a valid partition, not finding a valid + FAT file system or an I/O error. + */ + uint8_t init(Sd2Card* dev) + { + return init(dev, 1) ? true : init(dev, 0); + } + uint8_t init(Sd2Card* dev, uint8_t part); + + // inline functions that return volume info + /** \return The volume's cluster size in blocks. */ + uint8_t blocksPerCluster(void) const + { + return blocksPerCluster_; + } + /** \return The number of blocks in one FAT. */ + uint32_t blocksPerFat(void) const + { + return blocksPerFat_; + } + /** \return The total number of clusters in the volume. */ + uint32_t clusterCount(void) const + { + return clusterCount_; + } + /** \return The shift count required to multiply by blocksPerCluster. */ + uint8_t clusterSizeShift(void) const + { + return clusterSizeShift_; + } + /** \return The logical block number for the start of file data. */ + uint32_t dataStartBlock(void) const + { + return dataStartBlock_; + } + /** \return The number of FAT structures on the volume. */ + uint8_t fatCount(void) const + { + return fatCount_; + } + /** \return The logical block number for the start of the first FAT. */ + uint32_t fatStartBlock(void) const + { + return fatStartBlock_; + } + /** \return The FAT type of the volume. Values are 12, 16 or 32. */ + uint8_t fatType(void) const + { + return fatType_; + } + /** \return The number of entries in the root directory for FAT16 volumes. */ + uint32_t rootDirEntryCount(void) const + { + return rootDirEntryCount_; + } + /** \return The logical block number for the start of the root directory + on FAT16 volumes or the first cluster number on FAT32 volumes. */ + uint32_t rootDirStart(void) const + { + return rootDirStart_; + } + /** return a pointer to the Sd2Card object for this volume */ + static Sd2Card* sdCard(void) + { + return sdCard_; + } + //------------------------------------------------------------------------------ +#if ALLOW_DEPRECATED_FUNCTIONS + // Deprecated functions - suppress cpplint warnings with NOLINT comment + /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */ + uint8_t init(Sd2Card& dev) + { + return init(&dev); // NOLINT + } + + /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */ + uint8_t init(Sd2Card& dev, uint8_t part) // NOLINT + { + return init(&dev, part); + } +#endif // ALLOW_DEPRECATED_FUNCTIONS + //------------------------------------------------------------------------------ +private: + // Allow SdFile access to SdVolume private data. + friend class SdFile; + + // value for action argument in cacheRawBlock to indicate read from cache + static uint8_t const CACHE_FOR_READ = 0; + // value for action argument in cacheRawBlock to indicate cache dirty + static uint8_t const CACHE_FOR_WRITE = 1; + + static cache_t cacheBuffer_; // 512 byte cache for device blocks + static uint32_t cacheBlockNumber_; // Logical number of block in the cache + static Sd2Card* sdCard_; // Sd2Card object for cache + static uint8_t cacheDirty_; // cacheFlush() will write block if true + static uint32_t cacheMirrorBlock_; // block number for mirror FAT + // + uint32_t allocSearchStart_; // start cluster for alloc search + uint8_t blocksPerCluster_; // cluster size in blocks + uint32_t blocksPerFat_; // FAT size in blocks + uint32_t clusterCount_; // clusters in one FAT + uint8_t clusterSizeShift_; // shift to convert cluster count to block count + uint32_t dataStartBlock_; // first data block number + uint8_t fatCount_; // number of FATs on volume + uint32_t fatStartBlock_; // start block for first FAT + uint8_t fatType_; // volume type (12, 16, OR 32) + uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir + uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32 + //---------------------------------------------------------------------------- + uint8_t allocContiguous(uint32_t count, uint32_t* curCluster); + uint8_t blockOfCluster(uint32_t position) const + { + return (position >> 9) & (blocksPerCluster_ - 1); + } + uint32_t clusterStartBlock(uint32_t cluster) const + { + return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_); + } + uint32_t blockNumber(uint32_t cluster, uint32_t position) const + { + return clusterStartBlock(cluster) + blockOfCluster(position); + } + static uint8_t cacheFlush(void); + static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action); + static void cacheSetDirty(void) + { + cacheDirty_ |= CACHE_FOR_WRITE; + } + static uint8_t cacheZeroBlock(uint32_t blockNumber); + uint8_t chainSize(uint32_t beginCluster, uint32_t* size) const; + uint8_t fatGet(uint32_t cluster, uint32_t* value) const; + uint8_t fatPut(uint32_t cluster, uint32_t value); + uint8_t fatPutEOC(uint32_t cluster) + { + return fatPut(cluster, 0x0FFFFFFF); + } + uint8_t freeChain(uint32_t cluster); + uint8_t isEOC(uint32_t cluster) const + { + return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN); + } + uint8_t readBlock(uint32_t block, uint8_t* dst) + { + return sdCard_->readBlock(block, dst); + } + uint8_t readData(uint32_t block, uint16_t offset, + uint16_t count, uint8_t* dst) + { + return sdCard_->readData(block, offset, count, dst); + } + uint8_t writeBlock(uint32_t block, const uint8_t* dst) + { + return sdCard_->writeBlock(block, dst); + } +}; +#endif // SdFat_h diff --git a/libraries/SD/src/utility/SdFatUtil.h b/libraries/SD/src/utility/SdFatUtil.h index d1b4d538f6..65098a91f2 100644 --- a/libraries/SD/src/utility/SdFatUtil.h +++ b/libraries/SD/src/utility/SdFatUtil.h @@ -1,75 +1,84 @@ -/* Arduino SdFat Library - * Copyright (C) 2008 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ -#ifndef SdFatUtil_h -#define SdFatUtil_h -/** - * \file - * Useful utility functions. - */ -#include -#ifdef __AVR__ -#include -/** Store and print a string in flash memory.*/ -#define PgmPrint(x) SerialPrint_P(PSTR(x)) -/** Store and print a string in flash memory followed by a CR/LF.*/ -#define PgmPrintln(x) SerialPrintln_P(PSTR(x)) -/** Defined so doxygen works for function definitions. */ -#endif -#define NOINLINE __attribute__((noinline,unused)) -#define UNUSEDOK __attribute__((unused)) -//------------------------------------------------------------------------------ -/** Return the number of bytes currently free in RAM. */ -static UNUSEDOK int FreeRam(void) { - extern int __bss_end; - extern int* __brkval; - int free_memory; - if (reinterpret_cast(__brkval) == 0) { - // if no heap use from end of bss section - free_memory = reinterpret_cast(&free_memory) - - reinterpret_cast(&__bss_end); - } else { - // use from top of stack to heap - free_memory = reinterpret_cast(&free_memory) - - reinterpret_cast(__brkval); - } - return free_memory; -} -#ifdef __AVR__ -//------------------------------------------------------------------------------ -/** - * %Print a string in flash memory to the serial port. - * - * \param[in] str Pointer to string stored in flash memory. - */ -static NOINLINE void SerialPrint_P(PGM_P str) { - for (uint8_t c; (c = pgm_read_byte(str)); str++) Serial.write(c); -} -//------------------------------------------------------------------------------ -/** - * %Print a string in flash memory followed by a CR/LF. - * - * \param[in] str Pointer to string stored in flash memory. - */ -static NOINLINE void SerialPrintln_P(PGM_P str) { - SerialPrint_P(str); - Serial.println(); -} -#endif // __AVR__ -#endif // #define SdFatUtil_h +/* Arduino SdFat Library + Copyright (C) 2008 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ +#ifndef SdFatUtil_h +#define SdFatUtil_h +/** + \file + Useful utility functions. +*/ +#include +#ifdef __AVR__ +#include +/** Store and print a string in flash memory.*/ +#define PgmPrint(x) SerialPrint_P(PSTR(x)) +/** Store and print a string in flash memory followed by a CR/LF.*/ +#define PgmPrintln(x) SerialPrintln_P(PSTR(x)) +/** Defined so doxygen works for function definitions. */ +#endif +#define NOINLINE __attribute__((noinline,unused)) +#define UNUSEDOK __attribute__((unused)) +//------------------------------------------------------------------------------ +/** Return the number of bytes currently free in RAM. */ +static UNUSEDOK int FreeRam(void) +{ + extern int __bss_end; + extern int* __brkval; + int free_memory; + if (reinterpret_cast(__brkval) == 0) + { + // if no heap use from end of bss section + free_memory = reinterpret_cast(&free_memory) + - reinterpret_cast(&__bss_end); + } + else + { + // use from top of stack to heap + free_memory = reinterpret_cast(&free_memory) + - reinterpret_cast(__brkval); + } + return free_memory; +} +#ifdef __AVR__ +//------------------------------------------------------------------------------ +/** + %Print a string in flash memory to the serial port. + + \param[in] str Pointer to string stored in flash memory. +*/ +static NOINLINE void SerialPrint_P(PGM_P str) +{ + for (uint8_t c; (c = pgm_read_byte(str)); str++) + { + Serial.write(c); + } +} +//------------------------------------------------------------------------------ +/** + %Print a string in flash memory followed by a CR/LF. + + \param[in] str Pointer to string stored in flash memory. +*/ +static NOINLINE void SerialPrintln_P(PGM_P str) +{ + SerialPrint_P(str); + Serial.println(); +} +#endif // __AVR__ +#endif // #define SdFatUtil_h diff --git a/libraries/SD/src/utility/SdFatmainpage.h b/libraries/SD/src/utility/SdFatmainpage.h index 73b3b63bd4..d4dee426df 100644 --- a/libraries/SD/src/utility/SdFatmainpage.h +++ b/libraries/SD/src/utility/SdFatmainpage.h @@ -1,202 +1,202 @@ -/* Arduino SdFat Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ - -/** -\mainpage Arduino SdFat Library -
Copyright © 2009 by William Greiman -
- -\section Intro Introduction -The Arduino SdFat Library is a minimal implementation of FAT16 and FAT32 -file systems on SD flash memory cards. Standard SD and high capacity -SDHC cards are supported. - -The SdFat only supports short 8.3 names. - -The main classes in SdFat are Sd2Card, SdVolume, and SdFile. - -The Sd2Card class supports access to standard SD cards and SDHC cards. Most -applications will only need to call the Sd2Card::init() member function. - -The SdVolume class supports FAT16 and FAT32 partitions. Most applications -will only need to call the SdVolume::init() member function. - -The SdFile class provides file access functions such as open(), read(), -remove(), write(), close() and sync(). This class supports access to the root -directory and subdirectories. - -A number of example are provided in the SdFat/examples folder. These were -developed to test SdFat and illustrate its use. - -SdFat was developed for high speed data recording. SdFat was used to implement -an audio record/play class, WaveRP, for the Adafruit Wave Shield. This -application uses special Sd2Card calls to write to contiguous files in raw mode. -These functions reduce write latency so that audio can be recorded with the -small amount of RAM in the Arduino. - -\section SDcard SD\SDHC Cards - -Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and -most consumer devices use the 4-bit parallel SD protocol. A card that -functions well on A PC or Mac may not work well on the Arduino. - -Most cards have good SPI read performance but cards vary widely in SPI -write performance. Write performance is limited by how efficiently the -card manages internal erase/remapping operations. The Arduino cannot -optimize writes to reduce erase operations because of its limit RAM. - -SanDisk cards generally have good write performance. They seem to have -more internal RAM buffering than other cards and therefore can limit -the number of flash erase operations that the Arduino forces due to its -limited RAM. - -\section Hardware Hardware Configuration - -SdFat was developed using an - Adafruit Industries - Wave Shield. - -The hardware interface to the SD card should not use a resistor based level -shifter. SdFat sets the SPI bus frequency to 8 MHz which results in signal -rise times that are too slow for the edge detectors in many newer SD card -controllers when resistor voltage dividers are used. - -The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the -74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield -uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the -74LCX245. - -If you are using a resistor based level shifter and are having problems try -setting the SPI bus frequency to 4 MHz. This can be done by using -card.init(SPI_HALF_SPEED) to initialize the SD card. - -\section comment Bugs and Comments - -If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net. - -\section SdFatClass SdFat Usage - -SdFat uses a slightly restricted form of short names. -Only printable ASCII characters are supported. No characters with code point -values greater than 127 are allowed. Space is not allowed even though space -was allowed in the API of early versions of DOS. - -Short names are limited to 8 characters followed by an optional period (.) -and extension of up to 3 characters. The characters may be any combination -of letters and digits. The following special characters are also allowed: - -$ % ' - _ @ ~ ` ! ( ) { } ^ # & - -Short names are always converted to upper case and their original case -value is lost. - -\note - The Arduino Print class uses character -at a time writes so it was necessary to use a \link SdFile::sync() sync() \endlink -function to control when data is written to the SD card. - -\par -An application which writes to a file using \link Print::print() print()\endlink, -\link Print::println() println() \endlink -or \link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink -at the appropriate time to force data and directory information to be written -to the SD Card. Data and directory information are also written to the SD card -when \link SdFile::close() close() \endlink is called. - -\par -Applications must use care calling \link SdFile::sync() sync() \endlink -since 2048 bytes of I/O is required to update file and -directory information. This includes writing the current data block, reading -the block that contains the directory entry for update, writing the directory -block back and reading back the current data block. - -It is possible to open a file with two or more instances of SdFile. A file may -be corrupted if data is written to the file by more than one instance of SdFile. - -\section HowTo How to format SD Cards as FAT Volumes - -You should use a freshly formatted SD card for best performance. FAT -file systems become slower if many files have been created and deleted. -This is because the directory entry for a deleted file is marked as deleted, -but is not deleted. When a new file is created, these entries must be scanned -before creating the file, a flaw in the FAT design. Also files can become -fragmented which causes reads and writes to be slower. - -Microsoft operating systems support removable media formatted with a -Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector -in block zero. - -Microsoft operating systems expect MBR formatted removable media -to have only one partition. The first partition should be used. - -Microsoft operating systems do not support partitioning SD flash cards. -If you erase an SD card with a program like KillDisk, Most versions of -Windows will format the card as a super floppy. - -The best way to restore an SD card's format is to use SDFormatter -which can be downloaded from: - -http://www.sdcard.org/consumers/formatter/ - -SDFormatter aligns flash erase boundaries with file -system structures which reduces write latency and file system overhead. - -SDFormatter does not have an option for FAT type so it may format -small cards as FAT12. - -After the MBR is restored by SDFormatter you may need to reformat small -cards that have been formatted FAT12 to force the volume type to be FAT16. - -If you reformat the SD card with an OS utility, choose a cluster size that -will result in: - -4084 < CountOfClusters && CountOfClusters < 65525 - -The volume will then be FAT16. - -If you are formatting an SD card on OS X or Linux, be sure to use the first -partition. Format this partition with a cluster count in above range. - -\section References References - -Adafruit Industries: - -http://www.adafruit.com/ - -http://www.ladyada.net/make/waveshield/ - -The Arduino site: - -http://www.arduino.cc/ - -For more information about FAT file systems see: - -http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx - -For information about using SD cards as SPI devices see: - -http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf - -The ATmega328 datasheet: - -http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf - - - */ +/* Arduino SdFat Library + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ + +/** + \mainpage Arduino SdFat Library +
Copyright © 2009 by William Greiman +
+ + \section Intro Introduction + The Arduino SdFat Library is a minimal implementation of FAT16 and FAT32 + file systems on SD flash memory cards. Standard SD and high capacity + SDHC cards are supported. + + The SdFat only supports short 8.3 names. + + The main classes in SdFat are Sd2Card, SdVolume, and SdFile. + + The Sd2Card class supports access to standard SD cards and SDHC cards. Most + applications will only need to call the Sd2Card::init() member function. + + The SdVolume class supports FAT16 and FAT32 partitions. Most applications + will only need to call the SdVolume::init() member function. + + The SdFile class provides file access functions such as open(), read(), + remove(), write(), close() and sync(). This class supports access to the root + directory and subdirectories. + + A number of example are provided in the SdFat/examples folder. These were + developed to test SdFat and illustrate its use. + + SdFat was developed for high speed data recording. SdFat was used to implement + an audio record/play class, WaveRP, for the Adafruit Wave Shield. This + application uses special Sd2Card calls to write to contiguous files in raw mode. + These functions reduce write latency so that audio can be recorded with the + small amount of RAM in the Arduino. + + \section SDcard SD\SDHC Cards + + Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and + most consumer devices use the 4-bit parallel SD protocol. A card that + functions well on A PC or Mac may not work well on the Arduino. + + Most cards have good SPI read performance but cards vary widely in SPI + write performance. Write performance is limited by how efficiently the + card manages internal erase/remapping operations. The Arduino cannot + optimize writes to reduce erase operations because of its limit RAM. + + SanDisk cards generally have good write performance. They seem to have + more internal RAM buffering than other cards and therefore can limit + the number of flash erase operations that the Arduino forces due to its + limited RAM. + + \section Hardware Hardware Configuration + + SdFat was developed using an + Adafruit Industries + Wave Shield. + + The hardware interface to the SD card should not use a resistor based level + shifter. SdFat sets the SPI bus frequency to 8 MHz which results in signal + rise times that are too slow for the edge detectors in many newer SD card + controllers when resistor voltage dividers are used. + + The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the + 74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield + uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the + 74LCX245. + + If you are using a resistor based level shifter and are having problems try + setting the SPI bus frequency to 4 MHz. This can be done by using + card.init(SPI_HALF_SPEED) to initialize the SD card. + + \section comment Bugs and Comments + + If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net. + + \section SdFatClass SdFat Usage + + SdFat uses a slightly restricted form of short names. + Only printable ASCII characters are supported. No characters with code point + values greater than 127 are allowed. Space is not allowed even though space + was allowed in the API of early versions of DOS. + + Short names are limited to 8 characters followed by an optional period (.) + and extension of up to 3 characters. The characters may be any combination + of letters and digits. The following special characters are also allowed: + + $ % ' - _ @ ~ ` ! ( ) { } ^ # & + + Short names are always converted to upper case and their original case + value is lost. + + \note + The Arduino Print class uses character + at a time writes so it was necessary to use a \link SdFile::sync() sync() \endlink + function to control when data is written to the SD card. + + \par + An application which writes to a file using \link Print::print() print()\endlink, + \link Print::println() println() \endlink + or \link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink + at the appropriate time to force data and directory information to be written + to the SD Card. Data and directory information are also written to the SD card + when \link SdFile::close() close() \endlink is called. + + \par + Applications must use care calling \link SdFile::sync() sync() \endlink + since 2048 bytes of I/O is required to update file and + directory information. This includes writing the current data block, reading + the block that contains the directory entry for update, writing the directory + block back and reading back the current data block. + + It is possible to open a file with two or more instances of SdFile. A file may + be corrupted if data is written to the file by more than one instance of SdFile. + + \section HowTo How to format SD Cards as FAT Volumes + + You should use a freshly formatted SD card for best performance. FAT + file systems become slower if many files have been created and deleted. + This is because the directory entry for a deleted file is marked as deleted, + but is not deleted. When a new file is created, these entries must be scanned + before creating the file, a flaw in the FAT design. Also files can become + fragmented which causes reads and writes to be slower. + + Microsoft operating systems support removable media formatted with a + Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector + in block zero. + + Microsoft operating systems expect MBR formatted removable media + to have only one partition. The first partition should be used. + + Microsoft operating systems do not support partitioning SD flash cards. + If you erase an SD card with a program like KillDisk, Most versions of + Windows will format the card as a super floppy. + + The best way to restore an SD card's format is to use SDFormatter + which can be downloaded from: + + http://www.sdcard.org/consumers/formatter/ + + SDFormatter aligns flash erase boundaries with file + system structures which reduces write latency and file system overhead. + + SDFormatter does not have an option for FAT type so it may format + small cards as FAT12. + + After the MBR is restored by SDFormatter you may need to reformat small + cards that have been formatted FAT12 to force the volume type to be FAT16. + + If you reformat the SD card with an OS utility, choose a cluster size that + will result in: + + 4084 < CountOfClusters && CountOfClusters < 65525 + + The volume will then be FAT16. + + If you are formatting an SD card on OS X or Linux, be sure to use the first + partition. Format this partition with a cluster count in above range. + + \section References References + + Adafruit Industries: + + http://www.adafruit.com/ + + http://www.ladyada.net/make/waveshield/ + + The Arduino site: + + http://www.arduino.cc/ + + For more information about FAT file systems see: + + http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + + For information about using SD cards as SPI devices see: + + http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf + + The ATmega328 datasheet: + + http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf + + +*/ diff --git a/libraries/SD/src/utility/SdFile.cpp b/libraries/SD/src/utility/SdFile.cpp index f43d8067c7..dd1aa56d31 100644 --- a/libraries/SD/src/utility/SdFile.cpp +++ b/libraries/SD/src/utility/SdFile.cpp @@ -1,1265 +1,1720 @@ -/* Arduino SdFat Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ -#include "SdFat.h" -#ifdef __AVR__ -#include -#endif -#include -#define PRINT_PORT Serial -//------------------------------------------------------------------------------ -// callback function for date/time -void (*SdFile::dateTime_)(uint16_t* date, uint16_t* time) = NULL; - -#if ALLOW_DEPRECATED_FUNCTIONS -// suppress cpplint warnings with NOLINT comment -void (*SdFile::oldDateTime_)(uint16_t& date, uint16_t& time) = NULL; // NOLINT -#endif // ALLOW_DEPRECATED_FUNCTIONS -//------------------------------------------------------------------------------ -// add a cluster to a file -uint8_t SdFile::addCluster() { - if (!vol_->allocContiguous(1, &curCluster_)) return false; - - // if first cluster of file link to directory entry - if (firstCluster_ == 0) { - firstCluster_ = curCluster_; - flags_ |= F_FILE_DIR_DIRTY; - } - return true; -} -//------------------------------------------------------------------------------ -// Add a cluster to a directory file and zero the cluster. -// return with first block of cluster in the cache -uint8_t SdFile::addDirCluster(void) { - if (!addCluster()) return false; - - // zero data in cluster insure first cluster is in cache - uint32_t block = vol_->clusterStartBlock(curCluster_); - for (uint8_t i = vol_->blocksPerCluster_; i != 0; i--) { - if (!SdVolume::cacheZeroBlock(block + i - 1)) return false; - } - // Increase directory file size by cluster size - fileSize_ += 512UL << vol_->clusterSizeShift_; - return true; -} -//------------------------------------------------------------------------------ -// cache a file's directory entry -// return pointer to cached entry or null for failure -dir_t* SdFile::cacheDirEntry(uint8_t action) { - if (!SdVolume::cacheRawBlock(dirBlock_, action)) return NULL; - return SdVolume::cacheBuffer_.dir + dirIndex_; -} -//------------------------------------------------------------------------------ -/** - * Close a file and force cached data and directory information - * to be written to the storage device. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include no file is open or an I/O error. - */ -uint8_t SdFile::close(void) { - if (!sync())return false; - type_ = FAT_FILE_TYPE_CLOSED; - return true; -} -//------------------------------------------------------------------------------ -/** - * Check for contiguous file and return its raw block range. - * - * \param[out] bgnBlock the first block address for the file. - * \param[out] endBlock the last block address for the file. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include file is not contiguous, file has zero length - * or an I/O error occurred. - */ -uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { - // error if no blocks - if (firstCluster_ == 0) return false; - - for (uint32_t c = firstCluster_; ; c++) { - uint32_t next; - if (!vol_->fatGet(c, &next)) return false; - - // check for contiguous - if (next != (c + 1)) { - // error if not end of chain - if (!vol_->isEOC(next)) return false; - *bgnBlock = vol_->clusterStartBlock(firstCluster_); - *endBlock = vol_->clusterStartBlock(c) - + vol_->blocksPerCluster_ - 1; - return true; - } - } -} -//------------------------------------------------------------------------------ -/** - * Create and open a new contiguous file of a specified size. - * - * \note This function only supports short DOS 8.3 names. - * See open() for more information. - * - * \param[in] dirFile The directory where the file will be created. - * \param[in] fileName A valid DOS 8.3 file name. - * \param[in] size The desired file size. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include \a fileName contains - * an invalid DOS 8.3 file name, the FAT volume has not been initialized, - * a file is already open, the file already exists, the root - * directory is full or an I/O error. - * - */ -uint8_t SdFile::createContiguous(SdFile* dirFile, - const char* fileName, uint32_t size) { - // don't allow zero length file - if (size == 0) return false; - if (!open(dirFile, fileName, O_CREAT | O_EXCL | O_RDWR)) return false; - - // calculate number of clusters needed - uint32_t count = ((size - 1) >> (vol_->clusterSizeShift_ + 9)) + 1; - - // allocate clusters - if (!vol_->allocContiguous(count, &firstCluster_)) { - remove(); - return false; - } - fileSize_ = size; - - // insure sync() will update dir entry - flags_ |= F_FILE_DIR_DIRTY; - return sync(); -} -//------------------------------------------------------------------------------ -/** - * Return a files directory entry - * - * \param[out] dir Location for return of the files directory entry. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ -uint8_t SdFile::dirEntry(dir_t* dir) { - // make sure fields on SD are correct - if (!sync()) return false; - - // read entry - dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_READ); - if (!p) return false; - - // copy to caller's struct - memcpy(dir, p, sizeof(dir_t)); - return true; -} -//------------------------------------------------------------------------------ -/** - * Format the name field of \a dir into the 13 byte array - * \a name in standard 8.3 short name format. - * - * \param[in] dir The directory structure containing the name. - * \param[out] name A 13 byte char array for the formatted name. - */ -void SdFile::dirName(const dir_t& dir, char* name) { - uint8_t j = 0; - for (uint8_t i = 0; i < 11; i++) { - if (dir.name[i] == ' ')continue; - if (i == 8) name[j++] = '.'; - name[j++] = dir.name[i]; - } - name[j] = 0; -} -//------------------------------------------------------------------------------ -/** List directory contents to Serial. - * - * \param[in] flags The inclusive OR of - * - * LS_DATE - %Print file modification date - * - * LS_SIZE - %Print file size. - * - * LS_R - Recursive list of subdirectories. - * - * \param[in] indent Amount of space before file name. Used for recursive - * list to indicate subdirectory level. - */ -void SdFile::ls(uint8_t flags, uint8_t indent) { - dir_t* p; - - rewind(); - while ((p = readDirCache())) { - // done if past last used entry - if (p->name[0] == DIR_NAME_FREE) break; - - // skip deleted entry and entries for . and .. - if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; - - // only list subdirectories and files - if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; - - // print any indent spaces - for (int8_t i = 0; i < indent; i++) PRINT_PORT.print(' '); - - // print file name with possible blank fill - printDirName(*p, flags & (LS_DATE | LS_SIZE) ? 14 : 0); - - // print modify date/time if requested - if (flags & LS_DATE) { - printFatDate(p->lastWriteDate); - PRINT_PORT.print(' '); - printFatTime(p->lastWriteTime); - } - // print size if requested - if (!DIR_IS_SUBDIR(p) && (flags & LS_SIZE)) { - PRINT_PORT.print(' '); - PRINT_PORT.print(p->fileSize); - } - PRINT_PORT.println(); - - // list subdirectory content if requested - if ((flags & LS_R) && DIR_IS_SUBDIR(p)) { - uint16_t index = curPosition()/32 - 1; - SdFile s; - if (s.open(this, index, O_READ)) s.ls(flags, indent + 2); - seekSet(32 * (index + 1)); - } - } -} -//------------------------------------------------------------------------------ -// format directory name field from a 8.3 name string -uint8_t SdFile::make83Name(const char* str, uint8_t* name) { - uint8_t c; - uint8_t n = 7; // max index for part before dot - uint8_t i = 0; - // blank fill name and extension - while (i < 11) name[i++] = ' '; - i = 0; - while ((c = *str++) != '\0') { - if (c == '.') { - if (n == 10) return false; // only one dot allowed - n = 10; // max index for full 8.3 name - i = 8; // place for extension - } else { - // illegal FAT characters -#if defined(__AVR__) - uint8_t b; - PGM_P p = PSTR("|<>^+=?/[];,*\"\\"); - while ((b = pgm_read_byte(p++))) if (b == c) return false; -#elif defined(__arm__) - uint8_t b; - const uint8_t valid[] = "|<>^+=?/[];,*\"\\"; - const uint8_t *p = valid; - while ((b = *p++)) if (b == c) return false; -#endif - // check size and only allow ASCII printable characters - if (i > n || c < 0X21 || c > 0X7E)return false; - // only upper case allowed in 8.3 names - convert lower to upper - name[i++] = c < 'a' || c > 'z' ? c : c + ('A' - 'a'); - } - } - // must have a file name, extension is optional - return name[0] != ' '; -} -//------------------------------------------------------------------------------ -/** Make a new directory. - * - * \param[in] dir An open SdFat instance for the directory that will containing - * the new directory. - * - * \param[in] dirName A valid 8.3 DOS name for the new directory. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include this SdFile is already open, \a dir is not a - * directory, \a dirName is invalid or already exists in \a dir. - */ -uint8_t SdFile::makeDir(SdFile* dir, const char* dirName) { - dir_t d; - - // create a normal file - if (!open(dir, dirName, O_CREAT | O_EXCL | O_RDWR)) return false; - - // convert SdFile to directory - flags_ = O_READ; - type_ = FAT_FILE_TYPE_SUBDIR; - - // allocate and zero first cluster - if (!addDirCluster())return false; - - // force entry to SD - if (!sync()) return false; - - // cache entry - should already be in cache due to sync() call - dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!p) return false; - - // change directory entry attribute - p->attributes = DIR_ATT_DIRECTORY; - - // make entry for '.' - memcpy(&d, p, sizeof(d)); - for (uint8_t i = 1; i < 11; i++) d.name[i] = ' '; - d.name[0] = '.'; - - // cache block for '.' and '..' - uint32_t block = vol_->clusterStartBlock(firstCluster_); - if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) return false; - - // copy '.' to block - memcpy(&SdVolume::cacheBuffer_.dir[0], &d, sizeof(d)); - - // make entry for '..' - d.name[1] = '.'; - if (dir->isRoot()) { - d.firstClusterLow = 0; - d.firstClusterHigh = 0; - } else { - d.firstClusterLow = dir->firstCluster_ & 0XFFFF; - d.firstClusterHigh = dir->firstCluster_ >> 16; - } - // copy '..' to block - memcpy(&SdVolume::cacheBuffer_.dir[1], &d, sizeof(d)); - - // set position after '..' - curPosition_ = 2 * sizeof(d); - - // write first block - return SdVolume::cacheFlush(); -} -//------------------------------------------------------------------------------ -/** - * Open a file or directory by name. - * - * \param[in] dirFile An open SdFat instance for the directory containing the - * file to be opened. - * - * \param[in] fileName A valid 8.3 DOS name for a file to be opened. - * - * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive - * OR of flags from the following list - * - * O_READ - Open for reading. - * - * O_RDONLY - Same as O_READ. - * - * O_WRITE - Open for writing. - * - * O_WRONLY - Same as O_WRITE. - * - * O_RDWR - Open for reading and writing. - * - * O_APPEND - If set, the file offset shall be set to the end of the - * file prior to each write. - * - * O_CREAT - If the file exists, this flag has no effect except as noted - * under O_EXCL below. Otherwise, the file shall be created - * - * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists. - * - * O_SYNC - Call sync() after each write. This flag should not be used with - * write(uint8_t), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class. - * These functions do character at a time writes so sync() will be called - * after each byte. - * - * O_TRUNC - If the file exists and is a regular file, and the file is - * successfully opened and is not read only, its length shall be truncated to 0. - * - * \note Directory files must be opened read only. Write and truncation is - * not allowed for directory files. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include this SdFile is already open, \a difFile is not - * a directory, \a fileName is invalid, the file does not exist - * or can't be opened in the access mode specified by oflag. - */ -uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag) { - uint8_t dname[11]; - dir_t* p; - - // error if already open - if (isOpen())return false; - - if (!make83Name(fileName, dname)) return false; - vol_ = dirFile->vol_; - dirFile->rewind(); - - // bool for empty entry found - uint8_t emptyFound = false; - - // search for file - while (dirFile->curPosition_ < dirFile->fileSize_) { - uint8_t index = 0XF & (dirFile->curPosition_ >> 5); - p = dirFile->readDirCache(); - if (p == NULL) return false; - - if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) { - // remember first empty slot - if (!emptyFound) { - emptyFound = true; - dirIndex_ = index; - dirBlock_ = SdVolume::cacheBlockNumber_; - } - // done if no entries follow - if (p->name[0] == DIR_NAME_FREE) break; - } else if (!memcmp(dname, p->name, 11)) { - // don't open existing file if O_CREAT and O_EXCL - if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) return false; - - // open found file - return openCachedEntry(0XF & index, oflag); - } - } - // only create file if O_CREAT and O_WRITE - if ((oflag & (O_CREAT | O_WRITE)) != (O_CREAT | O_WRITE)) return false; - - // cache found slot or add cluster if end of file - if (emptyFound) { - p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!p) return false; - } else { - if (dirFile->type_ == FAT_FILE_TYPE_ROOT16) return false; - - // add and zero cluster for dirFile - first cluster is in cache for write - if (!dirFile->addDirCluster()) return false; - - // use first entry in cluster - dirIndex_ = 0; - p = SdVolume::cacheBuffer_.dir; - } - // initialize as empty file - memset(p, 0, sizeof(dir_t)); - memcpy(p->name, dname, 11); - - // set timestamps - if (dateTime_) { - // call user function - dateTime_(&p->creationDate, &p->creationTime); - } else { - // use default date/time - p->creationDate = FAT_DEFAULT_DATE; - p->creationTime = FAT_DEFAULT_TIME; - } - p->lastAccessDate = p->creationDate; - p->lastWriteDate = p->creationDate; - p->lastWriteTime = p->creationTime; - - // force write of entry to SD - if (!SdVolume::cacheFlush()) return false; - - // open entry in cache - return openCachedEntry(dirIndex_, oflag); -} -//------------------------------------------------------------------------------ -/** - * Open a file by index. - * - * \param[in] dirFile An open SdFat instance for the directory. - * - * \param[in] index The \a index of the directory entry for the file to be - * opened. The value for \a index is (directory file position)/32. - * - * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive - * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. - * - * See open() by fileName for definition of flags and return values. - * - */ -uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag) { - // error if already open - if (isOpen())return false; - - // don't open existing file if O_CREAT and O_EXCL - user call error - if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) return false; - - vol_ = dirFile->vol_; - - // seek to location of entry - if (!dirFile->seekSet(32 * index)) return false; - - // read entry into cache - dir_t* p = dirFile->readDirCache(); - if (p == NULL) return false; - - // error if empty slot or '.' or '..' - if (p->name[0] == DIR_NAME_FREE || - p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { - return false; - } - // open cached entry - return openCachedEntry(index & 0XF, oflag); -} -//------------------------------------------------------------------------------ -// open a cached directory entry. Assumes vol_ is initializes -uint8_t SdFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { - // location of entry in cache - dir_t* p = SdVolume::cacheBuffer_.dir + dirIndex; - - // write or truncate is an error for a directory or read-only file - if (p->attributes & (DIR_ATT_READ_ONLY | DIR_ATT_DIRECTORY)) { - if (oflag & (O_WRITE | O_TRUNC)) return false; - } - // remember location of directory entry on SD - dirIndex_ = dirIndex; - dirBlock_ = SdVolume::cacheBlockNumber_; - - // copy first cluster number for directory fields - firstCluster_ = (uint32_t)p->firstClusterHigh << 16; - firstCluster_ |= p->firstClusterLow; - - // make sure it is a normal file or subdirectory - if (DIR_IS_FILE(p)) { - fileSize_ = p->fileSize; - type_ = FAT_FILE_TYPE_NORMAL; - } else if (DIR_IS_SUBDIR(p)) { - if (!vol_->chainSize(firstCluster_, &fileSize_)) return false; - type_ = FAT_FILE_TYPE_SUBDIR; - } else { - return false; - } - // save open flags for read/write - flags_ = oflag & (O_ACCMODE | O_SYNC | O_APPEND); - - // set to start of file - curCluster_ = 0; - curPosition_ = 0; - - // truncate file to zero length if requested - if (oflag & O_TRUNC) return truncate(0); - return true; -} -//------------------------------------------------------------------------------ -/** - * Open a volume's root directory. - * - * \param[in] vol The FAT volume containing the root directory to be opened. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include the FAT volume has not been initialized - * or it a FAT12 volume. - */ -uint8_t SdFile::openRoot(SdVolume* vol) { - // error if file is already open - if (isOpen()) return false; - - if (vol->fatType() == 16) { - type_ = FAT_FILE_TYPE_ROOT16; - firstCluster_ = 0; - fileSize_ = 32 * vol->rootDirEntryCount(); - } else if (vol->fatType() == 32) { - type_ = FAT_FILE_TYPE_ROOT32; - firstCluster_ = vol->rootDirStart(); - if (!vol->chainSize(firstCluster_, &fileSize_)) return false; - } else { - // volume is not initialized or FAT12 - return false; - } - vol_ = vol; - // read only - flags_ = O_READ; - - // set to start of file - curCluster_ = 0; - curPosition_ = 0; - - // root has no directory entry - dirBlock_ = 0; - dirIndex_ = 0; - return true; -} -//------------------------------------------------------------------------------ -/** %Print the name field of a directory entry in 8.3 format to Serial. - * - * \param[in] dir The directory structure containing the name. - * \param[in] width Blank fill name if length is less than \a width. - */ -void SdFile::printDirName(const dir_t& dir, uint8_t width) { - uint8_t w = 0; - for (uint8_t i = 0; i < 11; i++) { - if (dir.name[i] == ' ')continue; - if (i == 8) { - PRINT_PORT.print('.'); - w++; - } - PRINT_PORT.write(dir.name[i]); - w++; - } - if (DIR_IS_SUBDIR(&dir)) { - PRINT_PORT.print('/'); - w++; - } - while (w < width) { - PRINT_PORT.print(' '); - w++; - } -} -//------------------------------------------------------------------------------ -/** %Print a directory date field to Serial. - * - * Format is yyyy-mm-dd. - * - * \param[in] fatDate The date field from a directory entry. - */ -void SdFile::printFatDate(uint16_t fatDate) { - PRINT_PORT.print(FAT_YEAR(fatDate)); - PRINT_PORT.print('-'); - printTwoDigits(FAT_MONTH(fatDate)); - PRINT_PORT.print('-'); - printTwoDigits(FAT_DAY(fatDate)); -} -//------------------------------------------------------------------------------ -/** %Print a directory time field to Serial. - * - * Format is hh:mm:ss. - * - * \param[in] fatTime The time field from a directory entry. - */ -void SdFile::printFatTime(uint16_t fatTime) { - printTwoDigits(FAT_HOUR(fatTime)); - PRINT_PORT.print(':'); - printTwoDigits(FAT_MINUTE(fatTime)); - PRINT_PORT.print(':'); - printTwoDigits(FAT_SECOND(fatTime)); -} -//------------------------------------------------------------------------------ -/** %Print a value as two digits to Serial. - * - * \param[in] v Value to be printed, 0 <= \a v <= 99 - */ -void SdFile::printTwoDigits(uint8_t v) { - char str[3]; - str[0] = '0' + v/10; - str[1] = '0' + v % 10; - str[2] = 0; - PRINT_PORT.print(str); -} -//------------------------------------------------------------------------------ -/** - * Read data from a file starting at the current position. - * - * \param[out] buf Pointer to the location that will receive the data. - * - * \param[in] nbyte Maximum number of bytes to read. - * - * \return For success read() returns the number of bytes read. - * A value less than \a nbyte, including zero, will be returned - * if end of file is reached. - * If an error occurs, read() returns -1. Possible errors include - * read() called before a file has been opened, corrupt file system - * or an I/O error occurred. - */ -int16_t SdFile::read(void* buf, uint16_t nbyte) { - uint8_t* dst = reinterpret_cast(buf); - - // error if not open or write only - if (!isOpen() || !(flags_ & O_READ)) return -1; - - // max bytes left in file - if (nbyte > (fileSize_ - curPosition_)) nbyte = fileSize_ - curPosition_; - - // amount left to read - uint16_t toRead = nbyte; - while (toRead > 0) { - uint32_t block; // raw device block number - uint16_t offset = curPosition_ & 0X1FF; // offset in block - if (type_ == FAT_FILE_TYPE_ROOT16) { - block = vol_->rootDirStart() + (curPosition_ >> 9); - } else { - uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); - if (offset == 0 && blockOfCluster == 0) { - // start of new cluster - if (curPosition_ == 0) { - // use first cluster in file - curCluster_ = firstCluster_; - } else { - // get next cluster from FAT - if (!vol_->fatGet(curCluster_, &curCluster_)) return -1; - } - } - block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; - } - uint16_t n = toRead; - - // amount to be read from current block - if (n > (512 - offset)) n = 512 - offset; - - // no buffering needed if n == 512 or user requests no buffering - if ((unbufferedRead() || n == 512) && - block != SdVolume::cacheBlockNumber_) { - if (!vol_->readData(block, offset, n, dst)) return -1; - dst += n; - } else { - // read block to cache and copy data to caller - if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) return -1; - uint8_t* src = SdVolume::cacheBuffer_.data + offset; - uint8_t* end = src + n; - while (src != end) *dst++ = *src++; - } - curPosition_ += n; - toRead -= n; - } - return nbyte; -} -//------------------------------------------------------------------------------ -/** - * Read the next directory entry from a directory file. - * - * \param[out] dir The dir_t struct that will receive the data. - * - * \return For success readDir() returns the number of bytes read. - * A value of zero will be returned if end of file is reached. - * If an error occurs, readDir() returns -1. Possible errors include - * readDir() called before a directory has been opened, this is not - * a directory file or an I/O error occurred. - */ -int8_t SdFile::readDir(dir_t* dir) { - int8_t n; - // if not a directory file or miss-positioned return an error - if (!isDir() || (0X1F & curPosition_)) return -1; - - while ((n = read(dir, sizeof(dir_t))) == sizeof(dir_t)) { - // last entry if DIR_NAME_FREE - if (dir->name[0] == DIR_NAME_FREE) break; - // skip empty entries and entry for . and .. - if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') continue; - // return if normal file or subdirectory - if (DIR_IS_FILE_OR_SUBDIR(dir)) return n; - } - // error, end of file, or past last entry - return n < 0 ? -1 : 0; -} -//------------------------------------------------------------------------------ -// Read next directory entry into the cache -// Assumes file is correctly positioned -dir_t* SdFile::readDirCache(void) { - // error if not directory - if (!isDir()) return NULL; - - // index of entry in cache - uint8_t i = (curPosition_ >> 5) & 0XF; - - // use read to locate and cache block - if (read() < 0) return NULL; - - // advance to next entry - curPosition_ += 31; - - // return pointer to entry - return (SdVolume::cacheBuffer_.dir + i); -} -//------------------------------------------------------------------------------ -/** - * Remove a file. - * - * The directory entry and all data for the file are deleted. - * - * \note This function should not be used to delete the 8.3 version of a - * file that has a long name. For example if a file has the long name - * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include the file read-only, is a directory, - * or an I/O error occurred. - */ -uint8_t SdFile::remove(void) { - // free any clusters - will fail if read-only or directory - if (!truncate(0)) return false; - - // cache directory entry - dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) return false; - - // mark entry deleted - d->name[0] = DIR_NAME_DELETED; - - // set this SdFile closed - type_ = FAT_FILE_TYPE_CLOSED; - - // write entry to SD - return SdVolume::cacheFlush(); -} -//------------------------------------------------------------------------------ -/** - * Remove a file. - * - * The directory entry and all data for the file are deleted. - * - * \param[in] dirFile The directory that contains the file. - * \param[in] fileName The name of the file to be removed. - * - * \note This function should not be used to delete the 8.3 version of a - * file that has a long name. For example if a file has the long name - * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include the file is a directory, is read only, - * \a dirFile is not a directory, \a fileName is not found - * or an I/O error occurred. - */ -uint8_t SdFile::remove(SdFile* dirFile, const char* fileName) { - SdFile file; - if (!file.open(dirFile, fileName, O_WRITE)) return false; - return file.remove(); -} -//------------------------------------------------------------------------------ -/** Remove a directory file. - * - * The directory file will be removed only if it is empty and is not the - * root directory. rmDir() follows DOS and Windows and ignores the - * read-only attribute for the directory. - * - * \note This function should not be used to delete the 8.3 version of a - * directory that has a long name. For example if a directory has the - * long name "New folder" you should not delete the 8.3 name "NEWFOL~1". - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include the file is not a directory, is the root - * directory, is not empty, or an I/O error occurred. - */ -uint8_t SdFile::rmDir(void) { - // must be open subdirectory - if (!isSubDir()) return false; - - rewind(); - - // make sure directory is empty - while (curPosition_ < fileSize_) { - dir_t* p = readDirCache(); - if (p == NULL) return false; - // done if past last used entry - if (p->name[0] == DIR_NAME_FREE) break; - // skip empty slot or '.' or '..' - if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; - // error not empty - if (DIR_IS_FILE_OR_SUBDIR(p)) return false; - } - // convert empty directory to normal file for remove - type_ = FAT_FILE_TYPE_NORMAL; - flags_ |= O_WRITE; - return remove(); -} -//------------------------------------------------------------------------------ -/** Recursively delete a directory and all contained files. - * - * This is like the Unix/Linux 'rm -rf *' if called with the root directory - * hence the name. - * - * Warning - This will remove all contents of the directory including - * subdirectories. The directory will then be removed if it is not root. - * The read-only attribute for files will be ignored. - * - * \note This function should not be used to delete the 8.3 version of - * a directory that has a long name. See remove() and rmDir(). - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ -uint8_t SdFile::rmRfStar(void) { - rewind(); - while (curPosition_ < fileSize_) { - SdFile f; - - // remember position - uint16_t index = curPosition_/32; - - dir_t* p = readDirCache(); - if (!p) return false; - - // done if past last entry - if (p->name[0] == DIR_NAME_FREE) break; - - // skip empty slot or '.' or '..' - if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; - - // skip if part of long file name or volume label in root - if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; - - if (!f.open(this, index, O_READ)) return false; - if (f.isSubDir()) { - // recursively delete - if (!f.rmRfStar()) return false; - } else { - // ignore read-only - f.flags_ |= O_WRITE; - if (!f.remove()) return false; - } - // position to next entry if required - if (curPosition_ != (32*((uint32_t)index + 1))) { - if (!seekSet(32*(index + 1))) return false; - } - } - // don't try to delete root - if (isRoot()) return true; - return rmDir(); -} -//------------------------------------------------------------------------------ -/** - * Sets a file's position. - * - * \param[in] pos The new position in bytes from the beginning of the file. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ -uint8_t SdFile::seekSet(uint32_t pos) { - // error if file not open or seek past end of file - if (!isOpen() || pos > fileSize_) return false; - - if (type_ == FAT_FILE_TYPE_ROOT16) { - curPosition_ = pos; - return true; - } - if (pos == 0) { - // set position to start of file - curCluster_ = 0; - curPosition_ = 0; - return true; - } - // calculate cluster index for cur and new position - uint32_t nCur = (curPosition_ - 1) >> (vol_->clusterSizeShift_ + 9); - uint32_t nNew = (pos - 1) >> (vol_->clusterSizeShift_ + 9); - - if (nNew < nCur || curPosition_ == 0) { - // must follow chain from first cluster - curCluster_ = firstCluster_; - } else { - // advance from curPosition - nNew -= nCur; - } - while (nNew--) { - if (!vol_->fatGet(curCluster_, &curCluster_)) return false; - } - curPosition_ = pos; - return true; -} -//------------------------------------------------------------------------------ -/** - * The sync() call causes all modified data and directory fields - * to be written to the storage device. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include a call to sync() before a file has been - * opened or an I/O error. - */ -uint8_t SdFile::sync(void) { - // only allow open files and directories - if (!isOpen()) return false; - - if (flags_ & F_FILE_DIR_DIRTY) { - dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) return false; - - // do not set filesize for dir files - if (!isDir()) d->fileSize = fileSize_; - - // update first cluster fields - d->firstClusterLow = firstCluster_ & 0XFFFF; - d->firstClusterHigh = firstCluster_ >> 16; - - // set modify time if user supplied a callback date/time function - if (dateTime_) { - dateTime_(&d->lastWriteDate, &d->lastWriteTime); - d->lastAccessDate = d->lastWriteDate; - } - // clear directory dirty - flags_ &= ~F_FILE_DIR_DIRTY; - } - return SdVolume::cacheFlush(); -} -//------------------------------------------------------------------------------ -/** - * Set a file's timestamps in its directory entry. - * - * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive - * OR of flags from the following list - * - * T_ACCESS - Set the file's last access date. - * - * T_CREATE - Set the file's creation date and time. - * - * T_WRITE - Set the file's last write/modification date and time. - * - * \param[in] year Valid range 1980 - 2107 inclusive. - * - * \param[in] month Valid range 1 - 12 inclusive. - * - * \param[in] day Valid range 1 - 31 inclusive. - * - * \param[in] hour Valid range 0 - 23 inclusive. - * - * \param[in] minute Valid range 0 - 59 inclusive. - * - * \param[in] second Valid range 0 - 59 inclusive - * - * \note It is possible to set an invalid date since there is no check for - * the number of days in a month. - * - * \note - * Modify and access timestamps may be overwritten if a date time callback - * function has been set by dateTimeCallback(). - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ -uint8_t SdFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, - uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { - if (!isOpen() - || year < 1980 - || year > 2107 - || month < 1 - || month > 12 - || day < 1 - || day > 31 - || hour > 23 - || minute > 59 - || second > 59) { - return false; - } - dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) return false; - - uint16_t dirDate = FAT_DATE(year, month, day); - uint16_t dirTime = FAT_TIME(hour, minute, second); - if (flags & T_ACCESS) { - d->lastAccessDate = dirDate; - } - if (flags & T_CREATE) { - d->creationDate = dirDate; - d->creationTime = dirTime; - // seems to be units of 1/100 second not 1/10 as Microsoft states - d->creationTimeTenths = second & 1 ? 100 : 0; - } - if (flags & T_WRITE) { - d->lastWriteDate = dirDate; - d->lastWriteTime = dirTime; - } - SdVolume::cacheSetDirty(); - return sync(); -} -//------------------------------------------------------------------------------ -/** - * Truncate a file to a specified length. The current file position - * will be maintained if it is less than or equal to \a length otherwise - * it will be set to end of file. - * - * \param[in] length The desired length for the file. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include file is read only, file is a directory, - * \a length is greater than the current file size or an I/O error occurs. - */ -uint8_t SdFile::truncate(uint32_t length) { -// error if not a normal file or read-only - if (!isFile() || !(flags_ & O_WRITE)) return false; - - // error if length is greater than current size - if (length > fileSize_) return false; - - // fileSize and length are zero - nothing to do - if (fileSize_ == 0) return true; - - // remember position for seek after truncation - uint32_t newPos = curPosition_ > length ? length : curPosition_; - - // position to last cluster in truncated file - if (!seekSet(length)) return false; - - if (length == 0) { - // free all clusters - if (!vol_->freeChain(firstCluster_)) return false; - firstCluster_ = 0; - } else { - uint32_t toFree; - if (!vol_->fatGet(curCluster_, &toFree)) return false; - - if (!vol_->isEOC(toFree)) { - // free extra clusters - if (!vol_->freeChain(toFree)) return false; - - // current cluster is end of chain - if (!vol_->fatPutEOC(curCluster_)) return false; - } - } - fileSize_ = length; - - // need to update directory entry - flags_ |= F_FILE_DIR_DIRTY; - - if (!sync()) return false; - - // set file to correct position - return seekSet(newPos); -} -//------------------------------------------------------------------------------ -/** - * Write data to an open file. - * - * \note Data is moved to the cache but may not be written to the - * storage device until sync() is called. - * - * \param[in] buf Pointer to the location of the data to be written. - * - * \param[in] nbyte Number of bytes to write. - * - * \return For success write() returns the number of bytes written, always - * \a nbyte. If an error occurs, write() returns -1. Possible errors - * include write() is called before a file has been opened, write is called - * for a read-only file, device is full, a corrupt file system or an I/O error. - * - */ -size_t SdFile::write(const void* buf, uint16_t nbyte) { - // convert void* to uint8_t* - must be before goto statements - const uint8_t* src = reinterpret_cast(buf); - - // number of bytes left to write - must be before goto statements - uint16_t nToWrite = nbyte; - - // error if not a normal file or is read-only - if (!isFile() || !(flags_ & O_WRITE)) goto writeErrorReturn; - - // seek to end of file if append flag - if ((flags_ & O_APPEND) && curPosition_ != fileSize_) { - if (!seekEnd()) goto writeErrorReturn; - } - - while (nToWrite > 0) { - uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); - uint16_t blockOffset = curPosition_ & 0X1FF; - if (blockOfCluster == 0 && blockOffset == 0) { - // start of new cluster - if (curCluster_ == 0) { - if (firstCluster_ == 0) { - // allocate first cluster of file - if (!addCluster()) goto writeErrorReturn; - } else { - curCluster_ = firstCluster_; - } - } else { - uint32_t next; - if (!vol_->fatGet(curCluster_, &next)) return false; - if (vol_->isEOC(next)) { - // add cluster if at end of chain - if (!addCluster()) goto writeErrorReturn; - } else { - curCluster_ = next; - } - } - } - // max space in block - uint16_t n = 512 - blockOffset; - - // lesser of space and amount to write - if (n > nToWrite) n = nToWrite; - - // block for data write - uint32_t block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; - if (n == 512) { - // full block - don't need to use cache - // invalidate cache if block is in cache - if (SdVolume::cacheBlockNumber_ == block) { - SdVolume::cacheBlockNumber_ = 0XFFFFFFFF; - } - if (!vol_->writeBlock(block, src)) goto writeErrorReturn; - src += 512; - } else { - if (blockOffset == 0 && curPosition_ >= fileSize_) { - // start of new block don't need to read into cache - if (!SdVolume::cacheFlush()) goto writeErrorReturn; - SdVolume::cacheBlockNumber_ = block; - SdVolume::cacheSetDirty(); - } else { - // rewrite part of block - if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) { - goto writeErrorReturn; - } - } - uint8_t* dst = SdVolume::cacheBuffer_.data + blockOffset; - uint8_t* end = dst + n; - while (dst != end) *dst++ = *src++; - } - nToWrite -= n; - curPosition_ += n; - } - if (curPosition_ > fileSize_) { - // update fileSize and insure sync will update dir entry - fileSize_ = curPosition_; - flags_ |= F_FILE_DIR_DIRTY; - } else if (dateTime_ && nbyte) { - // insure sync will update modified date and time - flags_ |= F_FILE_DIR_DIRTY; - } - - if (flags_ & O_SYNC) { - if (!sync()) goto writeErrorReturn; - } - return nbyte; - - writeErrorReturn: - // return for write error - //writeError = true; - setWriteError(); - return 0; -} -//------------------------------------------------------------------------------ -/** - * Write a byte to a file. Required by the Arduino Print class. - * - * Use SdFile::writeError to check for errors. - */ -size_t SdFile::write(uint8_t b) { - return write(&b, 1); -} -//------------------------------------------------------------------------------ -/** - * Write a string to a file. Used by the Arduino Print class. - * - * Use SdFile::writeError to check for errors. - */ -size_t SdFile::write(const char* str) { - return write(str, strlen(str)); -} -#ifdef __AVR__ -//------------------------------------------------------------------------------ -/** - * Write a PROGMEM string to a file. - * - * Use SdFile::writeError to check for errors. - */ -void SdFile::write_P(PGM_P str) { - for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c); -} -//------------------------------------------------------------------------------ -/** - * Write a PROGMEM string followed by CR/LF to a file. - * - * Use SdFile::writeError to check for errors. - */ -void SdFile::writeln_P(PGM_P str) { - write_P(str); - println(); -} -#endif +/* Arduino SdFat Library + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ +#include "SdFat.h" +#ifdef __AVR__ +#include +#endif +#include +#define PRINT_PORT Serial +//------------------------------------------------------------------------------ +// callback function for date/time +void (*SdFile::dateTime_)(uint16_t* date, uint16_t* time) = NULL; + +#if ALLOW_DEPRECATED_FUNCTIONS +// suppress cpplint warnings with NOLINT comment +void (*SdFile::oldDateTime_)(uint16_t& date, uint16_t& time) = NULL; // NOLINT +#endif // ALLOW_DEPRECATED_FUNCTIONS +//------------------------------------------------------------------------------ +// add a cluster to a file +uint8_t SdFile::addCluster() +{ + if (!vol_->allocContiguous(1, &curCluster_)) + { + return false; + } + + // if first cluster of file link to directory entry + if (firstCluster_ == 0) + { + firstCluster_ = curCluster_; + flags_ |= F_FILE_DIR_DIRTY; + } + return true; +} +//------------------------------------------------------------------------------ +// Add a cluster to a directory file and zero the cluster. +// return with first block of cluster in the cache +uint8_t SdFile::addDirCluster(void) +{ + if (!addCluster()) + { + return false; + } + + // zero data in cluster insure first cluster is in cache + uint32_t block = vol_->clusterStartBlock(curCluster_); + for (uint8_t i = vol_->blocksPerCluster_; i != 0; i--) + { + if (!SdVolume::cacheZeroBlock(block + i - 1)) + { + return false; + } + } + // Increase directory file size by cluster size + fileSize_ += 512UL << vol_->clusterSizeShift_; + return true; +} +//------------------------------------------------------------------------------ +// cache a file's directory entry +// return pointer to cached entry or null for failure +dir_t* SdFile::cacheDirEntry(uint8_t action) +{ + if (!SdVolume::cacheRawBlock(dirBlock_, action)) + { + return NULL; + } + return SdVolume::cacheBuffer_.dir + dirIndex_; +} +//------------------------------------------------------------------------------ +/** + Close a file and force cached data and directory information + to be written to the storage device. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include no file is open or an I/O error. +*/ +uint8_t SdFile::close(void) +{ + if (!sync()) + { + return false; + } + type_ = FAT_FILE_TYPE_CLOSED; + return true; +} +//------------------------------------------------------------------------------ +/** + Check for contiguous file and return its raw block range. + + \param[out] bgnBlock the first block address for the file. + \param[out] endBlock the last block address for the file. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include file is not contiguous, file has zero length + or an I/O error occurred. +*/ +uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) +{ + // error if no blocks + if (firstCluster_ == 0) + { + return false; + } + + for (uint32_t c = firstCluster_; ; c++) + { + uint32_t next; + if (!vol_->fatGet(c, &next)) + { + return false; + } + + // check for contiguous + if (next != (c + 1)) + { + // error if not end of chain + if (!vol_->isEOC(next)) + { + return false; + } + *bgnBlock = vol_->clusterStartBlock(firstCluster_); + *endBlock = vol_->clusterStartBlock(c) + + vol_->blocksPerCluster_ - 1; + return true; + } + } +} +//------------------------------------------------------------------------------ +/** + Create and open a new contiguous file of a specified size. + + \note This function only supports short DOS 8.3 names. + See open() for more information. + + \param[in] dirFile The directory where the file will be created. + \param[in] fileName A valid DOS 8.3 file name. + \param[in] size The desired file size. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include \a fileName contains + an invalid DOS 8.3 file name, the FAT volume has not been initialized, + a file is already open, the file already exists, the root + directory is full or an I/O error. + +*/ +uint8_t SdFile::createContiguous(SdFile* dirFile, + const char* fileName, uint32_t size) +{ + // don't allow zero length file + if (size == 0) + { + return false; + } + if (!open(dirFile, fileName, O_CREAT | O_EXCL | O_RDWR)) + { + return false; + } + + // calculate number of clusters needed + uint32_t count = ((size - 1) >> (vol_->clusterSizeShift_ + 9)) + 1; + + // allocate clusters + if (!vol_->allocContiguous(count, &firstCluster_)) + { + remove(); + return false; + } + fileSize_ = size; + + // insure sync() will update dir entry + flags_ |= F_FILE_DIR_DIRTY; + return sync(); +} +//------------------------------------------------------------------------------ +/** + Return a files directory entry + + \param[out] dir Location for return of the files directory entry. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ +uint8_t SdFile::dirEntry(dir_t* dir) +{ + // make sure fields on SD are correct + if (!sync()) + { + return false; + } + + // read entry + dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_READ); + if (!p) + { + return false; + } + + // copy to caller's struct + memcpy(dir, p, sizeof(dir_t)); + return true; +} +//------------------------------------------------------------------------------ +/** + Format the name field of \a dir into the 13 byte array + \a name in standard 8.3 short name format. + + \param[in] dir The directory structure containing the name. + \param[out] name A 13 byte char array for the formatted name. +*/ +void SdFile::dirName(const dir_t& dir, char* name) +{ + uint8_t j = 0; + for (uint8_t i = 0; i < 11; i++) + { + if (dir.name[i] == ' ') + { + continue; + } + if (i == 8) + { + name[j++] = '.'; + } + name[j++] = dir.name[i]; + } + name[j] = 0; +} +//------------------------------------------------------------------------------ +/** List directory contents to Serial. + + \param[in] flags The inclusive OR of + + LS_DATE - %Print file modification date + + LS_SIZE - %Print file size. + + LS_R - Recursive list of subdirectories. + + \param[in] indent Amount of space before file name. Used for recursive + list to indicate subdirectory level. +*/ +void SdFile::ls(uint8_t flags, uint8_t indent) +{ + dir_t* p; + + rewind(); + while ((p = readDirCache())) + { + // done if past last used entry + if (p->name[0] == DIR_NAME_FREE) + { + break; + } + + // skip deleted entry and entries for . and .. + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') + { + continue; + } + + // only list subdirectories and files + if (!DIR_IS_FILE_OR_SUBDIR(p)) + { + continue; + } + + // print any indent spaces + for (int8_t i = 0; i < indent; i++) + { + PRINT_PORT.print(' '); + } + + // print file name with possible blank fill + printDirName(*p, flags & (LS_DATE | LS_SIZE) ? 14 : 0); + + // print modify date/time if requested + if (flags & LS_DATE) + { + printFatDate(p->lastWriteDate); + PRINT_PORT.print(' '); + printFatTime(p->lastWriteTime); + } + // print size if requested + if (!DIR_IS_SUBDIR(p) && (flags & LS_SIZE)) + { + PRINT_PORT.print(' '); + PRINT_PORT.print(p->fileSize); + } + PRINT_PORT.println(); + + // list subdirectory content if requested + if ((flags & LS_R) && DIR_IS_SUBDIR(p)) + { + uint16_t index = curPosition() / 32 - 1; + SdFile s; + if (s.open(this, index, O_READ)) + { + s.ls(flags, indent + 2); + } + seekSet(32 * (index + 1)); + } + } +} +//------------------------------------------------------------------------------ +// format directory name field from a 8.3 name string +uint8_t SdFile::make83Name(const char* str, uint8_t* name) +{ + uint8_t c; + uint8_t n = 7; // max index for part before dot + uint8_t i = 0; + // blank fill name and extension + while (i < 11) + { + name[i++] = ' '; + } + i = 0; + while ((c = *str++) != '\0') + { + if (c == '.') + { + if (n == 10) + { + return false; // only one dot allowed + } + n = 10; // max index for full 8.3 name + i = 8; // place for extension + } + else + { + // illegal FAT characters +#if defined(__AVR__) + uint8_t b; + PGM_P p = PSTR("|<>^+=?/[];,*\"\\"); + while ((b = pgm_read_byte(p++))) if (b == c) + { + return false; + } +#elif defined(__arm__) + uint8_t b; + const uint8_t valid[] = "|<>^+=?/[];,*\"\\"; + const uint8_t *p = valid; + while ((b = *p++)) if (b == c) + { + return false; + } +#endif + // check size and only allow ASCII printable characters + if (i > n || c < 0X21 || c > 0X7E) + { + return false; + } + // only upper case allowed in 8.3 names - convert lower to upper + name[i++] = c < 'a' || c > 'z' ? c : c + ('A' - 'a'); + } + } + // must have a file name, extension is optional + return name[0] != ' '; +} +//------------------------------------------------------------------------------ +/** Make a new directory. + + \param[in] dir An open SdFat instance for the directory that will containing + the new directory. + + \param[in] dirName A valid 8.3 DOS name for the new directory. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include this SdFile is already open, \a dir is not a + directory, \a dirName is invalid or already exists in \a dir. +*/ +uint8_t SdFile::makeDir(SdFile* dir, const char* dirName) +{ + dir_t d; + + // create a normal file + if (!open(dir, dirName, O_CREAT | O_EXCL | O_RDWR)) + { + return false; + } + + // convert SdFile to directory + flags_ = O_READ; + type_ = FAT_FILE_TYPE_SUBDIR; + + // allocate and zero first cluster + if (!addDirCluster()) + { + return false; + } + + // force entry to SD + if (!sync()) + { + return false; + } + + // cache entry - should already be in cache due to sync() call + dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!p) + { + return false; + } + + // change directory entry attribute + p->attributes = DIR_ATT_DIRECTORY; + + // make entry for '.' + memcpy(&d, p, sizeof(d)); + for (uint8_t i = 1; i < 11; i++) + { + d.name[i] = ' '; + } + d.name[0] = '.'; + + // cache block for '.' and '..' + uint32_t block = vol_->clusterStartBlock(firstCluster_); + if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) + { + return false; + } + + // copy '.' to block + memcpy(&SdVolume::cacheBuffer_.dir[0], &d, sizeof(d)); + + // make entry for '..' + d.name[1] = '.'; + if (dir->isRoot()) + { + d.firstClusterLow = 0; + d.firstClusterHigh = 0; + } + else + { + d.firstClusterLow = dir->firstCluster_ & 0XFFFF; + d.firstClusterHigh = dir->firstCluster_ >> 16; + } + // copy '..' to block + memcpy(&SdVolume::cacheBuffer_.dir[1], &d, sizeof(d)); + + // set position after '..' + curPosition_ = 2 * sizeof(d); + + // write first block + return SdVolume::cacheFlush(); +} +//------------------------------------------------------------------------------ +/** + Open a file or directory by name. + + \param[in] dirFile An open SdFat instance for the directory containing the + file to be opened. + + \param[in] fileName A valid 8.3 DOS name for a file to be opened. + + \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + OR of flags from the following list + + O_READ - Open for reading. + + O_RDONLY - Same as O_READ. + + O_WRITE - Open for writing. + + O_WRONLY - Same as O_WRITE. + + O_RDWR - Open for reading and writing. + + O_APPEND - If set, the file offset shall be set to the end of the + file prior to each write. + + O_CREAT - If the file exists, this flag has no effect except as noted + under O_EXCL below. Otherwise, the file shall be created + + O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists. + + O_SYNC - Call sync() after each write. This flag should not be used with + write(uint8_t), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class. + These functions do character at a time writes so sync() will be called + after each byte. + + O_TRUNC - If the file exists and is a regular file, and the file is + successfully opened and is not read only, its length shall be truncated to 0. + + \note Directory files must be opened read only. Write and truncation is + not allowed for directory files. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include this SdFile is already open, \a difFile is not + a directory, \a fileName is invalid, the file does not exist + or can't be opened in the access mode specified by oflag. +*/ +uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag) +{ + uint8_t dname[11]; + dir_t* p; + + // error if already open + if (isOpen()) + { + return false; + } + + if (!make83Name(fileName, dname)) + { + return false; + } + vol_ = dirFile->vol_; + dirFile->rewind(); + + // bool for empty entry found + uint8_t emptyFound = false; + + // search for file + while (dirFile->curPosition_ < dirFile->fileSize_) + { + uint8_t index = 0XF & (dirFile->curPosition_ >> 5); + p = dirFile->readDirCache(); + if (p == NULL) + { + return false; + } + + if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) + { + // remember first empty slot + if (!emptyFound) + { + emptyFound = true; + dirIndex_ = index; + dirBlock_ = SdVolume::cacheBlockNumber_; + } + // done if no entries follow + if (p->name[0] == DIR_NAME_FREE) + { + break; + } + } + else if (!memcmp(dname, p->name, 11)) + { + // don't open existing file if O_CREAT and O_EXCL + if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + { + return false; + } + + // open found file + return openCachedEntry(0XF & index, oflag); + } + } + // only create file if O_CREAT and O_WRITE + if ((oflag & (O_CREAT | O_WRITE)) != (O_CREAT | O_WRITE)) + { + return false; + } + + // cache found slot or add cluster if end of file + if (emptyFound) + { + p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!p) + { + return false; + } + } + else + { + if (dirFile->type_ == FAT_FILE_TYPE_ROOT16) + { + return false; + } + + // add and zero cluster for dirFile - first cluster is in cache for write + if (!dirFile->addDirCluster()) + { + return false; + } + + // use first entry in cluster + dirIndex_ = 0; + p = SdVolume::cacheBuffer_.dir; + } + // initialize as empty file + memset(p, 0, sizeof(dir_t)); + memcpy(p->name, dname, 11); + + // set timestamps + if (dateTime_) + { + // call user function + dateTime_(&p->creationDate, &p->creationTime); + } + else + { + // use default date/time + p->creationDate = FAT_DEFAULT_DATE; + p->creationTime = FAT_DEFAULT_TIME; + } + p->lastAccessDate = p->creationDate; + p->lastWriteDate = p->creationDate; + p->lastWriteTime = p->creationTime; + + // force write of entry to SD + if (!SdVolume::cacheFlush()) + { + return false; + } + + // open entry in cache + return openCachedEntry(dirIndex_, oflag); +} +//------------------------------------------------------------------------------ +/** + Open a file by index. + + \param[in] dirFile An open SdFat instance for the directory. + + \param[in] index The \a index of the directory entry for the file to be + opened. The value for \a index is (directory file position)/32. + + \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. + + See open() by fileName for definition of flags and return values. + +*/ +uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag) +{ + // error if already open + if (isOpen()) + { + return false; + } + + // don't open existing file if O_CREAT and O_EXCL - user call error + if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + { + return false; + } + + vol_ = dirFile->vol_; + + // seek to location of entry + if (!dirFile->seekSet(32 * index)) + { + return false; + } + + // read entry into cache + dir_t* p = dirFile->readDirCache(); + if (p == NULL) + { + return false; + } + + // error if empty slot or '.' or '..' + if (p->name[0] == DIR_NAME_FREE || + p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') + { + return false; + } + // open cached entry + return openCachedEntry(index & 0XF, oflag); +} +//------------------------------------------------------------------------------ +// open a cached directory entry. Assumes vol_ is initializes +uint8_t SdFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) +{ + // location of entry in cache + dir_t* p = SdVolume::cacheBuffer_.dir + dirIndex; + + // write or truncate is an error for a directory or read-only file + if (p->attributes & (DIR_ATT_READ_ONLY | DIR_ATT_DIRECTORY)) + { + if (oflag & (O_WRITE | O_TRUNC)) + { + return false; + } + } + // remember location of directory entry on SD + dirIndex_ = dirIndex; + dirBlock_ = SdVolume::cacheBlockNumber_; + + // copy first cluster number for directory fields + firstCluster_ = (uint32_t)p->firstClusterHigh << 16; + firstCluster_ |= p->firstClusterLow; + + // make sure it is a normal file or subdirectory + if (DIR_IS_FILE(p)) + { + fileSize_ = p->fileSize; + type_ = FAT_FILE_TYPE_NORMAL; + } + else if (DIR_IS_SUBDIR(p)) + { + if (!vol_->chainSize(firstCluster_, &fileSize_)) + { + return false; + } + type_ = FAT_FILE_TYPE_SUBDIR; + } + else + { + return false; + } + // save open flags for read/write + flags_ = oflag & (O_ACCMODE | O_SYNC | O_APPEND); + + // set to start of file + curCluster_ = 0; + curPosition_ = 0; + + // truncate file to zero length if requested + if (oflag & O_TRUNC) + { + return truncate(0); + } + return true; +} +//------------------------------------------------------------------------------ +/** + Open a volume's root directory. + + \param[in] vol The FAT volume containing the root directory to be opened. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include the FAT volume has not been initialized + or it a FAT12 volume. +*/ +uint8_t SdFile::openRoot(SdVolume* vol) +{ + // error if file is already open + if (isOpen()) + { + return false; + } + + if (vol->fatType() == 16) + { + type_ = FAT_FILE_TYPE_ROOT16; + firstCluster_ = 0; + fileSize_ = 32 * vol->rootDirEntryCount(); + } + else if (vol->fatType() == 32) + { + type_ = FAT_FILE_TYPE_ROOT32; + firstCluster_ = vol->rootDirStart(); + if (!vol->chainSize(firstCluster_, &fileSize_)) + { + return false; + } + } + else + { + // volume is not initialized or FAT12 + return false; + } + vol_ = vol; + // read only + flags_ = O_READ; + + // set to start of file + curCluster_ = 0; + curPosition_ = 0; + + // root has no directory entry + dirBlock_ = 0; + dirIndex_ = 0; + return true; +} +//------------------------------------------------------------------------------ +/** %Print the name field of a directory entry in 8.3 format to Serial. + + \param[in] dir The directory structure containing the name. + \param[in] width Blank fill name if length is less than \a width. +*/ +void SdFile::printDirName(const dir_t& dir, uint8_t width) +{ + uint8_t w = 0; + for (uint8_t i = 0; i < 11; i++) + { + if (dir.name[i] == ' ') + { + continue; + } + if (i == 8) + { + PRINT_PORT.print('.'); + w++; + } + PRINT_PORT.write(dir.name[i]); + w++; + } + if (DIR_IS_SUBDIR(&dir)) + { + PRINT_PORT.print('/'); + w++; + } + while (w < width) + { + PRINT_PORT.print(' '); + w++; + } +} +//------------------------------------------------------------------------------ +/** %Print a directory date field to Serial. + + Format is yyyy-mm-dd. + + \param[in] fatDate The date field from a directory entry. +*/ +void SdFile::printFatDate(uint16_t fatDate) +{ + PRINT_PORT.print(FAT_YEAR(fatDate)); + PRINT_PORT.print('-'); + printTwoDigits(FAT_MONTH(fatDate)); + PRINT_PORT.print('-'); + printTwoDigits(FAT_DAY(fatDate)); +} +//------------------------------------------------------------------------------ +/** %Print a directory time field to Serial. + + Format is hh:mm:ss. + + \param[in] fatTime The time field from a directory entry. +*/ +void SdFile::printFatTime(uint16_t fatTime) +{ + printTwoDigits(FAT_HOUR(fatTime)); + PRINT_PORT.print(':'); + printTwoDigits(FAT_MINUTE(fatTime)); + PRINT_PORT.print(':'); + printTwoDigits(FAT_SECOND(fatTime)); +} +//------------------------------------------------------------------------------ +/** %Print a value as two digits to Serial. + + \param[in] v Value to be printed, 0 <= \a v <= 99 +*/ +void SdFile::printTwoDigits(uint8_t v) +{ + char str[3]; + str[0] = '0' + v / 10; + str[1] = '0' + v % 10; + str[2] = 0; + PRINT_PORT.print(str); +} +//------------------------------------------------------------------------------ +/** + Read data from a file starting at the current position. + + \param[out] buf Pointer to the location that will receive the data. + + \param[in] nbyte Maximum number of bytes to read. + + \return For success read() returns the number of bytes read. + A value less than \a nbyte, including zero, will be returned + if end of file is reached. + If an error occurs, read() returns -1. Possible errors include + read() called before a file has been opened, corrupt file system + or an I/O error occurred. +*/ +int16_t SdFile::read(void* buf, uint16_t nbyte) +{ + uint8_t* dst = reinterpret_cast(buf); + + // error if not open or write only + if (!isOpen() || !(flags_ & O_READ)) + { + return -1; + } + + // max bytes left in file + if (nbyte > (fileSize_ - curPosition_)) + { + nbyte = fileSize_ - curPosition_; + } + + // amount left to read + uint16_t toRead = nbyte; + while (toRead > 0) + { + uint32_t block; // raw device block number + uint16_t offset = curPosition_ & 0X1FF; // offset in block + if (type_ == FAT_FILE_TYPE_ROOT16) + { + block = vol_->rootDirStart() + (curPosition_ >> 9); + } + else + { + uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); + if (offset == 0 && blockOfCluster == 0) + { + // start of new cluster + if (curPosition_ == 0) + { + // use first cluster in file + curCluster_ = firstCluster_; + } + else + { + // get next cluster from FAT + if (!vol_->fatGet(curCluster_, &curCluster_)) + { + return -1; + } + } + } + block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; + } + uint16_t n = toRead; + + // amount to be read from current block + if (n > (512 - offset)) + { + n = 512 - offset; + } + + // no buffering needed if n == 512 or user requests no buffering + if ((unbufferedRead() || n == 512) && + block != SdVolume::cacheBlockNumber_) + { + if (!vol_->readData(block, offset, n, dst)) + { + return -1; + } + dst += n; + } + else + { + // read block to cache and copy data to caller + if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) + { + return -1; + } + uint8_t* src = SdVolume::cacheBuffer_.data + offset; + uint8_t* end = src + n; + while (src != end) + { + *dst++ = *src++; + } + } + curPosition_ += n; + toRead -= n; + } + return nbyte; +} +//------------------------------------------------------------------------------ +/** + Read the next directory entry from a directory file. + + \param[out] dir The dir_t struct that will receive the data. + + \return For success readDir() returns the number of bytes read. + A value of zero will be returned if end of file is reached. + If an error occurs, readDir() returns -1. Possible errors include + readDir() called before a directory has been opened, this is not + a directory file or an I/O error occurred. +*/ +int8_t SdFile::readDir(dir_t* dir) +{ + int8_t n; + // if not a directory file or miss-positioned return an error + if (!isDir() || (0X1F & curPosition_)) + { + return -1; + } + + while ((n = read(dir, sizeof(dir_t))) == sizeof(dir_t)) + { + // last entry if DIR_NAME_FREE + if (dir->name[0] == DIR_NAME_FREE) + { + break; + } + // skip empty entries and entry for . and .. + if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') + { + continue; + } + // return if normal file or subdirectory + if (DIR_IS_FILE_OR_SUBDIR(dir)) + { + return n; + } + } + // error, end of file, or past last entry + return n < 0 ? -1 : 0; +} +//------------------------------------------------------------------------------ +// Read next directory entry into the cache +// Assumes file is correctly positioned +dir_t* SdFile::readDirCache(void) +{ + // error if not directory + if (!isDir()) + { + return NULL; + } + + // index of entry in cache + uint8_t i = (curPosition_ >> 5) & 0XF; + + // use read to locate and cache block + if (read() < 0) + { + return NULL; + } + + // advance to next entry + curPosition_ += 31; + + // return pointer to entry + return (SdVolume::cacheBuffer_.dir + i); +} +//------------------------------------------------------------------------------ +/** + Remove a file. + + The directory entry and all data for the file are deleted. + + \note This function should not be used to delete the 8.3 version of a + file that has a long name. For example if a file has the long name + "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include the file read-only, is a directory, + or an I/O error occurred. +*/ +uint8_t SdFile::remove(void) +{ + // free any clusters - will fail if read-only or directory + if (!truncate(0)) + { + return false; + } + + // cache directory entry + dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) + { + return false; + } + + // mark entry deleted + d->name[0] = DIR_NAME_DELETED; + + // set this SdFile closed + type_ = FAT_FILE_TYPE_CLOSED; + + // write entry to SD + return SdVolume::cacheFlush(); +} +//------------------------------------------------------------------------------ +/** + Remove a file. + + The directory entry and all data for the file are deleted. + + \param[in] dirFile The directory that contains the file. + \param[in] fileName The name of the file to be removed. + + \note This function should not be used to delete the 8.3 version of a + file that has a long name. For example if a file has the long name + "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include the file is a directory, is read only, + \a dirFile is not a directory, \a fileName is not found + or an I/O error occurred. +*/ +uint8_t SdFile::remove(SdFile* dirFile, const char* fileName) +{ + SdFile file; + if (!file.open(dirFile, fileName, O_WRITE)) + { + return false; + } + return file.remove(); +} +//------------------------------------------------------------------------------ +/** Remove a directory file. + + The directory file will be removed only if it is empty and is not the + root directory. rmDir() follows DOS and Windows and ignores the + read-only attribute for the directory. + + \note This function should not be used to delete the 8.3 version of a + directory that has a long name. For example if a directory has the + long name "New folder" you should not delete the 8.3 name "NEWFOL~1". + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include the file is not a directory, is the root + directory, is not empty, or an I/O error occurred. +*/ +uint8_t SdFile::rmDir(void) +{ + // must be open subdirectory + if (!isSubDir()) + { + return false; + } + + rewind(); + + // make sure directory is empty + while (curPosition_ < fileSize_) + { + dir_t* p = readDirCache(); + if (p == NULL) + { + return false; + } + // done if past last used entry + if (p->name[0] == DIR_NAME_FREE) + { + break; + } + // skip empty slot or '.' or '..' + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') + { + continue; + } + // error not empty + if (DIR_IS_FILE_OR_SUBDIR(p)) + { + return false; + } + } + // convert empty directory to normal file for remove + type_ = FAT_FILE_TYPE_NORMAL; + flags_ |= O_WRITE; + return remove(); +} +//------------------------------------------------------------------------------ +/** Recursively delete a directory and all contained files. + + This is like the Unix/Linux 'rm -rf *' if called with the root directory + hence the name. + + Warning - This will remove all contents of the directory including + subdirectories. The directory will then be removed if it is not root. + The read-only attribute for files will be ignored. + + \note This function should not be used to delete the 8.3 version of + a directory that has a long name. See remove() and rmDir(). + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ +uint8_t SdFile::rmRfStar(void) +{ + rewind(); + while (curPosition_ < fileSize_) + { + SdFile f; + + // remember position + uint16_t index = curPosition_ / 32; + + dir_t* p = readDirCache(); + if (!p) + { + return false; + } + + // done if past last entry + if (p->name[0] == DIR_NAME_FREE) + { + break; + } + + // skip empty slot or '.' or '..' + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') + { + continue; + } + + // skip if part of long file name or volume label in root + if (!DIR_IS_FILE_OR_SUBDIR(p)) + { + continue; + } + + if (!f.open(this, index, O_READ)) + { + return false; + } + if (f.isSubDir()) + { + // recursively delete + if (!f.rmRfStar()) + { + return false; + } + } + else + { + // ignore read-only + f.flags_ |= O_WRITE; + if (!f.remove()) + { + return false; + } + } + // position to next entry if required + if (curPosition_ != (32 * ((uint32_t)index + 1))) + { + if (!seekSet(32 * (index + 1))) + { + return false; + } + } + } + // don't try to delete root + if (isRoot()) + { + return true; + } + return rmDir(); +} +//------------------------------------------------------------------------------ +/** + Sets a file's position. + + \param[in] pos The new position in bytes from the beginning of the file. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ +uint8_t SdFile::seekSet(uint32_t pos) +{ + // error if file not open or seek past end of file + if (!isOpen() || pos > fileSize_) + { + return false; + } + + if (type_ == FAT_FILE_TYPE_ROOT16) + { + curPosition_ = pos; + return true; + } + if (pos == 0) + { + // set position to start of file + curCluster_ = 0; + curPosition_ = 0; + return true; + } + // calculate cluster index for cur and new position + uint32_t nCur = (curPosition_ - 1) >> (vol_->clusterSizeShift_ + 9); + uint32_t nNew = (pos - 1) >> (vol_->clusterSizeShift_ + 9); + + if (nNew < nCur || curPosition_ == 0) + { + // must follow chain from first cluster + curCluster_ = firstCluster_; + } + else + { + // advance from curPosition + nNew -= nCur; + } + while (nNew--) + { + if (!vol_->fatGet(curCluster_, &curCluster_)) + { + return false; + } + } + curPosition_ = pos; + return true; +} +//------------------------------------------------------------------------------ +/** + The sync() call causes all modified data and directory fields + to be written to the storage device. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include a call to sync() before a file has been + opened or an I/O error. +*/ +uint8_t SdFile::sync(void) +{ + // only allow open files and directories + if (!isOpen()) + { + return false; + } + + if (flags_ & F_FILE_DIR_DIRTY) + { + dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) + { + return false; + } + + // do not set filesize for dir files + if (!isDir()) + { + d->fileSize = fileSize_; + } + + // update first cluster fields + d->firstClusterLow = firstCluster_ & 0XFFFF; + d->firstClusterHigh = firstCluster_ >> 16; + + // set modify time if user supplied a callback date/time function + if (dateTime_) + { + dateTime_(&d->lastWriteDate, &d->lastWriteTime); + d->lastAccessDate = d->lastWriteDate; + } + // clear directory dirty + flags_ &= ~F_FILE_DIR_DIRTY; + } + return SdVolume::cacheFlush(); +} +//------------------------------------------------------------------------------ +/** + Set a file's timestamps in its directory entry. + + \param[in] flags Values for \a flags are constructed by a bitwise-inclusive + OR of flags from the following list + + T_ACCESS - Set the file's last access date. + + T_CREATE - Set the file's creation date and time. + + T_WRITE - Set the file's last write/modification date and time. + + \param[in] year Valid range 1980 - 2107 inclusive. + + \param[in] month Valid range 1 - 12 inclusive. + + \param[in] day Valid range 1 - 31 inclusive. + + \param[in] hour Valid range 0 - 23 inclusive. + + \param[in] minute Valid range 0 - 59 inclusive. + + \param[in] second Valid range 0 - 59 inclusive + + \note It is possible to set an invalid date since there is no check for + the number of days in a month. + + \note + Modify and access timestamps may be overwritten if a date time callback + function has been set by dateTimeCallback(). + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ +uint8_t SdFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, + uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) +{ + if (!isOpen() + || year < 1980 + || year > 2107 + || month < 1 + || month > 12 + || day < 1 + || day > 31 + || hour > 23 + || minute > 59 + || second > 59) + { + return false; + } + dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); + if (!d) + { + return false; + } + + uint16_t dirDate = FAT_DATE(year, month, day); + uint16_t dirTime = FAT_TIME(hour, minute, second); + if (flags & T_ACCESS) + { + d->lastAccessDate = dirDate; + } + if (flags & T_CREATE) + { + d->creationDate = dirDate; + d->creationTime = dirTime; + // seems to be units of 1/100 second not 1/10 as Microsoft states + d->creationTimeTenths = second & 1 ? 100 : 0; + } + if (flags & T_WRITE) + { + d->lastWriteDate = dirDate; + d->lastWriteTime = dirTime; + } + SdVolume::cacheSetDirty(); + return sync(); +} +//------------------------------------------------------------------------------ +/** + Truncate a file to a specified length. The current file position + will be maintained if it is less than or equal to \a length otherwise + it will be set to end of file. + + \param[in] length The desired length for the file. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include file is read only, file is a directory, + \a length is greater than the current file size or an I/O error occurs. +*/ +uint8_t SdFile::truncate(uint32_t length) +{ + // error if not a normal file or read-only + if (!isFile() || !(flags_ & O_WRITE)) + { + return false; + } + + // error if length is greater than current size + if (length > fileSize_) + { + return false; + } + + // fileSize and length are zero - nothing to do + if (fileSize_ == 0) + { + return true; + } + + // remember position for seek after truncation + uint32_t newPos = curPosition_ > length ? length : curPosition_; + + // position to last cluster in truncated file + if (!seekSet(length)) + { + return false; + } + + if (length == 0) + { + // free all clusters + if (!vol_->freeChain(firstCluster_)) + { + return false; + } + firstCluster_ = 0; + } + else + { + uint32_t toFree; + if (!vol_->fatGet(curCluster_, &toFree)) + { + return false; + } + + if (!vol_->isEOC(toFree)) + { + // free extra clusters + if (!vol_->freeChain(toFree)) + { + return false; + } + + // current cluster is end of chain + if (!vol_->fatPutEOC(curCluster_)) + { + return false; + } + } + } + fileSize_ = length; + + // need to update directory entry + flags_ |= F_FILE_DIR_DIRTY; + + if (!sync()) + { + return false; + } + + // set file to correct position + return seekSet(newPos); +} +//------------------------------------------------------------------------------ +/** + Write data to an open file. + + \note Data is moved to the cache but may not be written to the + storage device until sync() is called. + + \param[in] buf Pointer to the location of the data to be written. + + \param[in] nbyte Number of bytes to write. + + \return For success write() returns the number of bytes written, always + \a nbyte. If an error occurs, write() returns -1. Possible errors + include write() is called before a file has been opened, write is called + for a read-only file, device is full, a corrupt file system or an I/O error. + +*/ +size_t SdFile::write(const void* buf, uint16_t nbyte) +{ + // convert void* to uint8_t* - must be before goto statements + const uint8_t* src = reinterpret_cast(buf); + + // number of bytes left to write - must be before goto statements + uint16_t nToWrite = nbyte; + + // error if not a normal file or is read-only + if (!isFile() || !(flags_ & O_WRITE)) + { + goto writeErrorReturn; + } + + // seek to end of file if append flag + if ((flags_ & O_APPEND) && curPosition_ != fileSize_) + { + if (!seekEnd()) + { + goto writeErrorReturn; + } + } + + while (nToWrite > 0) + { + uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); + uint16_t blockOffset = curPosition_ & 0X1FF; + if (blockOfCluster == 0 && blockOffset == 0) + { + // start of new cluster + if (curCluster_ == 0) + { + if (firstCluster_ == 0) + { + // allocate first cluster of file + if (!addCluster()) + { + goto writeErrorReturn; + } + } + else + { + curCluster_ = firstCluster_; + } + } + else + { + uint32_t next; + if (!vol_->fatGet(curCluster_, &next)) + { + return false; + } + if (vol_->isEOC(next)) + { + // add cluster if at end of chain + if (!addCluster()) + { + goto writeErrorReturn; + } + } + else + { + curCluster_ = next; + } + } + } + // max space in block + uint16_t n = 512 - blockOffset; + + // lesser of space and amount to write + if (n > nToWrite) + { + n = nToWrite; + } + + // block for data write + uint32_t block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; + if (n == 512) + { + // full block - don't need to use cache + // invalidate cache if block is in cache + if (SdVolume::cacheBlockNumber_ == block) + { + SdVolume::cacheBlockNumber_ = 0XFFFFFFFF; + } + if (!vol_->writeBlock(block, src)) + { + goto writeErrorReturn; + } + src += 512; + } + else + { + if (blockOffset == 0 && curPosition_ >= fileSize_) + { + // start of new block don't need to read into cache + if (!SdVolume::cacheFlush()) + { + goto writeErrorReturn; + } + SdVolume::cacheBlockNumber_ = block; + SdVolume::cacheSetDirty(); + } + else + { + // rewrite part of block + if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) + { + goto writeErrorReturn; + } + } + uint8_t* dst = SdVolume::cacheBuffer_.data + blockOffset; + uint8_t* end = dst + n; + while (dst != end) + { + *dst++ = *src++; + } + } + nToWrite -= n; + curPosition_ += n; + } + if (curPosition_ > fileSize_) + { + // update fileSize and insure sync will update dir entry + fileSize_ = curPosition_; + flags_ |= F_FILE_DIR_DIRTY; + } + else if (dateTime_ && nbyte) + { + // insure sync will update modified date and time + flags_ |= F_FILE_DIR_DIRTY; + } + + if (flags_ & O_SYNC) + { + if (!sync()) + { + goto writeErrorReturn; + } + } + return nbyte; + +writeErrorReturn: + // return for write error + //writeError = true; + setWriteError(); + return 0; +} +//------------------------------------------------------------------------------ +/** + Write a byte to a file. Required by the Arduino Print class. + + Use SdFile::writeError to check for errors. +*/ +size_t SdFile::write(uint8_t b) +{ + return write(&b, 1); +} +//------------------------------------------------------------------------------ +/** + Write a string to a file. Used by the Arduino Print class. + + Use SdFile::writeError to check for errors. +*/ +size_t SdFile::write(const char* str) +{ + return write(str, strlen(str)); +} +#ifdef __AVR__ +//------------------------------------------------------------------------------ +/** + Write a PROGMEM string to a file. + + Use SdFile::writeError to check for errors. +*/ +void SdFile::write_P(PGM_P str) +{ + for (uint8_t c; (c = pgm_read_byte(str)); str++) + { + write(c); + } +} +//------------------------------------------------------------------------------ +/** + Write a PROGMEM string followed by CR/LF to a file. + + Use SdFile::writeError to check for errors. +*/ +void SdFile::writeln_P(PGM_P str) +{ + write_P(str); + println(); +} +#endif diff --git a/libraries/SD/src/utility/SdInfo.h b/libraries/SD/src/utility/SdInfo.h index acde74d974..67e767c930 100644 --- a/libraries/SD/src/utility/SdInfo.h +++ b/libraries/SD/src/utility/SdInfo.h @@ -1,232 +1,236 @@ -/* Arduino Sd2Card Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino Sd2Card Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino Sd2Card Library. If not, see - * . - */ -#ifndef SdInfo_h -#define SdInfo_h -#include -// Based on the document: -// -// SD Specifications -// Part 1 -// Physical Layer -// Simplified Specification -// Version 2.00 -// September 25, 2006 -// -// www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf -//------------------------------------------------------------------------------ -// SD card commands -/** GO_IDLE_STATE - init card in spi mode if CS low */ -uint8_t const CMD0 = 0X00; -/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ -uint8_t const CMD8 = 0X08; -/** SEND_CSD - read the Card Specific Data (CSD register) */ -uint8_t const CMD9 = 0X09; -/** SEND_CID - read the card identification information (CID register) */ -uint8_t const CMD10 = 0X0A; -/** SEND_STATUS - read the card status register */ -uint8_t const CMD13 = 0X0D; -/** READ_BLOCK - read a single data block from the card */ -uint8_t const CMD17 = 0X11; -/** WRITE_BLOCK - write a single data block to the card */ -uint8_t const CMD24 = 0X18; -/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ -uint8_t const CMD25 = 0X19; -/** ERASE_WR_BLK_START - sets the address of the first block to be erased */ -uint8_t const CMD32 = 0X20; -/** ERASE_WR_BLK_END - sets the address of the last block of the continuous - range to be erased*/ -uint8_t const CMD33 = 0X21; -/** ERASE - erase all previously selected blocks */ -uint8_t const CMD38 = 0X26; -/** APP_CMD - escape for application specific command */ -uint8_t const CMD55 = 0X37; -/** READ_OCR - read the OCR register of a card */ -uint8_t const CMD58 = 0X3A; -/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be - pre-erased before writing */ -uint8_t const ACMD23 = 0X17; -/** SD_SEND_OP_COMD - Sends host capacity support information and - activates the card's initialization process */ -uint8_t const ACMD41 = 0X29; -//------------------------------------------------------------------------------ -/** status for card in the ready state */ -uint8_t const R1_READY_STATE = 0X00; -/** status for card in the idle state */ -uint8_t const R1_IDLE_STATE = 0X01; -/** status bit for illegal command */ -uint8_t const R1_ILLEGAL_COMMAND = 0X04; -/** start data token for read or write single block*/ -uint8_t const DATA_START_BLOCK = 0XFE; -/** stop token for write multiple blocks*/ -uint8_t const STOP_TRAN_TOKEN = 0XFD; -/** start data token for write multiple blocks*/ -uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC; -/** mask for data response tokens after a write block operation */ -uint8_t const DATA_RES_MASK = 0X1F; -/** write data accepted token */ -uint8_t const DATA_RES_ACCEPTED = 0X05; -//------------------------------------------------------------------------------ -typedef struct CID { - // byte 0 - uint8_t mid; // Manufacturer ID - // byte 1-2 - char oid[2]; // OEM/Application ID - // byte 3-7 - char pnm[5]; // Product name - // byte 8 - unsigned prv_m : 4; // Product revision n.m - unsigned prv_n : 4; - // byte 9-12 - uint32_t psn; // Product serial number - // byte 13 - unsigned mdt_year_high : 4; // Manufacturing date - unsigned reserved : 4; - // byte 14 - unsigned mdt_month : 4; - unsigned mdt_year_low :4; - // byte 15 - unsigned always1 : 1; - unsigned crc : 7; -}cid_t; -//------------------------------------------------------------------------------ -// CSD for version 1.00 cards -typedef struct CSDV1 { - // byte 0 - unsigned reserved1 : 6; - unsigned csd_ver : 2; - // byte 1 - uint8_t taac; - // byte 2 - uint8_t nsac; - // byte 3 - uint8_t tran_speed; - // byte 4 - uint8_t ccc_high; - // byte 5 - unsigned read_bl_len : 4; - unsigned ccc_low : 4; - // byte 6 - unsigned c_size_high : 2; - unsigned reserved2 : 2; - unsigned dsr_imp : 1; - unsigned read_blk_misalign :1; - unsigned write_blk_misalign : 1; - unsigned read_bl_partial : 1; - // byte 7 - uint8_t c_size_mid; - // byte 8 - unsigned vdd_r_curr_max : 3; - unsigned vdd_r_curr_min : 3; - unsigned c_size_low :2; - // byte 9 - unsigned c_size_mult_high : 2; - unsigned vdd_w_cur_max : 3; - unsigned vdd_w_curr_min : 3; - // byte 10 - unsigned sector_size_high : 6; - unsigned erase_blk_en : 1; - unsigned c_size_mult_low : 1; - // byte 11 - unsigned wp_grp_size : 7; - unsigned sector_size_low : 1; - // byte 12 - unsigned write_bl_len_high : 2; - unsigned r2w_factor : 3; - unsigned reserved3 : 2; - unsigned wp_grp_enable : 1; - // byte 13 - unsigned reserved4 : 5; - unsigned write_partial : 1; - unsigned write_bl_len_low : 2; - // byte 14 - unsigned reserved5: 2; - unsigned file_format : 2; - unsigned tmp_write_protect : 1; - unsigned perm_write_protect : 1; - unsigned copy : 1; - unsigned file_format_grp : 1; - // byte 15 - unsigned always1 : 1; - unsigned crc : 7; -}csd1_t; -//------------------------------------------------------------------------------ -// CSD for version 2.00 cards -typedef struct CSDV2 { - // byte 0 - unsigned reserved1 : 6; - unsigned csd_ver : 2; - // byte 1 - uint8_t taac; - // byte 2 - uint8_t nsac; - // byte 3 - uint8_t tran_speed; - // byte 4 - uint8_t ccc_high; - // byte 5 - unsigned read_bl_len : 4; - unsigned ccc_low : 4; - // byte 6 - unsigned reserved2 : 4; - unsigned dsr_imp : 1; - unsigned read_blk_misalign :1; - unsigned write_blk_misalign : 1; - unsigned read_bl_partial : 1; - // byte 7 - unsigned reserved3 : 2; - unsigned c_size_high : 6; - // byte 8 - uint8_t c_size_mid; - // byte 9 - uint8_t c_size_low; - // byte 10 - unsigned sector_size_high : 6; - unsigned erase_blk_en : 1; - unsigned reserved4 : 1; - // byte 11 - unsigned wp_grp_size : 7; - unsigned sector_size_low : 1; - // byte 12 - unsigned write_bl_len_high : 2; - unsigned r2w_factor : 3; - unsigned reserved5 : 2; - unsigned wp_grp_enable : 1; - // byte 13 - unsigned reserved6 : 5; - unsigned write_partial : 1; - unsigned write_bl_len_low : 2; - // byte 14 - unsigned reserved7: 2; - unsigned file_format : 2; - unsigned tmp_write_protect : 1; - unsigned perm_write_protect : 1; - unsigned copy : 1; - unsigned file_format_grp : 1; - // byte 15 - unsigned always1 : 1; - unsigned crc : 7; -}csd2_t; -//------------------------------------------------------------------------------ -// union of old and new style CSD register -union csd_t { - csd1_t v1; - csd2_t v2; -}; -#endif // SdInfo_h +/* Arduino Sd2Card Library + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino Sd2Card Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino Sd2Card Library. If not, see + . +*/ +#ifndef SdInfo_h +#define SdInfo_h +#include +// Based on the document: +// +// SD Specifications +// Part 1 +// Physical Layer +// Simplified Specification +// Version 2.00 +// September 25, 2006 +// +// www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf +//------------------------------------------------------------------------------ +// SD card commands +/** GO_IDLE_STATE - init card in spi mode if CS low */ +uint8_t const CMD0 = 0X00; +/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ +uint8_t const CMD8 = 0X08; +/** SEND_CSD - read the Card Specific Data (CSD register) */ +uint8_t const CMD9 = 0X09; +/** SEND_CID - read the card identification information (CID register) */ +uint8_t const CMD10 = 0X0A; +/** SEND_STATUS - read the card status register */ +uint8_t const CMD13 = 0X0D; +/** READ_BLOCK - read a single data block from the card */ +uint8_t const CMD17 = 0X11; +/** WRITE_BLOCK - write a single data block to the card */ +uint8_t const CMD24 = 0X18; +/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ +uint8_t const CMD25 = 0X19; +/** ERASE_WR_BLK_START - sets the address of the first block to be erased */ +uint8_t const CMD32 = 0X20; +/** ERASE_WR_BLK_END - sets the address of the last block of the continuous + range to be erased*/ +uint8_t const CMD33 = 0X21; +/** ERASE - erase all previously selected blocks */ +uint8_t const CMD38 = 0X26; +/** APP_CMD - escape for application specific command */ +uint8_t const CMD55 = 0X37; +/** READ_OCR - read the OCR register of a card */ +uint8_t const CMD58 = 0X3A; +/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be + pre-erased before writing */ +uint8_t const ACMD23 = 0X17; +/** SD_SEND_OP_COMD - Sends host capacity support information and + activates the card's initialization process */ +uint8_t const ACMD41 = 0X29; +//------------------------------------------------------------------------------ +/** status for card in the ready state */ +uint8_t const R1_READY_STATE = 0X00; +/** status for card in the idle state */ +uint8_t const R1_IDLE_STATE = 0X01; +/** status bit for illegal command */ +uint8_t const R1_ILLEGAL_COMMAND = 0X04; +/** start data token for read or write single block*/ +uint8_t const DATA_START_BLOCK = 0XFE; +/** stop token for write multiple blocks*/ +uint8_t const STOP_TRAN_TOKEN = 0XFD; +/** start data token for write multiple blocks*/ +uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC; +/** mask for data response tokens after a write block operation */ +uint8_t const DATA_RES_MASK = 0X1F; +/** write data accepted token */ +uint8_t const DATA_RES_ACCEPTED = 0X05; +//------------------------------------------------------------------------------ +typedef struct CID +{ + // byte 0 + uint8_t mid; // Manufacturer ID + // byte 1-2 + char oid[2]; // OEM/Application ID + // byte 3-7 + char pnm[5]; // Product name + // byte 8 + unsigned prv_m : 4; // Product revision n.m + unsigned prv_n : 4; + // byte 9-12 + uint32_t psn; // Product serial number + // byte 13 + unsigned mdt_year_high : 4; // Manufacturing date + unsigned reserved : 4; + // byte 14 + unsigned mdt_month : 4; + unsigned mdt_year_low : 4; + // byte 15 + unsigned always1 : 1; + unsigned crc : 7; +} cid_t; +//------------------------------------------------------------------------------ +// CSD for version 1.00 cards +typedef struct CSDV1 +{ + // byte 0 + unsigned reserved1 : 6; + unsigned csd_ver : 2; + // byte 1 + uint8_t taac; + // byte 2 + uint8_t nsac; + // byte 3 + uint8_t tran_speed; + // byte 4 + uint8_t ccc_high; + // byte 5 + unsigned read_bl_len : 4; + unsigned ccc_low : 4; + // byte 6 + unsigned c_size_high : 2; + unsigned reserved2 : 2; + unsigned dsr_imp : 1; + unsigned read_blk_misalign : 1; + unsigned write_blk_misalign : 1; + unsigned read_bl_partial : 1; + // byte 7 + uint8_t c_size_mid; + // byte 8 + unsigned vdd_r_curr_max : 3; + unsigned vdd_r_curr_min : 3; + unsigned c_size_low : 2; + // byte 9 + unsigned c_size_mult_high : 2; + unsigned vdd_w_cur_max : 3; + unsigned vdd_w_curr_min : 3; + // byte 10 + unsigned sector_size_high : 6; + unsigned erase_blk_en : 1; + unsigned c_size_mult_low : 1; + // byte 11 + unsigned wp_grp_size : 7; + unsigned sector_size_low : 1; + // byte 12 + unsigned write_bl_len_high : 2; + unsigned r2w_factor : 3; + unsigned reserved3 : 2; + unsigned wp_grp_enable : 1; + // byte 13 + unsigned reserved4 : 5; + unsigned write_partial : 1; + unsigned write_bl_len_low : 2; + // byte 14 + unsigned reserved5: 2; + unsigned file_format : 2; + unsigned tmp_write_protect : 1; + unsigned perm_write_protect : 1; + unsigned copy : 1; + unsigned file_format_grp : 1; + // byte 15 + unsigned always1 : 1; + unsigned crc : 7; +} csd1_t; +//------------------------------------------------------------------------------ +// CSD for version 2.00 cards +typedef struct CSDV2 +{ + // byte 0 + unsigned reserved1 : 6; + unsigned csd_ver : 2; + // byte 1 + uint8_t taac; + // byte 2 + uint8_t nsac; + // byte 3 + uint8_t tran_speed; + // byte 4 + uint8_t ccc_high; + // byte 5 + unsigned read_bl_len : 4; + unsigned ccc_low : 4; + // byte 6 + unsigned reserved2 : 4; + unsigned dsr_imp : 1; + unsigned read_blk_misalign : 1; + unsigned write_blk_misalign : 1; + unsigned read_bl_partial : 1; + // byte 7 + unsigned reserved3 : 2; + unsigned c_size_high : 6; + // byte 8 + uint8_t c_size_mid; + // byte 9 + uint8_t c_size_low; + // byte 10 + unsigned sector_size_high : 6; + unsigned erase_blk_en : 1; + unsigned reserved4 : 1; + // byte 11 + unsigned wp_grp_size : 7; + unsigned sector_size_low : 1; + // byte 12 + unsigned write_bl_len_high : 2; + unsigned r2w_factor : 3; + unsigned reserved5 : 2; + unsigned wp_grp_enable : 1; + // byte 13 + unsigned reserved6 : 5; + unsigned write_partial : 1; + unsigned write_bl_len_low : 2; + // byte 14 + unsigned reserved7: 2; + unsigned file_format : 2; + unsigned tmp_write_protect : 1; + unsigned perm_write_protect : 1; + unsigned copy : 1; + unsigned file_format_grp : 1; + // byte 15 + unsigned always1 : 1; + unsigned crc : 7; +} csd2_t; +//------------------------------------------------------------------------------ +// union of old and new style CSD register +union csd_t +{ + csd1_t v1; + csd2_t v2; +}; +#endif // SdInfo_h diff --git a/libraries/SD/src/utility/SdVolume.cpp b/libraries/SD/src/utility/SdVolume.cpp index 2fbb8100b9..da4dcc3f02 100644 --- a/libraries/SD/src/utility/SdVolume.cpp +++ b/libraries/SD/src/utility/SdVolume.cpp @@ -1,295 +1,405 @@ -/* Arduino SdFat Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ -#include "SdFat.h" -//------------------------------------------------------------------------------ -// raw block cache -// init cacheBlockNumber_to invalid SD block number -uint32_t SdVolume::cacheBlockNumber_ = 0XFFFFFFFF; -cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card -Sd2Card* SdVolume::sdCard_; // pointer to SD card object -uint8_t SdVolume::cacheDirty_ = 0; // cacheFlush() will write block if true -uint32_t SdVolume::cacheMirrorBlock_ = 0; // mirror block for second FAT -//------------------------------------------------------------------------------ -// find a contiguous group of clusters -uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { - // start of group - uint32_t bgnCluster; - - // flag to save place to start next search - uint8_t setStart; - - // set search start cluster - if (*curCluster) { - // try to make file contiguous - bgnCluster = *curCluster + 1; - - // don't save new start location - setStart = false; - } else { - // start at likely place for free cluster - bgnCluster = allocSearchStart_; - - // save next search start if one cluster - setStart = 1 == count; - } - // end of group - uint32_t endCluster = bgnCluster; - - // last cluster of FAT - uint32_t fatEnd = clusterCount_ + 1; - - // search the FAT for free clusters - for (uint32_t n = 0;; n++, endCluster++) { - // can't find space checked all clusters - if (n >= clusterCount_) return false; - - // past end - start from beginning of FAT - if (endCluster > fatEnd) { - bgnCluster = endCluster = 2; - } - uint32_t f; - if (!fatGet(endCluster, &f)) return false; - - if (f != 0) { - // cluster in use try next cluster as bgnCluster - bgnCluster = endCluster + 1; - } else if ((endCluster - bgnCluster + 1) == count) { - // done - found space - break; - } - } - // mark end of chain - if (!fatPutEOC(endCluster)) return false; - - // link clusters - while (endCluster > bgnCluster) { - if (!fatPut(endCluster - 1, endCluster)) return false; - endCluster--; - } - if (*curCluster != 0) { - // connect chains - if (!fatPut(*curCluster, bgnCluster)) return false; - } - // return first cluster number to caller - *curCluster = bgnCluster; - - // remember possible next free cluster - if (setStart) allocSearchStart_ = bgnCluster + 1; - - return true; -} -//------------------------------------------------------------------------------ -uint8_t SdVolume::cacheFlush(void) { - if (cacheDirty_) { - if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) { - return false; - } - // mirror FAT tables - if (cacheMirrorBlock_) { - if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) { - return false; - } - cacheMirrorBlock_ = 0; - } - cacheDirty_ = 0; - } - return true; -} -//------------------------------------------------------------------------------ -uint8_t SdVolume::cacheRawBlock(uint32_t blockNumber, uint8_t action) { - if (cacheBlockNumber_ != blockNumber) { - if (!cacheFlush()) return false; - if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) return false; - cacheBlockNumber_ = blockNumber; - } - cacheDirty_ |= action; - return true; -} -//------------------------------------------------------------------------------ -// cache a zero block for blockNumber -uint8_t SdVolume::cacheZeroBlock(uint32_t blockNumber) { - if (!cacheFlush()) return false; - - // loop take less flash than memset(cacheBuffer_.data, 0, 512); - for (uint16_t i = 0; i < 512; i++) { - cacheBuffer_.data[i] = 0; - } - cacheBlockNumber_ = blockNumber; - cacheSetDirty(); - return true; -} -//------------------------------------------------------------------------------ -// return the size in bytes of a cluster chain -uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const { - uint32_t s = 0; - do { - if (!fatGet(cluster, &cluster)) return false; - s += 512UL << clusterSizeShift_; - } while (!isEOC(cluster)); - *size = s; - return true; -} -//------------------------------------------------------------------------------ -// Fetch a FAT entry -uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const { - if (cluster > (clusterCount_ + 1)) return false; - uint32_t lba = fatStartBlock_; - lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7; - if (lba != cacheBlockNumber_) { - if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false; - } - if (fatType_ == 16) { - *value = cacheBuffer_.fat16[cluster & 0XFF]; - } else { - *value = cacheBuffer_.fat32[cluster & 0X7F] & FAT32MASK; - } - return true; -} -//------------------------------------------------------------------------------ -// Store a FAT entry -uint8_t SdVolume::fatPut(uint32_t cluster, uint32_t value) { - // error if reserved cluster - if (cluster < 2) return false; - - // error if not in FAT - if (cluster > (clusterCount_ + 1)) return false; - - // calculate block address for entry - uint32_t lba = fatStartBlock_; - lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7; - - if (lba != cacheBlockNumber_) { - if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false; - } - // store entry - if (fatType_ == 16) { - cacheBuffer_.fat16[cluster & 0XFF] = value; - } else { - cacheBuffer_.fat32[cluster & 0X7F] = value; - } - cacheSetDirty(); - - // mirror second FAT - if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; - return true; -} -//------------------------------------------------------------------------------ -// free a cluster chain -uint8_t SdVolume::freeChain(uint32_t cluster) { - // clear free cluster location - allocSearchStart_ = 2; - - do { - uint32_t next; - if (!fatGet(cluster, &next)) return false; - - // free cluster - if (!fatPut(cluster, 0)) return false; - - cluster = next; - } while (!isEOC(cluster)); - - return true; -} -//------------------------------------------------------------------------------ -/** - * Initialize a FAT volume. - * - * \param[in] dev The SD card where the volume is located. - * - * \param[in] part The partition to be used. Legal values for \a part are - * 1-4 to use the corresponding partition on a device formatted with - * a MBR, Master Boot Record, or zero if the device is formatted as - * a super floppy with the FAT boot sector in block zero. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. Reasons for - * failure include not finding a valid partition, not finding a valid - * FAT file system in the specified partition or an I/O error. - */ -uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) { - uint32_t volumeStartBlock = 0; - sdCard_ = dev; - // if part == 0 assume super floppy with FAT boot sector in block zero - // if part > 0 assume mbr volume with partition table - if (part) { - if (part > 4)return false; - if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false; - part_t* p = &cacheBuffer_.mbr.part[part-1]; - if ((p->boot & 0X7F) !=0 || - p->totalSectors < 100 || - p->firstSector == 0) { - // not a valid partition - return false; - } - volumeStartBlock = p->firstSector; - } - if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false; - bpb_t* bpb = &cacheBuffer_.fbs.bpb; - if (bpb->bytesPerSector != 512 || - bpb->fatCount == 0 || - bpb->reservedSectorCount == 0 || - bpb->sectorsPerCluster == 0) { - // not valid FAT volume - return false; - } - fatCount_ = bpb->fatCount; - blocksPerCluster_ = bpb->sectorsPerCluster; - - // determine shift that is same as multiply by blocksPerCluster_ - clusterSizeShift_ = 0; - while (blocksPerCluster_ != (1 << clusterSizeShift_)) { - // error if not power of 2 - if (clusterSizeShift_++ > 7) return false; - } - blocksPerFat_ = bpb->sectorsPerFat16 ? - bpb->sectorsPerFat16 : bpb->sectorsPerFat32; - - fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount; - - // count for FAT16 zero for FAT32 - rootDirEntryCount_ = bpb->rootDirEntryCount; - - // directory start for FAT16 dataStart for FAT32 - rootDirStart_ = fatStartBlock_ + bpb->fatCount * blocksPerFat_; - - // data start for FAT16 and FAT32 - dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511)/512); - - // total blocks for FAT16 or FAT32 - uint32_t totalBlocks = bpb->totalSectors16 ? - bpb->totalSectors16 : bpb->totalSectors32; - // total data blocks - clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock); - - // divide by cluster size to get cluster count - clusterCount_ >>= clusterSizeShift_; - - // FAT type is determined by cluster count - if (clusterCount_ < 4085) { - fatType_ = 12; - } else if (clusterCount_ < 65525) { - fatType_ = 16; - } else { - rootDirStart_ = bpb->fat32RootCluster; - fatType_ = 32; - } - return true; -} +/* Arduino SdFat Library + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ +#include "SdFat.h" +//------------------------------------------------------------------------------ +// raw block cache +// init cacheBlockNumber_to invalid SD block number +uint32_t SdVolume::cacheBlockNumber_ = 0XFFFFFFFF; +cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card +Sd2Card* SdVolume::sdCard_; // pointer to SD card object +uint8_t SdVolume::cacheDirty_ = 0; // cacheFlush() will write block if true +uint32_t SdVolume::cacheMirrorBlock_ = 0; // mirror block for second FAT +//------------------------------------------------------------------------------ +// find a contiguous group of clusters +uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) +{ + // start of group + uint32_t bgnCluster; + + // flag to save place to start next search + uint8_t setStart; + + // set search start cluster + if (*curCluster) + { + // try to make file contiguous + bgnCluster = *curCluster + 1; + + // don't save new start location + setStart = false; + } + else + { + // start at likely place for free cluster + bgnCluster = allocSearchStart_; + + // save next search start if one cluster + setStart = 1 == count; + } + // end of group + uint32_t endCluster = bgnCluster; + + // last cluster of FAT + uint32_t fatEnd = clusterCount_ + 1; + + // search the FAT for free clusters + for (uint32_t n = 0;; n++, endCluster++) + { + // can't find space checked all clusters + if (n >= clusterCount_) + { + return false; + } + + // past end - start from beginning of FAT + if (endCluster > fatEnd) + { + bgnCluster = endCluster = 2; + } + uint32_t f; + if (!fatGet(endCluster, &f)) + { + return false; + } + + if (f != 0) + { + // cluster in use try next cluster as bgnCluster + bgnCluster = endCluster + 1; + } + else if ((endCluster - bgnCluster + 1) == count) + { + // done - found space + break; + } + } + // mark end of chain + if (!fatPutEOC(endCluster)) + { + return false; + } + + // link clusters + while (endCluster > bgnCluster) + { + if (!fatPut(endCluster - 1, endCluster)) + { + return false; + } + endCluster--; + } + if (*curCluster != 0) + { + // connect chains + if (!fatPut(*curCluster, bgnCluster)) + { + return false; + } + } + // return first cluster number to caller + *curCluster = bgnCluster; + + // remember possible next free cluster + if (setStart) + { + allocSearchStart_ = bgnCluster + 1; + } + + return true; +} +//------------------------------------------------------------------------------ +uint8_t SdVolume::cacheFlush(void) +{ + if (cacheDirty_) + { + if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) + { + return false; + } + // mirror FAT tables + if (cacheMirrorBlock_) + { + if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) + { + return false; + } + cacheMirrorBlock_ = 0; + } + cacheDirty_ = 0; + } + return true; +} +//------------------------------------------------------------------------------ +uint8_t SdVolume::cacheRawBlock(uint32_t blockNumber, uint8_t action) +{ + if (cacheBlockNumber_ != blockNumber) + { + if (!cacheFlush()) + { + return false; + } + if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) + { + return false; + } + cacheBlockNumber_ = blockNumber; + } + cacheDirty_ |= action; + return true; +} +//------------------------------------------------------------------------------ +// cache a zero block for blockNumber +uint8_t SdVolume::cacheZeroBlock(uint32_t blockNumber) +{ + if (!cacheFlush()) + { + return false; + } + + // loop take less flash than memset(cacheBuffer_.data, 0, 512); + for (uint16_t i = 0; i < 512; i++) + { + cacheBuffer_.data[i] = 0; + } + cacheBlockNumber_ = blockNumber; + cacheSetDirty(); + return true; +} +//------------------------------------------------------------------------------ +// return the size in bytes of a cluster chain +uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const +{ + uint32_t s = 0; + do + { + if (!fatGet(cluster, &cluster)) + { + return false; + } + s += 512UL << clusterSizeShift_; + } while (!isEOC(cluster)); + *size = s; + return true; +} +//------------------------------------------------------------------------------ +// Fetch a FAT entry +uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const +{ + if (cluster > (clusterCount_ + 1)) + { + return false; + } + uint32_t lba = fatStartBlock_; + lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7; + if (lba != cacheBlockNumber_) + { + if (!cacheRawBlock(lba, CACHE_FOR_READ)) + { + return false; + } + } + if (fatType_ == 16) + { + *value = cacheBuffer_.fat16[cluster & 0XFF]; + } + else + { + *value = cacheBuffer_.fat32[cluster & 0X7F] & FAT32MASK; + } + return true; +} +//------------------------------------------------------------------------------ +// Store a FAT entry +uint8_t SdVolume::fatPut(uint32_t cluster, uint32_t value) +{ + // error if reserved cluster + if (cluster < 2) + { + return false; + } + + // error if not in FAT + if (cluster > (clusterCount_ + 1)) + { + return false; + } + + // calculate block address for entry + uint32_t lba = fatStartBlock_; + lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7; + + if (lba != cacheBlockNumber_) + { + if (!cacheRawBlock(lba, CACHE_FOR_READ)) + { + return false; + } + } + // store entry + if (fatType_ == 16) + { + cacheBuffer_.fat16[cluster & 0XFF] = value; + } + else + { + cacheBuffer_.fat32[cluster & 0X7F] = value; + } + cacheSetDirty(); + + // mirror second FAT + if (fatCount_ > 1) + { + cacheMirrorBlock_ = lba + blocksPerFat_; + } + return true; +} +//------------------------------------------------------------------------------ +// free a cluster chain +uint8_t SdVolume::freeChain(uint32_t cluster) +{ + // clear free cluster location + allocSearchStart_ = 2; + + do + { + uint32_t next; + if (!fatGet(cluster, &next)) + { + return false; + } + + // free cluster + if (!fatPut(cluster, 0)) + { + return false; + } + + cluster = next; + } while (!isEOC(cluster)); + + return true; +} +//------------------------------------------------------------------------------ +/** + Initialize a FAT volume. + + \param[in] dev The SD card where the volume is located. + + \param[in] part The partition to be used. Legal values for \a part are + 1-4 to use the corresponding partition on a device formatted with + a MBR, Master Boot Record, or zero if the device is formatted as + a super floppy with the FAT boot sector in block zero. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. Reasons for + failure include not finding a valid partition, not finding a valid + FAT file system in the specified partition or an I/O error. +*/ +uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) +{ + uint32_t volumeStartBlock = 0; + sdCard_ = dev; + // if part == 0 assume super floppy with FAT boot sector in block zero + // if part > 0 assume mbr volume with partition table + if (part) + { + if (part > 4) + { + return false; + } + if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) + { + return false; + } + part_t* p = &cacheBuffer_.mbr.part[part - 1]; + if ((p->boot & 0X7F) != 0 || + p->totalSectors < 100 || + p->firstSector == 0) + { + // not a valid partition + return false; + } + volumeStartBlock = p->firstSector; + } + if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) + { + return false; + } + bpb_t* bpb = &cacheBuffer_.fbs.bpb; + if (bpb->bytesPerSector != 512 || + bpb->fatCount == 0 || + bpb->reservedSectorCount == 0 || + bpb->sectorsPerCluster == 0) + { + // not valid FAT volume + return false; + } + fatCount_ = bpb->fatCount; + blocksPerCluster_ = bpb->sectorsPerCluster; + + // determine shift that is same as multiply by blocksPerCluster_ + clusterSizeShift_ = 0; + while (blocksPerCluster_ != (1 << clusterSizeShift_)) + { + // error if not power of 2 + if (clusterSizeShift_++ > 7) + { + return false; + } + } + blocksPerFat_ = bpb->sectorsPerFat16 ? + bpb->sectorsPerFat16 : bpb->sectorsPerFat32; + + fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount; + + // count for FAT16 zero for FAT32 + rootDirEntryCount_ = bpb->rootDirEntryCount; + + // directory start for FAT16 dataStart for FAT32 + rootDirStart_ = fatStartBlock_ + bpb->fatCount * blocksPerFat_; + + // data start for FAT16 and FAT32 + dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511) / 512); + + // total blocks for FAT16 or FAT32 + uint32_t totalBlocks = bpb->totalSectors16 ? + bpb->totalSectors16 : bpb->totalSectors32; + // total data blocks + clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock); + + // divide by cluster size to get cluster count + clusterCount_ >>= clusterSizeShift_; + + // FAT type is determined by cluster count + if (clusterCount_ < 4085) + { + fatType_ = 12; + } + else if (clusterCount_ < 65525) + { + fatType_ = 16; + } + else + { + rootDirStart_ = bpb->fat32RootCluster; + fatType_ = 32; + } + return true; +} diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index f67c6d69fc..830bcdcdbd 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,77 +162,95 @@ 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) - 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; @@ -223,7 +264,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; @@ -232,13 +274,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; } @@ -249,19 +297,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; } @@ -276,46 +329,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); @@ -323,12 +388,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 @@ -339,49 +407,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 @@ -389,31 +469,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); @@ -421,7 +507,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++; @@ -429,33 +516,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; } @@ -528,74 +646,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 66199b4070..b8ce8fb1b0 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 } @@ -93,25 +104,25 @@ void hspi_slave_begin(uint8_t status_len, void * arg) SPI1P = (1 << 19); SPI1CMD = SPIBUSY; - 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 hspi_slave_setStatus(uint32_t status) @@ -126,11 +137,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,44 @@ 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 +311,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 +443,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 +469,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 5fa2dc9608..a6015a9eda 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() { - 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 d006c8ffc3..8d1501f3d6 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(); - -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(); + +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 e3ce0d7b60..67ea185958 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,219 +58,263 @@ 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)(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 79d47ec14b..8ac58e79be 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,26 +63,38 @@ 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 (*)(size_t) ); - 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 (*)(size_t)); + 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/astyle_core.conf b/tests/astyle_core.conf new file mode 100644 index 0000000000..f9ee90adb6 --- /dev/null +++ b/tests/astyle_core.conf @@ -0,0 +1,32 @@ +# Code formatting rules for Arduino examples, taken from: +# +# https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf +# + +mode=c +lineend=linux +style=allman + +# 4 spaces indentation +indent=spaces=4 + +# also indent macros +#indent-preprocessor + +# indent classes, switches (and cases), comments starting at column 1 +indent-col1-comments + +# put a space around operators +pad-oper + +# put a space after if/for/while +pad-header + +# if you like one-liners, keep them +keep-one-line-statements + +attach-closing-while +unpad-paren +pad-oper +remove-comment-prefix +add-braces diff --git a/tests/examples_style.conf b/tests/astyle_examples.conf similarity index 100% rename from tests/examples_style.conf rename to tests/astyle_examples.conf diff --git a/tests/ci/style_check.sh b/tests/ci/style_check.sh index d51d9c54cd..a6b3532426 100755 --- a/tests/ci/style_check.sh +++ b/tests/ci/style_check.sh @@ -4,10 +4,8 @@ set -ev -find $TRAVIS_BUILD_DIR/libraries -name '*.ino' -exec \ - astyle \ - --suffix=none \ - --options=$TRAVIS_BUILD_DIR/tests/examples_style.conf {} \; +org=$(cd ${0%/*}; pwd) +${org}/../restyle.sh # Revert changes which astyle might have done to the submodules, # as we don't want to fail the build because of the 3rd party libraries diff --git a/tests/examples_restyle.sh b/tests/examples_restyle.sh deleted file mode 100755 index 9d56aa41a2..0000000000 --- a/tests/examples_restyle.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -cd $(cd ${0%/*}; pwd) -astyle --options=examples_style.conf ../libraries/*/examples/*{,/*}/*.ino diff --git a/tests/restyle.sh b/tests/restyle.sh new file mode 100755 index 0000000000..fe1443a0c1 --- /dev/null +++ b/tests/restyle.sh @@ -0,0 +1,25 @@ +#!/bin/sh -x + +set -ev + +org=$(cd ${0%/*}; pwd) +cd ${org}/.. +pwd +test -d cores/esp8266 +test -d libraries + +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