mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
255 lines
5.9 KiB
C++
255 lines
5.9 KiB
C++
/*
|
|
UdpContext.h - emulation of 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.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
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
|
|
|
|
#include <functional>
|
|
|
|
class UdpContext;
|
|
|
|
#define GET_IP_HDR(pb) reinterpret_cast<ip_hdr*>(((uint8_t*)((pb)->payload)) - UDP_HLEN - IP_HLEN);
|
|
#define GET_UDP_HDR(pb) reinterpret_cast<udp_hdr*>(((uint8_t*)((pb)->payload)) - UDP_HLEN);
|
|
|
|
class UdpContext
|
|
{
|
|
public:
|
|
|
|
typedef std::function<void(void)> rxhandler_t;
|
|
|
|
UdpContext(): _on_rx(nullptr), _refcnt(0)
|
|
{
|
|
_sock = mockUDPSocket();
|
|
}
|
|
|
|
~UdpContext()
|
|
{
|
|
}
|
|
|
|
void ref()
|
|
{
|
|
++_refcnt;
|
|
}
|
|
|
|
void unref()
|
|
{
|
|
if(--_refcnt == 0) {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
bool connect (const ip_addr_t* addr, uint16_t port)
|
|
{
|
|
_dst = *addr;
|
|
_dstport = port;
|
|
return true;
|
|
}
|
|
|
|
bool listen(const ip_addr_t* addr, uint16_t port)
|
|
{
|
|
bool ret = mockUDPListen(_sock, addr->addr, port, staticMCastAddr);
|
|
register_udp(_sock, this);
|
|
return ret;
|
|
}
|
|
|
|
void disconnect()
|
|
{
|
|
if (_sock >= 0)
|
|
{
|
|
close(_sock);
|
|
register_udp(_sock, nullptr);
|
|
}
|
|
_sock = -1;
|
|
}
|
|
|
|
void setMulticastInterface(const ip_addr_t& addr)
|
|
{
|
|
// user multicast, and this is how it works with posix: send to multicast address:
|
|
_dst.addr = staticMCastAddr;
|
|
}
|
|
|
|
void setMulticastTTL(int ttl)
|
|
{
|
|
//fprintf(stderr, MOCK "TODO: UdpContext::setMulticastTTL\n");
|
|
}
|
|
|
|
// 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) {
|
|
_on_rx = handler;
|
|
}
|
|
|
|
size_t getSize()
|
|
{
|
|
return _inbufsize;
|
|
}
|
|
|
|
size_t tell() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void seek(const size_t pos)
|
|
{
|
|
fprintf(stderr, MOCK "TODO: implement UDP offset\n");
|
|
if (!isValidOffset(pos))
|
|
{
|
|
fprintf(stderr, MOCK "UDPContext::seek too far (%zd >= %zd)\n", pos, _inbufsize);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
bool isValidOffset(const size_t pos) const {
|
|
return pos <= _inbufsize;
|
|
}
|
|
|
|
uint32_t getRemoteAddress()
|
|
{
|
|
return _dst.addr;
|
|
}
|
|
|
|
uint16_t getRemotePort()
|
|
{
|
|
return _dstport;
|
|
}
|
|
|
|
uint32_t getDestAddress()
|
|
{
|
|
fprintf(stderr, MOCK "TODO: implement UDP getDestAddress\n");
|
|
return 0; //ip_hdr* iphdr = GET_IP_HDR(_rx_buf);
|
|
}
|
|
|
|
uint16_t getLocalPort()
|
|
{
|
|
fprintf(stderr, MOCK "TODO: implement UDP getLocalPort\n");
|
|
return 0; //
|
|
}
|
|
|
|
bool next()
|
|
{
|
|
_inbufsize = 0;
|
|
mockUDPFillInBuf(_sock, _inbuf, _inbufsize, addrsize, addr, _dstport);
|
|
if (_inbufsize > 0)
|
|
{
|
|
translate_addr();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int read()
|
|
{
|
|
char c;
|
|
return read(&c, 1)? c: -1;
|
|
}
|
|
|
|
size_t read(char* dst, size_t size)
|
|
{
|
|
return mockUDPRead(_sock, dst, size, _timeout_ms, _inbuf, _inbufsize);
|
|
}
|
|
|
|
int peek()
|
|
{
|
|
char c;
|
|
return mockUDPPeekBytes(_sock, &c, 1, _timeout_ms, _inbuf, _inbufsize)?: -1;
|
|
}
|
|
|
|
void flush()
|
|
{
|
|
//fprintf(stderr, MOCK "UdpContext::flush() does not follow arduino's flush concept\n");
|
|
//exit(EXIT_FAILURE);
|
|
// would be:
|
|
_inbufsize = 0;
|
|
}
|
|
|
|
size_t append (const char* data, size_t size)
|
|
{
|
|
if (size + _outbufsize > sizeof _outbuf)
|
|
{
|
|
fprintf(stderr, MOCK "UdpContext::append: increase CCBUFSIZE (%d -> %zd)\n", CCBUFSIZE, (size + _outbufsize));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
memcpy(_outbuf + _outbufsize, data, size);
|
|
_outbufsize += size;
|
|
return size;
|
|
}
|
|
|
|
bool send (ip_addr_t* addr = 0, uint16_t port = 0)
|
|
{
|
|
uint32_t dst = addr? addr->addr: _dst.addr;
|
|
uint16_t dstport = port?: _dstport;
|
|
size_t ret = mockUDPWrite(_sock, (const uint8_t*)_outbuf, _outbufsize, _timeout_ms, dst, dstport);
|
|
_outbufsize = 0;
|
|
return ret > 0;
|
|
}
|
|
|
|
void mock_cb (void)
|
|
{
|
|
if (_on_rx) _on_rx();
|
|
}
|
|
|
|
public:
|
|
|
|
static uint32_t staticMCastAddr;
|
|
|
|
private:
|
|
|
|
void translate_addr ()
|
|
{
|
|
if (addrsize == 4)
|
|
{
|
|
uint32_t ipv4;
|
|
memcpy(&ipv4, addr, 4);
|
|
ip4_addr_set_u32(&ip_2_ip4(_dst), ipv4);
|
|
// ^ this is a workaround for "type-punned pointer" with "*(uint32*)addr"
|
|
//ip4_addr_set_u32(&ip_2_ip4(_dst), *(uint32_t*)addr);
|
|
}
|
|
else
|
|
fprintf(stderr, MOCK "TODO unhandled udp address of size %d\n", (int)addrsize);
|
|
}
|
|
|
|
int _sock = -1;
|
|
rxhandler_t _on_rx;
|
|
int _refcnt = 0;
|
|
|
|
ip_addr_t _dst;
|
|
uint16_t _dstport;
|
|
|
|
char _inbuf [CCBUFSIZE];
|
|
size_t _inbufsize = 0;
|
|
char _outbuf [CCBUFSIZE];
|
|
size_t _outbufsize = 0;
|
|
|
|
int _timeout_ms = 0;
|
|
|
|
uint8_t addrsize;
|
|
uint8_t addr[16];
|
|
};
|
|
|
|
inline err_t igmp_joingroup (const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
|
|
{
|
|
(void)ifaddr;
|
|
UdpContext::staticMCastAddr = groupaddr->addr;
|
|
return ERR_OK;
|
|
}
|
|
|
|
#endif//UDPCONTEXT_H
|