diff --git a/libraries/ESP8266WiFi/src/WiFiUdp.cpp b/libraries/ESP8266WiFi/src/WiFiUdp.cpp new file mode 100644 index 000000000..8bc83a1f2 --- /dev/null +++ b/libraries/ESP8266WiFi/src/WiFiUdp.cpp @@ -0,0 +1,190 @@ +/* + WiFiUdp.cpp - 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 distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + 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 "debug.h" +#include "ESP8266WiFi.h" +#include "WiFiUdp.h" +#include "lwip/opt.h" +#include "lwip/udp.h" +#include "lwip/inet.h" +#include "include/UdpContext.h" + +/* Constructor */ +WiFiUDP::WiFiUDP() : _ctx(0) {} + +WiFiUDP::WiFiUDP(const WiFiUDP& other) +{ + _ctx = other._ctx; + if (_ctx) + _ctx->ref(); +} + +WiFiUDP& WiFiUDP::operator=(const WiFiUDP& rhs) +{ + _ctx = rhs._ctx; + if (_ctx) + _ctx->ref(); +} + +WiFiUDP::~WiFiUDP() +{ + if (_ctx) + _ctx->unref(); +} + +/* Start WiFiUDP socket, listening at local port */ +uint8_t WiFiUDP::begin(uint16_t port) +{ + if (_ctx) + { + _ctx->unref(); + } + _ctx = new UdpContext; + ip_addr_t addr; + addr.addr = INADDR_ANY; + return (_ctx->listen(addr, port)) ? 1 : 0; +} + +/* return number of bytes available in the current packet, + will return zero if parsePacket hasn't been called yet */ +int WiFiUDP::available() { + if (!_ctx) + return 0; + return static_cast(_ctx->getSize()); +} + +/* Release any resources being used by this WiFiUDP instance */ +void WiFiUDP::stop() +{ + if (_ctx) + _ctx->disconnect(); + _ctx->unref(); + _ctx = 0; +} + +int WiFiUDP::beginPacket(const char *host, uint16_t port) +{ + IPAddress remote_addr; + if (WiFi.hostByName(host, remote_addr)) + { + return beginPacket(remote_addr, port); + } + return 0; +} + +int WiFiUDP::beginPacket(IPAddress ip, uint16_t port) +{ + ip_addr_t addr; + addr.addr = ip; + + if (_ctx) + _ctx->unref(); + _ctx = new UdpContext; + return (_ctx->connect(addr, port)) ? 1 : 0; +} + +int WiFiUDP::endPacket() +{ + if (!_ctx) + return 0; + + _ctx->send(); +} + +size_t WiFiUDP::write(uint8_t byte) +{ + return write(&byte, 1); +} + +size_t WiFiUDP::write(const uint8_t *buffer, size_t size) +{ + if (!_ctx) + return 0; + + return _ctx->append(reinterpret_cast(buffer), size); +} + +int WiFiUDP::parsePacket() +{ + if (!_ctx) + return 0; + if (!_ctx->next()) + return 0; + return _ctx->getSize(); +} + +int WiFiUDP::read() +{ + if (!_ctx) + return -1; + + return _ctx->read(); +} + +int WiFiUDP::read(unsigned char* buffer, size_t len) +{ + if (!_ctx) + return 0; + + return _ctx->read(reinterpret_cast(buffer), len); +} + +int WiFiUDP::peek() +{ + if (!_ctx) + return 0; + + return _ctx->peek(); +} + +void WiFiUDP::flush() +{ + if (_ctx) + _ctx->flush(); +} + +IPAddress WiFiUDP::remoteIP() +{ + uint8_t _remoteIp[4] = {0}; + uint8_t _remotePort[2] = {0}; + + // WiFiDrv::getRemoteData(_sock, _remoteIp, _remotePort); + IPAddress ip(_remoteIp); + return ip; +} + +uint16_t WiFiUDP::remotePort() +{ + uint8_t _remoteIp[4] = {0}; + uint8_t _remotePort[2] = {0}; + + // WiFiDrv::getRemoteData(_sock, _remoteIp, _remotePort); + uint16_t port = (_remotePort[0]<<8)+_remotePort[1]; + return port; +} + diff --git a/libraries/ESP8266WiFi/src/WiFiUdp.cpp.unused b/libraries/ESP8266WiFi/src/WiFiUdp.cpp.unused deleted file mode 100644 index d82d1e523..000000000 --- a/libraries/ESP8266WiFi/src/WiFiUdp.cpp.unused +++ /dev/null @@ -1,181 +0,0 @@ -/* - WiFiUdp.cpp - 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 distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -extern "C" { - #include "utility/debug.h" - #include "utility/wifi_spi.h" -} -#include -#include "utility/server_drv.h" -#include "utility/wifi_drv.h" - -#include "WiFi.h" -#include "WiFiUdp.h" -#include "WiFiClient.h" -#include "WiFiServer.h" - - -/* Constructor */ -WiFiUDP::WiFiUDP() : _sock(NO_SOCKET_AVAIL) {} - -/* Start WiFiUDP socket, listening at local port PORT */ -uint8_t WiFiUDP::begin(uint16_t port) { - - uint8_t sock = WiFiClass::getSocket(); - if (sock != NO_SOCKET_AVAIL) - { - ServerDrv::startServer(port, sock, UDP_MODE); - WiFiClass::_server_port[sock] = port; - _sock = sock; - _port = port; - return 1; - } - return 0; - -} - -/* return number of bytes available in the current packet, - will return zero if parsePacket hasn't been called yet */ -int WiFiUDP::available() { - if (_sock != NO_SOCKET_AVAIL) - { - return ServerDrv::availData(_sock); - } - return 0; -} - -/* Release any resources being used by this WiFiUDP instance */ -void WiFiUDP::stop() -{ - if (_sock == NO_SOCKET_AVAIL) - return; - - ServerDrv::stopClient(_sock); - - _sock = NO_SOCKET_AVAIL; -} - -int WiFiUDP::beginPacket(const char *host, uint16_t port) -{ - // Look up the host first - int ret = 0; - IPAddress remote_addr; - if (WiFi.hostByName(host, remote_addr)) - { - return beginPacket(remote_addr, port); - } - return ret; -} - -int WiFiUDP::beginPacket(IPAddress ip, uint16_t port) -{ - if (_sock == NO_SOCKET_AVAIL) - _sock = WiFiClass::getSocket(); - if (_sock != NO_SOCKET_AVAIL) - { - ServerDrv::startClient(uint32_t(ip), port, _sock, UDP_MODE); - WiFiClass::_state[_sock] = _sock; - return 1; - } - return 0; -} - -int WiFiUDP::endPacket() -{ - return ServerDrv::sendUdpData(_sock); -} - -size_t WiFiUDP::write(uint8_t byte) -{ - return write(&byte, 1); -} - -size_t WiFiUDP::write(const uint8_t *buffer, size_t size) -{ - ServerDrv::insertDataBuf(_sock, buffer, size); - return size; -} - -int WiFiUDP::parsePacket() -{ - return available(); -} - -int WiFiUDP::read() -{ - uint8_t b; - if (available()) - { - ServerDrv::getData(_sock, &b); - return b; - }else{ - return -1; - } -} - -int WiFiUDP::read(unsigned char* buffer, size_t len) -{ - if (available()) - { - uint16_t size = 0; - if (!ServerDrv::getDataBuf(_sock, buffer, &size)) - return -1; - // TODO check if the buffer is too smal respect to buffer size - return size; - }else{ - return -1; - } -} - -int WiFiUDP::peek() -{ - uint8_t b; - if (!available()) - return -1; - - ServerDrv::getData(_sock, &b, 1); - return b; -} - -void WiFiUDP::flush() -{ - while (available()) - read(); -} - -IPAddress WiFiUDP::remoteIP() -{ - uint8_t _remoteIp[4] = {0}; - uint8_t _remotePort[2] = {0}; - - WiFiDrv::getRemoteData(_sock, _remoteIp, _remotePort); - IPAddress ip(_remoteIp); - return ip; -} - -uint16_t WiFiUDP::remotePort() -{ - uint8_t _remoteIp[4] = {0}; - uint8_t _remotePort[2] = {0}; - - WiFiDrv::getRemoteData(_sock, _remoteIp, _remotePort); - uint16_t port = (_remotePort[0]<<8)+_remotePort[1]; - return port; -} - diff --git a/libraries/ESP8266WiFi/src/WiFiUdp.h b/libraries/ESP8266WiFi/src/WiFiUdp.h index fe6e0255a..de58ddb68 100644 --- a/libraries/ESP8266WiFi/src/WiFiUdp.h +++ b/libraries/ESP8266WiFi/src/WiFiUdp.h @@ -17,21 +17,26 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef wifiudp_h -#define wifiudp_h +#ifndef WIFIUDP_H +#define WIFIUDP_H #include -#define UDP_TX_PACKET_MAX_SIZE 24 +#define UDP_TX_PACKET_MAX_SIZE 8192 + +class UdpContext; class WiFiUDP : public UDP { private: - uint8_t _sock; // socket ID for Wiz5100 - uint16_t _port; // local port to listen on + UdpContext* _ctx; public: WiFiUDP(); // 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 + WiFiUDP(const WiFiUDP& other); + WiFiUDP& operator=(const WiFiUDP& rhs); + ~WiFiUDP(); + + virtual uint8_t begin(uint16_t port); // 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 @@ -74,7 +79,7 @@ public: // Return the port of the host who sent the current incoming packet virtual uint16_t remotePort(); - friend class WiFiDrv; }; -#endif +#endif //WIFIUDP_H + diff --git a/libraries/ESP8266WiFi/src/include/UdpContext.h b/libraries/ESP8266WiFi/src/include/UdpContext.h new file mode 100644 index 000000000..5ad5599ea --- /dev/null +++ b/libraries/ESP8266WiFi/src/include/UdpContext.h @@ -0,0 +1,304 @@ +/* + 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. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + 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 + +class UdpContext; + +extern "C" void esp_yield(); +extern "C" void esp_schedule(); + + +class UdpContext +{ +public: + 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 = udp_new(); + } + + ~UdpContext() + { + udp_remove(_pcb); + _pcb = 0; + if (_tx_buf_head) + { + pbuf_free(_tx_buf_head); + _tx_buf_head = 0; + _tx_buf_cur = 0; + _tx_buf_offset = 0; + } + if (_rx_buf) + { + pbuf_free(_rx_buf); + _rx_buf = 0; + _rx_buf_offset = 0; + } + } + + void ref() + { + ++_refcnt; + } + + void unref() + { + DEBUGV(":ur %d\r\n", _refcnt); + if (--_refcnt == 0) + { + delete this; + } + } + + bool connect(ip_addr_t addr, uint16_t port) + { + err_t err = udp_connect(_pcb, &addr, port); + return err == ERR_OK; + } + + bool listen(ip_addr_t addr, uint16_t port) + { + udp_recv(_pcb, &_s_recv, (void *) this); + err_t err = udp_bind(_pcb, &addr, port); + return err == ERR_OK; + } + + void disconnect() + { + udp_disconnect(_pcb); + } + + size_t getSize() const + { + if (!_rx_buf) + return 0; + + return _rx_buf->len - _rx_buf_offset; + } + + bool next() + { + if (!_rx_buf) + return false; + + if (!_first_buf_taken) + { + _first_buf_taken = true; + return true; + } + + auto head = _rx_buf; + _rx_buf = _rx_buf->next; + _rx_buf_offset = 0; + + if (_rx_buf) + { + pbuf_ref(_rx_buf); + } + pbuf_free(head); + return _rx_buf != 0; + } + + char read() + { + if (!_rx_buf || _rx_buf->len == _rx_buf_offset) + return 0; + + char c = reinterpret_cast(_rx_buf->payload)[_rx_buf_offset]; + _consume(1); + return c; + } + + 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; + DEBUGV(":rd %d, %d, %d\r\n", size, _rx_buf->len, _rx_buf_offset); + + os_memcpy(dst, reinterpret_cast(_rx_buf->payload) + _rx_buf_offset, size); + _consume(size); + + return size; + } + + char peek() + { + if (!_rx_buf) + return 0; + + return reinterpret_cast(_rx_buf->payload)[_rx_buf_offset]; + } + + void flush() + { + if (!_rx_buf) + return; + + _consume(_rx_buf->len - _rx_buf_offset); + } + + + size_t append(const char* data, size_t size) + { + if (!_tx_buf_head || _tx_buf_head->tot_len < _tx_buf_offset + size) + { + _reserve(_tx_buf_offset + size); + } + + size_t left_to_copy = size; + 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); + size_t free_cur = _tx_buf_cur->len - used_cur; + if (free_cur == 0) + { + _tx_buf_cur = _tx_buf_cur->next; + continue; + } + size_t will_copy = (left_to_copy < free_cur) ? left_to_copy : free_cur; + os_memcpy(reinterpret_cast(_tx_buf_cur->payload) + used_cur, data, will_copy); + _tx_buf_offset += will_copy; + left_to_copy -= will_copy; + data += will_copy; + } + return size; + } + + void send() + { + size_t orig_size = _tx_buf_head->tot_len; + + size_t data_size = _tx_buf_offset; + size_t size_adjustment = orig_size - data_size; + for (pbuf* p = _tx_buf_head; p; p = p->next) + { + p->tot_len -= size_adjustment; + if (!p->next) + { + p->len = p->tot_len; + } + } + + udp_send(_pcb, _tx_buf_head); + + for (pbuf* p = _tx_buf_head; p; p = p->next) + { + p->tot_len += size_adjustment; + if (!p->next) + { + p->len = p->tot_len; + } + } + + pbuf_free(_tx_buf_head); + _tx_buf_head = 0; + _tx_buf_cur = 0; + _tx_buf_offset = 0; + } + +private: + + void _reserve(size_t size) + { + const size_t pbuf_unit_size = 1024; + if (!_tx_buf_head) + { + _tx_buf_head = pbuf_alloc(PBUF_TRANSPORT, pbuf_unit_size, PBUF_RAM); + _tx_buf_cur = _tx_buf_head; + _tx_buf_offset = 0; + } + + size_t cur_size = _tx_buf_head->tot_len; + if (size < cur_size) + return; + + size_t grow_size = size - cur_size; + + while(grow_size) + { + pbuf* pb = pbuf_alloc(PBUF_TRANSPORT, pbuf_unit_size, PBUF_RAM); + pbuf_cat(_tx_buf_head, pb); + if (grow_size < pbuf_unit_size) + return; + grow_size -= pbuf_unit_size; + } + } + + void _consume(size_t size) + { + _rx_buf_offset += size; + } + + void _recv(udp_pcb *upcb, pbuf *pb, + ip_addr_t *addr, u16_t port) + { + if (_rx_buf) + { + // there is some unread data + // chain the new pbuf to the existing one + DEBUGV(":rch %d, %d\r\n", _rx_buf->tot_len, pb->tot_len); + pbuf_cat(_rx_buf, pb); + } + else + { + DEBUGV(":rn %d\r\n", pb->tot_len); + _first_buf_taken = false; + _rx_buf = pb; + _rx_buf_offset = 0; + } + } + + + static void _s_recv(void *arg, + udp_pcb *upcb, pbuf *p, + ip_addr_t *addr, u16_t port) + { + reinterpret_cast(arg)->_recv(upcb, p, addr, port); + } + +private: + int _refcnt; + udp_pcb* _pcb; + + bool _first_buf_taken; + pbuf* _rx_buf; + size_t _rx_buf_offset; + + pbuf* _tx_buf_head; + pbuf* _tx_buf_cur; + size_t _tx_buf_offset; +}; + + + +#endif//CLIENTCONTEXT_H