4#ifdef USE_SOCKET_IMPL_LWIP_TCP
15#elif defined(USE_RP2040)
16#include <hardware/sync.h>
25static volatile bool s_socket_woke =
false;
39 s_socket_woke =
false;
40 esp_delay(ms, []() {
return !s_socket_woke; });
47#elif defined(USE_RP2040)
59static volatile bool s_socket_woke =
false;
61static volatile bool s_delay_expired =
false;
63static int64_t alarm_callback(alarm_id_t
id,
void *user_data) {
66 s_delay_expired =
true;
82 s_socket_woke =
false;
88 s_delay_expired =
false;
91 alarm_id_t alarm = add_alarm_in_ms(ms, alarm_callback,
nullptr,
true);
99 while (!s_socket_woke && !s_delay_expired) {
103 if (!s_delay_expired)
105 s_socket_woke =
false;
111 s_socket_woke =
true;
134#define LWIP_LOCK() esphome::LwIPLock lwip_lock_guard
136static const char *
const TAG =
"socket.lwip";
140#define LWIP_LOG(msg, ...) ESP_LOGVV(TAG, "socket %p: " msg, this, ##__VA_ARGS__)
142#define LWIP_LOG(msg, ...)
152static void pcb_detach_abort(
struct tcp_pcb *pcb) {
153 tcp_arg(pcb,
nullptr);
154 tcp_recv(pcb,
nullptr);
155 tcp_err(pcb,
nullptr);
167static err_t pcb_detach_close(
struct tcp_pcb *pcb) {
168 tcp_arg(pcb,
nullptr);
169 tcp_recv(pcb,
nullptr);
170 tcp_err(pcb,
nullptr);
171 err_t err = tcp_close(pcb);
182 if (this->
pcb_ !=
nullptr) {
183 LWIP_LOG(
"tcp_abort(%p)", this->
pcb_);
184 pcb_detach_abort(this->
pcb_);
185 this->
pcb_ =
nullptr;
191 if (this->
pcb_ ==
nullptr) {
195 if (name ==
nullptr) {
202 if (this->
family_ == AF_INET) {
207 auto *addr4 =
reinterpret_cast<const sockaddr_in *
>(name);
208 port = ntohs(addr4->sin_port);
209 ip.type = IPADDR_TYPE_V4;
210 ip.u_addr.ip4.addr = addr4->sin_addr.s_addr;
211 LWIP_LOG(
"tcp_bind(%p ip=%s port=%u)", this->
pcb_, ip4addr_ntoa(&ip.u_addr.ip4), port);
212 }
else if (this->
family_ == AF_INET6) {
217 auto *addr6 =
reinterpret_cast<const sockaddr_in6 *
>(name);
218 port = ntohs(addr6->sin6_port);
219 ip.type = IPADDR_TYPE_ANY;
220 memcpy(&ip.u_addr.ip6.addr, &addr6->sin6_addr.un.u8_addr, 16);
221 LWIP_LOG(
"tcp_bind(%p ip=%s port=%u)", this->
pcb_, ip6addr_ntoa(&ip.u_addr.ip6), port);
227 if (this->
family_ != AF_INET) {
231 auto *addr4 =
reinterpret_cast<const sockaddr_in *
>(name);
232 port = ntohs(addr4->sin_port);
233 ip.addr = addr4->sin_addr.s_addr;
234 LWIP_LOG(
"tcp_bind(%p ip=%u port=%u)", this->
pcb_, ip.addr, port);
236 err_t err = tcp_bind(this->
pcb_, &ip, port);
237 if (err == ERR_USE) {
238 LWIP_LOG(
" -> err ERR_USE");
242 if (err == ERR_VAL) {
243 LWIP_LOG(
" -> err ERR_VAL");
248 LWIP_LOG(
" -> err %d", err);
257 if (this->
pcb_ ==
nullptr) {
261 LWIP_LOG(
"tcp_close(%p)", this->
pcb_);
262 err_t err = pcb_detach_close(this->
pcb_);
263 this->
pcb_ =
nullptr;
265 LWIP_LOG(
" -> err %d", err);
266 errno = err == ERR_MEM ? ENOMEM : EIO;
274 if (this->
pcb_ ==
nullptr) {
278 bool shut_rx =
false, shut_tx =
false;
279 if (how == SHUT_RD) {
281 }
else if (how == SHUT_WR) {
283 }
else if (how == SHUT_RDWR) {
284 shut_rx = shut_tx =
true;
289 LWIP_LOG(
"tcp_shutdown(%p shut_rx=%d shut_tx=%d)", this->
pcb_, shut_rx ? 1 : 0, shut_tx ? 1 : 0);
290 err_t err = tcp_shutdown(this->
pcb_, shut_rx, shut_tx);
292 LWIP_LOG(
" -> err %d", err);
293 errno = err == ERR_MEM ? ENOMEM : EIO;
301 if (this->
pcb_ ==
nullptr) {
305 if (name ==
nullptr || addrlen ==
nullptr) {
309 return this->
ip2sockaddr_(&this->
pcb_->remote_ip, this->pcb_->remote_port, name, addrlen);
314 if (this->
pcb_ ==
nullptr) {
318 if (name ==
nullptr || addrlen ==
nullptr) {
322 return this->
ip2sockaddr_(&this->
pcb_->local_ip, this->pcb_->local_port, name, addrlen);
347 if (this->
pcb_ ==
nullptr) {
351 if (optlen ==
nullptr || optval ==
nullptr) {
355 if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
362 *
reinterpret_cast<int *
>(optval) = 1;
366 if (level == SOL_SOCKET && optname == SO_RCVTIMEO) {
367 if (*optlen <
sizeof(
struct timeval)) {
372 auto *tv =
reinterpret_cast<struct timeval *
>(optval);
373 tv->tv_sec = ms / 1000;
374 tv->tv_usec = (ms % 1000) * 1000;
375 *optlen =
sizeof(
struct timeval);
378 if (level == IPPROTO_TCP && optname == TCP_NODELAY) {
383 *
reinterpret_cast<int *
>(optval) = this->
nodelay_;
394 if (this->
pcb_ ==
nullptr) {
398 if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
407 if (level == SOL_SOCKET && optname == SO_RCVTIMEO) {
408 if (optlen <
sizeof(
struct timeval)) {
412 const auto *tv =
reinterpret_cast<const struct timeval *
>(optval);
413 uint32_t ms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
418 if (level == SOL_SOCKET && optname == SO_SNDTIMEO) {
422 if (level == IPPROTO_TCP && optname == TCP_NODELAY) {
427 int val = *
reinterpret_cast<const int *
>(optval);
437 if (this->
family_ == AF_INET) {
447 inet_addr_from_ip4addr(&addr->
sin_addr, ip_2_ip4(ip));
451 else if (this->
family_ == AF_INET6) {
465 ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&mapped), ip_2_ip4(ip));
466 inet6_addr_from_ip6addr(&addr->
sin6_addr, ip_2_ip6(&mapped));
468 inet6_addr_from_ip6addr(&addr->
sin6_addr, ip_2_ip6(ip));
483 if (this->
rx_buf_ !=
nullptr) {
492 LWIP_LOG(
"init(%p)", this->
pcb_);
493 tcp_arg(this->
pcb_,
this);
496 if (initial_rx !=
nullptr) {
511 auto *arg_this =
reinterpret_cast<LWIPRawImpl *
>(arg);
512 ESP_LOGVV(TAG,
"socket %p: err(err=%d)", arg_this, err);
513 arg_this->pcb_ =
nullptr;
517 auto *arg_this =
reinterpret_cast<LWIPRawImpl *
>(arg);
518 return arg_this->
recv_fn(pb, err);
524 LWIP_LOG(
"recv(pb=%p err=%d)", pb, err);
538 if (this->
rx_buf_ ==
nullptr) {
545#if (defined(USE_ESP8266) || defined(USE_RP2040))
563 if (elapsed >= timeout_ms)
571 if (this->
pcb_ ==
nullptr) {
581 if (this->
rx_buf_ ==
nullptr) {
587 uint8_t *buf8 =
reinterpret_cast<uint8_t *
>(buf);
589 size_t pb_len = this->
rx_buf_->len;
593 size_t copysize = std::min(
len, pb_left);
594 memcpy(buf8,
reinterpret_cast<uint8_t *
>(this->
rx_buf_->payload) + this->rx_buf_offset_, copysize);
596 if (pb_left == copysize) {
598 if (this->
rx_buf_->next ==
nullptr) {
602 this->rx_buf_offset_ = 0;
608 this->rx_buf_offset_ = 0;
611 this->rx_buf_offset_ += copysize;
613 LWIP_LOG(
"tcp_recved(%p %u)", this->
pcb_, copysize);
614 tcp_recved(this->
pcb_, copysize);
647 for (
int i = 0; i < iovcnt; i++) {
648 ssize_t err = this->
read_locked_(
reinterpret_cast<uint8_t *
>(iov[i].iov_base), iov[i].iov_len);
657 if ((
size_t) err != iov[i].iov_len)
665 if (this->
pcb_ ==
nullptr) {
671 if (buf ==
nullptr) {
675 auto space = tcp_sndbuf(this->
pcb_);
680 size_t to_send = std::min((
size_t) space,
len);
681 LWIP_LOG(
"tcp_write(%p buf=%p %u)", this->
pcb_, buf, to_send);
682 err_t err = tcp_write(this->
pcb_, buf, to_send, TCP_WRITE_FLAG_COPY);
683 if (err == ERR_MEM) {
684 LWIP_LOG(
" -> err ERR_MEM");
689 LWIP_LOG(
" -> err %d", err);
698 if (this->
pcb_ ==
nullptr) {
702 LWIP_LOG(
"tcp_output(%p)", this->
pcb_);
703 err_t err = tcp_output(this->
pcb_);
704 if (err == ERR_ABRT) {
709 LWIP_LOG(
" -> err ERR_ABRT");
713 LWIP_LOG(
" -> err %d", err);
740 for (
int i = 0; i < iovcnt; i++) {
750 if ((
size_t) err != iov[i].iov_len)
770 for (uint8_t i = 0; i < this->accepted_socket_count_; i++) {
771 auto &entry = this->accepted_pcbs_[i];
772 if (entry.pcb !=
nullptr) {
773 pcb_detach_abort(entry.pcb);
776 if (entry.rx_buf !=
nullptr) {
777 pbuf_free(entry.rx_buf);
778 entry.rx_buf =
nullptr;
781 this->accepted_socket_count_ = 0;
790 if (this->
pcb_ !=
nullptr) {
791 tcp_close(this->
pcb_);
792 this->
pcb_ =
nullptr;
798 LWIP_LOG(
"init(%p)", this->
pcb_);
799 tcp_arg(this->
pcb_,
this);
800 tcp_accept(this->
pcb_, LWIPRawListenImpl::s_accept_fn);
808 ESP_LOGVV(TAG,
"socket %p: err(err=%d)", arg_this, err);
809 arg_this->pcb_ =
nullptr;
812void LWIPRawListenImpl::s_queued_err_fn(
void *arg, err_t err) {
818 auto *entry =
reinterpret_cast<QueuedPcb *
>(arg);
819 entry->pcb =
nullptr;
823err_t LWIPRawListenImpl::s_queued_recv_fn(
void *arg,
struct tcp_pcb *pcb,
struct pbuf *pb, err_t err) {
830 auto *entry =
reinterpret_cast<QueuedPcb *
>(arg);
831 if (pb ==
nullptr || err != ERR_OK) {
836 entry->rx_closed =
true;
840 if (entry->rx_buf ==
nullptr) {
843 pbuf_cat(entry->rx_buf, pb);
848err_t LWIPRawListenImpl::s_accept_fn(
void *arg,
struct tcp_pcb *newpcb, err_t err) {
849 auto *arg_this =
reinterpret_cast<LWIPRawListenImpl *
>(arg);
850 return arg_this->accept_fn_(newpcb, err);
855 if (this->
pcb_ ==
nullptr) {
861 while (this->accepted_socket_count_ > 0) {
862 QueuedPcb entry = this->accepted_pcbs_[0];
865 for (uint8_t i = 1; i < this->accepted_socket_count_; i++) {
866 this->accepted_pcbs_[i - 1] = this->accepted_pcbs_[i];
867 if (this->accepted_pcbs_[i - 1].pcb !=
nullptr) {
868 tcp_arg(this->accepted_pcbs_[i - 1].pcb, &this->accepted_pcbs_[i - 1]);
871 this->accepted_pcbs_[this->accepted_socket_count_ - 1] = {};
872 this->accepted_socket_count_--;
873 if (entry.pcb ==
nullptr) {
875 if (entry.rx_buf !=
nullptr) {
876 pbuf_free(entry.rx_buf);
880 LWIP_LOG(
"Connection accepted by application, queue size: %d", this->accepted_socket_count_);
883 auto sock = make_unique<LWIPRawImpl>(this->
family_, entry.pcb);
884 sock->init(entry.rx_buf, entry.rx_closed);
885 if (addr !=
nullptr) {
886 sock->getpeername(addr, addrlen);
888 LWIP_LOG(
"accept(%p)", sock.get());
897 if (this->
pcb_ ==
nullptr) {
901 LWIP_LOG(
"tcp_listen_with_backlog(%p backlog=%d)", this->
pcb_, backlog);
902 struct tcp_pcb *listen_pcb = tcp_listen_with_backlog(this->
pcb_, backlog);
903 if (listen_pcb ==
nullptr) {
904 tcp_abort(this->
pcb_);
905 this->
pcb_ =
nullptr;
910 this->
pcb_ = listen_pcb;
912 LWIP_LOG(
"tcp_arg(%p)", this->
pcb_);
913 tcp_arg(this->
pcb_,
this);
914 tcp_accept(this->
pcb_, LWIPRawListenImpl::s_accept_fn);
921err_t LWIPRawListenImpl::accept_fn_(
struct tcp_pcb *newpcb, err_t err) {
924 LWIP_LOG(
"accept(newpcb=%p err=%d)", newpcb, err);
925 if (err != ERR_OK || newpcb ==
nullptr) {
933 if (this->accepted_socket_count_ >= MAX_ACCEPTED_SOCKETS) {
934 LWIP_LOG(
"Rejecting connection, queue full (%d)", this->accepted_socket_count_);
942 uint8_t idx = this->accepted_socket_count_++;
943 this->accepted_pcbs_[idx] = {newpcb,
nullptr,
false};
949 tcp_arg(newpcb, &this->accepted_pcbs_[idx]);
950 tcp_err(newpcb, LWIPRawListenImpl::s_queued_err_fn);
951 tcp_recv(newpcb, LWIPRawListenImpl::s_queued_recv_fn);
952 LWIP_LOG(
"Accepted connection, queue size: %d", this->accepted_socket_count_);
953#if (defined(USE_ESP8266) || defined(USE_RP2040))
962std::unique_ptr<Socket>
socket(
int domain,
int type,
int protocol) {
963 if (
type != SOCK_STREAM) {
964 ESP_LOGE(TAG,
"UDP sockets not supported on this platform, use WiFiUDP");
969 auto *pcb = tcp_new();
972 auto *sock =
new LWIPRawImpl((
sa_family_t) domain, pcb);
974 return std::unique_ptr<Socket>{sock};
983 if (
type != SOCK_STREAM) {
984 ESP_LOGE(TAG,
"UDP sockets not supported on this platform, use WiFiUDP");
989 auto *pcb = tcp_new();
994 return std::unique_ptr<ListenSocket>{sock};
int getsockname(struct sockaddr *name, socklen_t *addrlen)
size_t getsockname_to(std::span< char, SOCKADDR_STR_LEN > buf)
Format local address into a fixed-size buffer (no heap allocation)
int bind(const struct sockaddr *name, socklen_t addrlen)
int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen)
int setsockopt(int level, int optname, const void *optval, socklen_t optlen)
int getsockopt(int level, int optname, void *optval, socklen_t *optlen)
int getpeername(struct sockaddr *name, socklen_t *addrlen)
size_t getpeername_to(std::span< char, SOCKADDR_STR_LEN > buf)
Format peer address into a fixed-size buffer (no heap allocation)
Connected socket implementation for LWIP raw TCP.
ssize_t read_locked_(void *buf, size_t len)
static err_t s_recv_fn(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err)
void init(struct pbuf *initial_rx=nullptr, bool initial_rx_closed=false)
bool waiting_for_data_() const
ssize_t readv(const struct iovec *iov, int iovcnt)
static void s_err_fn(void *arg, err_t err)
err_t recv_fn(struct pbuf *pb, err_t err)
ssize_t internal_write_(const void *buf, size_t len)
ssize_t write(const void *buf, size_t len)
ssize_t read(void *buf, size_t len)
ssize_t writev(const struct iovec *iov, int iovcnt)
Listening socket implementation for LWIP raw TCP.
static void s_err_fn(void *arg, err_t err)
std::unique_ptr< LWIPRawImpl > accept(struct sockaddr *addr, socklen_t *addrlen)
size_t format_sockaddr_to(const struct sockaddr *addr_ptr, socklen_t len, std::span< char, SOCKADDR_STR_LEN > buf)
Format sockaddr into caller-provided buffer, returns length written (excluding null)
std::unique_ptr< ListenSocket > socket_listen(int domain, int type, int protocol)
Create a listening socket of the given domain, type and protocol.
std::unique_ptr< ListenSocket > socket_listen_loop_monitored(int domain, int type, int protocol)
void IRAM_ATTR socket_wake()
Signal socket/IO activity and wake the main loop early.
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.
std::unique_ptr< Socket > socket_loop_monitored(int domain, int type, int protocol)
Create a socket and monitor it for data in the main loop.
void socket_delay(uint32_t ms)
Delay that can be woken early by socket activity.
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
struct in6_addr sin6_addr