Files
earlephilhower_arduino-pico/libraries/WiFi/src/WiFiServer.cpp
T
Earle F. Philhower, III abf2c586c7 Add Pico W WiFi support (#670)
* Add support for the WiFi chip on the Pico W board.
* USB interrupt now no longer hard coded (conflicted with the WiFi IRQ).
* Add in Pico W board to makeboards.py
* Add in GPIO and variant support
* Initialize WiFi in the Variant
* Use manual LWIP, fix size accounting
* Remove the SDK WiFi overrides
* Pulling in work done in the ESP8266 core.
* Make IPAddress support IPv6
* Build LWIP with IPv4 and IPv6 support
* Use proper MAC
* Avoid cyw_warn crash.  Make macro to a comment while building
* Add WiFiServer
* Add WiFiUdp
* Move LWIP-specific support files to LWIP_Ethernet
* Add WiFi::ping (ICMP ping)
* Move ICMP echo (ping) to LWIPIntfDev
* Move hostByName to LwipIntfDev
* Add AP mode with simple DHCP server
* Add some examples and basic ESP8266 compat hacks
* Update Adafruit TinyUSB to fix crash
* Set DHCP hostname
* Make Wifi.begin() return CONNECTED with link + IP
* Return connected() on WiFi::begin
* Fix spurious TCP retransmission
* Protect LWIP from reentrancy

The Pico SDK calls "sys_check_timeouts() from inside a periodic interrupt.
This appears unsafe, as the interrupt could happen while already in the
(non-reentrant) LWIP code.

Block the interrupt from calling sys_check_timeouts by using a global flag
manually set via an RAII recursive lock.

Add interrupt protection macros around critical sections inside LWIP via
the standard defines.

These two changes should make LWIP significantly more stable and long
running.

* Support disconnecting and reconnecting WiFi
* Add WiFiServer simple example
* Update documentation

Fixes #666
Fixed #665
2022-07-15 16:47:53 -07:00

221 lines
5.5 KiB
C++

/*
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.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
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 "WiFi.h"
#include "lwip/opt.h"
#include "lwip/tcp.h"
#include "lwip/inet.h"
#include <include/ClientContext.h>
#ifndef MAX_PENDING_CLIENTS_PER_PORT
#define MAX_PENDING_CLIENTS_PER_PORT 5
#endif
WiFiServer::WiFiServer(const IPAddress& addr, uint16_t port)
: _port(port)
, _addr(addr) {
}
WiFiServer::WiFiServer(uint16_t port)
: _port(port)
, _addr(IP_ANY_TYPE) {
}
void WiFiServer::begin() {
begin(_port);
}
void WiFiServer::begin(uint16_t port) {
return begin(port, MAX_PENDING_CLIENTS_PER_PORT);
}
void WiFiServer::begin(uint16_t port, uint8_t backlog) {
close();
if (!backlog) {
return;
}
_port = port;
LWIPMutex m; // Block the timer sys_check_timeouts call
tcp_pcb* pcb = tcp_new();
if (!pcb) {
return;
}
pcb->so_options |= SOF_REUSEADDR;
// (IPAddress _addr) operator-converted to (const ip_addr_t*)
if (tcp_bind(pcb, _addr, _port) != ERR_OK) {
tcp_close(pcb);
return;
}
tcp_pcb* listen_pcb = tcp_listen_with_backlog(pcb, backlog);
if (!listen_pcb) {
tcp_close(pcb);
return;
}
_listen_pcb = listen_pcb;
_port = _listen_pcb->local_port;
tcp_accept(listen_pcb, &WiFiServer::_s_accept);
tcp_arg(listen_pcb, (void*) this);
}
void WiFiServer::setNoDelay(bool nodelay) {
_noDelay = nodelay ? _ndTrue : _ndFalse;
}
bool WiFiServer::getNoDelay() {
switch (_noDelay) {
case _ndFalse: return false;
case _ndTrue: return true;
default: return WiFiClient::getDefaultNoDelay();
}
}
bool WiFiServer::hasClient() {
if (_unclaimed) {
return true;
}
return false;
}
size_t WiFiServer::hasClientData() {
ClientContext *next = _unclaimed;
while (next) {
size_t s = next->getSize();
// return the amount of data available from the first connection that has any
if (s) {
return s;
}
next = next->next();
}
return 0;
}
bool WiFiServer::hasMaxPendingClients() {
#if TCP_LISTEN_BACKLOG
return ((struct tcp_pcb_listen *)_listen_pcb)->accepts_pending >= MAX_PENDING_CLIENTS_PER_PORT;
#else
return false;
#endif
}
WiFiClient WiFiServer::available(byte* status) {
(void) status;
return accept();
}
WiFiClient WiFiServer::accept() {
if (_unclaimed) {
WiFiClient result(_unclaimed);
// pcb can be null when peer has already closed the connection
if (_unclaimed->getPCB()) {
LWIPMutex m; // Block the timer sys_check_timeouts call
// give permission to lwIP to accept one more peer
tcp_backlog_accepted(_unclaimed->getPCB());
}
_unclaimed = _unclaimed->next();
result.setNoDelay(getNoDelay());
DEBUGV("WS:av status=%d WCav=%d\r\n", result.status(), result.available());
return result;
}
return WiFiClient();
}
uint8_t WiFiServer::status() {
if (!_listen_pcb) {
return CLOSED;
}
return _listen_pcb->state;
}
uint16_t WiFiServer::port() const {
return _port;
}
void WiFiServer::close() {
if (!_listen_pcb) {
return;
}
LWIPMutex m; // Block the timer sys_check_timeouts call
tcp_close(_listen_pcb);
_listen_pcb = nullptr;
}
void WiFiServer::stop() {
close();
}
template<typename T>
T* slist_append_tail(T* head, T* item) {
if (!head) {
return item;
}
T* last = head;
while (last->next()) {
last = last->next();
}
last->next(item);
return head;
}
err_t WiFiServer::_accept(tcp_pcb* apcb, err_t err) {
(void) err;
DEBUGV("WS:ac\r\n");
// always accept new PCB so incoming data can be stored in our buffers even before
// user calls ::available()
ClientContext* client = new ClientContext(apcb, &WiFiServer::_s_discard, this);
// backlog doc:
// http://lwip.100.n7.nabble.com/Problem-re-opening-listening-pbc-tt32484.html#a32494
// https://www.nongnu.org/lwip/2_1_x/group__tcp__raw.html#gaeff14f321d1eecd0431611f382fcd338
// increase lwIP's backlog
LWIPMutex m; // Block the timer sys_check_timeouts call
tcp_backlog_delayed(apcb);
_unclaimed = slist_append_tail(_unclaimed, client);
return ERR_OK;
}
void WiFiServer::_discard(ClientContext* client) {
(void) client;
// _discarded = slist_append_tail(_discarded, client);
DEBUGV("WS:dis\r\n");
}
err_t WiFiServer::_s_accept(void *arg, tcp_pcb* newpcb, err_t err) {
return reinterpret_cast<WiFiServer*>(arg)->_accept(newpcb, err);
}
void WiFiServer::_s_discard(void* server, ClientContext* ctx) {
reinterpret_cast<WiFiServer*>(server)->_discard(ctx);
}