mirror of
https://github.com/esp8266/Arduino.git
synced 2025-07-26 07:02:15 +03:00
Initial UDP support
This commit is contained in:
304
libraries/ESP8266WiFi/src/include/UdpContext.h
Normal file
304
libraries/ESP8266WiFi/src/include/UdpContext.h
Normal file
@ -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<char*>(_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<char*>(_rx_buf->payload) + _rx_buf_offset, size);
|
||||
_consume(size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
char peek()
|
||||
{
|
||||
if (!_rx_buf)
|
||||
return 0;
|
||||
|
||||
return reinterpret_cast<char*>(_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<char*>(_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<UdpContext*>(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
|
Reference in New Issue
Block a user