From 960d8bc9897819885884f71aa7af60af195e8646 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 28 Dec 2023 11:06:07 +0100
Subject: [PATCH 01/79] reimplementing the ethernet drivers

---
 libraries/Ethernet/src/Ethernet.cpp       | 207 ------
 libraries/Ethernet/src/EthernetC33.h      |  63 +-
 libraries/Ethernet/src/EthernetDriver.cpp | 764 +++++++++++-----------
 libraries/Ethernet/src/EthernetDriver.h   | 104 ++-
 4 files changed, 477 insertions(+), 661 deletions(-)

diff --git a/libraries/Ethernet/src/Ethernet.cpp b/libraries/Ethernet/src/Ethernet.cpp
index 22c90ff62..0311f9135 100644
--- a/libraries/Ethernet/src/Ethernet.cpp
+++ b/libraries/Ethernet/src/Ethernet.cpp
@@ -1,208 +1 @@
 #include <EthernetC33.h>
-
-/*
- * The old implementation of the begin set a default mac address:
- * this does not make any sense.
- * Default mac address is in the hardware, when lwip start that mac
- * address is passed to lwip
- * If mac address needs to be changed then call the appropriate function
- * of lwIpIf before to get the interface 
- */
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::begin(unsigned long timeout, unsigned long responseTimeout) {
-/* -------------------------------------------------------------------------- */  
-  (void)responseTimeout;
-
-  int rv = 0;
-
-  ni = CLwipIf::getInstance().get(NI_ETHERNET);
-  if(ni != nullptr) {
-    ni->DhcpSetTimeout(timeout);
-    rv = (int)ni->DhcpStart();
-  }
-  
-  return rv;
-}
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::begin(IPAddress local_ip) {
-/* -------------------------------------------------------------------------- */
-  // Assume the DNS server will be the machine on the same network as the local IP
-  // but with last octet being '1'
-  IPAddress dns_server = local_ip;
-  dns_server[3] = 1;
-  return begin(local_ip, dns_server);
-}
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::begin(IPAddress local_ip, IPAddress dns_server) {
-/* -------------------------------------------------------------------------- */  
-  // Assume the gateway will be the machine on the same network as the local IP
-  // but with last octet being '1'
-  IPAddress gateway = local_ip;
-  gateway[3] = 1;
-  return begin(local_ip, dns_server, gateway);
-}
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::begin(IPAddress local_ip, IPAddress dns_server, IPAddress gateway) {
-/* -------------------------------------------------------------------------- */  
-  IPAddress subnet(255, 255, 255, 0);
-  return begin(local_ip, dns_server, gateway, subnet);
-}
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::begin(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) {
-/* -------------------------------------------------------------------------- */  
-  
-  ni = CLwipIf::getInstance().get(NI_ETHERNET,  local_ip, gateway, subnet);
-  if(ni == nullptr) {
-    return 0;
-  }
-
-  /* If there is a local DHCP informs it of our manual IP configuration to prevent IP conflict */
-  ni->DhcpNotUsed();
-  CLwipIf::getInstance().addDns(dns_server);
-  return 1;
-}
-
-/* -------------------------------------------------------------------------- */
-void CEthernet::setDNS(IPAddress dns_server) {
-/* -------------------------------------------------------------------------- */  
-  CLwipIf::getInstance().addDns(dns_server);
-} 
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::begin(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) {
-/* -------------------------------------------------------------------------- */  
-  CLwipIf::getInstance().setMacAddress(NI_ETHERNET, mac);
-  return begin(timeout, responseTimeout);
-}
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::begin(uint8_t *mac_address, IPAddress local_ip) {
-/* -------------------------------------------------------------------------- */
-  // Assume the DNS server will be the machine on the same network as the local IP
-  // but with last octet being '1'
-  IPAddress dns_server = local_ip;
-  dns_server[3] = 1;
-  return begin(mac_address, local_ip, dns_server);
-}
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server) {
-/* -------------------------------------------------------------------------- */  
-  // Assume the gateway will be the machine on the same network as the local IP
-  // but with last octet being '1'
-  IPAddress gateway = local_ip;
-  gateway[3] = 1;
-  return begin(mac_address, local_ip, dns_server, gateway);
-}
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway) {
-/* -------------------------------------------------------------------------- */  
-  IPAddress subnet(255, 255, 255, 0);
-  return begin(mac_address, local_ip, dns_server, gateway, subnet);
-}
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet, unsigned long timeout, unsigned long responseTimeout) {
-/* -------------------------------------------------------------------------- */  
-  CLwipIf::getInstance().setMacAddress(NI_ETHERNET, mac);
-  return begin(local_ip, dns_server, gateway, subnet);
-}
-
-/* -------------------------------------------------------------------------- */
-EthernetLinkStatus CEthernet::linkStatus() {
-/* -------------------------------------------------------------------------- */  
-  if(ni != nullptr) {
-    return (!CLwipIf::getInstance().isEthInitialized()) ? Unknown : (ni->isLinkUp() ? LinkON : LinkOFF);
-  }
-  return Unknown;
-}
-
-/* -------------------------------------------------------------------------- */
-EthernetHardwareStatus CEthernet::hardwareStatus() {
-/* -------------------------------------------------------------------------- */
-  return EthernetLwip;
-}
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::disconnect() {
-/* -------------------------------------------------------------------------- */
-  return 1;
-}
-
-/* -------------------------------------------------------------------------- */
-int CEthernet::maintain() {
-/* -------------------------------------------------------------------------- */  
-  int rc = DHCP_CHECK_NONE;
-
-  if (ni != NULL) {
-    //we have a pointer to dhcp, use it
-    rc = ni->checkLease();
-    switch (rc) {
-      case DHCP_CHECK_NONE:
-        //nothing done
-        break;
-      case DHCP_CHECK_RENEW_OK:
-      case DHCP_CHECK_REBIND_OK:
-	//_dnsServerAddress = _dhcp->getDnsServerIp();
-        break;
-      default:
-        //this is actually a error, it will retry though
-        break;
-    }
-  }
-  return rc;
-}
-
-/*
- * This function updates the LwIP stack and can be called to be sure to update
- * the stack (e.g. in case of a long loop).
- */
-void CEthernet::schedule(void) {
-  if (ni != NULL) {
-    ni->task();
-  }
-}
-
-
-
-uint8_t *CEthernet::MACAddress(void) {
-  CLwipIf::getInstance().getMacAddress(NI_ETHERNET, mac_address);
-  return mac_address;
-}
-
-void CEthernet::MACAddress(uint8_t *mac) {
-  CLwipIf::getInstance().getMacAddress(NI_ETHERNET, mac);
-}
-
-IPAddress CEthernet::localIP() {
-  if(ni != nullptr) {
-      return IPAddress(ni->getIpAdd());   
-   }
-   return IPAddress((uint32_t)0);
-}
-
-IPAddress CEthernet::subnetMask() {
-  if(ni != nullptr) {
-      return IPAddress(ni->getNmAdd());   
-   }
-   return IPAddress((uint32_t)0);
-}
-
-IPAddress CEthernet::gatewayIP() {
-  if(ni != nullptr) {
-      return IPAddress(ni->getGwAdd());   
-   }
-   return IPAddress((uint32_t)0);
-}
-
-IPAddress CEthernet::dnsServerIP() {
-  return CLwipIf::getInstance().getDns();
-}
-
-CEthernet Ethernet;
diff --git a/libraries/Ethernet/src/EthernetC33.h b/libraries/Ethernet/src/EthernetC33.h
index 97fa54511..b2b9e4288 100644
--- a/libraries/Ethernet/src/EthernetC33.h
+++ b/libraries/Ethernet/src/EthernetC33.h
@@ -1,5 +1,4 @@
-#ifndef ARDUINO_C_ETHERNET_H
-#define ARDUINO_C_ETHERNET_H
+#pragma once
 
 #ifndef ARDUINO_PORTENTA_C33
 // force discovering wth shield library
@@ -13,65 +12,15 @@
 
 #include "EthernetClient.h"
 #include "EthernetServer.h"
+#include "EthernetDriver.h"
 
 #include "CNetIf.h"
-#include "lwipMem.h"
 
-enum EthernetLinkStatus {
-  Unknown,
-  LinkON,
-  LinkOFF
-};
+#ifdef ARDUINO_PORTENTA_C33
 
-enum EthernetHardwareStatus {
-  EthernetNoHardware,
-  EthernetLwip = 7
-};
+// TODO Instantiate the drivers for ethernet with default configuration parameters
+// EthernetC33Driver C33EthernetDriver(2, 2, mem_malloc, 1536);
 
-class CEthernet {
-
-  private:
-    CNetIf *ni;
-    
-    uint8_t mac_address[6];
-  public:
-    // Initialise the Ethernet with the internal provided MAC address and gain the rest of the
-    // configuration through DHCP.
-    // Returns 0 if the DHCP configuration failed, and 1 if it succeeded
-    int begin(unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
-    EthernetLinkStatus linkStatus();
-    int begin(IPAddress local_ip);
-    int begin(IPAddress local_ip, IPAddress dns_server);
-    int begin(IPAddress local_ip, IPAddress dns_server, IPAddress gateway);
-    int begin(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet);
-    // Initialise the Ethernet shield to use the provided MAC address and gain the rest of the
-    // configuration through DHCP.
-    // Returns 0 if the DHCP configuration failed, and 1 if it succeeded
-    int begin(uint8_t *mac_address, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
-    int begin(uint8_t *mac_address, IPAddress local_ip);
-    int begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server);
-    int begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway);
-    int begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
-    EthernetHardwareStatus hardwareStatus();
-
-    void setDNS(IPAddress dns_server); 
-
-    int disconnect(void);
-    int maintain();
-    void schedule(void);
-
-   
-    uint8_t *MACAddress(void);
-    void MACAddress(uint8_t *mac);
-    IPAddress localIP();
-    IPAddress subnetMask();
-    IPAddress gatewayIP();
-    IPAddress dnsServerIP();
-
-    friend class EthernetClient;
-    friend class EthernetServer;
-};
-
-extern CEthernet Ethernet;
+// FIXME Instantiate a global variable from CEth, calling it Ethernet
 
 #endif
diff --git a/libraries/Ethernet/src/EthernetDriver.cpp b/libraries/Ethernet/src/EthernetDriver.cpp
index 70eaf43d1..ae69c8a40 100644
--- a/libraries/Ethernet/src/EthernetDriver.cpp
+++ b/libraries/Ethernet/src/EthernetDriver.cpp
@@ -1,145 +1,73 @@
 #include "EthernetDriver.h"
-#include "IRQManager.h"
+#include <IRQManager.h>
+#include <malloc.h>
+#include "utils.h"
+#include <Arduino_DebugUtils.h>
 
-/* IMPORTANT NOTE: this driver is configured to use ZERO COPY 
-   This means when a frame is received the Read function do not perform
-   a copy of the data bu just returns the pointer to them
-   */
-
-#define USE_ZERO_COPY
-
-
-typedef enum {
-    ETH_LINK_UP,
-    ETH_LINK_DOWN
-} EthLinkStatus_t;
-
-
-
-
-
-#define ETHERNET_DEBUG_ENABLED 0
-
-class EthernetDriver {
-public:
-    EthernetDriver();
-    bool enableIrq(uint32_t priority = 12); 
-
-    uint8_t mac_address[MAC_ADDRESS_DIM];
-    /* ETHERNET PHY */
-    ether_phy_cfg_t             phy_cfg;
-    ether_phy_instance_ctrl_t   phy_ctrl;
-    ether_phy_instance_t        phy_instance;
-    
-    /* ETHERNET */
-    ether_cfg_t                 cfg;
-    ether_instance_ctrl_t       ctrl;
-    ether_extended_cfg_t        extended_cfg; 
-
-    ether_cfg_t *get_cfg() {return &cfg;}
-    ether_instance_ctrl_t *get_ctrl() {return &ctrl;}
-
-private:
-    static EthLinkStatus_t link_status;
-    static void irq_callback(ether_callback_args_t * p_args);
-    #ifndef USE_ZERO_COPY
-    uint8_t *buffs[2];
-    #else
-    uint8_t *buffs[1];
-    #endif
-
-    __attribute__((__aligned__(32)))uint8_t buffer0[ETH_BUFF_DIM] ;
-    #ifndef USE_ZERO_COPY
-    __attribute__((__aligned__(32)))uint8_t buffer1[ETH_BUFF_DIM] ;
-    #endif
-
-    __attribute__((__aligned__(16))) ether_instance_descriptor_t tx_descriptors[1] ;
-    __attribute__((__aligned__(16))) ether_instance_descriptor_t rx_descriptors[1] ;
-
-};
 
+#define ETHERNET_PIN_CFG ((uint32_t) ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_ETHER_RMII))
 #define ETHERNET_CHANNEL                        (0)
-
+#define ADE_BIT_MASK                            (1 << 23)
 #define ETHER_FRAME_RECEIVED_MASK               (1UL << 18)
 #define ETHER_FRAME_TRANSFER_COMPLETED          (1UL << 21)
 #define ETHER_MAGIC_PACKET_DETECTED_MASK        (1UL << 1)
+#define ETHER_RD0_RACT                          (0x80000000UL)
+
+// utility/proxy local functions
+void _irq_ether_callback(ether_callback_args_t* p_args);
+
+extern void dump_buffer(uint8_t* b, uint32_t len, uint8_t blocks=4, uint8_t cols=16);
+
+EthernetC33Driver::EthernetC33Driver(
+    uint8_t rx_descriptors_len,
+    uint8_t tx_descriptors_len,
+    void* (*buffer_allocator)(unsigned int),
+    uint16_t buffer_size,
+    uint8_t* mac_address, uint8_t len)
+: NetworkDriver(),
+rx_descriptors_len(rx_descriptors_len), tx_descriptors_len(tx_descriptors_len),
+buffer_allocator(buffer_allocator), buffer_size(buffer_size) {
+    if(mac_address != nullptr && (len == 6 || len == 8)) {
+        memcpy(this->macaddress, mac_address, len);
+        this->macaddress_len = len;
+    } else {
+        const bsp_unique_id_t* t = R_BSP_UniqueIdGet();
+        this->macaddress[0] = 0xA8;
+        this->macaddress[1] = 0x61;
+        this->macaddress[2] = 0x0A;
+        this->macaddress[3] = t->unique_id_words[0] ^ t->unique_id_words[1];
+        this->macaddress[4] = t->unique_id_words[2];
+        this->macaddress[5] = t->unique_id_words[3];
+    }
 
-static volatile bool frame_being_transmitted = false;
-static EthernetDriver eth_driver;
+    this->rx_descriptors = (ether_instance_descriptor_t*)
+        memalign(16, sizeof(ether_instance_descriptor_t)*rx_descriptors_len);
+    this->tx_descriptors = (ether_instance_descriptor_t*)
+        memalign(16, sizeof(ether_instance_descriptor_t)*tx_descriptors_len);
+        // memalign(16, sizeof(ether_instance_descriptor_t)*1);
 
-static uint8_t eth_tx_buffer[ETH_BUFF_DIM];
+    rx_buffers      = (uint8_t**) malloc(sizeof(void*)*rx_descriptors_len);
 
-uint8_t *eth_get_tx_buffer(uint16_t *size) {
-    *size = ETH_BUFF_DIM;
-    return eth_tx_buffer;
+    tx_buffers_info = (_tx_buffer_info*) malloc(sizeof(_tx_buffer_info)*tx_descriptors_len);
+    memset(tx_buffers_info, 0, sizeof(_tx_buffer_info)*tx_descriptors_len);
+
+    this->init();
 }
 
+EthernetC33Driver::~EthernetC33Driver() {
+    free(this->rx_descriptors);
+    this->rx_descriptors = nullptr;
+    free(this->tx_descriptors);
+    this->tx_descriptors = nullptr;
 
-#define ETHERNET_PIN_CFG ((uint32_t) ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_ETHER_RMII))
+    // TODO free memory of buffers, callback with size 0?
+    // this->rx_buffers
+    // this->tx_buffers
+}
 
-/* -------------------------------------------------------------------------- */
-EthernetDriver::EthernetDriver() {
-/* -------------------------------------------------------------------------- */    
-
-    /* default MAC ADDRESS */
-    const bsp_unique_id_t* t = R_BSP_UniqueIdGet();
-    mac_address[0] = 0xA8;
-    mac_address[1] = 0x61;
-    mac_address[2] = 0x0A;
-    mac_address[3] = t->unique_id_words[0] ^ t->unique_id_words[1];
-    mac_address[4] = t->unique_id_words[2];
-    mac_address[5] = t->unique_id_words[3];
-
-    /* ethernet PHY _________________________________________________________ */
-    phy_cfg.channel                     = ETHERNET_CHANNEL;
-    phy_cfg.phy_lsi_address             = 0;
-    phy_cfg.phy_reset_wait_time         = 0x00020000;
-    phy_cfg.mii_bit_access_wait_time    = 8;
-    phy_cfg.phy_lsi_type                = ETHER_PHY_LSI_TYPE_DEFAULT;
-    phy_cfg.flow_control                = ETHER_PHY_FLOW_CONTROL_DISABLE;
-    phy_cfg.mii_type                    = ETHER_PHY_MII_TYPE_RMII;
-    phy_cfg.p_context                   = nullptr;
-    phy_cfg.p_extend                    = nullptr;
-    /* ethernet PHY instance (used by ethernet, below) ______________________ */
-    phy_instance.p_cfg                  = &phy_cfg;
-    phy_instance.p_ctrl                 = &phy_ctrl;
-    phy_instance.p_api                  = &g_ether_phy_on_ether_phy;
-    /* ethernet _____________________________________________________________ */
-    extended_cfg.p_rx_descriptors       = rx_descriptors;
-    extended_cfg.p_tx_descriptors       = tx_descriptors;
-
-
-    buffs[0]                            = buffer0;
-    #ifndef USE_ZERO_COPY
-    buffs[1]                            = buffer1;
-    #endif
-    cfg.channel                         = ETHERNET_CHANNEL; 
-    #ifndef USE_ZERO_COPY
-    cfg.zerocopy                        = ETHER_ZEROCOPY_DISABLE;
-    #else
-    cfg.zerocopy                        = ETHER_ZEROCOPY_ENABLE;
-    #endif
-    cfg.multicast                       = ETHER_MULTICAST_ENABLE;
-    cfg.promiscuous                     = ETHER_PROMISCUOUS_DISABLE;
-    cfg.flow_control                    = ETHER_FLOW_CONTROL_DISABLE;
-    cfg.padding                         = ETHER_PADDING_DISABLE; 
-    cfg.padding_offset                  = 0; 
-    cfg.broadcast_filter                = 0;
-    cfg.p_mac_address                   = mac_address;
-    //cfg.p_rx_descriptors                = rx_descriptors;
-    //cfg.p_tx_descriptors                = tx_descriptors;
-    cfg.num_tx_descriptors              = 1;
-    cfg.num_rx_descriptors              = 1;
-    cfg.pp_ether_buffers                = buffs;
-    cfg.ether_buffer_size               = ETH_BUFF_DIM;
-    cfg.irq                             = FSP_INVALID_VECTOR;
-    cfg.interrupt_priority              = (12);
-    cfg.p_callback                      = irq_callback;
-    cfg.p_ether_phy_instance            = &phy_instance; 
-    cfg.p_context                       = nullptr; 
-    cfg.p_extend                        = &extended_cfg;
-    /* PIN configuration ____________________________________________________ */
-   
+void EthernetC33Driver::init() {
+    // FIXME understand the configuration performed here
+    // FIXME understand how to pass this configuration as a parameter
     R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_02_PIN_14, ETHERNET_PIN_CFG);
     R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_02_PIN_11, ETHERNET_PIN_CFG);
     R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, ETHERNET_PIN_CFG);
@@ -150,304 +78,390 @@ EthernetDriver::EthernetDriver() {
     R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_07_PIN_03, ETHERNET_PIN_CFG);
     R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_07_PIN_04, ETHERNET_PIN_CFG);
     R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_07_PIN_05, ETHERNET_PIN_CFG);
-    //R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_07_PIN_04, ETHERNET_PIN_CFG);
+
+    // phy setup
+    this->phy_cfg.channel                     = ETHERNET_CHANNEL;
+    this->phy_cfg.phy_lsi_address             = 0;
+    this->phy_cfg.phy_reset_wait_time         = 0x00020000;
+    this->phy_cfg.mii_bit_access_wait_time    = 8;
+    this->phy_cfg.phy_lsi_type                = ETHER_PHY_LSI_TYPE_DEFAULT;
+    this->phy_cfg.flow_control                = ETHER_PHY_FLOW_CONTROL_DISABLE;
+    this->phy_cfg.mii_type                    = ETHER_PHY_MII_TYPE_RMII;
+    this->phy_cfg.p_context                   = nullptr;
+    this->phy_cfg.p_extend                    = nullptr;
+
+    this->phy_instance.p_cfg                  = &this->phy_cfg;
+    this->phy_instance.p_ctrl                 = &this->phy_ctrl;
+    this->phy_instance.p_api                  = &g_ether_phy_on_ether_phy;
+
+    // setup the driver
+    this->extended_cfg.p_rx_descriptors       = this->rx_descriptors; // FIXME
+    this->extended_cfg.p_tx_descriptors       = this->tx_descriptors; // FIXME
+
+
+    this->cfg.channel                         = ETHERNET_CHANNEL;
+    this->cfg.zerocopy                        = ETHER_ZEROCOPY_ENABLE;
+    this->cfg.multicast                       = ETHER_MULTICAST_ENABLE;
+    this->cfg.promiscuous                     = ETHER_PROMISCUOUS_DISABLE;
+    this->cfg.flow_control                    = ETHER_FLOW_CONTROL_DISABLE;
+    this->cfg.padding                         = ETHER_PADDING_DISABLE; // TODO
+    this->cfg.padding_offset                  = 0;
+    this->cfg.broadcast_filter                = 0;
+    this->cfg.p_mac_address                   = this->macaddress;
+    this->cfg.num_tx_descriptors              = this->tx_descriptors_len;
+    this->cfg.num_rx_descriptors              = this->rx_descriptors_len;
+    this->cfg.pp_ether_buffers                = this->rx_buffers;
+    this->cfg.ether_buffer_size               = this->buffer_size;
+    this->cfg.irq                             = FSP_INVALID_VECTOR;
+    this->cfg.interrupt_priority              = (this->irq_priority);
+    this->cfg.p_callback                      = _irq_ether_callback;
+    this->cfg.p_ether_phy_instance            = &this->phy_instance;
+    this->cfg.p_context                       = this;
+    this->cfg.p_extend                        = &this->extended_cfg;
 }
 
-/* -------------------------------------------------------------------------- */
-bool EthernetDriver::enableIrq(uint32_t priority /*== 12*/) {
-/* -------------------------------------------------------------------------- */
-    bool rv = IRQManager::getInstance().addPeripheral(IRQ_ETHERNET,&cfg);
-    if(rv) {
-        cfg.interrupt_priority = priority;
-        R_BSP_IrqCfgEnable(cfg.irq, cfg.interrupt_priority, &ctrl); /* ??? */
+void EthernetC33Driver::begin() {
+    // Fill the rx_buffers
+    uint8_t i=0;
+    for(; i < rx_descriptors_len; i++) {
+        // buffer_allocator has to take into account memory alignment
+        rx_buffers[i] = (uint8_t*)buffer_allocator(buffer_size);
+
+        if(rx_buffers[i] == nullptr) {
+            break;
+        }
+    }
+
+    // If at least a buffer is allocated, wait for the link to be up, otherwise report an error
+    // TODO report error
+    if(i > 0) {
+        this->up();
     }
-    return rv;
 }
 
-EthLinkStatus_t EthernetDriver::link_status = ETH_LINK_DOWN;
-EtherCallback_f frame_received = nullptr;
-EtherCallback_f frame_transmitted = nullptr;
-EtherCallback_f magic_packet_received = nullptr;
-EtherCallback_f link_on = nullptr;
-EtherCallback_f link_off = nullptr;
-EtherCallback_f lan_wake_up = nullptr;
+void EthernetC33Driver::poll() {
+    // Polling the rx descriptor for available data
 
-#define ADE_BIT_MASK (1 << 23)
+    uint32_t rx_frame_dim = 0;
+    uint8_t* rx_frame_buf = nullptr;
+    fsp_err_t err = FSP_SUCCESS;
+    struct pbuf *p = nullptr;
 
-/* This function performs a sort of reset of the EDMAC and  Ethernet controller
-   when the ADE bit in EESR EDMAC register is 1 
-   (however it does not solve the problem since the cause of this in unkknown)*/
+    if(this->consume_cbk == nullptr) {
+        // Callback is not set, no meaning to release buffers
+        // TODO put assertion
+        return;
+    }
 
-/* -------------------------------------------------------------------------- */
-void eth_reset_due_to_ADE_bit() {
-/* -------------------------------------------------------------------------- */    
-    uint32_t *EDMAC_EESR_REG = (uint32_t *)0x40114028;
-    uint32_t *EDMAC_CONTROL_REG = (uint32_t *)0x40114000;
-    if( (*EDMAC_EESR_REG & ADE_BIT_MASK) == ADE_BIT_MASK) {
-        R_ETHER_Close(eth_driver.get_ctrl());
-        *EDMAC_CONTROL_REG |= 0x1;
-        R_ETHER_Open(eth_driver.get_ctrl(), eth_driver.get_cfg());
-    }               
-}
+    netif_stats &stats = *(this->stats);
+    NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(stats);
 
+    // arduino::lock();
+    while(ETHER_RD0_RACT != (ctrl.p_rx_descriptor->status & ETHER_RD0_RACT)) {
+        NETIF_STATS_RX_TIME_START(stats); // FIXME add stats
+        // Getting the available data in the Eth DMA buffer
+        err = R_ETHER_Read(&this->ctrl, &rx_frame_buf, &rx_frame_dim);
+        // DEBUG_INFO("[polling] read %08X, %u, %u", rx_frame_buf, rx_frame_dim, err);
+        if(err != FSP_SUCCESS) {
+            NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(stats);
+            NETIF_STATS_INCREMENT_ERROR(stats, err);
+            NETIF_STATS_RX_TIME_AVERAGE(stats);
 
 
-/* -------------------------------------------------------------------------- */
-void EthernetDriver::irq_callback(ether_callback_args_t * p_args) {
-/* -------------------------------------------------------------------------- */  
-    p_args->status_ecsr;
-    uint32_t reg_eesr = p_args->status_eesr;
-    if(p_args->channel == ETHERNET_CHANNEL) {
-        if(p_args->event == ETHER_EVENT_WAKEON_LAN) {
-            
-            /* WAKE ON */
-            if(lan_wake_up != nullptr) {
-                lan_wake_up();
-            }
-        }
-        else if(p_args->event == ETHER_EVENT_LINK_ON) {
-            
-            /* LINK ON */
-            if(link_on != nullptr) {
-                link_on();
-            }
+            // Error, discarding the buffer without consuming it
+            R_ETHER_BufferRelease(&ctrl);
+            break;
         }
-        else if(p_args->event == ETHER_EVENT_LINK_OFF) {
-            
-            /* LINK OFF */
-            if(link_off != nullptr) {
-                link_off();
-            }
+
+        // giving the ownership of the buffer to the module using the driver,
+        // this memory should be now handled by the new owner
+        if(!this->consumed) {
+            // DEBUG_INFO("buf %8X", rx_frame_buf);
+            this->consume_cbk(rx_frame_buf, rx_frame_dim);
         }
-        else if(p_args->event == ETHER_EVENT_INTERRUPT) {
-            if (ETHER_MAGIC_PACKET_DETECTED_MASK == (p_args->status_ecsr & ETHER_MAGIC_PACKET_DETECTED_MASK)) {
-                
-                /* MAGIC PACKET DETECTED */
-                if(magic_packet_received != nullptr) {
-                    magic_packet_received();
-                }
-            }
-            if (ETHER_FRAME_TRANSFER_COMPLETED == (reg_eesr & ETHER_FRAME_TRANSFER_COMPLETED)) {
-                
-                
-                frame_being_transmitted = false;
-                /* FRAME TRANSMISSION COMPLETED */
-                if(frame_transmitted != nullptr) {
-                    frame_transmitted();
-                }
-            }
-            if (ETHER_FRAME_RECEIVED_MASK == (reg_eesr & ETHER_FRAME_RECEIVED_MASK)) {
-                
-                /* FRAME RECEIVED */
-                if(frame_received != nullptr) {
-                    frame_received();
-                }
-            } 
-            if( (reg_eesr & ADE_BIT_MASK) == ADE_BIT_MASK) {
-                /* weird error with ADE bit set as soon as reception is enabled */
-                eth_reset_due_to_ADE_bit();
-            }
+
+        // TODO find a way to put a limit into the number of mem_malloc
+        arduino::lock();
+        uint8_t* new_buffer = (uint8_t*)buffer_allocator(buffer_size);
+        arduino::unlock();
+
+        if(new_buffer == nullptr) {
+            this->consumed = true; // this indicates that the buffer had been consumed, but the new buffer isn't allocated
+            break;
+        } else {
+            this->consumed = false; // this indicates that the buffer had been consumed and the new buffer is allocated
         }
-        else {
-            
+        err = R_ETHER_RxBufferUpdate(&ctrl, new_buffer);
+        NETIF_STATS_INCREMENT_ERROR(stats, err);
+        NETIF_STATS_RX_TIME_AVERAGE(stats);
+        if(err != FSP_SUCCESS) {
+            // DEBUG_INFO("%u", err); // FIXME handle this
         }
     }
+    // arduino::unlock();
+
+    // Polling the tx descriptor for Tx transmission completed
+    // while(R_ETHER_TxStatusGet(this->ctrl, tx_buffers_info[first].buffer) {
+        // // FIXME check that first and the completed packet are valid
+        // // FIXME move this into the poll function
+        // tx_buffers_info[first].len = 0;
+
+        // if(tx_buffers_info[first].free_function) {
+        //     tx_buffers_info[first].free_function(tx_buffers_info[first].buffer);
+        //     tx_buffers_info[first].free_function = nullptr;
+        // } else {
+        //     free(tx_buffers_info[first].buffer);
+        // }
+        // tx_buffers_info[first].buffer = nullptr;
+        // first = (first + 1) % tx_descriptors_len;
+    // }
 }
 
-/* -------------------------------------------------------------------------- */
-void eth_set_mac_address(const uint8_t *mad) {
-/* -------------------------------------------------------------------------- */    
-    for(int i = 0; i < MAC_ADDRESS_DIM; i++) {
-        eth_driver.mac_address[i] = *(mad + i);
+network_driver_send_err_t EthernetC33Driver::send(
+    uint8_t* data, uint16_t len, network_driver_send_flags_t flags, void(*free_function)(void*)) {
+    // dump_buffer(tx_buffers_info[last].buffer, len);
+    // dump_buffer(data, len);
+    // DEBUG_INFO("[send] %08X, %u", data, len);
+    network_driver_send_err_t res = NETWORK_DRIVER_SEND_ERR_OK;
+    fsp_err_t err;
+
+    arduino::lock();
+    // the current buffer we are referencing is not yet consumed
+    if(this->tx_buffers_info[this->last].len != 0) {
+        res = NETWORK_DRIVER_SEND_ERR_BUFFER;
+        goto exit;
     }
-}
 
-/* -------------------------------------------------------------------------- */
-int eth_get_mac_address(uint8_t *mad) {
-/* -------------------------------------------------------------------------- */    
-    for(int i = 0; i < MAC_ADDRESS_DIM; i++) {
-        *(mad + i) = eth_driver.mac_address[i];
-    }
-    return MAC_ADDRESS_DIM;
-}
+    if(flags == NETWORK_DRIVER_SEND_FLAGS_NONE) {
+        // it could be nice to use buffer_allocator, but we need a way to deallocate it
+        this->tx_buffers_info[this->last].buffer = (uint8_t*)memalign(32, len); // TODO does this need to be memaligned? I think not
 
+        if(this->tx_buffers_info[this->last].buffer == nullptr) {
+            res = NETWORK_DRIVER_SEND_ERR_BUFFER;
+            goto exit;
+        }
 
-/* -------------------------------------------------------------------------- */
-bool eth_init() {
-/* -------------------------------------------------------------------------- */    
-    bool rv = false;
-    
-    if(!eth_driver.enableIrq(ETHERNET_IRQ_PRIORITY)) {
-        return rv;
+        // perform a memcpy to the local tx_buffer
+        memcpy(this->tx_buffers_info[this->last].buffer, data, len);
+    } else if(flags == NETWORK_DRIVER_SEND_FLAGS_ZERO_COPY) {
+        this->tx_buffers_info[this->last].buffer = data; // FIXME verify this mode
     }
 
+    this->tx_buffers_info[this->last].len = len;
+    this->tx_buffers_info[this->last].free_function = free_function;
 
-    /* ----
-     * open
-     * ---- */
-    fsp_err_t err = R_ETHER_Open(eth_driver.get_ctrl(), eth_driver.get_cfg());
-    
-    /*
-    uint32_t *reg =  (uint32_t *)0x40114118;
-    *reg = 0x37;    
-    reg =  (uint32_t *)0x40114030;
-    *reg = 0x47FF099F;
-    */
-    if(err == FSP_SUCCESS) {
-        rv = true;
-    }
-    #ifdef ETHERNET_DEBUG_ENABLED
-    else if(err == FSP_ERR_ALREADY_OPEN) {
-        rv = false;
-    }
-    else if(err == FSP_ERR_ETHER_ERROR_PHY_COMMUNICATION) {
-        rv = false;
-    }
-    else if(err == FSP_ERR_ETHER_PHY_ERROR_LINK) {
-        rv = false;
+    // dump_buffer(this->tx_buffers_info[this->last].buffer, len);
+    // dump_buffer(data, len);
+
+    // put this buffer in the next circular buffer position and then increment the index
+    // TODO handle the case where a packet is already being transmitted, should WRITE be called after the queued packet is correctly sent?
+    err = R_ETHER_Write(
+        &this->ctrl, this->tx_buffers_info[this->last].buffer, this->tx_buffers_info[this->last].len);
+    this->last = (this->last + 1) % this->tx_descriptors_len;
+    if(err != FSP_SUCCESS) {
+        res = NETWORK_DRIVER_SEND_ERR_DRIVER;
     }
-    #endif
-    
-    
-    
-    #ifdef IGMP_HARDWARE_LEVEL
-    /* CODE TO BE VERIFIED  */
-    #if LWIP_IGMP
-    netif_set_igmp_mac_filter(netif, igmp_mac_filter);
-    #endif
-    HAL_ETH_ReadPHYRegister(&EthHandle, PHY_IMR, &regvalue);
-
-    regvalue |= PHY_ISFR_INT4;
-
-    /* Enable Interrupt on change of link status */
-    HAL_ETH_WritePHYRegister(&EthHandle, PHY_IMR, regvalue);
-    #if LWIP_IGMP
-    ETH_HashTableHigh = EthHandle.Instance->MACHTHR;
-    ETH_HashTableLow = EthHandle.Instance->MACHTLR;
-    #endif
-    #endif
-
-    return rv;
-}
 
-void eth_execute_link_process() {
-    R_ETHER_LinkProcess(eth_driver.get_ctrl()); 
+exit:
+    arduino::unlock();
+    return res;
 }
 
-void eth_release_rx_buffer() {
-    R_ETHER_BufferRelease(eth_driver.get_ctrl());
-}
+fsp_err_t EthernetC33Driver::open() {
+    bool rv = IRQManager::getInstance().addPeripheral(IRQ_ETHERNET, &cfg);
 
+    if(rv) {
+        // cfg.interrupt_priority = irq_priority;
+        R_BSP_IrqCfgEnable(cfg.irq, cfg.interrupt_priority, &this->ctrl); /* ??? */
+    } else {
+        // DEBUG_ERROR("Error setting up irq for ethernet");
 
+        // return -1000; // FIXME error codes should be defined at ArduinoAPI level
+        return FSP_ERR_ABORTED;
+    }
 
-bool eth_output(uint8_t *buf, uint16_t dim) {
-    bool retval = true;
+    return R_ETHER_Open(&this->ctrl, &this->cfg);
+}
 
-    fsp_err_t err = R_ETHER_Write(eth_driver.get_ctrl(), buf, dim);
-    if(err == FSP_SUCCESS) {
-        frame_being_transmitted = true;
-        retval = true;
-    }
-    else {
-        retval = false;
+void EthernetC33Driver::up() {
+    fsp_err_t err = FSP_SUCCESS;
+
+    err = this->open();
+
+    if(err != FSP_SUCCESS) {
+        // return err;
     }
 
-    return retval;
+    do {
+        err = this->linkProcess();
+        // TODO check if error assumes values that are not correct
+        // TODO put a timeout in here
+    } while(err != FSP_SUCCESS);
+
+    // return err; // FIXME find a proper way of returning an error
 }
 
-// this function return true if the tx buffer is not being used for the transmission of another frame
-bool eth_output_can_transimit() {
-    return !frame_being_transmitted;
+void EthernetC33Driver::down() {
+    // return
+    // FIXME implement this
 }
 
-uint8_t *eth_input(volatile uint32_t *dim) {
-    /* NOTE: ZERO COPY IMPLEMENTATION 
-       just the pointer and not the data are copied with the Read Function */
-    
-    uint8_t *ptr1 = nullptr;
+fsp_err_t EthernetC33Driver::linkProcess() {
+    return R_ETHER_LinkProcess(&this->ctrl);
+}
 
-    fsp_err_t err = R_ETHER_Read ( eth_driver.get_ctrl(), &ptr1, (uint32_t *)dim);
-    if(err == FSP_SUCCESS) {
-        return ptr1;
-    }
-    else {
-        return nullptr;
+
+void EthernetC33Driver::eth_reset_due_to_ADE_bit() {
+    uint32_t *EDMAC_EESR_REG = (uint32_t *)0x40114028;
+    uint32_t *EDMAC_CONTROL_REG = (uint32_t *)0x40114000;
+    if( (*EDMAC_EESR_REG & ADE_BIT_MASK) == ADE_BIT_MASK) {
+        R_ETHER_Close(&this->ctrl);
+        *EDMAC_CONTROL_REG |= 0x1;
+        R_ETHER_Open(&this->ctrl, &this->cfg);
     }
 }
 
+void EthernetC33Driver::irq_ether_callback(ether_callback_args_t* p_args) {
+    // ether_callback_args_t* p_args = (ether_callback_args_t*) args;
+    p_args->status_ecsr;
+    uint32_t reg_eesr = p_args->status_eesr;
+
+    if(p_args->channel == ETHERNET_CHANNEL) {
+        if(p_args->event == ETHER_EVENT_WAKEON_LAN) {
+            /* WAKE ON */
+            if(this->wake_lan_cbk != nullptr) {
+                this->wake_lan_cbk();
+            }
+        } else if(p_args->event == ETHER_EVENT_LINK_ON) {
+            // /* LINK ON */
+            if(this->link_up_cbk != nullptr) {
+                this->link_up_cbk();
+            }
+        } else if(p_args->event == ETHER_EVENT_LINK_OFF) {
+            /* LINK OFF */
+            if(this->link_down_cbk != nullptr) {
+                this->link_down_cbk();
+            }
+        } else if(p_args->event == ETHER_EVENT_INTERRUPT) {
+            if (ETHER_MAGIC_PACKET_DETECTED_MASK == (p_args->status_ecsr & ETHER_MAGIC_PACKET_DETECTED_MASK)) {
+                // /* MAGIC PACKET DETECTED */
+                if(this->magic_packet_cbk != nullptr) {
+                    this->magic_packet_cbk();
+                }
+            }
+            if (ETHER_FRAME_TRANSFER_COMPLETED  == (reg_eesr & ETHER_FRAME_TRANSFER_COMPLETED)) {
 
-void eth_set_rx_frame_cbk    (EtherCallback_f fn) { frame_received = fn; }
-void eth_set_tx_frame_cbk    (EtherCallback_f fn) { frame_transmitted = fn; }
-void eth_set_link_on_cbk     (EtherCallback_f fn) { link_on = fn; }
-void eth_set_link_off_cbk    (EtherCallback_f fn) { link_off = fn; }
-void eth_set_lan_wake_up_cbk (EtherCallback_f fn) { lan_wake_up = fn;}
-void eth_set_magic_packet_cbk(EtherCallback_f fn) { magic_packet_received = fn;}
 
+                // FIXME check that first and the completed packet are valid
+                // FIXME move this into the poll function
+                // FIXME define a function out of this
+                if(tx_buffers_info[first].len == 0 || tx_buffers_info[first].buffer == nullptr) {
+                    return;
+                }
+                arduino::lock();
 
+                if(tx_buffers_info[first].free_function) {
+                    tx_buffers_info[first].free_function(tx_buffers_info[first].buffer);
+                    tx_buffers_info[first].free_function = nullptr;
+                } else {
+                    free(tx_buffers_info[first].buffer);
+                }
+                tx_buffers_info[first].len = 0;
+                tx_buffers_info[first].buffer = nullptr;
+                first = (first + 1) % tx_descriptors_len;
+                arduino::unlock();
 
-#ifdef IGMP_HARDWARE_LEVEL
-#if LWIP_IGMP
-#ifndef HASH_BITS
-#define HASH_BITS 6 /* #bits in hash */
-#endif
 
-uint32_t ethcrc(const uint8_t *data, size_t length)
-{
-  uint32_t crc = 0xffffffff;
-  size_t i;
-  int j;
+                if(this->tx_frame_cbk != nullptr) {
+                    this->tx_frame_cbk();
+                }
+            }
+            if (ETHER_FRAME_RECEIVED_MASK       == (reg_eesr & ETHER_FRAME_RECEIVED_MASK)) {
+                /* FRAME RECEIVED */
 
-  for (i = 0; i < length; i++) {
-    for (j = 0; j < 8; j++) {
-      if (((crc >> 31) ^ (data[i] >> j)) & 0x01) {
-        /* x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1 */
-        crc = (crc << 1) ^ 0x04C11DB7;
-      } else {
-        crc = crc << 1;
-      }
+                // We are using polling mode, we don't need this
+            }
+            if (ADE_BIT_MASK                    == (reg_eesr & ADE_BIT_MASK)) {
+                /* weird error with ADE bit set as soon as reception is enabled */
+                this->eth_reset_due_to_ADE_bit();
+            }
+        }
     }
-  }
-  return ~crc;
-}
-
-void register_multicast_address(const uint8_t *mac)
-{
-  uint32_t crc;
-  uint8_t hash;
-
-  /* Calculate crc32 value of mac address */
-  crc = ethcrc(mac, HASH_BITS);
-
-  /*
-   * Only upper HASH_BITS are used
-   * which point to specific bit in the hash registers
-   */
-  hash = (crc >> 26) & 0x3F;
-
-  if (hash > 31) {
-    ETH_HashTableHigh |= 1 << (hash - 32);
-    EthHandle.Instance->MACHTHR = ETH_HashTableHigh;
-  } else {
-    ETH_HashTableLow |= 1 << hash;
-    EthHandle.Instance->MACHTLR = ETH_HashTableLow;
-  }
 }
 
-err_t igmp_mac_filter(struct netif *netif, const ip4_addr_t *ip4_addr, netif_mac_filter_action action)
-{
-  uint8_t mac[6];
-  const uint8_t *p = (const uint8_t *)ip4_addr;
+void _irq_ether_callback(ether_callback_args_t* p_args) {
+    // _IRQEtherHandler* context = (_IRQEtherHandler*)p_args->p_context;
+        // dynamic_cast<_IRQEtherHandler*>(p_args->p_context);
+    EthernetC33Driver* context =
+        // dynamic_cast<EthernetC33Driver*>(p_args->p_context);
+        (EthernetC33Driver*)p_args->p_context;
 
-  mac[0] = 0x01;
-  mac[1] = 0x00;
-  mac[2] = 0x5E;
-  mac[3] = *(p + 1) & 0x7F;
-  mac[4] = *(p + 2);
-  mac[5] = *(p + 3);
-
-  register_multicast_address(mac);
-
-  return 0;
+    context->irq_ether_callback(p_args);
 }
-#endif /* LWIP_IGMP */
-#endif
+
+// #ifdef IGMP_HARDWARE_LEVEL
+// #if LWIP_IGMP
+// #ifndef HASH_BITS
+// #define HASH_BITS 6 /* #bits in hash */
+// #endif
+// FIXME integrate these functions correctly into the library
+// uint32_t ethcrc(const uint8_t *data, size_t length)
+// {
+//   uint32_t crc = 0xffffffff;
+//   size_t i;
+//   int j;
+
+//   for (i = 0; i < length; i++) {
+//     for (j = 0; j < 8; j++) {
+//       if (((crc >> 31) ^ (data[i] >> j)) & 0x01) {
+//         /* x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1 */
+//         crc = (crc << 1) ^ 0x04C11DB7;
+//       } else {
+//         crc = crc << 1;
+//       }
+//     }
+//   }
+//   return ~crc;
+// }
+
+// void register_multicast_address(const uint8_t *mac)
+// {
+//   uint32_t crc;
+//   uint8_t hash;
+
+//   /* Calculate crc32 value of mac address */
+//   crc = ethcrc(mac, HASH_BITS);
+
+//   /*
+//    * Only upper HASH_BITS are used
+//    * which point to specific bit in the hash registers
+//    */
+//   hash = (crc >> 26) & 0x3F;
+
+//   if (hash > 31) {
+//     ETH_HashTableHigh |= 1 << (hash - 32);
+//     EthHandle.Instance->MACHTHR = ETH_HashTableHigh;
+//   } else {
+//     ETH_HashTableLow |= 1 << hash;
+//     EthHandle.Instance->MACHTLR = ETH_HashTableLow;
+//   }
+// }
+
+// err_t igmp_mac_filter(struct netif *netif, const ip4_addr_t *ip4_addr, netif_mac_filter_action action)
+// {
+//   uint8_t mac[6];
+//   const uint8_t *p = (const uint8_t *)ip4_addr;
+
+//   mac[0] = 0x01;
+//   mac[1] = 0x00;
+//   mac[2] = 0x5E;
+//   mac[3] = *(p + 1) & 0x7F;
+//   mac[4] = *(p + 2);
+//   mac[5] = *(p + 3);
+
+//   register_multicast_address(mac);
+
+//   return 0;
+// }
+// #endif /* LWIP_IGMP */
+// #endif
diff --git a/libraries/Ethernet/src/EthernetDriver.h b/libraries/Ethernet/src/EthernetDriver.h
index 18cf68f9f..8af05d6c2 100644
--- a/libraries/Ethernet/src/EthernetDriver.h
+++ b/libraries/Ethernet/src/EthernetDriver.h
@@ -1,38 +1,98 @@
-#ifndef _ARDUINO_RENESAS_ETHERNET_DRIVER_
-#define _ARDUINO_RENESAS_ETHERNET_DRIVER_
+#pragma once
 
 #include "r_ether_phy_api.h"
 #include "r_ether_phy.h"
 #include "r_ether_api.h"
 #include "r_ether.h"
 #include <functional>
+#include <driver.h>
 
-using EtherCallback_f        = std::function<void(void)>;
+class EthernetC33Driver: public NetworkDriver {
+public:
+    EthernetC33Driver(
+        uint8_t rx_descriptors_len=1,
+        uint8_t tx_descriptors_len=1,
+        void* (*buffer_allocator)(unsigned int)=malloc, // The allocator should return 16 byte aligned
+        uint16_t buffer_size=1536,
+        uint8_t* mac_address=nullptr, uint8_t len=0); // TODO provide pinmapping as parameter to the constructor
+    ~EthernetC33Driver();
 
-#define ETHERNET_IRQ_PRIORITY   10
 
+    /*
+     * TODO define the meaning of begin: open + link up?
+     */
+    virtual void begin();
 
-#define MAC_ADDRESS_DIM     6
-#define ETH_BUFF_DIM        1536 
+    // Provide a function to poll the driver
+    virtual void poll();
 
+    virtual fsp_err_t open(); // FIXME errors should be abstracted
+    virtual fsp_err_t linkProcess();
+    virtual void up();
+    virtual void down();
 
-/* set the MAC ADDRESS, use the function before the initialization */
-void eth_set_mac_address(const uint8_t *mad);
-int  eth_get_mac_address(uint8_t *mad);
+    virtual network_driver_send_err_t send(uint8_t* data, uint16_t len,
+        network_driver_send_flags_t flags=NETWORK_DRIVER_SEND_FLAGS_NONE,
+        void(*free_function)(void*)=nullptr);
 
-bool eth_init();
-void eth_execute_link_process();
-uint8_t *eth_input(volatile uint32_t *dim);
-bool eth_output(uint8_t *buf, uint16_t dim);
-bool eth_output_can_transimit();
-void eth_release_rx_buffer();
-uint8_t *eth_get_tx_buffer(uint16_t *size);
-void eth_set_rx_frame_cbk(EtherCallback_f fn);
-void eth_set_tx_frame_cbk(EtherCallback_f fn);
-void eth_set_link_on_cbk(EtherCallback_f fn);
-void eth_set_link_off_cbk(EtherCallback_f fn);
-void eth_set_lan_wake_up_cbk(EtherCallback_f fn);
-void eth_set_magic_packet_cbk(EtherCallback_f fn);
 
+    // TODO add callbacks getters/setters
+    virtual uint8_t* getMacAddress() override { return this->macaddress; }
+protected:
 
+    // extend the callbacks and add the Driver specific callbacks
+    std::function<void()> wake_lan_cbk;
+    std::function<void()> magic_packet_cbk;
+
+private:
+    ether_instance_descriptor_t *tx_descriptors;
+    ether_instance_descriptor_t *rx_descriptors;
+
+    uint8_t **rx_buffers;
+
+    // array containing the info of the buffers queued to be sent
+    struct _tx_buffer_info {
+        uint16_t len=0;
+        uint8_t* buffer=nullptr;
+        void(*free_function)(void*)=nullptr;
+    };
+    _tx_buffer_info *tx_buffers_info;
+
+    // tx circular buffer cursors
+    uint8_t last = 0, first=0;
+
+    // uint8_t tx_buffer[1536];
+    volatile bool frame_in_transmission = false;
+
+    uint8_t macaddress[8]; // FIXME differentiate between 6 and 8 len
+    uint8_t macaddress_len = 0;
+
+    // FSP structures for control and configuration of the driver
+    ether_phy_cfg_t           phy_cfg;
+    ether_phy_instance_ctrl_t phy_ctrl;
+    ether_phy_instance_t      phy_instance;
+    ether_cfg_t               cfg;
+    ether_instance_ctrl_t     ctrl;
+    ether_extended_cfg_t      extended_cfg;
+    const uint32_t            irq_priority = 10;
+
+    uint8_t rx_descriptors_len;
+    uint8_t tx_descriptors_len;
+    void* (*buffer_allocator)(unsigned int);
+    uint16_t buffer_size;
+
+    bool consumed = false;
+
+    // This function initializes the driver and its configuration
+    // TODO provide a way for the user to override the settings
+    void init();
+
+    // Strange function that needs to be present, for whatever reason, keeping it
+    void eth_reset_due_to_ADE_bit();
+
+    virtual void irq_ether_callback(ether_callback_args_t* p_args);
+    friend void _irq_ether_callback(ether_callback_args_t* p_args);
 #endif
+};
+
+extern EthernetC33Driver C33EthernetDriver;
\ No newline at end of file

From 452accfed2a111aeba71b0894dde24ea2d202d76 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 28 Dec 2023 11:07:12 +0100
Subject: [PATCH 02/79] added library containing abstract network stack classes

---
 libraries/NetworkAPI/library.properties |  9 +++
 libraries/NetworkAPI/src/driver.h       | 74 +++++++++++++++++++++++++
 libraries/NetworkAPI/src/interface.h    |  1 +
 3 files changed, 84 insertions(+)
 create mode 100644 libraries/NetworkAPI/library.properties
 create mode 100644 libraries/NetworkAPI/src/driver.h
 create mode 100644 libraries/NetworkAPI/src/interface.h

diff --git a/libraries/NetworkAPI/library.properties b/libraries/NetworkAPI/library.properties
new file mode 100644
index 000000000..1f161d41d
--- /dev/null
+++ b/libraries/NetworkAPI/library.properties
@@ -0,0 +1,9 @@
+name=NetworkAPI
+version=1.0.0
+author=Arduino, Andrea Gilardoni
+maintainer=Arduino <info@arduino.cc>
+sentence=Definition of Network classes shared among different types of network interfaces
+paragraph=This library contains the definition of the abstract classes used in the networking stack
+category=Communication
+url=https://github.com/arduino/ArduinoCore-renesas/tree/master/libraries/NetworkAPI
+architectures=renesas,renesas_portenta
diff --git a/libraries/NetworkAPI/src/driver.h b/libraries/NetworkAPI/src/driver.h
new file mode 100644
index 000000000..3b347f773
--- /dev/null
+++ b/libraries/NetworkAPI/src/driver.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include <functional>
+#include <stdint.h>
+
+enum network_driver_send_flags_t: uint8_t {
+    NETWORK_DRIVER_SEND_FLAGS_NONE      = 0,
+    // this option instructs the send function that it doesn't need to perform a memcpy to the passed argument
+    // and is in charge of deleting the buffer
+    NETWORK_DRIVER_SEND_FLAGS_ZERO_COPY = 1
+};
+
+enum network_driver_send_err_t: uint8_t {
+    NETWORK_DRIVER_SEND_ERR_OK      = 0,
+    NETWORK_DRIVER_SEND_ERR_MEM     = 1, // memory issues when trying to send a packet
+    NETWORK_DRIVER_SEND_ERR_BUFFER  = 2, // there is no available buffer for sending the packet
+    NETWORK_DRIVER_SEND_ERR_DRIVER  = 3  // generic error happening at fsp level
+};
+
+
+class NetworkDriver {
+public:
+    NetworkDriver() {};
+    virtual ~NetworkDriver() {};
+
+    /*
+     * This function is used by the Interface handling the driver,
+     * if used in polling mode, leave empty definition if the driver works though interrupts.
+     * When working with interrupts it is expected that the constructor definces them
+     */
+    virtual void poll() {}; // TODO is it better to have a function pointer, that when set to null is not called?
+
+    /*
+     * This function is used to inistialize the driver at runtime and start using it
+     */
+    virtual void begin() = 0;
+
+    /*
+     * this function is used to send data to the network
+     * + flags are used to specify additional options when sending
+     * + when NETWORK_DRIVER_SEND_FLAGS_ZERO_COPY is provided, a free function must be passed, [default libc free()]
+     */
+    virtual network_driver_send_err_t send(uint8_t* data, uint16_t len,
+        network_driver_send_flags_t flags=NETWORK_DRIVER_SEND_FLAGS_NONE,
+        void(*free_function)(void*)=free) = 0;
+
+    /*
+     * Sets the callback funtion that is then used to consume incoming data
+     */
+    virtual void setConsumeCallback(std::function<void(uint8_t*, uint32_t)> consume_cbk) {this->consume_cbk = consume_cbk;}
+    virtual void setLinkUpCallback(std::function<void()> link_up_cbk) {this->link_up_cbk = link_up_cbk;}
+    virtual void setLinkDownCallback(std::function<void()> link_down_cbk) {this->link_down_cbk = link_down_cbk;}
+
+    /*
+     * FIXME define interfaces for RX zero copy
+     */
+
+
+    /*
+     * The following functions should set the low level interface to up or down state
+     */
+    virtual void up() = 0;
+    virtual void down() = 0;
+
+    // TODO maybe we can manage mac address in the interface
+    virtual uint8_t* getMacAddress() = 0;
+    // TODO define callback functions for generic functionalities a network driver has to cope with, like link_up event
+protected:
+    std::function<void(uint8_t*, uint32_t)> consume_cbk; // TODO move in callbacks
+
+    std::function<void()> tx_frame_cbk;
+    std::function<void()> link_up_cbk;
+    std::function<void()> link_down_cbk;
+};
\ No newline at end of file
diff --git a/libraries/NetworkAPI/src/interface.h b/libraries/NetworkAPI/src/interface.h
new file mode 100644
index 000000000..7b9637ef9
--- /dev/null
+++ b/libraries/NetworkAPI/src/interface.h
@@ -0,0 +1 @@
+#pragma once
\ No newline at end of file

From 30725adc180fa44d5806c34d2e345bcbede604a8 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 28 Dec 2023 11:08:21 +0100
Subject: [PATCH 03/79] reimplementing the tcp client with the aim of solving
 issues

the issue solved is tcp window were missing and that was due to:
- ACK were sent as soonas packets were received and not when they were
  consumed by the application layer
- pbufs where not cleared correctly and following lwip indications
---
 libraries/lwIpWrapper/src/lwipClient.cpp | 511 +++++++++++++++--------
 libraries/lwIpWrapper/src/lwipClient.h   |  87 ++--
 2 files changed, 396 insertions(+), 202 deletions(-)

diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index 745f4d504..c22237e2a 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -6,36 +6,41 @@ extern "C" {
 
 #include "lwipClient.h"
 
-/* -------------------------------------------------------------------------- */
+// FIXME understand hos to syncronize the interrupt thread and "userspace"
+// TODO look into tcp_bind_netif for Ethernet and WiFiClient classes
+// TODO generalize the functions for extracting and inserting data into pbufs, they may be reused in UDP
+// TODO look into application polling:
+//      When a connection is idle (i.e., no data is either transmitted or received), lwIP will repeatedly poll the application by calling a specified callback function. This can be used either as a watchdog timer for killing connections that have stayed idle for too long, or as a method of waiting for memory to become available. For instance, if a call to tcp_write() has failed because memory wasn't available, the application may use the polling functionality to call tcp_write() again when the connection has been idle for a while.
+
+
+// Forward declarations
+err_t _lwip_tcp_connected_callback(void* arg, struct tcp_pcb* tpcb, err_t err);
+err_t _lwip_tcp_recv_callback(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err);
+static err_t _lwip_tcp_sent_callback(void* arg, struct tcp_pcb* tpcb, u16_t len);
+void _lwip_tcp_err_callback(void *arg, err_t err);
+
 lwipClient::lwipClient()
-    : _tcp_client(NULL)
-{
+    : pcb(NULL) {
 }
-/* -------------------------------------------------------------------------- */
 
 /* Deprecated constructor. Keeps compatibility with W5100 architecture
 sketches but sock is ignored. */
-/* -------------------------------------------------------------------------- */
 lwipClient::lwipClient(uint8_t sock)
-    : _tcp_client(NULL)
-{
+    : pcb(NULL) {
 }
-/* -------------------------------------------------------------------------- */
 
-/* -------------------------------------------------------------------------- */
-lwipClient::lwipClient(struct tcp_struct* tcpClient)
-{
-    _tcp_client = tcpClient;
+lwipClient::lwipClient(struct tcp_pcb* pcb)
+: pcb(pcb) {
 }
-/* -------------------------------------------------------------------------- */
 
-/* -------------------------------------------------------------------------- */
-int lwipClient::connect(const char* host, uint16_t port)
-{
-    /* -------------------------------------------------------------------------- */
+lwipClient::~lwipClient() {
+    this->stop();
+}
+
+int lwipClient::connect(const char* host, uint16_t port) {
     IPAddress remote_addr;
 
-    int ret = CLwipIf::getInstance().getHostByName(host, remote_addr);
+    int ret = CLwipIf::getInstance().getHostByName(host, remote_addr); // TODO test this
     if (ret == 1) {
         return connect(remote_addr, port);
     } else {
@@ -43,222 +48,388 @@ int lwipClient::connect(const char* host, uint16_t port)
     }
 }
 
-/* -------------------------------------------------------------------------- */
-int lwipClient::connect(IPAddress ip, uint16_t port)
-{
-    /* -------------------------------------------------------------------------- */
-    if (_tcp_client == NULL) {
-        /* Allocates memory for client */
-        _tcp_client = (struct tcp_struct*)mem_malloc(sizeof(struct tcp_struct));
+int lwipClient::connect(IPAddress ip, uint16_t port) {
+    err_t err = ERR_OK;
+    this->pcb = tcp_new();
 
-        if (_tcp_client == NULL) {
-            return 0;
-        }
+    if(this->pcb == nullptr) {
+        // return ; // TODO find the proper error code
+        return err;
     }
 
-    /* Creates a new TCP protocol control block */
-    _tcp_client->pcb = tcp_new();
+    tcp_err(this->pcb, _lwip_tcp_err_callback); // FIXME make this a user callback?
+    if(err != ERR_OK) {
+        return err;
+    }
 
-    if (_tcp_client->pcb == NULL) {
-        return 0;
+    this->state = TCP_NONE;
+
+    tcp_arg(this->pcb, this);
+
+    this->_ip = fromArduinoIP(ip);
+
+    // FIXME this doesn't include timeout of connection, does lwip have it by default?
+    err = tcp_connect(
+        this->pcb, &this->_ip, port, // FIXME check if _ip gets copied
+        _lwip_tcp_connected_callback // FIXME we need to define a static private function
+    );
+    return err;
+}
+
+err_t _lwip_tcp_connected_callback(void* arg, struct tcp_pcb* tpcb, err_t err) {
+    if(arg == NULL) {
+        // Setup was not performed correctly and the arg was not setup properly
+        // _lwip_tcp_connection_close(tpcb, tcp_arg);
+        // this->stop();// FIXME this doesn't exist
+
+        return ERR_ARG;
     }
 
-    _tcp_client->data.p = NULL;
-    _tcp_client->data.available = 0;
-    _tcp_client->state = TCP_NONE;
+    lwipClient* client = (lwipClient*)arg;
 
-    uint32_t startTime = millis();
-    ip_addr_t ipaddr;
-    tcp_arg(_tcp_client->pcb, _tcp_client);
-    if (ERR_OK != tcp_connect(_tcp_client->pcb, u8_to_ip_addr(rawIPAddress(ip), &ipaddr), port, &tcp_connected_callback)) {
-        stop();
-        return 0;
+    client->connected_callback(tpcb, err);
+}
+
+err_t lwipClient::connected_callback(struct tcp_pcb* tpcb, err_t err) {
+    if(err != ERR_OK) {
+        // lwip_tcp_connection_close(tpcb, tcp_arg);
+        this->stop();
+
+        return err;
     }
 
-    startTime = millis();
-    while (_tcp_client->state == TCP_NONE) {
-        CLwipIf::getInstance().lwip_task();
-        if ((_tcp_client->state == TCP_CLOSING) || ((millis() - startTime) >= _timeout)) {
-            stop();
-            return 0;
-        }
+    if(tcp_arg == NULL) {
+        // Setup was not performed correctly and the arg was not setup properly
+        // lwip_tcp_connection_close(tpcb, tcp_arg);
+        this->stop();
+
+        return ERR_ARG;
     }
 
-    return 1;
+    this->state = TCP_CONNECTED;
+
+    /* initialize LwIP tcp_recv callback function */
+    tcp_recv(tpcb, _lwip_tcp_recv_callback);
+
+    /* initialize LwIP tcp_sent callback function */
+    tcp_sent(tpcb, _lwip_tcp_sent_callback); // FIXME do we actually need it?
+
+    /* initialize LwIP tcp_err callback function */
+    // tcp_err(tpcb, lwip_tcp_err_callback); // initialized before, because we may get error during connection
+
+    // TODO understand if this could be helpful
+    // tcp_poll(tpcb, NULL, 0);
+
+    return err;
 }
 
-/* -------------------------------------------------------------------------- */
-size_t lwipClient::write(uint8_t b)
-{
-    /* -------------------------------------------------------------------------- */
-    return write(&b, 1);
+static err_t _lwip_tcp_sent_callback(void* arg, struct tcp_pcb* tpcb, u16_t len) {
+    if(arg == NULL) {
+        // Setup was not performed correctly and the arg was not setup properly
+        // _lwip_tcp_connection_close(tpcb, tcp_arg);
+        // this->stop(); // FIXME this doesn't exist
+
+        return ERR_ARG;
+    }
+
+    lwipClient* client = (lwipClient*)arg;
 }
 
-/* -------------------------------------------------------------------------- */
-size_t lwipClient::write(const uint8_t* buf, size_t size)
-{
-    /* -------------------------------------------------------------------------- */
-    if ((_tcp_client == NULL) || (_tcp_client->pcb == NULL) || (buf == NULL) || (size == 0)) {
-        return 0;
+// callback function that should be called when data has successfully been received (i.e., acknowledged)
+// by the remote host. The len argument passed to the callback function gives the amount bytes that
+// was acknowledged by the last acknowledgment.
+void _lwip_tcp_err_callback(void *arg, err_t err) {
+    if(arg == NULL) {
+        // Setup was not performed correctly and the arg was not setup properly
+        // _lwip_tcp_connection_close(tpcb, tcp_arg);
+        // this->stop(); // FIXME this doesn't exist
+
+        // return ERR_ARG;
+        return;
     }
 
-    /* If client not connected or accepted, it can't write because connection is
-    not ready */
-    if ((_tcp_client->state != TCP_ACCEPTED) && (_tcp_client->state != TCP_CONNECTED)) {
-        return 0;
+    lwipClient* client = (lwipClient*)arg;
+    // TODO add a callback for tcp errors in lwipClient
+}
+
+err_t _lwip_tcp_recv_callback(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err) {
+    if(arg == NULL) {
+        // Setup was not performed correctly and the arg was not setup properly
+        // _lwip_tcp_connection_close(tpcb, tcp_arg);
+        // this->stop(); // FIXME this doesn't exist
+
+        return ERR_ARG;
     }
 
-    size_t max_send_size, bytes_to_send;
-    size_t bytes_sent = 0;
-    size_t bytes_left = size;
-    err_t res;
+    lwipClient* client = (lwipClient*)arg;
 
-    do {
-        max_send_size = tcp_sndbuf(_tcp_client->pcb);
-        bytes_to_send = bytes_left > max_send_size ? max_send_size : bytes_left;
-
-        if (bytes_to_send > 0) {
-            res = tcp_write(_tcp_client->pcb, &buf[bytes_sent], bytes_to_send, TCP_WRITE_FLAG_COPY);
-
-            if (res == ERR_OK) {
-                bytes_sent += bytes_to_send;
-                bytes_left = size - bytes_sent;
-            } else if (res != ERR_MEM) {
-                // other error, cannot continue
-                return 0;
-            }
-        }
+    client->recv_callback(tpcb, p, err);
+}
+
+err_t lwipClient::recv_callback(struct tcp_pcb* tpcb, struct pbuf* p, err_t err) {
+    err_t ret_err = ERR_OK;
+
+    // FIXME this checks should be done on every callback
+    if(err != ERR_OK) {
+        this->stop();
+        return err;
+    }
 
-        // Force to send data right now!
-        if (ERR_OK != tcp_output(_tcp_client->pcb)) {
-            return 0;
+    if (p == NULL) {
+        // Remote host has closed the connection -> close from our side
+        this->stop();
+
+        return ERR_OK;
+    }
+    arduino::lock();
+    if(this->state == TCP_CONNECTED) {
+        if (this->pbuf_head == nullptr) {
+            // no need to increment the references of the pbuf,
+            // since it is already 1 and lwip shifts the control to this code
+            this->pbuf_head = p;
+        } else {
+            // no need to increment the references of p, since it is already 1 and the only reference is this->pbuf_head->next
+            pbuf_cat(this->pbuf_head, p);
         }
-        CLwipIf::getInstance().lwip_task();
 
-    } while (bytes_sent != size);
+        ret_err = ERR_OK;
+    }
+    arduino::unlock();
 
-    return size;
+    return ret_err;
 }
 
-/* -------------------------------------------------------------------------- */
-int lwipClient::available()
-{
-    /* -------------------------------------------------------------------------- */
-    CLwipIf::getInstance().lwip_task();
-    if (_tcp_client != NULL) {
-        return _tcp_client->data.available;
-    }
-    return 0;
+size_t lwipClient::write(uint8_t b) {
+    return write(&b, 1);
 }
 
-/* -------------------------------------------------------------------------- */
-int lwipClient::read()
-{
-    /* -------------------------------------------------------------------------- */
-    uint8_t b;
-    if ((_tcp_client != NULL) && (_tcp_client->data.p != NULL)) {
-        __disable_irq();
-        pbuffer_get_data(&(_tcp_client->data), &b, 1);
-        __enable_irq();
-        return b;
-    }
-    // No data available
-    return -1;
+size_t lwipClient::write(const uint8_t* buf, size_t size) {
+    arduino::lock();
+
+    uint8_t* buffer_cursor = (uint8_t*)buffer;
+    uint8_t bytes_to_send = 0;
+
+    do {
+        bytes_to_send = min(size - (buffer - buffer_cursor), tcp_sndbuf(this->pcb));
+
+        /*
+         * TODO: Look into the following flags, especially for write of 1 byte
+         * TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
+         * TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will not be set on last segment sent
+         */
+        err_t res = tcp_write(this->pcb, buffer_cursor, bytes_to_send, TCP_WRITE_FLAG_COPY);
+
+        if(res == ERR_OK) {
+            buffer_cursor += bytes_to_send;
+        } else if(res == ERR_MEM) {
+            // FIXME handle this: we get into this case only if the sent data cannot be put in the send queue
+        }
+
+        // TODO understand if the tcp_write will send data if the buffer is not full
+        // force send only if we filled the send buffer
+        // if (ERR_OK != tcp_output(this->pcb)) {
+        //     // return 0;
+        //     break;
+        // }
+    } while(buffer_cursor < buffer + size);
+    arduino::unlock();
+
+    return buffer - buffer_cursor;
+}
+
+int lwipClient::read() {
+    uint8_t c = 0;
+
+    int res = read(&c, 1);
+    return res == 1 ? c : res;
 }
 
-/* -------------------------------------------------------------------------- */
-int lwipClient::read(uint8_t* buf, size_t size)
-{
-    /* -------------------------------------------------------------------------- */
-    if ((_tcp_client != NULL) && (_tcp_client->data.p != NULL)) {
-        __disable_irq();
-        int rv = pbuffer_get_data(&(_tcp_client->data), buf, size);
-        __enable_irq();
-        return rv;
+int lwipClient::read(uint8_t* buf, size_t size) {
+    if(buffer_size==0 || buffer==nullptr || this->pbuf_head==nullptr) {
+        return 0; // TODO extend checks
     }
-    return -1;
+    // copy data from the lwip buffer to the app provided buffer
+    // TODO look into pbuf_get_contiguous(this->pbuf_head, buffer_cursor, len);
+    // pbuf_get_contiguous: returns the pointer to the payload if buffer_size <= pbuf.len
+    //      otherwise copies data in the user provided buffer. This can be used in a callback paradigm,
+    //      in order to avoid memcpy data
+
+    /*
+     * a chain of pbuf is not granted to have a size multiple of buffer_size length
+     * meaning that across different calls of this function a pbuf could be partially copied
+     * we need to account that
+     */
+    arduino::lock();
+    uint16_t copied = pbuf_copy_partial(this->pbuf_head, buffer, buffer_size, this->pbuf_offset);
+
+    this->free_pbuf_chain(copied);
+    // __enable_irq();
+    arduino::unlock();
+
+    return copied;
 }
 
-/* -------------------------------------------------------------------------- */
-int lwipClient::peek()
-{
-    /* -------------------------------------------------------------------------- */
+int lwipClient::peek() {
     uint8_t b;
     // Unlike recv, peek doesn't check to see if there's any data available, so we must
     if (!available()) {
         return -1;
     }
-    __disable_irq();
-    b = pbuf_get_at(_tcp_client->data.p, 0);
-    __enable_irq();
+
+    arduino::lock();
+    b = pbuf_get_at(this->pbuf_head, 0); // TODO test this
+    arduino::unlock();
+
     return b;
 }
 
-/* -------------------------------------------------------------------------- */
-void lwipClient::flush()
-{
-    /* -------------------------------------------------------------------------- */
-    if ((_tcp_client == NULL) || (_tcp_client->pcb == NULL)) {
+void lwipClient::flush() {
+    if ((this->pcb == NULL)) {
         return;
     }
-    tcp_output(_tcp_client->pcb);
-    CLwipIf::getInstance().lwip_task();
+    tcp_output(this->pcb);
 }
 
-/* -------------------------------------------------------------------------- */
-void lwipClient::stop()
-{
-    /* -------------------------------------------------------------------------- */
-    if (_tcp_client == NULL) {
-        return;
-    }
+void lwipClient::stop() {
+    tcp_recv(this->pcb, nullptr);
+    tcp_sent(this->pcb, nullptr);
+    tcp_poll(this->pcb, nullptr, 0);
+    tcp_err(this->pcb, nullptr);
+    tcp_accept(this->pcb, nullptr);
 
-    // close tcp connection if not closed yet
-    if (status() != TCP_CLOSING) {
-        tcp_connection_close(_tcp_client->pcb, _tcp_client);
+    if(this->pcb != nullptr) {
+        err_t err = tcp_close(this->pcb);
+        this->state = TCP_CLOSING;
+
+        this->pcb = nullptr;
+
+        // FIXME if err != ERR_OK retry, there may be memory issues, retry?
     }
+
+    // reset all the other variables in this class
+
+    // if(tcp->p != nullptr) {
+    //     pbuf_free(tcp->p); // FIXME it happens that a pbuf, with ref == 0 is added for some reason
+    // }
 }
 
-/* -------------------------------------------------------------------------- */
-uint8_t lwipClient::connected()
-{
-    /* -------------------------------------------------------------------------- */
-    uint8_t s = status();
-    return ((available() && (s == TCP_CLOSING)) || (s == TCP_CONNECTED) || (s == TCP_ACCEPTED));
+uint8_t lwipClient::connected() {
+    return this->state != TCP_NONE; //TODO
 }
 
-/* -------------------------------------------------------------------------- */
-uint8_t lwipClient::status()
-{
-    if (_tcp_client == NULL) {
+uint8_t lwipClient::status() {
+    if (this == NULL) {
         return TCP_NONE;
     }
-    return _tcp_client->state;
+    return this->state;
 }
 
 // the next function allows us to use the client returned by
 // EthernetServer::available() as the condition in an if-statement.
 
-/* -------------------------------------------------------------------------- */
-lwipClient::operator bool()
-{
-    /* -------------------------------------------------------------------------- */
-    return (_tcp_client != nullptr);
+lwipClient::operator bool() {
+    return (this->pcb != nullptr);
 }
 
-/* -------------------------------------------------------------------------- */
-bool lwipClient::operator==(const lwipClient& rhs)
-{
-    /* -------------------------------------------------------------------------- */
-    return _tcp_client == rhs._tcp_client && _tcp_client->pcb == rhs._tcp_client->pcb;
+bool lwipClient::operator==(const lwipClient& rhs) {
+    // return pcb == rhs.this && this->pcb == rhs.this->pcb;
 }
 
 /* This function is not a function defined by Arduino. This is a function
 specific to the W5100 architecture. To keep the compatibility we leave it and
 returns always 0. */
-/* -------------------------------------------------------------------------- */
-uint8_t lwipClient::getSocketNumber()
-{
-    /* -------------------------------------------------------------------------- */
+uint8_t lwipClient::getSocketNumber() {
     return 0;
 }
+
+// This function is useful for protocol that provide sequence delimiter, like http,
+// this allows the user to avoid using temporary buffers
+size_t lwipClient::read_until_token(
+    const uint8_t* buffer, uint16_t buffer_size, char* token, bool &found) {
+    if(buffer_size==0 || buffer==nullptr || this->pbuf_head==nullptr) {
+        return 0; // TODO extend checks
+    }
+    arduino::lock();
+    // TODO check that the buffer size is less than the token len
+
+    uint16_t offset=this->pbuf_offset;
+    /* iterate over pbufs until:
+    * - the first occurrence of token
+    * - the provided buffer is full
+    * - the available pbufs have been consumed
+    */
+    size_t tkn_len = strlen(token);
+
+    // FIXME if we have already found the token we hare wasting time to check the entire buffer again
+    uint16_t position = pbuf_memfind(this->pbuf_head, token, tkn_len, this->pbuf_offset); // TODO check efficiency of this function
+    uint16_t buf_copy_len = buffer_size;
+
+    // TODO triple check the indices of these conditions
+    if(position != 0xffff && position + tkn_len <= buffer_size) { // TODO consider how to handle the case that the chain is long 0xffff
+        // We found the token and it fits the user provided buffer
+        buf_copy_len = position + tkn_len;
+        found = true;
+    } else if(position != 0xffff && position < buffer_size && position + tkn_len > buffer_size) {
+        // if the token is found and fits partially with the user provided buffer
+        buf_copy_len = position - 1; // copy without consuming the token
+        found = false;
+    } else {
+        /*
+         * we cover 2 cases here:
+         * - we didn't find the token
+         * - we found the token, but it doesn't fit the user provided buffer
+         */
+        found = false;
+    }
+
+    uint16_t copied = pbuf_copy_partial(this->pbuf_head, (uint8_t*)buffer, buf_copy_len, this->pbuf_offset);
+
+    this->free_pbuf_chain(copied);
+    arduino::unlock();
+
+    return copied;
+}
+
+void lwipClient::free_pbuf_chain(uint16_t copied) {
+    arduino::lock();
+    /*
+     * free pbufs that have been copied, if copied == 0 we have an error
+     * free the buffer chain starting from the head up to the last entire pbuf ingested
+     * taking into account the previously not entirely consumed pbuf
+     */
+    uint32_t tobefreed = 0;
+    copied += this->pbuf_offset;
+
+    // in order to clean up the chain we need to find the pbuf in the last pbuf in the chain
+    // that got completely consumed by the application, dechain it from it successor and delete the chain before it
+
+    struct pbuf *head = this->pbuf_head, *last=head, *prev=nullptr; // FIXME little optimization prev can be substituted by last->next
+
+    while(last!=nullptr && last->len + tobefreed <= copied) {
+        tobefreed += last->len;
+        prev = last;
+        last = last->next;
+    }
+
+    // dechain if we are not at the end of the chain (last == nullptr)
+    // and if we haven't copied entirely the first pbuf (prev == nullptr) (head == last)
+    // if we reached the end of the chain set the this pbuf pointer to nullptr
+    if(prev != nullptr && last != nullptr) {
+        prev->next = nullptr;
+        this->pbuf_head = last;
+    } if(last == nullptr) {
+        this->pbuf_head = nullptr;
+    }
+
+    // the chain that is referenced by head is detached by the one referenced by this->pbuf_head
+    // free the chain if we haven't copied entirely the first pbuf (prev == nullptr)
+    if(this->pbuf_head != head) {
+        uint8_t refs = pbuf_free(head);
+    }
+
+    this->pbuf_offset = copied - tobefreed; // This offset should be referenced to the first pbuf in queue
+
+    // acknowledge the received data
+    tcp_recved(this->pcb, copied);
+    arduino::unlock();
+}
diff --git a/libraries/lwIpWrapper/src/lwipClient.h b/libraries/lwIpWrapper/src/lwipClient.h
index 511b3c7f1..b087122f4 100644
--- a/libraries/lwIpWrapper/src/lwipClient.h
+++ b/libraries/lwIpWrapper/src/lwipClient.h
@@ -1,63 +1,68 @@
-#ifndef ARDUINO_LWIP_CLIENT_H
-#define ARDUINO_LWIP_CLIENT_H
-
-#include "Arduino.h"
-#include "CNetIf.h"
-#include "Client.h"
-#include "IPAddress.h"
-#include "Print.h"
-#include "lwipMem.h"
-#include "lwipTcp.h"
-#include "lwipTypes.h"
+#pragma once
+#include <Client.h>
+#include <lwip/include/lwip/tcp.h>
+#include <IPAddress.h>
+#include <Print.h>
 
 class lwipClient : public Client {
 
 public:
     lwipClient();
     lwipClient(uint8_t sock);
-    lwipClient(struct tcp_struct* tcpClient);
+    lwipClient(struct tcp_struct* tcpClient); // FIXME this should be a private constructor, friend of Server
 
-    uint8_t status();
+    // disable copy constructor
+    LWIPTCPClient(const LWIPTCPClient&) = delete;
+    void operator=(const LWIPTCPClient&) = delete;
+
+    // keep move constructor
+    LWIPTCPClient(LWIPTCPClient&&);
+    void operator=(LWIPTCPClient&&);
+
+    virtual ~LWIPTCPClient();
+
+    virtual uint8_t status();
     virtual int connect(IPAddress ip, uint16_t port);
     virtual int connect(const char* host, uint16_t port);
+
     virtual size_t write(uint8_t);
     virtual size_t write(const uint8_t* buf, size_t size);
-    virtual int available();
+
+    inline virtual int available() { return this->pbuf_head == nullptr ? 0 : this->pbuf_head->tot_len; }
+
     virtual int read();
     virtual int read(uint8_t* buf, size_t size);
+    size_t read_until_token(
+        const uint8_t* buffer, uint16_t buffer_size, char* token, bool &found);
+
     virtual int peek();
     virtual void flush();
     virtual void stop();
     virtual uint8_t connected();
     virtual operator bool();
-    virtual bool operator==(const bool value)
-    {
+
+    virtual bool operator==(const bool value) {
         return bool() == value;
     }
-    virtual bool operator!=(const bool value)
-    {
+    virtual bool operator!=(const bool value) {
         return bool() != value;
     }
-    virtual bool operator==(const lwipClient&);
-    virtual bool operator!=(const lwipClient& rhs)
-    {
+    virtual bool operator==(const lwipClient&); // TODO why do we need this comparison operators?
+    virtual bool operator!=(const lwipClient& rhs) {
         return !this->operator==(rhs);
     };
+
     uint8_t getSocketNumber();
-    virtual uint16_t localPort()
-    {
+    virtual uint16_t localPort() {
         return (_tcp_client->pcb->local_port);
     };
-    virtual IPAddress remoteIP()
-    {
+    virtual IPAddress remoteIP() {
         return (IPAddress(_tcp_client->pcb->remote_ip.addr));
     };
-    virtual uint16_t remotePort()
-    {
+    virtual uint16_t remotePort() {
         return (_tcp_client->pcb->remote_port);
     };
-    void setConnectionTimeout(uint16_t timeout)
-    {
+    void setConnectionTimeout(uint16_t timeout) {
         _timeout = timeout;
     }
 
@@ -66,8 +71,26 @@ class lwipClient : public Client {
     using Print::write;
 
 private:
-    struct tcp_struct* _tcp_client;
+    enum _tcp_state_t: uint8_t {
+        TCP_NONE = 0,
+        // TCP_ACCEPTED,
+        TCP_CONNECTED,
+        TCP_CLOSING
+    };
+
+    // TCP related info of the socket
+    _tcp_state_t state =        TCP_NONE;
+    struct pbuf* pbuf_head =    nullptr;
+    struct tcp_pcb* pcb =              nullptr;
+    uint16_t pbuf_offset =      0;
+
     uint16_t _timeout = 10000;
-};
+    ip_addr_t _ip;
+
+    err_t connected_callback(struct tcp_pcb* tpcb, err_t err);
+    void free_pbuf_chain(uint16_t copied);
+    err_t recv_callback(struct tcp_pcb* tpcb, struct pbuf* p, err_t err);
 
-#endif
+    friend err_t _lwip_tcp_recv_callback(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err);
+    friend err_t _lwip_tcp_connected_callback(void* arg, struct tcp_pcb* tpcb, err_t err);
+};

From a0666afb0f534b283868ee164fd98534d2abea1c Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 28 Dec 2023 11:19:03 +0100
Subject: [PATCH 04/79] cleaning up

---
 libraries/lwIpWrapper/src/CNetIf.h | 16 ----------------
 1 file changed, 16 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 0b805d202..59efacf19 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -43,17 +43,6 @@
 
 #define WL_MAC_ADDR_LENGTH 6
 
-/* DEFAULT ADDRESS FOR ETHERNET CONFIGURATION */
-
-#define ETH_IFNAME0 'e'
-#define ETH_IFNAME1 't'
-
-#define WST_IFNAME0 'w'
-#define WST_IFNAME1 'f'
-
-#define WSA_IFNAME0 'w'
-#define WSA_IFNAME1 'a'
-
 typedef enum {
     WL_NO_SHIELD = 255,
     WL_NO_MODULE = WL_NO_SHIELD,
@@ -94,11 +83,6 @@ typedef enum {
 #define MAX_DHCP_TRIES 4
 #define TIMEOUT_DNS_REQUEST 10000U
 
-class CNetIf;
-
-using NetIfRxCb_f = int (*)(CNetIf*);
-using LwipInit_f = err_t (*)(struct netif* netif);
-using LwipInput_f = err_t (*)(struct pbuf* p, struct netif* inp);
 
 #define DHCP_CHECK_NONE (0)
 #define DHCP_CHECK_RENEW_FAIL (1)

From 4c0123f92641efad266d9b4cfffc0fdf55f3fee0 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 28 Dec 2023 11:19:20 +0100
Subject: [PATCH 05/79] redefinition of network Interface classes

---
 libraries/lwIpWrapper/src/CNetIf.h | 315 +++++++++++------------------
 1 file changed, 120 insertions(+), 195 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 59efacf19..c3dd913d6 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -95,77 +95,66 @@ typedef enum {
 #define TRUNCATED -3
 #define INVALID_RESPONSE -4
 
-typedef enum {
-    DHCP_IDLE_STATUS,
-    DHCP_START_STATUS,
-    DHCP_WAIT_STATUS,
-    DHCP_GOT_STATUS,
-    DHCP_RELEASE_STATUS,
-    DHCP_STOP_STATUS
-} DhcpSt_t;
 
 ip_addr_t* u8_to_ip_addr(uint8_t* ipu8, ip_addr_t* ipaddr);
 
 uint32_t ip_addr_to_u32(ip_addr_t* ipaddr);
 
 /* Base class implements DHCP, derived class will switch it on or off */
-/* -------------------------------------------------------------------------- */
 class CNetIf {
-    /* -------------------------------------------------------------------------- */
 protected:
-    int id;
     struct netif ni;
-#if LWIP_NETIF_HOSTNAME
-    char hostname[MAX_HOSTNAME_DIM];
-#endif
-
-    ip_addr_t ip;
-    ip_addr_t nm;
-    ip_addr_t gw;
 
-    /* these can be overridden by a config() function called before begin() */
-    static IPAddress default_ip;
-    static IPAddress default_nm;
-    static IPAddress default_gw;
-    static IPAddress default_dhcp_server_ip;
-
-    unsigned long dhcp_timeout;
-    DhcpSt_t dhcp_st;
-    bool dhcp_started;
-    volatile bool dhcp_acquired;
-    uint8_t _dhcp_lease_state;
-    void dhcp_task();
-    void dhcp_reset();
-    bool dhcp_request();
-    uint8_t dhcp_get_lease_state();
+#ifdef LWIP_DHCP
+    bool dhcp_acquired;
+#endif
 
-    IPAddress _dnsServerAddress;
+    // IPAddress _dnsServerAddress;
 
+    // Driver interface pointer
+    NetworkDriver *driver = nullptr;
 public:
     CNetIf();
     virtual ~CNetIf();
+    /*
+     * The begin function is called by the user in the sketch to initialize the network interface
+     * that he is planning on using in the sketch.
+     */
+    virtual void begin(
+        const IPAddress &ip = INADDR_NONE,
+        const IPAddress &nm = INADDR_NONE,
+        const IPAddress &gw = INADDR_NONE);
+
+    /*
+     * This method performs interface specific tasks (if any)
+     */
+    virtual void task();
+
+#ifdef LWIP_DHCP
     /* --------------
      * DHCP functions
      * -------------- */
-    bool DhcpIsStarted() { return dhcp_started; }
-    void DhcpSetTimeout(unsigned long t);
-    /* stops DHCP */
-    void DhcpStop();
-    /* tells DHCP is not used on that interface */
-    void DhcpNotUsed();
-    /* starts DHCP and tries to acquire addresses, return true if acquired, false otherwise */
-    bool DhcpStart();
-    /* tells if DHCP has acquired addresses or not */
+    // starts DHCP and tries to acquire addresses, return true if request was made successfully (ususally memory issues)
+    bool dhcpStart();
+    // stops DHCP
+    void dhcpStop();
+    // tells DHCP server that the interface uses a statically provided ip address
+    void dhcpNotUsed();
+    // force DHCP renewal, returns false on error (ususally memory issues)
+    bool dhcpRenew();
+    // force DHCP release, usually called before dhcp stop (ususally memory issues)
+    bool dhcpRelease();
+    // tells if DHCP has acquired addresses or not
     bool isDhcpAcquired();
-    int checkLease();
+#endif
 
     virtual void setLinkUp();
     virtual void setLinkDown();
-    bool isLinkUp() { return (bool)netif_is_link_up(&ni); }
 
-    /* getters / setters */
-    void setId(int _id) { id = _id; }
-    int getId() { return id; }
+    virtual void up();
+    virtual void down();
+
+    bool isLinkUp() { return (bool)netif_is_link_up(&ni); }
 
     struct netif* getNi() { return &ni; }
 
@@ -179,22 +168,8 @@ class CNetIf {
         memcpy(hostname, name, strlen(name) < MAX_HOSTNAME_DIM ? strlen(name) : MAX_HOSTNAME_DIM);
     }
 
-    /* add */
-    virtual void begin(IPAddress _ip,
-        IPAddress _gw,
-        IPAddress _nm)
-        = 0;
-    virtual void task() = 0;
 
     virtual int getMacAddress(uint8_t* mac) = 0;
-
-    /* default dummy implementation because ethernet does not have that */
-    virtual const char* getSSID() { return nullptr; }
-    virtual uint8_t* getBSSID(uint8_t* bssid) { return nullptr; }
-    virtual int32_t getRSSI() { return 0; }
-    virtual uint8_t getEncryptionType() { return 0; }
-
-   friend class CWifi;
 };
 
 /* -------------------------------------------------------------------------- */
@@ -204,62 +179,83 @@ class CEth : public CNetIf {
     /*
      * this function is used to initialize the netif structure of lwip
      */
-    static err_t init(struct netif* ni);
+    static err_t init(struct netif* ni) override;
 
     /*
      * This function is passed to lwip and used to send a buffer to the driver in order to transmit it
      */
-    static err_t output(struct netif* ni, struct pbuf* p);
+    static err_t output(struct netif* ni, struct pbuf* p) override;
+    static const char eth_ifname_prefix = 'e';
+    static uint8_t eth_id;
 public:
     CEth();
     virtual ~CEth();
-    virtual void begin(IPAddress _ip,
-        IPAddress _gw,
-        IPAddress _nm) override;
+    virtual void begin(
+        const IPAddress &ip = INADDR_NONE,
+        const IPAddress &nm = INADDR_NONE,
+        const IPAddress &gw = INADDR_NONE) override;
+
     virtual void task() override;
 
-    virtual int getMacAddress(uint8_t* mac)
-    {
-        UNUSED(mac);
+    virtual int getMacAddress(uint8_t* mac) override {
+        UNUSED(mac); // FIXME not implemented
         return 1;
     }
 
-    virtual void handleEthRx();
+private:
+    /*
+     * This function is passed to the driver class and it is meant to
+     * take a pointer to a buffer, and pass it to lwip to process it
+     */
+    void consume_callback(uint8_t* buffer, uint32_t len);
 };
 
 /* -------------------------------------------------------------------------- */
 class CWifiStation : public CNetIf {
     /* -------------------------------------------------------------------------- */
 protected:
+    static const char wifistation_ifname_prefix = 'w';
+    static uint8_t wifistation_id;
+
     /*
      * this function is used to initialize the netif structure of lwip
      */
-    static err_t init(struct netif* ni);
+    static err_t init(struct netif* ni) override;
 
     /*
      * This function is passed to lwip and used to send a buffer to the driver in order to transmit it
      */
-    static err_t output(struct netif* ni, struct pbuf* p);
+    static err_t output(struct netif* ni, struct pbuf* p) override;
+
+    WifiApCfg_t access_point_cfg;
 public:
     CWifiStation();
     virtual ~CWifiStation();
-    virtual void begin(IPAddress _ip,
-        IPAddress _gw,
-        IPAddress _nm) override;
+    virtual void begin(
+        const IPAddress &ip = INADDR_NONE,
+        const IPAddress &nm = INADDR_NONE,
+        const IPAddress &gw = INADDR_NONE) override;
+
+    int connectToAP(const char* ssid, const char *passphrase=nullptr);
+    int disconnectFromAp();
+    int scanForAp();
+
     virtual void task() override;
 
     virtual int getMacAddress(uint8_t* mac) override;
 
-    virtual const char* getSSID() override;
-    virtual uint8_t* getBSSID(uint8_t* bssid) override;
-    virtual int32_t getRSSI() override;
-    virtual uint8_t getEncryptionType() override;
+    virtual const char* getSSID();
+    virtual uint8_t* getBSSID(uint8_t* bssid);
+    virtual int32_t getRSSI();
+    virtual uint8_t getEncryptionType();
 };
 
 /* -------------------------------------------------------------------------- */
 class CWifiSoftAp : public CNetIf {
     /* -------------------------------------------------------------------------- */
 protected:
+    static const char wifistation_ifname_prefix = 'w';
+    static uint8_t wifistation_id;
     /*
      * this function is used to initialize the netif structure of lwip
      */
@@ -269,148 +265,77 @@ class CWifiSoftAp : public CNetIf {
      * This function is passed to lwip and used to send a buffer to the driver in order to transmit it
      */
     static err_t output(struct netif* ni, struct pbuf* p);
+
+    SoftApCfg_t soft_ap_cfg;
 public:
     CWifiSoftAp();
     virtual ~CWifiSoftAp();
-    virtual void begin(IPAddress _ip,
-        IPAddress _gw,
-        IPAddress _nm) override;
+    virtual void begin(
+        const IPAddress &ip = INADDR_NONE,
+        const IPAddress &nm = INADDR_NONE,
+        const IPAddress &gw = INADDR_NONE) override;
     virtual void task() override;
 
     virtual int getMacAddress(uint8_t* mac) override;
 
-    virtual const char* getSSID() override;
-    virtual uint8_t* getBSSID(uint8_t* bssid) override;
-    virtual int32_t getRSSI() override;
-    virtual uint8_t getEncryptionType() override;
+    virtual const char* getSSID();
+    virtual uint8_t* getBSSID(uint8_t* bssid);
+    virtual int32_t getRSSI();
+    virtual uint8_t getEncryptionType();
 };
 
-/* -------------------------------------------------------------------------- */
 class CLwipIf {
-    /* -------------------------------------------------------------------------- */
-private:
-    bool eth_initialized;
-
-    int dns_num;
-    bool willing_to_start_sync_req;
-    bool async_requests_ongoing;
-
-    friend CWifiStation;
-    friend CWifiSoftAp;
-    static CNetIf* net_ifs[NETWORK_INTERFACES_MAX_NUM];
-    static WifiStatus_t wifi_status;
-
-    /* initialize lwIP and timer */
-    CLwipIf();
-
-/* timer */
-#ifdef LWIP_USE_TIMER
-    static FspTimer timer;
-    static void timer_cb(timer_callback_args_t* arg);
-#endif
-
-    std::vector<AccessPoint_t> access_points;
-    WifiApCfg_t access_point_cfg;
-
-    SoftApCfg_t soft_ap_cfg;
-
-    static bool wifi_hw_initialized;
-    static bool connected_to_access_point;
-    static int initEventCb(CCtrlMsgWrapper* resp);
-    static bool initWifiHw(bool asStation);
-
-    static bool pending_eth_rx;
-
-    static int disconnectEventcb(CCtrlMsgWrapper* resp);
-
-    static void dns_callback(const char* name, const ip_addr_t* ipaddr, void* callback_arg);
-    int8_t get_ip_address_from_hostname(const char* hostname, uint32_t* ipaddr);
-    int inet2aton(const char* aIPAddrString, IPAddress& aResult);
-
 public:
-    static CLwipIf& getInstance();
     CLwipIf(CLwipIf const&) = delete;
     void operator=(CLwipIf const&) = delete;
-    ~CLwipIf();
 
-    bool isEthInitialized() { return eth_initialized; }
 
-    void startSyncRequest()
-    {
-        if (async_requests_ongoing) {
-            synchronized
-            {
-                willing_to_start_sync_req = true;
-            }
-            while (willing_to_start_sync_req) {
-                delay(1);
-            }
-        }
+    static CLwipIf& getInstance() {
+        //FIXME this doesn't to seem good
+        static CLwipIf instance; // this is private in case we need to synch the access to the singleton
+        return instance;
     }
 
-    void restartAsyncRequest()
-    {
-        async_requests_ongoing = true;
-        delay(10);
-        timer.enable_overflow_irq();
-    }
+    // run polling tasks from all the LWIP Network Interfaces
+    // this needs to be called in the loop() if we are not running it
+    // with a timer
+    void task();
 
-    /* --------------
-     * DNS functions
-     * -------------- */
+    // Function that provides a Client of the correct kind given the protocol provided in url
+    // Client* connect(std::string url);
+    // void request(std::string url, std::function<void(uint8_t*, size_t)>);
 
-    int getHostByName(const char* aHostname, IPAddress& aResult);
-    void beginDns(IPAddress aDNSServer);
-    void addDns(IPAddress aDNSServer);
-    IPAddress getDns(int _num = 0);
+    // function for setting an iface as default
+    void setDefaultIface(CNetIf* iface);
 
-    /* when you 'get' a network interface, you get a pointer to one of the pointers
-       held by net_ifs array
-       if the array element then an attempt to set up the network interface is made
-       this function actually calls the private function setUp... and that ones
-       call the private _get */
+    // functions that handle DNS resolution
+    // DNS servers are also set by dhcp
+#if LWIP_DNS
+    // add a dns server, priority set to 0 means it is the first being queryed, -1 means the last
+    uint8_t addDnsServer(const IPAddress& aDNSServer, int8_t priority=-1);
+    void clearDnsServers();
 
-    CNetIf* get(NetIfType_t type,
-        IPAddress _ip = INADDR_NONE,
-        IPAddress _gw = INADDR_NONE,
-        IPAddress _nm = INADDR_NONE);
-
-    static void ethLinkUp();
-    static void ethLinkDown();
-
-    /* this function set the mac address of the corresponding interface to mac
-       and set this value for lwip */
-    bool setMacAddress(NetIfType_t type, uint8_t* mac = nullptr);
-    int getMacAddress(NetIfType_t type, uint8_t* mac);
+    // DNS resolution works with a callback if the resolution doesn't return immediately
+    int getHostByName(const char* aHostname, IPAddress& aResult, bool execute_task=false); // blocking call
+    int getHostByName(const char* aHostname, std::function<void(const IPAddress&)> cbk); // callback version
+#endif
+private:
+    CLwipIf();
 
-    int scanForAp();
-    int getApNum();
-    const char* getSSID(uint8_t i);
-    int32_t getRSSI(uint8_t i);
-    uint8_t getEncrType(uint8_t i);
-    uint8_t* getBSSID(uint8_t i, uint8_t* bssid);
-    uint8_t getChannel(uint8_t i);
-    int connectToAp(const char* ssid, const char* pwd);
-    int disconnectFromAp();
-    const char* getSSID();
-    uint8_t* getBSSID(uint8_t* bssid);
-    uint32_t getRSSI();
-    uint8_t getEncrType();
+    // TODO define a Timer for calling tasks
 
-    WifiStatus_t getWifiStatus() { return wifi_status; }
+    std::vector<CNetIf*> ifaces;
 
-    int startSoftAp(const char* ssid, const char* passphrase, uint8_t channel);
-    int setLowPowerMode();
-    int resetLowPowerMode();
+    virtual void add_iface(CNetIf* iface);
+    // virtual void del_iface(CNetIf* iface);
 
-    const char* getSSID(NetIfType_t type);
-    uint8_t* getBSSID(NetIfType_t type, uint8_t* bssid);
-    int32_t getRSSI(NetIfType_t type);
-    uint8_t getEncryptionType(NetIfType_t type);
+    // lwip stores the netif in a linked list called: netif_list
 
-    int setWifiMode(WifiMode_t mode);
+    friend class CNetIf;
 
-    void lwip_task();
+#ifdef NETWORKSTACK_USE_TIMER
+    FspTimer timer;
+#endif
 };
 
 #endif

From 2637cdfd5dd5cf41d8a2301a331ff601e06db178 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 28 Dec 2023 15:48:52 +0100
Subject: [PATCH 06/79] restructuring CLwipIf singleton class

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 455 +++++++++------------------
 1 file changed, 154 insertions(+), 301 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 53f01dd78..8d7555dc0 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -1,32 +1,57 @@
 #include "CNetIf.h"
 #include <functional>
 
-IPAddress CNetIf::default_ip("192.168.0.10");
-IPAddress CNetIf::default_nm("255.255.255.0");
-IPAddress CNetIf::default_gw("192.168.0.1");
-IPAddress CNetIf::default_dhcp_server_ip("192.168.4.1");
+// TODO make better documentation on how this works
+// TODO hostname should be defined at network stack level and shared among ifaces
+// TODO buffer management (allocation/deallocation/trim/etc.) should be properly handled by a wrapper class and be transparent wrt the user
+// TODO the device could be moving and as a consequence it may be nice to rescan APs to get one with the best rssi
+// TODO implement setLowPowerMode and resetLowPowerMode in WIFI driver
+// TODO implement stop softAP and include it in the destructor of the class
+// TODO split netif definition in different files
+// TODO implement WIFINetworkDriver that is then being used by both Wifi station and softAP. This will allow to use both at the same time
+
+err_t _netif_init(struct netif* ni);
+err_t _netif_output(struct netif* ni, struct pbuf* p);
 
-CNetIf* CLwipIf::net_ifs[] = { nullptr };
-bool CLwipIf::wifi_hw_initialized = false;
-bool CLwipIf::connected_to_access_point = false;
-WifiStatus_t CLwipIf::wifi_status = WL_IDLE_STATUS;
-bool CLwipIf::pending_eth_rx = false;
+#if LWIP_DNS
+static void _getHostByNameCBK(const char *name, const ip_addr_t *ipaddr, void *callback_arg);
+#endif // LWIP_DNS
 
-FspTimer CLwipIf::timer;
+// Custom Pbuf definition used to handle RX zero copy
+// TODO Move this in a separate file (understand if it is required)
+typedef struct zerocopy_pbuf {
+    struct pbuf_custom p;
+    uint8_t* buffer;
+    uint32_t size;
+    void(*buffer_free)(void*);
+} zerocopy_pbuf_t;
 
-ip_addr_t* u8_to_ip_addr(uint8_t* ipu8, ip_addr_t* ipaddr)
-{
-    IP_ADDR4(ipaddr, ipu8[0], ipu8[1], ipu8[2], ipu8[3]);
-    return ipaddr;
+static void zerocopy_pbuf_mem_free(struct pbuf *p) {
+    // SYS_ARCH_DECL_PROTECT(zerocopy_pbuf_free);
+    zerocopy_pbuf_t* zcpbuf = (zerocopy_pbuf_t*) p;
+
+    // arduino::lock();
+    // SYS_ARCH_PROTECT(zerocopy_pbuf_free);
+
+    // FIXME pbufs may be allocated in a different memory pool, deallocate them accordingly
+    zcpbuf->buffer_free(zcpbuf->buffer);
+    zcpbuf->buffer = nullptr;
+    mem_free(zcpbuf); // TODO understand if pbuf_free deletes the pbuf
+    // SYS_ARCH_UNPROTECT(zerocopy_pbuf_free);
+
+    // arduino::unlock();
 }
 
-uint32_t ip_addr_to_u32(ip_addr_t* ipaddr)
-{
-    return ip4_addr_get_u32(ipaddr);
+static inline zerocopy_pbuf_t* get_zerocopy_pbuf(uint8_t *buffer, uint32_t size, void(*buffer_free)(void*) = mem_free) {
+    zerocopy_pbuf_t* p = (zerocopy_pbuf_t*)mem_malloc(sizeof(zerocopy_pbuf_t));
+    p->buffer = buffer;
+    p->size = size;
+    p->p.custom_free_function = zerocopy_pbuf_mem_free;
+    p->buffer_free = buffer_free;
+    return p;
 }
 
-static uint8_t Encr2wl_enc(int enc)
-{
+static uint8_t Encr2wl_enc(int enc) {
     if (enc == WIFI_AUTH_OPEN) {
         return ENC_TYPE_NONE;
     } else if (enc == WIFI_AUTH_WEP) {
@@ -48,20 +73,13 @@ static uint8_t Encr2wl_enc(int enc)
     }
 }
 
-/* -------------------------------------------------------------------------- */
-CLwipIf::CLwipIf()
-    : eth_initialized(false)
-    , dns_num(-1)
-    , willing_to_start_sync_req(false)
-    , async_requests_ongoing(true)
-{
-    /* -------------------------------------------------------------------------- */
+CLwipIf::CLwipIf() {
+
     /* Initialize lwIP stack, singletone implementation guarantees that lwip is
        initialized just once  */
     lwip_init();
 
-/* START THE TIMER FOR LWIP tasks - #CORE_DEPENDENT_STUFF */
-#ifdef LWIP_USE_TIMER
+#ifdef NETWORKSTACK_USE_TIMER
     uint8_t type = 8;
     int8_t ch = FspTimer::get_available_timer(type);
 
@@ -84,327 +102,162 @@ CLwipIf::CLwipIf()
      * Since this is a constrained environment we could accept performance loss and
      * delegate lwip to handle lost packets.
      */
-    timer.begin(TIMER_MODE_PERIODIC, type, ch, 100.0, 50.0, timer_cb);
-    timer.setup_overflow_irq();
-    timer.open();
-    timer.start();
+    timer.begin(TIMER_MODE_PERIODIC, type, ch, 100.0, 0, timer_cb, this); // TODO make the user decide how to handle these parameters
 #endif
 }
 
-/* -------------------------------------------------------------------------- */
-void CLwipIf::lwip_task()
-{
-    /* -------------------------------------------------------------------------- */
-    if (CLwipIf::wifi_hw_initialized)
-        CEspControl::getInstance().communicateWithEsp();
-
-    if (net_ifs[NI_ETHERNET] != nullptr) {
-        net_ifs[NI_ETHERNET]->task();
+void CLwipIf::task() {
+    for(CNetIf* iface: this->ifaces) { // FIXME is this affecting performances?
+        iface->task();
     }
 
-    if (net_ifs[NI_WIFI_STATION] != nullptr) {
-        net_ifs[NI_WIFI_STATION]->task();
-    }
-
-    if (net_ifs[NI_WIFI_SOFTAP] != nullptr) {
-        net_ifs[NI_WIFI_SOFTAP]->task();
-    }
-
-    /* Handle LwIP timeouts */
+    arduino::lock();
     sys_check_timeouts();
-
-    if (willing_to_start_sync_req) {
-        timer.disable_overflow_irq();
-        willing_to_start_sync_req = false;
-        async_requests_ongoing = false;
-    }
+    arduino::unlock();
 }
 
-/* -------------------------------------------------------------------------- */
-/* GET INSTANCE SINGLETONE FUNCTION */
-/* -------------------------------------------------------------------------- */
-CLwipIf& CLwipIf::getInstance()
-{
-    /* -------------------------------------------------------------------------- */
-    static CLwipIf instance;
-    return instance;
-}
+void CLwipIf::setDefaultIface(CNetif* iface) {
+    // TODO check if the iface is in the vector
 
-/* -------------------------------------------------------------------------- */
-CLwipIf::~CLwipIf()
-{
-    /* -------------------------------------------------------------------------- */
-    for (int i = 0; i < NETWORK_INTERFACES_MAX_NUM; i++) {
-        if (net_ifs[i] != nullptr) {
-            delete net_ifs[i];
-            net_ifs[i] = nullptr;
-        }
-    }
+    netif_set_default(&iface->ni);
 }
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::disconnectEventcb(CCtrlMsgWrapper *resp) {
-    (void)resp;
-    if(CLwipIf::connected_to_access_point) {
-        wifi_status = WL_DISCONNECTED;
-        if(net_ifs[NI_WIFI_STATION] != nullptr) {
-            net_ifs[NI_WIFI_STATION]->setLinkDown();
-        }
+void CLwipIf::add_iface(CNetif* iface) {
+    // if it is the first interface set it as the default route
+    if(this->ifaces.empty()) {
+        netif_set_default(&iface->ni); // TODO let the user decide which is the default one
+
+#ifdef NETWORKSTACK_USE_TIMER
+        timer.setup_overflow_irq();
+        timer.open();
+        timer.start();
+#endif
     }
-    return ESP_CONTROL_OK;
-}
 
+    // add the interface if not already present in the vector
+    this->ifaces.push_back(iface);
+}
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::initEventCb(CCtrlMsgWrapper *resp) {
-    (void)resp;
-    CLwipIf::wifi_hw_initialized = true;
-    return ESP_CONTROL_OK;
+CLwipIf::~CLwipIf() {
+    // TODO free iface array
 }
 
 
-/* -------------------------------------------------------------------------- */
 int CLwipIf::setWifiMode(WifiMode_t mode) {
-/* -------------------------------------------------------------------------- */   
-      CLwipIf::getInstance().startSyncRequest();
-      int rv = CEspControl::getInstance().setWifiMode(mode);
-      CLwipIf::getInstance().restartAsyncRequest();
-      return rv;
+    // TODO adapt this
+    // CLwipIf::getInstance().startSyncRequest();
+    // int rv = CEspControl::getInstance().setWifiMode(mode);
+    // CLwipIf::getInstance().restartAsyncRequest();
+    // return rv;
 }
 
-/* -------------------------------------------------------------------------- */
-bool CLwipIf::initWifiHw(bool asStation)
-{
-    /* -------------------------------------------------------------------------- */
-    bool rv = true;
-
-    if (!CLwipIf::wifi_hw_initialized) {
-
-        CEspControl::getInstance().listenForStationDisconnectEvent(CLwipIf::disconnectEventcb);
-        CEspControl::getInstance().listenForInitEvent(CLwipIf::initEventCb);
-        if (CEspControl::getInstance().initSpiDriver() == 0) {
-            wifi_status = WL_NO_SSID_AVAIL;
-        }
+/* ***************************************************************************
+ *                               DNS related functions
+ * ****************************************************************************/
 
-        if (wifi_status == WL_NO_SSID_AVAIL) {
-            int time_num = 0;
-            while (time_num < WIFI_INIT_TIMEOUT_MS && !CLwipIf::wifi_hw_initialized) {
-                CEspControl::getInstance().communicateWithEsp();
-                R_BSP_SoftwareDelay(100, BSP_DELAY_UNITS_MILLISECONDS);
-                time_num++;
-            }
+#if LWIP_DNS
 
-            if (asStation) {
-                int res = CLwipIf::getInstance().setWifiMode(WIFI_MODE_STA);
+struct dns_callback {
+    std::function<void(const IPAddress&)> cbk;
+};
 
-                if (res == ESP_CONTROL_OK) {
-                    CLwipIf::getInstance().scanForAp();
-                }
-            } else {
-                CEspControl::getInstance().setWifiMode(WIFI_MODE_AP);
-            }
-        }
-    }
+static void _getHostByNameCBK(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
+    dns_callback* cbk = (dns_callback*)callback_arg;
 
-    if (wifi_status != WL_SCAN_COMPLETED) {
-        rv = false;
-    }
+    cbk->cbk(toArduinoIP(ipaddr));
 
-    return rv;
+    delete cbk;
 }
 
-/* -------------------------------------------------------------------------- */
-/* Sort of factory method, dependig on the requested type it setUp a different
-   Network interface and returns it to the caller */
-/* -------------------------------------------------------------------------- */
-CNetIf* CLwipIf::get(NetIfType_t type,
-    IPAddress _ip,
-    IPAddress _gw,
-    IPAddress _nm)
-{
-    /* -------------------------------------------------------------------------- */
-    static int id = 0;
-    CNetIf* rv = nullptr;
-    bool isStation = true;
-    bool isEth = false;
-
-    if (type >= 0 && type < NETWORK_INTERFACES_MAX_NUM) {
-        if (net_ifs[type] == nullptr) {
-            switch (type) {
-            case NI_WIFI_STATION:
-                net_ifs[type] = new CWifiStation();
-                isStation = true;
-                break;
+// add a dns server, priority set to 0 means it is the first being queryed, -1 means the last
+uint8_t CLwipIf::addDnsServer(const IPAddress& aDNSServer, int8_t priority) {
+    // TODO test this function with all the possible cases of dns server position
+    if(priority == -1) {
+        // lwip has an array for dns servers that can be iterated with dns_getserver(num)
+        // when a dns server is set to any value, it means it is the last
 
-            case NI_WIFI_SOFTAP:
-                net_ifs[type] = new CWifiSoftAp();
-                isStation = false;
-                break;
-
-            case NI_ETHERNET:
-                net_ifs[type] = new CEth();
-                isEth = true;
-                break;
-            default:
-                break;
-            }
-
-            if (net_ifs[type] != nullptr) {
-                if (!isEth) {
-                    CLwipIf::initWifiHw(isStation);
-                    net_ifs[type]->begin(_ip, _gw, _nm);
-                    net_ifs[type]->setId(0);
-                } else {
-                    eth_init();
-                    net_ifs[type]->begin(_ip, _gw, _nm);
-                    eth_initialized = true;
-                }
-            }
-        }
-        rv = net_ifs[type];
+        for(priority=0;
+            priority<DNS_MAX_SERVERS && !ip_addr_isany_val(*dns_getserver(priority));
+            priority++) {}
     }
-    return rv;
-}
-
-/* -------------------------------------------------------------------------- */
-void CEth::handleEthRx()
-{
-    /*
-     * This function is called by the ethernet driver, when a frame is receiverd,
-     * as a callback inside an interrupt context.
-     * It is required to be as fast as possible and not perform busy waits.
-     *
-     * The idea is the following:
-     * - take the rx buffer pointer
-     * - try to allocate a pbuf of the desired size
-     *   - if it is possible copy the the buffer inside the pbuf and give it to lwip netif
-     * - release the buffer
-     *
-     * If the packet is discarded the upper TCP/IP layers should handle the retransmission of the lost packets.
-     * This should not happen really often if the buffers and timers are designed taking into account the
-     * desired performance
-     */
-    __disable_irq();
-
-    volatile uint32_t rx_frame_dim = 0;
-    volatile uint8_t* rx_frame_buf = eth_input(&rx_frame_dim);
-    if (rx_frame_dim > 0 && rx_frame_buf != nullptr) {
-        struct pbuf* p=nullptr;
 
-        p = pbuf_alloc(PBUF_RAW, rx_frame_dim, PBUF_RAM);
-
-        if (p != NULL) {
-            /* Copy ethernet frame into pbuf */
-            pbuf_take((struct pbuf*)p, (uint8_t*)rx_frame_buf, (uint32_t)rx_frame_dim);
-
-            if (ni.input((struct pbuf*)p, &ni) != ERR_OK) {
-                pbuf_free((struct pbuf*)p);
-            }
-        }
-
-        eth_release_rx_buffer();
+    if(priority >= DNS_MAX_SERVERS) {
+        // unable to add another dns server, because priority is more than the dns server available space
+        return -1;
     }
-    __enable_irq();
-}
 
-/* -------------------------------------------------------------------------- */
-err_t CEth::init(struct netif* _ni)
-{
-    /* -------------------------------------------------------------------------- */
-#if LWIP_NETIF_HOSTNAME
-    /* Initialize interface hostname */
-    _ni->hostname = "C33-onEth";
-#endif /* LWIP_NETIF_HOSTNAME */
-
-    _ni->name[0] = ETH_IFNAME0;
-    _ni->name[1] = ETH_IFNAME1;
-    /* We directly use etharp_output() here to save a function call.
-     * You can instead declare your own function an call etharp_output()
-     * from it if you have to do some checks before sending (e.g. if link
-     * is available...) */
-    _ni->output = etharp_output;
-    _ni->linkoutput = CEth::output;
-
-    /* set MAC hardware address */
-    _ni->hwaddr_len = eth_get_mac_address(_ni->hwaddr);
+    ip_addr_t ip = fromArduinoIP(aDNSServer);
 
-    /* maximum transfer unit */
-    _ni->mtu = 1500;
-
-    /* device capabilities */
-    /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
-    _ni->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
+    dns_setserver(priority, &ip);
+}
 
-    return ERR_OK;
+void CLwipIf::clearDnsServers() {
+    for(uint8_t i=0; i<DNS_MAX_SERVERS; i++) {
+        dns_setserver(i, IP_ANY_TYPE);
+    }
 }
 
-/* -------------------------------------------------------------------------- */
-err_t CEth::output(struct netif* _ni, struct pbuf *p) {
-/* -------------------------------------------------------------------------- */
-    /*
-     * This function is called inside the lwip timeout engine. Since we are working inside
-     * an environment without threads it is required to not lock. For this reason we should
-     * avoid busy waiting and instead discard the transmission. Lwip will handle the retransmission
-     * of the packet.
+// DNS resolution works with a callback if the resolution doesn't return immediately
+int CLwipIf::getHostByName(const char* aHostname, IPAddress& aResult, bool execute_task) {
+    /* this has to be a blocking call but we need to understand how to handle wait time
+     * - we can have issues when running concurrently from different contextes,
+     *   meaning that issues may arise if we run task() method of this class from an interrupt
+     *   context and the "userspace".
+     * - this function is expected to be called in the application layer, while the lwip stack is
+     *   being run in an interrupt context, otherwise this call won't work because it will block
+     *   everything
+     * - this function shouldn't be called when lwip is run in the same context as the application
      */
-    (void)_ni;
-
-    err_t errval = ERR_OK;
+    volatile bool completed = false;
 
-    if(eth_output_can_transimit()) {
-        uint16_t tx_buf_dim = p->tot_len;
+    uint8_t res = this->getHostByName(aHostname, [&aResult, &completed](const IPAddress& ip){
+        aResult = ip;
+        completed = true;
+    });
 
-        // TODO analyze the race conditions that may arise from sharing a non synchronized buffer
-        uint8_t *tx_buf = eth_get_tx_buffer(&tx_buf_dim);
-        if (p->tot_len <= tx_buf_dim) {
-
-            uint16_t bytes_actually_copied = pbuf_copy_partial(p, tx_buf, p->tot_len, 0);
-
-            if (bytes_actually_copied > 0 && !eth_output(tx_buf, bytes_actually_copied)) {
-                errval = ERR_IF;
-            }
-        } else {
-            errval = ERR_MEM;
+    while(res == 1 && !completed) { // DNS timeouts seems to be handled by lwip, no need to put one here
+        delay(1);
+        if(execute_task) {
+            this->task();
         }
-    } else {
-        errval = ERR_INPROGRESS;
     }
-    return errval;
+
+    return res == 1 ? 0 : res;
 }
 
-/* -------------------------------------------------------------------------- */
-err_t CWifiStation::output(struct netif* _ni, struct pbuf *p) {
-/* -------------------------------------------------------------------------- */   
-    (void)_ni;
-    err_t errval = ERR_IF;
-    uint8_t *buf = new uint8_t[p->tot_len];
-    if (buf != nullptr) {
-        uint16_t bytes_actually_copied = pbuf_copy_partial(p, buf, p->tot_len, 0);
-        if (bytes_actually_copied > 0) {
-            int ifn = 0;
-            if (CLwipIf::net_ifs[NI_WIFI_STATION] != nullptr) {
-                ifn = CLwipIf::net_ifs[NI_WIFI_STATION]->getId();
-            }
+// TODO instead of returning int return an enum value
+int CLwipIf::getHostByName(const char* aHostname, std::function<void(const IPAddress&)> cbk) {
+    ip_addr_t addr; // TODO understand if this needs to be in the heap
+    uint8_t res = 0;
 
-#ifdef DEBUG_OUTPUT_DISABLED
-            Serial.println("Bytes LWIP wants to send: ");
+    dns_callback* dns_cbk = new dns_callback;
+    dns_cbk->cbk = cbk;
+    err_t err = dns_gethostbyname(aHostname, &addr, _getHostByNameCBK, dns_cbk);
 
-            for (int i = 0; i < bytes_actually_copied; i++) {
-                Serial.print(buf[i], HEX);
-                Serial.print(" ");
-            }
-            Serial.println();
-#endif
+    switch(err) {
+    case ERR_OK:
+        // the address was already present in the local cache
+        cbk(toArduinoIP(&addr));
 
-            if (CEspControl::getInstance().sendBuffer(ESP_STA_IF, ifn, buf, bytes_actually_copied) == ESP_CONTROL_OK) {
-                errval = ERR_OK;
-            }
-        }
-        delete[] buf;
+        delete dns_cbk;
+        break;
+    case ERR_INPROGRESS:
+        // the address is not present in the local cache, return and wait for the address resolution to complete
+        res = 1;
+        break;
+    case ERR_ARG: // there are issues in the arguments passed
+    default:
+        delete dns_cbk;
+        res = -1;
     }
 
-    return errval;
+    return res;
 }
+#endif
+
+/* ##########################################################################
+ *                      BASE NETWORK INTERFACE CLASS
+ * ########################################################################## */
 
 /* -------------------------------------------------------------------------- */
 err_t CWifiStation::init(struct netif* _ni)

From 216e3aab05b7b40e62952cab93316b358a4160f9 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 28 Dec 2023 16:46:57 +0100
Subject: [PATCH 07/79] rewriting CNetif base class

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 267 +++++++++------------------
 1 file changed, 88 insertions(+), 179 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 8d7555dc0..dc1ddd0e0 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -259,221 +259,130 @@ int CLwipIf::getHostByName(const char* aHostname, std::function<void(const IPAdd
  *                      BASE NETWORK INTERFACE CLASS
  * ########################################################################## */
 
-/* -------------------------------------------------------------------------- */
-err_t CWifiStation::init(struct netif* _ni)
+CNetIf::CNetIf()
+:
+#ifdef LWIP_DHCP
+    dhcp_acquired(false)
+#endif
 {
-    /* -------------------------------------------------------------------------- */
-#if LWIP_NETIF_HOSTNAME
-    /* Initialize interface hostname */
-    _ni->hostname = "C33-WifiSta";
-#endif /* LWIP_NETIF_HOSTNAME */
-
-    _ni->name[0] = WST_IFNAME0;
-    _ni->name[1] = WST_IFNAME1;
-    /* We directly use etharp_output() here to save a function call.
-     * You can instead declare your own function an call etharp_output()
-     * from it if you have to do some checks before sending (e.g. if link
-     * is available...) */
-    _ni->output = etharp_output;
-    _ni->linkoutput = CWifiStation::output;
+    // NETIF_STATS_INIT(this->stats); // TODO create a proper stats interface
 
-    /* maximum transfer unit */
-    _ni->mtu = 1500;
-
-    /* device capabilities */
-    /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
-    _ni->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
-
-    /* set MAC hardware address */
-    _ni->hwaddr_len = CLwipIf::getInstance().getMacAddress(NI_WIFI_STATION, _ni->hwaddr);
-
-    return ERR_OK;
+    if(driver != nullptr) {
+        // driver->stats = this->stats; // TODO
+        // TODO check that this calls are effective
+        driver->setLinkDownCallback(std::bind(&CNetIf::linkDownCallback, this));
+        driver->setLinkUpCallback(std::bind(&CNetIf::linkUpCallback, this));
+    }
 }
 
-/* -------------------------------------------------------------------------- */
-err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p)
-{
-    /* -------------------------------------------------------------------------- */
-    (void)_ni;
-    err_t errval = ERR_IF;
-
-    uint8_t* buf = new uint8_t[p->tot_len];
+void CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
+    ip_addr_t _ip = fromArduinoIP(ip);
+    ip_addr_t _nm = fromArduinoIP(nm);
+    ip_addr_t _gw = fromArduinoIP(gw);
+
+    // netif add copies the ip addresses into the netif, no need to store them also in the object
+    struct netif *_ni = netif_add(
+        &this->ni,
+        &_ip, &_nm, &_gw, // ip addresses are being copied and not taken as reference, use a local defined variable
+        this,
+        _netif_init,
+        ethernet_input
+    );
+    if(_ni == nullptr) {
+        // FIXME error if netif_add, return error
+        return;
+    }
 
-    if (buf != nullptr) {
-        uint16_t bytes_actually_copied = pbuf_copy_partial(p, buf, p->tot_len, 0);
-        if (bytes_actually_copied > 0) {
-            int ifn = 0;
-            if (CLwipIf::net_ifs[NI_WIFI_SOFTAP] != nullptr) {
-                ifn = CLwipIf::net_ifs[NI_WIFI_SOFTAP]->getId();
-            }
+    //TODO add link up and down callback and set the link
+    netif_set_up(&this->ni);
 
-            if (CEspControl::getInstance().sendBuffer(ESP_AP_IF, ifn, buf, bytes_actually_copied) == ESP_CONTROL_OK) {
-                errval = ERR_OK;
-            }
-        }
-        delete[] buf;
+#ifdef LWIP_DHCP
+    // dhcp is started when begin gets ip == nullptr
+    if(ip != INADDR_NONE) {
+        this->dhcpNotUsed();
+    } else {
+        this->dhcpStart();
     }
+#endif
 
-    return errval;
+    // add the interface to the network stack
+    CLwipIf::getInstance().add_iface(this); // TODO remove interface when it is needed (??)
 }
 
-/* -------------------------------------------------------------------------- */
-err_t CWifiSoftAp::init(struct netif* _ni)
-{
-    /* -------------------------------------------------------------------------- */
-#if LWIP_NETIF_HOSTNAME
-    /* Initialize interface hostname */
-    _ni->hostname = "C33-WifiSta";
-#endif /* LWIP_NETIF_HOSTNAME */
-
-    _ni->name[0] = WSA_IFNAME0;
-    _ni->name[1] = WSA_IFNAME1;
-    /* We directly use etharp_output() here to save a function call.
-     * You can instead declare your own function an call etharp_output()
-     * from it if you have to do some checks before sending (e.g. if link
-     * is available...) */
-    _ni->output = etharp_output;
-    _ni->linkoutput = CWifiSoftAp::output;
+void CNetif::task() {
+#ifdef LWIP_DHCP
+    // TODO we can add a lazy evaluated timer for this condition if dhcp_supplied_address takes too long
+    if(!this->dhcp_acquired && dhcp_supplied_address(&this->ni)) {
+        dhcp_acquired = true;
+    }
 
-    /* maximum transfer unit */
-    _ni->mtu = 1500;
+#endif
+    if(driver != nullptr) {
+        driver->poll();
+    }
+}
 
-    /* device capabilities */
-    /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
-    _ni->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
 
-    /* set MAC hardware address */
-    _ni->hwaddr_len = CLwipIf::getInstance().getMacAddress(NI_WIFI_SOFTAP, _ni->hwaddr);
+err_t _netif_init(struct netif* ni) {
+    CNetif *iface = (CNetif*)ni->state;
 
-    return ERR_OK;
+    return iface->init(ni); // This function call can be a jmp instruction
 }
 
-/* -------------------------------------------------------------------------- */
-bool CLwipIf::setMacAddress(NetIfType_t type, uint8_t* mac)
-{
-    /* -------------------------------------------------------------------------- */
+err_t _netif_output(struct netif* ni, struct pbuf* p) {
+    CNetif *iface = (CNetif*)ni->state;
 
-    CLwipIf::getInstance().startSyncRequest();
-    WifiMac_t MAC;
-    CNetUtilities::macArray2macStr(MAC.mac, mac);
+    return iface->output(ni, p); // This function call can be a jmp instruction
+}
 
-    if (type == NI_WIFI_STATION) {
-        MAC.mode = WIFI_MODE_STA;
-        if (CEspControl::getInstance().setWifiMacAddress(MAC) != ESP_CONTROL_OK) {
-            return false;
-        }
+void CNetIf::up() {
+    netif_set_up(&this->ni);
+}
 
-    } else if (type == NI_WIFI_SOFTAP) {
-        MAC.mode = WIFI_MODE_AP;
-        if (CEspControl::getInstance().setWifiMacAddress(MAC) != ESP_CONTROL_OK) {
-            return false;
-        }
-    } else {
-        eth_set_mac_address(mac);
-    }
+void CNetIf::down() {
+    netif_set_down(&this->ni);
+}
 
-    CLwipIf::getInstance().restartAsyncRequest();
-    return true;
+void CNetIf::linkUpCallback() {
+    netif_set_link_up(&this->ni); // TODO check that this sets the interface up also
 }
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::getMacAddress(NetIfType_t type, uint8_t* mac)
-{
-    /* -------------------------------------------------------------------------- */
-    int rv = 0;
-    WifiMac_t MAC;
+void CNetIf::linkDownCallback() {
+    netif_set_link_down(&this->ni); // TODO check that this sets the interface down also
+}
 
-    CLwipIf::getInstance().startSyncRequest();
+/* ##########################################################################
+ *                               DHCP related functions
+ * ########################################################################## */
 
-    if (type == NI_WIFI_STATION) {
-        MAC.mode = WIFI_MODE_STA;
-        if (CEspControl::getInstance().getWifiMacAddress(MAC) == ESP_CONTROL_OK) {
-            CNetUtilities::macStr2macArray(mac, MAC.mac);
-            rv = MAC_ADDRESS_DIM;
-        }
-    } else if (type == NI_WIFI_SOFTAP) {
-        MAC.mode = WIFI_MODE_AP;
-        if (CEspControl::getInstance().getWifiMacAddress(MAC) == ESP_CONTROL_OK) {
-            CNetUtilities::macStr2macArray(mac, MAC.mac);
-            rv = MAC_ADDRESS_DIM;
-        }
-    } else {
-        eth_get_mac_address(mac);
-        rv = MAC_ADDRESS_DIM;
-    }
 
-    CLwipIf::getInstance().restartAsyncRequest();
-    return rv;
-}
+#ifdef LWIP_DHCP
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::scanForAp()
-{
-    /* -------------------------------------------------------------------------- */
-    access_points.clear();
-    CLwipIf::getInstance().startSyncRequest();
-    int res = CEspControl::getInstance().getAccessPointScanList(access_points);
-    CLwipIf::getInstance().restartAsyncRequest();
-    if (res == ESP_CONTROL_OK) {
-        wifi_status = WL_SCAN_COMPLETED;
-    } else {
-        wifi_status = WL_NO_SSID_AVAIL;
-    }
-    return res;
+void CNetIf::dhcpNotUsed() {
+    dhcp_inform(&this->ni);
 }
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::getApNum() { return access_points.size(); }
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-const char* CLwipIf::getSSID(uint8_t i)
-{
-    /* -------------------------------------------------------------------------- */
-    if (access_points.size() > 0 && i < access_points.size()) {
-        return (const char*)access_points[i].ssid;
+bool CNetIf::isDhcpAcquired() {
+    if(dhcp_acquired) {
+        Serial.println(ip_2_ip4(ni.ip_addr).addr, HEX);
     }
-    return nullptr;
+    return dhcp_acquired;
 }
 
-/* -------------------------------------------------------------------------- */
-int32_t CLwipIf::getRSSI(uint8_t i)
-{
-    /* -------------------------------------------------------------------------- */
-    if (access_points.size() > 0 && i < access_points.size()) {
-        return (int32_t)access_points[i].rssi;
-    }
-    return 0;
+bool CNetIf::dhcpStart() {
+    return dhcp_start(&this->ni) == ERR_OK;
 }
 
-/* -------------------------------------------------------------------------- */
-uint8_t CLwipIf::getEncrType(uint8_t i)
-{
-    /* -------------------------------------------------------------------------- */
-    if (access_points.size() > 0 && i < access_points.size()) {
-        return Encr2wl_enc(access_points[i].encryption_mode);
-    }
-    return 0;
+void CNetIf::dhcpStop() {
+    this->dhcpRelease();
+    dhcp_stop(&this->ni);
 }
-
-/* -------------------------------------------------------------------------- */
-uint8_t* CLwipIf::getBSSID(uint8_t i, uint8_t* bssid)
-{
-    /* -------------------------------------------------------------------------- */
-    if (access_points.size() > 0 && i < access_points.size()) {
-        CNetUtilities::macStr2macArray(bssid, (const char*)access_points[i].bssid);
-        return bssid;
-    }
-    return nullptr;
+bool CNetIf::dhcpRelease() {
+    return dhcp_release(&this->ni) == ERR_OK;
 }
 
-/* -------------------------------------------------------------------------- */
-uint8_t CLwipIf::getChannel(uint8_t i)
-{
-    /* -------------------------------------------------------------------------- */
-    if (access_points.size() > 0 && i < access_points.size()) {
-        return (uint8_t)access_points[i].channel;
-    }
-    return 0;
+bool CNetIf::dhcpRenew() {
+    return dhcp_renew(&this->ni) == ERR_OK;
 }
 /* -------------------------------------------------------------------------- */
 int CLwipIf::connectToAp(const char* ssid, const char* pwd)

From 28f91995ac6003ecf66e334bea138b55e89138b2 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 28 Dec 2023 16:48:22 +0100
Subject: [PATCH 08/79] rewriting CEth CwifiStation and CSoftAP classes

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 1243 ++++++++++----------------
 1 file changed, 469 insertions(+), 774 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index dc1ddd0e0..86f7413e1 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -384,927 +384,622 @@ bool CNetIf::dhcpRelease() {
 bool CNetIf::dhcpRenew() {
     return dhcp_renew(&this->ni) == ERR_OK;
 }
-/* -------------------------------------------------------------------------- */
-int CLwipIf::connectToAp(const char* ssid, const char* pwd)
-{
-    /* -------------------------------------------------------------------------- */
-    WifiApCfg_t ap;
-    int rv = ESP_CONTROL_CTRL_ERROR;
-    bool found = false;
-    uint8_t index = 0;
-    for (uint8_t i = 0; i < access_points.size() && !found; i++) {
-        if (strcmp(ssid, (const char*)access_points[i].ssid) == 0) {
-            found = true;
-            index = i;
-        }
-    }
-
-    if (found) {
-        memset(ap.ssid, 0x00, SSID_LENGTH);
-        memcpy(ap.ssid, access_points[index].ssid, SSID_LENGTH);
-        memset(ap.pwd, 0x00, PASSWORD_LENGTH);
-        if (pwd != nullptr) {
-            memcpy(ap.pwd, pwd, (strlen(pwd) < PASSWORD_LENGTH) ? strlen(pwd) : PASSWORD_LENGTH);
-        }
-        memset(ap.bssid, 0x00, BSSID_LENGTH);
-        memcpy(ap.bssid, access_points[index].bssid, BSSID_LENGTH);
-
-        CLwipIf::getInstance().startSyncRequest();
-        if (CEspControl::getInstance().connectAccessPoint(ap) == ESP_CONTROL_OK) {
-            CLwipIf::connected_to_access_point = true;
-            wifi_status = WL_CONNECTED;
-            CEspControl::getInstance().getAccessPointConfig(access_point_cfg);
 
-            rv = ESP_CONTROL_OK;
-            /* when we get the connection to access point we are sure we are STATION
-               and we are connected */
-            if (CLwipIf::net_ifs[NI_WIFI_STATION] != nullptr) {
-                CLwipIf::net_ifs[NI_WIFI_STATION]->setLinkUp();
-            }
-
-      }
-      else {
-
-         wifi_status = WL_CONNECT_FAILED;
-         CLwipIf::connected_to_access_point = false;
-      }
-
-      CLwipIf::getInstance().restartAsyncRequest();
-   }
-   else {
-      /* in case SSID was available scan again for access point 
-         (perhaps a wifi hostpoint has been added) */
-      CLwipIf::getInstance().scanForAp();
-      //Serial.println("SSID not found in the list of available AP");
-   }
-   return rv;
-}
+#endif
 
-/* -------------------------------------------------------------------------- */
-const char* CLwipIf::getSSID()
-{
-    /* -------------------------------------------------------------------------- */
-    return (const char*)access_point_cfg.ssid;
-}
+/* ##########################################################################
+ *                      ETHERNET NETWORK INTERFACE CLASS
+ * ########################################################################## */
+uint8_t CEth::eth_id = 0;
 
-/* -------------------------------------------------------------------------- */
-uint8_t* CLwipIf::getBSSID(uint8_t* bssid)
-{
-    /* -------------------------------------------------------------------------- */
-    CNetUtilities::macStr2macArray(bssid, (const char*)access_point_cfg.bssid);
-    return bssid;
+CEth::CEth() {
+    CNetif::driver = &C33EthernetDriver; // FIXME driver is the pointer to C33 ethernet driver implementation
+    C33EthernetDriver.stats = &this->stats;
 }
 
-/* -------------------------------------------------------------------------- */
-uint32_t CLwipIf::getRSSI()
-{
-    /* -------------------------------------------------------------------------- */
-    return (uint32_t)access_point_cfg.rssi;
-}
+void CEth::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
+    // The driver needs a callback to consume the incoming buffer
+    this->driver->setConsumeCallback(
+        std::bind(&CEth::consume_callback,
+            this, std::placeholders:: _1, std::placeholders::_2));
 
-/* -------------------------------------------------------------------------- */
-uint8_t CLwipIf::getEncrType()
-{
-    /* -------------------------------------------------------------------------- */
-    return Encr2wl_enc(access_point_cfg.encryption_mode);
+    // Call the begin function on the Parent class to init the interface
+    CNetif::begin(ip, nm, gw);
+    netif_set_link_up(&this->ni); // TODO test that moving this here still makes ethernet work
 }
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::disconnectFromAp()
-{
-    /* -------------------------------------------------------------------------- */
-    wifi_status = WL_DISCONNECTED;
-    CLwipIf::getInstance().startSyncRequest();
-    int rv = CEspControl::getInstance().disconnectAccessPoint();
-    CLwipIf::getInstance().restartAsyncRequest();
-    wifi_status = WL_DISCONNECTED;
-    if (net_ifs[NI_WIFI_STATION] != nullptr) {
-        net_ifs[NI_WIFI_STATION]->setLinkDown();
-    }
-    return rv;
-}
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::startSoftAp(const char* ssid, const char* passphrase, uint8_t channel)
-{
-    /* -------------------------------------------------------------------------- */
-    CLwipIf::getInstance().startSyncRequest();
-    SoftApCfg_t cfg;
-    memset(cfg.ssid, 0x00, SSID_LENGTH);
-    memcpy(cfg.ssid, ssid, (strlen(ssid) < SSID_LENGTH) ? strlen(ssid) : SSID_LENGTH);
-    memset(cfg.pwd, 0x00, PASSWORD_LENGTH);
-    if (passphrase == nullptr) {
-        memcpy(cfg.pwd, "arduinocc", strlen("arduinocc"));
-    } else {
-        memcpy(cfg.pwd, passphrase, strlen(passphrase) < PASSWORD_LENGTH ? strlen(passphrase) : PASSWORD_LENGTH);
-    }
-    channel = (channel == 0) ? 1 : channel;
-    cfg.channel = (channel > MAX_CHNL_NO) ? MAX_CHNL_NO : channel;
-    cfg.max_connections = MAX_SOFAT_CONNECTION_DEF;
-    cfg.encryption_mode = WIFI_AUTH_WPA_WPA2_PSK;
-    cfg.bandwidth = WIFI_BW_HT40;
-    cfg.ssid_hidden = false;
+err_t CEth::init(struct netif* ni) {
+    // Setting up netif
+#if LWIP_NETIF_HOSTNAME
+    // TODO pass the hostname in the constructor os with a setter
+    ni->hostname                       = "C33_eth";
+#endif
+    ni->name[0]                        = CEth::eth_ifname_prefix;
+    ni->name[1]                        = '0' + CEth::eth_id++;
+    ni->mtu                            = 1500; // FIXME get this from the network
+    ni->flags                          |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
 
-    int rv = CEspControl::getInstance().startSoftAccessPoint(cfg);
-    if (rv == ESP_CONTROL_OK) {
-        CEspControl::getInstance().getSoftAccessPointConfig(soft_ap_cfg);
-        wifi_status = WL_AP_LISTENING;
-        if (net_ifs[NI_WIFI_SOFTAP] != nullptr) {
-            net_ifs[NI_WIFI_SOFTAP]->setLinkUp();
-        }
-    } else {
-        wifi_status = WL_AP_FAILED;
-    }
-    CLwipIf::getInstance().restartAsyncRequest();
+    memcpy(ni->hwaddr, this->driver->getMacAddress(), 6); // FIXME handle this using a constant
+    // ni->hwaddr                         = C33EthernetDriver.getMacAddress();
+    // ni->hwaddr_len                     = sizeof(macaddress);
+    ni->hwaddr_len                     = 6;
 
-    return rv;
-}
+    ni->output                         = etharp_output;
+    ni->linkoutput                     = _netif_output;
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::setLowPowerMode()
-{
-    /* -------------------------------------------------------------------------- */
-    CLwipIf::getInstance().startSyncRequest();
-    int rv = CEspControl::getInstance().setPowerSaveMode(1);
-    CLwipIf::getInstance().restartAsyncRequest();
-    return rv;
+    return ERR_OK;
 }
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::resetLowPowerMode()
-{
-    /* -------------------------------------------------------------------------- */
-    CLwipIf::getInstance().startSyncRequest();
-    int rv = CEspControl::getInstance().setPowerSaveMode(0);
-    CLwipIf::getInstance().restartAsyncRequest();
-    return rv;
-}
+err_t CEth::output(struct netif* ni, struct pbuf* p) {
+    err_t errval = ERR_OK;
 
-#ifdef LWIP_USE_TIMER
-/* -------------------------------------------------------------------------- */
-void CLwipIf::timer_cb(timer_callback_args_t *arg) {
-/*  -------------------------------------------------------------------------- */   
-  (void)arg;
-  CLwipIf::getInstance().lwip_task();
-}
-#endif
+    /* TODO check if this makes sense, I may get a pbuf chain
+     * it could happen that if I get a pbuf chain
+     * - there are enough tx_buffers available to accomodate all the packets in the chain
+     * - most of the chain is enqueued for delivery, but a certain point the driver.send call returns error
+     *   then lwip is supposed to handle that, that may be an issue
+     */
+    struct pbuf *q = p;
+    do {
+        NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
+        NETIF_STATS_TX_TIME_START(this->stats);
+        auto err = C33EthernetDriver.send((uint8_t*)q->payload, q->len);
+        if(err != 0) {
+            NETIF_STATS_INCREMENT_ERROR(this->stats, err);
+            NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
+            errval = ERR_IF;
+            break;
+        }
+        NETIF_STATS_INCREMENT_TX_BYTES(this->stats, q->len);
+        NETIF_STATS_TX_TIME_AVERAGE(this->stats);
+        q = q->next;
 
-/* ***************************************************************************
- *                               DNS related functions
- * ****************************************************************************/
+        // FIXME remove this, only purpose is to verify if I ever deal with a pbuf chain
+        // if(q!=nullptr) {
+        //     NETIF_STATS_INCREMENT_ERROR(this->stats, 1024);
+        // }
+    } while(q != nullptr && errval != ERR_OK);
 
-/* -------------------------------------------------------------------------- */
-void CLwipIf::dns_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
-/* -------------------------------------------------------------------------- */   
-  (void)name;
-  if (ipaddr != NULL) {
-    *((uint32_t *)callback_arg) = ip4_addr_get_u32(ipaddr);
-  } else {
-    *((uint32_t *)callback_arg) = 0;
-  }
+    return errval;
 }
 
-/* -------------------------------------------------------------------------- */
-int8_t CLwipIf::get_ip_address_from_hostname(const char* hostname, uint32_t* ipaddr)
-{
-    /* -------------------------------------------------------------------------- */
-    ip_addr_t iphost;
-    err_t err;
-    unsigned long dns_request_sent = 0;
-    int8_t ret = 0;
+void CEth::consume_callback(uint8_t* buffer, uint32_t len) {
+    // TODO understand if this callback can be moved into the base class
+    // arduino::lock();
 
-    *ipaddr = 0;
-    err = dns_gethostbyname(hostname, &iphost, &dns_callback, ipaddr);
+    const uint16_t trimmed_size = len;
 
-    switch (err) {
-    case ERR_OK:
-        *ipaddr = ip4_addr_get_u32(&iphost);
-        ret = 1;
-        break;
+    // zerocopy_pbuf_t *custom_pbuf = get_zerocopy_pbuf(buffer, 1536);
+    zerocopy_pbuf_t *custom_pbuf = get_zerocopy_pbuf(buffer, trimmed_size);
 
-    case ERR_INPROGRESS:
-        dns_request_sent = millis();
-        while (*ipaddr == 0) {
-            lwip_task();
-            if ((millis() - dns_request_sent) >= TIMEOUT_DNS_REQUEST) {
-                ret = -1;
-                break;
-            }
-        }
+    // mem_trim should be passed as an argument, since it depends on the kind of allocation performed
+    void* buf = mem_trim(buffer, trimmed_size);
 
-        if (ret == 0) {
-            if (*ipaddr == 0) {
-                ret = -2;
-            } else {
-                ret = 1;
-            }
-        }
-        break;
+    // TODO consider allocating a custom pool for RX or use PBUF_POOL
+    struct pbuf *p = pbuf_alloced_custom(
+        PBUF_RAW, len, PBUF_RAM, &custom_pbuf->p, buffer, trimmed_size);
 
-    case ERR_ARG:
-        ret = -4;
-        break;
+    err_t err = this->ni.input((struct pbuf*)p, &this->ni);
+    if (err != ERR_OK) {
+        NETIF_STATS_INCREMENT_ERROR(this->stats, err);
 
-    default:
-        ret = -4;
-        break;
+        NETIF_STATS_INCREMENT_RX_NI_INPUT_FAILED_CALLS(this->stats);
+        NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(this->stats);
+        pbuf_free((struct pbuf*)p);
+    } else {
+        NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len);
     }
+    // arduino::unlock();
+}
 
-    return ret;
+/* ########################################################################## */
+/*                    CWifiStation NETWORK INTERFACE CLASS                    */
+/* ########################################################################## */
+uint8_t CWifiStation::wifistation_id = 0;
+
+CWifiStation::CWifiStation()
+: hw_init(false) {
+    // TODO this class should implement the driver interface
+    // CLwipIf::getInstance()
 }
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::getHostByName(const char* aHostname, IPAddress& aResult)
-{
-    /* -------------------------------------------------------------------------- */
-    int ret = 0;
-    uint32_t ipResult = 0;
-
-    // See if it's a numeric IP address
-    if (inet2aton(aHostname, aResult)) {
-        // It is, our work here is done
-        return 1;
+int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) { // TODO This should be called only once, make it private
+    int res = 0;
+    int time_num = 0;
+
+    // arduino::lock();
+    CEspControl::getInstance().listenForStationDisconnectEvent([this] (CCtrlMsgWrapper *resp) -> int {
+        netif_set_link_down(&this->ni);
+        return ESP_CONTROL_OK;
+    });
+    CEspControl::getInstance().listenForInitEvent([this] (CCtrlMsgWrapper *resp) -> int {
+        // Serial.println("init");
+        this->hw_init = true;
+        return ESP_CONTROL_OK;
+    });
+
+    if ((res=CEspControl::getInstance().initSpiDriver()) != 0) {
+        res = -1; // FIXME put a proper error code
+        goto exit;
     }
 
-    if (getDns(0) == IPAddress(0, 0, 0, 0)) {
-        return INVALID_SERVER;
+    while (time_num < 100 && !hw_init) { // TODO #define WIFI_INIT_TIMEOUT_MS 10000
+        CEspControl::getInstance().communicateWithEsp();
+        R_BSP_SoftwareDelay(100, BSP_DELAY_UNITS_MILLISECONDS);
+        time_num++;
     }
 
-#if LWIP_DNS
-    ret = get_ip_address_from_hostname(aHostname, &ipResult);
-    aResult = IPAddress(ipResult);
-#endif
-    return ret;
+    res = CEspControl::getInstance().setWifiMode(WIFI_MODE_STA);
+    CNetif::begin(ip, nm, gw);
+    // netif_set_link_up(&this->ni); // TODO this should be set only when successfully connected to an AP
+exit:
+    // arduino::unlock();
+    return res;
 }
 
-/* -------------------------------------------------------------------------- */
-int CLwipIf::inet2aton(const char* address, IPAddress& result)
-{
-    /* -------------------------------------------------------------------------- */
-    uint16_t acc = 0; // Accumulator
-    uint8_t dots = 0;
+int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
+    WifiApCfg_t ap;
+    int rv = ESP_CONTROL_CTRL_ERROR; // FIXME this should be set with an error meaning AP not found
+    bool found = false;
+    int8_t best_index = -1; // this index is used to find the ap with the best rssi
+    // AccessPoint_t* best_matching_ap;
+    // arduino::lock();
 
-    if (address == NULL) {
-        return 0;
+    // if(access_points.size() == 0) {
+    //     this->scanForAp();
+    // }
+    if((rv=this->scanForAp()) != WL_SCAN_COMPLETED) {
+        // rv = -1; // FIXME set proper error code
+        goto exit;
+    }
+    this->printAps();
+
+    // find the AP with the best rssi
+    for (uint8_t i = 0; i < access_points.size(); i++) {
+        if(strcmp(ssid, (const char*)access_points[i].ssid) == 0
+            && (best_index == -1 || access_points[best_index].rssi < access_points[i].rssi)
+            ) {
+            best_index=i;
+        }
     }
+    if(best_index != -1) {
+        // memset(ap.ssid, 0x00, SSID_LENGTH); // I shouldn't need to zero the ssid string pointer
+        strncpy((char*)ap.ssid, ssid, SSID_LENGTH);
+        // memcpy(ap.ssid, access_points[best_index].ssid, SSID_LENGTH);
 
-    while (*address) {
-        char c = *address++;
-        if (c >= '0' && c <= '9') {
-            acc = acc * 10 + (c - '0');
-            if (acc > 255) {
-                // Value out of [0..255] range
-                return 0;
-            }
-        } else if (c == '.') {
-            if (dots == 3) {
-                // Too much dots (there must be 3 dots)
-                return 0;
-            }
-            result[dots++] = acc;
-            acc = 0;
+        // memset(ap.pwd, 0x00, PASSWORD_LENGTH);
+        if(passphrase != nullptr) {
+            auto slen = strlen(passphrase)+1;
+            strncpy((char*)ap.pwd, passphrase, (slen < PASSWORD_LENGTH) ? slen : PASSWORD_LENGTH);
+            // memcpy(ap.pwd, passphrase, (slen < PASSWORD_LENGTH) ? slen : PASSWORD_LENGTH);
         } else {
-            // Invalid char
-            return 0;
+            // memset(ap.pwd, 0x00, PASSWORD_LENGTH);
+            ap.pwd[0] = '\0';
         }
-    }
 
-    if (dots != 3) {
-        // Too few dots (there must be 3 dots)
-        return 0;
-    }
-    result[3] = acc;
-    return 1;
-}
+        memset(ap.bssid, 0x00, BSSID_LENGTH);
+        memcpy(ap.bssid, access_points[best_index].bssid, BSSID_LENGTH);
 
-/* -------------------------------------------------------------------------- */
-void CLwipIf::beginDns(IPAddress aDNSServer)
-{
-    /* -------------------------------------------------------------------------- */
-    addDns(aDNSServer);
-}
+        // arduino::lock();
+        CEspControl::getInstance().communicateWithEsp(); // TODO make this shared between SoftAP and station
 
+        rv=CEspControl::getInstance().connectAccessPoint(ap);
+        // arduino::unlock();
 
+        if (rv == ESP_CONTROL_OK) {
+            CEspControl::getInstance().getAccessPointConfig(access_point_cfg);
 
-/* -------------------------------------------------------------------------- */
-void CLwipIf::addDns(IPAddress aDNSServer)
-{
-    /* -------------------------------------------------------------------------- */
-#if LWIP_DNS
-    ip_addr_t ip;
-    dns_num++;
-    /* DNS initialized by DHCP when call dhcp_start() */
-    bool dhcp_started = false;
-
-    for (int i = 0; i < NETWORK_INTERFACES_MAX_NUM; i++) {
-        if (net_ifs[i] != nullptr) {
-            if (net_ifs[i]->DhcpIsStarted()) {
-                dhcp_started = true;
-                break;
-            }
+            netif_set_link_up(&this->ni);
         }
+        // arduino::unlock();
     }
+    // else {
+    //     // TODO return AP not found error
+    // }
 
-    if (!dhcp_started) {
-        dns_init();
-        IP_ADDR4(&ip, aDNSServer[0], aDNSServer[1], aDNSServer[2], aDNSServer[3]);
-        dns_setserver(dns_num, &ip);
-    }
-#endif
+exit:
+    // arduino::unlock();
+
+    return rv;
 }
 
-/* -------------------------------------------------------------------------- */
-IPAddress CLwipIf::getDns(int _num)
-{
-    /* -------------------------------------------------------------------------- */
-#if LWIP_DNS
-    const ip_addr_t* tmp = dns_getserver(_num);
-    return IPAddress(ip4_addr_get_u32(tmp));
-#else
-    IPAddress(0, 0, 0, 0);
-#endif
+// disconnect
+int CWifiStation::disconnectFromAp() {
+    return CEspControl::getInstance().disconnectAccessPoint();
 }
 
+err_t CWifiStation::init(struct netif* ni) {
+    // Setting up netif
+#if LWIP_NETIF_HOSTNAME
+    // TODO pass the hostname in the constructor os with a setter
+    ni->hostname                       = "C33-WifiSta";
+#endif
+    ni->name[0]                        = CWifiStation::wifistation_ifname_prefix;
+    ni->name[1]                        = '0' + CWifiStation::wifistation_id++;
+    ni->mtu                            = 1500; // FIXME get this from the network
+    ni->flags                          |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
 
+    WifiMac_t MAC;
+    MAC.mode = WIFI_MODE_STA;
+    CEspControl::getInstance().getWifiMacAddress(MAC);
+    CNetUtilities::macStr2macArray(ni->hwaddr, MAC.mac);
+    ni->hwaddr_len = 6; // FIXME this should be a macro defined somewhere
+    // ni->hwaddr_len = CLwipIf::getInstance().getMacAddress(NI_WIFI_STATION, ni->hwaddr);
 
+    ni->output                         = etharp_output;
+    ni->linkoutput                     = _netif_output;
 
-/* -------------------------------------------------------------------------- */
-const char* CLwipIf::getSSID(NetIfType_t type)
-{
-    /* -------------------------------------------------------------------------- */
-    if (type == NI_WIFI_STATION) {
-        return (char*)access_point_cfg.ssid;
-    } else if (type == NI_WIFI_SOFTAP) {
-        return (char*)soft_ap_cfg.ssid;
-    } else {
-        return nullptr;
-    }
+    return ERR_OK;
 }
 
-/* -------------------------------------------------------------------------- */
-uint8_t* CLwipIf::getBSSID(NetIfType_t type, uint8_t* bssid)
-{
-    /* -------------------------------------------------------------------------- */
-    if (type == NI_WIFI_STATION) {
-        CNetUtilities::macStr2macArray(bssid, (const char*)access_point_cfg.bssid);
-        return bssid;
-    } else if (type == NI_WIFI_SOFTAP) {
-        CNetUtilities::macStr2macArray(bssid, (const char*)soft_ap_cfg.out_mac);
-        return bssid;
+err_t CWifiStation::output(struct netif* _ni, struct pbuf* p) {
+    // FIXME set ifn
+    int ifn = 0; // interface number in CNetif.cpp seems to not be set anywhere
+    uint8_t *buf = nullptr;
+    uint16_t size=p->tot_len;
+    err_t errval = ERR_IF;
+    int err = ESP_CONTROL_OK;
+
+    NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
+    NETIF_STATS_TX_TIME_START(this->stats);
+
+    // arduino::lock();
+    // p may be a chain of pbufs
+    if(p->next != nullptr) {
+        buf = (uint8_t*) malloc(size*sizeof(uint8_t));
+        if(buf == nullptr) {\
+            NETIF_STATS_INCREMENT_ERROR(this->stats, ERR_MEM);
+            NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
+            errval = ERR_MEM;
+            goto exit;
+        }
+
+        // copy the content of pbuf
+        assert(pbuf_copy_partial(p, buf, size, 0) == size);
     } else {
-        return nullptr;
+        buf = (uint8_t*)p->payload;
     }
-}
 
-/* -------------------------------------------------------------------------- */
-int32_t CLwipIf::getRSSI(NetIfType_t type)
-{
-    /* -------------------------------------------------------------------------- */
-    if (type == NI_WIFI_STATION) {
-        return access_point_cfg.rssi;
+    // sendBuffer makes a memcpy of buffer
+    // TODO send buffer should handle the buffer deletion and avoid a memcpy
+    if ((err = CEspControl::getInstance().sendBuffer(
+            ESP_STA_IF, ifn, buf, size)) == ESP_CONTROL_OK) {
+        errval = ERR_OK;
+        NETIF_STATS_INCREMENT_TX_BYTES(this->stats, size);
+        NETIF_STATS_TX_TIME_AVERAGE(this->stats);
     } else {
-        return 0;
+        NETIF_STATS_INCREMENT_ERROR(this->stats, err);
+        NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
     }
-}
 
-/* -------------------------------------------------------------------------- */
-uint8_t CLwipIf::getEncryptionType(NetIfType_t type)
-{
-    /* -------------------------------------------------------------------------- */
-    if (type == NI_WIFI_STATION) {
-        return Encr2wl_enc(access_point_cfg.encryption_mode);
-    } else if (type == NI_WIFI_SOFTAP) {
-        return Encr2wl_enc((uint8_t)soft_ap_cfg.encryption_mode);
-    } else {
-        return ENC_TYPE_UNKNOWN;
+exit:
+    if(p->next != nullptr && buf != nullptr) {
+        free(buf);
     }
+    // arduino::unlock();
+    return errval;
 }
 
-/* ########################################################################## */
-/*                      BASE NETWORK INTERFACE CLASS                          */
-/* ########################################################################## */
-
-/* -------------------------------------------------------------------------- */
-CNetIf::CNetIf()
-    : dhcp_timeout(30000)
-    , dhcp_started(false)
-    , dhcp_acquired(false)
-    , id(0)
-    , dhcp_st(DHCP_IDLE_STATUS)
-    , _dhcp_lease_state(DHCP_CHECK_NONE)
-{
-    /* -------------------------------------------------------------------------- */
-    memset(hostname, 0x00, MAX_HOSTNAME_DIM);
-    hostname[0] = 'C';
-    hostname[1] = '3';
-    hostname[2] = '3';
-#if LWIP_NETIF_HOSTNAME
-    ni.hostname = (const char*)&hostname;
-#endif
-#ifdef CNETWORK_INTERFACE_DEBUG
-    Serial.println("[CNET]: CNetIf constructor");
-#endif
-}
+void CWifiStation::task() {
+    // calling the base class task, in order to make thigs work
+    CNetif::task();
 
-/* -------------------------------------------------------------------------- */
-CNetIf::~CNetIf()
-{
-    /* -------------------------------------------------------------------------- */
-#ifdef CNETWORK_INTERFACE_DEBUG
-    Serial.println("[CNET]: CNetIf destructor");
-#endif
-}
+    // TODO in order to make things easier this should be implemented inside of Wifi driver
+    // and not override LWIPInterface method
 
-/* ***************************************************************************
- *                               DHCP related functions
- * ****************************************************************************/
+    uint8_t if_num = 0;
+    uint16_t dim = 0;
+    uint8_t* buffer = nullptr;
+    struct pbuf* p = nullptr;
 
-/* -------------------------------------------------------------------------- */
-void CNetIf::DhcpNotUsed()
-{
-    /* -------------------------------------------------------------------------- */
-    DhcpStop();
-    dhcp_inform(getNi());
-}
+    NETIF_STATS_RX_TIME_START(this->stats);
+    // arduino::lock();
+    // TODO do not perform this when not connected to an AP
+    if(hw_init) {
+        CEspControl::getInstance().communicateWithEsp(); // TODO make this shared between SoftAP and station
 
-/* -------------------------------------------------------------------------- */
-int CNetIf::checkLease()
-{
-    /* -------------------------------------------------------------------------- */
-    int rc = DHCP_CHECK_NONE;
+        // TODO handling buffer this way may be harmful for the memory
+        buffer = CEspControl::getInstance().getStationRx(if_num, dim);
+    }
 
-    task();
-    rc = dhcp_get_lease_state();
+    // empty the ESP32 queue
+    while(buffer != nullptr) {
+        // FIXME this section is redundant and should be generalized toghether with CEth::consume_callback
+        // TODO understand if this should be moved into the base class
+        NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(this->stats);
+        // NETIF_STATS_RX_TIME_START(this->stats);
 
-    if (rc != _dhcp_lease_state) {
-        switch (_dhcp_lease_state) {
-        case DHCP_CHECK_NONE:
-            _dhcp_lease_state = rc;
-            rc = DHCP_CHECK_NONE;
-            break;
+        zerocopy_pbuf_t *custom_pbuf = get_zerocopy_pbuf(buffer, dim, free);
 
-        case DHCP_CHECK_RENEW_OK:
-            _dhcp_lease_state = rc;
-            if (rc == DHCP_CHECK_NONE) {
-                rc = DHCP_CHECK_RENEW_OK;
-            } else {
-                rc = DHCP_CHECK_RENEW_FAIL;
-            }
-            break;
+        // TODO consider allocating a custom pool for RX or use PBUF_POOL
+        struct pbuf *p = pbuf_alloced_custom(
+            PBUF_RAW, dim, PBUF_RAM, &custom_pbuf->p, buffer, dim);
 
-        case DHCP_CHECK_REBIND_OK:
-            _dhcp_lease_state = rc;
-            if (rc == DHCP_CHECK_NONE) {
-                rc = DHCP_CHECK_REBIND_OK;
-            } else {
-                rc = DHCP_CHECK_REBIND_FAIL;
-            }
-            break;
+        err_t err = this->ni.input((struct pbuf*)p, &this->ni);
+        if (err != ERR_OK) {
+            NETIF_STATS_INCREMENT_ERROR(this->stats, err);
 
-        default:
-            _dhcp_lease_state = DHCP_CHECK_NONE;
-            break;
+            NETIF_STATS_INCREMENT_RX_NI_INPUT_FAILED_CALLS(this->stats);
+            NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(this->stats);
+            pbuf_free((struct pbuf*)p);
+        } else {
+            NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len);
         }
-    }
-
-    return rc;
-}
 
-/* -------------------------------------------------------------------------- */
-uint8_t CNetIf::dhcp_get_lease_state()
-{
-    /* -------------------------------------------------------------------------- */
-    uint8_t res = 0;
-    struct dhcp* dhcp = (struct dhcp*)netif_get_client_data(getNi(), LWIP_NETIF_CLIENT_DATA_INDEX_DHCP);
-
-    if (dhcp->state == 5 /*DHCP_STATE_RENEWING*/) {
-        res = 2;
-    } else if (dhcp->state == 4 /* DHCP_STATE_REBINDING */) {
-        res = 4;
+        buffer = CEspControl::getInstance().getStationRx(if_num, dim);
+        // NETIF_STATS_RX_TIME_AVERAGE(this->stats);
     }
-    return res;
+    NETIF_STATS_RX_TIME_AVERAGE(this->stats);
+    // arduino::unlock();
 }
 
-/* -------------------------------------------------------------------------- */
-bool CNetIf::dhcp_request()
-{
-    /* -------------------------------------------------------------------------- */
-    /* make a DHCP request: it runs till an address is acquired or a timeout
-       expires */
-    unsigned long startTime = millis();
-    bool acquired = false;
-
-    do {
-        acquired = isDhcpAcquired();
-        if (!acquired && ((millis() - startTime) > dhcp_timeout)) {
-            break;
-        }
-
-    } while (!acquired);
-
-    return acquired;
+void CWifiStation::consume_callback(uint8_t* buffer, uint32_t len) {
+    // FIXME take what is written in task and put it in here
 }
 
-/* -------------------------------------------------------------------------- */
-void CNetIf::dhcp_reset()
-{
-    /* -------------------------------------------------------------------------- */
-    /* it resets the DHCP status to IDLE */
-    while (dhcp_st != DHCP_IDLE_STATUS) {
-        task();
-    }
+const char* CWifiStation::getSSID() {
+    return (const char*)access_point_cfg.ssid;
 }
 
-/* -------------------------------------------------------------------------- */
-void CNetIf::DhcpSetTimeout(unsigned long t)
-{
-    /* -------------------------------------------------------------------------- */
-    dhcp_timeout = t;
+uint8_t* CWifiStation::getBSSID(uint8_t* bssid){
+    CNetUtilities::macStr2macArray(bssid, (const char*)access_point_cfg.bssid);
+    return bssid;
 }
 
-/* -------------------------------------------------------------------------- */
-bool CNetIf::isDhcpAcquired()
-{
-    return dhcp_acquired;
-}
-/* -------------------------------------------------------------------------- */
-bool CNetIf::DhcpStart()
-{
-    /* first stop / reset */
-    DhcpStop();
-    /* then actually start */
-    dhcp_started = true;
-    dhcp_st = DHCP_START_STATUS;
-    return dhcp_request();
-}
-/* -------------------------------------------------------------------------- */
-void CNetIf::DhcpStop()
-{
-    /* -------------------------------------------------------------------------- */
-    dhcp_started = false;
-    if (dhcp_st == DHCP_IDLE_STATUS) {
-        return;
-    }
-    if (dhcp_st == DHCP_GOT_STATUS && netif_is_link_up(getNi())) {
-        dhcp_st = DHCP_RELEASE_STATUS;
-    } else {
-        dhcp_st = DHCP_STOP_STATUS;
-    }
-    dhcp_reset();
+int32_t CWifiStation::getRSSI() {
+    // TODO should this be updated?
+    return (uint32_t)access_point_cfg.rssi;
 }
-/* -------------------------------------------------------------------------- */
-void CNetIf::dhcp_task()
-{
-    /* -------------------------------------------------------------------------- */
 
-    struct dhcp* lwip_dhcp;
-    static unsigned long DHCPStartTime;
-
-    switch (dhcp_st) {
-    case DHCP_IDLE_STATUS:
-        /* nothing to do... wait for DhcpStart() to start the process */
-        break;
-    case DHCP_START_STATUS:
-        if (netif_is_link_up(getNi())) {
-            ip_addr_set_zero_ip4(&(getNi()->ip_addr));
-            ip_addr_set_zero_ip4(&(getNi()->netmask));
-            ip_addr_set_zero_ip4(&(getNi()->gw));
-            /* start lwIP dhcp */
-            dhcp_start(getNi());
-
-            DHCPStartTime = millis();
-            dhcp_st = DHCP_WAIT_STATUS;
-        }
-        break;
-    case DHCP_WAIT_STATUS:
-        if (netif_is_link_up(getNi())) {
-            if (dhcp_supplied_address(getNi())) {
-                dhcp_acquired = true;
-                dhcp_st = DHCP_GOT_STATUS;
-            } else if (millis() - DHCPStartTime > 1000) {
-                /* TIMEOUT */
-                lwip_dhcp = (struct dhcp*)netif_get_client_data(getNi(), LWIP_NETIF_CLIENT_DATA_INDEX_DHCP);
-                if (lwip_dhcp->tries > MAX_DHCP_TRIES) {
-                    dhcp_st = DHCP_STOP_STATUS;
-                }
-            }
-        } else {
-            dhcp_st = DHCP_START_STATUS;
-        }
-        break;
-    case DHCP_GOT_STATUS:
-        if (!netif_is_link_up(getNi())) {
-            dhcp_st = DHCP_STOP_STATUS;
-        }
-
-        break;
-    case DHCP_RELEASE_STATUS:
-        dhcp_release(getNi());
-        dhcp_acquired = false;
-        dhcp_st = DHCP_STOP_STATUS;
-        break;
-    case DHCP_STOP_STATUS:
-        dhcp_acquired = false;
-        dhcp_stop(getNi());
-        if (dhcp_started) {
-            dhcp_st = DHCP_START_STATUS;
-        } else {
-            dhcp_st = DHCP_IDLE_STATUS;
-        }
-        break;
-    }
+uint8_t CWifiStation::getEncryptionType() {
+    return Encr2wl_enc(access_point_cfg.encryption_mode);
 }
 
-/* -------------------------------------------------------------------------- */
-void CNetIf::setLinkUp()
-{
-    /* -------------------------------------------------------------------------- */
-    netif_set_link_up(&ni);
-    /* When the netif is fully configured this function must be called.*/
-    netif_set_up(&ni);
-}
+// int CWifiStation::getMacAddress(uint8_t* mac) {
+// }
 
-/* -------------------------------------------------------------------------- */
-void CNetIf::setLinkDown()
-{
-    /* -------------------------------------------------------------------------- */
-    netif_set_link_down(&ni);
-    /* When the netif is fully configured this function must be called.*/
-    netif_set_down(&ni);
-}
+// uint8_t CWifiStation::getChannel() {
+//     return (uint8_t)access_point_cfg.channel;
+// }
 
 /* ########################################################################## */
-/*                      ETHERNET NETWORK INTERFACE CLASS                      */
+/*                      CWifiSoftAp NETWORK INTERFACE CLASS                   */
 /* ########################################################################## */
-CEth::CEth() { }
-CEth::~CEth() { }
 
-/* -------------------------------------------------------------------------- */
-void CEth::begin(IPAddress _ip, IPAddress _gw, IPAddress _nm)
-{
-    if (_ip == INADDR_NONE) {
-      _ip = default_ip;
-      _nm = default_nm;
-      _gw = default_gw;
-    }
-    IP_ADDR4(&ip, _ip[0], _ip[1], _ip[2], _ip[3]);
-    IP_ADDR4(&nm, _nm[0], _nm[1], _nm[2], _nm[3]);
-    IP_ADDR4(&gw, _gw[0], _gw[1], _gw[2], _gw[3]);
+CWifiSoftAp::CWifiSoftAp() { }
+CWifiSoftAp::~CWifiSoftAp() { }
 
-    netif_add(&ni, &ip, &nm, &gw, NULL, CEth::init, ethernet_input);
-    netif_set_default(&ni);
+uint8_t CWifiSoftAp::softap_id = 0;
 
-    if (netif_is_link_up(&ni)) {
-        /* When the netif is fully configured this function must be called */
-        netif_set_up(&ni);
-    } else {
-        /* When the netif link is down this function must be called */
-        netif_set_down(&ni);
-    }
+// This is required for dhcp server to assign ip addresses to AP clients
+IPAddress default_nm("255.255.255.0");
+IPAddress default_dhcp_server_ip("192.168.4.1");
+
+CWifiSoftAp::CWifiSoftAp()
+: hw_init(false) {
 
-#if LWIP_NETIF_LINK_CALLBACK
-    /* Set the link callback function, this function is called on change of link status */
-    // netif_set_link_callback(&eth0if, eht0if_link_toggle_cbk);
-#endif /* LWIP_NETIF_LINK_CALLBACK */
-    /*
-     * set the callback function that is called when an ethernet frame is physically
-     * received, it is important that the callbacks are set before the initializiation
-     */
-    eth_set_rx_frame_cbk(std::bind(&CEth::handleEthRx, this));
-    eth_set_link_on_cbk(std::bind(&CEth::setLinkUp, this));
-    eth_set_link_off_cbk(std::bind(&CEth::setLinkDown, this));
 }
 
-/* -------------------------------------------------------------------------- */
-void CEth::task()
-{
-    /* -------------------------------------------------------------------------- */
+int CWifiSoftAp::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) { // TODO use provided ip address, instead of default ones
+    int res = 0;
+    int time_num = 0;
 
-    eth_execute_link_process();
+    // arduino::lock();
+    CEspControl::getInstance().listenForInitEvent([this] (CCtrlMsgWrapper *resp) -> int {
+        // Serial.println("init");
+        this->hw_init = true;
+        return ESP_CONTROL_OK;
+    });
 
-#if LWIP_DHCP
-    static unsigned long dhcp_last_time_call = 0;
-    if (dhcp_last_time_call == 0 || millis() - dhcp_last_time_call > DHCP_FINE_TIMER_MSECS) {
-        dhcp_task();
-        dhcp_last_time_call = millis();
+    if ((res=CEspControl::getInstance().initSpiDriver()) != 0) {
+        // res = -1; // FIXME put a proper error code
+        goto exit;
     }
-#endif
-}
 
-/* ########################################################################## */
-/*                    CWifiStation NETWORK INTERFACE CLASS                    */
-/* ########################################################################## */
+    while (time_num < 100 && !hw_init) { // TODO #define WIFI_INIT_TIMEOUT_MS 10000
+        CEspControl::getInstance().communicateWithEsp();
+        R_BSP_SoftwareDelay(100, BSP_DELAY_UNITS_MILLISECONDS);
+        time_num++;
+    }
 
-CWifiStation::CWifiStation() { }
-CWifiStation::~CWifiStation() { }
+    res = CEspControl::getInstance().setWifiMode(WIFI_MODE_AP);
 
-void CWifiStation::begin(IPAddress _ip, IPAddress _gw, IPAddress _nm)
-{
-    if (_ip == INADDR_NONE) {
-      _ip = default_ip;
-      _nm = default_nm;
-      _gw = default_gw;
-    }
-    IP_ADDR4(&ip, _ip[0], _ip[1], _ip[2], _ip[3]);
-    IP_ADDR4(&nm, _nm[0], _nm[1], _nm[2], _nm[3]);
-    IP_ADDR4(&gw, _gw[0], _gw[1], _gw[2], _gw[3]);
+    // netif_set_link_up(&this->ni); // TODO this should be set only when successfully connected to an AP
+    CNetif::begin(
+        default_dhcp_server_ip,
+        default_nm,
+        default_dhcp_server_ip
+    );
+exit:
+    // arduino::unlock();
+    return res;
+}
 
-    netif_add(&ni, &ip, &nm, &gw, NULL, CWifiStation::init, ethernet_input);
-    netif_set_default(&ni);
+// TODO scan the other access point first and then set the channel if 0
+// TODO there are requirements for ssid and password
+int CWifiSoftAp::startSoftAp(const char* ssid, const char* passphrase, uint8_t channel) {
+    SoftApCfg_t cfg;
 
-    if (netif_is_link_up(&ni)) {
-        /* When the netif is fully configured this function must be called */
-        netif_set_up(&ni);
+    strncpy((char*)cfg.ssid, ssid, SSID_LENGTH);
+
+    if (passphrase == nullptr) {
+        cfg.pwd[0] = '\0';
+        cfg.encryption_mode = WIFI_AUTH_OPEN;
     } else {
-        /* When the netif link is down this function must be called */
-        netif_set_down(&ni);
+        auto slen = strlen(passphrase)+1;
+        strncpy((char*)cfg.pwd, passphrase, (slen < PASSWORD_LENGTH) ? slen : PASSWORD_LENGTH);
+
+        cfg.encryption_mode = WIFI_AUTH_WPA_WPA2_PSK;
     }
 
-#if LWIP_NETIF_LINK_CALLBACK
-    /* Set the link callback function, this function is called on change of link status */
-    // netif_set_link_callback(&eth0if, eht0if_link_toggle_cbk);
-#endif /* LWIP_NETIF_LINK_CALLBACK */
-}
+    channel = (channel == 0) ? 1 : channel;
+    cfg.channel = (channel > MAX_CHNL_NO) ? MAX_CHNL_NO : channel;
+    cfg.max_connections = 10; // FIXME
+    // cfg.max_connections = MAX_SOFAT_CONNECTION_DEF; // FIXME
+    cfg.bandwidth = WIFI_BW_HT40;
+    cfg.ssid_hidden = false;
 
-/* -------------------------------------------------------------------------- */
-void CWifiStation::task()
-{
-    /* -------------------------------------------------------------------------- */
-    /* get messages and process it  */
-    uint8_t if_num;
-    uint16_t dim = 0;
-    uint8_t* buf = nullptr;
-    struct pbuf* p = nullptr;
+    int rv = CEspControl::getInstance().startSoftAccessPoint(cfg);
+    if (rv == ESP_CONTROL_OK) {
+        CEspControl::getInstance().getSoftAccessPointConfig(soft_ap_cfg);
+        // wifi_status = WL_AP_LISTENING;
+        netif_set_link_up(&this->ni);
 
-    /* shall we verify something about if_num??? */
-    do {
-        dim = CEspControl::getInstance().peekStationRxMsgSize();
-        if (dim > 0)
-        {
-            p = pbuf_alloc(PBUF_RAW, dim, PBUF_RAM);
-            if (p != nullptr)
-            {
-                buf = CEspControl::getInstance().getStationRx(if_num, dim);
-                /* Copy ethernet frame into pbuf */
-                pbuf_take((struct pbuf*)p, (uint8_t*)buf, (uint32_t)dim);
-                if (ni.input(p, &ni) != ERR_OK) {
-                    pbuf_free(p);
-                }
-                delete[] buf;
-            }
-        }
-    } while(dim > 0 && p != nullptr);
+        // FIXME the dhcp server should be started somewhere else
+        dhcps_start(&(this->ni));
+    } else {
+        // wifi_status = WL_AP_FAILED;
+    }
 
 
-#if LWIP_DHCP
-    static unsigned long dhcp_last_time_call = 0;
-    if (dhcp_last_time_call == 0 || millis() - dhcp_last_time_call > DHCP_FINE_TIMER_MSECS) {
-        dhcp_task();
-        dhcp_last_time_call = millis();
-    }
-#endif
+    return rv;
 }
 
-/* -------------------------------------------------------------------------- */
-int CWifiStation::getMacAddress(uint8_t* mac) {
-/* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getMacAddress(NI_WIFI_STATION, mac);
-}
+int CWifiSoftAp::stopSoftAp() {
 
-/* -------------------------------------------------------------------------- */
-const char* CWifiStation::getSSID()
-{
-    /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getSSID(NI_WIFI_STATION);
 }
 
-/* -------------------------------------------------------------------------- */
-uint8_t* CWifiStation::getBSSID(uint8_t* bssid)
-{
-    /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getBSSID(NI_WIFI_STATION, bssid);
-}
+err_t CWifiSoftAp::init(struct netif* ni) {
+    // Setting up netif
+#if LWIP_NETIF_HOSTNAME
+    // TODO pass the hostname in the constructor os with a setter
+    ni->hostname                       = "C33-SoftAP";
+#endif
+    ni->name[0]                        = CWifiSoftAp::softap_ifname_prefix;
+    ni->name[1]                        = '0' + CWifiSoftAp::softap_id++;
+    ni->mtu                            = 1500; // FIXME get this from the network
+    ni->flags                          |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
 
-/* -------------------------------------------------------------------------- */
-int32_t CWifiStation::getRSSI()
-{
-    /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getRSSI(NI_WIFI_STATION);
-}
+    WifiMac_t MAC;
+    MAC.mode = WIFI_MODE_AP;
+    CEspControl::getInstance().getWifiMacAddress(MAC);
+    CNetUtilities::macStr2macArray(ni->hwaddr, MAC.mac);
+    ni->hwaddr_len = 6; // FIXME this should be a macro defined somewhere
 
-/* -------------------------------------------------------------------------- */
-uint8_t CWifiStation::getEncryptionType()
-{
-    /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getEncryptionType(NI_WIFI_STATION);
+    ni->output                         = etharp_output;
+    ni->linkoutput                     = _netif_output;
+
+    return ERR_OK;
 }
 
-/* ########################################################################## */
-/*                      CWifiSoftAp NETWORK INTERFACE CLASS                   */
-/* ########################################################################## */
+err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
+    // FIXME set ifn
+    int ifn = 0; // interface number in CNetif.cpp seems to not be set anywhere
+    uint8_t *buf = nullptr;
+    uint16_t size=p->tot_len;
+    err_t errval = ERR_IF;
+    int err = ESP_CONTROL_OK;
 
-CWifiSoftAp::CWifiSoftAp() { }
-CWifiSoftAp::~CWifiSoftAp() { }
+    NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
+    NETIF_STATS_TX_TIME_START(this->stats);
 
-void CWifiSoftAp::begin(IPAddress _ip, IPAddress _gw, IPAddress _nm)
-{
-    if (_ip == INADDR_NONE) {
-      _ip = default_dhcp_server_ip;
-      _nm = default_nm;
-      _gw = default_dhcp_server_ip;
+    // arduino::lock();
+    // p may be a chain of pbufs
+    if(p->next != nullptr) {
+        buf = (uint8_t*) malloc(size*sizeof(uint8_t));
+        if(buf == nullptr) {\
+            NETIF_STATS_INCREMENT_ERROR(this->stats, ERR_MEM);
+            NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
+            errval = ERR_MEM;
+            goto exit;
+        }
+
+        // copy the content of pbuf
+        assert(pbuf_copy_partial(p, buf, size, 0) == size);
+    } else {
+        buf = (uint8_t*)p->payload;
     }
-    IP_ADDR4(&ip, _ip[0], _ip[1], _ip[2], _ip[3]);
-    IP_ADDR4(&nm, _nm[0], _nm[1], _nm[2], _nm[3]);
-    IP_ADDR4(&gw, _gw[0], _gw[1], _gw[2], _gw[3]);
-
-    netif_add(&ni, &ip, &nm, &gw, NULL, CWifiSoftAp::init, ethernet_input);
-    netif_set_default(&ni);
-    if (netif_is_link_up(&ni)) {
-        /* When the netif is fully configured this function must be called */
-        netif_set_up(&ni);
+
+    // sendBuffer makes a memcpy of buffer
+    // TODO send buffer should handle the buffer deletion and avoid a memcpy
+    if ((err = CEspControl::getInstance().sendBuffer(
+            ESP_AP_IF, ifn, buf, size)) == ESP_CONTROL_OK) {
+        errval = ERR_OK;
+        NETIF_STATS_INCREMENT_TX_BYTES(this->stats, size);
+        NETIF_STATS_TX_TIME_AVERAGE(this->stats);
     } else {
-        /* When the netif link is down this function must be called */
-        netif_set_down(&ni);
+        NETIF_STATS_INCREMENT_ERROR(this->stats, err);
+        NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
     }
 
-#if LWIP_NETIF_LINK_CALLBACK
-    /* Set the link callback function, this function is called on change of link status */
-    // netif_set_link_callback(&eth0if, eht0if_link_toggle_cbk);
-#endif /* LWIP_NETIF_LINK_CALLBACK */
+exit:
+    if(p->next != nullptr && buf != nullptr) {
+        free(buf);
+    }
+    // arduino::unlock();
+    return errval;
 }
 
-/* -------------------------------------------------------------------------- */
-void CWifiSoftAp::task()
-{
-    /* -------------------------------------------------------------------------- */
-    /* get messages and process it
-     * TODO change the algorithm and make it similar to WiFiStation */
-    uint8_t if_num;
-    uint16_t dim;
-    uint8_t* buf = nullptr;
-    /* shall we verify something about if_num??? */
-    do {
+void CWifiSoftAp::task() {
+    // calling the base class task, in order to make thigs work
+    CNetif::task();
 
-        buf = CEspControl::getInstance().getSoftApRx(if_num, dim);
+    // TODO in order to make things easier this should be implemented inside of Wifi driver
+    // and not override LWIPInterface method
 
-        if (buf != nullptr) {
-            struct pbuf* p = pbuf_alloc(PBUF_RAW, dim, PBUF_RAM);
-            if (p != NULL) {
-                /* Copy ethernet frame into pbuf */
-                pbuf_take((struct pbuf*)p, (uint8_t*)buf, (uint32_t)dim);
-                delete[] buf;
+    uint8_t if_num = 0;
+    uint16_t dim = 0;
+    uint8_t* buffer = nullptr;
+    struct pbuf* p = nullptr;
 
-                if (ni.input(p, &ni) != ERR_OK) {
-                    pbuf_free(p);
-                }
-            }
-        }
-    } while(buf != nullptr);
+    NETIF_STATS_RX_TIME_START(this->stats);
+    // arduino::lock();
+    // TODO do not perform this when not connected to an AP
+    if(hw_init) {
+        CEspControl::getInstance().communicateWithEsp(); // TODO make this shared between SoftAP and station
 
-#if LWIP_DHCP
-    static unsigned long dhcp_last_time_call = 0;
-    if (dhcp_last_time_call == 0 || millis() - dhcp_last_time_call > DHCP_FINE_TIMER_MSECS) {
-        dhcp_task();
-        dhcp_last_time_call = millis();
+        // TODO handling buffer this way may be harmful for the memory
+        buffer = CEspControl::getInstance().getSoftApRx(if_num, dim);
     }
-#endif
-}
 
-/* -------------------------------------------------------------------------- */
-int CWifiSoftAp::getMacAddress(uint8_t* mac)
-{
-    /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getMacAddress(NI_WIFI_SOFTAP, mac);
-}
+    // empty the ESP32 queue
+    while(buffer != nullptr) {
+        // FIXME this section is redundant and should be generalized toghether with CEth::consume_callback
+        // TODO understand if this should be moved into the base class
+        NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(this->stats);
+        // NETIF_STATS_RX_TIME_START(this->stats);
 
-/* -------------------------------------------------------------------------- */
-const char* CWifiSoftAp::getSSID()
-{
-    /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getSSID(NI_WIFI_SOFTAP);
+        zerocopy_pbuf_t *custom_pbuf = get_zerocopy_pbuf(buffer, dim, free);
+
+        // TODO consider allocating a custom pool for RX or use PBUF_POOL
+        struct pbuf *p = pbuf_alloced_custom(
+            PBUF_RAW, dim, PBUF_RAM, &custom_pbuf->p, buffer, dim);
+
+        err_t err = this->ni.input((struct pbuf*)p, &this->ni);
+        if (err != ERR_OK) {
+            NETIF_STATS_INCREMENT_ERROR(this->stats, err);
+
+            NETIF_STATS_INCREMENT_RX_NI_INPUT_FAILED_CALLS(this->stats);
+            NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(this->stats);
+            pbuf_free((struct pbuf*)p);
+        } else {
+            NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len);
+        }
+
+        buffer = CEspControl::getInstance().getStationRx(if_num, dim);
+        // NETIF_STATS_RX_TIME_AVERAGE(this->stats);
+    }
+    NETIF_STATS_RX_TIME_AVERAGE(this->stats);
+    // arduino::unlock();
 }
 
-/* -------------------------------------------------------------------------- */
-uint8_t* CWifiSoftAp::getBSSID(uint8_t* bssid)
-{
-    /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getBSSID(NI_WIFI_SOFTAP, bssid);
+const char* CWifiSoftAp::getSSID() {
+    return (const char*)soft_ap_cfg.ssid;
 }
 
-/* -------------------------------------------------------------------------- */
-int32_t CWifiSoftAp::getRSSI()
-{
-    /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getRSSI(NI_WIFI_SOFTAP);
+uint8_t* CWifiSoftAp::getBSSID(uint8_t* bssid){
+    // CNetUtilities::macStr2macArray(bssid, (const char*)soft_ap_cfg.bssid);
+    // return bssid;
 }
 
-/* -------------------------------------------------------------------------- */
-uint8_t CWifiSoftAp::getEncryptionType()
-{
-    /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getEncryptionType(NI_WIFI_SOFTAP);
+uint8_t CWifiSoftAp::getEncryptionType() {
+    return Encr2wl_enc(soft_ap_cfg.encryption_mode);
 }
 
+/* ##########################################################################
+ *                      DEBUG UTILS
+ * ########################################################################## */
 
 #if DHCPS_DEBUG == 1
 char b_dbg[512];

From d40a06a92eafb857bf5a2b82f5008e627f702fc8 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:25:06 +0100
Subject: [PATCH 09/79] added possibility to pass a std::function to esphosted
 callbacks

---
 libraries/ESPhost/src/CEspCbks.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libraries/ESPhost/src/CEspCbks.h b/libraries/ESPhost/src/CEspCbks.h
index 892fc71d5..203ce9962 100644
--- a/libraries/ESPhost/src/CEspCbks.h
+++ b/libraries/ESPhost/src/CEspCbks.h
@@ -32,13 +32,14 @@
 
 
 #include "CCtrlWrapper.h"
+#include <functional>
 
 #define RESPONSE_TABLE_DIM                  (CTRL_RESP_MAX - CTRL_RESP_BASE) 
 #define EVENT_TABLE_DIM                     (CTRL_EVENT_MAX - CTRL_EVENT_BASE) 
 #define TOTAL_TABLE_DIM                     RESPONSE_TABLE_DIM + EVENT_TABLE_DIM
 
 
-using EspCallback_f    = int (*)(CCtrlMsgWrapper *resp);
+using EspCallback_f    = std::function<int(CCtrlMsgWrapper *resp)>;
 
 
 

From 58c28605c41cbd42ec55fae85d6e6f5a736d9850 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:25:55 +0100
Subject: [PATCH 10/79] defined Ethernet driver and interface instance

---
 libraries/Ethernet/src/EthernetC33.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libraries/Ethernet/src/EthernetC33.h b/libraries/Ethernet/src/EthernetC33.h
index b2b9e4288..4ff7daf0f 100644
--- a/libraries/Ethernet/src/EthernetC33.h
+++ b/libraries/Ethernet/src/EthernetC33.h
@@ -19,8 +19,9 @@
 #ifdef ARDUINO_PORTENTA_C33
 
 // TODO Instantiate the drivers for ethernet with default configuration parameters
-// EthernetC33Driver C33EthernetDriver(2, 2, mem_malloc, 1536);
+inline EthernetC33Driver EthernetDriver(2, 2, mem_malloc, 1536);
 
 // FIXME Instantiate a global variable from CEth, calling it Ethernet
+inline CEth Ethernet(&EthernetDriver);
 
 #endif

From 3850758a97d22426e25794b9296b09c9e16a4abf Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:26:34 +0100
Subject: [PATCH 11/79] removed unused endif

---
 libraries/Ethernet/src/EthernetDriver.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libraries/Ethernet/src/EthernetDriver.h b/libraries/Ethernet/src/EthernetDriver.h
index 8af05d6c2..64cde03bf 100644
--- a/libraries/Ethernet/src/EthernetDriver.h
+++ b/libraries/Ethernet/src/EthernetDriver.h
@@ -92,7 +92,6 @@ class EthernetC33Driver: public NetworkDriver {
 
     virtual void irq_ether_callback(ether_callback_args_t* p_args);
     friend void _irq_ether_callback(ether_callback_args_t* p_args);
-#endif
 };
 
 extern EthernetC33Driver C33EthernetDriver;
\ No newline at end of file

From 9784bacc3a7a37b607e36e1623d2912ab5bfcd2d Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:26:58 +0100
Subject: [PATCH 12/79] commented netif stats

---
 libraries/Ethernet/src/EthernetDriver.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/libraries/Ethernet/src/EthernetDriver.cpp b/libraries/Ethernet/src/EthernetDriver.cpp
index ae69c8a40..6c6b2fb21 100644
--- a/libraries/Ethernet/src/EthernetDriver.cpp
+++ b/libraries/Ethernet/src/EthernetDriver.cpp
@@ -153,19 +153,19 @@ void EthernetC33Driver::poll() {
         return;
     }
 
-    netif_stats &stats = *(this->stats);
-    NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(stats);
+    // netif_stats &stats = *(this->stats);
+    // NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(stats);
 
     // arduino::lock();
     while(ETHER_RD0_RACT != (ctrl.p_rx_descriptor->status & ETHER_RD0_RACT)) {
-        NETIF_STATS_RX_TIME_START(stats); // FIXME add stats
+        // NETIF_STATS_RX_TIME_START(stats); // FIXME add stats
         // Getting the available data in the Eth DMA buffer
         err = R_ETHER_Read(&this->ctrl, &rx_frame_buf, &rx_frame_dim);
         // DEBUG_INFO("[polling] read %08X, %u, %u", rx_frame_buf, rx_frame_dim, err);
         if(err != FSP_SUCCESS) {
-            NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(stats);
-            NETIF_STATS_INCREMENT_ERROR(stats, err);
-            NETIF_STATS_RX_TIME_AVERAGE(stats);
+            // NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(stats);
+            // NETIF_STATS_INCREMENT_ERROR(stats, err);
+            // NETIF_STATS_RX_TIME_AVERAGE(stats);
 
 
             // Error, discarding the buffer without consuming it
@@ -192,8 +192,8 @@ void EthernetC33Driver::poll() {
             this->consumed = false; // this indicates that the buffer had been consumed and the new buffer is allocated
         }
         err = R_ETHER_RxBufferUpdate(&ctrl, new_buffer);
-        NETIF_STATS_INCREMENT_ERROR(stats, err);
-        NETIF_STATS_RX_TIME_AVERAGE(stats);
+        // NETIF_STATS_INCREMENT_ERROR(stats, err);
+        // NETIF_STATS_RX_TIME_AVERAGE(stats);
         if(err != FSP_SUCCESS) {
             // DEBUG_INFO("%u", err); // FIXME handle this
         }

From f5633d6728a04326f4f8d12bcaa2c3c0a6d86603 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:27:27 +0100
Subject: [PATCH 13/79] defined network interface abstrac class

---
 libraries/NetworkAPI/src/interface.h | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/libraries/NetworkAPI/src/interface.h b/libraries/NetworkAPI/src/interface.h
index 7b9637ef9..7cf72e929 100644
--- a/libraries/NetworkAPI/src/interface.h
+++ b/libraries/NetworkAPI/src/interface.h
@@ -1 +1,14 @@
-#pragma once
\ No newline at end of file
+#pragma once
+
+/*
+ * The following class represent a generic network interface independently of the
+ * Network engine that is working on top of.
+ */
+class NetworkInterface {
+public:
+    virtual ~NetworkInterface() {};
+    virtual int begin(const IPAddress &ip = INADDR_NONE, const IPAddress &nm = INADDR_NONE, const IPAddress &gw = INADDR_NONE) = 0;
+    virtual void task() = 0;
+    virtual void up() = 0;
+    virtual void down() = 0;
+};
\ No newline at end of file

From 40772d1c9438dfb9bdd948132fb5c1d3dc1b4f15 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:28:05 +0100
Subject: [PATCH 14/79] fixing lwipClient

---
 libraries/lwIpWrapper/src/lwipClient.h | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/libraries/lwIpWrapper/src/lwipClient.h b/libraries/lwIpWrapper/src/lwipClient.h
index b087122f4..d0d4e569d 100644
--- a/libraries/lwIpWrapper/src/lwipClient.h
+++ b/libraries/lwIpWrapper/src/lwipClient.h
@@ -4,22 +4,23 @@
 #include <IPAddress.h>
 #include <Print.h>
 
-class lwipClient : public Client {
+// TODO improve documentation
 
+class lwipClient : public arduino::Client {
 public:
     lwipClient();
     lwipClient(uint8_t sock);
-    lwipClient(struct tcp_struct* tcpClient); // FIXME this should be a private constructor, friend of Server
+    lwipClient(struct tcp_pcb* tcpClient); // FIXME this should be a private constructor, friend of Server
 
     // disable copy constructor
-    LWIPTCPClient(const LWIPTCPClient&) = delete;
-    void operator=(const LWIPTCPClient&) = delete;
+    lwipClient(const lwipClient&) = delete;
+    void operator=(const lwipClient&) = delete;
 
     // keep move constructor
-    LWIPTCPClient(LWIPTCPClient&&);
-    void operator=(LWIPTCPClient&&);
+    lwipClient(lwipClient&&);
+    void operator=(lwipClient&&);
 
-    virtual ~LWIPTCPClient();
+    virtual ~lwipClient();
 
     virtual uint8_t status();
     virtual int connect(IPAddress ip, uint16_t port);
@@ -54,13 +55,13 @@ class lwipClient : public Client {
 
     uint8_t getSocketNumber();
     virtual uint16_t localPort() {
-        return (_tcp_client->pcb->local_port);
+        return (this->pcb->local_port);
     };
     virtual IPAddress remoteIP() {
-        return (IPAddress(_tcp_client->pcb->remote_ip.addr));
+        return (IPAddress(this->pcb->remote_ip.addr));
     };
     virtual uint16_t remotePort() {
-        return (_tcp_client->pcb->remote_port);
+        return (this->pcb->remote_port);
     };
     void setConnectionTimeout(uint16_t timeout) {
         _timeout = timeout;

From cef42dafae7f36661a802d5d434638db8164ea5d Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:28:36 +0100
Subject: [PATCH 15/79] fixing imports and variable names

---
 libraries/lwIpWrapper/src/lwipClient.cpp | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index c22237e2a..91919e995 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -5,7 +5,8 @@ extern "C" {
 #include "Arduino.h"
 
 #include "lwipClient.h"
-
+#include "CNetIf.h"
+#include "utils.h"
 // FIXME understand hos to syncronize the interrupt thread and "userspace"
 // TODO look into tcp_bind_netif for Ethernet and WiFiClient classes
 // TODO generalize the functions for extracting and inserting data into pbufs, they may be reused in UDP
@@ -203,7 +204,7 @@ size_t lwipClient::write(uint8_t b) {
     return write(&b, 1);
 }
 
-size_t lwipClient::write(const uint8_t* buf, size_t size) {
+size_t lwipClient::write(const uint8_t* buffer, size_t size) {
     arduino::lock();
 
     uint8_t* buffer_cursor = (uint8_t*)buffer;
@@ -244,23 +245,23 @@ int lwipClient::read() {
     return res == 1 ? c : res;
 }
 
-int lwipClient::read(uint8_t* buf, size_t size) {
-    if(buffer_size==0 || buffer==nullptr || this->pbuf_head==nullptr) {
+int lwipClient::read(uint8_t* buffer, size_t size) {
+    if(size==0 || buffer==nullptr || this->pbuf_head==nullptr) {
         return 0; // TODO extend checks
     }
     // copy data from the lwip buffer to the app provided buffer
     // TODO look into pbuf_get_contiguous(this->pbuf_head, buffer_cursor, len);
-    // pbuf_get_contiguous: returns the pointer to the payload if buffer_size <= pbuf.len
+    // pbuf_get_contiguous: returns the pointer to the payload if size <= pbuf.len
     //      otherwise copies data in the user provided buffer. This can be used in a callback paradigm,
     //      in order to avoid memcpy data
 
     /*
-     * a chain of pbuf is not granted to have a size multiple of buffer_size length
+     * a chain of pbuf is not granted to have a size multiple of size length
      * meaning that across different calls of this function a pbuf could be partially copied
      * we need to account that
      */
     arduino::lock();
-    uint16_t copied = pbuf_copy_partial(this->pbuf_head, buffer, buffer_size, this->pbuf_offset);
+    uint16_t copied = pbuf_copy_partial(this->pbuf_head, buffer, size, this->pbuf_offset);
 
     this->free_pbuf_chain(copied);
     // __enable_irq();

From e76ded75295b7938c5d415f95b8e7db80a74be40 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:29:18 +0100
Subject: [PATCH 16/79] cleaning CNetIf classes definition

---
 libraries/lwIpWrapper/src/CNetIf.h | 158 ++++++++++++++++-------------
 1 file changed, 88 insertions(+), 70 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index c3dd913d6..9dafca0da 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -1,5 +1,4 @@
-#ifndef _ARDUINO_LWIP_NETIF_H_
-#define _ARDUINO_LWIP_NETIF_H_
+#pragma once
 
 // #define LWIP_USE_TIMER
 #define UNUSED(x) (void)(x)
@@ -12,6 +11,8 @@
 #include "IPAddress.h"
 #include "EthernetDriver.h"
 #include <string>
+#include <interface.h>
+
 #ifdef USE_LWIP_AS_LIBRARY
 #include "lwip/include/lwip/dhcp.h"
 #include "lwip/include/lwip/dns.h"
@@ -27,7 +28,7 @@
 #include "lwIP_Arduino.h"
 #endif
 
-#define LWIP_USE_TIMER
+// #define LWIP_USE_TIMER
 
 #ifdef LWIP_USE_TIMER
 #include "FspTimer.h"
@@ -95,32 +96,18 @@ typedef enum {
 #define TRUNCATED -3
 #define INVALID_RESPONSE -4
 
-
-ip_addr_t* u8_to_ip_addr(uint8_t* ipu8, ip_addr_t* ipaddr);
-
-uint32_t ip_addr_to_u32(ip_addr_t* ipaddr);
+class CLwipIf;
 
 /* Base class implements DHCP, derived class will switch it on or off */
-class CNetIf {
-protected:
-    struct netif ni;
-
-#ifdef LWIP_DHCP
-    bool dhcp_acquired;
-#endif
-
-    // IPAddress _dnsServerAddress;
-
-    // Driver interface pointer
-    NetworkDriver *driver = nullptr;
+class CNetIf: public NetworkInterface {
 public:
-    CNetIf();
-    virtual ~CNetIf();
+    CNetIf(NetworkDriver *driver=nullptr);
+    virtual ~CNetIf() {}
     /*
      * The begin function is called by the user in the sketch to initialize the network interface
      * that he is planning on using in the sketch.
      */
-    virtual void begin(
+    virtual int begin(
         const IPAddress &ip = INADDR_NONE,
         const IPAddress &nm = INADDR_NONE,
         const IPAddress &gw = INADDR_NONE);
@@ -162,46 +149,72 @@ class CNetIf {
     uint32_t getNmAdd() { return ip4_addr_get_u32(&(ni.netmask)); }
     uint32_t getGwAdd() { return ip4_addr_get_u32(&(ni.gw)); }
 
-    void setHostname(const char* name)
-    {
-        memset(hostname, 0x00, MAX_HOSTNAME_DIM);
-        memcpy(hostname, name, strlen(name) < MAX_HOSTNAME_DIM ? strlen(name) : MAX_HOSTNAME_DIM);
-    }
+    // void setHostname(const char* name)
+    // {
+    //     memset(hostname, 0x00, MAX_HOSTNAME_DIM);
+    //     memcpy(hostname, name, strlen(name) < MAX_HOSTNAME_DIM ? strlen(name) : MAX_HOSTNAME_DIM);
+    // }
 
 
     virtual int getMacAddress(uint8_t* mac) = 0;
-};
 
-/* -------------------------------------------------------------------------- */
-class CEth : public CNetIf {
-    /* -------------------------------------------------------------------------- */
+    friend CLwipIf;
 protected:
+    struct netif ni;
+
+#ifdef LWIP_DHCP
+    bool dhcp_acquired;
+#endif
+
     /*
      * this function is used to initialize the netif structure of lwip
      */
-    static err_t init(struct netif* ni) override;
+    virtual err_t init(struct netif* ni) = 0;
 
     /*
      * This function is passed to lwip and used to send a buffer to the driver in order to transmit it
      */
-    static err_t output(struct netif* ni, struct pbuf* p) override;
-    static const char eth_ifname_prefix = 'e';
-    static uint8_t eth_id;
+    virtual err_t output(struct netif* ni, struct pbuf* p) = 0;
+
+    // the following functions are used to call init and output from lwip in the object context in the C code
+    friend err_t _netif_init(struct netif* ni);
+    friend err_t _netif_output(struct netif* ni, struct pbuf* p);
+
+    // IPAddress _dnsServerAddress;
+
+    // Driver interface pointer
+    NetworkDriver *driver = nullptr;
+
+    void linkDownCallback();
+    void linkUpCallback();
+};
+
+class CEth : public CNetIf {
 public:
-    CEth();
-    virtual ~CEth();
-    virtual void begin(
+    CEth(NetworkDriver *driver=nullptr);
+    // virtual ~CEth();
+    virtual int begin(
         const IPAddress &ip = INADDR_NONE,
         const IPAddress &nm = INADDR_NONE,
         const IPAddress &gw = INADDR_NONE) override;
 
-    virtual void task() override;
-
     virtual int getMacAddress(uint8_t* mac) override {
         UNUSED(mac); // FIXME not implemented
         return 1;
     }
+protected:
+    /*
+     * this function is used to initialize the netif structure of lwip
+     */
+    err_t init(struct netif* ni) override;
+
+    /*
+     * This function is passed to lwip and used to send a buffer to the driver in order to transmit it
+     */
+    err_t output(struct netif* ni, struct pbuf* p) override;
 
+    static const char eth_ifname_prefix = 'e';
+    static uint8_t eth_id;
 private:
     /*
      * This function is passed to the driver class and it is meant to
@@ -210,28 +223,11 @@ class CEth : public CNetIf {
     void consume_callback(uint8_t* buffer, uint32_t len);
 };
 
-/* -------------------------------------------------------------------------- */
 class CWifiStation : public CNetIf {
-    /* -------------------------------------------------------------------------- */
-protected:
-    static const char wifistation_ifname_prefix = 'w';
-    static uint8_t wifistation_id;
-
-    /*
-     * this function is used to initialize the netif structure of lwip
-     */
-    static err_t init(struct netif* ni) override;
-
-    /*
-     * This function is passed to lwip and used to send a buffer to the driver in order to transmit it
-     */
-    static err_t output(struct netif* ni, struct pbuf* p) override;
-
-    WifiApCfg_t access_point_cfg;
 public:
     CWifiStation();
     virtual ~CWifiStation();
-    virtual void begin(
+    virtual int begin(
         const IPAddress &ip = INADDR_NONE,
         const IPAddress &nm = INADDR_NONE,
         const IPAddress &gw = INADDR_NONE) override;
@@ -248,40 +244,62 @@ class CWifiStation : public CNetIf {
     virtual uint8_t* getBSSID(uint8_t* bssid);
     virtual int32_t getRSSI();
     virtual uint8_t getEncryptionType();
-};
-
-/* -------------------------------------------------------------------------- */
-class CWifiSoftAp : public CNetIf {
-    /* -------------------------------------------------------------------------- */
 protected:
     static const char wifistation_ifname_prefix = 'w';
     static uint8_t wifistation_id;
+
     /*
      * this function is used to initialize the netif structure of lwip
      */
-    static err_t init(struct netif* ni);
+    err_t init(struct netif* ni) override;
 
     /*
      * This function is passed to lwip and used to send a buffer to the driver in order to transmit it
      */
-    static err_t output(struct netif* ni, struct pbuf* p);
+    err_t output(struct netif* ni, struct pbuf* p) override;
 
-    SoftApCfg_t soft_ap_cfg;
+private:
+    std::vector<AccessPoint_t> access_points;
+    WifiApCfg_t access_point_cfg;
+    bool hw_init; // TODO this should be moved to the wifi driver class
+};
+
+class CWifiSoftAp : public CNetIf {
 public:
     CWifiSoftAp();
     virtual ~CWifiSoftAp();
-    virtual void begin(
+    virtual int begin(
         const IPAddress &ip = INADDR_NONE,
         const IPAddress &nm = INADDR_NONE,
         const IPAddress &gw = INADDR_NONE) override;
     virtual void task() override;
 
+    int startSoftAp(const char* ssid, const char* passphrase=nullptr, uint8_t channel=0);
+    int stopSoftAp();
+
     virtual int getMacAddress(uint8_t* mac) override;
 
     virtual const char* getSSID();
     virtual uint8_t* getBSSID(uint8_t* bssid);
     virtual int32_t getRSSI();
     virtual uint8_t getEncryptionType();
+protected:
+    static const char softap_ifname_prefix = 's';
+    static uint8_t softap_id;
+    /*
+     * this function is used to initialize the netif structure of lwip
+     */
+    err_t init(struct netif* ni);
+
+    /*
+     * This function is passed to lwip and used to send a buffer to the driver in order to transmit it
+     */
+    err_t output(struct netif* ni, struct pbuf* p);
+
+private:
+    std::vector<AccessPoint_t> access_points;
+    SoftApCfg_t soft_ap_cfg;
+    bool hw_init; // TODO this should be moved to the wifi driver class
 };
 
 class CLwipIf {
@@ -307,6 +325,7 @@ class CLwipIf {
 
     // function for setting an iface as default
     void setDefaultIface(CNetIf* iface);
+    // TODO get iface method
 
     // functions that handle DNS resolution
     // DNS servers are also set by dhcp
@@ -321,6 +340,7 @@ class CLwipIf {
 #endif
 private:
     CLwipIf();
+    ~CLwipIf();
 
     // TODO define a Timer for calling tasks
 
@@ -333,9 +353,7 @@ class CLwipIf {
 
     friend class CNetIf;
 
-#ifdef NETWORKSTACK_USE_TIMER
+#ifdef LWIP_USE_TIMER
     FspTimer timer;
 #endif
 };
-
-#endif

From d39a90ae1f20f8b32f64336460bc2b3fa718f15b Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:30:11 +0100
Subject: [PATCH 17/79] fixing class implementation and making it able to
 compile

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 188 +++++++++++++++------------
 1 file changed, 105 insertions(+), 83 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 86f7413e1..e14ab239c 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -1,5 +1,6 @@
 #include "CNetIf.h"
 #include <functional>
+#include "utils.h"
 
 // TODO make better documentation on how this works
 // TODO hostname should be defined at network stack level and shared among ifaces
@@ -10,6 +11,8 @@
 // TODO split netif definition in different files
 // TODO implement WIFINetworkDriver that is then being used by both Wifi station and softAP. This will allow to use both at the same time
 
+extern "C" void dhcps_start(struct netif *netif); // TODO understand why not include
+
 err_t _netif_init(struct netif* ni);
 err_t _netif_output(struct netif* ni, struct pbuf* p);
 
@@ -17,6 +20,10 @@ err_t _netif_output(struct netif* ni, struct pbuf* p);
 static void _getHostByNameCBK(const char *name, const ip_addr_t *ipaddr, void *callback_arg);
 #endif // LWIP_DNS
 
+#ifdef LWIP_USE_TIMER
+static void timer_cb(timer_callback_args_t* arg);
+#endif
+
 // Custom Pbuf definition used to handle RX zero copy
 // TODO Move this in a separate file (understand if it is required)
 typedef struct zerocopy_pbuf {
@@ -79,7 +86,7 @@ CLwipIf::CLwipIf() {
        initialized just once  */
     lwip_init();
 
-#ifdef NETWORKSTACK_USE_TIMER
+#ifdef LWIP_USE_TIMER
     uint8_t type = 8;
     int8_t ch = FspTimer::get_available_timer(type);
 
@@ -88,6 +95,7 @@ CLwipIf::CLwipIf() {
     }
 
     /*
+     * FIXME update this comment
      * NOTE Timer and buffer size
      * The frequency for the timer highly influences the memory requirements for the desired transfer speed
      * You can calculate the buffer size required to achieve that performance from the following formula:
@@ -106,6 +114,14 @@ CLwipIf::CLwipIf() {
 #endif
 }
 
+#ifdef LWIP_USE_TIMER
+static void timer_cb(timer_callback_args_t* arg) {
+    CLwipIf* context = (CLwipIf*)arg->p_context;
+
+    context->task();
+}
+#endif
+
 void CLwipIf::task() {
     for(CNetIf* iface: this->ifaces) { // FIXME is this affecting performances?
         iface->task();
@@ -116,18 +132,18 @@ void CLwipIf::task() {
     arduino::unlock();
 }
 
-void CLwipIf::setDefaultIface(CNetif* iface) {
+void CLwipIf::setDefaultIface(CNetIf* iface) {
     // TODO check if the iface is in the vector
 
     netif_set_default(&iface->ni);
 }
 
-void CLwipIf::add_iface(CNetif* iface) {
+void CLwipIf::add_iface(CNetIf* iface) {
     // if it is the first interface set it as the default route
     if(this->ifaces.empty()) {
         netif_set_default(&iface->ni); // TODO let the user decide which is the default one
 
-#ifdef NETWORKSTACK_USE_TIMER
+#ifdef LWIP_USE_TIMER
         timer.setup_overflow_irq();
         timer.open();
         timer.start();
@@ -143,13 +159,13 @@ CLwipIf::~CLwipIf() {
 }
 
 
-int CLwipIf::setWifiMode(WifiMode_t mode) {
+// int CLwipIf::setWifiMode(WifiMode_t mode) {
     // TODO adapt this
     // CLwipIf::getInstance().startSyncRequest();
     // int rv = CEspControl::getInstance().setWifiMode(mode);
     // CLwipIf::getInstance().restartAsyncRequest();
     // return rv;
-}
+// }
 
 /* ***************************************************************************
  *                               DNS related functions
@@ -259,10 +275,10 @@ int CLwipIf::getHostByName(const char* aHostname, std::function<void(const IPAdd
  *                      BASE NETWORK INTERFACE CLASS
  * ########################################################################## */
 
-CNetIf::CNetIf()
-:
+CNetIf::CNetIf(NetworkDriver *driver)
+: driver(driver)
 #ifdef LWIP_DHCP
-    dhcp_acquired(false)
+    , dhcp_acquired(false)
 #endif
 {
     // NETIF_STATS_INIT(this->stats); // TODO create a proper stats interface
@@ -275,7 +291,9 @@ CNetIf::CNetIf()
     }
 }
 
-void CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
+int CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
+    driver->begin();
+
     ip_addr_t _ip = fromArduinoIP(ip);
     ip_addr_t _nm = fromArduinoIP(nm);
     ip_addr_t _gw = fromArduinoIP(gw);
@@ -290,7 +308,7 @@ void CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw
     );
     if(_ni == nullptr) {
         // FIXME error if netif_add, return error
-        return;
+        return -1;
     }
 
     //TODO add link up and down callback and set the link
@@ -307,9 +325,10 @@ void CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw
 
     // add the interface to the network stack
     CLwipIf::getInstance().add_iface(this); // TODO remove interface when it is needed (??)
+    return 0;
 }
 
-void CNetif::task() {
+void CNetIf::task() {
 #ifdef LWIP_DHCP
     // TODO we can add a lazy evaluated timer for this condition if dhcp_supplied_address takes too long
     if(!this->dhcp_acquired && dhcp_supplied_address(&this->ni)) {
@@ -324,13 +343,13 @@ void CNetif::task() {
 
 
 err_t _netif_init(struct netif* ni) {
-    CNetif *iface = (CNetif*)ni->state;
+    CNetIf *iface = (CNetIf*)ni->state;
 
     return iface->init(ni); // This function call can be a jmp instruction
 }
 
 err_t _netif_output(struct netif* ni, struct pbuf* p) {
-    CNetif *iface = (CNetif*)ni->state;
+    CNetIf *iface = (CNetIf*)ni->state;
 
     return iface->output(ni, p); // This function call can be a jmp instruction
 }
@@ -343,6 +362,14 @@ void CNetIf::down() {
     netif_set_down(&this->ni);
 }
 
+void CNetIf::setLinkUp() {
+    this->up();
+}
+
+void CNetIf::setLinkDown() {
+    this->down();
+}
+
 void CNetIf::linkUpCallback() {
     netif_set_link_up(&this->ni); // TODO check that this sets the interface up also
 }
@@ -392,20 +419,22 @@ bool CNetIf::dhcpRenew() {
  * ########################################################################## */
 uint8_t CEth::eth_id = 0;
 
-CEth::CEth() {
-    CNetif::driver = &C33EthernetDriver; // FIXME driver is the pointer to C33 ethernet driver implementation
-    C33EthernetDriver.stats = &this->stats;
+CEth::CEth(NetworkDriver *driver)
+: CNetIf(driver) {
+    // driver.stats = &this->stats;
 }
 
-void CEth::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
+int CEth::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
     // The driver needs a callback to consume the incoming buffer
     this->driver->setConsumeCallback(
         std::bind(&CEth::consume_callback,
             this, std::placeholders:: _1, std::placeholders::_2));
 
     // Call the begin function on the Parent class to init the interface
-    CNetif::begin(ip, nm, gw);
+    CNetIf::begin(ip, nm, gw);
     netif_set_link_up(&this->ni); // TODO test that moving this here still makes ethernet work
+
+    return 0;
 }
 
 
@@ -442,23 +471,18 @@ err_t CEth::output(struct netif* ni, struct pbuf* p) {
      */
     struct pbuf *q = p;
     do {
-        NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
-        NETIF_STATS_TX_TIME_START(this->stats);
-        auto err = C33EthernetDriver.send((uint8_t*)q->payload, q->len);
+        // NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
+        // NETIF_STATS_TX_TIME_START(this->stats);
+        auto err = driver->send((uint8_t*)q->payload, q->len);
         if(err != 0) {
-            NETIF_STATS_INCREMENT_ERROR(this->stats, err);
-            NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
+            // NETIF_STATS_INCREMENT_ERROR(this->stats, err);
+            // NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
             errval = ERR_IF;
             break;
         }
-        NETIF_STATS_INCREMENT_TX_BYTES(this->stats, q->len);
-        NETIF_STATS_TX_TIME_AVERAGE(this->stats);
+        // NETIF_STATS_INCREMENT_TX_BYTES(this->stats, q->len);
+        // NETIF_STATS_TX_TIME_AVERAGE(this->stats);
         q = q->next;
-
-        // FIXME remove this, only purpose is to verify if I ever deal with a pbuf chain
-        // if(q!=nullptr) {
-        //     NETIF_STATS_INCREMENT_ERROR(this->stats, 1024);
-        // }
     } while(q != nullptr && errval != ERR_OK);
 
     return errval;
@@ -482,13 +506,13 @@ void CEth::consume_callback(uint8_t* buffer, uint32_t len) {
 
     err_t err = this->ni.input((struct pbuf*)p, &this->ni);
     if (err != ERR_OK) {
-        NETIF_STATS_INCREMENT_ERROR(this->stats, err);
+        // NETIF_STATS_INCREMENT_ERROR(this->stats, err);
 
-        NETIF_STATS_INCREMENT_RX_NI_INPUT_FAILED_CALLS(this->stats);
-        NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(this->stats);
+        // NETIF_STATS_INCREMENT_RX_NI_INPUT_FAILED_CALLS(this->stats);
+        // NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(this->stats);
         pbuf_free((struct pbuf*)p);
     } else {
-        NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len);
+        // NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len);
     }
     // arduino::unlock();
 }
@@ -504,6 +528,10 @@ CWifiStation::CWifiStation()
     // CLwipIf::getInstance()
 }
 
+CWifiStation::~CWifiStation() {
+
+}
+
 int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) { // TODO This should be called only once, make it private
     int res = 0;
     int time_num = 0;
@@ -531,7 +559,7 @@ int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddres
     }
 
     res = CEspControl::getInstance().setWifiMode(WIFI_MODE_STA);
-    CNetif::begin(ip, nm, gw);
+    CNetIf::begin(ip, nm, gw);
     // netif_set_link_up(&this->ni); // TODO this should be set only when successfully connected to an AP
 exit:
     // arduino::unlock();
@@ -553,7 +581,6 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
         // rv = -1; // FIXME set proper error code
         goto exit;
     }
-    this->printAps();
 
     // find the AP with the best rssi
     for (uint8_t i = 0; i < access_points.size(); i++) {
@@ -635,22 +662,22 @@ err_t CWifiStation::init(struct netif* ni) {
 
 err_t CWifiStation::output(struct netif* _ni, struct pbuf* p) {
     // FIXME set ifn
-    int ifn = 0; // interface number in CNetif.cpp seems to not be set anywhere
+    int ifn = 0; // interface number in CNetIf.cpp seems to not be set anywhere
     uint8_t *buf = nullptr;
     uint16_t size=p->tot_len;
     err_t errval = ERR_IF;
     int err = ESP_CONTROL_OK;
 
-    NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
-    NETIF_STATS_TX_TIME_START(this->stats);
+    // NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
+    // NETIF_STATS_TX_TIME_START(this->stats);
 
     // arduino::lock();
     // p may be a chain of pbufs
     if(p->next != nullptr) {
         buf = (uint8_t*) malloc(size*sizeof(uint8_t));
         if(buf == nullptr) {\
-            NETIF_STATS_INCREMENT_ERROR(this->stats, ERR_MEM);
-            NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
+            // NETIF_STATS_INCREMENT_ERROR(this->stats, ERR_MEM);
+            // NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
             errval = ERR_MEM;
             goto exit;
         }
@@ -666,11 +693,11 @@ err_t CWifiStation::output(struct netif* _ni, struct pbuf* p) {
     if ((err = CEspControl::getInstance().sendBuffer(
             ESP_STA_IF, ifn, buf, size)) == ESP_CONTROL_OK) {
         errval = ERR_OK;
-        NETIF_STATS_INCREMENT_TX_BYTES(this->stats, size);
-        NETIF_STATS_TX_TIME_AVERAGE(this->stats);
+        // NETIF_STATS_INCREMENT_TX_BYTES(this->stats, size);
+        // NETIF_STATS_TX_TIME_AVERAGE(this->stats);
     } else {
-        NETIF_STATS_INCREMENT_ERROR(this->stats, err);
-        NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
+        // NETIF_STATS_INCREMENT_ERROR(this->stats, err);
+        // NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
     }
 
 exit:
@@ -683,7 +710,7 @@ err_t CWifiStation::output(struct netif* _ni, struct pbuf* p) {
 
 void CWifiStation::task() {
     // calling the base class task, in order to make thigs work
-    CNetif::task();
+    CNetIf::task();
 
     // TODO in order to make things easier this should be implemented inside of Wifi driver
     // and not override LWIPInterface method
@@ -693,7 +720,7 @@ void CWifiStation::task() {
     uint8_t* buffer = nullptr;
     struct pbuf* p = nullptr;
 
-    NETIF_STATS_RX_TIME_START(this->stats);
+    // NETIF_STATS_RX_TIME_START(this->stats);
     // arduino::lock();
     // TODO do not perform this when not connected to an AP
     if(hw_init) {
@@ -707,8 +734,7 @@ void CWifiStation::task() {
     while(buffer != nullptr) {
         // FIXME this section is redundant and should be generalized toghether with CEth::consume_callback
         // TODO understand if this should be moved into the base class
-        NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(this->stats);
-        // NETIF_STATS_RX_TIME_START(this->stats);
+        // NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(this->stats);
 
         zerocopy_pbuf_t *custom_pbuf = get_zerocopy_pbuf(buffer, dim, free);
 
@@ -718,25 +744,24 @@ void CWifiStation::task() {
 
         err_t err = this->ni.input((struct pbuf*)p, &this->ni);
         if (err != ERR_OK) {
-            NETIF_STATS_INCREMENT_ERROR(this->stats, err);
+            // NETIF_STATS_INCREMENT_ERROR(this->stats, err);
 
-            NETIF_STATS_INCREMENT_RX_NI_INPUT_FAILED_CALLS(this->stats);
-            NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(this->stats);
+            // NETIF_STATS_INCREMENT_RX_NI_INPUT_FAILED_CALLS(this->stats);
+            // NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(this->stats);
             pbuf_free((struct pbuf*)p);
         } else {
-            NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len);
+            // NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len);
         }
 
         buffer = CEspControl::getInstance().getStationRx(if_num, dim);
-        // NETIF_STATS_RX_TIME_AVERAGE(this->stats);
     }
-    NETIF_STATS_RX_TIME_AVERAGE(this->stats);
+    // NETIF_STATS_RX_TIME_AVERAGE(this->stats);
     // arduino::unlock();
 }
 
-void CWifiStation::consume_callback(uint8_t* buffer, uint32_t len) {
-    // FIXME take what is written in task and put it in here
-}
+// void CWifiStation::consume_callback(uint8_t* buffer, uint32_t len) {
+//     // FIXME take what is written in task and put it in here
+// }
 
 const char* CWifiStation::getSSID() {
     return (const char*)access_point_cfg.ssid;
@@ -766,10 +791,6 @@ uint8_t CWifiStation::getEncryptionType() {
 /* ########################################################################## */
 /*                      CWifiSoftAp NETWORK INTERFACE CLASS                   */
 /* ########################################################################## */
-
-CWifiSoftAp::CWifiSoftAp() { }
-CWifiSoftAp::~CWifiSoftAp() { }
-
 uint8_t CWifiSoftAp::softap_id = 0;
 
 // This is required for dhcp server to assign ip addresses to AP clients
@@ -781,6 +802,9 @@ CWifiSoftAp::CWifiSoftAp()
 
 }
 
+CWifiSoftAp::~CWifiSoftAp() { }
+
+
 int CWifiSoftAp::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) { // TODO use provided ip address, instead of default ones
     int res = 0;
     int time_num = 0;
@@ -806,7 +830,7 @@ int CWifiSoftAp::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress
     res = CEspControl::getInstance().setWifiMode(WIFI_MODE_AP);
 
     // netif_set_link_up(&this->ni); // TODO this should be set only when successfully connected to an AP
-    CNetif::begin(
+    CNetIf::begin(
         default_dhcp_server_ip,
         default_nm,
         default_dhcp_server_ip
@@ -885,22 +909,22 @@ err_t CWifiSoftAp::init(struct netif* ni) {
 
 err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
     // FIXME set ifn
-    int ifn = 0; // interface number in CNetif.cpp seems to not be set anywhere
+    int ifn = 0; // interface number in CNetIf.cpp seems to not be set anywhere
     uint8_t *buf = nullptr;
     uint16_t size=p->tot_len;
     err_t errval = ERR_IF;
     int err = ESP_CONTROL_OK;
 
-    NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
-    NETIF_STATS_TX_TIME_START(this->stats);
+    // NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
+    // NETIF_STATS_TX_TIME_START(this->stats);
 
     // arduino::lock();
     // p may be a chain of pbufs
     if(p->next != nullptr) {
         buf = (uint8_t*) malloc(size*sizeof(uint8_t));
         if(buf == nullptr) {\
-            NETIF_STATS_INCREMENT_ERROR(this->stats, ERR_MEM);
-            NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
+            // NETIF_STATS_INCREMENT_ERROR(this->stats, ERR_MEM);
+            // NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
             errval = ERR_MEM;
             goto exit;
         }
@@ -916,11 +940,11 @@ err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
     if ((err = CEspControl::getInstance().sendBuffer(
             ESP_AP_IF, ifn, buf, size)) == ESP_CONTROL_OK) {
         errval = ERR_OK;
-        NETIF_STATS_INCREMENT_TX_BYTES(this->stats, size);
-        NETIF_STATS_TX_TIME_AVERAGE(this->stats);
+        // NETIF_STATS_INCREMENT_TX_BYTES(this->stats, size);
+        // NETIF_STATS_TX_TIME_AVERAGE(this->stats);
     } else {
-        NETIF_STATS_INCREMENT_ERROR(this->stats, err);
-        NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
+        // NETIF_STATS_INCREMENT_ERROR(this->stats, err);
+        // NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
     }
 
 exit:
@@ -933,7 +957,7 @@ err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
 
 void CWifiSoftAp::task() {
     // calling the base class task, in order to make thigs work
-    CNetif::task();
+    CNetIf::task();
 
     // TODO in order to make things easier this should be implemented inside of Wifi driver
     // and not override LWIPInterface method
@@ -943,7 +967,7 @@ void CWifiSoftAp::task() {
     uint8_t* buffer = nullptr;
     struct pbuf* p = nullptr;
 
-    NETIF_STATS_RX_TIME_START(this->stats);
+    // NETIF_STATS_RX_TIME_START(this->stats);
     // arduino::lock();
     // TODO do not perform this when not connected to an AP
     if(hw_init) {
@@ -957,8 +981,7 @@ void CWifiSoftAp::task() {
     while(buffer != nullptr) {
         // FIXME this section is redundant and should be generalized toghether with CEth::consume_callback
         // TODO understand if this should be moved into the base class
-        NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(this->stats);
-        // NETIF_STATS_RX_TIME_START(this->stats);
+        // NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(this->stats);
 
         zerocopy_pbuf_t *custom_pbuf = get_zerocopy_pbuf(buffer, dim, free);
 
@@ -968,19 +991,18 @@ void CWifiSoftAp::task() {
 
         err_t err = this->ni.input((struct pbuf*)p, &this->ni);
         if (err != ERR_OK) {
-            NETIF_STATS_INCREMENT_ERROR(this->stats, err);
+            // NETIF_STATS_INCREMENT_ERROR(this->stats, err);
 
-            NETIF_STATS_INCREMENT_RX_NI_INPUT_FAILED_CALLS(this->stats);
-            NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(this->stats);
+            // NETIF_STATS_INCREMENT_RX_NI_INPUT_FAILED_CALLS(this->stats);
+            // NETIF_STATS_INCREMENT_RX_INTERRUPT_FAILED_CALLS(this->stats);
             pbuf_free((struct pbuf*)p);
         } else {
-            NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len);
+            // NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len);
         }
 
         buffer = CEspControl::getInstance().getStationRx(if_num, dim);
-        // NETIF_STATS_RX_TIME_AVERAGE(this->stats);
     }
-    NETIF_STATS_RX_TIME_AVERAGE(this->stats);
+    // NETIF_STATS_RX_TIME_AVERAGE(this->stats);
     // arduino::unlock();
 }
 

From a28de2dacae81ff83c764504e4c4ba6e30479932 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:30:47 +0100
Subject: [PATCH 18/79] defined tmp utils library

---
 libraries/lwIpWrapper/src/utils.h | 84 +++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 libraries/lwIpWrapper/src/utils.h

diff --git a/libraries/lwIpWrapper/src/utils.h b/libraries/lwIpWrapper/src/utils.h
new file mode 100644
index 000000000..e4a2ac1b6
--- /dev/null
+++ b/libraries/lwIpWrapper/src/utils.h
@@ -0,0 +1,84 @@
+#pragma once
+#include <Arduino.h>
+#include "lwip/include/lwip/ip_addr.h"
+
+inline ip_addr_t fromArduinoIP(const IPAddress& ip) {
+#if LWIP_IPV4
+    ip_addr_t res;
+    if(ip.type() == arduino::IPv4) {
+        if(ip == INADDR_NONE) {
+            ip_addr_copy(res, *IP4_ADDR_ANY);
+        } else {
+            IP_ADDR4(&res, ip[0], ip[1], ip[2], ip[3]);
+        }
+    }
+#endif // LWIP_IPV4
+#if LWIP_IPV4 && LWIP_IPV6
+    else
+#endif // LWIP_IPV4 && LWIP_IPV6
+#if LWIP_IPV6 // TODO change the setting and try ipv6: This is currently set to 0
+    if(ip.type() == arduino::IPv6) {
+        if(ip == INADDR_NONE) {
+            // ip_addr_copy(res, *IP6_ADDR_ANY);
+            // FIXME implement this
+        } else {
+            // FIXME implement this, it could be useful to have a function in the IPAddress class to help this out
+        }
+    }
+#endif // LWIP_IPV6
+    return res;
+}
+
+inline IPAddress toArduinoIP(const ip_addr_t* ip) {
+    if(ip == nullptr) {
+        return INADDR_NONE;
+    }
+
+#if LWIP_IPV4
+    if(IP_IS_V4(ip)) {
+        if(ip_addr_isany_val(*ip)) {
+            return INADDR_NONE;
+        } else {
+            return IPAddress(arduino::IPv4, (uint8_t*)&ip_2_ip4(ip)->addr);
+        }
+    }
+#endif // LWIP_IPV4
+
+#if LWIP_IPV6 // TODO change the setting and try ipv6: This is currently set to 0
+    if(IP_IS_V6(ip)) {
+        if(ip_addr_isany_val(*ip)) {
+            return IN6ADDR_ANY;
+        } else {
+            return IPAddress(arduino::IPv6, (uint8_t*)ip_2_ip6(ip)->addr);
+        }
+    }
+#endif
+
+#if LWIP_IPV4 && LWIP_IPV6
+    if(IP_IS_ANY_TYPE_VAL(ip)) {
+        // FIXME understand what this means
+    }
+#endif
+
+    return INADDR_NONE;
+}
+
+
+namespace arduino {
+    // TODO leverage on RAII
+    inline volatile uint32_t lock_counter;
+    inline void lock() {
+        __disable_irq();
+        lock_counter++; // This action breaks everything
+    }
+
+    inline void unlock() {
+        if(lock_counter > 0) {
+            lock_counter--;
+        }
+
+        if(lock_counter == 0) {
+            __enable_irq(); // this could be called multiple times if the calls are not setup properly
+        }
+    }
+}
\ No newline at end of file

From 460a0d5c2df9929b982f9f9ef4d5d3c66da4a7a2 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:31:18 +0100
Subject: [PATCH 19/79] reimplementing wifi handle

---
 libraries/WiFi/src/WiFiC3.h | 267 +-----------------------------------
 1 file changed, 7 insertions(+), 260 deletions(-)

diff --git a/libraries/WiFi/src/WiFiC3.h b/libraries/WiFi/src/WiFiC3.h
index 2995cdcf9..ffe55cbf3 100644
--- a/libraries/WiFi/src/WiFiC3.h
+++ b/libraries/WiFi/src/WiFiC3.h
@@ -1,267 +1,14 @@
-#ifndef C_ARDUINO_WIFI_H
-#define C_ARDUINO_WIFI_H
+#pragma once
 
 #include "CNetIf.h"
 
-#define WIFI_FIRMWARE_LATEST_VERSION "1.5.0"
-
-class CWifi {
-private: 
-   void _config(IPAddress local_ip, IPAddress gateway, IPAddress subnet);
-   unsigned long _timeout;
-   bool _useStaticIp = false;
-   CNetIf *ni;
-    
-public:
-    CWifi();
-
-    /*
-     * Get firmware version
-     */
-    static const char* firmwareVersion();
-
-
-    /* 
-     * Start WiFi connection for OPEN networks 
-     * param ssid: Pointer to the SSID string.
-     */
-    int begin(const char* ssid);
-
-    
-
-    /* Start WiFi connection with passphrase
-     * the most secure supported mode will be automatically selected
-     *
-     * param ssid: Pointer to the SSID string.
-     * param passphrase: Passphrase. Valid characters in a passphrase
-     *        must be between ASCII 32-126 (decimal).
-     */
-    int begin(const char* ssid, const char *passphrase);
-
-    /* connect as Access Point with  a standard passphrase */
-    uint8_t beginAP(const char *ssid);
-    uint8_t beginAP(const char *ssid, uint8_t channel);
-    uint8_t beginAP(const char *ssid, const char* passphrase);
-    uint8_t beginAP(const char *ssid, const char* passphrase, uint8_t channel);
-
-    /* Change IP configuration settings disabling the DHCP client
-        *
-        * param local_ip:  Static IP configuration
-        */
-    void config(IPAddress local_ip);
-
-    /* Change IP configuration settings disabling the DHCP client
-        *
-        * param local_ip:  Static IP configuration
-   * param dns_server:     IP configuration for DNS server 1
-        */
-    void config(IPAddress local_ip, IPAddress dns_server);
-
-    /* Change IP configuration settings disabling the DHCP client
-        *
-        * param local_ip:  Static IP configuration
-   * param dns_server:     IP configuration for DNS server 1
-        * param gateway :  Static gateway configuration
-        */
-    void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway);
-
-    /* Change IP configuration settings disabling the DHCP client
-        *
-        * param local_ip:  Static IP configuration
-   * param dns_server:     IP configuration for DNS server 1
-        * param gateway:   Static gateway configuration
-        * param subnet:    Static Subnet mask
-        */
-    void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet);
-    
-    /* Change DNS IP configuration
-     *
-     * param dns_server1: IP configuration for DNS server 1
-     */
-    void setDNS(IPAddress dns_server1);
-
-    /* Change DNS IP configuration
-     *
-     * param dns_server1: IP configuration for DNS server 1
-     * param dns_server2: IP configuration for DNS server 2
-     *
-     */
-    void setDNS(IPAddress dns_server1, IPAddress dns_server2);
-
-
-    /* Set the hostname used for DHCP requests
-     *
-     * param name: hostname to set
-     *
-     */
-    void setHostname(const char* name);
-
-    /*
-     * Disconnect from the network
-     *
-     * return: one value of wl_status_t enum
-     */
-    int disconnect(void);
-
-    void end(void);
-
-    /*
-     * Get the interface MAC address.
-     *
-     * return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
-     * 
-     * the value returned by this function is meaningfull only if called 
-     * afert a begin (both begin or beginAP) or a ScanNetwork function
-     * otherwise an empty mac address is returned
-     */
-    uint8_t* macAddress(uint8_t* mac);
-
-    /*
-     * Get the interface IP address.
-     *
-     * return: IP address value
-     */
-    IPAddress localIP();
-
-    /*
-     * Get the interface subnet mask address.
-     *
-     * return: subnet mask address value
-     */
-    IPAddress subnetMask();
-
-    /*
-     * Get the gateway IP address.
-     *
-     * return: gateway IP address value
-     */
-   IPAddress gatewayIP();
-
-   /*
-    * Get the DNS server IP address.
-    *
-    * return: DNS server IP address value
-    */
-   IPAddress dnsIP(int n = 0);
-
-    /*
-     * Return the current SSID associated with the network
-     *
-     * return: ssid string
-     */
-    const char* SSID();
-
-    /*
-      * Return the current BSSID associated with the network.
-      * It is the MAC address of the Access Point
-      *
-      * return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
-      */
-    uint8_t* BSSID(uint8_t* bssid);
-
-    /*
-      * Return the current RSSI/Received Signal Strength in dBm)
-      * associated with the network
-      *
-      * return: signed value
-      */
-    int32_t RSSI();
-
-    /*
-      * Return the Encryption Type associated with the network
-      *
-      * return: one value of wl_enc_type enum
-      */
-    uint8_t encryptionType();
-
-    /*
-     * Start scan WiFi networks available
-     *
-     * return: Number of discovered networks
-     */
-    int8_t scanNetworks();
-
-    /*
-     * Return the SSID discovered during the network scan.
-     *
-     * param networkItem: specify from which network item want to get the information
-    *
-     * return: SSID string of the specified item on the networks scanned list
-     */
-    const char*   SSID(uint8_t networkItem);
-
-    /*
-     * Return the encryption type of the networks discovered during the scanNetworks
-     *
-     * param networkItem: specify from which network item want to get the information
-    *
-     * return: encryption type (enum wl_enc_type) of the specified item on the networks scanned list
-
-     enum wl_enc_type : 
-      ENC_TYPE_WEP,
-      ENC_TYPE_WPA,
-      ENC_TYPE_WPA2,
-      ENC_TYPE_WPA2_ENTERPRISE,
-      ENC_TYPE_WPA3,
-      ENC_TYPE_NONE,
-      ENC_TYPE_AUTO,
-
-      ENC_TYPE_UNKNOWN = 255
-      
-     */
-    uint8_t encryptionType(uint8_t networkItem);
-
-    uint8_t* BSSID(uint8_t networkItem, uint8_t* bssid);
-    uint8_t channel(uint8_t networkItem);
-
-    /*
-     * Return the RSSI of the networks discovered during the scanNetworks
-     *
-     * param networkItem: specify from which network item want to get the information
-    *
-     * return: signed value of RSSI of the specified item on the networks scanned list
-     */
-    int32_t RSSI(uint8_t networkItem);
-
-    /*
-     * Return Connection status.
-     *
-     * return: one of the value defined in wl_status_t
-     */
-    uint8_t status();
-
-    /*
-     * Return The deauthentication reason code.
-     *
-     * return: the deauthentication reason code
-     */
-    uint8_t reasonCode();
-
-    /*
-     * Resolve the given hostname to an IP address.
-     * param aHostname: Name to be resolved
-     * param aResult: IPAddress structure to store the returned IP address
-     * result: 1 if aIPAddrString was successfully converted to an IP address,
-     *          else error code
-     */
-    int hostByName(const char* aHostname, IPAddress& aResult);
-
-    unsigned long getTime();
-
-    void lowPowerMode();
-    void noLowPowerMode();
-
-    
-
-    void setTimeout(unsigned long timeout);
-
-    
-};
-
-extern CWifi WiFi;
-
 #include "WiFiClient.h"
 #include "WiFiServer.h"
 #include "WiFiUdp.h"
 
-#endif
+#define WIFI_FIRMWARE_LATEST_VERSION "1.5.0"
+
+// TODO Instantiate the drivers for wifi with default configuration parameters
+
+// Instantiate a global variable from CWifiStation calling it WiFi
+

From ae29df794ff14baf1da8d939d58db2e79a211bc6 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 29 Dec 2023 13:34:09 +0100
Subject: [PATCH 20/79] commnting code not yet ready to be compiled

---
 libraries/Ethernet/src/EthernetClient.h  |  2 +-
 libraries/lwIpWrapper/src/lwipServer.cpp | 37 ++++++++++++------------
 libraries/lwIpWrapper/src/lwipUDP.cpp    | 26 ++++++++---------
 3 files changed, 33 insertions(+), 32 deletions(-)

diff --git a/libraries/Ethernet/src/EthernetClient.h b/libraries/Ethernet/src/EthernetClient.h
index dbfa0012d..c3b16242a 100644
--- a/libraries/Ethernet/src/EthernetClient.h
+++ b/libraries/Ethernet/src/EthernetClient.h
@@ -7,7 +7,7 @@
 class EthernetClient : public lwipClient {
    public:
    EthernetClient() {}
-   EthernetClient(struct tcp_struct *tcpClient) : lwipClient(tcpClient) {}
+   EthernetClient(struct tcp_struct *tcpClient) : lwipClient() {} // FIXME
 };
 
 #endif
\ No newline at end of file
diff --git a/libraries/lwIpWrapper/src/lwipServer.cpp b/libraries/lwIpWrapper/src/lwipServer.cpp
index be42b2a43..768b5887e 100644
--- a/libraries/lwIpWrapper/src/lwipServer.cpp
+++ b/libraries/lwIpWrapper/src/lwipServer.cpp
@@ -51,11 +51,11 @@ void lwipServer::accept()
     /* Free client if disconnected */
     for (int n = 0; n < MAX_CLIENT; n++) {
         if (_tcp_client[n] != NULL) {
-            lwipClient client(_tcp_client[n]);
-            if (client.status() == TCP_CLOSING) {
-                mem_free(_tcp_client[n]);
-                _tcp_client[n] = NULL;
-            }
+            // lwipClient client(_tcp_client[n]); // FIXME
+            // if (client.status() == TCP_CLOSING) {
+            //     mem_free(_tcp_client[n]);
+            //     _tcp_client[n] = NULL;
+            // }
         }
     }
 }
@@ -67,19 +67,20 @@ lwipClient lwipServer::available()
     for (int n = 0; n < MAX_CLIENT; n++) {
         if (_tcp_client[n] != NULL) {
             if (_tcp_client[n]->pcb != NULL) {
-                lwipClient client(_tcp_client[n]);
-                uint8_t s = client.status();
-                if (s == TCP_ACCEPTED) {
-                    if (client.available()) {
-                        return client;
-                    }
-                }
+                // lwipClient client(_tcp_client[n]); // FIXME
+                // uint8_t s = client.status();
+                // if (s == TCP_ACCEPTED) {
+                //     if (client.available()) {
+                //         return client;
+                //     }
+                // }
             }
         }
     }
 
     struct tcp_struct* default_client = NULL;
-    return lwipClient(default_client);
+    return lwipClient(); // FIXME
+    // return lwipClient(default_client);
 }
 
 size_t lwipServer::write(uint8_t b)
@@ -96,11 +97,11 @@ size_t lwipServer::write(const uint8_t* buffer, size_t size)
     for (int n = 0; n < MAX_CLIENT; n++) {
         if (_tcp_client[n] != NULL) {
             if (_tcp_client[n]->pcb != NULL) {
-                lwipClient client(_tcp_client[n]);
-                uint8_t s = client.status();
-                if (s == TCP_ACCEPTED) {
-                    n += client.write(buffer, size);
-                }
+                // lwipClient client(_tcp_client[n]);
+                // uint8_t s = client.status();
+                // if (s == TCP_ACCEPTED) {
+                //     n += client.write(buffer, size);
+                // }
             }
         }
     }
diff --git a/libraries/lwIpWrapper/src/lwipUDP.cpp b/libraries/lwIpWrapper/src/lwipUDP.cpp
index 0ea680f8b..2e84ac269 100644
--- a/libraries/lwIpWrapper/src/lwipUDP.cpp
+++ b/libraries/lwIpWrapper/src/lwipUDP.cpp
@@ -64,7 +64,7 @@ uint8_t lwipUDP::begin(IPAddress ip, uint16_t port, bool multicast)
 
     ip_addr_t ipaddr;
     err_t err;
-    u8_to_ip_addr(rawIPAddress(ip), &ipaddr);
+    // u8_to_ip_addr(rawIPAddress(ip), &ipaddr); // FIXME
     if (multicast) {
         err = udp_bind(_udp.pcb, IP_ADDR_ANY, port);
     } else {
@@ -86,7 +86,7 @@ uint8_t lwipUDP::begin(IPAddress ip, uint16_t port, bool multicast)
     _port = port;
     _remaining = 0;
 
-    CLwipIf::getInstance().lwip_task();
+    // CLwipIf::getInstance().lwip_task();
 
     return 1;
 }
@@ -107,7 +107,7 @@ void lwipUDP::stop()
         _udp.pcb = NULL;
     }
 
-    CLwipIf::getInstance().lwip_task();
+    // CLwipIf::getInstance().lwip_task();
 }
 
 int lwipUDP::beginPacket(const char* host, uint16_t port)
@@ -135,7 +135,7 @@ int lwipUDP::beginPacket(IPAddress ip, uint16_t port)
     _sendtoPort = port;
 
     udp_recv(_udp.pcb, &udp_receive_callback, &_udp);
-    CLwipIf::getInstance().lwip_task();
+    // CLwipIf::getInstance().lwip_task();
 
     return 1;
 }
@@ -147,16 +147,16 @@ int lwipUDP::endPacket()
     }
 
     ip_addr_t ipaddr;
-    if (ERR_OK != udp_sendto(_udp.pcb, _data, u8_to_ip_addr(rawIPAddress(_sendtoIP), &ipaddr), _sendtoPort)) {
-        __disable_irq();
-        _data = pbuffer_free_data(_data);
-        __enable_irq();
-        return 0;
-    }
+    // if (ERR_OK != udp_sendto(_udp.pcb, _data, u8_to_ip_addr(rawIPAddress(_sendtoIP), &ipaddr), _sendtoPort)) {
+    //     __disable_irq();
+    //     _data = pbuffer_free_data(_data);
+    //     __enable_irq();
+    //     return 0;
+    // }
 
     _data = NULL;
 
-    CLwipIf::getInstance().lwip_task();
+    // CLwipIf::getInstance().lwip_task();
 
     return 1;
 }
@@ -188,10 +188,10 @@ int lwipUDP::parsePacket()
     //   read();
     // }
 
-    CLwipIf::getInstance().lwip_task();
+    // CLwipIf::getInstance().lwip_task();
 
     if (_udp.data.available > 0) {
-        _remoteIP = IPAddress(ip_addr_to_u32(&(_udp.ip)));
+        // _remoteIP = IPAddress(ip_addr_to_u32(&(_udp.ip))); // FIXME
         _remotePort = _udp.port;
         _remaining = _udp.data.available;
 

From 9a871ef1c6734bcf5c78bd18858a13b14f20ce74 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 2 Jan 2024 15:58:05 +0100
Subject: [PATCH 21/79] deleted lwip tcp wrapper

---
 libraries/lwIpWrapper/src/lwipTcp.cpp | 231 --------------------------
 libraries/lwIpWrapper/src/lwipTcp.h   |  16 --
 2 files changed, 247 deletions(-)
 delete mode 100644 libraries/lwIpWrapper/src/lwipTcp.cpp
 delete mode 100644 libraries/lwIpWrapper/src/lwipTcp.h

diff --git a/libraries/lwIpWrapper/src/lwipTcp.cpp b/libraries/lwIpWrapper/src/lwipTcp.cpp
deleted file mode 100644
index 680262790..000000000
--- a/libraries/lwIpWrapper/src/lwipTcp.cpp
+++ /dev/null
@@ -1,231 +0,0 @@
-#include "lwipTcp.h"
-
-#if LWIP_TCP
-static err_t tcp_recv_callback(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err);
-static err_t tcp_sent_callback(void* arg, struct tcp_pcb* tpcb, u16_t len);
-static void tcp_err_callback(void* arg, err_t err);
-/**
- * @brief Function called when TCP connection established
- * @param arg: user supplied argument
- * @param tpcb: pointer on the connection control block
- * @param err: when connection correctly established err should be ERR_OK
- * @retval err_t: returned error
- */
-err_t tcp_connected_callback(void* arg, struct tcp_pcb* tpcb, err_t err)
-{
-    struct tcp_struct* tcp_arg = (struct tcp_struct*)arg;
-
-    if (err == ERR_OK) {
-        if ((tcp_arg != NULL) && (tcp_arg->pcb == tpcb)) {
-            tcp_arg->state = TCP_CONNECTED;
-
-            /* initialize LwIP tcp_recv callback function */
-            tcp_recv(tpcb, tcp_recv_callback);
-
-            /* initialize LwIP tcp_sent callback function */
-            tcp_sent(tpcb, tcp_sent_callback);
-
-            /* initialize LwIP tcp_err callback function */
-            tcp_err(tpcb, tcp_err_callback);
-
-            return ERR_OK;
-        } else {
-            /* close connection */
-            tcp_connection_close(tpcb, tcp_arg);
-
-            return ERR_ARG;
-        }
-    } else {
-        /* close connection */
-        tcp_connection_close(tpcb, tcp_arg);
-    }
-    return err;
-}
-
-/**
- * @brief  This function is the implementation of tcp_accept LwIP callback
- * @param arg user supplied argument
- * @param  newpcb: pointer on tcp_pcb struct for the newly created tcp connection
- * @param err: when connection correctly established err should be ERR_OK
- * @retval err_t: error status
- */
-err_t tcp_accept_callback(void* arg, struct tcp_pcb* newpcb, err_t err)
-{
-    err_t ret_err;
-    uint8_t accepted;
-    struct tcp_struct** tcpClient = (struct tcp_struct**)arg;
-
-    /* set priority for the newly accepted tcp connection newpcb */
-    tcp_setprio(newpcb, TCP_PRIO_MIN);
-
-    if ((tcpClient != NULL) && (ERR_OK == err)) {
-        struct tcp_struct* client = (struct tcp_struct*)mem_malloc(sizeof(struct tcp_struct));
-
-        if (client != NULL) {
-            client->state = TCP_ACCEPTED;
-            client->pcb = newpcb;
-            client->data.p = NULL;
-            client->data.available = 0;
-
-            /* Looking for an empty socket */
-            for (uint16_t i = 0; i < MAX_CLIENT; i++) {
-                if (tcpClient[i] == NULL) {
-                    tcpClient[i] = client;
-                    accepted = 1;
-                    break;
-                }
-            }
-
-            if (accepted) {
-                /* pass newly allocated client structure as argument to newpcb */
-                tcp_arg(newpcb, client);
-
-                /* initialize lwip tcp_recv callback function for newpcb  */
-                tcp_recv(newpcb, tcp_recv_callback);
-
-                /* initialize lwip tcp_err callback function for newpcb  */
-                tcp_err(newpcb, tcp_err_callback);
-
-                /* initialize LwIP tcp_sent callback function */
-                tcp_sent(newpcb, tcp_sent_callback);
-
-                ret_err = ERR_OK;
-            } else {
-                /*  close tcp connection */
-                tcp_connection_close(newpcb, client);
-                mem_free(client);
-
-                /* return memory error */
-                ret_err = ERR_MEM;
-            }
-        } else {
-            /*  close tcp connection */
-            tcp_connection_close(newpcb, client);
-            mem_free(client);
-
-            /* return memory error */
-            ret_err = ERR_MEM;
-        }
-    } else {
-        tcp_close(newpcb);
-        ret_err = ERR_ARG;
-    }
-    return ret_err;
-}
-
-/**
- * @brief tcp_receiv callback
- * @param arg: argument to be passed to receive callback
- * @param tpcb: tcp connection control block
- * @param err: receive error code
- * @retval err_t: returned error
- */
-static err_t tcp_recv_callback(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err)
-{
-    struct tcp_struct* tcp_arg = (struct tcp_struct*)arg;
-    err_t ret_err;
-
-    /* if we receive an empty tcp frame from server => close connection */
-    if (p == NULL) {
-        /* we're done sending, close connection */
-        tcp_connection_close(tpcb, tcp_arg);
-        ret_err = ERR_OK;
-    }
-    /* else : a non empty frame was received from echo server but for some reason err != ERR_OK */
-    else if (err != ERR_OK) {
-        /* free received pbuf*/
-        if (p != NULL) {
-            pbuf_free(p);
-        }
-        ret_err = err;
-    } else if ((tcp_arg->state == TCP_CONNECTED) || (tcp_arg->state == TCP_ACCEPTED)) {
-        /* Acknowledge data reception */
-        tcp_recved(tpcb, p->tot_len);
-
-        if (tcp_arg->data.p == NULL) {
-            tcp_arg->data.p = p;
-        } else {
-            pbuf_chain(tcp_arg->data.p, p);
-        }
-
-        tcp_arg->data.available += p->len;
-        ret_err = ERR_OK;
-    }
-    /* data received when connection already closed */
-    else {
-        /* Acknowledge data reception */
-        tcp_recved(tpcb, p->tot_len);
-
-        /* free pbuf and do nothing */
-        pbuf_free(p);
-        ret_err = ERR_OK;
-    }
-    return ret_err;
-}
-
-/**
- * @brief  This function implements the tcp_sent LwIP callback (called when ACK
- *         is received from remote host for sent data)
- * @param  arg: pointer on argument passed to callback
- * @param  tcp_pcb: tcp connection control block
- * @param  len: length of data sent
- * @retval err_t: returned error code
- */
-static err_t tcp_sent_callback(void* arg, struct tcp_pcb* tpcb, u16_t len)
-{
-    struct tcp_struct* tcp_arg = (struct tcp_struct*)arg;
-
-    LWIP_UNUSED_ARG(len);
-
-    if ((tcp_arg != NULL) && (tcp_arg->pcb == tpcb)) {
-        return ERR_OK;
-    }
-
-    return ERR_ARG;
-}
-
-/** Function prototype for tcp error callback functions. Called when the pcb
- * receives a RST or is unexpectedly closed for any other reason.
- *
- * @note The corresponding pcb is already freed when this callback is called!
- *
- * @param arg Additional argument to pass to the callback function (@see tcp_arg())
- * @param err Error code to indicate why the pcb has been closed
- *            ERR_ABRT: aborted through tcp_abort or by a TCP timer
- *            ERR_RST: the connection was reset by the remote host
- */
-static void tcp_err_callback(void* arg, err_t err)
-{
-    struct tcp_struct* tcp_arg = (struct tcp_struct*)arg;
-
-    if (tcp_arg != NULL) {
-        if (ERR_OK != err) {
-            tcp_arg->pcb = NULL;
-            tcp_arg->state = TCP_CLOSING;
-        }
-    }
-}
-
-/**
- * @brief This function is used to close the tcp connection with server
- * @param tpcb: tcp connection control block
- * @param es: pointer on echoclient structure
- * @retval None
- */
-void tcp_connection_close(struct tcp_pcb* tpcb, struct tcp_struct* tcp)
-{
-    /* remove callbacks */
-    tcp_recv(tpcb, NULL);
-    tcp_sent(tpcb, NULL);
-    tcp_poll(tpcb, NULL, 0);
-    tcp_err(tpcb, NULL);
-    tcp_accept(tpcb, NULL);
-
-    /* close tcp connection */
-    tcp_close(tpcb);
-
-    tcp->pcb = NULL;
-    tcp->state = TCP_CLOSING;
-}
-
-#endif /* LWIP_TCP */
diff --git a/libraries/lwIpWrapper/src/lwipTcp.h b/libraries/lwIpWrapper/src/lwipTcp.h
deleted file mode 100644
index 3c4e689bd..000000000
--- a/libraries/lwIpWrapper/src/lwipTcp.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef _ARDUINO_LWIP_IF_TCP_HELPERS_H
-#define _ARDUINO_LWIP_IF_TCP_HELPERS_H
-
-#include "CNetIf.h"
-#include "lwipTypes.h"
-
-#if LWIP_TCP
-err_t tcp_connected_callback(void* arg, struct tcp_pcb* tpcb, err_t err);
-err_t tcp_accept_callback(void* arg, struct tcp_pcb* newpcb, err_t err);
-void tcp_connection_close(struct tcp_pcb* tpcb, struct tcp_struct* tcp);
-
-#else
-#error "LWIP_TCP must be enabled in lwipopts.h"
-#endif
-
-#endif
\ No newline at end of file

From 0b6dd35ce1264250d54ad10a47df7a473adbbb81 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 2 Jan 2024 16:07:43 +0100
Subject: [PATCH 22/79] implemented netowrk interfaces specific clients

---
 libraries/Ethernet/src/EthernetClient.h | 10 +++++++---
 libraries/WiFi/src/WiFiClient.h         |  8 ++++++--
 libraries/lwIpWrapper/src/CNetIf.h      |  8 ++++++--
 libraries/lwIpWrapper/src/lwipClient.h  |  7 ++++++-
 4 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/libraries/Ethernet/src/EthernetClient.h b/libraries/Ethernet/src/EthernetClient.h
index c3b16242a..7bb5cca23 100644
--- a/libraries/Ethernet/src/EthernetClient.h
+++ b/libraries/Ethernet/src/EthernetClient.h
@@ -5,9 +5,13 @@
 #include "lwipClient.h"
 
 class EthernetClient : public lwipClient {
-   public:
-   EthernetClient() {}
-   EthernetClient(struct tcp_struct *tcpClient) : lwipClient() {} // FIXME
+public:
+   EthernetClient() {
+      this->bindCNetIf(Ethernet);
+   }
+   EthernetClient(struct tcp_pcb *pcb) : lwipClient(pcb) {
+      this->bindCNetIf(Ethernet);
+   }
 };
 
 #endif
\ No newline at end of file
diff --git a/libraries/WiFi/src/WiFiClient.h b/libraries/WiFi/src/WiFiClient.h
index 19c7e83d4..e6f26fd63 100644
--- a/libraries/WiFi/src/WiFiClient.h
+++ b/libraries/WiFi/src/WiFiClient.h
@@ -6,8 +6,12 @@
 
 class WiFiClient : public lwipClient {
    public:
-   WiFiClient() {}
-   WiFiClient(struct tcp_struct *tcpClient) : lwipClient(tcpClient) {}
+   WiFiClient() {
+      this->bindCNetIf(WiFi);
+   }
+   WiFiClient(struct tcp_pcb *pcb) : lwipClient(pcb) {
+      this->bindCNetIf(WiFi);
+   }
 };
 
 #endif
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 9dafca0da..8fd964455 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -97,6 +97,7 @@ typedef enum {
 #define INVALID_RESPONSE -4
 
 class CLwipIf;
+class lwipClient;
 
 /* Base class implements DHCP, derived class will switch it on or off */
 class CNetIf: public NetworkInterface {
@@ -238,7 +239,7 @@ class CWifiStation : public CNetIf {
 
     virtual void task() override;
 
-    virtual int getMacAddress(uint8_t* mac) override;
+    virtual int getMacAddress(uint8_t* mac) override {}
 
     virtual const char* getSSID();
     virtual uint8_t* getBSSID(uint8_t* bssid);
@@ -277,7 +278,7 @@ class CWifiSoftAp : public CNetIf {
     int startSoftAp(const char* ssid, const char* passphrase=nullptr, uint8_t channel=0);
     int stopSoftAp();
 
-    virtual int getMacAddress(uint8_t* mac) override;
+    virtual int getMacAddress(uint8_t* mac) override {}
 
     virtual const char* getSSID();
     virtual uint8_t* getBSSID(uint8_t* bssid);
@@ -357,3 +358,6 @@ class CLwipIf {
     FspTimer timer;
 #endif
 };
+
+extern CEth Ethernet;
+extern CWifiStation WiFi;
\ No newline at end of file
diff --git a/libraries/lwIpWrapper/src/lwipClient.h b/libraries/lwIpWrapper/src/lwipClient.h
index d0d4e569d..5188f42e1 100644
--- a/libraries/lwIpWrapper/src/lwipClient.h
+++ b/libraries/lwIpWrapper/src/lwipClient.h
@@ -3,6 +3,7 @@
 #include <lwip/include/lwip/tcp.h>
 #include <IPAddress.h>
 #include <Print.h>
+#include "CNetIf.h"
 
 // TODO improve documentation
 
@@ -67,6 +68,10 @@ class lwipClient : public arduino::Client {
         _timeout = timeout;
     }
 
+    void bindCNetIf(CNetIf &n) {
+        tcp_bind_netif(this->pcb, n.getNi());
+    }
+
     friend class lwipServer;
 
     using Print::write;
@@ -82,7 +87,7 @@ class lwipClient : public arduino::Client {
     // TCP related info of the socket
     _tcp_state_t state =        TCP_NONE;
     struct pbuf* pbuf_head =    nullptr;
-    struct tcp_pcb* pcb =              nullptr;
+    struct tcp_pcb* pcb =       nullptr;
     uint16_t pbuf_offset =      0;
 
     uint16_t _timeout = 10000;

From f911f6711ae3e1f931613128f8fe8c89bb23a1fb Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 2 Jan 2024 16:08:43 +0100
Subject: [PATCH 23/79] defined Wifi global variable for network interface

---
 libraries/WiFi/src/WiFi.cpp | 620 ++++++++++++++++++------------------
 libraries/WiFi/src/WiFiC3.h |   4 +
 2 files changed, 314 insertions(+), 310 deletions(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index 59e742a0f..d91a212ef 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -1,331 +1,331 @@
 #include "WiFiC3.h"
 
 
-extern "C" void dhcps_start(struct netif *netif);
+// extern "C" void dhcps_start(struct netif *netif);
 
 
 
-/* -------------------------------------------------------------------------- */
-CWifi::CWifi() : _timeout(50000), ni(nullptr) {
-}
-/* -------------------------------------------------------------------------- */
+// /* -------------------------------------------------------------------------- */
+// CWifi::CWifi() : _timeout(50000), ni(nullptr) {
+// }
+// /* -------------------------------------------------------------------------- */
 
-/* -------------------------------------------------------------------------- */
-const char* CWifi::firmwareVersion() {
-/* -------------------------------------------------------------------------- */   
-   /* silly "dummy" implementation to keep compatibility, at the present
-      the WiFi fw does not return any version number */
-   return WIFI_FIRMWARE_LATEST_VERSION;
-}
+// /* -------------------------------------------------------------------------- */
+// const char* CWifi::firmwareVersion() {
+// /* -------------------------------------------------------------------------- */   
+//    /* silly "dummy" implementation to keep compatibility, at the present
+//       the WiFi fw does not return any version number */
+//    return WIFI_FIRMWARE_LATEST_VERSION;
+// }
 
 
 
-/* -------------------------------------------------------------------------- */
-int CWifi::begin(const char* ssid) {
-/* -------------------------------------------------------------------------- */   
-   ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
-   CLwipIf::getInstance().connectToAp(ssid, nullptr);
-   if(ni != nullptr && !_useStaticIp) {
-      ni->DhcpStart();
-   }
+// /* -------------------------------------------------------------------------- */
+// int CWifi::begin(const char* ssid) {
+// /* -------------------------------------------------------------------------- */   
+//    ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
+//    CLwipIf::getInstance().connectToAp(ssid, nullptr);
+//    if(ni != nullptr && !_useStaticIp) {
+//       ni->DhcpStart();
+//    }
    
-   return CLwipIf::getInstance().getWifiStatus();
-}
+//    return CLwipIf::getInstance().getWifiStatus();
+// }
 
 
-/* -------------------------------------------------------------------------- */
-int CWifi::begin(const char* ssid, const char *passphrase) {
-/* -------------------------------------------------------------------------- */   
+// /* -------------------------------------------------------------------------- */
+// int CWifi::begin(const char* ssid, const char *passphrase) {
+// /* -------------------------------------------------------------------------- */   
    
-   ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
-   CLwipIf::getInstance().connectToAp(ssid, passphrase); 
-   if(ni != nullptr && !_useStaticIp) {
-      ni->DhcpStart();
-   }
+//    ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
+//    CLwipIf::getInstance().connectToAp(ssid, passphrase); 
+//    if(ni != nullptr && !_useStaticIp) {
+//       ni->DhcpStart();
+//    }
    
-   return CLwipIf::getInstance().getWifiStatus();
-}
-
-/* passphrase is needed so a default one will be set */
-/* -------------------------------------------------------------------------- */
-uint8_t CWifi::beginAP(const char *ssid) {
-/* -------------------------------------------------------------------------- */   
-   return beginAP(ssid,1);
-}
-
-/* -------------------------------------------------------------------------- */
-uint8_t CWifi::beginAP(const char *ssid, uint8_t channel) {
-/* -------------------------------------------------------------------------- */   
-   return beginAP(ssid,nullptr,channel);
-}
-
-/* -------------------------------------------------------------------------- */
-uint8_t CWifi::beginAP(const char *ssid, const char* passphrase) {
-/* -------------------------------------------------------------------------- */   
-   return beginAP(ssid,passphrase,1);
-}
-
-/* -------------------------------------------------------------------------- */
-uint8_t CWifi::beginAP(const char *ssid, const char* passphrase, uint8_t channel) {
-/* -------------------------------------------------------------------------- */   
+//    return CLwipIf::getInstance().getWifiStatus();
+// }
+
+// /* passphrase is needed so a default one will be set */
+// /* -------------------------------------------------------------------------- */
+// uint8_t CWifi::beginAP(const char *ssid) {
+// /* -------------------------------------------------------------------------- */   
+//    return beginAP(ssid,1);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// uint8_t CWifi::beginAP(const char *ssid, uint8_t channel) {
+// /* -------------------------------------------------------------------------- */   
+//    return beginAP(ssid,nullptr,channel);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// uint8_t CWifi::beginAP(const char *ssid, const char* passphrase) {
+// /* -------------------------------------------------------------------------- */   
+//    return beginAP(ssid,passphrase,1);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// uint8_t CWifi::beginAP(const char *ssid, const char* passphrase, uint8_t channel) {
+// /* -------------------------------------------------------------------------- */   
    
-   ni = CLwipIf::getInstance().get(NI_WIFI_SOFTAP);
-   CLwipIf::getInstance().startSoftAp(ssid,passphrase,channel); 
-   if(ni != nullptr) {
+//    ni = CLwipIf::getInstance().get(NI_WIFI_SOFTAP);
+//    CLwipIf::getInstance().startSoftAp(ssid,passphrase,channel); 
+//    if(ni != nullptr) {
       
-      dhcps_start(ni->getNi());
-   }
+//       dhcps_start(ni->getNi());
+//    }
    
-   return CLwipIf::getInstance().getWifiStatus();   
-}
-
-
-
-/* -------------------------------------------------------------------------- */
-void CWifi::config(IPAddress local_ip) {
-/* -------------------------------------------------------------------------- */   
-   IPAddress _nm(255, 255, 255, 0);
-   IPAddress _gw = local_ip;
-   _gw[3] = 1;
-
-   _config(local_ip, _gw, _nm);
-}
-
-extern uint8_t *IpAddress2uint8(IPAddress a);
-
-/* -------------------------------------------------------------------------- */
-void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) {
-/* -------------------------------------------------------------------------- */    
-   _useStaticIp = local_ip != INADDR_NONE;
-   if(ni != nullptr) {
-      ni->DhcpStop();
-      ni->DhcpNotUsed();
-      IP_ADDR4(&ni->ip, local_ip[0], local_ip[1], local_ip[2], local_ip[3]);
-      IP_ADDR4(&ni->gw, gateway[0], gateway[1], gateway[2], gateway[3]);
-      IP_ADDR4(&ni->nm, subnet[0], subnet[1], subnet[2], subnet[3]);
-   }
-   else {
-      CNetIf::default_ip = local_ip;
-      CNetIf::default_nm = subnet; 
-      CNetIf::default_gw = gateway;
-      CNetIf::default_dhcp_server_ip = local_ip;
-   }
-}
-
-/* -------------------------------------------------------------------------- */
-void CWifi::config(IPAddress local_ip, IPAddress dns_server) {
-/* -------------------------------------------------------------------------- */   
-   config(local_ip);
-   CLwipIf::getInstance().addDns(dns_server);
-}
-
-/* -------------------------------------------------------------------------- */
-void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway) {
-/* -------------------------------------------------------------------------- */   
-   IPAddress _nm(255, 255, 255, 0);
-   _config(local_ip, gateway, _nm);
-   CLwipIf::getInstance().addDns(dns_server);
-}
-
-/* -------------------------------------------------------------------------- */
-void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) {
-/* -------------------------------------------------------------------------- */
-   _config(local_ip, gateway, subnet);
-   CLwipIf::getInstance().addDns(dns_server);
-}
-
-/* -------------------------------------------------------------------------- */
-void CWifi::setDNS(IPAddress dns_server1) {
-/* -------------------------------------------------------------------------- */   
-   CLwipIf::getInstance().addDns(dns_server1);
-}
-
-/* -------------------------------------------------------------------------- */
-void CWifi::setDNS(IPAddress dns_server1, IPAddress dns_server2) {
-/* -------------------------------------------------------------------------- */   
-   CLwipIf::getInstance().addDns(dns_server1);
-   CLwipIf::getInstance().addDns(dns_server2);
+//    return CLwipIf::getInstance().getWifiStatus();   
+// }
+
+
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::config(IPAddress local_ip) {
+// /* -------------------------------------------------------------------------- */   
+//    IPAddress _nm(255, 255, 255, 0);
+//    IPAddress _gw = local_ip;
+//    _gw[3] = 1;
+
+//    _config(local_ip, _gw, _nm);
+// }
+
+// extern uint8_t *IpAddress2uint8(IPAddress a);
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) {
+// /* -------------------------------------------------------------------------- */    
+//    _useStaticIp = local_ip != INADDR_NONE;
+//    if(ni != nullptr) {
+//       ni->DhcpStop();
+//       ni->DhcpNotUsed();
+//       IP_ADDR4(&ni->ip, local_ip[0], local_ip[1], local_ip[2], local_ip[3]);
+//       IP_ADDR4(&ni->gw, gateway[0], gateway[1], gateway[2], gateway[3]);
+//       IP_ADDR4(&ni->nm, subnet[0], subnet[1], subnet[2], subnet[3]);
+//    }
+//    else {
+//       CNetIf::default_ip = local_ip;
+//       CNetIf::default_nm = subnet; 
+//       CNetIf::default_gw = gateway;
+//       CNetIf::default_dhcp_server_ip = local_ip;
+//    }
+// }
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::config(IPAddress local_ip, IPAddress dns_server) {
+// /* -------------------------------------------------------------------------- */   
+//    config(local_ip);
+//    CLwipIf::getInstance().addDns(dns_server);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway) {
+// /* -------------------------------------------------------------------------- */   
+//    IPAddress _nm(255, 255, 255, 0);
+//    _config(local_ip, gateway, _nm);
+//    CLwipIf::getInstance().addDns(dns_server);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) {
+// /* -------------------------------------------------------------------------- */
+//    _config(local_ip, gateway, subnet);
+//    CLwipIf::getInstance().addDns(dns_server);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::setDNS(IPAddress dns_server1) {
+// /* -------------------------------------------------------------------------- */   
+//    CLwipIf::getInstance().addDns(dns_server1);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::setDNS(IPAddress dns_server1, IPAddress dns_server2) {
+// /* -------------------------------------------------------------------------- */   
+//    CLwipIf::getInstance().addDns(dns_server1);
+//    CLwipIf::getInstance().addDns(dns_server2);
    
-}
-
-/* -------------------------------------------------------------------------- */
-void CWifi::setHostname(const char* name) {
-/* -------------------------------------------------------------------------- */   
-   if(ni != nullptr) {
-      ni->setHostname(name);
-   }
-}
-
-/* -------------------------------------------------------------------------- */
-int CWifi::disconnect() {
-/* -------------------------------------------------------------------------- */   
-   CLwipIf::getInstance().disconnectFromAp();
-}
-
-/* -------------------------------------------------------------------------- */
-void CWifi::end(void) {
-/* -------------------------------------------------------------------------- */   
-
-}
-
-/* -------------------------------------------------------------------------- */
-uint8_t* CWifi::macAddress(uint8_t* mac) {
-/* -------------------------------------------------------------------------- */   
-   if(ni != nullptr) {
-      if(ni->getMacAddress(mac) == WL_MAC_ADDR_LENGTH) {
-         return mac;
-      }
-   }
-   memset(mac,0x00,6);
-   return mac;
-}
-
-/* -------------------------------------------------------------------------- */
-int8_t CWifi::scanNetworks() {
-/* -------------------------------------------------------------------------- */   
-   ni = CLwipIf::getInstance().get(NI_WIFI_STATION);   
-   if(CLwipIf::getInstance().scanForAp() == ESP_CONTROL_OK) {
-      return CLwipIf::getInstance().getApNum();
-   }
-   return 0;
-}
+// }
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::setHostname(const char* name) {
+// /* -------------------------------------------------------------------------- */   
+//    if(ni != nullptr) {
+//       ni->setHostname(name);
+//    }
+// }
+
+// /* -------------------------------------------------------------------------- */
+// int CWifi::disconnect() {
+// /* -------------------------------------------------------------------------- */   
+//    CLwipIf::getInstance().disconnectFromAp();
+// }
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::end(void) {
+// /* -------------------------------------------------------------------------- */   
+
+// }
+
+// /* -------------------------------------------------------------------------- */
+// uint8_t* CWifi::macAddress(uint8_t* mac) {
+// /* -------------------------------------------------------------------------- */   
+//    if(ni != nullptr) {
+//       if(ni->getMacAddress(mac) == WL_MAC_ADDR_LENGTH) {
+//          return mac;
+//       }
+//    }
+//    memset(mac,0x00,6);
+//    return mac;
+// }
+
+// /* -------------------------------------------------------------------------- */
+// int8_t CWifi::scanNetworks() {
+// /* -------------------------------------------------------------------------- */   
+//    ni = CLwipIf::getInstance().get(NI_WIFI_STATION);   
+//    if(CLwipIf::getInstance().scanForAp() == ESP_CONTROL_OK) {
+//       return CLwipIf::getInstance().getApNum();
+//    }
+//    return 0;
+// }
  
-/* -------------------------------------------------------------------------- */   
-IPAddress CWifi::localIP() {
-/* -------------------------------------------------------------------------- */   
-   if(ni != nullptr) {
-      return IPAddress(ni->getIpAdd());   
-   }
-   return IPAddress((uint32_t)0);
-}
-
-/* -------------------------------------------------------------------------- */
-IPAddress CWifi::subnetMask() {
-/* -------------------------------------------------------------------------- */
-   if(ni != nullptr) {
-      return IPAddress(ni->getNmAdd());   
-   }
-   return IPAddress((uint32_t)0);
-}
-
-/* -------------------------------------------------------------------------- */
-IPAddress CWifi::gatewayIP() {
-/* -------------------------------------------------------------------------- */   
-   if(ni != nullptr) {
-      return IPAddress(ni->getGwAdd());   
-   }
-   return IPAddress((uint32_t)0);
-}
-
-/* -------------------------------------------------------------------------- */
-IPAddress CWifi::dnsIP(int n) {
-   return CLwipIf::getInstance().getDns(n);
-}
-
-/* -------------------------------------------------------------------------- */
-const char* CWifi::SSID(uint8_t networkItem) {  
-   return CLwipIf::getInstance().getSSID(networkItem);
-}
-/* -------------------------------------------------------------------------- */ 
-
-/* -------------------------------------------------------------------------- */
-int32_t CWifi::RSSI(uint8_t networkItem) {
-   return CLwipIf::getInstance().getRSSI(networkItem);
-}
-/* -------------------------------------------------------------------------- */ 
-
-/* -------------------------------------------------------------------------- */
-uint8_t CWifi::encryptionType(uint8_t networkItem) {
-   return CLwipIf::getInstance().getEncrType(networkItem);
-}
-/* -------------------------------------------------------------------------- */ 
-
-/* -------------------------------------------------------------------------- */
-uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) {
-   return CLwipIf::getInstance().getBSSID(networkItem,bssid);
-}
-/* -------------------------------------------------------------------------- */ 
-
-/* -------------------------------------------------------------------------- */
-uint8_t CWifi::channel(uint8_t networkItem) { 
-   return CLwipIf::getInstance().getChannel(networkItem);
-}
-/* -------------------------------------------------------------------------- */ 
-
-/* -------------------------------------------------------------------------- */ 
-const char* CWifi::SSID() {
-/* -------------------------------------------------------------------------- */    
-   if(ni != nullptr) {
-      return ni->getSSID();
-   }
-   return ""; 
-}
-
-/* -------------------------------------------------------------------------- */ 
-uint8_t* CWifi::BSSID(uint8_t* bssid) {
-/* -------------------------------------------------------------------------- */    
-   if(ni != nullptr) {
-      return ni->getBSSID(bssid);
-   }
-   return nullptr;
-}
-
-/* -------------------------------------------------------------------------- */ 
-int32_t CWifi::RSSI() {
-/* -------------------------------------------------------------------------- */    
-   if(ni != nullptr) {
-      return ni->getRSSI();
-   }
-   return 0;
-}
-
-/* -------------------------------------------------------------------------- */ 
-uint8_t CWifi::encryptionType() {
-/* -------------------------------------------------------------------------- */    
-   if(ni != nullptr) {
-      return ni->getEncryptionType();
-   }
-   return 0;
-}
-
-/* -------------------------------------------------------------------------- */
-uint8_t CWifi::status() {
-/* -------------------------------------------------------------------------- */   
-   return CLwipIf::getInstance().getWifiStatus(); 
-}
-
-/* -------------------------------------------------------------------------- */
-int CWifi::hostByName(const char* aHostname, IPAddress& aResult) {
-/* -------------------------------------------------------------------------- */   
-   return CLwipIf::getInstance().getHostByName(aHostname,aResult);
-}
-
-/* -------------------------------------------------------------------------- */
-void CWifi::lowPowerMode() {
-/* -------------------------------------------------------------------------- */   
-   CLwipIf::getInstance().setLowPowerMode();
-}
-
-/* -------------------------------------------------------------------------- */
-void CWifi::noLowPowerMode() {
-/* -------------------------------------------------------------------------- */   
-   CLwipIf::getInstance().resetLowPowerMode();
-}
-
-uint8_t CWifi::reasonCode() {
-   return 0;
-}
-
-unsigned long CWifi::getTime() {
-   return 0;
-}
-
-
-
-void CWifi::setTimeout(unsigned long timeout) {
-   (void)(timeout);  
-}
-
-
-CWifi WiFi;
+// /* -------------------------------------------------------------------------- */   
+// IPAddress CWifi::localIP() {
+// /* -------------------------------------------------------------------------- */   
+//    if(ni != nullptr) {
+//       return IPAddress(ni->getIpAdd());   
+//    }
+//    return IPAddress((uint32_t)0);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// IPAddress CWifi::subnetMask() {
+// /* -------------------------------------------------------------------------- */
+//    if(ni != nullptr) {
+//       return IPAddress(ni->getNmAdd());   
+//    }
+//    return IPAddress((uint32_t)0);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// IPAddress CWifi::gatewayIP() {
+// /* -------------------------------------------------------------------------- */   
+//    if(ni != nullptr) {
+//       return IPAddress(ni->getGwAdd());   
+//    }
+//    return IPAddress((uint32_t)0);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// IPAddress CWifi::dnsIP(int n) {
+//    return CLwipIf::getInstance().getDns(n);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// const char* CWifi::SSID(uint8_t networkItem) {  
+//    return CLwipIf::getInstance().getSSID(networkItem);
+// }
+// /* -------------------------------------------------------------------------- */ 
+
+// /* -------------------------------------------------------------------------- */
+// int32_t CWifi::RSSI(uint8_t networkItem) {
+//    return CLwipIf::getInstance().getRSSI(networkItem);
+// }
+// /* -------------------------------------------------------------------------- */ 
+
+// /* -------------------------------------------------------------------------- */
+// uint8_t CWifi::encryptionType(uint8_t networkItem) {
+//    return CLwipIf::getInstance().getEncrType(networkItem);
+// }
+// /* -------------------------------------------------------------------------- */ 
+
+// /* -------------------------------------------------------------------------- */
+// uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) {
+//    return CLwipIf::getInstance().getBSSID(networkItem,bssid);
+// }
+// /* -------------------------------------------------------------------------- */ 
+
+// /* -------------------------------------------------------------------------- */
+// uint8_t CWifi::channel(uint8_t networkItem) { 
+//    return CLwipIf::getInstance().getChannel(networkItem);
+// }
+// /* -------------------------------------------------------------------------- */ 
+
+// /* -------------------------------------------------------------------------- */ 
+// const char* CWifi::SSID() {
+// /* -------------------------------------------------------------------------- */    
+//    if(ni != nullptr) {
+//       return ni->getSSID();
+//    }
+//    return ""; 
+// }
+
+// /* -------------------------------------------------------------------------- */ 
+// uint8_t* CWifi::BSSID(uint8_t* bssid) {
+// /* -------------------------------------------------------------------------- */    
+//    if(ni != nullptr) {
+//       return ni->getBSSID(bssid);
+//    }
+//    return nullptr;
+// }
+
+// /* -------------------------------------------------------------------------- */ 
+// int32_t CWifi::RSSI() {
+// /* -------------------------------------------------------------------------- */    
+//    if(ni != nullptr) {
+//       return ni->getRSSI();
+//    }
+//    return 0;
+// }
+
+// /* -------------------------------------------------------------------------- */ 
+// uint8_t CWifi::encryptionType() {
+// /* -------------------------------------------------------------------------- */    
+//    if(ni != nullptr) {
+//       return ni->getEncryptionType();
+//    }
+//    return 0;
+// }
+
+// /* -------------------------------------------------------------------------- */
+// uint8_t CWifi::status() {
+// /* -------------------------------------------------------------------------- */   
+//    return CLwipIf::getInstance().getWifiStatus(); 
+// }
+
+// /* -------------------------------------------------------------------------- */
+// int CWifi::hostByName(const char* aHostname, IPAddress& aResult) {
+// /* -------------------------------------------------------------------------- */   
+//    return CLwipIf::getInstance().getHostByName(aHostname,aResult);
+// }
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::lowPowerMode() {
+// /* -------------------------------------------------------------------------- */   
+//    CLwipIf::getInstance().setLowPowerMode();
+// }
+
+// /* -------------------------------------------------------------------------- */
+// void CWifi::noLowPowerMode() {
+// /* -------------------------------------------------------------------------- */   
+//    CLwipIf::getInstance().resetLowPowerMode();
+// }
+
+// uint8_t CWifi::reasonCode() {
+//    return 0;
+// }
+
+// unsigned long CWifi::getTime() {
+//    return 0;
+// }
+
+
+
+// void CWifi::setTimeout(unsigned long timeout) {
+//    (void)(timeout);  
+// }
+
+
+// CWifi WiFi;
 
diff --git a/libraries/WiFi/src/WiFiC3.h b/libraries/WiFi/src/WiFiC3.h
index ffe55cbf3..3abde9230 100644
--- a/libraries/WiFi/src/WiFiC3.h
+++ b/libraries/WiFi/src/WiFiC3.h
@@ -8,7 +8,11 @@
 
 #define WIFI_FIRMWARE_LATEST_VERSION "1.5.0"
 
+#ifdef ARDUINO_PORTENTA_C33
 // TODO Instantiate the drivers for wifi with default configuration parameters
+// ESPHostFGDriver WifiDriver;
 
 // Instantiate a global variable from CWifiStation calling it WiFi
 
+inline CWifiStation WiFi;
+#endif // ARDUINO_PORTENTA_C33

From 4c86ebf70b3988176c4285ac7d107f9535d0ab65 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 2 Jan 2024 16:09:22 +0100
Subject: [PATCH 24/79] added scanforap method

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index e14ab239c..d944e4a34 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -292,6 +292,7 @@ CNetIf::CNetIf(NetworkDriver *driver)
 }
 
 int CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
+    CLwipIf::getInstance(); // This call is required in order to setup the network stack
     driver->begin();
 
     ip_addr_t _ip = fromArduinoIP(ip);
@@ -631,6 +632,23 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
     return rv;
 }
 
+int CWifiStation::scanForAp() {
+        // arduino::lock();
+    access_points.clear(); // FIXME create access_points vector
+
+    int res = CEspControl::getInstance().getAccessPointScanList(access_points);
+    if (res == ESP_CONTROL_OK) {
+        res = WL_SCAN_COMPLETED;
+    }
+    // else {
+    //     res = WL_NO_SSID_AVAIL; // TODO
+    // }
+
+    // arduino::unlock();
+
+    return res;
+}
+
 // disconnect
 int CWifiStation::disconnectFromAp() {
     return CEspControl::getInstance().disconnectAccessPoint();
@@ -880,10 +898,6 @@ int CWifiSoftAp::startSoftAp(const char* ssid, const char* passphrase, uint8_t c
     return rv;
 }
 
-int CWifiSoftAp::stopSoftAp() {
-
-}
-
 err_t CWifiSoftAp::init(struct netif* ni) {
     // Setting up netif
 #if LWIP_NETIF_HOSTNAME
@@ -922,7 +936,7 @@ err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
     // p may be a chain of pbufs
     if(p->next != nullptr) {
         buf = (uint8_t*) malloc(size*sizeof(uint8_t));
-        if(buf == nullptr) {\
+        if(buf == nullptr) {
             // NETIF_STATS_INCREMENT_ERROR(this->stats, ERR_MEM);
             // NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
             errval = ERR_MEM;

From b3713312b24c0b32cdefcec762e3bb14d7e9d786 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 4 Jan 2024 15:10:01 +0100
Subject: [PATCH 25/79] added comment

---
 libraries/Ethernet/src/EthernetC33.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/Ethernet/src/EthernetC33.h b/libraries/Ethernet/src/EthernetC33.h
index 4ff7daf0f..412880402 100644
--- a/libraries/Ethernet/src/EthernetC33.h
+++ b/libraries/Ethernet/src/EthernetC33.h
@@ -24,4 +24,4 @@ inline EthernetC33Driver EthernetDriver(2, 2, mem_malloc, 1536);
 // FIXME Instantiate a global variable from CEth, calling it Ethernet
 inline CEth Ethernet(&EthernetDriver);
 
-#endif
+#endif // ARDUINO_PORTENTA_C33

From 1c588992e1e50e8d4427c1d8ac7a6ae017b7615f Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 4 Jan 2024 15:11:00 +0100
Subject: [PATCH 26/79] reimplementing Server class with ethernet and wifi
 subclasses

---
 libraries/Ethernet/src/EthernetClient.h  |  28 +++--
 libraries/Ethernet/src/EthernetServer.h  |  40 ++----
 libraries/WiFi/src/WiFiClient.h          |  31 +++--
 libraries/WiFi/src/WiFiServer.h          |  41 ++----
 libraries/lwIpWrapper/src/lwipClient.cpp | 154 +++++++++++++++--------
 libraries/lwIpWrapper/src/lwipClient.h   |  50 +++++---
 libraries/lwIpWrapper/src/lwipServer.cpp | 151 +++++++++++++---------
 libraries/lwIpWrapper/src/lwipServer.h   |  53 +++++---
 8 files changed, 316 insertions(+), 232 deletions(-)

diff --git a/libraries/Ethernet/src/EthernetClient.h b/libraries/Ethernet/src/EthernetClient.h
index 7bb5cca23..7e201c76d 100644
--- a/libraries/Ethernet/src/EthernetClient.h
+++ b/libraries/Ethernet/src/EthernetClient.h
@@ -1,17 +1,23 @@
-#ifndef ARDUINO_LWIP_ETHERNET_CLIENT_H
-#define ARDUINO_LWIP_ETHERNET_CLIENT_H
-
+#pragma once
 
 #include "lwipClient.h"
 
 class EthernetClient : public lwipClient {
 public:
-   EthernetClient() {
-      this->bindCNetIf(Ethernet);
-   }
-   EthernetClient(struct tcp_pcb *pcb) : lwipClient(pcb) {
-      this->bindCNetIf(Ethernet);
-   }
-};
+    EthernetClient() {
+    }
+    EthernetClient(struct tcp_pcb *pcb, lwipServer *server)
+    : lwipClient(pcb, server) {
+    }
+    EthernetClient(lwipClient c)
+    : lwipClient(c) {
+    }
+
+    int connect(IPAddress ip, uint16_t port) {
+        auto res = lwipClient::connect(ip, port);
 
-#endif
\ No newline at end of file
+        this->bindCNetIf(Ethernet);
+
+        return res;
+    }
+};
diff --git a/libraries/Ethernet/src/EthernetServer.h b/libraries/Ethernet/src/EthernetServer.h
index 2afb5060c..302af571a 100644
--- a/libraries/Ethernet/src/EthernetServer.h
+++ b/libraries/Ethernet/src/EthernetServer.h
@@ -1,36 +1,16 @@
-#ifndef ARDUINO_LWIP_ETHERNET_SERVER_H
-#define ARDUINO_LWIP_ETHERNET_SERVER_H
-
-
+#pragma once
 
 #include "lwipServer.h"
 #include "EthernetClient.h"
 
-class EthernetServer : public lwipServer {
-   public:
-   EthernetServer() {}
-   EthernetServer(uint16_t port) : lwipServer(port) {}
-
-   EthernetClient available() {
-      accept();
+class EthernetServer: public lwipServer {
+public:
+    void begin() {
+        lwipServer::begin();
+        this->bindCNetIf(WiFi);
+    }
 
-      for (int n = 0; n < MAX_CLIENT; n++) {
-         if (_tcp_client[n] != NULL) {
-            if (_tcp_client[n]->pcb != NULL) {
-               EthernetClient client(_tcp_client[n]);
-               uint8_t s = client.status();
-               if (s == TCP_ACCEPTED) {
-                  if (client.available()) {
-                     return client;
-                  }
-               }
-            }
-         }
-     }
-
-     struct tcp_struct *default_client = NULL;
-     return EthernetClient(default_client);
-   }
+    EthernetClient available() {
+        return EthernetClient(lwipServer::available());
+    }
 };
-
-#endif
\ No newline at end of file
diff --git a/libraries/WiFi/src/WiFiClient.h b/libraries/WiFi/src/WiFiClient.h
index e6f26fd63..98761c51f 100644
--- a/libraries/WiFi/src/WiFiClient.h
+++ b/libraries/WiFi/src/WiFiClient.h
@@ -1,18 +1,23 @@
-#ifndef ARDUINO_LWIP_WIFI_CLIENT_H
-#define ARDUINO_LWIP_WIFI_CLIENT_H
-
+#pragma once
 
 #include "lwipClient.h"
 
-class WiFiClient : public lwipClient {
-   public:
-   WiFiClient() {
-      this->bindCNetIf(WiFi);
-   }
-   WiFiClient(struct tcp_pcb *pcb) : lwipClient(pcb) {
-      this->bindCNetIf(WiFi);
-   }
-};
+class WiFiClient: public lwipClient {
+public:
+    WiFiClient() {
+    }
+    WiFiClient(struct tcp_pcb *pcb, lwipServer *server)
+    : lwipClient(pcb, server) {
+    }
+    WiFiClient(lwipClient c)
+    : lwipClient(c) {
+    }
 
-#endif
+    int connect(IPAddress ip, uint16_t port) {
+        auto res = lwipClient::connect(ip, port);
 
+        this->bindCNetIf(WiFi);
+
+        return res;
+    }
+};
diff --git a/libraries/WiFi/src/WiFiServer.h b/libraries/WiFi/src/WiFiServer.h
index 1199814fd..c72c86597 100644
--- a/libraries/WiFi/src/WiFiServer.h
+++ b/libraries/WiFi/src/WiFiServer.h
@@ -1,35 +1,16 @@
-#ifndef ARDUINO_LWIP_WIFI_SERVER_H
-#define ARDUINO_LWIP_WIFI_SERVER_H
-
+#pragma once
 
 #include "lwipServer.h"
-#include "WiFiClient.h"
-
-class WiFiServer : public lwipServer {
-   public:
-   WiFiServer() {}
-   WiFiServer(uint16_t port) : lwipServer(port) {}
+#include "lwipClient.h"
 
-   WiFiClient available() {
-      accept();
+class WiFiServer: public lwipServer {
+public:
+    void begin() {
+        lwipServer::begin();
+        this->bindCNetIf(WiFi);
+    }
 
-      for (int n = 0; n < MAX_CLIENT; n++) {
-         if (_tcp_client[n] != NULL) {
-            if (_tcp_client[n]->pcb != NULL) {
-               WiFiClient client(_tcp_client[n]);
-               uint8_t s = client.status();
-               if (s == TCP_ACCEPTED) {
-                  if (client.available()) {
-                     return client;
-                  }
-               }
-            }
-         }
-     }
-
-     struct tcp_struct *default_client = NULL;
-     return WiFiClient(default_client);
-   }
+    WiFiClient available() {
+        return WiFiClient(lwipServer::available());
+    }
 };
-
-#endif
\ No newline at end of file
diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index 91919e995..998214302 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -21,17 +21,61 @@ static err_t _lwip_tcp_sent_callback(void* arg, struct tcp_pcb* tpcb, u16_t len)
 void _lwip_tcp_err_callback(void *arg, err_t err);
 
 lwipClient::lwipClient()
-    : pcb(NULL) {
+: tcp_info(new tcp_info_t)
+{
+    // tcp_info = std::shared_ptr<tcp_info_t>(new tcp_info_t);
+    this->tcp_info->state         = TCP_NONE;
+    this->tcp_info->pcb           = nullptr;
+    this->tcp_info->server        = nullptr;
+    this->tcp_info->pbuf_offset   = 0;
+    this->tcp_info->pbuf_head     = nullptr;
 }
 
 /* Deprecated constructor. Keeps compatibility with W5100 architecture
 sketches but sock is ignored. */
-lwipClient::lwipClient(uint8_t sock)
-    : pcb(NULL) {
+lwipClient::lwipClient(uint8_t sock) {}
+
+lwipClient::lwipClient(struct tcp_pcb* pcb, lwipServer *server)
+: tcp_info(new tcp_info_t)
+{
+    // tcp_info = std::shared_ptr<tcp_info_t>(new tcp_info_t);
+    this->tcp_info->state         = TCP_ACCEPTED;
+    this->tcp_info->pcb           = pcb;
+    this->tcp_info->server        = server;
+    this->tcp_info->pbuf_offset   = 0;
+    this->tcp_info->pbuf_head     = nullptr;
+
+    tcp_arg(this->tcp_info->pcb, this);
+
+    tcp_err(this->tcp_info->pcb, _lwip_tcp_err_callback); // FIXME make this a user callback?
+
+    /* initialize LwIP tcp_recv callback function */
+    tcp_recv(this->tcp_info->pcb, _lwip_tcp_recv_callback);
+
+    /* initialize LwIP tcp_sent callback function */
+    tcp_sent(this->tcp_info->pcb, _lwip_tcp_sent_callback); // FIXME do we actually need it?
 }
 
-lwipClient::lwipClient(struct tcp_pcb* pcb)
-: pcb(pcb) {
+lwipClient::lwipClient(const lwipClient& c)
+: tcp_info(c.tcp_info), _timeout(c._timeout), _ip(c._ip) {
+}
+
+lwipClient& lwipClient::operator=(const lwipClient& rhs) {
+    this->tcp_info =    rhs.tcp_info;
+    this->_timeout =    rhs._timeout;
+    this->_ip =         rhs._ip;
+    return *this;
+}
+
+lwipClient::lwipClient(lwipClient&& c)
+: tcp_info(std::move(c.tcp_info)), _timeout(std::move(c._timeout)), _ip(std::move(c._ip)) {
+}
+
+lwipClient& lwipClient::operator=(lwipClient&& rhs) {
+    this->tcp_info =    std::move(rhs.tcp_info);
+    this->_timeout =    std::move(rhs._timeout);
+    this->_ip =         std::move(rhs._ip);
+    return *this;
 }
 
 lwipClient::~lwipClient() {
@@ -51,27 +95,30 @@ int lwipClient::connect(const char* host, uint16_t port) {
 
 int lwipClient::connect(IPAddress ip, uint16_t port) {
     err_t err = ERR_OK;
-    this->pcb = tcp_new();
 
-    if(this->pcb == nullptr) {
+    // the connect method is only connected when trying to connect a client to a server
+    // and not when a client is created out of a listening socket
+    this->tcp_info->pcb = tcp_new();
+
+    if(this->tcp_info->pcb == nullptr) {
         // return ; // TODO find the proper error code
         return err;
     }
 
-    tcp_err(this->pcb, _lwip_tcp_err_callback); // FIXME make this a user callback?
+    tcp_err(this->tcp_info->pcb, _lwip_tcp_err_callback); // FIXME make this a user callback?
     if(err != ERR_OK) {
         return err;
     }
 
-    this->state = TCP_NONE;
+    this->tcp_info->state = TCP_NONE;
 
-    tcp_arg(this->pcb, this);
+    tcp_arg(this->tcp_info->pcb, this);
 
     this->_ip = fromArduinoIP(ip);
 
     // FIXME this doesn't include timeout of connection, does lwip have it by default?
     err = tcp_connect(
-        this->pcb, &this->_ip, port, // FIXME check if _ip gets copied
+        this->tcp_info->pcb, &this->_ip, port, // FIXME check if _ip gets copied
         _lwip_tcp_connected_callback // FIXME we need to define a static private function
     );
     return err;
@@ -107,7 +154,7 @@ err_t lwipClient::connected_callback(struct tcp_pcb* tpcb, err_t err) {
         return ERR_ARG;
     }
 
-    this->state = TCP_CONNECTED;
+    this->tcp_info->state = TCP_CONNECTED;
 
     /* initialize LwIP tcp_recv callback function */
     tcp_recv(tpcb, _lwip_tcp_recv_callback);
@@ -183,14 +230,14 @@ err_t lwipClient::recv_callback(struct tcp_pcb* tpcb, struct pbuf* p, err_t err)
         return ERR_OK;
     }
     arduino::lock();
-    if(this->state == TCP_CONNECTED) {
-        if (this->pbuf_head == nullptr) {
+    if(this->tcp_info->state == TCP_CONNECTED) {
+        if (this->tcp_info->pbuf_head == nullptr) {
             // no need to increment the references of the pbuf,
             // since it is already 1 and lwip shifts the control to this code
-            this->pbuf_head = p;
+            this->tcp_info->pbuf_head = p;
         } else {
-            // no need to increment the references of p, since it is already 1 and the only reference is this->pbuf_head->next
-            pbuf_cat(this->pbuf_head, p);
+            // no need to increment the references of p, since it is already 1 and the only reference is this->tcp_info->pbuf_head->next
+            pbuf_cat(this->tcp_info->pbuf_head, p);
         }
 
         ret_err = ERR_OK;
@@ -211,14 +258,14 @@ size_t lwipClient::write(const uint8_t* buffer, size_t size) {
     uint8_t bytes_to_send = 0;
 
     do {
-        bytes_to_send = min(size - (buffer - buffer_cursor), tcp_sndbuf(this->pcb));
+        bytes_to_send = min(size - (buffer - buffer_cursor), tcp_sndbuf(this->tcp_info->pcb));
 
         /*
          * TODO: Look into the following flags, especially for write of 1 byte
          * TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
          * TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will not be set on last segment sent
          */
-        err_t res = tcp_write(this->pcb, buffer_cursor, bytes_to_send, TCP_WRITE_FLAG_COPY);
+        err_t res = tcp_write(this->tcp_info->pcb, buffer_cursor, bytes_to_send, TCP_WRITE_FLAG_COPY);
 
         if(res == ERR_OK) {
             buffer_cursor += bytes_to_send;
@@ -228,7 +275,7 @@ size_t lwipClient::write(const uint8_t* buffer, size_t size) {
 
         // TODO understand if the tcp_write will send data if the buffer is not full
         // force send only if we filled the send buffer
-        // if (ERR_OK != tcp_output(this->pcb)) {
+        // if (ERR_OK != tcp_output(this->tcp_info->pcb)) {
         //     // return 0;
         //     break;
         // }
@@ -246,11 +293,11 @@ int lwipClient::read() {
 }
 
 int lwipClient::read(uint8_t* buffer, size_t size) {
-    if(size==0 || buffer==nullptr || this->pbuf_head==nullptr) {
+    if(size==0 || buffer==nullptr || this->tcp_info->pbuf_head==nullptr) {
         return 0; // TODO extend checks
     }
     // copy data from the lwip buffer to the app provided buffer
-    // TODO look into pbuf_get_contiguous(this->pbuf_head, buffer_cursor, len);
+    // TODO look into pbuf_get_contiguous(this->tcp_info->pbuf_head, buffer_cursor, len);
     // pbuf_get_contiguous: returns the pointer to the payload if size <= pbuf.len
     //      otherwise copies data in the user provided buffer. This can be used in a callback paradigm,
     //      in order to avoid memcpy data
@@ -261,7 +308,7 @@ int lwipClient::read(uint8_t* buffer, size_t size) {
      * we need to account that
      */
     arduino::lock();
-    uint16_t copied = pbuf_copy_partial(this->pbuf_head, buffer, size, this->pbuf_offset);
+    uint16_t copied = pbuf_copy_partial(this->tcp_info->pbuf_head, buffer, size, this->tcp_info->pbuf_offset);
 
     this->free_pbuf_chain(copied);
     // __enable_irq();
@@ -278,31 +325,31 @@ int lwipClient::peek() {
     }
 
     arduino::lock();
-    b = pbuf_get_at(this->pbuf_head, 0); // TODO test this
+    b = pbuf_get_at(this->tcp_info->pbuf_head, 0); // TODO test this
     arduino::unlock();
 
     return b;
 }
 
 void lwipClient::flush() {
-    if ((this->pcb == NULL)) {
+    if ((this->tcp_info->pcb == NULL)) {
         return;
     }
-    tcp_output(this->pcb);
+    tcp_output(this->tcp_info->pcb);
 }
 
 void lwipClient::stop() {
-    tcp_recv(this->pcb, nullptr);
-    tcp_sent(this->pcb, nullptr);
-    tcp_poll(this->pcb, nullptr, 0);
-    tcp_err(this->pcb, nullptr);
-    tcp_accept(this->pcb, nullptr);
+    tcp_recv(this->tcp_info->pcb, nullptr);
+    tcp_sent(this->tcp_info->pcb, nullptr);
+    tcp_poll(this->tcp_info->pcb, nullptr, 0);
+    tcp_err(this->tcp_info->pcb, nullptr);
+    tcp_accept(this->tcp_info->pcb, nullptr);
 
-    if(this->pcb != nullptr) {
-        err_t err = tcp_close(this->pcb);
-        this->state = TCP_CLOSING;
+    if(this->tcp_info->pcb != nullptr) {
+        err_t err = tcp_close(this->tcp_info->pcb);
+        this->tcp_info->state = TCP_CLOSING;
 
-        this->pcb = nullptr;
+        this->tcp_info->pcb = nullptr;
 
         // FIXME if err != ERR_OK retry, there may be memory issues, retry?
     }
@@ -315,25 +362,26 @@ void lwipClient::stop() {
 }
 
 uint8_t lwipClient::connected() {
-    return this->state != TCP_NONE; //TODO
+    return this->tcp_info->state == TCP_CONNECTED || this->tcp_info->state == TCP_ACCEPTED;
 }
 
 uint8_t lwipClient::status() {
-    if (this == NULL) {
+    if (this == nullptr) {
         return TCP_NONE;
     }
-    return this->state;
+    return this->tcp_info->state;
 }
 
 // the next function allows us to use the client returned by
 // EthernetServer::available() as the condition in an if-statement.
 
 lwipClient::operator bool() {
-    return (this->pcb != nullptr);
+    return (this->tcp_info->pcb != nullptr);
 }
 
 bool lwipClient::operator==(const lwipClient& rhs) {
-    // return pcb == rhs.this && this->pcb == rhs.this->pcb;
+    // return pcb == rhs.this && this->tcp_info->pcb == rhs.this->tcp_info->pcb;
+    return this->tcp_info == rhs.tcp_info;
 }
 
 /* This function is not a function defined by Arduino. This is a function
@@ -347,13 +395,13 @@ uint8_t lwipClient::getSocketNumber() {
 // this allows the user to avoid using temporary buffers
 size_t lwipClient::read_until_token(
     const uint8_t* buffer, uint16_t buffer_size, char* token, bool &found) {
-    if(buffer_size==0 || buffer==nullptr || this->pbuf_head==nullptr) {
+    if(buffer_size==0 || buffer==nullptr || this->tcp_info->pbuf_head==nullptr) {
         return 0; // TODO extend checks
     }
     arduino::lock();
     // TODO check that the buffer size is less than the token len
 
-    uint16_t offset=this->pbuf_offset;
+    uint16_t offset=this->tcp_info->pbuf_offset;
     /* iterate over pbufs until:
     * - the first occurrence of token
     * - the provided buffer is full
@@ -362,7 +410,7 @@ size_t lwipClient::read_until_token(
     size_t tkn_len = strlen(token);
 
     // FIXME if we have already found the token we hare wasting time to check the entire buffer again
-    uint16_t position = pbuf_memfind(this->pbuf_head, token, tkn_len, this->pbuf_offset); // TODO check efficiency of this function
+    uint16_t position = pbuf_memfind(this->tcp_info->pbuf_head, token, tkn_len, this->tcp_info->pbuf_offset); // TODO check efficiency of this function
     uint16_t buf_copy_len = buffer_size;
 
     // TODO triple check the indices of these conditions
@@ -383,7 +431,7 @@ size_t lwipClient::read_until_token(
         found = false;
     }
 
-    uint16_t copied = pbuf_copy_partial(this->pbuf_head, (uint8_t*)buffer, buf_copy_len, this->pbuf_offset);
+    uint16_t copied = pbuf_copy_partial(this->tcp_info->pbuf_head, (uint8_t*)buffer, buf_copy_len, this->tcp_info->pbuf_offset);
 
     this->free_pbuf_chain(copied);
     arduino::unlock();
@@ -399,12 +447,12 @@ void lwipClient::free_pbuf_chain(uint16_t copied) {
      * taking into account the previously not entirely consumed pbuf
      */
     uint32_t tobefreed = 0;
-    copied += this->pbuf_offset;
+    copied += this->tcp_info->pbuf_offset;
 
     // in order to clean up the chain we need to find the pbuf in the last pbuf in the chain
     // that got completely consumed by the application, dechain it from it successor and delete the chain before it
 
-    struct pbuf *head = this->pbuf_head, *last=head, *prev=nullptr; // FIXME little optimization prev can be substituted by last->next
+    struct pbuf *head = this->tcp_info->pbuf_head, *last=head, *prev=nullptr; // FIXME little optimization prev can be substituted by last->next
 
     while(last!=nullptr && last->len + tobefreed <= copied) {
         tobefreed += last->len;
@@ -415,22 +463,20 @@ void lwipClient::free_pbuf_chain(uint16_t copied) {
     // dechain if we are not at the end of the chain (last == nullptr)
     // and if we haven't copied entirely the first pbuf (prev == nullptr) (head == last)
     // if we reached the end of the chain set the this pbuf pointer to nullptr
-    if(prev != nullptr && last != nullptr) {
+    if(prev != nullptr) {
         prev->next = nullptr;
-        this->pbuf_head = last;
-    } if(last == nullptr) {
-        this->pbuf_head = nullptr;
+        this->tcp_info->pbuf_head = last;
     }
 
-    // the chain that is referenced by head is detached by the one referenced by this->pbuf_head
+    // the chain that is referenced by head is detached by the one referenced by this->tcp_info->pbuf_head
     // free the chain if we haven't copied entirely the first pbuf (prev == nullptr)
-    if(this->pbuf_head != head) {
+    if(this->tcp_info->pbuf_head != head) {
         uint8_t refs = pbuf_free(head);
     }
 
-    this->pbuf_offset = copied - tobefreed; // This offset should be referenced to the first pbuf in queue
+    this->tcp_info->pbuf_offset = copied - tobefreed; // This offset should be referenced to the first pbuf in queue
 
     // acknowledge the received data
-    tcp_recved(this->pcb, copied);
+    tcp_recved(this->tcp_info->pcb, copied);
     arduino::unlock();
 }
diff --git a/libraries/lwIpWrapper/src/lwipClient.h b/libraries/lwIpWrapper/src/lwipClient.h
index 5188f42e1..17e9985d7 100644
--- a/libraries/lwIpWrapper/src/lwipClient.h
+++ b/libraries/lwIpWrapper/src/lwipClient.h
@@ -4,22 +4,31 @@
 #include <IPAddress.h>
 #include <Print.h>
 #include "CNetIf.h"
+#include "lwipServer.h"
+#include <memory>
 
 // TODO improve documentation
 
+enum _tcp_state_t: uint8_t {
+    TCP_NONE = 0,
+    TCP_ACCEPTED,
+    TCP_CONNECTED,
+    TCP_CLOSING
+};
+
 class lwipClient : public arduino::Client {
 public:
     lwipClient();
     lwipClient(uint8_t sock);
-    lwipClient(struct tcp_pcb* tcpClient); // FIXME this should be a private constructor, friend of Server
+    lwipClient(struct tcp_pcb* tcpClient, lwipServer *server); // FIXME this should be a private constructor, friend of Server
 
     // disable copy constructor
-    lwipClient(const lwipClient&) = delete;
-    void operator=(const lwipClient&) = delete;
+    lwipClient(const lwipClient&);
+    lwipClient& operator=(const lwipClient&);
 
     // keep move constructor
     lwipClient(lwipClient&&);
-    void operator=(lwipClient&&);
+    lwipClient& operator=(lwipClient&&);
 
     virtual ~lwipClient();
 
@@ -30,7 +39,9 @@ class lwipClient : public arduino::Client {
     virtual size_t write(uint8_t);
     virtual size_t write(const uint8_t* buf, size_t size);
 
-    inline virtual int available() { return this->pbuf_head == nullptr ? 0 : this->pbuf_head->tot_len; }
+    inline virtual int available() {
+        return this->tcp_info->pbuf_head == nullptr ? 0 : this->tcp_info->pbuf_head->tot_len - this->tcp_info->pbuf_offset;
+    }
 
     virtual int read();
     virtual int read(uint8_t* buf, size_t size);
@@ -56,20 +67,20 @@ class lwipClient : public arduino::Client {
 
     uint8_t getSocketNumber();
     virtual uint16_t localPort() {
-        return (this->pcb->local_port);
+        return (this->tcp_info->pcb->local_port);
     };
     virtual IPAddress remoteIP() {
-        return (IPAddress(this->pcb->remote_ip.addr));
+        return (IPAddress(this->tcp_info->pcb->remote_ip.addr));
     };
     virtual uint16_t remotePort() {
-        return (this->pcb->remote_port);
+        return (this->tcp_info->pcb->remote_port);
     };
     void setConnectionTimeout(uint16_t timeout) {
         _timeout = timeout;
     }
 
     void bindCNetIf(CNetIf &n) {
-        tcp_bind_netif(this->pcb, n.getNi());
+        tcp_bind_netif(this->tcp_info->pcb, n.getNi());
     }
 
     friend class lwipServer;
@@ -77,18 +88,17 @@ class lwipClient : public arduino::Client {
     using Print::write;
 
 private:
-    enum _tcp_state_t: uint8_t {
-        TCP_NONE = 0,
-        // TCP_ACCEPTED,
-        TCP_CONNECTED,
-        TCP_CLOSING
+    // TCP related info of the socket
+    struct tcp_info_t {
+        _tcp_state_t state;
+        struct pbuf* pbuf_head;
+        struct tcp_pcb* pcb;
+        uint16_t pbuf_offset;
+        // this pointer is used to correctly clean the lwipClient when created from a server class
+        lwipServer* server;
     };
 
-    // TCP related info of the socket
-    _tcp_state_t state =        TCP_NONE;
-    struct pbuf* pbuf_head =    nullptr;
-    struct tcp_pcb* pcb =       nullptr;
-    uint16_t pbuf_offset =      0;
+    std::shared_ptr<tcp_info_t> tcp_info;
 
     uint16_t _timeout = 10000;
     ip_addr_t _ip;
@@ -100,3 +110,5 @@ class lwipClient : public arduino::Client {
     friend err_t _lwip_tcp_recv_callback(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err);
     friend err_t _lwip_tcp_connected_callback(void* arg, struct tcp_pcb* tpcb, err_t err);
 };
+
+inline const lwipClient CLIENT_NONE(nullptr, nullptr);
\ No newline at end of file
diff --git a/libraries/lwIpWrapper/src/lwipServer.cpp b/libraries/lwIpWrapper/src/lwipServer.cpp
index 768b5887e..151b1f9d8 100644
--- a/libraries/lwIpWrapper/src/lwipServer.cpp
+++ b/libraries/lwIpWrapper/src/lwipServer.cpp
@@ -6,38 +6,38 @@ extern "C" {
 #include "lwipClient.h"
 #include "lwipServer.h"
 
+err_t tcp_accept_callback(void* arg, struct tcp_pcb* newpcb, err_t err);
+
+lwipServer::lwipServer(const IPAddress &listen_ip, uint16_t port)
+: _port(port), listen_address(listen_ip), server_pcb(nullptr) {
+}
+
 lwipServer::lwipServer(uint16_t port)
-{
-    _port = port;
-    for (int i = 0; i < MAX_CLIENT; i++) {
-        _tcp_client[i] = {};
-    }
-    _tcp_server = {};
+: _port(port), listen_address(INADDR_NONE), server_pcb(nullptr) {
 }
 
 void lwipServer::begin()
 {
-    if (_tcp_server.pcb != NULL) {
+    if (server_pcb != NULL) {
         return;
     }
 
-    _tcp_server.pcb = tcp_new();
+    server_pcb = tcp_new();
 
-    if (_tcp_server.pcb == NULL) {
+    if (server_pcb == NULL) {
         return;
     }
 
-    tcp_arg(_tcp_server.pcb, &_tcp_client);
-    _tcp_server.state = TCP_NONE;
+    tcp_arg(server_pcb, this);
 
-    if (ERR_OK != tcp_bind(_tcp_server.pcb, IP_ADDR_ANY, _port)) {
-        memp_free(MEMP_TCP_PCB, _tcp_server.pcb);
-        _tcp_server.pcb = NULL;
+    if (ERR_OK != tcp_bind(server_pcb, IP_ADDR_ANY, _port)) { // TODO Put the listen address here
+        memp_free(MEMP_TCP_PCB, server_pcb);
+        server_pcb = NULL;
         return;
     }
 
-    _tcp_server.pcb = tcp_listen(_tcp_server.pcb);
-    tcp_accept(_tcp_server.pcb, tcp_accept_callback);
+    server_pcb = tcp_listen(server_pcb);
+    tcp_accept(server_pcb, tcp_accept_callback);
 }
 
 void lwipServer::begin(uint16_t port)
@@ -46,41 +46,64 @@ void lwipServer::begin(uint16_t port)
     begin();
 }
 
-void lwipServer::accept()
-{
-    /* Free client if disconnected */
-    for (int n = 0; n < MAX_CLIENT; n++) {
-        if (_tcp_client[n] != NULL) {
-            // lwipClient client(_tcp_client[n]); // FIXME
-            // if (client.status() == TCP_CLOSING) {
-            //     mem_free(_tcp_client[n]);
-            //     _tcp_client[n] = NULL;
-            // }
+// void lwipServer::clean() {
+//     // this index is a placeholder to the first empty position that needs to be filled
+//     int8_t moveto = -1;
+
+//     new_size = size;
+//     // remove all the closed clients
+//     for (int i=0; i < size; i++) {
+//         if (client.status() == TCP_CLOSING) {
+//             delete clients[n];
+//             clients[n] = nullptr;
+//             new_size--;
+
+//             if(moveto == -1) {
+//                 moveto = n;
+//             }
+//         }
+
+//         if(moveto >= 0 && clients[n] != nullptr) {
+//             clients[moveto] = clients[n];
+//             clients[n] = nullptr;
+//             moveto++;
+//         }
+//     }
+
+//     size = new_size
+// }
+
+void lwipServer::remove(lwipClient* client) {
+    bool found = false;
+    for (int i=0; i < size; i++) {
+        if(found) {
+            clients[i-1] = clients[i];
+        } else if(*client == *clients[i]) {
+            found = true;
         }
     }
+
+    delete clients[--size];
+    clients[size] = nullptr;
+}
+
+void lwipServer::accept(struct tcp_pcb* new_client) {
+    // this->clean();
+
+    if(size < MAX_CLIENT-1) {
+        clients[size] = new lwipClient(new_client, this);
+        size++;
+        clients_available++;
+    }
 }
 
 lwipClient lwipServer::available()
 {
-    accept();
-
-    for (int n = 0; n < MAX_CLIENT; n++) {
-        if (_tcp_client[n] != NULL) {
-            if (_tcp_client[n]->pcb != NULL) {
-                // lwipClient client(_tcp_client[n]); // FIXME
-                // uint8_t s = client.status();
-                // if (s == TCP_ACCEPTED) {
-                //     if (client.available()) {
-                //         return client;
-                //     }
-                // }
-            }
-        }
+    if(size > 0 && clients_available>0) {
+        return *clients[size-clients_available--]; // TODO verify index
+    } else {
+        return CLIENT_NONE;
     }
-
-    struct tcp_struct* default_client = NULL;
-    return lwipClient(); // FIXME
-    // return lwipClient(default_client);
 }
 
 size_t lwipServer::write(uint8_t b)
@@ -88,23 +111,29 @@ size_t lwipServer::write(uint8_t b)
     return write(&b, 1);
 }
 
-size_t lwipServer::write(const uint8_t* buffer, size_t size)
-{
-    size_t n = 0;
-
-    accept();
-
-    for (int n = 0; n < MAX_CLIENT; n++) {
-        if (_tcp_client[n] != NULL) {
-            if (_tcp_client[n]->pcb != NULL) {
-                // lwipClient client(_tcp_client[n]);
-                // uint8_t s = client.status();
-                // if (s == TCP_ACCEPTED) {
-                //     n += client.write(buffer, size);
-                // }
-            }
-        }
+size_t lwipServer::write(const uint8_t* buffer, size_t size) {
+    size_t written=0;
+    // this->clean();
+
+    for (int i = 0; i < MAX_CLIENT; i++) {
+        written += clients[i]->write(buffer, size);
     }
 
-    return n;
+    return written;
 }
+
+err_t tcp_accept_callback(void* arg, struct tcp_pcb* newpcb, err_t err) {
+    lwipServer* server = (lwipServer*) arg;
+    err_t ret_err;
+
+    /* set priority for the newly accepted tcp connection newpcb */
+    tcp_setprio(newpcb, TCP_PRIO_MIN);
+
+    if ((arg != NULL) && (ERR_OK == err)) {
+        server->accept(newpcb);
+    } else {
+        tcp_close(newpcb);
+        ret_err = ERR_ARG;
+    }
+    return ret_err;
+}
\ No newline at end of file
diff --git a/libraries/lwIpWrapper/src/lwipServer.h b/libraries/lwIpWrapper/src/lwipServer.h
index e7d4456b0..c08514adb 100644
--- a/libraries/lwIpWrapper/src/lwipServer.h
+++ b/libraries/lwIpWrapper/src/lwipServer.h
@@ -1,27 +1,52 @@
-#ifndef _ARDUINO_LWIP_SERVER_H
-#define _ARDUINO_LWIP_SERVER_H
+#pragma once
 
-#include "Server.h"
-#include "lwipTcp.h"
+#include <Server.h>
+#include <IPAddress.h>
+#include <CNetIf.h>
+// #include "lwipClient.h"
 
 class lwipClient;
 
-class lwipServer : public Server {
-protected:
-    uint16_t _port;
-    struct tcp_struct _tcp_server;
-    struct tcp_struct* _tcp_client[MAX_CLIENT];
-
-    void accept(void);
-
+class lwipServer: public Server {
 public:
+    lwipServer(const IPAddress &listen_ip = INADDR_NONE, uint16_t port = 80);
     lwipServer(uint16_t port = 80);
     lwipClient available();
+
     virtual void begin();
     virtual void begin(uint16_t port);
     virtual size_t write(uint8_t);
     virtual size_t write(const uint8_t* buf, size_t size);
     using Print::write;
-};
 
-#endif
\ No newline at end of file
+    void bindCNetIf(CNetIf &n) {
+        tcp_bind_netif(this->server_pcb, n.getNi());
+    }
+protected:
+    /*
+     * these methods are used to insert and remove lwipClients in lwipServer class
+     * the idea is the following:
+     * - a client is created by lwip, we wrap it around a lwipClient class and and returned by available() call
+     * - the client is inserted in the last empty position of the clients array
+     * - when a client connection is closed (by calling stop on it or delete on the client)
+     *   the server is notified and the remove() method is called thus the client is removed from the server list.
+     */
+    void accept(struct tcp_pcb* new_client);
+    // void clean();
+    void remove(lwipClient* client);
+
+    uint16_t _port;
+    const IPAddress &listen_address;
+    tcp_pcb* server_pcb;
+    uint16_t port;
+
+    // this array in managed as a fixed size list with all the null values in the back.
+    // size var indicates how full is the array
+    // the total number of pcb should be MEMP_NUM_TCP_PCB, -1 that is the server PCB
+private:
+    uint8_t size=0, clients_available=0;
+    lwipClient* clients[MAX_CLIENT-1];
+
+    friend err_t tcp_accept_callback(void* arg, struct tcp_pcb* newpcb, err_t err);
+    friend lwipClient;
+};

From ed6886dc104b33eae51e6cc5b5e391fab56c552f Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 4 Jan 2024 15:12:08 +0100
Subject: [PATCH 27/79] fixing bug of missing nullptr check

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index d944e4a34..c6668a638 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -293,7 +293,10 @@ CNetIf::CNetIf(NetworkDriver *driver)
 
 int CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
     CLwipIf::getInstance(); // This call is required in order to setup the network stack
-    driver->begin();
+
+    if(driver != nullptr) {
+        driver->begin();
+    }
 
     ip_addr_t _ip = fromArduinoIP(ip);
     ip_addr_t _nm = fromArduinoIP(nm);

From 10a05bd2e36c4585d2e68d3089b4d61249636422 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 4 Jan 2024 15:12:30 +0100
Subject: [PATCH 28/79] removed unused types

---
 libraries/lwIpWrapper/src/lwipTypes.h | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/libraries/lwIpWrapper/src/lwipTypes.h b/libraries/lwIpWrapper/src/lwipTypes.h
index c108e15c7..a0282de4d 100644
--- a/libraries/lwIpWrapper/src/lwipTypes.h
+++ b/libraries/lwIpWrapper/src/lwipTypes.h
@@ -6,16 +6,6 @@
 #include <functional>
 
 /* Exported types ------------------------------------------------------------*/
-/* TCP connection state */
-typedef enum {
-    TCP_NONE = 0,
-    TCP_CONNECTED,
-    TCP_RECEIVED,
-    TCP_SENT,
-    TCP_ACCEPTED,
-    TCP_CLOSING,
-} tcp_client_states;
-
 /* Struct to store received data */
 struct pbuf_data {
     struct pbuf* p; // the packet buffer that was received
@@ -31,11 +21,4 @@ struct udp_struct {
     std::function<void()> onDataArrival;
 };
 
-/* TCP structure */
-struct tcp_struct {
-    struct tcp_pcb* pcb; /* pointer on the current tcp_pcb */
-    struct pbuf_data data;
-    tcp_client_states state; /* current connection state */
-};
-
 #endif

From 42063d1c08108b28f1c04a0bb686f1461c79f426 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 4 Jan 2024 15:12:36 +0100
Subject: [PATCH 29/79] minor changes on file

---
 libraries/lwIpWrapper/src/CNetIf.h | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 8fd964455..8c96f1fe3 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -1,6 +1,5 @@
 #pragma once
 
-// #define LWIP_USE_TIMER
 #define UNUSED(x) (void)(x)
 
 #define USE_LWIP_AS_LIBRARY
@@ -80,7 +79,7 @@ typedef enum {
     NI_ETHERNET
 } NetIfType_t;
 
-#define MAX_CLIENT 32
+#define MAX_CLIENT MEMP_NUM_TCP_PCB
 #define MAX_DHCP_TRIES 4
 #define TIMEOUT_DNS_REQUEST 10000U
 
@@ -97,7 +96,6 @@ typedef enum {
 #define INVALID_RESPONSE -4
 
 class CLwipIf;
-class lwipClient;
 
 /* Base class implements DHCP, derived class will switch it on or off */
 class CNetIf: public NetworkInterface {
@@ -331,7 +329,7 @@ class CLwipIf {
     // functions that handle DNS resolution
     // DNS servers are also set by dhcp
 #if LWIP_DNS
-    // add a dns server, priority set to 0 means it is the first being queryed, -1 means the last
+    // add a dns server, priority set to 0 means it is the first being queried, -1 means the last
     uint8_t addDnsServer(const IPAddress& aDNSServer, int8_t priority=-1);
     void clearDnsServers();
 

From e41294a2dcc515c026f57e26e6dab0a039787942 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 5 Jan 2024 10:47:01 +0100
Subject: [PATCH 30/79] Revert "defined Wifi global variable for network
 interface"

This reverts commit c2db54f2cf7de3f985e7f70c84d49e0faf68567c.
---
 libraries/WiFi/src/WiFi.cpp | 620 ++++++++++++++++++------------------
 libraries/WiFi/src/WiFiC3.h |   4 -
 2 files changed, 310 insertions(+), 314 deletions(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index d91a212ef..59e742a0f 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -1,331 +1,331 @@
 #include "WiFiC3.h"
 
 
-// extern "C" void dhcps_start(struct netif *netif);
+extern "C" void dhcps_start(struct netif *netif);
 
 
 
-// /* -------------------------------------------------------------------------- */
-// CWifi::CWifi() : _timeout(50000), ni(nullptr) {
-// }
-// /* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+CWifi::CWifi() : _timeout(50000), ni(nullptr) {
+}
+/* -------------------------------------------------------------------------- */
 
-// /* -------------------------------------------------------------------------- */
-// const char* CWifi::firmwareVersion() {
-// /* -------------------------------------------------------------------------- */   
-//    /* silly "dummy" implementation to keep compatibility, at the present
-//       the WiFi fw does not return any version number */
-//    return WIFI_FIRMWARE_LATEST_VERSION;
-// }
+/* -------------------------------------------------------------------------- */
+const char* CWifi::firmwareVersion() {
+/* -------------------------------------------------------------------------- */   
+   /* silly "dummy" implementation to keep compatibility, at the present
+      the WiFi fw does not return any version number */
+   return WIFI_FIRMWARE_LATEST_VERSION;
+}
 
 
 
-// /* -------------------------------------------------------------------------- */
-// int CWifi::begin(const char* ssid) {
-// /* -------------------------------------------------------------------------- */   
-//    ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
-//    CLwipIf::getInstance().connectToAp(ssid, nullptr);
-//    if(ni != nullptr && !_useStaticIp) {
-//       ni->DhcpStart();
-//    }
+/* -------------------------------------------------------------------------- */
+int CWifi::begin(const char* ssid) {
+/* -------------------------------------------------------------------------- */   
+   ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
+   CLwipIf::getInstance().connectToAp(ssid, nullptr);
+   if(ni != nullptr && !_useStaticIp) {
+      ni->DhcpStart();
+   }
    
-//    return CLwipIf::getInstance().getWifiStatus();
-// }
+   return CLwipIf::getInstance().getWifiStatus();
+}
 
 
-// /* -------------------------------------------------------------------------- */
-// int CWifi::begin(const char* ssid, const char *passphrase) {
-// /* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
+int CWifi::begin(const char* ssid, const char *passphrase) {
+/* -------------------------------------------------------------------------- */   
    
-//    ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
-//    CLwipIf::getInstance().connectToAp(ssid, passphrase); 
-//    if(ni != nullptr && !_useStaticIp) {
-//       ni->DhcpStart();
-//    }
+   ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
+   CLwipIf::getInstance().connectToAp(ssid, passphrase); 
+   if(ni != nullptr && !_useStaticIp) {
+      ni->DhcpStart();
+   }
    
-//    return CLwipIf::getInstance().getWifiStatus();
-// }
-
-// /* passphrase is needed so a default one will be set */
-// /* -------------------------------------------------------------------------- */
-// uint8_t CWifi::beginAP(const char *ssid) {
-// /* -------------------------------------------------------------------------- */   
-//    return beginAP(ssid,1);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// uint8_t CWifi::beginAP(const char *ssid, uint8_t channel) {
-// /* -------------------------------------------------------------------------- */   
-//    return beginAP(ssid,nullptr,channel);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// uint8_t CWifi::beginAP(const char *ssid, const char* passphrase) {
-// /* -------------------------------------------------------------------------- */   
-//    return beginAP(ssid,passphrase,1);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// uint8_t CWifi::beginAP(const char *ssid, const char* passphrase, uint8_t channel) {
-// /* -------------------------------------------------------------------------- */   
+   return CLwipIf::getInstance().getWifiStatus();
+}
+
+/* passphrase is needed so a default one will be set */
+/* -------------------------------------------------------------------------- */
+uint8_t CWifi::beginAP(const char *ssid) {
+/* -------------------------------------------------------------------------- */   
+   return beginAP(ssid,1);
+}
+
+/* -------------------------------------------------------------------------- */
+uint8_t CWifi::beginAP(const char *ssid, uint8_t channel) {
+/* -------------------------------------------------------------------------- */   
+   return beginAP(ssid,nullptr,channel);
+}
+
+/* -------------------------------------------------------------------------- */
+uint8_t CWifi::beginAP(const char *ssid, const char* passphrase) {
+/* -------------------------------------------------------------------------- */   
+   return beginAP(ssid,passphrase,1);
+}
+
+/* -------------------------------------------------------------------------- */
+uint8_t CWifi::beginAP(const char *ssid, const char* passphrase, uint8_t channel) {
+/* -------------------------------------------------------------------------- */   
    
-//    ni = CLwipIf::getInstance().get(NI_WIFI_SOFTAP);
-//    CLwipIf::getInstance().startSoftAp(ssid,passphrase,channel); 
-//    if(ni != nullptr) {
+   ni = CLwipIf::getInstance().get(NI_WIFI_SOFTAP);
+   CLwipIf::getInstance().startSoftAp(ssid,passphrase,channel); 
+   if(ni != nullptr) {
       
-//       dhcps_start(ni->getNi());
-//    }
+      dhcps_start(ni->getNi());
+   }
    
-//    return CLwipIf::getInstance().getWifiStatus();   
-// }
-
-
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::config(IPAddress local_ip) {
-// /* -------------------------------------------------------------------------- */   
-//    IPAddress _nm(255, 255, 255, 0);
-//    IPAddress _gw = local_ip;
-//    _gw[3] = 1;
-
-//    _config(local_ip, _gw, _nm);
-// }
-
-// extern uint8_t *IpAddress2uint8(IPAddress a);
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) {
-// /* -------------------------------------------------------------------------- */    
-//    _useStaticIp = local_ip != INADDR_NONE;
-//    if(ni != nullptr) {
-//       ni->DhcpStop();
-//       ni->DhcpNotUsed();
-//       IP_ADDR4(&ni->ip, local_ip[0], local_ip[1], local_ip[2], local_ip[3]);
-//       IP_ADDR4(&ni->gw, gateway[0], gateway[1], gateway[2], gateway[3]);
-//       IP_ADDR4(&ni->nm, subnet[0], subnet[1], subnet[2], subnet[3]);
-//    }
-//    else {
-//       CNetIf::default_ip = local_ip;
-//       CNetIf::default_nm = subnet; 
-//       CNetIf::default_gw = gateway;
-//       CNetIf::default_dhcp_server_ip = local_ip;
-//    }
-// }
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::config(IPAddress local_ip, IPAddress dns_server) {
-// /* -------------------------------------------------------------------------- */   
-//    config(local_ip);
-//    CLwipIf::getInstance().addDns(dns_server);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway) {
-// /* -------------------------------------------------------------------------- */   
-//    IPAddress _nm(255, 255, 255, 0);
-//    _config(local_ip, gateway, _nm);
-//    CLwipIf::getInstance().addDns(dns_server);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) {
-// /* -------------------------------------------------------------------------- */
-//    _config(local_ip, gateway, subnet);
-//    CLwipIf::getInstance().addDns(dns_server);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::setDNS(IPAddress dns_server1) {
-// /* -------------------------------------------------------------------------- */   
-//    CLwipIf::getInstance().addDns(dns_server1);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::setDNS(IPAddress dns_server1, IPAddress dns_server2) {
-// /* -------------------------------------------------------------------------- */   
-//    CLwipIf::getInstance().addDns(dns_server1);
-//    CLwipIf::getInstance().addDns(dns_server2);
+   return CLwipIf::getInstance().getWifiStatus();   
+}
+
+
+
+/* -------------------------------------------------------------------------- */
+void CWifi::config(IPAddress local_ip) {
+/* -------------------------------------------------------------------------- */   
+   IPAddress _nm(255, 255, 255, 0);
+   IPAddress _gw = local_ip;
+   _gw[3] = 1;
+
+   _config(local_ip, _gw, _nm);
+}
+
+extern uint8_t *IpAddress2uint8(IPAddress a);
+
+/* -------------------------------------------------------------------------- */
+void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) {
+/* -------------------------------------------------------------------------- */    
+   _useStaticIp = local_ip != INADDR_NONE;
+   if(ni != nullptr) {
+      ni->DhcpStop();
+      ni->DhcpNotUsed();
+      IP_ADDR4(&ni->ip, local_ip[0], local_ip[1], local_ip[2], local_ip[3]);
+      IP_ADDR4(&ni->gw, gateway[0], gateway[1], gateway[2], gateway[3]);
+      IP_ADDR4(&ni->nm, subnet[0], subnet[1], subnet[2], subnet[3]);
+   }
+   else {
+      CNetIf::default_ip = local_ip;
+      CNetIf::default_nm = subnet; 
+      CNetIf::default_gw = gateway;
+      CNetIf::default_dhcp_server_ip = local_ip;
+   }
+}
+
+/* -------------------------------------------------------------------------- */
+void CWifi::config(IPAddress local_ip, IPAddress dns_server) {
+/* -------------------------------------------------------------------------- */   
+   config(local_ip);
+   CLwipIf::getInstance().addDns(dns_server);
+}
+
+/* -------------------------------------------------------------------------- */
+void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway) {
+/* -------------------------------------------------------------------------- */   
+   IPAddress _nm(255, 255, 255, 0);
+   _config(local_ip, gateway, _nm);
+   CLwipIf::getInstance().addDns(dns_server);
+}
+
+/* -------------------------------------------------------------------------- */
+void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) {
+/* -------------------------------------------------------------------------- */
+   _config(local_ip, gateway, subnet);
+   CLwipIf::getInstance().addDns(dns_server);
+}
+
+/* -------------------------------------------------------------------------- */
+void CWifi::setDNS(IPAddress dns_server1) {
+/* -------------------------------------------------------------------------- */   
+   CLwipIf::getInstance().addDns(dns_server1);
+}
+
+/* -------------------------------------------------------------------------- */
+void CWifi::setDNS(IPAddress dns_server1, IPAddress dns_server2) {
+/* -------------------------------------------------------------------------- */   
+   CLwipIf::getInstance().addDns(dns_server1);
+   CLwipIf::getInstance().addDns(dns_server2);
    
-// }
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::setHostname(const char* name) {
-// /* -------------------------------------------------------------------------- */   
-//    if(ni != nullptr) {
-//       ni->setHostname(name);
-//    }
-// }
-
-// /* -------------------------------------------------------------------------- */
-// int CWifi::disconnect() {
-// /* -------------------------------------------------------------------------- */   
-//    CLwipIf::getInstance().disconnectFromAp();
-// }
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::end(void) {
-// /* -------------------------------------------------------------------------- */   
-
-// }
-
-// /* -------------------------------------------------------------------------- */
-// uint8_t* CWifi::macAddress(uint8_t* mac) {
-// /* -------------------------------------------------------------------------- */   
-//    if(ni != nullptr) {
-//       if(ni->getMacAddress(mac) == WL_MAC_ADDR_LENGTH) {
-//          return mac;
-//       }
-//    }
-//    memset(mac,0x00,6);
-//    return mac;
-// }
-
-// /* -------------------------------------------------------------------------- */
-// int8_t CWifi::scanNetworks() {
-// /* -------------------------------------------------------------------------- */   
-//    ni = CLwipIf::getInstance().get(NI_WIFI_STATION);   
-//    if(CLwipIf::getInstance().scanForAp() == ESP_CONTROL_OK) {
-//       return CLwipIf::getInstance().getApNum();
-//    }
-//    return 0;
-// }
+}
+
+/* -------------------------------------------------------------------------- */
+void CWifi::setHostname(const char* name) {
+/* -------------------------------------------------------------------------- */   
+   if(ni != nullptr) {
+      ni->setHostname(name);
+   }
+}
+
+/* -------------------------------------------------------------------------- */
+int CWifi::disconnect() {
+/* -------------------------------------------------------------------------- */   
+   CLwipIf::getInstance().disconnectFromAp();
+}
+
+/* -------------------------------------------------------------------------- */
+void CWifi::end(void) {
+/* -------------------------------------------------------------------------- */   
+
+}
+
+/* -------------------------------------------------------------------------- */
+uint8_t* CWifi::macAddress(uint8_t* mac) {
+/* -------------------------------------------------------------------------- */   
+   if(ni != nullptr) {
+      if(ni->getMacAddress(mac) == WL_MAC_ADDR_LENGTH) {
+         return mac;
+      }
+   }
+   memset(mac,0x00,6);
+   return mac;
+}
+
+/* -------------------------------------------------------------------------- */
+int8_t CWifi::scanNetworks() {
+/* -------------------------------------------------------------------------- */   
+   ni = CLwipIf::getInstance().get(NI_WIFI_STATION);   
+   if(CLwipIf::getInstance().scanForAp() == ESP_CONTROL_OK) {
+      return CLwipIf::getInstance().getApNum();
+   }
+   return 0;
+}
  
-// /* -------------------------------------------------------------------------- */   
-// IPAddress CWifi::localIP() {
-// /* -------------------------------------------------------------------------- */   
-//    if(ni != nullptr) {
-//       return IPAddress(ni->getIpAdd());   
-//    }
-//    return IPAddress((uint32_t)0);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// IPAddress CWifi::subnetMask() {
-// /* -------------------------------------------------------------------------- */
-//    if(ni != nullptr) {
-//       return IPAddress(ni->getNmAdd());   
-//    }
-//    return IPAddress((uint32_t)0);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// IPAddress CWifi::gatewayIP() {
-// /* -------------------------------------------------------------------------- */   
-//    if(ni != nullptr) {
-//       return IPAddress(ni->getGwAdd());   
-//    }
-//    return IPAddress((uint32_t)0);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// IPAddress CWifi::dnsIP(int n) {
-//    return CLwipIf::getInstance().getDns(n);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// const char* CWifi::SSID(uint8_t networkItem) {  
-//    return CLwipIf::getInstance().getSSID(networkItem);
-// }
-// /* -------------------------------------------------------------------------- */ 
-
-// /* -------------------------------------------------------------------------- */
-// int32_t CWifi::RSSI(uint8_t networkItem) {
-//    return CLwipIf::getInstance().getRSSI(networkItem);
-// }
-// /* -------------------------------------------------------------------------- */ 
-
-// /* -------------------------------------------------------------------------- */
-// uint8_t CWifi::encryptionType(uint8_t networkItem) {
-//    return CLwipIf::getInstance().getEncrType(networkItem);
-// }
-// /* -------------------------------------------------------------------------- */ 
-
-// /* -------------------------------------------------------------------------- */
-// uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) {
-//    return CLwipIf::getInstance().getBSSID(networkItem,bssid);
-// }
-// /* -------------------------------------------------------------------------- */ 
-
-// /* -------------------------------------------------------------------------- */
-// uint8_t CWifi::channel(uint8_t networkItem) { 
-//    return CLwipIf::getInstance().getChannel(networkItem);
-// }
-// /* -------------------------------------------------------------------------- */ 
-
-// /* -------------------------------------------------------------------------- */ 
-// const char* CWifi::SSID() {
-// /* -------------------------------------------------------------------------- */    
-//    if(ni != nullptr) {
-//       return ni->getSSID();
-//    }
-//    return ""; 
-// }
-
-// /* -------------------------------------------------------------------------- */ 
-// uint8_t* CWifi::BSSID(uint8_t* bssid) {
-// /* -------------------------------------------------------------------------- */    
-//    if(ni != nullptr) {
-//       return ni->getBSSID(bssid);
-//    }
-//    return nullptr;
-// }
-
-// /* -------------------------------------------------------------------------- */ 
-// int32_t CWifi::RSSI() {
-// /* -------------------------------------------------------------------------- */    
-//    if(ni != nullptr) {
-//       return ni->getRSSI();
-//    }
-//    return 0;
-// }
-
-// /* -------------------------------------------------------------------------- */ 
-// uint8_t CWifi::encryptionType() {
-// /* -------------------------------------------------------------------------- */    
-//    if(ni != nullptr) {
-//       return ni->getEncryptionType();
-//    }
-//    return 0;
-// }
-
-// /* -------------------------------------------------------------------------- */
-// uint8_t CWifi::status() {
-// /* -------------------------------------------------------------------------- */   
-//    return CLwipIf::getInstance().getWifiStatus(); 
-// }
-
-// /* -------------------------------------------------------------------------- */
-// int CWifi::hostByName(const char* aHostname, IPAddress& aResult) {
-// /* -------------------------------------------------------------------------- */   
-//    return CLwipIf::getInstance().getHostByName(aHostname,aResult);
-// }
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::lowPowerMode() {
-// /* -------------------------------------------------------------------------- */   
-//    CLwipIf::getInstance().setLowPowerMode();
-// }
-
-// /* -------------------------------------------------------------------------- */
-// void CWifi::noLowPowerMode() {
-// /* -------------------------------------------------------------------------- */   
-//    CLwipIf::getInstance().resetLowPowerMode();
-// }
-
-// uint8_t CWifi::reasonCode() {
-//    return 0;
-// }
-
-// unsigned long CWifi::getTime() {
-//    return 0;
-// }
-
-
-
-// void CWifi::setTimeout(unsigned long timeout) {
-//    (void)(timeout);  
-// }
-
-
-// CWifi WiFi;
+/* -------------------------------------------------------------------------- */   
+IPAddress CWifi::localIP() {
+/* -------------------------------------------------------------------------- */   
+   if(ni != nullptr) {
+      return IPAddress(ni->getIpAdd());   
+   }
+   return IPAddress((uint32_t)0);
+}
+
+/* -------------------------------------------------------------------------- */
+IPAddress CWifi::subnetMask() {
+/* -------------------------------------------------------------------------- */
+   if(ni != nullptr) {
+      return IPAddress(ni->getNmAdd());   
+   }
+   return IPAddress((uint32_t)0);
+}
+
+/* -------------------------------------------------------------------------- */
+IPAddress CWifi::gatewayIP() {
+/* -------------------------------------------------------------------------- */   
+   if(ni != nullptr) {
+      return IPAddress(ni->getGwAdd());   
+   }
+   return IPAddress((uint32_t)0);
+}
+
+/* -------------------------------------------------------------------------- */
+IPAddress CWifi::dnsIP(int n) {
+   return CLwipIf::getInstance().getDns(n);
+}
+
+/* -------------------------------------------------------------------------- */
+const char* CWifi::SSID(uint8_t networkItem) {  
+   return CLwipIf::getInstance().getSSID(networkItem);
+}
+/* -------------------------------------------------------------------------- */ 
+
+/* -------------------------------------------------------------------------- */
+int32_t CWifi::RSSI(uint8_t networkItem) {
+   return CLwipIf::getInstance().getRSSI(networkItem);
+}
+/* -------------------------------------------------------------------------- */ 
+
+/* -------------------------------------------------------------------------- */
+uint8_t CWifi::encryptionType(uint8_t networkItem) {
+   return CLwipIf::getInstance().getEncrType(networkItem);
+}
+/* -------------------------------------------------------------------------- */ 
+
+/* -------------------------------------------------------------------------- */
+uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) {
+   return CLwipIf::getInstance().getBSSID(networkItem,bssid);
+}
+/* -------------------------------------------------------------------------- */ 
+
+/* -------------------------------------------------------------------------- */
+uint8_t CWifi::channel(uint8_t networkItem) { 
+   return CLwipIf::getInstance().getChannel(networkItem);
+}
+/* -------------------------------------------------------------------------- */ 
+
+/* -------------------------------------------------------------------------- */ 
+const char* CWifi::SSID() {
+/* -------------------------------------------------------------------------- */    
+   if(ni != nullptr) {
+      return ni->getSSID();
+   }
+   return ""; 
+}
+
+/* -------------------------------------------------------------------------- */ 
+uint8_t* CWifi::BSSID(uint8_t* bssid) {
+/* -------------------------------------------------------------------------- */    
+   if(ni != nullptr) {
+      return ni->getBSSID(bssid);
+   }
+   return nullptr;
+}
+
+/* -------------------------------------------------------------------------- */ 
+int32_t CWifi::RSSI() {
+/* -------------------------------------------------------------------------- */    
+   if(ni != nullptr) {
+      return ni->getRSSI();
+   }
+   return 0;
+}
+
+/* -------------------------------------------------------------------------- */ 
+uint8_t CWifi::encryptionType() {
+/* -------------------------------------------------------------------------- */    
+   if(ni != nullptr) {
+      return ni->getEncryptionType();
+   }
+   return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+uint8_t CWifi::status() {
+/* -------------------------------------------------------------------------- */   
+   return CLwipIf::getInstance().getWifiStatus(); 
+}
+
+/* -------------------------------------------------------------------------- */
+int CWifi::hostByName(const char* aHostname, IPAddress& aResult) {
+/* -------------------------------------------------------------------------- */   
+   return CLwipIf::getInstance().getHostByName(aHostname,aResult);
+}
+
+/* -------------------------------------------------------------------------- */
+void CWifi::lowPowerMode() {
+/* -------------------------------------------------------------------------- */   
+   CLwipIf::getInstance().setLowPowerMode();
+}
+
+/* -------------------------------------------------------------------------- */
+void CWifi::noLowPowerMode() {
+/* -------------------------------------------------------------------------- */   
+   CLwipIf::getInstance().resetLowPowerMode();
+}
+
+uint8_t CWifi::reasonCode() {
+   return 0;
+}
+
+unsigned long CWifi::getTime() {
+   return 0;
+}
+
+
+
+void CWifi::setTimeout(unsigned long timeout) {
+   (void)(timeout);  
+}
+
+
+CWifi WiFi;
 
diff --git a/libraries/WiFi/src/WiFiC3.h b/libraries/WiFi/src/WiFiC3.h
index 3abde9230..ffe55cbf3 100644
--- a/libraries/WiFi/src/WiFiC3.h
+++ b/libraries/WiFi/src/WiFiC3.h
@@ -8,11 +8,7 @@
 
 #define WIFI_FIRMWARE_LATEST_VERSION "1.5.0"
 
-#ifdef ARDUINO_PORTENTA_C33
 // TODO Instantiate the drivers for wifi with default configuration parameters
-// ESPHostFGDriver WifiDriver;
 
 // Instantiate a global variable from CWifiStation calling it WiFi
 
-inline CWifiStation WiFi;
-#endif // ARDUINO_PORTENTA_C33

From f20a25817207770b79aa23ab4e71156b9dd8964d Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 5 Jan 2024 10:47:46 +0100
Subject: [PATCH 31/79] Revert "reimplementing wifi handle"

This reverts commit d1e284b37c2c95d62d21bc6482571670ee2c89f1.
---
 libraries/WiFi/src/WiFiC3.h | 267 +++++++++++++++++++++++++++++++++++-
 1 file changed, 260 insertions(+), 7 deletions(-)

diff --git a/libraries/WiFi/src/WiFiC3.h b/libraries/WiFi/src/WiFiC3.h
index ffe55cbf3..2995cdcf9 100644
--- a/libraries/WiFi/src/WiFiC3.h
+++ b/libraries/WiFi/src/WiFiC3.h
@@ -1,14 +1,267 @@
-#pragma once
+#ifndef C_ARDUINO_WIFI_H
+#define C_ARDUINO_WIFI_H
 
 #include "CNetIf.h"
 
-#include "WiFiClient.h"
-#include "WiFiServer.h"
-#include "WiFiUdp.h"
-
 #define WIFI_FIRMWARE_LATEST_VERSION "1.5.0"
 
-// TODO Instantiate the drivers for wifi with default configuration parameters
+class CWifi {
+private: 
+   void _config(IPAddress local_ip, IPAddress gateway, IPAddress subnet);
+   unsigned long _timeout;
+   bool _useStaticIp = false;
+   CNetIf *ni;
+    
+public:
+    CWifi();
+
+    /*
+     * Get firmware version
+     */
+    static const char* firmwareVersion();
+
+
+    /* 
+     * Start WiFi connection for OPEN networks 
+     * param ssid: Pointer to the SSID string.
+     */
+    int begin(const char* ssid);
+
+    
+
+    /* Start WiFi connection with passphrase
+     * the most secure supported mode will be automatically selected
+     *
+     * param ssid: Pointer to the SSID string.
+     * param passphrase: Passphrase. Valid characters in a passphrase
+     *        must be between ASCII 32-126 (decimal).
+     */
+    int begin(const char* ssid, const char *passphrase);
+
+    /* connect as Access Point with  a standard passphrase */
+    uint8_t beginAP(const char *ssid);
+    uint8_t beginAP(const char *ssid, uint8_t channel);
+    uint8_t beginAP(const char *ssid, const char* passphrase);
+    uint8_t beginAP(const char *ssid, const char* passphrase, uint8_t channel);
+
+    /* Change IP configuration settings disabling the DHCP client
+        *
+        * param local_ip:  Static IP configuration
+        */
+    void config(IPAddress local_ip);
+
+    /* Change IP configuration settings disabling the DHCP client
+        *
+        * param local_ip:  Static IP configuration
+   * param dns_server:     IP configuration for DNS server 1
+        */
+    void config(IPAddress local_ip, IPAddress dns_server);
+
+    /* Change IP configuration settings disabling the DHCP client
+        *
+        * param local_ip:  Static IP configuration
+   * param dns_server:     IP configuration for DNS server 1
+        * param gateway :  Static gateway configuration
+        */
+    void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway);
+
+    /* Change IP configuration settings disabling the DHCP client
+        *
+        * param local_ip:  Static IP configuration
+   * param dns_server:     IP configuration for DNS server 1
+        * param gateway:   Static gateway configuration
+        * param subnet:    Static Subnet mask
+        */
+    void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet);
+    
+    /* Change DNS IP configuration
+     *
+     * param dns_server1: IP configuration for DNS server 1
+     */
+    void setDNS(IPAddress dns_server1);
+
+    /* Change DNS IP configuration
+     *
+     * param dns_server1: IP configuration for DNS server 1
+     * param dns_server2: IP configuration for DNS server 2
+     *
+     */
+    void setDNS(IPAddress dns_server1, IPAddress dns_server2);
+
+
+    /* Set the hostname used for DHCP requests
+     *
+     * param name: hostname to set
+     *
+     */
+    void setHostname(const char* name);
+
+    /*
+     * Disconnect from the network
+     *
+     * return: one value of wl_status_t enum
+     */
+    int disconnect(void);
+
+    void end(void);
+
+    /*
+     * Get the interface MAC address.
+     *
+     * return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
+     * 
+     * the value returned by this function is meaningfull only if called 
+     * afert a begin (both begin or beginAP) or a ScanNetwork function
+     * otherwise an empty mac address is returned
+     */
+    uint8_t* macAddress(uint8_t* mac);
+
+    /*
+     * Get the interface IP address.
+     *
+     * return: IP address value
+     */
+    IPAddress localIP();
+
+    /*
+     * Get the interface subnet mask address.
+     *
+     * return: subnet mask address value
+     */
+    IPAddress subnetMask();
+
+    /*
+     * Get the gateway IP address.
+     *
+     * return: gateway IP address value
+     */
+   IPAddress gatewayIP();
+
+   /*
+    * Get the DNS server IP address.
+    *
+    * return: DNS server IP address value
+    */
+   IPAddress dnsIP(int n = 0);
 
-// Instantiate a global variable from CWifiStation calling it WiFi
+    /*
+     * Return the current SSID associated with the network
+     *
+     * return: ssid string
+     */
+    const char* SSID();
+
+    /*
+      * Return the current BSSID associated with the network.
+      * It is the MAC address of the Access Point
+      *
+      * return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
+      */
+    uint8_t* BSSID(uint8_t* bssid);
+
+    /*
+      * Return the current RSSI/Received Signal Strength in dBm)
+      * associated with the network
+      *
+      * return: signed value
+      */
+    int32_t RSSI();
+
+    /*
+      * Return the Encryption Type associated with the network
+      *
+      * return: one value of wl_enc_type enum
+      */
+    uint8_t encryptionType();
+
+    /*
+     * Start scan WiFi networks available
+     *
+     * return: Number of discovered networks
+     */
+    int8_t scanNetworks();
+
+    /*
+     * Return the SSID discovered during the network scan.
+     *
+     * param networkItem: specify from which network item want to get the information
+    *
+     * return: SSID string of the specified item on the networks scanned list
+     */
+    const char*   SSID(uint8_t networkItem);
+
+    /*
+     * Return the encryption type of the networks discovered during the scanNetworks
+     *
+     * param networkItem: specify from which network item want to get the information
+    *
+     * return: encryption type (enum wl_enc_type) of the specified item on the networks scanned list
+
+     enum wl_enc_type : 
+      ENC_TYPE_WEP,
+      ENC_TYPE_WPA,
+      ENC_TYPE_WPA2,
+      ENC_TYPE_WPA2_ENTERPRISE,
+      ENC_TYPE_WPA3,
+      ENC_TYPE_NONE,
+      ENC_TYPE_AUTO,
+
+      ENC_TYPE_UNKNOWN = 255
+      
+     */
+    uint8_t encryptionType(uint8_t networkItem);
+
+    uint8_t* BSSID(uint8_t networkItem, uint8_t* bssid);
+    uint8_t channel(uint8_t networkItem);
+
+    /*
+     * Return the RSSI of the networks discovered during the scanNetworks
+     *
+     * param networkItem: specify from which network item want to get the information
+    *
+     * return: signed value of RSSI of the specified item on the networks scanned list
+     */
+    int32_t RSSI(uint8_t networkItem);
+
+    /*
+     * Return Connection status.
+     *
+     * return: one of the value defined in wl_status_t
+     */
+    uint8_t status();
+
+    /*
+     * Return The deauthentication reason code.
+     *
+     * return: the deauthentication reason code
+     */
+    uint8_t reasonCode();
+
+    /*
+     * Resolve the given hostname to an IP address.
+     * param aHostname: Name to be resolved
+     * param aResult: IPAddress structure to store the returned IP address
+     * result: 1 if aIPAddrString was successfully converted to an IP address,
+     *          else error code
+     */
+    int hostByName(const char* aHostname, IPAddress& aResult);
+
+    unsigned long getTime();
+
+    void lowPowerMode();
+    void noLowPowerMode();
+
+    
+
+    void setTimeout(unsigned long timeout);
+
+    
+};
+
+extern CWifi WiFi;
+
+#include "WiFiClient.h"
+#include "WiFiServer.h"
+#include "WiFiUdp.h"
 
+#endif

From 552a15309139f8075a7edf09c6490be5dc089d78 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 5 Jan 2024 11:01:10 +0100
Subject: [PATCH 32/79] applying a codestyle

---
 libraries/WiFi/src/WiFi.cpp | 314 ++++++++++++++++++------------------
 libraries/WiFi/src/WiFiC3.h | 101 ++++++------
 2 files changed, 198 insertions(+), 217 deletions(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index 59e742a0f..4dda14acc 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -12,320 +12,312 @@ CWifi::CWifi() : _timeout(50000), ni(nullptr) {
 
 /* -------------------------------------------------------------------------- */
 const char* CWifi::firmwareVersion() {
-/* -------------------------------------------------------------------------- */   
-   /* silly "dummy" implementation to keep compatibility, at the present
+/* -------------------------------------------------------------------------- */
+    /* silly "dummy" implementation to keep compatibility, at the present
       the WiFi fw does not return any version number */
-   return WIFI_FIRMWARE_LATEST_VERSION;
+    return WIFI_FIRMWARE_LATEST_VERSION;
 }
 
 
 
 /* -------------------------------------------------------------------------- */
 int CWifi::begin(const char* ssid) {
-/* -------------------------------------------------------------------------- */   
-   ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
-   CLwipIf::getInstance().connectToAp(ssid, nullptr);
-   if(ni != nullptr && !_useStaticIp) {
-      ni->DhcpStart();
-   }
-   
-   return CLwipIf::getInstance().getWifiStatus();
+/* -------------------------------------------------------------------------- */
+    ni = CLwipIf::getInstance().get(NI_WIFI_STATION); // FIXME
+    CLwipIf::getInstance().connectToAp(ssid, nullptr); // FIXME
+    if(ni != nullptr && !_useStaticIp) {
+        ni->DhcpStart();
+    }
+
+    return CLwipIf::getInstance().getWifiStatus(); // FIXME
 }
 
 
 /* -------------------------------------------------------------------------- */
 int CWifi::begin(const char* ssid, const char *passphrase) {
-/* -------------------------------------------------------------------------- */   
-   
-   ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
-   CLwipIf::getInstance().connectToAp(ssid, passphrase); 
-   if(ni != nullptr && !_useStaticIp) {
-      ni->DhcpStart();
-   }
-   
-   return CLwipIf::getInstance().getWifiStatus();
+/* -------------------------------------------------------------------------- */
+    ni = CLwipIf::getInstance().get(NI_WIFI_STATION); // FIXME
+    CLwipIf::getInstance().connectToAp(ssid, passphrase); // FIXME
+    if(ni != nullptr && !_useStaticIp) {
+        ni->DhcpStart();
+    }
+
+    return CLwipIf::getInstance().getWifiStatus(); // FIXME
 }
 
 /* passphrase is needed so a default one will be set */
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::beginAP(const char *ssid) {
-/* -------------------------------------------------------------------------- */   
-   return beginAP(ssid,1);
+/* -------------------------------------------------------------------------- */
+    return beginAP(ssid,1);
 }
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::beginAP(const char *ssid, uint8_t channel) {
-/* -------------------------------------------------------------------------- */   
-   return beginAP(ssid,nullptr,channel);
+/* -------------------------------------------------------------------------- */
+    return beginAP(ssid,nullptr,channel);
 }
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::beginAP(const char *ssid, const char* passphrase) {
-/* -------------------------------------------------------------------------- */   
-   return beginAP(ssid,passphrase,1);
+/* -------------------------------------------------------------------------- */
+    return beginAP(ssid,passphrase,1);
 }
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::beginAP(const char *ssid, const char* passphrase, uint8_t channel) {
-/* -------------------------------------------------------------------------- */   
-   
-   ni = CLwipIf::getInstance().get(NI_WIFI_SOFTAP);
-   CLwipIf::getInstance().startSoftAp(ssid,passphrase,channel); 
-   if(ni != nullptr) {
-      
-      dhcps_start(ni->getNi());
-   }
-   
-   return CLwipIf::getInstance().getWifiStatus();   
+/* -------------------------------------------------------------------------- */
+    ni = CLwipIf::getInstance().get(NI_WIFI_SOFTAP); // FIXME
+    CLwipIf::getInstance().startSoftAp(ssid,passphrase,channel); // FIXME
+    if(ni != nullptr) {
+        dhcps_start(ni->getNi());
+    }
+
+    return CLwipIf::getInstance().getWifiStatus(); // FIXME
 }
 
 
 
 /* -------------------------------------------------------------------------- */
 void CWifi::config(IPAddress local_ip) {
-/* -------------------------------------------------------------------------- */   
-   IPAddress _nm(255, 255, 255, 0);
-   IPAddress _gw = local_ip;
-   _gw[3] = 1;
+/* -------------------------------------------------------------------------- */
+    IPAddress _nm(255, 255, 255, 0);
+    IPAddress _gw = local_ip;
+    _gw[3] = 1;
 
-   _config(local_ip, _gw, _nm);
+    _config(local_ip, _gw, _nm);
 }
 
 extern uint8_t *IpAddress2uint8(IPAddress a);
 
 /* -------------------------------------------------------------------------- */
 void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) {
-/* -------------------------------------------------------------------------- */    
-   _useStaticIp = local_ip != INADDR_NONE;
-   if(ni != nullptr) {
-      ni->DhcpStop();
-      ni->DhcpNotUsed();
-      IP_ADDR4(&ni->ip, local_ip[0], local_ip[1], local_ip[2], local_ip[3]);
-      IP_ADDR4(&ni->gw, gateway[0], gateway[1], gateway[2], gateway[3]);
-      IP_ADDR4(&ni->nm, subnet[0], subnet[1], subnet[2], subnet[3]);
-   }
-   else {
-      CNetIf::default_ip = local_ip;
-      CNetIf::default_nm = subnet; 
-      CNetIf::default_gw = gateway;
-      CNetIf::default_dhcp_server_ip = local_ip;
-   }
+/* -------------------------------------------------------------------------- */
+    _useStaticIp = local_ip != INADDR_NONE;
+    if(ni != nullptr) {
+        ni->DhcpStop();
+        ni->DhcpNotUsed();
+        IP_ADDR4(&ni->ip, local_ip[0], local_ip[1], local_ip[2], local_ip[3]);
+        IP_ADDR4(&ni->gw, gateway[0], gateway[1], gateway[2], gateway[3]);
+        IP_ADDR4(&ni->nm, subnet[0], subnet[1], subnet[2], subnet[3]);
+    }
+    else {
+        CNetIf::default_ip = local_ip;
+        CNetIf::default_nm = subnet;
+        CNetIf::default_gw = gateway;
+        CNetIf::default_dhcp_server_ip = local_ip;
+    }
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::config(IPAddress local_ip, IPAddress dns_server) {
-/* -------------------------------------------------------------------------- */   
-   config(local_ip);
-   CLwipIf::getInstance().addDns(dns_server);
+/* -------------------------------------------------------------------------- */
+    config(local_ip);
+    CLwipIf::getInstance().addDns(dns_server); // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway) {
-/* -------------------------------------------------------------------------- */   
-   IPAddress _nm(255, 255, 255, 0);
-   _config(local_ip, gateway, _nm);
-   CLwipIf::getInstance().addDns(dns_server);
+/* -------------------------------------------------------------------------- */
+    IPAddress _nm(255, 255, 255, 0);
+    _config(local_ip, gateway, _nm);
+    CLwipIf::getInstance().addDns(dns_server); // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) {
 /* -------------------------------------------------------------------------- */
-   _config(local_ip, gateway, subnet);
-   CLwipIf::getInstance().addDns(dns_server);
+    _config(local_ip, gateway, subnet);
+    CLwipIf::getInstance().addDns(dns_server); // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::setDNS(IPAddress dns_server1) {
-/* -------------------------------------------------------------------------- */   
-   CLwipIf::getInstance().addDns(dns_server1);
+/* -------------------------------------------------------------------------- */
+    CLwipIf::getInstance().addDns(dns_server1); // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::setDNS(IPAddress dns_server1, IPAddress dns_server2) {
-/* -------------------------------------------------------------------------- */   
-   CLwipIf::getInstance().addDns(dns_server1);
-   CLwipIf::getInstance().addDns(dns_server2);
-   
+/* -------------------------------------------------------------------------- */
+    CLwipIf::getInstance().addDns(dns_server1); // FIXME
+    CLwipIf::getInstance().addDns(dns_server2); // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::setHostname(const char* name) {
-/* -------------------------------------------------------------------------- */   
-   if(ni != nullptr) {
-      ni->setHostname(name);
-   }
+/* -------------------------------------------------------------------------- */
+    if(ni != nullptr) {
+        ni->setHostname(name);
+    }
 }
 
 /* -------------------------------------------------------------------------- */
 int CWifi::disconnect() {
-/* -------------------------------------------------------------------------- */   
-   CLwipIf::getInstance().disconnectFromAp();
+/* -------------------------------------------------------------------------- */
+    CLwipIf::getInstance().disconnectFromAp(); // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::end(void) {
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
 
 }
 
 /* -------------------------------------------------------------------------- */
 uint8_t* CWifi::macAddress(uint8_t* mac) {
-/* -------------------------------------------------------------------------- */   
-   if(ni != nullptr) {
-      if(ni->getMacAddress(mac) == WL_MAC_ADDR_LENGTH) {
-         return mac;
-      }
-   }
-   memset(mac,0x00,6);
-   return mac;
+/* -------------------------------------------------------------------------- */
+    if(ni != nullptr) {
+        if(ni->getMacAddress(mac) == WL_MAC_ADDR_LENGTH) {
+            return mac;
+        }
+    }
+    memset(mac,0x00,6);
+    return mac;
 }
 
 /* -------------------------------------------------------------------------- */
 int8_t CWifi::scanNetworks() {
-/* -------------------------------------------------------------------------- */   
-   ni = CLwipIf::getInstance().get(NI_WIFI_STATION);   
-   if(CLwipIf::getInstance().scanForAp() == ESP_CONTROL_OK) {
-      return CLwipIf::getInstance().getApNum();
-   }
-   return 0;
-}
- 
-/* -------------------------------------------------------------------------- */   
+/* -------------------------------------------------------------------------- */
+    ni = CLwipIf::getInstance().get(NI_WIFI_STATION); // FIXME
+    if(CLwipIf::getInstance().scanForAp() == ESP_CONTROL_OK) { // FIXME
+        return CLwipIf::getInstance().getApNum(); // FIXME
+    }
+    return 0;
+}
+
+/* -------------------------------------------------------------------------- */
 IPAddress CWifi::localIP() {
-/* -------------------------------------------------------------------------- */   
-   if(ni != nullptr) {
-      return IPAddress(ni->getIpAdd());   
-   }
-   return IPAddress((uint32_t)0);
+/* -------------------------------------------------------------------------- */
+    if(ni != nullptr) {
+        return IPAddress(ni->getIpAdd());
+    }
+    return IPAddress((uint32_t)0);
 }
 
 /* -------------------------------------------------------------------------- */
 IPAddress CWifi::subnetMask() {
 /* -------------------------------------------------------------------------- */
-   if(ni != nullptr) {
-      return IPAddress(ni->getNmAdd());   
-   }
-   return IPAddress((uint32_t)0);
+    if(ni != nullptr) {
+        return IPAddress(ni->getNmAdd());
+    }
+    return IPAddress((uint32_t)0);
 }
 
 /* -------------------------------------------------------------------------- */
 IPAddress CWifi::gatewayIP() {
-/* -------------------------------------------------------------------------- */   
-   if(ni != nullptr) {
-      return IPAddress(ni->getGwAdd());   
-   }
-   return IPAddress((uint32_t)0);
+/* -------------------------------------------------------------------------- */
+    if(ni != nullptr) {
+        return IPAddress(ni->getGwAdd());
+    }
+    return IPAddress((uint32_t)0);
 }
 
 /* -------------------------------------------------------------------------- */
 IPAddress CWifi::dnsIP(int n) {
-   return CLwipIf::getInstance().getDns(n);
+    return CLwipIf::getInstance().getDns(n); // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
-const char* CWifi::SSID(uint8_t networkItem) {  
-   return CLwipIf::getInstance().getSSID(networkItem);
+const char* CWifi::SSID(uint8_t networkItem) {
+    return CLwipIf::getInstance().getSSID(networkItem); // FIXME
 }
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
 int32_t CWifi::RSSI(uint8_t networkItem) {
-   return CLwipIf::getInstance().getRSSI(networkItem);
+    return CLwipIf::getInstance().getRSSI(networkItem); // FIXME
 }
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::encryptionType(uint8_t networkItem) {
-   return CLwipIf::getInstance().getEncrType(networkItem);
+    return CLwipIf::getInstance().getEncrType(networkItem); // FIXME
 }
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
 uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) {
-   return CLwipIf::getInstance().getBSSID(networkItem,bssid);
+    return CLwipIf::getInstance().getBSSID(networkItem,bssid); // FIXME
 }
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
-uint8_t CWifi::channel(uint8_t networkItem) { 
-   return CLwipIf::getInstance().getChannel(networkItem);
+uint8_t CWifi::channel(uint8_t networkItem) {
+    return CLwipIf::getInstance().getChannel(networkItem); // FIXME
 }
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 const char* CWifi::SSID() {
-/* -------------------------------------------------------------------------- */    
-   if(ni != nullptr) {
-      return ni->getSSID();
-   }
-   return ""; 
+/* -------------------------------------------------------------------------- */
+    if(ni != nullptr) {
+        return ni->getSSID();
+    }
+    return "";
 }
 
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 uint8_t* CWifi::BSSID(uint8_t* bssid) {
-/* -------------------------------------------------------------------------- */    
-   if(ni != nullptr) {
-      return ni->getBSSID(bssid);
-   }
-   return nullptr;
+/* -------------------------------------------------------------------------- */
+    if(ni != nullptr) {
+        return ni->getBSSID(bssid);
+    }
+    return nullptr;
 }
 
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 int32_t CWifi::RSSI() {
-/* -------------------------------------------------------------------------- */    
-   if(ni != nullptr) {
-      return ni->getRSSI();
-   }
-   return 0;
+/* -------------------------------------------------------------------------- */
+    if(ni != nullptr) {
+        return ni->getRSSI();
+    }
+    return 0;
 }
 
-/* -------------------------------------------------------------------------- */ 
+/* -------------------------------------------------------------------------- */
 uint8_t CWifi::encryptionType() {
-/* -------------------------------------------------------------------------- */    
-   if(ni != nullptr) {
-      return ni->getEncryptionType();
-   }
-   return 0;
+/* -------------------------------------------------------------------------- */
+    if(ni != nullptr) {
+        return ni->getEncryptionType();
+    }
+    return 0;
 }
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::status() {
-/* -------------------------------------------------------------------------- */   
-   return CLwipIf::getInstance().getWifiStatus(); 
+/* -------------------------------------------------------------------------- */
+    return CLwipIf::getInstance().getWifiStatus(); // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
 int CWifi::hostByName(const char* aHostname, IPAddress& aResult) {
-/* -------------------------------------------------------------------------- */   
-   return CLwipIf::getInstance().getHostByName(aHostname,aResult);
+/* -------------------------------------------------------------------------- */
+    return CLwipIf::getInstance().getHostByName(aHostname,aResult); // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::lowPowerMode() {
-/* -------------------------------------------------------------------------- */   
-   CLwipIf::getInstance().setLowPowerMode();
+/* -------------------------------------------------------------------------- */
+    CLwipIf::getInstance().setLowPowerMode(); // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::noLowPowerMode() {
-/* -------------------------------------------------------------------------- */   
-   CLwipIf::getInstance().resetLowPowerMode();
+/* -------------------------------------------------------------------------- */
+    CLwipIf::getInstance().resetLowPowerMode(); // FIXME
 }
 
 uint8_t CWifi::reasonCode() {
-   return 0;
+    return 0;
 }
 
 unsigned long CWifi::getTime() {
-   return 0;
+    return 0;
 }
 
 
 
 void CWifi::setTimeout(unsigned long timeout) {
-   (void)(timeout);  
+    (void)(timeout);
 }
-
-
-CWifi WiFi;
-
diff --git a/libraries/WiFi/src/WiFiC3.h b/libraries/WiFi/src/WiFiC3.h
index 2995cdcf9..2080e5383 100644
--- a/libraries/WiFi/src/WiFiC3.h
+++ b/libraries/WiFi/src/WiFiC3.h
@@ -1,17 +1,14 @@
-#ifndef C_ARDUINO_WIFI_H
-#define C_ARDUINO_WIFI_H
+#pragma once
 
 #include "CNetIf.h"
 
 #define WIFI_FIRMWARE_LATEST_VERSION "1.5.0"
 
 class CWifi {
-private: 
-   void _config(IPAddress local_ip, IPAddress gateway, IPAddress subnet);
-   unsigned long _timeout;
-   bool _useStaticIp = false;
-   CNetIf *ni;
-    
+private:
+    void _config(IPAddress local_ip, IPAddress gateway, IPAddress subnet);
+    unsigned long _timeout;
+    bool _useStaticIp = false;
 public:
     CWifi();
 
@@ -21,14 +18,12 @@ class CWifi {
     static const char* firmwareVersion();
 
 
-    /* 
-     * Start WiFi connection for OPEN networks 
+    /*
+     * Start WiFi connection for OPEN networks
      * param ssid: Pointer to the SSID string.
      */
     int begin(const char* ssid);
 
-    
-
     /* Start WiFi connection with passphrase
      * the most secure supported mode will be automatically selected
      *
@@ -51,29 +46,29 @@ class CWifi {
     void config(IPAddress local_ip);
 
     /* Change IP configuration settings disabling the DHCP client
-        *
-        * param local_ip:  Static IP configuration
-   * param dns_server:     IP configuration for DNS server 1
-        */
+     *
+     * param local_ip:  Static IP configuration
+     * param dns_server:     IP configuration for DNS server 1
+     */
     void config(IPAddress local_ip, IPAddress dns_server);
 
     /* Change IP configuration settings disabling the DHCP client
-        *
-        * param local_ip:  Static IP configuration
-   * param dns_server:     IP configuration for DNS server 1
-        * param gateway :  Static gateway configuration
-        */
+     *
+     * param local_ip:  Static IP configuration
+     * param dns_server:     IP configuration for DNS server 1
+     * param gateway :  Static gateway configuration
+     */
     void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway);
 
     /* Change IP configuration settings disabling the DHCP client
-        *
-        * param local_ip:  Static IP configuration
-   * param dns_server:     IP configuration for DNS server 1
-        * param gateway:   Static gateway configuration
-        * param subnet:    Static Subnet mask
-        */
+     *
+     * param local_ip:  Static IP configuration
+     * param dns_server:     IP configuration for DNS server 1
+     * param gateway:   Static gateway configuration
+     * param subnet:    Static Subnet mask
+     */
     void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet);
-    
+
     /* Change DNS IP configuration
      *
      * param dns_server1: IP configuration for DNS server 1
@@ -109,8 +104,8 @@ class CWifi {
      * Get the interface MAC address.
      *
      * return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
-     * 
-     * the value returned by this function is meaningfull only if called 
+     *
+     * the value returned by this function is meaningfull only if called
      * afert a begin (both begin or beginAP) or a ScanNetwork function
      * otherwise an empty mac address is returned
      */
@@ -135,14 +130,14 @@ class CWifi {
      *
      * return: gateway IP address value
      */
-   IPAddress gatewayIP();
+    IPAddress gatewayIP();
 
-   /*
-    * Get the DNS server IP address.
-    *
-    * return: DNS server IP address value
-    */
-   IPAddress dnsIP(int n = 0);
+    /*
+     * Get the DNS server IP address.
+     *
+     * return: DNS server IP address value
+     */
+    IPAddress dnsIP(int n = 0);
 
     /*
      * Return the current SSID associated with the network
@@ -194,20 +189,20 @@ class CWifi {
      * Return the encryption type of the networks discovered during the scanNetworks
      *
      * param networkItem: specify from which network item want to get the information
-    *
+     *
      * return: encryption type (enum wl_enc_type) of the specified item on the networks scanned list
 
-     enum wl_enc_type : 
-      ENC_TYPE_WEP,
-      ENC_TYPE_WPA,
-      ENC_TYPE_WPA2,
-      ENC_TYPE_WPA2_ENTERPRISE,
-      ENC_TYPE_WPA3,
-      ENC_TYPE_NONE,
-      ENC_TYPE_AUTO,
-
-      ENC_TYPE_UNKNOWN = 255
-      
+     * enum wl_enc_type :
+     *  ENC_TYPE_WEP,
+     *  ENC_TYPE_WPA,
+     *  ENC_TYPE_WPA2,
+     *  ENC_TYPE_WPA2_ENTERPRISE,
+     *  ENC_TYPE_WPA3,
+     *  ENC_TYPE_NONE,
+     *  ENC_TYPE_AUTO,
+     *
+     *  ENC_TYPE_UNKNOWN = 255
+     *
      */
     uint8_t encryptionType(uint8_t networkItem);
 
@@ -251,17 +246,11 @@ class CWifi {
     void lowPowerMode();
     void noLowPowerMode();
 
-    
-
     void setTimeout(unsigned long timeout);
-
-    
 };
 
-extern CWifi WiFi;
+inline CWifi WiFi;
 
 #include "WiFiClient.h"
 #include "WiFiServer.h"
 #include "WiFiUdp.h"
-
-#endif

From d2395979254f5c4735964b040cc02837a01274f6 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 8 Jan 2024 10:24:32 +0100
Subject: [PATCH 33/79] fixing EthernetServer class

---
 libraries/Ethernet/src/EthernetServer.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libraries/Ethernet/src/EthernetServer.h b/libraries/Ethernet/src/EthernetServer.h
index 302af571a..1dbf791d4 100644
--- a/libraries/Ethernet/src/EthernetServer.h
+++ b/libraries/Ethernet/src/EthernetServer.h
@@ -5,9 +5,10 @@
 
 class EthernetServer: public lwipServer {
 public:
+    EthernetServer(uint16_t port) : lwipServer(port) {}
     void begin() {
         lwipServer::begin();
-        this->bindCNetIf(WiFi);
+        this->bindCNetIf(Ethernet);
     }
 
     EthernetClient available() {

From 6e6c8376e92ebf9c4e298f35ca695bd3706dfb1e Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 8 Jan 2024 10:25:14 +0100
Subject: [PATCH 34/79] making WiFi class compile

---
 libraries/WiFi/src/WiFi.cpp | 234 +++++++++++++++++-------------------
 libraries/WiFi/src/WiFiC3.h |   8 ++
 2 files changed, 116 insertions(+), 126 deletions(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index 4dda14acc..6535732b8 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -6,7 +6,7 @@ extern "C" void dhcps_start(struct netif *netif);
 
 
 /* -------------------------------------------------------------------------- */
-CWifi::CWifi() : _timeout(50000), ni(nullptr) {
+CWifi::CWifi() : _timeout(50000) {
 }
 /* -------------------------------------------------------------------------- */
 
@@ -23,97 +23,85 @@ const char* CWifi::firmwareVersion() {
 /* -------------------------------------------------------------------------- */
 int CWifi::begin(const char* ssid) {
 /* -------------------------------------------------------------------------- */
-    ni = CLwipIf::getInstance().get(NI_WIFI_STATION); // FIXME
-    CLwipIf::getInstance().connectToAp(ssid, nullptr); // FIXME
-    if(ni != nullptr && !_useStaticIp) {
-        ni->DhcpStart();
-    }
-
-    return CLwipIf::getInstance().getWifiStatus(); // FIXME
+    WiFiStation.begin();
+    return WiFiStation.connectToAP(ssid, nullptr);
 }
 
 
 /* -------------------------------------------------------------------------- */
 int CWifi::begin(const char* ssid, const char *passphrase) {
 /* -------------------------------------------------------------------------- */
-    ni = CLwipIf::getInstance().get(NI_WIFI_STATION); // FIXME
-    CLwipIf::getInstance().connectToAp(ssid, passphrase); // FIXME
-    if(ni != nullptr && !_useStaticIp) {
-        ni->DhcpStart();
-    }
-
-    return CLwipIf::getInstance().getWifiStatus(); // FIXME
+    WiFiStation.begin();
+    return WiFiStation.connectToAP(ssid, passphrase);
 }
 
 /* passphrase is needed so a default one will be set */
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::beginAP(const char *ssid) {
 /* -------------------------------------------------------------------------- */
-    return beginAP(ssid,1);
+    WiFiSoftAP.begin();
+    return WiFiSoftAP.startSoftAp(ssid); // FIXME put default password here
 }
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::beginAP(const char *ssid, uint8_t channel) {
 /* -------------------------------------------------------------------------- */
-    return beginAP(ssid,nullptr,channel);
+    WiFiSoftAP.begin();
+    return WiFiSoftAP.startSoftAp(ssid, nullptr, channel);
 }
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::beginAP(const char *ssid, const char* passphrase) {
 /* -------------------------------------------------------------------------- */
-    return beginAP(ssid,passphrase,1);
+    WiFiSoftAP.begin();
+    return WiFiSoftAP.startSoftAp(ssid, passphrase);
 }
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::beginAP(const char *ssid, const char* passphrase, uint8_t channel) {
 /* -------------------------------------------------------------------------- */
-    ni = CLwipIf::getInstance().get(NI_WIFI_SOFTAP); // FIXME
-    CLwipIf::getInstance().startSoftAp(ssid,passphrase,channel); // FIXME
-    if(ni != nullptr) {
-        dhcps_start(ni->getNi());
-    }
-
-    return CLwipIf::getInstance().getWifiStatus(); // FIXME
+    WiFiSoftAP.begin();
+    return WiFiSoftAP.startSoftAp(ssid, passphrase, channel);
 }
 
 
 
 /* -------------------------------------------------------------------------- */
-void CWifi::config(IPAddress local_ip) {
+void CWifi::config(IPAddress local_ip) { // FIXME
 /* -------------------------------------------------------------------------- */
-    IPAddress _nm(255, 255, 255, 0);
-    IPAddress _gw = local_ip;
-    _gw[3] = 1;
+    // IPAddress _nm(255, 255, 255, 0);
+    // IPAddress _gw = local_ip;
+    // _gw[3] = 1;
 
-    _config(local_ip, _gw, _nm);
+    // _config(local_ip, _gw, _nm);
 }
 
 extern uint8_t *IpAddress2uint8(IPAddress a);
 
 /* -------------------------------------------------------------------------- */
-void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) {
+void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) { // FIXME
 /* -------------------------------------------------------------------------- */
-    _useStaticIp = local_ip != INADDR_NONE;
-    if(ni != nullptr) {
-        ni->DhcpStop();
-        ni->DhcpNotUsed();
-        IP_ADDR4(&ni->ip, local_ip[0], local_ip[1], local_ip[2], local_ip[3]);
-        IP_ADDR4(&ni->gw, gateway[0], gateway[1], gateway[2], gateway[3]);
-        IP_ADDR4(&ni->nm, subnet[0], subnet[1], subnet[2], subnet[3]);
-    }
-    else {
-        CNetIf::default_ip = local_ip;
-        CNetIf::default_nm = subnet;
-        CNetIf::default_gw = gateway;
-        CNetIf::default_dhcp_server_ip = local_ip;
-    }
+    // _useStaticIp = local_ip != INADDR_NONE;
+    // if(ni != nullptr) {
+    //     ni->DhcpStop();
+    //     ni->DhcpNotUsed();
+    //     IP_ADDR4(&ni->ip, local_ip[0], local_ip[1], local_ip[2], local_ip[3]);
+    //     IP_ADDR4(&ni->gw, gateway[0], gateway[1], gateway[2], gateway[3]);
+    //     IP_ADDR4(&ni->nm, subnet[0], subnet[1], subnet[2], subnet[3]);
+    // }
+    // else {
+    //     CNetIf::default_ip = local_ip;
+    //     CNetIf::default_nm = subnet;
+    //     CNetIf::default_gw = gateway;
+    //     CNetIf::default_dhcp_server_ip = local_ip;
+    // }
 }
 
 /* -------------------------------------------------------------------------- */
-void CWifi::config(IPAddress local_ip, IPAddress dns_server) {
+void CWifi::config(IPAddress local_ip, IPAddress dns_server) { // FIXME
 /* -------------------------------------------------------------------------- */
     config(local_ip);
-    CLwipIf::getInstance().addDns(dns_server); // FIXME
+    // CLwipIf::getInstance().addDns(dns_server);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -121,41 +109,41 @@ void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway)
 /* -------------------------------------------------------------------------- */
     IPAddress _nm(255, 255, 255, 0);
     _config(local_ip, gateway, _nm);
-    CLwipIf::getInstance().addDns(dns_server); // FIXME
+    // CLwipIf::getInstance().addDns(dns_server);
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) {
 /* -------------------------------------------------------------------------- */
     _config(local_ip, gateway, subnet);
-    CLwipIf::getInstance().addDns(dns_server); // FIXME
+    // CLwipIf::getInstance().addDns(dns_server);
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::setDNS(IPAddress dns_server1) {
 /* -------------------------------------------------------------------------- */
-    CLwipIf::getInstance().addDns(dns_server1); // FIXME
+    // CLwipIf::getInstance().addDns(dns_server1);
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::setDNS(IPAddress dns_server1, IPAddress dns_server2) {
 /* -------------------------------------------------------------------------- */
-    CLwipIf::getInstance().addDns(dns_server1); // FIXME
-    CLwipIf::getInstance().addDns(dns_server2); // FIXME
+    // CLwipIf::getInstance().addDns(dns_server1);
+    // CLwipIf::getInstance().addDns(dns_server2);
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::setHostname(const char* name) {
 /* -------------------------------------------------------------------------- */
-    if(ni != nullptr) {
-        ni->setHostname(name);
-    }
+//     if(ni != nullptr) {
+//         ni->setHostname(name);
+//     }
 }
 
 /* -------------------------------------------------------------------------- */
 int CWifi::disconnect() {
 /* -------------------------------------------------------------------------- */
-    CLwipIf::getInstance().disconnectFromAp(); // FIXME
+    WiFiStation.disconnectFromAp();
 }
 
 /* -------------------------------------------------------------------------- */
@@ -165,147 +153,143 @@ void CWifi::end(void) {
 }
 
 /* -------------------------------------------------------------------------- */
-uint8_t* CWifi::macAddress(uint8_t* mac) {
+uint8_t* CWifi::macAddress(uint8_t* mac) { // FIXME
 /* -------------------------------------------------------------------------- */
-    if(ni != nullptr) {
-        if(ni->getMacAddress(mac) == WL_MAC_ADDR_LENGTH) {
-            return mac;
-        }
-    }
-    memset(mac,0x00,6);
-    return mac;
+//     if(ni != nullptr) {
+//         if(ni->getMacAddress(mac) == WL_MAC_ADDR_LENGTH) {
+//             return mac;
+//         }
+//     }
+//     memset(mac,0x00,6);
+//     return mac;
 }
 
 /* -------------------------------------------------------------------------- */
 int8_t CWifi::scanNetworks() {
 /* -------------------------------------------------------------------------- */
-    ni = CLwipIf::getInstance().get(NI_WIFI_STATION); // FIXME
-    if(CLwipIf::getInstance().scanForAp() == ESP_CONTROL_OK) { // FIXME
-        return CLwipIf::getInstance().getApNum(); // FIXME
-    }
-    return 0;
+    return WiFiStation.scanForAp();;
 }
 
 /* -------------------------------------------------------------------------- */
-IPAddress CWifi::localIP() {
+IPAddress CWifi::localIP() { //FIXME
 /* -------------------------------------------------------------------------- */
-    if(ni != nullptr) {
-        return IPAddress(ni->getIpAdd());
-    }
-    return IPAddress((uint32_t)0);
+    // if(ni != nullptr) {
+    //     return IPAddress(ni->getIpAdd());
+    // }
+    // return IPAddress((uint32_t)0);
 }
 
 /* -------------------------------------------------------------------------- */
-IPAddress CWifi::subnetMask() {
+IPAddress CWifi::subnetMask() { // FIXME
 /* -------------------------------------------------------------------------- */
-    if(ni != nullptr) {
-        return IPAddress(ni->getNmAdd());
-    }
-    return IPAddress((uint32_t)0);
+//     if(ni != nullptr) {
+//         return IPAddress(ni->getNmAdd());
+//     }
+//     return IPAddress((uint32_t)0);
 }
 
 /* -------------------------------------------------------------------------- */
-IPAddress CWifi::gatewayIP() {
+IPAddress CWifi::gatewayIP() { // FIXME
 /* -------------------------------------------------------------------------- */
-    if(ni != nullptr) {
-        return IPAddress(ni->getGwAdd());
-    }
-    return IPAddress((uint32_t)0);
+    // if(ni != nullptr) {
+    //     return IPAddress(ni->getGwAdd());
+    // }
+    // return IPAddress((uint32_t)0);
 }
 
 /* -------------------------------------------------------------------------- */
-IPAddress CWifi::dnsIP(int n) {
-    return CLwipIf::getInstance().getDns(n); // FIXME
+IPAddress CWifi::dnsIP(int n) { // FIXME
+    // return CLwipIf::getInstance().getDns(n);
 }
 
 /* -------------------------------------------------------------------------- */
-const char* CWifi::SSID(uint8_t networkItem) {
-    return CLwipIf::getInstance().getSSID(networkItem); // FIXME
+const char* CWifi::SSID(uint8_t networkItem) { // FIXME
+    // return CLwipIf::getInstance().getSSID(networkItem);
 }
 /* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
-int32_t CWifi::RSSI(uint8_t networkItem) {
-    return CLwipIf::getInstance().getRSSI(networkItem); // FIXME
+int32_t CWifi::RSSI(uint8_t networkItem) { // FIXME
+    // return CLwipIf::getInstance().getRSSI(networkItem);
 }
 /* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
-uint8_t CWifi::encryptionType(uint8_t networkItem) {
-    return CLwipIf::getInstance().getEncrType(networkItem); // FIXME
+uint8_t CWifi::encryptionType(uint8_t networkItem) { // FIXME
+    // return CLwipIf::getInstance().getEncrType(networkItem);
 }
 /* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
-uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) {
-    return CLwipIf::getInstance().getBSSID(networkItem,bssid); // FIXME
+uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) { // FIXME
+    // return CLwipIf::getInstance().getBSSID(networkItem,bssid);
 }
 /* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
-uint8_t CWifi::channel(uint8_t networkItem) {
-    return CLwipIf::getInstance().getChannel(networkItem); // FIXME
+uint8_t CWifi::channel(uint8_t networkItem) { // FIXME
+    // return CLwipIf::getInstance().getChannel(networkItem);
 }
 /* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
-const char* CWifi::SSID() {
+const char* CWifi::SSID() { // FIXME
 /* -------------------------------------------------------------------------- */
-    if(ni != nullptr) {
-        return ni->getSSID();
-    }
-    return "";
+    // if(ni != nullptr) {
+    //     return ni->getSSID();
+    // }
+    // return "";
 }
 
 /* -------------------------------------------------------------------------- */
-uint8_t* CWifi::BSSID(uint8_t* bssid) {
+uint8_t* CWifi::BSSID(uint8_t* bssid) { // FIXME
 /* -------------------------------------------------------------------------- */
-    if(ni != nullptr) {
-        return ni->getBSSID(bssid);
-    }
-    return nullptr;
+    // if(ni != nullptr) {
+    //     return ni->getBSSID(bssid);
+    // }
+    // return nullptr;
 }
 
 /* -------------------------------------------------------------------------- */
-int32_t CWifi::RSSI() {
+int32_t CWifi::RSSI() { // FIXME
 /* -------------------------------------------------------------------------- */
-    if(ni != nullptr) {
-        return ni->getRSSI();
-    }
-    return 0;
+    // if(ni != nullptr) {
+    //     return ni->getRSSI();
+    // }
+    // return 0;
 }
 
 /* -------------------------------------------------------------------------- */
-uint8_t CWifi::encryptionType() {
+uint8_t CWifi::encryptionType() { // FIXME
 /* -------------------------------------------------------------------------- */
-    if(ni != nullptr) {
-        return ni->getEncryptionType();
-    }
-    return 0;
+    // if(ni != nullptr) {
+    //     return ni->getEncryptionType();
+    // }
+    // return 0;
 }
 
 /* -------------------------------------------------------------------------- */
-uint8_t CWifi::status() {
+uint8_t CWifi::status() { // FIXME
 /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getWifiStatus(); // FIXME
+    // return CLwipIf::getInstance().getWifiStatus();
 }
 
 /* -------------------------------------------------------------------------- */
-int CWifi::hostByName(const char* aHostname, IPAddress& aResult) {
+int CWifi::hostByName(const char* aHostname, IPAddress& aResult) { // FIXME
 /* -------------------------------------------------------------------------- */
-    return CLwipIf::getInstance().getHostByName(aHostname,aResult); // FIXME
+    // return CLwipIf::getInstance().getHostByName(aHostname,aResult);
 }
 
 /* -------------------------------------------------------------------------- */
-void CWifi::lowPowerMode() {
+void CWifi::lowPowerMode() { // FIXME
 /* -------------------------------------------------------------------------- */
-    CLwipIf::getInstance().setLowPowerMode(); // FIXME
+    // CLwipIf::getInstance().setLowPowerMode();
 }
 
 /* -------------------------------------------------------------------------- */
-void CWifi::noLowPowerMode() {
+void CWifi::noLowPowerMode() { // FIXME
 /* -------------------------------------------------------------------------- */
-    CLwipIf::getInstance().resetLowPowerMode(); // FIXME
+    // CLwipIf::getInstance().resetLowPowerMode();
 }
 
 uint8_t CWifi::reasonCode() {
@@ -316,8 +300,6 @@ unsigned long CWifi::getTime() {
     return 0;
 }
 
-
-
 void CWifi::setTimeout(unsigned long timeout) {
     (void)(timeout);
 }
diff --git a/libraries/WiFi/src/WiFiC3.h b/libraries/WiFi/src/WiFiC3.h
index 2080e5383..9192028ef 100644
--- a/libraries/WiFi/src/WiFiC3.h
+++ b/libraries/WiFi/src/WiFiC3.h
@@ -4,6 +4,14 @@
 
 #define WIFI_FIRMWARE_LATEST_VERSION "1.5.0"
 
+// TODO Instantiate the drivers for wifi with default configuration parameters
+// ESPHostFGDriver WifiDriver;
+
+// Instantiate a global variable from CWifiStation calling it WiFi
+
+inline CWifiStation WiFiStation;
+inline CWifiSoftAp WiFiSoftAP;
+
 class CWifi {
 private:
     void _config(IPAddress local_ip, IPAddress gateway, IPAddress subnet);

From ea4ae60d7588c40853efaa78a282f37a0d1ef968 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 8 Jan 2024 10:25:56 +0100
Subject: [PATCH 35/79] fixing implementation of client and server

---
 libraries/WiFi/src/WiFiClient.h | 2 +-
 libraries/WiFi/src/WiFiServer.h | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/libraries/WiFi/src/WiFiClient.h b/libraries/WiFi/src/WiFiClient.h
index 98761c51f..29d229ee1 100644
--- a/libraries/WiFi/src/WiFiClient.h
+++ b/libraries/WiFi/src/WiFiClient.h
@@ -16,7 +16,7 @@ class WiFiClient: public lwipClient {
     int connect(IPAddress ip, uint16_t port) {
         auto res = lwipClient::connect(ip, port);
 
-        this->bindCNetIf(WiFi);
+        this->bindCNetIf(WiFiStation);
 
         return res;
     }
diff --git a/libraries/WiFi/src/WiFiServer.h b/libraries/WiFi/src/WiFiServer.h
index c72c86597..c6d6a35f1 100644
--- a/libraries/WiFi/src/WiFiServer.h
+++ b/libraries/WiFi/src/WiFiServer.h
@@ -5,9 +5,10 @@
 
 class WiFiServer: public lwipServer {
 public:
+    WiFiServer(uint16_t port) : lwipServer(port) {}
     void begin() {
         lwipServer::begin();
-        this->bindCNetIf(WiFi);
+        this->bindCNetIf(WiFiStation);
     }
 
     WiFiClient available() {

From 87ae27a0b94d3922d4a968bb40606f06fceae33f Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 8 Jan 2024 10:26:28 +0100
Subject: [PATCH 36/79] fixing global definitions of WiFi interfaces

---
 libraries/lwIpWrapper/src/CNetIf.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 8c96f1fe3..0f7b421a8 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -280,7 +280,6 @@ class CWifiSoftAp : public CNetIf {
 
     virtual const char* getSSID();
     virtual uint8_t* getBSSID(uint8_t* bssid);
-    virtual int32_t getRSSI();
     virtual uint8_t getEncryptionType();
 protected:
     static const char softap_ifname_prefix = 's';
@@ -358,4 +357,5 @@ class CLwipIf {
 };
 
 extern CEth Ethernet;
-extern CWifiStation WiFi;
\ No newline at end of file
+extern CWifiStation WiFiStation;
+extern CWifiSoftAp WiFiSoftAP;

From 0c210ee25478e4ef1a5fd0094e3bc0cd49e4aa31 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 8 Jan 2024 10:46:33 +0100
Subject: [PATCH 37/79] making UDP derived classes compile

---
 libraries/Ethernet/src/EthernetUdp.h | 2 +-
 libraries/WiFi/src/WiFiUdp.h         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/Ethernet/src/EthernetUdp.h b/libraries/Ethernet/src/EthernetUdp.h
index 0d450b76a..21f5f6b7c 100644
--- a/libraries/Ethernet/src/EthernetUdp.h
+++ b/libraries/Ethernet/src/EthernetUdp.h
@@ -10,7 +10,7 @@ class EthernetUDP : public lwipUDP {
    public:
    EthernetUDP() {}
    virtual uint8_t begin(uint16_t port) override {
-      CNetIf *ni = CLwipIf::getInstance().get(NI_ETHERNET);
+      CNetIf *ni = &Ethernet;
       if(ni != nullptr) {
          return lwipUDP::begin(IPAddress(ni->getIpAdd()), port);
       }
diff --git a/libraries/WiFi/src/WiFiUdp.h b/libraries/WiFi/src/WiFiUdp.h
index 4b0f35313..f9eb8c3dd 100644
--- a/libraries/WiFi/src/WiFiUdp.h
+++ b/libraries/WiFi/src/WiFiUdp.h
@@ -8,7 +8,7 @@ class WiFiUDP : public lwipUDP {
    public:
    WiFiUDP() {}
    virtual uint8_t begin(uint16_t port) override {
-      CNetIf *ni = CLwipIf::getInstance().get(NI_WIFI_STATION);
+      CNetIf *ni = &WiFiStation;
       if(ni != nullptr) {
          return lwipUDP::begin(IPAddress(ni->getIpAdd()), port);
       }

From 2c10e0edbfddc7cf4779ebb9fe327da9b5029a3e Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 8 Jan 2024 13:34:46 +0100
Subject: [PATCH 38/79] fixing retrocompatibility issues

---
 libraries/WiFi/src/WiFi.cpp        | 3 +++
 libraries/lwIpWrapper/src/CNetIf.h | 7 +++++++
 2 files changed, 10 insertions(+)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index 6535732b8..e96d58d45 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -177,6 +177,7 @@ IPAddress CWifi::localIP() { //FIXME
     //     return IPAddress(ni->getIpAdd());
     // }
     // return IPAddress((uint32_t)0);
+    return WiFiStation.localIP();
 }
 
 /* -------------------------------------------------------------------------- */
@@ -186,6 +187,7 @@ IPAddress CWifi::subnetMask() { // FIXME
 //         return IPAddress(ni->getNmAdd());
 //     }
 //     return IPAddress((uint32_t)0);
+    return WiFiStation.subnetMask();
 }
 
 /* -------------------------------------------------------------------------- */
@@ -195,6 +197,7 @@ IPAddress CWifi::gatewayIP() { // FIXME
     //     return IPAddress(ni->getGwAdd());
     // }
     // return IPAddress((uint32_t)0);
+    return WiFiStation.gatewayIP();
 }
 
 /* -------------------------------------------------------------------------- */
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 0f7b421a8..75ca27d29 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -148,6 +148,13 @@ class CNetIf: public NetworkInterface {
     uint32_t getNmAdd() { return ip4_addr_get_u32(&(ni.netmask)); }
     uint32_t getGwAdd() { return ip4_addr_get_u32(&(ni.gw)); }
 
+    // FIXME when dhcp has not provided an ip address yet return IPADDR_NONE
+    IPAddress localIP()     { return IPAddress(this->getIpAdd()); }
+    IPAddress subnetMask()  { return IPAddress(this->getNmAdd()); }
+    IPAddress gatewayIP()   { return IPAddress(this->getGwAdd()); }
+    IPAddress dnsServerIP() { /* FIXME understand where dns should be managed */}
+
+    // FIXME hostname should be defined in the NetworkStack singleton
     // void setHostname(const char* name)
     // {
     //     memset(hostname, 0x00, MAX_HOSTNAME_DIM);

From 4cb8171f94afc81f40ae1cf1c02f6b78e7f7ac25 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 8 Jan 2024 14:09:21 +0100
Subject: [PATCH 39/79] dealing with retrocompatibility

---
 libraries/lwIpWrapper/src/CNetIf.cpp |  5 +++--
 libraries/lwIpWrapper/src/CNetIf.h   | 12 +++++++++++-
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index c6668a638..f090f8e52 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -428,7 +428,7 @@ CEth::CEth(NetworkDriver *driver)
     // driver.stats = &this->stats;
 }
 
-int CEth::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
+int CEth::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw, const IPAddress &dns) {
     // The driver needs a callback to consume the incoming buffer
     this->driver->setConsumeCallback(
         std::bind(&CEth::consume_callback,
@@ -438,10 +438,11 @@ int CEth::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
     CNetIf::begin(ip, nm, gw);
     netif_set_link_up(&this->ni); // TODO test that moving this here still makes ethernet work
 
+    // TODO set dns server
+
     return 0;
 }
 
-
 err_t CEth::init(struct netif* ni) {
     // Setting up netif
 #if LWIP_NETIF_HOSTNAME
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 75ca27d29..fd2fbe5a1 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -202,12 +202,22 @@ class CEth : public CNetIf {
     virtual int begin(
         const IPAddress &ip = INADDR_NONE,
         const IPAddress &nm = INADDR_NONE,
-        const IPAddress &gw = INADDR_NONE) override;
+        const IPAddress &gw = INADDR_NONE,
+        const IPAddress &dns = INADDR_NONE);
+
+    // virtual int begin(
+    //     const IPAddress &ip = INADDR_NONE,
+    //     const IPAddress &nm = INADDR_NONE,
+    //     const IPAddress &gw = INADDR_NONE,
+    //     const IPAddress &dns = INADDR_NONE);
 
     virtual int getMacAddress(uint8_t* mac) override {
         UNUSED(mac); // FIXME not implemented
         return 1;
     }
+
+    int maintain() {} // Deprecated method for retrocompatibility
+    void schedule(void) {} // Deprecated method for retrocompatibility
 protected:
     /*
      * this function is used to initialize the netif structure of lwip

From c9ed50ccf001e40788508d3ad3665036a4a4dc2f Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 9 Jan 2024 16:12:27 +0100
Subject: [PATCH 40/79] solving FIXME

---
 libraries/WiFi/src/WiFi.cpp          | 104 +++++++-----------------
 libraries/WiFi/src/WiFiC3.h          |  42 +---------
 libraries/lwIpWrapper/src/CNetIf.cpp | 116 +++++++++++++--------------
 libraries/lwIpWrapper/src/CNetIf.h   |  24 ++++--
 4 files changed, 102 insertions(+), 184 deletions(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index e96d58d45..1be48463b 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -153,15 +153,13 @@ void CWifi::end(void) {
 }
 
 /* -------------------------------------------------------------------------- */
-uint8_t* CWifi::macAddress(uint8_t* mac) { // FIXME
+uint8_t* CWifi::macAddress(uint8_t* mac) {
 /* -------------------------------------------------------------------------- */
-//     if(ni != nullptr) {
-//         if(ni->getMacAddress(mac) == WL_MAC_ADDR_LENGTH) {
-//             return mac;
-//         }
-//     }
-//     memset(mac,0x00,6);
-//     return mac;
+    if(WiFiStation.getMacAddress(mac) == WL_MAC_ADDR_LENGTH) {
+        return mac;
+    }
+    memset(mac,0x00,6);
+    return mac;
 }
 
 /* -------------------------------------------------------------------------- */
@@ -171,105 +169,57 @@ int8_t CWifi::scanNetworks() {
 }
 
 /* -------------------------------------------------------------------------- */
-IPAddress CWifi::localIP() { //FIXME
+IPAddress CWifi::localIP() {
 /* -------------------------------------------------------------------------- */
-    // if(ni != nullptr) {
-    //     return IPAddress(ni->getIpAdd());
-    // }
-    // return IPAddress((uint32_t)0);
     return WiFiStation.localIP();
 }
 
 /* -------------------------------------------------------------------------- */
-IPAddress CWifi::subnetMask() { // FIXME
+IPAddress CWifi::subnetMask() {
 /* -------------------------------------------------------------------------- */
-//     if(ni != nullptr) {
-//         return IPAddress(ni->getNmAdd());
-//     }
-//     return IPAddress((uint32_t)0);
     return WiFiStation.subnetMask();
 }
 
 /* -------------------------------------------------------------------------- */
-IPAddress CWifi::gatewayIP() { // FIXME
+IPAddress CWifi::gatewayIP() {
 /* -------------------------------------------------------------------------- */
-    // if(ni != nullptr) {
-    //     return IPAddress(ni->getGwAdd());
-    // }
-    // return IPAddress((uint32_t)0);
     return WiFiStation.gatewayIP();
 }
 
 /* -------------------------------------------------------------------------- */
-IPAddress CWifi::dnsIP(int n) { // FIXME
-    // return CLwipIf::getInstance().getDns(n);
+IPAddress CWifi::dnsIP(int n) {
+    return CLwipIf::getInstance().getDns(n);
 }
 
 /* -------------------------------------------------------------------------- */
-const char* CWifi::SSID(uint8_t networkItem) { // FIXME
-    // return CLwipIf::getInstance().getSSID(networkItem);
-}
-/* -------------------------------------------------------------------------- */
-
-/* -------------------------------------------------------------------------- */
-int32_t CWifi::RSSI(uint8_t networkItem) { // FIXME
-    // return CLwipIf::getInstance().getRSSI(networkItem);
-}
-/* -------------------------------------------------------------------------- */
-
-/* -------------------------------------------------------------------------- */
-uint8_t CWifi::encryptionType(uint8_t networkItem) { // FIXME
-    // return CLwipIf::getInstance().getEncrType(networkItem);
+const char* CWifi::SSID() {
+    return WiFiStation.getSSID();
 }
 /* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
-uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) { // FIXME
-    // return CLwipIf::getInstance().getBSSID(networkItem,bssid);
+int32_t CWifi::RSSI() {
+    return WiFiStation.getRSSI();
 }
 /* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
-uint8_t CWifi::channel(uint8_t networkItem) { // FIXME
-    // return CLwipIf::getInstance().getChannel(networkItem);
+uint8_t CWifi::encryptionType() {
+    return WiFiStation.getEncryptionType();
 }
 /* -------------------------------------------------------------------------- */
 
 /* -------------------------------------------------------------------------- */
-const char* CWifi::SSID() { // FIXME
-/* -------------------------------------------------------------------------- */
-    // if(ni != nullptr) {
-    //     return ni->getSSID();
-    // }
-    // return "";
+uint8_t* CWifi::BSSID(uint8_t* bssid) {
+    return WiFiStation.getBSSID(bssid);
 }
-
 /* -------------------------------------------------------------------------- */
-uint8_t* CWifi::BSSID(uint8_t* bssid) { // FIXME
-/* -------------------------------------------------------------------------- */
-    // if(ni != nullptr) {
-    //     return ni->getBSSID(bssid);
-    // }
-    // return nullptr;
-}
 
 /* -------------------------------------------------------------------------- */
-int32_t CWifi::RSSI() { // FIXME
-/* -------------------------------------------------------------------------- */
-    // if(ni != nullptr) {
-    //     return ni->getRSSI();
-    // }
-    // return 0;
+uint8_t CWifi::channel() {
+    return WiFiStation.getChannel();
 }
-
 /* -------------------------------------------------------------------------- */
-uint8_t CWifi::encryptionType() { // FIXME
-/* -------------------------------------------------------------------------- */
-    // if(ni != nullptr) {
-    //     return ni->getEncryptionType();
-    // }
-    // return 0;
-}
 
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::status() { // FIXME
@@ -278,21 +228,21 @@ uint8_t CWifi::status() { // FIXME
 }
 
 /* -------------------------------------------------------------------------- */
-int CWifi::hostByName(const char* aHostname, IPAddress& aResult) { // FIXME
+int CWifi::hostByName(const char* aHostname, IPAddress& aResult) {
 /* -------------------------------------------------------------------------- */
-    // return CLwipIf::getInstance().getHostByName(aHostname,aResult);
+    return CLwipIf::getInstance().getHostByName(aHostname, aResult);
 }
 
 /* -------------------------------------------------------------------------- */
-void CWifi::lowPowerMode() { // FIXME
+void CWifi::lowPowerMode() {
 /* -------------------------------------------------------------------------- */
-    // CLwipIf::getInstance().setLowPowerMode();
+    WiFiStation.setLowPowerMode();
 }
 
 /* -------------------------------------------------------------------------- */
-void CWifi::noLowPowerMode() { // FIXME
+void CWifi::noLowPowerMode() {
 /* -------------------------------------------------------------------------- */
-    // CLwipIf::getInstance().resetLowPowerMode();
+    WiFiStation.resetLowPowerMode();
 }
 
 uint8_t CWifi::reasonCode() {
diff --git a/libraries/WiFi/src/WiFiC3.h b/libraries/WiFi/src/WiFiC3.h
index 9192028ef..27da6b805 100644
--- a/libraries/WiFi/src/WiFiC3.h
+++ b/libraries/WiFi/src/WiFiC3.h
@@ -184,47 +184,7 @@ class CWifi {
      */
     int8_t scanNetworks();
 
-    /*
-     * Return the SSID discovered during the network scan.
-     *
-     * param networkItem: specify from which network item want to get the information
-    *
-     * return: SSID string of the specified item on the networks scanned list
-     */
-    const char*   SSID(uint8_t networkItem);
-
-    /*
-     * Return the encryption type of the networks discovered during the scanNetworks
-     *
-     * param networkItem: specify from which network item want to get the information
-     *
-     * return: encryption type (enum wl_enc_type) of the specified item on the networks scanned list
-
-     * enum wl_enc_type :
-     *  ENC_TYPE_WEP,
-     *  ENC_TYPE_WPA,
-     *  ENC_TYPE_WPA2,
-     *  ENC_TYPE_WPA2_ENTERPRISE,
-     *  ENC_TYPE_WPA3,
-     *  ENC_TYPE_NONE,
-     *  ENC_TYPE_AUTO,
-     *
-     *  ENC_TYPE_UNKNOWN = 255
-     *
-     */
-    uint8_t encryptionType(uint8_t networkItem);
-
-    uint8_t* BSSID(uint8_t networkItem, uint8_t* bssid);
-    uint8_t channel(uint8_t networkItem);
-
-    /*
-     * Return the RSSI of the networks discovered during the scanNetworks
-     *
-     * param networkItem: specify from which network item want to get the information
-    *
-     * return: signed value of RSSI of the specified item on the networks scanned list
-     */
-    int32_t RSSI(uint8_t networkItem);
+    uint8_t channel();
 
     /*
      * Return Connection status.
diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index f090f8e52..ab61d224e 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -10,8 +10,11 @@
 // TODO implement stop softAP and include it in the destructor of the class
 // TODO split netif definition in different files
 // TODO implement WIFINetworkDriver that is then being used by both Wifi station and softAP. This will allow to use both at the same time
+// TODO adapt network statistics collection
+// TODO define enum for error collection and return them instead of int value
+// FIXME Wifi driver requires interrupt safety in order to work properly in the timer
 
-extern "C" void dhcps_start(struct netif *netif); // TODO understand why not include
+extern "C" void dhcps_start(struct netif *netif);
 
 err_t _netif_init(struct netif* ni);
 err_t _netif_output(struct netif* ni, struct pbuf* p);
@@ -123,7 +126,7 @@ static void timer_cb(timer_callback_args_t* arg) {
 #endif
 
 void CLwipIf::task() {
-    for(CNetIf* iface: this->ifaces) { // FIXME is this affecting performances?
+    for(CNetIf* iface: this->ifaces) {
         iface->task();
     }
 
@@ -141,7 +144,7 @@ void CLwipIf::setDefaultIface(CNetIf* iface) {
 void CLwipIf::add_iface(CNetIf* iface) {
     // if it is the first interface set it as the default route
     if(this->ifaces.empty()) {
-        netif_set_default(&iface->ni); // TODO let the user decide which is the default one
+        netif_set_default(&iface->ni);
 
 #ifdef LWIP_USE_TIMER
         timer.setup_overflow_irq();
@@ -155,18 +158,8 @@ void CLwipIf::add_iface(CNetIf* iface) {
 }
 
 CLwipIf::~CLwipIf() {
-    // TODO free iface array
 }
 
-
-// int CLwipIf::setWifiMode(WifiMode_t mode) {
-    // TODO adapt this
-    // CLwipIf::getInstance().startSyncRequest();
-    // int rv = CEspControl::getInstance().setWifiMode(mode);
-    // CLwipIf::getInstance().restartAsyncRequest();
-    // return rv;
-// }
-
 /* ***************************************************************************
  *                               DNS related functions
  * ****************************************************************************/
@@ -182,6 +175,7 @@ static void _getHostByNameCBK(const char *name, const ip_addr_t *ipaddr, void *c
 
     cbk->cbk(toArduinoIP(ipaddr));
 
+    delete ipaddr;
     delete cbk;
 }
 
@@ -213,6 +207,12 @@ void CLwipIf::clearDnsServers() {
     }
 }
 
+IPAddress CLwipIf::getDns(int n) {
+    ip_addr_t dns = dns_getserver(i);
+
+    return toArduinoIP(dns);
+}
+
 // DNS resolution works with a callback if the resolution doesn't return immediately
 int CLwipIf::getHostByName(const char* aHostname, IPAddress& aResult, bool execute_task) {
     /* this has to be a blocking call but we need to understand how to handle wait time
@@ -243,18 +243,18 @@ int CLwipIf::getHostByName(const char* aHostname, IPAddress& aResult, bool execu
 
 // TODO instead of returning int return an enum value
 int CLwipIf::getHostByName(const char* aHostname, std::function<void(const IPAddress&)> cbk) {
-    ip_addr_t addr; // TODO understand if this needs to be in the heap
+    ip_addr_t *addr = new ip_addr_t;
     uint8_t res = 0;
 
     dns_callback* dns_cbk = new dns_callback;
     dns_cbk->cbk = cbk;
-    err_t err = dns_gethostbyname(aHostname, &addr, _getHostByNameCBK, dns_cbk);
+    err_t err = dns_gethostbyname(aHostname, addr, _getHostByNameCBK, dns_cbk);
 
     switch(err) {
     case ERR_OK:
         // the address was already present in the local cache
-        cbk(toArduinoIP(&addr));
-
+        cbk(toArduinoIP(addr));
+        delete ipaddr;
         delete dns_cbk;
         break;
     case ERR_INPROGRESS:
@@ -263,6 +263,7 @@ int CLwipIf::getHostByName(const char* aHostname, std::function<void(const IPAdd
         break;
     case ERR_ARG: // there are issues in the arguments passed
     default:
+        delete ipaddr;
         delete dns_cbk;
         res = -1;
     }
@@ -281,10 +282,10 @@ CNetIf::CNetIf(NetworkDriver *driver)
     , dhcp_acquired(false)
 #endif
 {
-    // NETIF_STATS_INIT(this->stats); // TODO create a proper stats interface
+    // NETIF_STATS_INIT(this->stats);
 
     if(driver != nullptr) {
-        // driver->stats = this->stats; // TODO
+        // driver->stats = this->stats;
         // TODO check that this calls are effective
         driver->setLinkDownCallback(std::bind(&CNetIf::linkDownCallback, this));
         driver->setLinkUpCallback(std::bind(&CNetIf::linkUpCallback, this));
@@ -311,11 +312,9 @@ int CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw)
         ethernet_input
     );
     if(_ni == nullptr) {
-        // FIXME error if netif_add, return error
         return -1;
     }
 
-    //TODO add link up and down callback and set the link
     netif_set_up(&this->ni);
 
 #ifdef LWIP_DHCP
@@ -334,7 +333,7 @@ int CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw)
 
 void CNetIf::task() {
 #ifdef LWIP_DHCP
-    // TODO we can add a lazy evaluated timer for this condition if dhcp_supplied_address takes too long
+    // TODO add timeout
     if(!this->dhcp_acquired && dhcp_supplied_address(&this->ni)) {
         dhcp_acquired = true;
     }
@@ -375,11 +374,11 @@ void CNetIf::setLinkDown() {
 }
 
 void CNetIf::linkUpCallback() {
-    netif_set_link_up(&this->ni); // TODO check that this sets the interface up also
+    netif_set_link_up(&this->ni);
 }
 
 void CNetIf::linkDownCallback() {
-    netif_set_link_down(&this->ni); // TODO check that this sets the interface down also
+    netif_set_link_down(&this->ni);
 }
 
 /* ##########################################################################
@@ -436,9 +435,9 @@ int CEth::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw, c
 
     // Call the begin function on the Parent class to init the interface
     CNetIf::begin(ip, nm, gw);
-    netif_set_link_up(&this->ni); // TODO test that moving this here still makes ethernet work
+    netif_set_link_up(&this->ni);
 
-    // TODO set dns server
+    CLwipIf::getInstance().addDnsServer(dns);
 
     return 0;
 }
@@ -446,12 +445,11 @@ int CEth::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw, c
 err_t CEth::init(struct netif* ni) {
     // Setting up netif
 #if LWIP_NETIF_HOSTNAME
-    // TODO pass the hostname in the constructor os with a setter
     ni->hostname                       = "C33_eth";
 #endif
     ni->name[0]                        = CEth::eth_ifname_prefix;
     ni->name[1]                        = '0' + CEth::eth_id++;
-    ni->mtu                            = 1500; // FIXME get this from the network
+    ni->mtu                            = 1500; // TODO get this from the network
     ni->flags                          |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
 
     memcpy(ni->hwaddr, this->driver->getMacAddress(), 6); // FIXME handle this using a constant
@@ -529,15 +527,13 @@ uint8_t CWifiStation::wifistation_id = 0;
 
 CWifiStation::CWifiStation()
 : hw_init(false) {
-    // TODO this class should implement the driver interface
-    // CLwipIf::getInstance()
 }
 
 CWifiStation::~CWifiStation() {
 
 }
 
-int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) { // TODO This should be called only once, make it private
+int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
     int res = 0;
     int time_num = 0;
 
@@ -565,7 +561,6 @@ int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddres
 
     res = CEspControl::getInstance().setWifiMode(WIFI_MODE_STA);
     CNetIf::begin(ip, nm, gw);
-    // netif_set_link_up(&this->ni); // TODO this should be set only when successfully connected to an AP
 exit:
     // arduino::unlock();
     return res;
@@ -579,11 +574,7 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
     // AccessPoint_t* best_matching_ap;
     // arduino::lock();
 
-    // if(access_points.size() == 0) {
-    //     this->scanForAp();
-    // }
     if((rv=this->scanForAp()) != WL_SCAN_COMPLETED) {
-        // rv = -1; // FIXME set proper error code
         goto exit;
     }
 
@@ -614,7 +605,7 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
         memcpy(ap.bssid, access_points[best_index].bssid, BSSID_LENGTH);
 
         // arduino::lock();
-        CEspControl::getInstance().communicateWithEsp(); // TODO make this shared between SoftAP and station
+        CEspControl::getInstance().communicateWithEsp();
 
         rv=CEspControl::getInstance().connectAccessPoint(ap);
         // arduino::unlock();
@@ -626,9 +617,6 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
         }
         // arduino::unlock();
     }
-    // else {
-    //     // TODO return AP not found error
-    // }
 
 exit:
     // arduino::unlock();
@@ -638,15 +626,14 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
 
 int CWifiStation::scanForAp() {
         // arduino::lock();
-    access_points.clear(); // FIXME create access_points vector
+    access_points.clear();
 
     int res = CEspControl::getInstance().getAccessPointScanList(access_points);
     if (res == ESP_CONTROL_OK) {
         res = WL_SCAN_COMPLETED;
+    } else {
+        res = WL_NO_SSID_AVAIL;
     }
-    // else {
-    //     res = WL_NO_SSID_AVAIL; // TODO
-    // }
 
     // arduino::unlock();
 
@@ -661,7 +648,6 @@ int CWifiStation::disconnectFromAp() {
 err_t CWifiStation::init(struct netif* ni) {
     // Setting up netif
 #if LWIP_NETIF_HOSTNAME
-    // TODO pass the hostname in the constructor os with a setter
     ni->hostname                       = "C33-WifiSta";
 #endif
     ni->name[0]                        = CWifiStation::wifistation_ifname_prefix;
@@ -734,9 +720,6 @@ void CWifiStation::task() {
     // calling the base class task, in order to make thigs work
     CNetIf::task();
 
-    // TODO in order to make things easier this should be implemented inside of Wifi driver
-    // and not override LWIPInterface method
-
     uint8_t if_num = 0;
     uint16_t dim = 0;
     uint8_t* buffer = nullptr;
@@ -746,7 +729,7 @@ void CWifiStation::task() {
     // arduino::lock();
     // TODO do not perform this when not connected to an AP
     if(hw_init) {
-        CEspControl::getInstance().communicateWithEsp(); // TODO make this shared between SoftAP and station
+        CEspControl::getInstance().communicateWithEsp();
 
         // TODO handling buffer this way may be harmful for the memory
         buffer = CEspControl::getInstance().getStationRx(if_num, dim);
@@ -755,7 +738,6 @@ void CWifiStation::task() {
     // empty the ESP32 queue
     while(buffer != nullptr) {
         // FIXME this section is redundant and should be generalized toghether with CEth::consume_callback
-        // TODO understand if this should be moved into the base class
         // NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(this->stats);
 
         zerocopy_pbuf_t *custom_pbuf = get_zerocopy_pbuf(buffer, dim, free);
@@ -795,7 +777,7 @@ uint8_t* CWifiStation::getBSSID(uint8_t* bssid){
 }
 
 int32_t CWifiStation::getRSSI() {
-    // TODO should this be updated?
+    // TODO update the rssi on request of this method
     return (uint32_t)access_point_cfg.rssi;
 }
 
@@ -806,9 +788,17 @@ uint8_t CWifiStation::getEncryptionType() {
 // int CWifiStation::getMacAddress(uint8_t* mac) {
 // }
 
-// uint8_t CWifiStation::getChannel() {
-//     return (uint8_t)access_point_cfg.channel;
-// }
+uint8_t CWifiStation::getChannel() {
+    return (uint8_t)access_point_cfg.channel;
+}
+
+int CWifiStation::setLowPowerMode() {
+    return CEspControl::getInstance().setPowerSaveMode(1);
+}
+
+int CWifiStation::resetLowPowerMode() {
+    return CEspControl::getInstance().setPowerSaveMode(1);
+}
 
 /* ########################################################################## */
 /*                      CWifiSoftAp NETWORK INTERFACE CLASS                   */
@@ -851,7 +841,6 @@ int CWifiSoftAp::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress
 
     res = CEspControl::getInstance().setWifiMode(WIFI_MODE_AP);
 
-    // netif_set_link_up(&this->ni); // TODO this should be set only when successfully connected to an AP
     CNetIf::begin(
         default_dhcp_server_ip,
         default_nm,
@@ -881,8 +870,7 @@ int CWifiSoftAp::startSoftAp(const char* ssid, const char* passphrase, uint8_t c
 
     channel = (channel == 0) ? 1 : channel;
     cfg.channel = (channel > MAX_CHNL_NO) ? MAX_CHNL_NO : channel;
-    cfg.max_connections = 10; // FIXME
-    // cfg.max_connections = MAX_SOFAT_CONNECTION_DEF; // FIXME
+    cfg.max_connections = MAX_SOFAT_CONNECTION_DEF; // FIXME make user decide this parameter
     cfg.bandwidth = WIFI_BW_HT40;
     cfg.ssid_hidden = false;
 
@@ -989,7 +977,7 @@ void CWifiSoftAp::task() {
     // arduino::lock();
     // TODO do not perform this when not connected to an AP
     if(hw_init) {
-        CEspControl::getInstance().communicateWithEsp(); // TODO make this shared between SoftAP and station
+        CEspControl::getInstance().communicateWithEsp();
 
         // TODO handling buffer this way may be harmful for the memory
         buffer = CEspControl::getInstance().getSoftApRx(if_num, dim);
@@ -1037,6 +1025,18 @@ uint8_t CWifiSoftAp::getEncryptionType() {
     return Encr2wl_enc(soft_ap_cfg.encryption_mode);
 }
 
+uint8_t CWifiSoftAp::getChannel() {
+    return (uint8_t)soft_ap_cfg.channel;
+}
+
+int CWifiSoftAp::setLowPowerMode() {
+    return CEspControl::getInstance().setPowerSaveMode(1);
+}
+
+int CWifiSoftAp::resetLowPowerMode() {
+    return CEspControl::getInstance().setPowerSaveMode(1);
+}
+
 /* ##########################################################################
  *                      DEBUG UTILS
  * ########################################################################## */
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index fd2fbe5a1..f412041dc 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -205,12 +205,6 @@ class CEth : public CNetIf {
         const IPAddress &gw = INADDR_NONE,
         const IPAddress &dns = INADDR_NONE);
 
-    // virtual int begin(
-    //     const IPAddress &ip = INADDR_NONE,
-    //     const IPAddress &nm = INADDR_NONE,
-    //     const IPAddress &gw = INADDR_NONE,
-    //     const IPAddress &dns = INADDR_NONE);
-
     virtual int getMacAddress(uint8_t* mac) override {
         UNUSED(mac); // FIXME not implemented
         return 1;
@@ -254,12 +248,18 @@ class CWifiStation : public CNetIf {
 
     virtual void task() override;
 
-    virtual int getMacAddress(uint8_t* mac) override {}
+    virtual int getMacAddress(uint8_t* mac) override {
+        // FIXME not implemented
+    }
 
     virtual const char* getSSID();
     virtual uint8_t* getBSSID(uint8_t* bssid);
     virtual int32_t getRSSI();
     virtual uint8_t getEncryptionType();
+    virtual uint8_t getChannel();
+
+    int setLowPowerMode();
+    int resetLowPowerMode();
 protected:
     static const char wifistation_ifname_prefix = 'w';
     static uint8_t wifistation_id;
@@ -293,11 +293,17 @@ class CWifiSoftAp : public CNetIf {
     int startSoftAp(const char* ssid, const char* passphrase=nullptr, uint8_t channel=0);
     int stopSoftAp();
 
-    virtual int getMacAddress(uint8_t* mac) override {}
+    virtual int getMacAddress(uint8_t* mac) override {
+        // FIXME not implemented
+    }
 
     virtual const char* getSSID();
     virtual uint8_t* getBSSID(uint8_t* bssid);
     virtual uint8_t getEncryptionType();
+    virtual uint8_t getChannel();
+
+    int setLowPowerMode();
+    int resetLowPowerMode();
 protected:
     static const char softap_ifname_prefix = 's';
     static uint8_t softap_id;
@@ -349,6 +355,8 @@ class CLwipIf {
     uint8_t addDnsServer(const IPAddress& aDNSServer, int8_t priority=-1);
     void clearDnsServers();
 
+    IPAddress getDns(int n);
+
     // DNS resolution works with a callback if the resolution doesn't return immediately
     int getHostByName(const char* aHostname, IPAddress& aResult, bool execute_task=false); // blocking call
     int getHostByName(const char* aHostname, std::function<void(const IPAddress&)> cbk); // callback version

From 719e63fb608e118522639b2dc1ae9777beec18cd Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 9 Jan 2024 16:43:00 +0100
Subject: [PATCH 41/79] fixing dns related functions

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index ab61d224e..f7d1f1628 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -208,7 +208,7 @@ void CLwipIf::clearDnsServers() {
 }
 
 IPAddress CLwipIf::getDns(int n) {
-    ip_addr_t dns = dns_getserver(i);
+    const ip_addr_t* dns = dns_getserver(n);
 
     return toArduinoIP(dns);
 }
@@ -254,7 +254,7 @@ int CLwipIf::getHostByName(const char* aHostname, std::function<void(const IPAdd
     case ERR_OK:
         // the address was already present in the local cache
         cbk(toArduinoIP(addr));
-        delete ipaddr;
+        delete addr;
         delete dns_cbk;
         break;
     case ERR_INPROGRESS:
@@ -263,7 +263,7 @@ int CLwipIf::getHostByName(const char* aHostname, std::function<void(const IPAdd
         break;
     case ERR_ARG: // there are issues in the arguments passed
     default:
-        delete ipaddr;
+        delete addr;
         delete dns_cbk;
         res = -1;
     }

From 58f95613958028207bd8d6b0d664f3208b8484f0 Mon Sep 17 00:00:00 2001
From: Juraj Andrassy <juraj.andrassy@gmail.com>
Date: Thu, 14 Dec 2023 19:30:48 +0100
Subject: [PATCH 42/79] CNetIf - added config method to apply new static IP
 settings

in CWifi::_config netif_set_address call was missing.
in Ethernet::begin static IP was not applied if invoked second time
now the new CNetIf.config does the static IP change for both.
---
 libraries/WiFi/src/WiFi.cpp          | 28 ++++++++++++----------------
 libraries/lwIpWrapper/src/CNetIf.cpp | 21 ++++++++++++++++++++-
 libraries/lwIpWrapper/src/CNetIf.h   |  1 +
 3 files changed, 33 insertions(+), 17 deletions(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index 1be48463b..2534795a1 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -79,22 +79,18 @@ void CWifi::config(IPAddress local_ip) { // FIXME
 extern uint8_t *IpAddress2uint8(IPAddress a);
 
 /* -------------------------------------------------------------------------- */
-void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) { // FIXME
-/* -------------------------------------------------------------------------- */
-    // _useStaticIp = local_ip != INADDR_NONE;
-    // if(ni != nullptr) {
-    //     ni->DhcpStop();
-    //     ni->DhcpNotUsed();
-    //     IP_ADDR4(&ni->ip, local_ip[0], local_ip[1], local_ip[2], local_ip[3]);
-    //     IP_ADDR4(&ni->gw, gateway[0], gateway[1], gateway[2], gateway[3]);
-    //     IP_ADDR4(&ni->nm, subnet[0], subnet[1], subnet[2], subnet[3]);
-    // }
-    // else {
-    //     CNetIf::default_ip = local_ip;
-    //     CNetIf::default_nm = subnet;
-    //     CNetIf::default_gw = gateway;
-    //     CNetIf::default_dhcp_server_ip = local_ip;
-    // }
+void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) {
+/* -------------------------------------------------------------------------- */    
+   _useStaticIp = local_ip != INADDR_NONE;
+   if(ni != nullptr) {
+     ni->config(local_ip, gateway, subnet);
+   }
+   else {
+      CNetIf::default_ip = local_ip;
+      CNetIf::default_nm = subnet; 
+      CNetIf::default_gw = gateway;
+      CNetIf::default_dhcp_server_ip = local_ip;
+   }
 }
 
 /* -------------------------------------------------------------------------- */
diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index f7d1f1628..c51a6793a 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -948,7 +948,7 @@ err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
         errval = ERR_OK;
         // NETIF_STATS_INCREMENT_TX_BYTES(this->stats, size);
         // NETIF_STATS_TX_TIME_AVERAGE(this->stats);
-    } else {
+            } else {
         // NETIF_STATS_INCREMENT_ERROR(this->stats, err);
         // NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats);
     }
@@ -961,6 +961,25 @@ err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
     return errval;
 }
 
+/* -------------------------------------------------------------------------- */
+void CNetIf::config(IPAddress _ip, IPAddress _gw, IPAddress _nm)
+{
+    DhcpStop();
+    DhcpNotUsed();
+
+    IP_ADDR4(&ip, _ip[0], _ip[1], _ip[2], _ip[3]);
+    IP_ADDR4(&nm, _nm[0], _nm[1], _nm[2], _nm[3]);
+    IP_ADDR4(&gw, _gw[0], _gw[1], _gw[2], _gw[3]);
+
+    netif_set_addr(&ni, &ip, &nm, &gw);
+
+    if (netif_is_link_up(&ni)) {
+        netif_set_down(&ni);
+        netif_set_up(&ni);
+    }
+}
+
+
 void CWifiSoftAp::task() {
     // calling the base class task, in order to make thigs work
     CNetIf::task();
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index f412041dc..faefa6bc3 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -160,6 +160,7 @@ class CNetIf: public NetworkInterface {
     //     memset(hostname, 0x00, MAX_HOSTNAME_DIM);
     //     memcpy(hostname, name, strlen(name) < MAX_HOSTNAME_DIM ? strlen(name) : MAX_HOSTNAME_DIM);
     // }
+    void config(IPAddress _ip, IPAddress _gw, IPAddress _nm);
 
 
     virtual int getMacAddress(uint8_t* mac) = 0;

From 45c85f35b8eaad61604a7e53b286f88de3c72dd6 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 9 Jan 2024 16:52:50 +0100
Subject: [PATCH 43/79] adapting cherry pick

---
 libraries/WiFi/src/WiFi.cpp          | 13 ++-----------
 libraries/lwIpWrapper/src/CNetIf.cpp | 10 +++++-----
 2 files changed, 7 insertions(+), 16 deletions(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index 2534795a1..33650bd76 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -80,17 +80,8 @@ extern uint8_t *IpAddress2uint8(IPAddress a);
 
 /* -------------------------------------------------------------------------- */
 void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) {
-/* -------------------------------------------------------------------------- */    
-   _useStaticIp = local_ip != INADDR_NONE;
-   if(ni != nullptr) {
-     ni->config(local_ip, gateway, subnet);
-   }
-   else {
-      CNetIf::default_ip = local_ip;
-      CNetIf::default_nm = subnet; 
-      CNetIf::default_gw = gateway;
-      CNetIf::default_dhcp_server_ip = local_ip;
-   }
+/* -------------------------------------------------------------------------- */
+    CWifiStation.config(local_ip, gateway, subnet)
 }
 
 /* -------------------------------------------------------------------------- */
diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index c51a6793a..c6900c9b0 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -964,12 +964,12 @@ err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
 /* -------------------------------------------------------------------------- */
 void CNetIf::config(IPAddress _ip, IPAddress _gw, IPAddress _nm)
 {
-    DhcpStop();
-    DhcpNotUsed();
+    dhcpStop();
+    dhcpStart();
 
-    IP_ADDR4(&ip, _ip[0], _ip[1], _ip[2], _ip[3]);
-    IP_ADDR4(&nm, _nm[0], _nm[1], _nm[2], _nm[3]);
-    IP_ADDR4(&gw, _gw[0], _gw[1], _gw[2], _gw[3]);
+    ip_addr_t ip = fromArduinoIP(_ip);
+    ip_addr_t nm = fromArduinoIP(_gw);
+    ip_addr_t gw = fromArduinoIP(_nm);
 
     netif_set_addr(&ni, &ip, &nm, &gw);
 

From 664fc698fc2d7041a5483effb56acd5e5bd68839 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 9 Jan 2024 16:56:04 +0100
Subject: [PATCH 44/79] moving funtion in correct section in file

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 37 ++++++++++++++--------------
 1 file changed, 18 insertions(+), 19 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index c6900c9b0..180107df4 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -385,6 +385,24 @@ void CNetIf::linkDownCallback() {
  *                               DHCP related functions
  * ########################################################################## */
 
+void CNetIf::config(IPAddress _ip, IPAddress _gw, IPAddress _nm) {
+#ifdef LWIP_DHCP
+    dhcpStop();
+    dhcpStart();
+#endif
+
+    ip_addr_t ip = fromArduinoIP(_ip);
+    ip_addr_t nm = fromArduinoIP(_gw);
+    ip_addr_t gw = fromArduinoIP(_nm);
+
+    netif_set_addr(&ni, &ip, &nm, &gw);
+
+    if (netif_is_link_up(&ni)) {
+        netif_set_down(&ni);
+        netif_set_up(&ni);
+    }
+}
+
 
 #ifdef LWIP_DHCP
 
@@ -961,25 +979,6 @@ err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
     return errval;
 }
 
-/* -------------------------------------------------------------------------- */
-void CNetIf::config(IPAddress _ip, IPAddress _gw, IPAddress _nm)
-{
-    dhcpStop();
-    dhcpStart();
-
-    ip_addr_t ip = fromArduinoIP(_ip);
-    ip_addr_t nm = fromArduinoIP(_gw);
-    ip_addr_t gw = fromArduinoIP(_nm);
-
-    netif_set_addr(&ni, &ip, &nm, &gw);
-
-    if (netif_is_link_up(&ni)) {
-        netif_set_down(&ni);
-        netif_set_up(&ni);
-    }
-}
-
-
 void CWifiSoftAp::task() {
     // calling the base class task, in order to make thigs work
     CNetIf::task();

From 3a1e29d1a54dd5ccf5aaaf56cd709e86ffc6f5ac Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 9 Jan 2024 16:56:22 +0100
Subject: [PATCH 45/79] added dhcp_inform call

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 180107df4..bb7603c7b 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -389,6 +389,7 @@ void CNetIf::config(IPAddress _ip, IPAddress _gw, IPAddress _nm) {
 #ifdef LWIP_DHCP
     dhcpStop();
     dhcpStart();
+    dhcpNotUsed();
 #endif
 
     ip_addr_t ip = fromArduinoIP(_ip);

From b5eb21b5c804d19fd4b8cf30d21426461549dc36 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 10 Jan 2024 01:47:18 +0100
Subject: [PATCH 46/79] fixing compilation issue

---
 libraries/WiFi/src/WiFi.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index 33650bd76..e39a27cbd 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -81,7 +81,7 @@ extern uint8_t *IpAddress2uint8(IPAddress a);
 /* -------------------------------------------------------------------------- */
 void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) {
 /* -------------------------------------------------------------------------- */
-    CWifiStation.config(local_ip, gateway, subnet)
+    WiFiStation.config(local_ip, gateway, subnet);
 }
 
 /* -------------------------------------------------------------------------- */

From ae01d585cb59e4a0981b0af4cdeb502cb4ae3432 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 10 Jan 2024 01:47:38 +0100
Subject: [PATCH 47/79] removing FIXME

---
 libraries/WiFi/src/WiFi.cpp | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index e39a27cbd..4a78fa279 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -67,13 +67,13 @@ uint8_t CWifi::beginAP(const char *ssid, const char* passphrase, uint8_t channel
 
 
 /* -------------------------------------------------------------------------- */
-void CWifi::config(IPAddress local_ip) { // FIXME
+void CWifi::config(IPAddress local_ip) {
 /* -------------------------------------------------------------------------- */
-    // IPAddress _nm(255, 255, 255, 0);
-    // IPAddress _gw = local_ip;
-    // _gw[3] = 1;
+    IPAddress _nm(255, 255, 255, 0);
+    IPAddress _gw = local_ip;
+    _gw[3] = 1;
 
-    // _config(local_ip, _gw, _nm);
+    _config(local_ip, _gw, _nm);
 }
 
 extern uint8_t *IpAddress2uint8(IPAddress a);
@@ -85,10 +85,10 @@ void CWifi::_config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) {
 }
 
 /* -------------------------------------------------------------------------- */
-void CWifi::config(IPAddress local_ip, IPAddress dns_server) { // FIXME
+void CWifi::config(IPAddress local_ip, IPAddress dns_server) {
 /* -------------------------------------------------------------------------- */
     config(local_ip);
-    // CLwipIf::getInstance().addDns(dns_server);
+    CLwipIf::getInstance().addDnsServer(dns_server, 0);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -109,14 +109,14 @@ void CWifi::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway,
 /* -------------------------------------------------------------------------- */
 void CWifi::setDNS(IPAddress dns_server1) {
 /* -------------------------------------------------------------------------- */
-    // CLwipIf::getInstance().addDns(dns_server1);
+    CLwipIf::getInstance().addDnsServer(dns_server1, 0);
 }
 
 /* -------------------------------------------------------------------------- */
 void CWifi::setDNS(IPAddress dns_server1, IPAddress dns_server2) {
 /* -------------------------------------------------------------------------- */
-    // CLwipIf::getInstance().addDns(dns_server1);
-    // CLwipIf::getInstance().addDns(dns_server2);
+    CLwipIf::getInstance().addDnsServer(dns_server1, 0);
+    CLwipIf::getInstance().addDnsServer(dns_server2, 1);
 }
 
 /* -------------------------------------------------------------------------- */

From f8507fab6f089fa364ff7ef02c05422dcddaf301 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 10 Jan 2024 03:09:06 +0100
Subject: [PATCH 48/79] restoring CWiFi methods to extract scanned access point
 info

---
 libraries/WiFi/src/WiFi.cpp          | 30 ++++++++++++++++++
 libraries/WiFi/src/WiFiC3.h          | 47 +++++++++++++++++++++++++---
 libraries/lwIpWrapper/src/CNetIf.cpp | 37 ++++++++++++++++++++++
 libraries/lwIpWrapper/src/CNetIf.h   |  6 ++++
 4 files changed, 116 insertions(+), 4 deletions(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index 4a78fa279..bfe4906de 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -208,6 +208,36 @@ uint8_t CWifi::channel() {
 }
 /* -------------------------------------------------------------------------- */
 
+/* -------------------------------------------------------------------------- */
+const char* CWifi::SSID(uint8_t networkItem) {
+    return WiFiStation.getSSID(networkItem);
+}
+/* -------------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
+int32_t CWifi::RSSI(uint8_t networkItem) {
+    return WiFiStation.getRSSI(networkItem);
+}
+/* -------------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
+uint8_t CWifi::encryptionType(uint8_t networkItem) {
+    return WiFiStation.getEncrType(networkItem);
+}
+/* -------------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
+uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) {
+    return WiFiStation.getBSSID(networkItem,bssid);
+}
+/* -------------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
+uint8_t CWifi::channel(uint8_t networkItem) {
+    return WiFiStation.getChannel(networkItem);
+}
+/* -------------------------------------------------------------------------- */
+
 /* -------------------------------------------------------------------------- */
 uint8_t CWifi::status() { // FIXME
 /* -------------------------------------------------------------------------- */
diff --git a/libraries/WiFi/src/WiFiC3.h b/libraries/WiFi/src/WiFiC3.h
index 27da6b805..ae5112f92 100644
--- a/libraries/WiFi/src/WiFiC3.h
+++ b/libraries/WiFi/src/WiFiC3.h
@@ -171,10 +171,10 @@ class CWifi {
     int32_t RSSI();
 
     /*
-      * Return the Encryption Type associated with the network
-      *
-      * return: one value of wl_enc_type enum
-      */
+     * Return the Encryption Type associated with the network
+     *
+     * return: one value of wl_enc_type enum
+     */
     uint8_t encryptionType();
 
     /*
@@ -183,6 +183,45 @@ class CWifi {
      * return: Number of discovered networks
      */
     int8_t scanNetworks();
+    /*
+     * Return the SSID discovered during the network scan.
+     *
+     * param networkItem: specify from which network item want to get the information
+     *
+     * return: SSID string of the specified item on the networks scanned list
+     */
+    const char*   SSID(uint8_t networkItem);
+
+    /*
+     * Return the encryption type of the networks discovered during the scanNetworks
+     *
+     * param networkItem: specify from which network item want to get the information
+     *
+     * return: encryption type (enum wl_enc_type) of the specified item on the networks scanned list
+     *    enum wl_enc_type :
+     *    ENC_TYPE_WEP,
+     *    ENC_TYPE_WPA,
+     *    ENC_TYPE_WPA2,
+     *    ENC_TYPE_WPA2_ENTERPRISE,
+     *    ENC_TYPE_WPA3,
+     *    ENC_TYPE_NONE,
+     *    ENC_TYPE_AUTO,
+     *    ENC_TYPE_UNKNOWN = 255
+     */
+    uint8_t encryptionType(uint8_t networkItem);
+
+    uint8_t* BSSID(uint8_t networkItem, uint8_t* bssid);
+    uint8_t channel(uint8_t networkItem);
+
+    /*
+     * Return the RSSI of the networks discovered during the scanNetworks
+     *
+     * param networkItem: specify from which network item want to get the information
+     *
+     * return: signed value of RSSI of the specified item on the networks scanned list
+     */
+    int32_t RSSI(uint8_t networkItem);
+
 
     uint8_t channel();
 
diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index bb7603c7b..fd38b2795 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -811,6 +811,43 @@ uint8_t CWifiStation::getChannel() {
     return (uint8_t)access_point_cfg.channel;
 }
 
+const char* CWifiStation::getSSID(uint8_t i)
+{
+    if (access_points.size() > 0 && i < access_points.size()) {
+        return (const char*)access_points[i].ssid;
+    }
+    return nullptr;
+}
+
+int32_t CWifiStation::getRSSI(uint8_t i) {
+    if (access_points.size() > 0 && i < access_points.size()) {
+        return (int32_t)access_points[i].rssi;
+    }
+    return 0;
+}
+
+uint8_t CWifiStation::getEncrType(uint8_t i) {
+    if (access_points.size() > 0 && i < access_points.size()) {
+        return Encr2wl_enc(access_points[i].encryption_mode);
+    }
+    return 0;
+}
+
+uint8_t* CWifiStation::getBSSID(uint8_t i, uint8_t* bssid) {
+    if (access_points.size() > 0 && i < access_points.size()) {
+        CNetUtilities::macStr2macArray(bssid, (const char*)access_points[i].bssid);
+        return bssid;
+    }
+    return nullptr;
+}
+
+uint8_t CWifiStation::getChannel(uint8_t i) {
+    if (access_points.size() > 0 && i < access_points.size()) {
+        return (uint8_t)access_points[i].channel;
+    }
+    return 0;
+}
+
 int CWifiStation::setLowPowerMode() {
     return CEspControl::getInstance().setPowerSaveMode(1);
 }
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index faefa6bc3..c49c73ef6 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -259,6 +259,12 @@ class CWifiStation : public CNetIf {
     virtual uint8_t getEncryptionType();
     virtual uint8_t getChannel();
 
+    const char* getSSID(uint8_t i);
+    int32_t getRSSI(uint8_t i);
+    uint8_t getEncrType(uint8_t i);
+    uint8_t* getBSSID(uint8_t i, uint8_t* bssid);
+    uint8_t getChannel(uint8_t i);
+
     int setLowPowerMode();
     int resetLowPowerMode();
 protected:

From e0ce22c258e21d7e9373b42e40d3527bc74417a8 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 10 Jan 2024 04:42:09 +0100
Subject: [PATCH 49/79] removing lwipMem files

---
 libraries/lwIpWrapper/src/lwipMem.cpp | 119 --------------------------
 libraries/lwIpWrapper/src/lwipMem.h   |  10 ---
 2 files changed, 129 deletions(-)
 delete mode 100644 libraries/lwIpWrapper/src/lwipMem.cpp
 delete mode 100644 libraries/lwIpWrapper/src/lwipMem.h

diff --git a/libraries/lwIpWrapper/src/lwipMem.cpp b/libraries/lwIpWrapper/src/lwipMem.cpp
deleted file mode 100644
index bf255e636..000000000
--- a/libraries/lwIpWrapper/src/lwipMem.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-#include "lwipMem.h"
-
-/* -------------------------------------------------------------------------- */
-/*                          MEMORY ALLOCATION FUNCTIONS                       */
-/* -------------------------------------------------------------------------- */
-/**
- * @brief  Allocate a pbuf with data pass in parameter
- * @param  p: pointer to pbuf
- * @param  buffer: pointer to data to store
- * @param  size: number of data to store
- * @retval pointer to the pbuf allocated
- */
-struct pbuf* pbuffer_put_data(struct pbuf* p, const uint8_t* buffer, size_t size)
-{
-    // Allocate memory if pbuf doesn't exit yet.
-    if (p == NULL) {
-        p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
-
-        if (p != NULL) {
-            // Copy data inside pbuf
-            if (ERR_OK == pbuf_take(p, (uint8_t*)buffer, size)) {
-                return p;
-            } else {
-                pbuf_free(p);
-            }
-        }
-    }
-    // If pbuf allocated, grow the size of pbuf and add new data
-    // NOTE: pbuf_realloc can't be used to grow the size of pbuf
-    else {
-        struct pbuf* q = pbuf_alloc(PBUF_TRANSPORT, size + p->tot_len, PBUF_RAM);
-
-        if (q != NULL) {
-            if (ERR_OK == pbuf_copy(q, p)) {
-                if (ERR_OK == pbuf_take_at(q, (uint8_t*)buffer, size, p->tot_len)) {
-                    pbuf_free(p);
-                    p = q;
-                    return p;
-                }
-            }
-
-            pbuf_free(q);
-        }
-    }
-
-    return 0;
-}
-
-/**
- * @brief  Free pbuf
- *
- * @param  p: pointer to pbuf
- * @retval return always NULL
- */
-struct pbuf* pbuffer_free_data(struct pbuf* p)
-{
-    uint16_t n;
-
-    if (p != NULL) {
-        do {
-            n = pbuf_free(p);
-        } while (n == 0);
-    }
-
-    return NULL;
-}
-
-/**
- * @brief This function passes pbuf data to uin8_t buffer. It takes account if
- * pbuf is chained.
- * @param data pointer to data structure
- * @param buffer the buffer where write the data read
- * @param size the number of data to read
- * @retval number of data read
- */
-uint16_t pbuffer_get_data(struct pbuf_data* data, uint8_t* buffer, size_t size)
-{
-    uint16_t i;
-    uint16_t offset;
-    uint16_t nb;
-    struct pbuf* ptr;
-
-    if ((data->p == NULL) || (buffer == NULL) || (size == 0) || (data->available == 0) || (data->available > data->p->tot_len)) {
-        return 0;
-    }
-
-    nb = 0;
-
-    while ((nb < size) && (data->p != NULL) && (data->available > 0)) {
-        ptr = data->p;
-        offset = ptr->tot_len - data->available;
-
-        /* Get data from p */
-        for (i = 0; (nb < size) && ((offset + i) < ptr->len) && (data->available > 0); i++) {
-            buffer[nb] = pbuf_get_at(ptr, offset + i);
-            nb++;
-            data->available--;
-        }
-
-        if (nb < size) {
-            /* continue with next pbuf in chain (if any) */
-            data->p = ptr->next;
-
-            if (data->p != NULL) {
-                /* increment reference count for p */
-                pbuf_ref(data->p);
-            }
-
-            /* chop first pbuf from chain */
-            ptr = pbuffer_free_data(ptr);
-        }
-    }
-
-    if (data->available == 0) {
-        data->p = pbuffer_free_data(data->p);
-    }
-
-    return nb;
-}
\ No newline at end of file
diff --git a/libraries/lwIpWrapper/src/lwipMem.h b/libraries/lwIpWrapper/src/lwipMem.h
deleted file mode 100644
index 8dc8be643..000000000
--- a/libraries/lwIpWrapper/src/lwipMem.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef _ARDUINO_LWIP_MEM_H
-#define _ARDUINO_LWIP_MEM_H
-
-#include "lwipTypes.h"
-
-struct pbuf* pbuffer_put_data(struct pbuf* p, const uint8_t* buffer, size_t size);
-struct pbuf* pbuffer_free_data(struct pbuf* p);
-uint16_t pbuffer_get_data(struct pbuf_data* data, uint8_t* buffer, size_t size);
-
-#endif
\ No newline at end of file

From 4a1e9d4d2e637cfe114e23600c43b5beea4b697b Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 10 Jan 2024 04:43:31 +0100
Subject: [PATCH 50/79] moving free_pbuf_chain to an external file in order to
 be shared from udp and tcp

---
 libraries/lwIpWrapper/src/lwipClient.cpp | 52 ++++--------------------
 libraries/lwIpWrapper/src/lwipClient.h   |  1 -
 libraries/lwIpWrapper/src/lwippbuf.cpp   | 44 ++++++++++++++++++++
 libraries/lwIpWrapper/src/lwippbuf.h     | 12 ++++++
 4 files changed, 64 insertions(+), 45 deletions(-)
 create mode 100644 libraries/lwIpWrapper/src/lwippbuf.cpp
 create mode 100644 libraries/lwIpWrapper/src/lwippbuf.h

diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index 998214302..8dfc454c4 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -5,6 +5,7 @@ extern "C" {
 #include "Arduino.h"
 
 #include "lwipClient.h"
+#include "lwippbuf.h"
 #include "CNetIf.h"
 #include "utils.h"
 // FIXME understand hos to syncronize the interrupt thread and "userspace"
@@ -310,8 +311,10 @@ int lwipClient::read(uint8_t* buffer, size_t size) {
     arduino::lock();
     uint16_t copied = pbuf_copy_partial(this->tcp_info->pbuf_head, buffer, size, this->tcp_info->pbuf_offset);
 
-    this->free_pbuf_chain(copied);
-    // __enable_irq();
+    this->tcp_info->pbuf_head = free_pbuf_chain(this->tcp_info->pbuf_head, copied, &this->tcp_info->pbuf_offset);
+
+    // acknowledge the received data
+    tcp_recved(this->tcp_info->pcb, copied);
     arduino::unlock();
 
     return copied;
@@ -433,50 +436,11 @@ size_t lwipClient::read_until_token(
 
     uint16_t copied = pbuf_copy_partial(this->tcp_info->pbuf_head, (uint8_t*)buffer, buf_copy_len, this->tcp_info->pbuf_offset);
 
-    this->free_pbuf_chain(copied);
-    arduino::unlock();
-
-    return copied;
-}
-
-void lwipClient::free_pbuf_chain(uint16_t copied) {
-    arduino::lock();
-    /*
-     * free pbufs that have been copied, if copied == 0 we have an error
-     * free the buffer chain starting from the head up to the last entire pbuf ingested
-     * taking into account the previously not entirely consumed pbuf
-     */
-    uint32_t tobefreed = 0;
-    copied += this->tcp_info->pbuf_offset;
-
-    // in order to clean up the chain we need to find the pbuf in the last pbuf in the chain
-    // that got completely consumed by the application, dechain it from it successor and delete the chain before it
-
-    struct pbuf *head = this->tcp_info->pbuf_head, *last=head, *prev=nullptr; // FIXME little optimization prev can be substituted by last->next
-
-    while(last!=nullptr && last->len + tobefreed <= copied) {
-        tobefreed += last->len;
-        prev = last;
-        last = last->next;
-    }
-
-    // dechain if we are not at the end of the chain (last == nullptr)
-    // and if we haven't copied entirely the first pbuf (prev == nullptr) (head == last)
-    // if we reached the end of the chain set the this pbuf pointer to nullptr
-    if(prev != nullptr) {
-        prev->next = nullptr;
-        this->tcp_info->pbuf_head = last;
-    }
-
-    // the chain that is referenced by head is detached by the one referenced by this->tcp_info->pbuf_head
-    // free the chain if we haven't copied entirely the first pbuf (prev == nullptr)
-    if(this->tcp_info->pbuf_head != head) {
-        uint8_t refs = pbuf_free(head);
-    }
-
-    this->tcp_info->pbuf_offset = copied - tobefreed; // This offset should be referenced to the first pbuf in queue
+    this->tcp_info->pbuf_head = free_pbuf_chain(this->tcp_info->pbuf_head, copied, &this->tcp_info->pbuf_offset);
 
     // acknowledge the received data
     tcp_recved(this->tcp_info->pcb, copied);
     arduino::unlock();
+
+    return copied;
 }
diff --git a/libraries/lwIpWrapper/src/lwipClient.h b/libraries/lwIpWrapper/src/lwipClient.h
index 17e9985d7..446c4288b 100644
--- a/libraries/lwIpWrapper/src/lwipClient.h
+++ b/libraries/lwIpWrapper/src/lwipClient.h
@@ -104,7 +104,6 @@ class lwipClient : public arduino::Client {
     ip_addr_t _ip;
 
     err_t connected_callback(struct tcp_pcb* tpcb, err_t err);
-    void free_pbuf_chain(uint16_t copied);
     err_t recv_callback(struct tcp_pcb* tpcb, struct pbuf* p, err_t err);
 
     friend err_t _lwip_tcp_recv_callback(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err);
diff --git a/libraries/lwIpWrapper/src/lwippbuf.cpp b/libraries/lwIpWrapper/src/lwippbuf.cpp
new file mode 100644
index 000000000..24c6c71d5
--- /dev/null
+++ b/libraries/lwIpWrapper/src/lwippbuf.cpp
@@ -0,0 +1,44 @@
+#include "lwippbuf.h"
+#include "utils.h"
+
+struct pbuf* free_pbuf_chain(struct pbuf* p, uint16_t copied, uint16_t *offset) {
+    arduino::lock();
+    /*
+     * free pbufs that have been copied, if copied == 0 we have an error
+     * free the buffer chain starting from the head up to the last entire pbuf ingested
+     * taking into account the previously not entirely consumed pbuf
+     */
+    uint32_t tobefreed = 0;
+    copied += *offset;
+
+    // in order to clean up the chain we need to find the pbuf in the last pbuf in the chain
+    // that got completely consumed by the application, dechain it from it successor and delete the chain before it
+
+    struct pbuf *head = p, *last=head, *prev=nullptr; // FIXME little optimization prev can be substituted by last->next
+
+    while(last!=nullptr && last->len + tobefreed <= copied) {
+        tobefreed += last->len;
+        prev = last;
+        last = last->next;
+    }
+
+    // dechain if we are not at the end of the chain (last == nullptr)
+    // and if we haven't copied entirely the first pbuf (prev == nullptr) (head == last)
+    // if we reached the end of the chain set the this pbuf pointer to nullptr
+    if(prev != nullptr) {
+        prev->next = nullptr;
+        p = last;
+    }
+
+    // the chain that is referenced by head is detached by the one referenced by p
+    // free the chain if we haven't copied entirely the first pbuf (prev == nullptr)
+    if(p != head) {
+        uint8_t refs = pbuf_free(head);
+    }
+
+    *offset = copied - tobefreed; // This offset should be referenced to the first pbuf in queue
+
+    arduino::unlock();
+
+    return p;
+}
\ No newline at end of file
diff --git a/libraries/lwIpWrapper/src/lwippbuf.h b/libraries/lwIpWrapper/src/lwippbuf.h
new file mode 100644
index 000000000..498d6f9a7
--- /dev/null
+++ b/libraries/lwIpWrapper/src/lwippbuf.h
@@ -0,0 +1,12 @@
+#pragma once
+#include <lwip/include/lwip/pbuf.h>
+
+/**
+ * This function aim to free a pbuf chain that has been partially consumed.
+ * @param p the head of the pbuf chain
+ * @param copied the size that had been consumed in the last operation
+ * @param offset the size that had been consumed in the previous operations,
+ *               this value will be updated with the ffset of the new head
+ * @return the new pbuf head
+ */
+struct pbuf* free_pbuf_chain(struct pbuf* p, uint16_t copied, uint16_t *offset);
\ No newline at end of file

From 4980778fcd65385219235de0424c2c68d726558e Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 10 Jan 2024 04:44:16 +0100
Subject: [PATCH 51/79] moving lwipTypes udp_struct into udp file

---
 libraries/lwIpWrapper/src/lwipTypes.h | 24 ------------------------
 libraries/lwIpWrapper/src/lwipUDP.h   | 12 ++++++++++--
 2 files changed, 10 insertions(+), 26 deletions(-)
 delete mode 100644 libraries/lwIpWrapper/src/lwipTypes.h

diff --git a/libraries/lwIpWrapper/src/lwipTypes.h b/libraries/lwIpWrapper/src/lwipTypes.h
deleted file mode 100644
index a0282de4d..000000000
--- a/libraries/lwIpWrapper/src/lwipTypes.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef ARDUINO_LWIP_IF_TYPES_H
-#define ARDUINO_LWIP_IF_TYPES_H
-
-#include "lwip/include/lwip/ip_addr.h"
-#include "lwip/include/lwip/pbuf.h"
-#include <functional>
-
-/* Exported types ------------------------------------------------------------*/
-/* Struct to store received data */
-struct pbuf_data {
-    struct pbuf* p; // the packet buffer that was received
-    uint16_t available; // number of data
-};
-
-/* UDP structure */
-struct udp_struct {
-    struct udp_pcb* pcb; /* pointer on the current udp_pcb */
-    struct pbuf_data data;
-    ip_addr_t ip; // the remote IP address from which the packet was received
-    u16_t port; // the remote port from which the packet was received
-    std::function<void()> onDataArrival;
-};
-
-#endif
diff --git a/libraries/lwIpWrapper/src/lwipUDP.h b/libraries/lwIpWrapper/src/lwipUDP.h
index 2b0ee8c11..a023e9a60 100644
--- a/libraries/lwIpWrapper/src/lwipUDP.h
+++ b/libraries/lwIpWrapper/src/lwipUDP.h
@@ -5,11 +5,19 @@
 #include <functional>
 
 #include "CNetIf.h"
-#include "lwipMem.h"
-#include "lwipTypes.h"
 
 #define UDP_TX_PACKET_MAX_SIZE 24
 
+/* UDP structure */
+struct udp_struct {
+    struct udp_pcb* pcb; /* pointer on the current udp_pcb */
+    struct pbuf* p;
+    uint16_t pbuf_offset;
+    ip_addr_t ip; // the remote IP address from which the packet was received
+    u16_t port; // the remote port from which the packet was received
+    std::function<void()> onDataArrival;
+};
+
 class lwipUDP : public UDP {
 private:
     uint16_t _port; // local port to listen on

From 56c1d076e95f16c508b5e321550ba828f91bfc2c Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 10 Jan 2024 04:44:52 +0100
Subject: [PATCH 52/79] readapting udp class

---
 libraries/lwIpWrapper/src/lwipUDP.cpp | 95 +++++++++++++++------------
 1 file changed, 54 insertions(+), 41 deletions(-)

diff --git a/libraries/lwIpWrapper/src/lwipUDP.cpp b/libraries/lwIpWrapper/src/lwipUDP.cpp
index 2e84ac269..5385236c2 100644
--- a/libraries/lwIpWrapper/src/lwipUDP.cpp
+++ b/libraries/lwIpWrapper/src/lwipUDP.cpp
@@ -5,6 +5,8 @@
 
 #include "lwip/include/lwip/igmp.h"
 #include "lwip/include/lwip/ip_addr.h"
+#include "utils.h"
+#include "lwippbuf.h"
 
 #if LWIP_UDP
 /**
@@ -25,12 +27,13 @@ void udp_receive_callback(void* arg, struct udp_pcb* pcb, struct pbuf* p,
     /* Send data to the application layer */
     if ((udp_arg != NULL) && (udp_arg->pcb == pcb)) {
         // Free the old p buffer if not read
-        if (udp_arg->data.p != NULL) {
-            pbuf_free(udp_arg->data.p);
+        if (udp_arg->p != NULL) {
+            pbuf_free(udp_arg->p);
+            udp_arg->p = NULL;
         }
 
-        udp_arg->data.p = p;
-        udp_arg->data.available = p->len;
+        udp_arg->p = p;
+        udp_arg->pbuf_offset = 0;
 
         ip_addr_copy(udp_arg->ip, *addr);
         udp_arg->port = port;
@@ -64,7 +67,8 @@ uint8_t lwipUDP::begin(IPAddress ip, uint16_t port, bool multicast)
 
     ip_addr_t ipaddr;
     err_t err;
-    // u8_to_ip_addr(rawIPAddress(ip), &ipaddr); // FIXME
+    ipaddr = fromArduinoIP(ip);
+
     if (multicast) {
         err = udp_bind(_udp.pcb, IP_ADDR_ANY, port);
     } else {
@@ -86,7 +90,7 @@ uint8_t lwipUDP::begin(IPAddress ip, uint16_t port, bool multicast)
     _port = port;
     _remaining = 0;
 
-    // CLwipIf::getInstance().lwip_task();
+    CLwipIf::getInstance().task();
 
     return 1;
 }
@@ -107,7 +111,7 @@ void lwipUDP::stop()
         _udp.pcb = NULL;
     }
 
-    // CLwipIf::getInstance().lwip_task();
+    CLwipIf::getInstance().task();
 }
 
 int lwipUDP::beginPacket(const char* host, uint16_t port)
@@ -135,7 +139,7 @@ int lwipUDP::beginPacket(IPAddress ip, uint16_t port)
     _sendtoPort = port;
 
     udp_recv(_udp.pcb, &udp_receive_callback, &_udp);
-    // CLwipIf::getInstance().lwip_task();
+    CLwipIf::getInstance().task();
 
     return 1;
 }
@@ -145,18 +149,24 @@ int lwipUDP::endPacket()
     if ((_udp.pcb == NULL) || (_data == NULL)) {
         return 0;
     }
-
-    ip_addr_t ipaddr;
-    // if (ERR_OK != udp_sendto(_udp.pcb, _data, u8_to_ip_addr(rawIPAddress(_sendtoIP), &ipaddr), _sendtoPort)) {
-    //     __disable_irq();
-    //     _data = pbuffer_free_data(_data);
-    //     __enable_irq();
-    //     return 0;
-    // }
+    /*
+     * FIXME in this way, the derived classes for wifi and ethernet won't send data through the correct iface
+     *       the solution to this issue is by using udp_sendto_if, by this needs further rework
+     */
+    ip_addr_t ipaddr = fromArduinoIP(_sendtoIP);
+    if (ERR_OK != udp_sendto(
+            _udp.pcb, _data,
+            &ipaddr,
+            _sendtoPort)) {
+        __disable_irq();
+        pbuf_free(_data);
+        __enable_irq();
+        return 0;
+    }
 
     _data = NULL;
 
-    // CLwipIf::getInstance().lwip_task();
+    CLwipIf::getInstance().task();
 
     return 1;
 }
@@ -169,11 +179,20 @@ size_t lwipUDP::write(uint8_t byte)
 size_t lwipUDP::write(const uint8_t* buffer, size_t size)
 {
     __disable_irq();
-    _data = pbuffer_put_data(_data, buffer, size);
-    __enable_irq();
-    if (_data == NULL) {
+    struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
+
+    if(p == NULL) {
         return 0;
     }
+    pbuf_take(p, buffer, size);
+
+    if(_data == NULL) {
+        _data = p;
+    } else {
+        // no need to increment the reference count of the pbuf, since it is already 1
+        pbuf_cat(_data, p);
+    }
+    __enable_irq();
 
     return size;
 }
@@ -188,12 +207,12 @@ int lwipUDP::parsePacket()
     //   read();
     // }
 
-    // CLwipIf::getInstance().lwip_task();
+    CLwipIf::getInstance().task();
 
-    if (_udp.data.available > 0) {
-        // _remoteIP = IPAddress(ip_addr_to_u32(&(_udp.ip))); // FIXME
+    if (_udp.p == nullptr? 0: _udp.p->tot_len) {
+        _remoteIP = toArduinoIP(&_udp.ip);
         _remotePort = _udp.port;
-        _remaining = _udp.data.available;
+        _remaining = _udp.p->tot_len;
 
         return _remaining;
     }
@@ -205,7 +224,7 @@ int lwipUDP::read()
 {
     uint8_t byte;
 
-    if (_udp.data.p == NULL) {
+    if (_udp.p == NULL) {
         return -1;
     }
 
@@ -221,25 +240,19 @@ int lwipUDP::read()
 
 int lwipUDP::read(unsigned char* buffer, size_t len)
 {
-    if (_udp.data.p == NULL) {
+    if (_udp.p == NULL) {
         return -1;
     }
 
     if (_remaining > 0) {
-        int got;
-
-        if (_remaining <= len) {
-            // data should fit in the buffer
-            __disable_irq();
-            got = (int)pbuffer_get_data(&(_udp.data), (uint8_t*)buffer, _remaining);
-            __enable_irq();
-        } else {
-            // too much data for the buffer,
-            // grab as much as will fit
-            __disable_irq();
-            got = (int)pbuffer_get_data(&(_udp.data), (uint8_t*)buffer, len);
-            __enable_irq();
-        }
+        __disable_irq();
+        int got = pbuf_copy_partial(
+            _udp.p,
+            buffer,
+            _remaining < len ? _remaining : len, _udp.pbuf_offset);
+
+        _udp.p = free_pbuf_chain(_udp.p, got, &_udp.pbuf_offset);
+        __enable_irq();
 
         if (got > 0) {
             _remaining -= got;
@@ -260,7 +273,7 @@ int lwipUDP::peek()
     if (!_remaining) {
         return -1;
     }
-    b = pbuf_get_at(_udp.data.p, 0);
+    b = pbuf_get_at(_udp.p, 0);
     return b;
 }
 

From e1af45e2c7fec80d226e06e7283b0fd070a43f59 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 12 Jan 2024 10:36:10 +0100
Subject: [PATCH 53/79] improving task syncrhonization between timer and main
 context

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 69 +++++++++++++---------------
 libraries/lwIpWrapper/src/CNetIf.h   | 19 +++++++-
 2 files changed, 49 insertions(+), 39 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index fd38b2795..16791b03a 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -25,7 +25,7 @@ static void _getHostByNameCBK(const char *name, const ip_addr_t *ipaddr, void *c
 
 #ifdef LWIP_USE_TIMER
 static void timer_cb(timer_callback_args_t* arg);
-#endif
+#endif // LWIP_USE_TIMER
 
 // Custom Pbuf definition used to handle RX zero copy
 // TODO Move this in a separate file (understand if it is required)
@@ -40,7 +40,6 @@ static void zerocopy_pbuf_mem_free(struct pbuf *p) {
     // SYS_ARCH_DECL_PROTECT(zerocopy_pbuf_free);
     zerocopy_pbuf_t* zcpbuf = (zerocopy_pbuf_t*) p;
 
-    // arduino::lock();
     // SYS_ARCH_PROTECT(zerocopy_pbuf_free);
 
     // FIXME pbufs may be allocated in a different memory pool, deallocate them accordingly
@@ -48,8 +47,6 @@ static void zerocopy_pbuf_mem_free(struct pbuf *p) {
     zcpbuf->buffer = nullptr;
     mem_free(zcpbuf); // TODO understand if pbuf_free deletes the pbuf
     // SYS_ARCH_UNPROTECT(zerocopy_pbuf_free);
-
-    // arduino::unlock();
 }
 
 static inline zerocopy_pbuf_t* get_zerocopy_pbuf(uint8_t *buffer, uint32_t size, void(*buffer_free)(void*) = mem_free) {
@@ -512,7 +509,6 @@ err_t CEth::output(struct netif* ni, struct pbuf* p) {
 
 void CEth::consume_callback(uint8_t* buffer, uint32_t len) {
     // TODO understand if this callback can be moved into the base class
-    // arduino::lock();
 
     const uint16_t trimmed_size = len;
 
@@ -536,7 +532,6 @@ void CEth::consume_callback(uint8_t* buffer, uint32_t len) {
     } else {
         // NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len);
     }
-    // arduino::unlock();
 }
 
 /* ########################################################################## */
@@ -556,7 +551,6 @@ int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddres
     int res = 0;
     int time_num = 0;
 
-    // arduino::lock();
     CEspControl::getInstance().listenForStationDisconnectEvent([this] (CCtrlMsgWrapper *resp) -> int {
         netif_set_link_down(&this->ni);
         return ESP_CONTROL_OK;
@@ -578,10 +572,12 @@ int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddres
         time_num++;
     }
 
+    CLwipIf::getInstance().sync_timer();
     res = CEspControl::getInstance().setWifiMode(WIFI_MODE_STA);
+    CLwipIf::getInstance().enable_timer();
+
     CNetIf::begin(ip, nm, gw);
 exit:
-    // arduino::unlock();
     return res;
 }
 
@@ -590,8 +586,6 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
     int rv = ESP_CONTROL_CTRL_ERROR; // FIXME this should be set with an error meaning AP not found
     bool found = false;
     int8_t best_index = -1; // this index is used to find the ap with the best rssi
-    // AccessPoint_t* best_matching_ap;
-    // arduino::lock();
 
     if((rv=this->scanForAp()) != WL_SCAN_COMPLETED) {
         goto exit;
@@ -606,62 +600,60 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
         }
     }
     if(best_index != -1) {
-        // memset(ap.ssid, 0x00, SSID_LENGTH); // I shouldn't need to zero the ssid string pointer
         strncpy((char*)ap.ssid, ssid, SSID_LENGTH);
-        // memcpy(ap.ssid, access_points[best_index].ssid, SSID_LENGTH);
 
-        // memset(ap.pwd, 0x00, PASSWORD_LENGTH);
         if(passphrase != nullptr) {
             auto slen = strlen(passphrase)+1;
             strncpy((char*)ap.pwd, passphrase, (slen < PASSWORD_LENGTH) ? slen : PASSWORD_LENGTH);
-            // memcpy(ap.pwd, passphrase, (slen < PASSWORD_LENGTH) ? slen : PASSWORD_LENGTH);
         } else {
-            // memset(ap.pwd, 0x00, PASSWORD_LENGTH);
             ap.pwd[0] = '\0';
         }
 
         memset(ap.bssid, 0x00, BSSID_LENGTH);
         memcpy(ap.bssid, access_points[best_index].bssid, BSSID_LENGTH);
 
-        // arduino::lock();
-        CEspControl::getInstance().communicateWithEsp();
-
+        CLwipIf::getInstance().sync_timer();
         rv=CEspControl::getInstance().connectAccessPoint(ap);
-        // arduino::unlock();
 
         if (rv == ESP_CONTROL_OK) {
             CEspControl::getInstance().getAccessPointConfig(access_point_cfg);
 
             netif_set_link_up(&this->ni);
         }
-        // arduino::unlock();
+        CLwipIf::getInstance().enable_timer();
     }
 
 exit:
-    // arduino::unlock();
-
     return rv;
 }
 
 int CWifiStation::scanForAp() {
-        // arduino::lock();
     access_points.clear();
 
+    CLwipIf::getInstance().sync_timer();
+
     int res = CEspControl::getInstance().getAccessPointScanList(access_points);
+    CLwipIf::getInstance().enable_timer();
+
     if (res == ESP_CONTROL_OK) {
         res = WL_SCAN_COMPLETED;
     } else {
         res = WL_NO_SSID_AVAIL;
     }
 
-    // arduino::unlock();
 
     return res;
 }
 
 // disconnect
 int CWifiStation::disconnectFromAp() {
-    return CEspControl::getInstance().disconnectAccessPoint();
+    CLwipIf::getInstance().sync_timer();
+
+    auto res = CEspControl::getInstance().disconnectAccessPoint();
+
+    CLwipIf::getInstance().enable_timer();
+
+    return res;
 }
 
 err_t CWifiStation::init(struct netif* ni) {
@@ -698,7 +690,6 @@ err_t CWifiStation::output(struct netif* _ni, struct pbuf* p) {
     // NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
     // NETIF_STATS_TX_TIME_START(this->stats);
 
-    // arduino::lock();
     // p may be a chain of pbufs
     if(p->next != nullptr) {
         buf = (uint8_t*) malloc(size*sizeof(uint8_t));
@@ -731,7 +722,6 @@ err_t CWifiStation::output(struct netif* _ni, struct pbuf* p) {
     if(p->next != nullptr && buf != nullptr) {
         free(buf);
     }
-    // arduino::unlock();
     return errval;
 }
 
@@ -745,7 +735,6 @@ void CWifiStation::task() {
     struct pbuf* p = nullptr;
 
     // NETIF_STATS_RX_TIME_START(this->stats);
-    // arduino::lock();
     // TODO do not perform this when not connected to an AP
     if(hw_init) {
         CEspControl::getInstance().communicateWithEsp();
@@ -779,7 +768,6 @@ void CWifiStation::task() {
         buffer = CEspControl::getInstance().getStationRx(if_num, dim);
     }
     // NETIF_STATS_RX_TIME_AVERAGE(this->stats);
-    // arduino::unlock();
 }
 
 // void CWifiStation::consume_callback(uint8_t* buffer, uint32_t len) {
@@ -877,7 +865,6 @@ int CWifiSoftAp::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress
     int res = 0;
     int time_num = 0;
 
-    // arduino::lock();
     CEspControl::getInstance().listenForInitEvent([this] (CCtrlMsgWrapper *resp) -> int {
         // Serial.println("init");
         this->hw_init = true;
@@ -895,7 +882,9 @@ int CWifiSoftAp::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress
         time_num++;
     }
 
+    CLwipIf::getInstance().sync_timer();
     res = CEspControl::getInstance().setWifiMode(WIFI_MODE_AP);
+    CLwipIf::getInstance().enable_timer();
 
     CNetIf::begin(
         default_dhcp_server_ip,
@@ -903,13 +892,13 @@ int CWifiSoftAp::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress
         default_dhcp_server_ip
     );
 exit:
-    // arduino::unlock();
     return res;
 }
 
 // TODO scan the other access point first and then set the channel if 0
 // TODO there are requirements for ssid and password
 int CWifiSoftAp::startSoftAp(const char* ssid, const char* passphrase, uint8_t channel) {
+    CLwipIf::getInstance().sync_timer();
     SoftApCfg_t cfg;
 
     strncpy((char*)cfg.ssid, ssid, SSID_LENGTH);
@@ -942,7 +931,7 @@ int CWifiSoftAp::startSoftAp(const char* ssid, const char* passphrase, uint8_t c
         // wifi_status = WL_AP_FAILED;
     }
 
-
+    CLwipIf::getInstance().enable_timer();
     return rv;
 }
 
@@ -980,7 +969,6 @@ err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
     // NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats);
     // NETIF_STATS_TX_TIME_START(this->stats);
 
-    // arduino::lock();
     // p may be a chain of pbufs
     if(p->next != nullptr) {
         buf = (uint8_t*) malloc(size*sizeof(uint8_t));
@@ -1013,7 +1001,6 @@ err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) {
     if(p->next != nullptr && buf != nullptr) {
         free(buf);
     }
-    // arduino::unlock();
     return errval;
 }
 
@@ -1030,7 +1017,6 @@ void CWifiSoftAp::task() {
     struct pbuf* p = nullptr;
 
     // NETIF_STATS_RX_TIME_START(this->stats);
-    // arduino::lock();
     // TODO do not perform this when not connected to an AP
     if(hw_init) {
         CEspControl::getInstance().communicateWithEsp();
@@ -1065,7 +1051,6 @@ void CWifiSoftAp::task() {
         buffer = CEspControl::getInstance().getStationRx(if_num, dim);
     }
     // NETIF_STATS_RX_TIME_AVERAGE(this->stats);
-    // arduino::unlock();
 }
 
 const char* CWifiSoftAp::getSSID() {
@@ -1086,11 +1071,19 @@ uint8_t CWifiSoftAp::getChannel() {
 }
 
 int CWifiSoftAp::setLowPowerMode() {
-    return CEspControl::getInstance().setPowerSaveMode(1);
+    CLwipIf::getInstance().sync_timer();
+    auto res = CEspControl::getInstance().setPowerSaveMode(1);
+    CLwipIf::getInstance().enable_timer();
+
+    return res;
 }
 
 int CWifiSoftAp::resetLowPowerMode() {
-    return CEspControl::getInstance().setPowerSaveMode(1);
+    CLwipIf::getInstance().sync_timer();
+    auto res = CEspControl::getInstance().setPowerSaveMode(1);
+    CLwipIf::getInstance().enable_timer();
+
+    return res;
 }
 
 /* ##########################################################################
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index c49c73ef6..c113db866 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -382,10 +382,27 @@ class CLwipIf {
     // lwip stores the netif in a linked list called: netif_list
 
     friend class CNetIf;
+    friend class CWifiSoftAp;
+    friend class CWifiStation;
 
 #ifdef LWIP_USE_TIMER
     FspTimer timer;
-#endif
+
+    inline void sync_timer() {
+        timer.disable_overflow_irq();
+        this->task();
+    }
+
+    inline void enable_timer() {
+        timer.enable_overflow_irq();
+    }
+#else // LWIP_USE_TIMER
+    inline void sync_timer() {
+        this->task();
+    }
+
+    inline void enable_timer() { }
+#endif // LWIP_USE_TIMER
 };
 
 extern CEth Ethernet;

From 08097921b99fc97292f681137d643ba80b5dd15b Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 12 Jan 2024 10:37:12 +0100
Subject: [PATCH 54/79] fixing dns resolution bug

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 16791b03a..9d0736b44 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -172,7 +172,6 @@ static void _getHostByNameCBK(const char *name, const ip_addr_t *ipaddr, void *c
 
     cbk->cbk(toArduinoIP(ipaddr));
 
-    delete ipaddr;
     delete cbk;
 }
 
@@ -240,18 +239,23 @@ int CLwipIf::getHostByName(const char* aHostname, IPAddress& aResult, bool execu
 
 // TODO instead of returning int return an enum value
 int CLwipIf::getHostByName(const char* aHostname, std::function<void(const IPAddress&)> cbk) {
-    ip_addr_t *addr = new ip_addr_t;
+    /*
+     * according to lwip documentation: addr is a pointer to a ip_addr_t where to store the address if it is already cached
+     * in the dns_table (only valid if ERR_OK is returned!); thus this won't be the same ip_addr_t passed to the callback,
+     * there is no need to allocate it in the heap and delete it afterwards.
+     * on the contrary the struct dns_cbk must be allocated in the heap
+     */
+    ip_addr_t addr;
     uint8_t res = 0;
 
     dns_callback* dns_cbk = new dns_callback;
     dns_cbk->cbk = cbk;
-    err_t err = dns_gethostbyname(aHostname, addr, _getHostByNameCBK, dns_cbk);
+    err_t err = dns_gethostbyname(aHostname, &addr, _getHostByNameCBK, dns_cbk);
 
     switch(err) {
     case ERR_OK:
         // the address was already present in the local cache
-        cbk(toArduinoIP(addr));
-        delete addr;
+        cbk(toArduinoIP(&addr));
         delete dns_cbk;
         break;
     case ERR_INPROGRESS:
@@ -260,7 +264,6 @@ int CLwipIf::getHostByName(const char* aHostname, std::function<void(const IPAdd
         break;
     case ERR_ARG: // there are issues in the arguments passed
     default:
-        delete addr;
         delete dns_cbk;
         res = -1;
     }

From 9734322a19e713659f2310f4e0f1527cf04c6eaf Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 12 Jan 2024 10:37:44 +0100
Subject: [PATCH 55/79] cleaning code

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 9d0736b44..51cc15509 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -6,7 +6,6 @@
 // TODO hostname should be defined at network stack level and shared among ifaces
 // TODO buffer management (allocation/deallocation/trim/etc.) should be properly handled by a wrapper class and be transparent wrt the user
 // TODO the device could be moving and as a consequence it may be nice to rescan APs to get one with the best rssi
-// TODO implement setLowPowerMode and resetLowPowerMode in WIFI driver
 // TODO implement stop softAP and include it in the destructor of the class
 // TODO split netif definition in different files
 // TODO implement WIFINetworkDriver that is then being used by both Wifi station and softAP. This will allow to use both at the same time
@@ -412,9 +411,9 @@ void CNetIf::dhcpNotUsed() {
 }
 
 bool CNetIf::isDhcpAcquired() {
-    if(dhcp_acquired) {
-        Serial.println(ip_2_ip4(ni.ip_addr).addr, HEX);
-    }
+    // if(dhcp_acquired) {
+    //     Serial.println(ip_2_ip4(ni.ip_addr).addr, HEX);
+    // }
     return dhcp_acquired;
 }
 

From c3ba2770aec80439cdf6b8fe74380dd0242cb822 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 12 Jan 2024 10:38:55 +0100
Subject: [PATCH 56/79] fine tuning lwip stack and compiling it

---
 extras/net/lwipopts.h                         |  84 ++++++++++++++----
 .../lwIpWrapper/src/cortex-m33/liblwIP.a      | Bin 274428 -> 277056 bytes
 2 files changed, 69 insertions(+), 15 deletions(-)

diff --git a/extras/net/lwipopts.h b/extras/net/lwipopts.h
index 87d0d9a5c..b6acf324a 100644
--- a/extras/net/lwipopts.h
+++ b/extras/net/lwipopts.h
@@ -95,7 +95,7 @@
  * ATTENTION: This is required when using lwIP from more than one context! If
  * you disable this, you must be sure what you are doing!
  */
-#if !defined SYS_LIGHTWEIGHT_PROT
+#ifndef SYS_LIGHTWEIGHT_PROT
 #define SYS_LIGHTWEIGHT_PROT            0
 #endif
 
@@ -277,7 +277,7 @@
  * (requires the LWIP_RAW option)
  */
 #ifndef MEMP_NUM_RAW_PCB
-#define MEMP_NUM_RAW_PCB                0
+#define MEMP_NUM_RAW_PCB                4
 #endif
 
 /**
@@ -318,7 +318,7 @@
  * reassembly (whole packets, not fragments!)
  */
 #ifndef MEMP_NUM_REASSDATA
-#define MEMP_NUM_REASSDATA              0
+#define MEMP_NUM_REASSDATA              5
 #endif
 
 /**
@@ -329,7 +329,7 @@
  * where the packet is not yet sent when netif->output returns.
  */
 #ifndef MEMP_NUM_FRAG_PBUF
-#define MEMP_NUM_FRAG_PBUF              0
+#define MEMP_NUM_FRAG_PBUF              15
 #endif
 
 /**
@@ -353,11 +353,18 @@
 #endif
 
 /**
- * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts.
- * (requires NO_SYS==0)
+ * The number of sys timeouts used by the core stack (not apps)
+ * The default number of timeouts is calculated here for all enabled modules.
+ */
+#define LWIP_NUM_SYS_TIMEOUT_INTERNAL   (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_ACD + LWIP_IGMP + LWIP_DNS + PPP_NUM_TIMEOUTS + (LWIP_IPV6 * (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD + LWIP_IPV6_DHCP6)))
+
+/**
+ * MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active timeouts.
+ * The default number of timeouts is calculated here for all enabled modules.
+ * The formula expects settings to be either '0' or '1'.
  */
 #ifndef MEMP_NUM_SYS_TIMEOUT
-#define MEMP_NUM_SYS_TIMEOUT            6
+#define MEMP_NUM_SYS_TIMEOUT            LWIP_NUM_SYS_TIMEOUT_INTERNAL
 #endif
 
 /**
@@ -416,7 +423,11 @@
  * PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
  */
 #ifndef PBUF_POOL_SIZE
-#define PBUF_POOL_SIZE                  4
+#define PBUF_POOL_SIZE                  16
+#endif
+
+#ifndef LWIP_SUPPORT_CUSTOM_PBUF
+#define LWIP_SUPPORT_CUSTOM_PBUF        1
 #endif
 
 /**
@@ -549,16 +560,27 @@
  * an upper limit on the MSS advertised by the remote host.
  */
 #ifndef TCP_MSS
-#define TCP_MSS                        536
+#define TCP_MSS                        1420
 #endif
 
+/**
+ * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really
+ * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which
+ * reflects the available reassembly buffer size at the remote host) and the
+ * largest size permitted by the IP layer" (RFC 1122)
+ * Setting this to 1 enables code that checks TCP_MSS against the MTU of the
+ * netif used for a connection and limits the MSS if it would be too big otherwise.
+ */
+#ifndef TCP_CALCULATE_EFF_SEND_MSS
+#define TCP_CALCULATE_EFF_SEND_MSS      1
+#endif
 
 /**
  * TCP_SND_BUF: TCP sender buffer space (bytes).
  * To achieve good performance, this should be at least 2 * TCP_MSS.
  */
 #ifndef TCP_SND_BUF
-#define TCP_SND_BUF                     1500
+#define TCP_SND_BUF                     (4 * TCP_MSS)
 #endif
 
 /**
@@ -596,7 +618,7 @@
  * Define to 0 if your device is low on memory.
  */
 #ifndef TCP_QUEUE_OOSEQ
-#define TCP_QUEUE_OOSEQ                 0
+#define TCP_QUEUE_OOSEQ                 (LWIP_TCP)
 #endif
 
 /**
@@ -738,13 +760,45 @@
 /**
  * LWIP_DEBUG==1: Enable Debug.
  */
+
+#define LWIP_DBG_MIN_LEVEL              LWIP_DBG_LEVEL_ALL
+
 #define LWIP_DEBUG                      0
+
+#define ETHARP_DEBUG                    LWIP_DBG_OFF
 #define NETIF_DEBUG                     LWIP_DBG_OFF
-#define DHCP_DEBUG                      LWIP_DBG_OFF
-#define UDP_DEBUG                       LWIP_DBG_OFF
-#define MEMP_DEBUG                      LWIP_DBG_OFF
-#define MEM_DEBUG                       LWIP_DBG_OFF
+#define PBUF_DEBUG                      LWIP_DBG_OFF
+#define API_LIB_DEBUG                   LWIP_DBG_OFF
+#define API_MSG_DEBUG                   LWIP_DBG_OFF
+#define SOCKETS_DEBUG                   LWIP_DBG_OFF
 #define ICMP_DEBUG                      LWIP_DBG_OFF
+#define IGMP_DEBUG                      LWIP_DBG_OFF
+#define INET_DEBUG                      LWIP_DBG_OFF
+#define IP_DEBUG                        LWIP_DBG_OFF
+#define IP_REASS_DEBUG                  LWIP_DBG_OFF
+#define RAW_DEBUG                       LWIP_DBG_OFF
+#define MEM_DEBUG                       LWIP_DBG_OFF
+#define MEMP_DEBUG                      LWIP_DBG_OFF
+#define SYS_DEBUG                       LWIP_DBG_OFF
+#define TIMERS_DEBUG                    LWIP_DBG_OFF
+#define TCP_DEBUG                       LWIP_DBG_OFF
+#define TCP_INPUT_DEBUG                 LWIP_DBG_OFF
+#define TCP_FR_DEBUG                    LWIP_DBG_OFF
+#define TCP_RTO_DEBUG                   LWIP_DBG_OFF
+#define TCP_CWND_DEBUG                  LWIP_DBG_OFF
+#define TCP_WND_DEBUG                   LWIP_DBG_OFF
+#define TCP_OUTPUT_DEBUG                LWIP_DBG_OFF
+#define TCP_RST_DEBUG                   LWIP_DBG_OFF
+#define TCP_QLEN_DEBUG                  LWIP_DBG_OFF
+#define UDP_DEBUG                       LWIP_DBG_OFF
+#define TCPIP_DEBUG                     LWIP_DBG_OFF
+#define SLIP_DEBUG                      LWIP_DBG_OFF
+#define DHCP_DEBUG                      LWIP_DBG_OFF
+#define AUTOIP_DEBUG                    LWIP_DBG_OFF
+#define ACD_DEBUG                       LWIP_DBG_OFF
+#define DNS_DEBUG                       LWIP_DBG_OFF
+#define IP6_DEBUG                       LWIP_DBG_OFF
+#define DHCP6_DEBUG                     LWIP_DBG_OFF
 #endif
 
 //#define LWIP_USE_EXTERNAL_MBEDTLS       1
diff --git a/libraries/lwIpWrapper/src/cortex-m33/liblwIP.a b/libraries/lwIpWrapper/src/cortex-m33/liblwIP.a
index adaabfb1cd45db2e25e5800f1377a10dcf103d47..0009e3f547eccf14b47246cd35170fd3bcb855bf 100644
GIT binary patch
delta 20760
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
Z`?QBfRat~<k6DwYnq#h*UYb_^{{dxdHxB>+

delta 18172
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


From 66943b8e2d72755371d36398359c8b1594a62edc Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 12 Jan 2024 10:40:04 +0100
Subject: [PATCH 57/79] adding dns resolution with lwip example

---
 .../examples/dns_resolution/arduino_secrets.h |   2 +
 .../dns_resolution/dns_resolution.ino         | 119 ++++++++++++++++++
 2 files changed, 121 insertions(+)
 create mode 100644 libraries/lwIpWrapper/examples/dns_resolution/arduino_secrets.h
 create mode 100644 libraries/lwIpWrapper/examples/dns_resolution/dns_resolution.ino

diff --git a/libraries/lwIpWrapper/examples/dns_resolution/arduino_secrets.h b/libraries/lwIpWrapper/examples/dns_resolution/arduino_secrets.h
new file mode 100644
index 000000000..0c9fdd556
--- /dev/null
+++ b/libraries/lwIpWrapper/examples/dns_resolution/arduino_secrets.h
@@ -0,0 +1,2 @@
+#define SECRET_SSID ""
+#define SECRET_PASS ""
diff --git a/libraries/lwIpWrapper/examples/dns_resolution/dns_resolution.ino b/libraries/lwIpWrapper/examples/dns_resolution/dns_resolution.ino
new file mode 100644
index 000000000..7459a02eb
--- /dev/null
+++ b/libraries/lwIpWrapper/examples/dns_resolution/dns_resolution.ino
@@ -0,0 +1,119 @@
+#include <Arduino_DebugUtils.h>
+#include <EthernetC33.h>
+#include <WiFiC3.h>
+#include "arduino_secrets.h"
+
+static char const SSID[] = SECRET_SSID;  /* your network SSID (name) */
+static char const PASS[] = SECRET_PASS;  /* your network password (use for WPA, or use as key for WEP) */
+
+void application();
+
+#define BLOCKING_DNS_RESOLUTION
+
+void setup() {
+    Serial.begin(115200);
+    while(!Serial);
+
+    DEBUG_INFO("Setting up netif");
+
+    Ethernet.begin();
+
+    int res = 0;
+    DEBUG_INFO("Connecting to AP");
+    while((res=WiFi.begin(SSID, SECRET_PASS)) != ESP_CONTROL_OK) {
+        DEBUG_INFO("Connection failed retry: %d", res);
+        delay(1000);
+    }
+    DEBUG_INFO("Connected to AP");
+    DEBUG_INFO("Beginning");
+}
+
+void loop() {
+#ifndef LWIP_USE_TIMER
+    CLwipIf::getInstance().task();
+#endif
+
+    application();
+}
+
+// application stuff
+volatile uint8_t state = 0;
+uint32_t counter = 0;
+
+char* domains[] = {
+    "google.it"
+    , "www.google.com"
+    , "arduino.cc"
+    , "oniudra.cc"
+    , "youtube.it"
+    , "youtube.com"
+    , "github.com"
+    , "drive.google.com"
+};
+
+#ifndef BLOCKING_DNS_RESOLUTION
+void dns_cbk(const IPAddress& ip) {
+    DEBUG_INFO("%u DNS response for %s: %s ",
+        counter,
+        domains[counter % (sizeof(domains)/sizeof(char*))],
+        ip.toString().c_str());
+    state = 1;
+    counter++;
+}
+#endif // BLOCKING_DNS_RESOLUTION
+
+void application() {
+
+    switch(state) {
+    case 0:
+        if(WiFiStation.isDhcpAcquired() && Ethernet.isDhcpAcquired()) {
+            DEBUG_INFO("dhcp acquired");
+
+            state = 1;
+        }
+        break;
+    case 1: {
+        DEBUG_INFO("changing default Interface: \"%s\"", counter%2==0 ? "Ethernet": "WiFiStation");
+
+        CLwipIf::getInstance().setDefaultIface(counter%2==0? (CNetIf*)&Ethernet: (CNetIf*)&WiFiStation);
+
+        DEBUG_INFO("%u Performing DNS request for %s",
+            counter,
+            domains[counter % (sizeof(domains)/sizeof(char*))]);
+#ifdef BLOCKING_DNS_RESOLUTION
+        IPAddress ip;
+
+        auto res = CLwipIf::getInstance().getHostByName(
+            domains[counter % (sizeof(domains)/sizeof(char*))],
+            ip,
+#ifndef LWIP_USE_TIMER
+            true);
+#else
+            false);
+#endif
+
+        counter++;
+        DEBUG_INFO("%u DNS response for %s: %u %s ",
+            counter,
+            domains[counter % (sizeof(domains)/sizeof(char*))],
+            res,
+            ip.toString().c_str());
+#else // BLOCKING_DNS_RESOLUTION
+        state = 2;
+        auto res = CLwipIf::getInstance().getHostByName(
+            domains[counter % (sizeof(domains)/sizeof(char*))],
+            dns_cbk);
+
+        if(res != 1) {
+            counter++;
+        }
+#endif // BLOCKING_DNS_RESOLUTION
+        break;
+    }
+    case 2:
+        // do nothing, request made, wait for request to complete
+        break;
+    }
+
+}
+

From 9cb6f5529bcdca6d65a0dee28970b91ad9530d4d Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 12 Jan 2024 10:44:01 +0100
Subject: [PATCH 58/79] enablig execution of network stack in interrrupt
 context]

---
 libraries/lwIpWrapper/src/CNetIf.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index c113db866..20e97045c 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -27,7 +27,7 @@
 #include "lwIP_Arduino.h"
 #endif
 
-// #define LWIP_USE_TIMER
+#define LWIP_USE_TIMER
 
 #ifdef LWIP_USE_TIMER
 #include "FspTimer.h"

From 99ca0fa8ce50de6b3793d52847cde198940045d3 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 17 Jan 2024 18:51:23 +0100
Subject: [PATCH 59/79] added disconnect and link status for retrocompatibility

---
 libraries/lwIpWrapper/src/CNetIf.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 20e97045c..757d48122 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -79,6 +79,12 @@ typedef enum {
     NI_ETHERNET
 } NetIfType_t;
 
+enum EthernetLinkStatus {
+    Unknown,
+    LinkON,
+    LinkOFF
+};
+
 #define MAX_CLIENT MEMP_NUM_TCP_PCB
 #define MAX_DHCP_TRIES 4
 #define TIMEOUT_DNS_REQUEST 10000U
@@ -140,6 +146,10 @@ class CNetIf: public NetworkInterface {
     virtual void up();
     virtual void down();
 
+    inline int disconnect() { this->down(); return 0; }
+
+    inline EthernetLinkStatus linkStatus() { return netif_is_link_up(&ni) ? LinkON : LinkOFF; }
+
     bool isLinkUp() { return (bool)netif_is_link_up(&ni); }
 
     struct netif* getNi() { return &ni; }

From 7e88a577e64119128aede33b897c273e26ef697a Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 19 Jan 2024 16:12:44 +0100
Subject: [PATCH 60/79] fixing lwip server

---
 libraries/Ethernet/src/EthernetClient.h  |  3 +-
 libraries/Ethernet/src/EthernetServer.h  |  3 +-
 libraries/WiFi/src/WiFiClient.h          |  3 +-
 libraries/WiFi/src/WiFiServer.h          |  3 +-
 libraries/lwIpWrapper/src/lwipClient.cpp | 35 +++++++++++----------
 libraries/lwIpWrapper/src/lwipServer.cpp | 40 ++++++++++++++++++------
 libraries/lwIpWrapper/src/lwipServer.h   |  4 ++-
 7 files changed, 61 insertions(+), 30 deletions(-)

diff --git a/libraries/Ethernet/src/EthernetClient.h b/libraries/Ethernet/src/EthernetClient.h
index 7e201c76d..8d648b574 100644
--- a/libraries/Ethernet/src/EthernetClient.h
+++ b/libraries/Ethernet/src/EthernetClient.h
@@ -9,8 +9,9 @@ class EthernetClient : public lwipClient {
     EthernetClient(struct tcp_pcb *pcb, lwipServer *server)
     : lwipClient(pcb, server) {
     }
-    EthernetClient(lwipClient c)
+    EthernetClient(const lwipClient &c)
     : lwipClient(c) {
+        this->bindCNetIf(Ethernet);
     }
 
     int connect(IPAddress ip, uint16_t port) {
diff --git a/libraries/Ethernet/src/EthernetServer.h b/libraries/Ethernet/src/EthernetServer.h
index 1dbf791d4..bb48d4264 100644
--- a/libraries/Ethernet/src/EthernetServer.h
+++ b/libraries/Ethernet/src/EthernetServer.h
@@ -12,6 +12,7 @@ class EthernetServer: public lwipServer {
     }
 
     EthernetClient available() {
-        return EthernetClient(lwipServer::available());
+        lwipClient* res = available_ptr();
+        return res != nullptr ? EthernetClient(*res) : EthernetClient(CLIENT_NONE);
     }
 };
diff --git a/libraries/WiFi/src/WiFiClient.h b/libraries/WiFi/src/WiFiClient.h
index 29d229ee1..cad993d39 100644
--- a/libraries/WiFi/src/WiFiClient.h
+++ b/libraries/WiFi/src/WiFiClient.h
@@ -9,8 +9,9 @@ class WiFiClient: public lwipClient {
     WiFiClient(struct tcp_pcb *pcb, lwipServer *server)
     : lwipClient(pcb, server) {
     }
-    WiFiClient(lwipClient c)
+    WiFiClient(const lwipClient &c)
     : lwipClient(c) {
+        this->bindCNetIf(WiFiStation);
     }
 
     int connect(IPAddress ip, uint16_t port) {
diff --git a/libraries/WiFi/src/WiFiServer.h b/libraries/WiFi/src/WiFiServer.h
index c6d6a35f1..f06d8f775 100644
--- a/libraries/WiFi/src/WiFiServer.h
+++ b/libraries/WiFi/src/WiFiServer.h
@@ -12,6 +12,7 @@ class WiFiServer: public lwipServer {
     }
 
     WiFiClient available() {
-        return WiFiClient(lwipServer::available());
+        lwipClient* res = available_ptr();
+        return res != nullptr ? WiFiClient(*res) : WiFiClient(CLIENT_NONE);
     }
 };
diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index 8dfc454c4..1147a9563 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -80,7 +80,9 @@ lwipClient& lwipClient::operator=(lwipClient&& rhs) {
 }
 
 lwipClient::~lwipClient() {
-    this->stop();
+    if(this->tcp_info->state != TCP_CLOSING) {
+        this->stop();
+    }
 }
 
 int lwipClient::connect(const char* host, uint16_t port) {
@@ -231,7 +233,7 @@ err_t lwipClient::recv_callback(struct tcp_pcb* tpcb, struct pbuf* p, err_t err)
         return ERR_OK;
     }
     arduino::lock();
-    if(this->tcp_info->state == TCP_CONNECTED) {
+    if(this->tcp_info->state == TCP_CONNECTED || this->tcp_info->state == TCP_ACCEPTED) {
         if (this->tcp_info->pbuf_head == nullptr) {
             // no need to increment the references of the pbuf,
             // since it is already 1 and lwip shifts the control to this code
@@ -273,16 +275,11 @@ size_t lwipClient::write(const uint8_t* buffer, size_t size) {
         } else if(res == ERR_MEM) {
             // FIXME handle this: we get into this case only if the sent data cannot be put in the send queue
         }
-
-        // TODO understand if the tcp_write will send data if the buffer is not full
-        // force send only if we filled the send buffer
-        // if (ERR_OK != tcp_output(this->tcp_info->pcb)) {
-        //     // return 0;
-        //     break;
-        // }
     } while(buffer_cursor < buffer + size);
-    arduino::unlock();
 
+    tcp_output(this->tcp_info->pcb);
+
+    arduino::unlock();
     return buffer - buffer_cursor;
 }
 
@@ -342,13 +339,13 @@ void lwipClient::flush() {
 }
 
 void lwipClient::stop() {
-    tcp_recv(this->tcp_info->pcb, nullptr);
-    tcp_sent(this->tcp_info->pcb, nullptr);
-    tcp_poll(this->tcp_info->pcb, nullptr, 0);
-    tcp_err(this->tcp_info->pcb, nullptr);
-    tcp_accept(this->tcp_info->pcb, nullptr);
-
     if(this->tcp_info->pcb != nullptr) {
+        tcp_recv(this->tcp_info->pcb, nullptr);
+        tcp_sent(this->tcp_info->pcb, nullptr);
+        tcp_poll(this->tcp_info->pcb, nullptr, 0);
+        tcp_err(this->tcp_info->pcb, nullptr);
+        tcp_accept(this->tcp_info->pcb, nullptr);
+
         err_t err = tcp_close(this->tcp_info->pcb);
         this->tcp_info->state = TCP_CLOSING;
 
@@ -362,6 +359,12 @@ void lwipClient::stop() {
     // if(tcp->p != nullptr) {
     //     pbuf_free(tcp->p); // FIXME it happens that a pbuf, with ref == 0 is added for some reason
     // }
+    if(this->tcp_info->server != nullptr) {
+        // need to first make the server point to nullptr, then remove the client, can cause infinite recursion
+        auto server = this->tcp_info->server;
+        this->tcp_info->server = nullptr;
+        server->remove(this);
+    }
 }
 
 uint8_t lwipClient::connected() {
diff --git a/libraries/lwIpWrapper/src/lwipServer.cpp b/libraries/lwIpWrapper/src/lwipServer.cpp
index 151b1f9d8..360c33fa9 100644
--- a/libraries/lwIpWrapper/src/lwipServer.cpp
+++ b/libraries/lwIpWrapper/src/lwipServer.cpp
@@ -5,6 +5,7 @@ extern "C" {
 #include "CNetIf.h"
 #include "lwipClient.h"
 #include "lwipServer.h"
+#include "utils.h"
 
 err_t tcp_accept_callback(void* arg, struct tcp_pcb* newpcb, err_t err);
 
@@ -74,9 +75,12 @@ void lwipServer::begin(uint16_t port)
 // }
 
 void lwipServer::remove(lwipClient* client) {
+    arduino::lock();
+
     bool found = false;
     for (int i=0; i < size; i++) {
         if(found) {
+            // we move the client to delete to the end of the array, then we remove it
             clients[i-1] = clients[i];
         } else if(*client == *clients[i]) {
             found = true;
@@ -85,25 +89,41 @@ void lwipServer::remove(lwipClient* client) {
 
     delete clients[--size];
     clients[size] = nullptr;
+
+    arduino::unlock();
 }
 
-void lwipServer::accept(struct tcp_pcb* new_client) {
+bool lwipServer::accept(struct tcp_pcb* new_client) {
+    bool res = false;
     // this->clean();
-
+    arduino::lock();
     if(size < MAX_CLIENT-1) {
         clients[size] = new lwipClient(new_client, this);
         size++;
         clients_available++;
+        res = true;
     }
+    arduino::unlock();
+
+    return res;
 }
 
 lwipClient lwipServer::available()
 {
+    lwipClient* res = available_ptr();
+    return res != nullptr ? *res : CLIENT_NONE;
+}
+
+lwipClient* lwipServer::available_ptr()
+{
+    lwipClient* res=nullptr;
+    arduino::lock();
     if(size > 0 && clients_available>0) {
-        return *clients[size-clients_available--]; // TODO verify index
-    } else {
-        return CLIENT_NONE;
+        res = clients[size-clients_available--]; // TODO verify index
     }
+    arduino::unlock();
+
+    return res;
 }
 
 size_t lwipServer::write(uint8_t b)
@@ -112,28 +132,30 @@ size_t lwipServer::write(uint8_t b)
 }
 
 size_t lwipServer::write(const uint8_t* buffer, size_t size) {
+    arduino::lock();
     size_t written=0;
     // this->clean();
 
     for (int i = 0; i < MAX_CLIENT; i++) {
         written += clients[i]->write(buffer, size);
     }
+    arduino::unlock();
 
     return written;
 }
 
 err_t tcp_accept_callback(void* arg, struct tcp_pcb* newpcb, err_t err) {
+    arduino::lock();
     lwipServer* server = (lwipServer*) arg;
-    err_t ret_err;
+    err_t ret_err = ERR_OK;
 
     /* set priority for the newly accepted tcp connection newpcb */
     tcp_setprio(newpcb, TCP_PRIO_MIN);
 
-    if ((arg != NULL) && (ERR_OK == err)) {
-        server->accept(newpcb);
-    } else {
+    if ((arg == NULL) || (ERR_OK != err) || !server->accept(newpcb)) {
         tcp_close(newpcb);
         ret_err = ERR_ARG;
     }
+    arduino::unlock();
     return ret_err;
 }
\ No newline at end of file
diff --git a/libraries/lwIpWrapper/src/lwipServer.h b/libraries/lwIpWrapper/src/lwipServer.h
index c08514adb..2a7446923 100644
--- a/libraries/lwIpWrapper/src/lwipServer.h
+++ b/libraries/lwIpWrapper/src/lwipServer.h
@@ -31,10 +31,12 @@ class lwipServer: public Server {
      * - when a client connection is closed (by calling stop on it or delete on the client)
      *   the server is notified and the remove() method is called thus the client is removed from the server list.
      */
-    void accept(struct tcp_pcb* new_client);
+    bool accept(struct tcp_pcb* new_client);
     // void clean();
     void remove(lwipClient* client);
 
+    lwipClient* available_ptr();
+
     uint16_t _port;
     const IPAddress &listen_address;
     tcp_pcb* server_pcb;

From 88f50911c3ba1bc93a1285e872fcb1236c014f76 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 19 Jan 2024 16:40:29 +0100
Subject: [PATCH 61/79] blocking begin call on dhcp acquisition

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 24 +++++++++++++++++-------
 libraries/lwIpWrapper/src/CNetIf.h   |  2 +-
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 51cc15509..842caa98b 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -316,18 +316,28 @@ int CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw)
 
     netif_set_up(&this->ni);
 
+    // add the interface to the network stack
+    CLwipIf::getInstance().add_iface(this); // TODO remove interface when it is needed (??)
+    netif_set_link_up(&this->ni);
+
 #ifdef LWIP_DHCP
     // dhcp is started when begin gets ip == nullptr
     if(ip != INADDR_NONE) {
         this->dhcpNotUsed();
     } else {
         this->dhcpStart();
+
+
+        CLwipIf::getInstance().sync_timer();
+        while(!this->isDhcpAcquired()) {
+            CLwipIf::getInstance().task();
+        }
+        CLwipIf::getInstance().enable_timer();
     }
+
 #endif
 
-    // add the interface to the network stack
-    CLwipIf::getInstance().add_iface(this); // TODO remove interface when it is needed (??)
-    return 0;
+    return this->isDhcpAcquired()? 1 : 0;
 }
 
 void CNetIf::task() {
@@ -452,12 +462,12 @@ int CEth::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw, c
             this, std::placeholders:: _1, std::placeholders::_2));
 
     // Call the begin function on the Parent class to init the interface
-    CNetIf::begin(ip, nm, gw);
-    netif_set_link_up(&this->ni);
+    // netif_set_link_up(&this->ni);
+    auto res = CNetIf::begin(ip, nm, gw);
 
     CLwipIf::getInstance().addDnsServer(dns);
 
-    return 0;
+    return res;
 }
 
 err_t CEth::init(struct netif* ni) {
@@ -578,7 +588,7 @@ int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddres
     res = CEspControl::getInstance().setWifiMode(WIFI_MODE_STA);
     CLwipIf::getInstance().enable_timer();
 
-    CNetIf::begin(ip, nm, gw);
+    return CNetIf::begin(ip, nm, gw);
 exit:
     return res;
 }
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 757d48122..c9c95d022 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -180,7 +180,7 @@ class CNetIf: public NetworkInterface {
     struct netif ni;
 
 #ifdef LWIP_DHCP
-    bool dhcp_acquired;
+    volatile bool dhcp_acquired;
 #endif
 
     /*

From 0f430c51c0fa18ac43c82681167695cb2be731aa Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 22 Jan 2024 14:13:23 +0100
Subject: [PATCH 62/79] Making it possible to call multiple time the begin
 function on an interfare

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 83 ++++++++++++++++++++--------
 libraries/lwIpWrapper/src/CNetIf.h   |  9 +--
 2 files changed, 63 insertions(+), 29 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 842caa98b..e83987937 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -302,18 +302,32 @@ int CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw)
     ip_addr_t _nm = fromArduinoIP(nm);
     ip_addr_t _gw = fromArduinoIP(gw);
 
-    // netif add copies the ip addresses into the netif, no need to store them also in the object
-    struct netif *_ni = netif_add(
-        &this->ni,
-        &_ip, &_nm, &_gw, // ip addresses are being copied and not taken as reference, use a local defined variable
-        this,
-        _netif_init,
-        ethernet_input
-    );
-    if(_ni == nullptr) {
-        return -1;
-    }
+    char name[3] = {
+        ni.name[0],
+        ni.name[1],
+        ni.num + '0',
+    };
+    if(netif_find(name) == nullptr) {
+
+        // netif add copies the ip addresses into the netif, no need to store them also in the object
+        struct netif *_ni = netif_add(
+            &this->ni,
+            &_ip, &_nm, &_gw, // ip addresses are being copied and not taken as reference, use a local defined variable
+            this,
+            _netif_init,
+            ethernet_input
+        );
+
+        if(_ni == nullptr) {
+            return -1;
+        }
+    } else {
+        if (netif_is_link_up(&this->ni)) {
+            netif_set_down(&this->ni);
+        }
 
+        // TODO check for any changes detected and update the iface
+    }
     netif_set_up(&this->ni);
 
     // add the interface to the network stack
@@ -448,7 +462,7 @@ bool CNetIf::dhcpRenew() {
 /* ##########################################################################
  *                      ETHERNET NETWORK INTERFACE CLASS
  * ########################################################################## */
-uint8_t CEth::eth_id = 0;
+const char CEth::eth_ifname[] = "en";
 
 CEth::CEth(NetworkDriver *driver)
 : CNetIf(driver) {
@@ -470,19 +484,42 @@ int CEth::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw, c
     return res;
 }
 
+int CEth::begin(
+        uint8_t *mac_address,
+        const IPAddress &local_ip,
+        const IPAddress &dns_server,
+        const IPAddress &gateway,
+        const IPAddress &subnet,
+        const unsigned long timeout,
+        const unsigned long responseTimeout) {
+
+    this->setMacAddress(mac_address);
+
+    return this->begin(local_ip, subnet, gateway, dns_server);
+}
+
+int CEth::begin(
+        uint8_t *mac_address,
+        const unsigned long timeout,
+        const unsigned long responseTimeout) {
+
+    this->setMacAddress(mac_address);
+
+    return this->begin();
+}
+
+
 err_t CEth::init(struct netif* ni) {
     // Setting up netif
 #if LWIP_NETIF_HOSTNAME
     ni->hostname                       = "C33_eth";
 #endif
-    ni->name[0]                        = CEth::eth_ifname_prefix;
-    ni->name[1]                        = '0' + CEth::eth_id++;
+    ni->name[0]                        = CEth::eth_ifname[0];
+    ni->name[1]                        = CEth::eth_ifname[1];
     ni->mtu                            = 1500; // TODO get this from the network
     ni->flags                          |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
 
     memcpy(ni->hwaddr, this->driver->getMacAddress(), 6); // FIXME handle this using a constant
-    // ni->hwaddr                         = C33EthernetDriver.getMacAddress();
-    // ni->hwaddr_len                     = sizeof(macaddress);
     ni->hwaddr_len                     = 6;
 
     ni->output                         = etharp_output;
@@ -549,7 +586,7 @@ void CEth::consume_callback(uint8_t* buffer, uint32_t len) {
 /* ########################################################################## */
 /*                    CWifiStation NETWORK INTERFACE CLASS                    */
 /* ########################################################################## */
-uint8_t CWifiStation::wifistation_id = 0;
+const char CWifiStation::wifistation_ifname[] = "ws";
 
 CWifiStation::CWifiStation()
 : hw_init(false) {
@@ -673,8 +710,8 @@ err_t CWifiStation::init(struct netif* ni) {
 #if LWIP_NETIF_HOSTNAME
     ni->hostname                       = "C33-WifiSta";
 #endif
-    ni->name[0]                        = CWifiStation::wifistation_ifname_prefix;
-    ni->name[1]                        = '0' + CWifiStation::wifistation_id++;
+    ni->name[0]                        = CWifiStation::wifistation_ifname[0];
+    ni->name[1]                        = CWifiStation::wifistation_ifname[1];
     ni->mtu                            = 1500; // FIXME get this from the network
     ni->flags                          |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
 
@@ -859,7 +896,7 @@ int CWifiStation::resetLowPowerMode() {
 /* ########################################################################## */
 /*                      CWifiSoftAp NETWORK INTERFACE CLASS                   */
 /* ########################################################################## */
-uint8_t CWifiSoftAp::softap_id = 0;
+const char CWifiSoftAp::softap_ifname[] = "sa";
 
 // This is required for dhcp server to assign ip addresses to AP clients
 IPAddress default_nm("255.255.255.0");
@@ -883,7 +920,7 @@ int CWifiSoftAp::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress
         return ESP_CONTROL_OK;
     });
 
-    if ((res=CEspControl::getInstance().initSpiDriver()) != 0) {
+    if ((res=CEspControl::getInstance().initSpiDriver()) != 0 && !hw_init) {
         // res = -1; // FIXME put a proper error code
         goto exit;
     }
@@ -953,8 +990,8 @@ err_t CWifiSoftAp::init(struct netif* ni) {
     // TODO pass the hostname in the constructor os with a setter
     ni->hostname                       = "C33-SoftAP";
 #endif
-    ni->name[0]                        = CWifiSoftAp::softap_ifname_prefix;
-    ni->name[1]                        = '0' + CWifiSoftAp::softap_id++;
+    ni->name[0]                        = CWifiSoftAp::softap_ifname[0];
+    ni->name[1]                        = CWifiSoftAp::softap_ifname[1];
     ni->mtu                            = 1500; // FIXME get this from the network
     ni->flags                          |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
 
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index c9c95d022..0c6393272 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -234,8 +234,7 @@ class CEth : public CNetIf {
      */
     err_t output(struct netif* ni, struct pbuf* p) override;
 
-    static const char eth_ifname_prefix = 'e';
-    static uint8_t eth_id;
+    static const char eth_ifname[];
 private:
     /*
      * This function is passed to the driver class and it is meant to
@@ -278,8 +277,7 @@ class CWifiStation : public CNetIf {
     int setLowPowerMode();
     int resetLowPowerMode();
 protected:
-    static const char wifistation_ifname_prefix = 'w';
-    static uint8_t wifistation_id;
+    static const char wifistation_ifname[];
 
     /*
      * this function is used to initialize the netif structure of lwip
@@ -322,8 +320,7 @@ class CWifiSoftAp : public CNetIf {
     int setLowPowerMode();
     int resetLowPowerMode();
 protected:
-    static const char softap_ifname_prefix = 's';
-    static uint8_t softap_id;
+    static const char softap_ifname[];
     /*
      * this function is used to initialize the netif structure of lwip
      */

From ec3cba55e47302e6b07fce224f91411a2c253658 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Mon, 22 Jan 2024 14:19:07 +0100
Subject: [PATCH 63/79] making netif compatible with connection handler usage

---
 libraries/WiFi/src/WiFi.cpp          |  9 +++--
 libraries/lwIpWrapper/src/CNetIf.cpp | 39 +++++++++---------
 libraries/lwIpWrapper/src/CNetIf.h   | 59 +++++++++++++++++++++++-----
 3 files changed, 76 insertions(+), 31 deletions(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index bfe4906de..a6154baa4 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -23,16 +23,16 @@ const char* CWifi::firmwareVersion() {
 /* -------------------------------------------------------------------------- */
 int CWifi::begin(const char* ssid) {
 /* -------------------------------------------------------------------------- */
-    WiFiStation.begin();
-    return WiFiStation.connectToAP(ssid, nullptr);
+    WiFiStation.connectToAP(ssid, nullptr);
+    return WiFiStation.begin();
 }
 
 
 /* -------------------------------------------------------------------------- */
 int CWifi::begin(const char* ssid, const char *passphrase) {
 /* -------------------------------------------------------------------------- */
-    WiFiStation.begin();
-    return WiFiStation.connectToAP(ssid, passphrase);
+    WiFiStation.connectToAP(ssid, passphrase);
+    return WiFiStation.begin();;
 }
 
 /* passphrase is needed so a default one will be set */
@@ -242,6 +242,7 @@ uint8_t CWifi::channel(uint8_t networkItem) {
 uint8_t CWifi::status() { // FIXME
 /* -------------------------------------------------------------------------- */
     // return CLwipIf::getInstance().getWifiStatus();
+    return WiFiStation.status();
 }
 
 /* -------------------------------------------------------------------------- */
diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index e83987937..1b33457cb 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -597,11 +597,19 @@ CWifiStation::~CWifiStation() {
 }
 
 int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) {
-    int res = 0;
+    return CNetIf::begin(ip, nm, gw);
+}
+
+int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
+    WifiApCfg_t ap;
+    int rv = ESP_CONTROL_CTRL_ERROR; // FIXME this should be set with an error meaning AP not found
+    bool found = false;
+    int8_t best_index = -1; // this index is used to find the ap with the best rssi
     int time_num = 0;
 
     CEspControl::getInstance().listenForStationDisconnectEvent([this] (CCtrlMsgWrapper *resp) -> int {
         netif_set_link_down(&this->ni);
+        wifi_status = WL_DISCONNECTED;
         return ESP_CONTROL_OK;
     });
     CEspControl::getInstance().listenForInitEvent([this] (CCtrlMsgWrapper *resp) -> int {
@@ -610,10 +618,11 @@ int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddres
         return ESP_CONTROL_OK;
     });
 
-    if ((res=CEspControl::getInstance().initSpiDriver()) != 0) {
-        res = -1; // FIXME put a proper error code
+    if ((rv=CEspControl::getInstance().initSpiDriver()) != 0  && !hw_init) {
+        rv = -1; // FIXME put a proper error code
         goto exit;
     }
+    wifi_status = WL_NO_SSID_AVAIL;
 
     while (time_num < 100 && !hw_init) { // TODO #define WIFI_INIT_TIMEOUT_MS 10000
         CEspControl::getInstance().communicateWithEsp();
@@ -622,21 +631,11 @@ int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddres
     }
 
     CLwipIf::getInstance().sync_timer();
-    res = CEspControl::getInstance().setWifiMode(WIFI_MODE_STA);
+    rv = CEspControl::getInstance().setWifiMode(WIFI_MODE_STA);
     CLwipIf::getInstance().enable_timer();
 
-    return CNetIf::begin(ip, nm, gw);
-exit:
-    return res;
-}
-
-int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
-    WifiApCfg_t ap;
-    int rv = ESP_CONTROL_CTRL_ERROR; // FIXME this should be set with an error meaning AP not found
-    bool found = false;
-    int8_t best_index = -1; // this index is used to find the ap with the best rssi
-
     if((rv=this->scanForAp()) != WL_SCAN_COMPLETED) {
+        rv = -2;
         goto exit;
     }
 
@@ -666,8 +665,12 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
 
         if (rv == ESP_CONTROL_OK) {
             CEspControl::getInstance().getAccessPointConfig(access_point_cfg);
+            wifi_status = WL_CONNECTED;
+
 
             netif_set_link_up(&this->ni);
+        } else {
+            wifi_status = WL_CONNECT_FAILED;
         }
         CLwipIf::getInstance().enable_timer();
     }
@@ -685,13 +688,13 @@ int CWifiStation::scanForAp() {
     CLwipIf::getInstance().enable_timer();
 
     if (res == ESP_CONTROL_OK) {
-        res = WL_SCAN_COMPLETED;
+        wifi_status = WL_SCAN_COMPLETED;
     } else {
-        res = WL_NO_SSID_AVAIL;
+        wifi_status = WL_NO_SSID_AVAIL;
     }
 
 
-    return res;
+    return wifi_status;
 }
 
 // disconnect
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 0c6393272..fc7bf876b 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -79,12 +79,17 @@ typedef enum {
     NI_ETHERNET
 } NetIfType_t;
 
-enum EthernetLinkStatus {
+enum LinkStatus {
     Unknown,
     LinkON,
     LinkOFF
 };
 
+enum EthernetHardwareStatus {
+    EthernetNoHardware,
+    EthernetLwip = 7
+};
+
 #define MAX_CLIENT MEMP_NUM_TCP_PCB
 #define MAX_DHCP_TRIES 4
 #define TIMEOUT_DNS_REQUEST 10000U
@@ -148,7 +153,7 @@ class CNetIf: public NetworkInterface {
 
     inline int disconnect() { this->down(); return 0; }
 
-    inline EthernetLinkStatus linkStatus() { return netif_is_link_up(&ni) ? LinkON : LinkOFF; }
+    inline LinkStatus linkStatus() { return netif_is_link_up(&ni) ? LinkON : LinkOFF; }
 
     bool isLinkUp() { return (bool)netif_is_link_up(&ni); }
 
@@ -164,16 +169,10 @@ class CNetIf: public NetworkInterface {
     IPAddress gatewayIP()   { return IPAddress(this->getGwAdd()); }
     IPAddress dnsServerIP() { /* FIXME understand where dns should be managed */}
 
-    // FIXME hostname should be defined in the NetworkStack singleton
-    // void setHostname(const char* name)
-    // {
-    //     memset(hostname, 0x00, MAX_HOSTNAME_DIM);
-    //     memcpy(hostname, name, strlen(name) < MAX_HOSTNAME_DIM ? strlen(name) : MAX_HOSTNAME_DIM);
-    // }
     void config(IPAddress _ip, IPAddress _gw, IPAddress _nm);
 
-
     virtual int getMacAddress(uint8_t* mac) = 0;
+    virtual int setMacAddress(uint8_t* mac) = 0;
 
     friend CLwipIf;
 protected:
@@ -216,13 +215,40 @@ class CEth : public CNetIf {
         const IPAddress &gw = INADDR_NONE,
         const IPAddress &dns = INADDR_NONE);
 
+    // The following are overloaded begin methods kept for retrocompatibility with other Arduino cores
+    // Initialise the Ethernet shield to use the provided MAC address and gain the rest of the
+    // configuration through DHCP.
+    // Returns 0 if the DHCP configuration failed, and 1 if it succeeded
+    virtual int begin(
+        uint8_t *mac_address,
+        const IPAddress &local_ip           = INADDR_NONE,
+        const IPAddress &dns_server         = INADDR_NONE,
+        const IPAddress &gateway            = INADDR_NONE,
+        const IPAddress &subnet             = INADDR_NONE,
+        const unsigned long timeout         = 60000,
+        const unsigned long responseTimeout = 4000);
+
+    virtual int begin(
+        uint8_t *mac_address,
+        const unsigned long timeout         = 60000,
+        const unsigned long responseTimeout = 4000);
+
+
     virtual int getMacAddress(uint8_t* mac) override {
         UNUSED(mac); // FIXME not implemented
         return 1;
     }
 
+    virtual int setMacAddress(uint8_t* mac) override {
+        UNUSED(mac); // FIXME not implemented
+        return 1;
+    }
+
+
     int maintain() {} // Deprecated method for retrocompatibility
     void schedule(void) {} // Deprecated method for retrocompatibility
+
+    inline EthernetHardwareStatus hardwareStatus() { return EthernetLwip; }
 protected:
     /*
      * this function is used to initialize the netif structure of lwip
@@ -262,6 +288,11 @@ class CWifiStation : public CNetIf {
         // FIXME not implemented
     }
 
+    virtual int setMacAddress(uint8_t* mac) override {
+        UNUSED(mac); // FIXME not implemented
+        return 1;
+    }
+
     virtual const char* getSSID();
     virtual uint8_t* getBSSID(uint8_t* bssid);
     virtual int32_t getRSSI();
@@ -276,6 +307,10 @@ class CWifiStation : public CNetIf {
 
     int setLowPowerMode();
     int resetLowPowerMode();
+
+    inline WifiStatus_t status() {
+        return wifi_status;
+    }
 protected:
     static const char wifistation_ifname[];
 
@@ -293,6 +328,7 @@ class CWifiStation : public CNetIf {
     std::vector<AccessPoint_t> access_points;
     WifiApCfg_t access_point_cfg;
     bool hw_init; // TODO this should be moved to the wifi driver class
+    WifiStatus_t wifi_status = WL_IDLE_STATUS; // TODO this should be moved to the wifi driver class
 };
 
 class CWifiSoftAp : public CNetIf {
@@ -312,6 +348,11 @@ class CWifiSoftAp : public CNetIf {
         // FIXME not implemented
     }
 
+    virtual int setMacAddress(uint8_t* mac) override {
+        UNUSED(mac); // FIXME not implemented
+        return 1;
+    }
+
     virtual const char* getSSID();
     virtual uint8_t* getBSSID(uint8_t* bssid);
     virtual uint8_t getEncryptionType();

From e56d9f9ae001ac31f74c934f6a6baed499f5123d Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 23 Jan 2024 15:14:33 +0100
Subject: [PATCH 64/79] fixing return status of begin

---
 libraries/WiFi/src/WiFi.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp
index a6154baa4..99de072cc 100644
--- a/libraries/WiFi/src/WiFi.cpp
+++ b/libraries/WiFi/src/WiFi.cpp
@@ -32,7 +32,8 @@ int CWifi::begin(const char* ssid) {
 int CWifi::begin(const char* ssid, const char *passphrase) {
 /* -------------------------------------------------------------------------- */
     WiFiStation.connectToAP(ssid, passphrase);
-    return WiFiStation.begin();;
+    WiFiStation.begin();
+    return WiFiStation.status();
 }
 
 /* passphrase is needed so a default one will be set */

From 1ac341d8b781706d4075bccf323c0d4da323db1c Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 23 Jan 2024 14:51:20 +0100
Subject: [PATCH 65/79] adding test files for lwipwrapper WIP

---
 .../ethernet_bare_lwipc33.ino                 | 551 +++++++++++++++++
 .../examples/mixed_lwipc33/arduino_secrets.h  |   2 +
 .../examples/mixed_lwipc33/mixed_lwipc33.ino  | 565 ++++++++++++++++++
 .../softap_bare_lwipc33.ino                   | 108 ++++
 .../wifi_bare_lwipc33/arduino_secrets.h       |   2 +
 .../wifi_bare_lwipc33/wifi_bare_lwipc33.ino   | 547 +++++++++++++++++
 6 files changed, 1775 insertions(+)
 create mode 100644 libraries/lwIpWrapper/examples/ethernet_bare_lwipc33/ethernet_bare_lwipc33.ino
 create mode 100644 libraries/lwIpWrapper/examples/mixed_lwipc33/arduino_secrets.h
 create mode 100644 libraries/lwIpWrapper/examples/mixed_lwipc33/mixed_lwipc33.ino
 create mode 100644 libraries/lwIpWrapper/examples/softap_bare_lwipc33/softap_bare_lwipc33.ino
 create mode 100644 libraries/lwIpWrapper/examples/wifi_bare_lwipc33/arduino_secrets.h
 create mode 100644 libraries/lwIpWrapper/examples/wifi_bare_lwipc33/wifi_bare_lwipc33.ino

diff --git a/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33/ethernet_bare_lwipc33.ino b/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33/ethernet_bare_lwipc33.ino
new file mode 100644
index 000000000..521983ce0
--- /dev/null
+++ b/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33/ethernet_bare_lwipc33.ino
@@ -0,0 +1,551 @@
+#include <Arduino.h>
+#include <Arduino_DebugUtils.h>
+#include <IRQManager.h>
+#include <regex>
+#include <utils.h>
+
+// #define CNETIF_STATS_ENABLED
+// #include "CNetifStats.h"
+
+#ifdef CNETIF_STATS_ENABLED
+#define STATS_BUFFER_SIZE 1000
+char cnetif_stats_buffer[STATS_BUFFER_SIZE];
+// netif_stats _stats;
+#endif // CNETIF_STATS_ENABLED
+
+#include <EthernetC33.h>
+#include <lwipClient.h>
+
+#define CHECK_PAYLOAD
+
+/* --------------------------------------- */
+void timer_cb(timer_callback_args_t *arg);
+void application();
+void dump_buffer(uint8_t* b, uint32_t len, uint8_t blocks=4, uint8_t cols=16);
+void dump_buffer_char(uint8_t* b, uint32_t len);
+void application_report(bool force=false);
+bool verify_buffer_sequential_faster_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print= false);
+bool verify_buffer_sequential_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print=false);
+void application_final_report();
+uint64_t debug_start;
+/* --------------------------------------- */
+
+void setup() {
+  Serial.begin(115200);
+  while(!Serial);
+
+  Serial.println("Renesas file download example");
+
+  DEBUG_INFO("Setting up netif");
+  // Ethernet.begin(&ip, &nm, &gw);
+  Ethernet.begin();
+
+  DEBUG_INFO("Begin of reception\n\n");
+  debug_start = millis();
+}
+
+uint32_t counter=0;
+void loop() {
+  // __disable_irq();
+  uint32_t start = micros();
+#ifndef LWIP_USE_TIMER
+  CLwipIf::getInstance().task();
+#endif
+  // Handle application FSM
+  application();
+
+  if(millis() - debug_start > 3000) { // print the debug _stats every x second
+    // DEBUG_INFO("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+    DEBUG_INFO("time: %12ums", millis());
+    // DEBUG_INFO("memory: %12u bytes \tmin: %12u bytes \tmax: %12u bytes",
+    //   memory_used, memory_used_min, memory_used_max);
+    DEBUG_INFO("loop counter %u\n", counter);
+    application_report();
+
+#ifdef CNETIF_STATS_ENABLED
+    netif_stats_sprintf(cnetif_stats_buffer, Ethernet.stats, STATS_BUFFER_SIZE, (8*1e6)/(1<<20), "Mbit/s");
+    // __disable_irq();
+    arduino::lock();
+    NETIF_STATS_RESET_AVERAGES(Ethernet.stats);
+    // __enable_irq();
+    arduino::unlock();
+
+    DEBUG_INFO(cnetif_stats_buffer);
+#endif // CNETIF_STATS_ENABLED
+    // DEBUG_INFO("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
+
+    counter = 0;
+    // reset some counters
+    debug_start = millis();
+  }
+  counter++;
+}
+
+// Application level Stuff
+enum app_state_t: uint8_t {
+  APP_STATE_NONE = 0,
+  APP_STATE_LINK_UP,
+  APP_STATE_LINK_DOWN,
+  APP_STATE_IFACE_UP,
+  APP_STATE_CONNECTING,
+  APP_STATE_CONNECTED,
+  APP_STATE_PARSE_HEADER,
+  APP_STATE_DOWNLOAD,
+  APP_STATE_DOWNLOAD_FAILED,
+  APP_STATE_DOWNLOAD_FINISHED,
+  APP_STATE_ERROR,
+  APP_STATE_RESET
+};
+
+static const char* state_strings[] = {
+  "APP_STATE_NONE",
+  "APP_STATE_LINK_UP",
+  "APP_STATE_LINK_DOWN",
+  "APP_STATE_IFACE_UP",
+  "APP_STATE_CONNECTING",
+  "APP_STATE_CONNECTED",
+  "APP_STATE_PARSE_HEADER",
+  "APP_STATE_DOWNLOAD",
+  "APP_STATE_DOWNLOAD_FAILED",
+  "APP_STATE_DOWNLOAD_FINISHED",
+  "APP_STATE_ERROR",
+  "APP_STATE_RESET"
+};
+
+#define APP_BUFFER_SIZE 1*1024
+
+
+struct App {
+  app_state_t current_state=APP_STATE_NONE;
+  app_state_t prev_state=APP_STATE_NONE;
+
+  lwipClient *tcp_client;
+  uint16_t port = 8000;
+  IPAddress server_ip = IPAddress(192, 168, 10, 250);
+
+  uint8_t buffer[APP_BUFFER_SIZE];
+
+  size_t file_length=0;
+  size_t downloaded_bytes=0;
+  std::string http_header;
+
+  // stats related variables
+  uint32_t start = 0;
+  uint32_t speed_start = 0;
+  uint32_t speed_bytes = 0;
+
+  // payload verification parameters
+  uint32_t payload_verify_offset=0;
+  uint8_t payload_verify_excess[4]={}; // this should be 3, but there are bugs
+  uint8_t payload_verify_excess_len=0;
+  uint32_t last_value=0;
+} app;
+
+void init_app(struct App& app) {
+  app.file_length = 0;
+  app.http_header = "";
+  app.downloaded_bytes = 0;
+  app.start = 0;
+  app.payload_verify_excess_len = 0;
+  app.payload_verify_offset = 0;
+  app.last_value=0;
+  app.speed_bytes = 0;
+}
+
+void reset_app(struct App& app) {
+  init_app(app);
+
+  if(app.tcp_client != nullptr) {
+    app.tcp_client->stop();
+    // delete app.tcp_client;
+  }
+}
+
+const char* http_request = "GET /test-4M HTTP/1.1\nHost: 192.168.10.250\nConnection: close\n\n";
+
+void application() {
+  bool found = false;
+  uint16_t bytes_read=0;
+
+  switch(app.current_state) {
+  case APP_STATE_NONE:
+    init_app(app);
+
+    // TODO we are not handling link connection and disconnection
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_LINK_UP;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_LINK_UP:
+    if(Ethernet.isDhcpAcquired()) {
+      app.prev_state = app.current_state;
+      app.current_state = APP_STATE_IFACE_UP;
+      DEBUG_INFO("State changed: to %s, from %s",
+        state_strings[app.current_state],
+        state_strings[app.prev_state]);
+    }
+    break;
+  case APP_STATE_IFACE_UP:
+    // The link is up we connect to the server
+    app.tcp_client = new lwipClient;
+
+    // Connection details:
+    app.tcp_client->connect(app.server_ip, app.port);
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_CONNECTING;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_CONNECTING:
+    // do nothing, until the TCP connection is established
+    // TODO handle timeout for connection and go to error state
+    if(app.tcp_client->connected()) {
+      app.prev_state = app.current_state;
+      app.current_state = APP_STATE_CONNECTED;
+      DEBUG_INFO("State changed: to %s, from %s",
+        state_strings[app.current_state],
+        state_strings[app.prev_state]);
+    }
+
+    break;
+
+  case APP_STATE_CONNECTED:
+    app.tcp_client->write((uint8_t*)http_request, strlen(http_request));
+    app.start = millis();
+    app.speed_start = app.start;
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_PARSE_HEADER;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_PARSE_HEADER:
+    // FIXME
+    bytes_read = app.tcp_client->read_until_token(app.buffer, APP_BUFFER_SIZE, "\r\n\r\n", found);
+    // DEBUG_INFO("%s", app.http_header.c_str());
+
+    if(bytes_read>0) {
+      // put the buffer into an http header string
+      std::string chunk((char*)app.buffer, bytes_read);
+      app.http_header += chunk;
+      app.speed_bytes += bytes_read;
+      DEBUG_INFO("%s", app.http_header.c_str());
+    }
+
+    if(found) { // FIXME reduce indentation level
+      // we found the http terminating token, go to the next app phase if we extracted the file len
+      // otherwise go in error phase
+
+      // Parse the http header and gather information needed for the download
+      // dump_buffer_char(app.buffer, APP_BUFFER_SIZE);
+
+      std::regex content_length_regex("Content-Length: ([0-9]+)", std::regex::icase);
+      std::smatch matches;
+
+      // DEBUG_INFO(app.http_header.c_str());
+
+      if(std::regex_search(app.http_header, matches, content_length_regex)) {
+        app.file_length = stoi(matches[1].str());
+
+        DEBUG_INFO("Download started, file length: %u", app.file_length);
+
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      } else {
+        // Failed to extract the content length from the header, going into an error state
+        // TODO report the reason of the error
+
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_ERROR;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      }
+    }
+    break;
+  case APP_STATE_DOWNLOAD:
+    if(app.tcp_client->available() <= 0) { // no data available
+      break;
+    }
+
+    bytes_read = app.tcp_client->read(app.buffer, APP_BUFFER_SIZE);
+    // DEBUG_INFO("read %6u, available %6u", bytes_read, app.tcp_client->available());
+
+    if(bytes_read > 0) {
+      app.downloaded_bytes += bytes_read;
+      app.speed_bytes += bytes_read;
+
+      // dump_buffer(app.buffer, APP_BUFFER_SIZE, 4, 128);
+#ifdef CHECK_PAYLOAD
+      // if(!verify_buffer_sequential_4B(
+      if(!verify_buffer_sequential_faster_4B(
+        app.buffer,
+        bytes_read,
+        app.payload_verify_offset,
+        app.payload_verify_excess,
+        app.payload_verify_excess_len,
+        false)) {
+
+        DEBUG_INFO("Payload verification failed");
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD_FAILED;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      }
+#endif // CHECK_PAYLOAD
+    }
+
+    // test for faking download failed
+    // if(app.downloaded_bytes > app.file_length/6) {
+    //     app.prev_state = app.current_state;
+    //     app.current_state = APP_STATE_DOWNLOAD_FAILED;
+    // }
+
+    if(app.downloaded_bytes == app.file_length) {
+      app.last_value =
+        *(app.buffer + bytes_read - 4) << 24 |
+        *(app.buffer + bytes_read - 3) << 16 |
+        *(app.buffer + bytes_read - 2) << 8  |
+        *(app.buffer + bytes_read - 1);
+
+      // if the download of the counter file is correct the last value should be
+      // the size of the file/4 -1
+      if(app.last_value == (app.downloaded_bytes/4 - 1)) {
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD_FINISHED;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      } else {
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD_FAILED;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      }
+    }
+    break;
+
+  case APP_STATE_DOWNLOAD_FAILED:
+    // TODO report error in file download and close the connection
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_ERROR;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_DOWNLOAD_FINISHED:
+    DEBUG_INFO("Download finished: %uMB", app.downloaded_bytes>>20);
+    DEBUG_INFO("Last value in the buffer: 0x%08X", app.last_value);
+    application_final_report();
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_RESET;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_ERROR:
+    // The app reached an expected error state
+    // TODO report this state and go in the default, status not defined handler to reset the state
+  case APP_STATE_RESET:
+    // in this state we reset the application and we start back from the beginning
+
+    reset_app(app);
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_IFACE_UP;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+  }
+}
+
+// application stats
+void application_report(bool force) {
+  if(force || app.current_state == APP_STATE_PARSE_HEADER || app.current_state == APP_STATE_DOWNLOAD) {
+
+    // float speed_conversion_factor = 1e3/(1<<10);
+    float speed_conversion_factor = 8*1e3/float(1<<20);
+    float elapsed = millis()-app.speed_start;
+
+    float speed = (app.speed_bytes / elapsed) * speed_conversion_factor;
+    DEBUG_INFO("Application layer: %12u/%12u speed: %.2f Mbit/s", app.downloaded_bytes, app.file_length, speed);
+
+    app.speed_start = millis();
+    app.speed_bytes = 0;
+  }
+}
+
+void application_final_report() {
+  // float speed_conversion_factor = 10e3/(1<<10);
+  float speed_conversion_factor = 1e3*8/float(1<<20);
+
+  float elapsed = millis()-app.start;
+  float speed = (app.downloaded_bytes / elapsed) * speed_conversion_factor;
+  DEBUG_INFO(
+    "Application layer: Downloaded %u MB in %.2fs average speed: %.2f Mbit/s",
+    app.downloaded_bytes>>20, elapsed/1000, speed);
+}
+
+// payload checking function
+bool verify_buffer_sequential_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print) {
+  size_t i=0;
+  bool res = true;
+  uint32_t value=0, first=0;
+
+  if(excess_len > 0) {
+    uint8_t j=0;
+    for(; j<excess_len; j++) {
+      value |= excess[j] << ((3-j)*8);
+    }
+
+    for(; j<4 && i<len; j++,i++) {
+      value |= buffer[i] << ((3-j)*8);
+
+      if(excess_len < 3) {
+        excess[j] = buffer[i];
+        excess_len++;
+      }
+    }
+
+    if(value != offset) {
+      DEBUG_INFO("perror %08X, %08X", value, offset);
+
+      res = false;
+    }
+    offset++;
+    first = value;
+  }
+
+  for(; i+4<=len; i+=4,offset++) {
+    // convert buffer from big endian bytearray to uint32
+    value =
+      *(buffer+i)   << 24 |
+      *(buffer+i+1) << 16 |
+      *(buffer+i+2) << 8  |
+      *(buffer+i+3);
+
+    if(first == 0) {
+      first = value;
+    }
+    // if(print) {
+    //   DEBUG_INFO("value: %X", value);
+    // }
+
+    if(value != offset && res) {
+      DEBUG_INFO("error %8X, %8X", value, offset);
+
+      res = false;
+    }
+  }
+
+  // put the bytes that exceed the modulo4 in the excess array
+  excess_len = len - i;
+  for(uint8_t j=0; i<len; j++,i++){
+    excess[j] = buffer[i];
+  }
+
+  if(print) {
+    DEBUG_INFO("packet First: %08X LAST %08X", first, value);
+  }
+
+
+  return res;
+}
+
+bool verify_buffer_sequential_faster_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print) {
+  size_t i=0;
+  bool res = true;
+  uint32_t first=0;
+
+  if(excess_len > 0) {
+    // the first value needs to be taken from the excess bytes of the previous buffer and the first of this
+    uint8_t j=0;
+    for(; j<excess_len; j++) {
+      first |= excess[j] << ((3-j)*8);
+    }
+
+    for(; j<4 && i<len; j++,i++) {
+      first |= buffer[i] << ((3-j)*8);
+    }
+  } else {
+    // the first value needs to be taken from the current buffer
+    for(; i<4; i++) {
+      first |= buffer[i] << ((3-i)*8);
+    }
+  }
+
+  // DEBUG_INFO("verify: found %08X, expected %08X, i %1u len %8u, excess_len %1u", first, offset, i, len, excess_len);
+  if(first != offset) {
+    DEBUG_INFO("perror: found %08X, expected %08X", first, offset);
+
+    res = false;
+  }
+  // offset++;
+
+  // After reconstructing the first integer, we can skip the verification of the rest of the payload,
+  // assuming that the issues are always caused by a missing section between buffers.
+  // This means that we only need to verify the first value, and update the value for offset
+
+  // The len of the returned excess is the following:
+  uint8_t new_excess_len = (len+excess_len) % 4;
+  i = len - new_excess_len;
+  offset = offset + (i+excess_len)/4;
+
+  // collect the excess for the next buffer
+  for(uint8_t j=0; i<len; j++,i++){
+    excess[j] = buffer[i];
+  }
+
+  excess_len = new_excess_len;
+
+  return res;
+}
+
+// Utility functions
+void dump_buffer(uint8_t* b, uint32_t len, uint8_t blocks, uint8_t cols) {
+
+  // TODO make sure blocks is less that cols
+  Serial.println("BUFFER >>>>>>>");
+  for(uint8_t *p=b; p<b+len; p++) {
+    if(p == nullptr) {
+      break;
+    }
+    if(*p < 0x10) {
+      Serial.print(0);
+    }
+    Serial.print(*p,  HEX);
+
+    if(cols != 0 && ((p-b)+1) % blocks == 0 && ((p-b)+1) % cols != 0){
+      Serial.print(" ");
+    }
+    if(cols != 0 && ((p-b)+1) % cols == 0){
+      Serial.println();
+    }
+  }
+  Serial.println("\nBUFFER <<<<<<<");
+}
+
+void dump_buffer_char(uint8_t* b, uint32_t len) {
+  Serial.println("BUFFER_CHAR >>>>>>>");
+  for(uint8_t *p=b; p<b+len; p++) {
+    Serial.print((char)*p);
+  }
+  Serial.println("\nBUFFER_CHAR <<<<<<<");
+}
\ No newline at end of file
diff --git a/libraries/lwIpWrapper/examples/mixed_lwipc33/arduino_secrets.h b/libraries/lwIpWrapper/examples/mixed_lwipc33/arduino_secrets.h
new file mode 100644
index 000000000..0c9fdd556
--- /dev/null
+++ b/libraries/lwIpWrapper/examples/mixed_lwipc33/arduino_secrets.h
@@ -0,0 +1,2 @@
+#define SECRET_SSID ""
+#define SECRET_PASS ""
diff --git a/libraries/lwIpWrapper/examples/mixed_lwipc33/mixed_lwipc33.ino b/libraries/lwIpWrapper/examples/mixed_lwipc33/mixed_lwipc33.ino
new file mode 100644
index 000000000..da41c324d
--- /dev/null
+++ b/libraries/lwIpWrapper/examples/mixed_lwipc33/mixed_lwipc33.ino
@@ -0,0 +1,565 @@
+#include <Arduino.h>
+#include <Arduino_DebugUtils.h>
+#include <regex>
+#include <utils.h>
+#include "arduino_secrets.h"
+#include <WiFiC3.h>
+#include <EthernetC33.h>
+
+// #define CNETIF_STATS_ENABLED
+// #include "CNetifStats.h"
+
+#ifdef CNETIF_STATS_ENABLED
+#define STATS_BUFFER_SIZE 1000
+char cnetif_stats_buffer[STATS_BUFFER_SIZE];
+// netif_stats _stats;
+#endif // CNETIF_STATS_ENABLED
+
+static char const SSID[] = SECRET_SSID;  /* your network SSID (name) */
+static char const PASS[] = SECRET_PASS;  /* your network password (use for WPA, or use as key for WEP) */
+
+// #define ETHER_CFG_PARAM_CHECKING_ENABLE
+// Renesas libraries
+
+#define CHECK_PAYLOAD
+
+/* --------------------------------------- */
+void timer_cb(timer_callback_args_t *arg);
+void application();
+void dump_buffer(uint8_t* b, uint32_t len, uint8_t blocks=4, uint8_t cols=16);
+void dump_buffer_char(uint8_t* b, uint32_t len);
+void application_report(bool force=false);
+bool verify_buffer_sequential_faster_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print= false);
+bool verify_buffer_sequential_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print=false);
+void application_final_report();
+uint64_t debug_start;
+/* --------------------------------------- */
+
+void setup() {
+  Serial.begin(115200);
+  while(!Serial);
+
+  Serial.println("Renesas file download example");
+  int res = 0;
+
+  DEBUG_INFO("Setting up Eth netif");
+  Ethernet.begin();
+
+  DEBUG_INFO("Connecting to AP");
+  while((res=WiFi.begin(SSID, SECRET_PASS)) != ESP_CONTROL_OK) {
+    DEBUG_INFO("Connection failed retry: %d", res);
+    delay(1000);
+  }
+  DEBUG_INFO("Connected to AP");
+
+  DEBUG_INFO("Begin of reception\n\n");
+  debug_start = millis();
+}
+
+uint32_t counter=0;
+void loop() {
+  // __disable_irq();
+  uint32_t start = micros();
+#ifndef LWIP_USE_TIMER
+  CLwipIf::getInstance().task();
+#endif
+  // Handle application FSM
+  application();
+
+  if(millis() - debug_start > 3000) { // print the debug _stats every x second
+    // DEBUG_INFO("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+    DEBUG_INFO("time: %12ums", millis());
+    // DEBUG_INFO("memory: %12u bytes \tmin: %12u bytes \tmax: %12u bytes",
+    //   memory_used, memory_used_min, memory_used_max);
+    DEBUG_INFO("loop counter %u\n", counter);
+    application_report();
+
+
+#ifdef CNETIF_STATS_ENABLED
+    netif_stats_sprintf(cnetif_stats_buffer, WiFi.stats, STATS_BUFFER_SIZE, (8*1e6)/(1<<20), "Mbit/s");
+    // __disable_irq();
+    arduino::lock();
+    NETIF_STATS_RESET_AVERAGES(WiFi.stats);
+    // __enable_irq();
+    arduino::unlock();
+
+    DEBUG_INFO(cnetif_stats_buffer);
+#endif // CNETIF_STATS_ENABLED
+    // DEBUG_INFO("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
+
+    counter = 0;
+    // reset some counters
+    debug_start = millis();
+  }
+  counter++;
+}
+
+// Application level Stuff
+enum app_state_t: uint8_t {
+  APP_STATE_NONE = 0,
+  APP_STATE_LINK_UP,
+  APP_STATE_LINK_DOWN,
+  APP_STATE_IFACE_UP,
+  APP_STATE_CONNECTING,
+  APP_STATE_CONNECTED,
+  APP_STATE_PARSE_HEADER,
+  APP_STATE_DOWNLOAD,
+  APP_STATE_DOWNLOAD_FAILED,
+  APP_STATE_DOWNLOAD_FINISHED,
+  APP_STATE_ERROR,
+  APP_STATE_RESET
+};
+
+static const char* state_strings[] = {
+  "APP_STATE_NONE",
+  "APP_STATE_LINK_UP",
+  "APP_STATE_LINK_DOWN",
+  "APP_STATE_IFACE_UP",
+  "APP_STATE_CONNECTING",
+  "APP_STATE_CONNECTED",
+  "APP_STATE_PARSE_HEADER",
+  "APP_STATE_DOWNLOAD",
+  "APP_STATE_DOWNLOAD_FAILED",
+  "APP_STATE_DOWNLOAD_FINISHED",
+  "APP_STATE_ERROR",
+  "APP_STATE_RESET"
+};
+
+#define APP_BUFFER_SIZE 1*1024
+
+
+struct App {
+  app_state_t current_state=APP_STATE_NONE;
+  app_state_t prev_state=APP_STATE_NONE;
+
+  uint8_t clients_counter=0;
+  lwipClient *tcp_client;
+  uint16_t port = 8000;
+  IPAddress server_ip = IPAddress(192, 168, 10, 250);
+
+  uint8_t buffer[APP_BUFFER_SIZE];
+
+  size_t file_length=0;
+  size_t downloaded_bytes=0;
+  std::string http_header;
+
+  // stats related variables
+  uint32_t start = 0;
+  uint32_t speed_start = 0;
+  uint32_t speed_bytes = 0;
+
+  // payload verification parameters
+  uint32_t payload_verify_offset=0;
+  uint8_t payload_verify_excess[4]={}; // this should be 3, but there are bugs
+  uint8_t payload_verify_excess_len=0;
+  uint32_t last_value=0;
+} app;
+
+void init_app(struct App& app) {
+  app.file_length = 0;
+  app.http_header = "";
+  app.downloaded_bytes = 0;
+  app.start = 0;
+  app.payload_verify_excess_len = 0;
+  app.payload_verify_offset = 0;
+  app.last_value=0;
+  app.speed_bytes = 0;
+}
+
+void reset_app(struct App& app) {
+  init_app(app);
+
+  if(app.tcp_client != nullptr) {
+    delete app.tcp_client;
+  }
+}
+
+const char* http_request = "GET /test-1M HTTP/1.1\nHost: 192.168.10.250\nConnection: close\n\n";
+
+void application() {
+  bool found = false;
+  uint16_t bytes_read=0;
+
+  switch(app.current_state) {
+  case APP_STATE_NONE:
+    init_app(app);
+
+    // TODO we are not handling link connection and disconnection
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_LINK_UP;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_LINK_UP:
+    if(WiFiStation.isDhcpAcquired() && Ethernet.isDhcpAcquired()) {
+      app.prev_state = app.current_state;
+      app.current_state = APP_STATE_IFACE_UP;
+      DEBUG_INFO("State changed: to %s, from %s",
+        state_strings[app.current_state],
+        state_strings[app.prev_state]);
+    }
+    break;
+  case APP_STATE_IFACE_UP:
+    // The link is up we connect to the server
+    // app.tcp_client = new lwipClient;
+
+    if((app.clients_counter++) % 2 == 1) {
+      DEBUG_INFO("Download with Eth interface");
+      app.tcp_client = new EthernetClient;
+    } else {
+      DEBUG_INFO("Download with WiFi interface");
+      app.tcp_client = new WiFiClient;
+    }
+    // CLwipIf::getInstance().setDefaultIface((app.clients_counter++)%2==1? (CNetIf*)&Ethernet: (CNetIf*)&WiFiStation);
+
+
+    // Connection details:
+    app.tcp_client->connect(app.server_ip, app.port);
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_CONNECTING;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_CONNECTING:
+    // do nothing, until the TCP connection is established
+    // TODO handle timeout for connection and go to error state
+    if(app.tcp_client->connected()) {
+      app.prev_state = app.current_state;
+      app.current_state = APP_STATE_CONNECTED;
+      DEBUG_INFO("State changed: to %s, from %s",
+        state_strings[app.current_state],
+        state_strings[app.prev_state]);
+    }
+
+    break;
+
+  case APP_STATE_CONNECTED:
+    app.tcp_client->write((uint8_t*)http_request, strlen(http_request));
+    app.start = millis();
+    app.speed_start = app.start;
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_PARSE_HEADER;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_PARSE_HEADER:
+    // FIXME
+    bytes_read = app.tcp_client->read_until_token(app.buffer, APP_BUFFER_SIZE, "\r\n\r\n", found);
+    // DEBUG_INFO("%s", app.http_header.c_str());
+
+    if(bytes_read>0) {
+      // put the buffer into an http header string
+      std::string chunk((char*)app.buffer, bytes_read);
+      app.http_header += chunk;
+      app.speed_bytes += bytes_read;
+      DEBUG_INFO("%s", app.http_header.c_str());
+    }
+
+    if(found) { // FIXME reduce indentation level
+      // we found the http terminating token, go to the next app phase if we extracted the file len
+      // otherwise go in error phase
+
+      // Parse the http header and gather information needed for the download
+      // dump_buffer_char(app.buffer, APP_BUFFER_SIZE);
+
+      std::regex content_length_regex("Content-Length: ([0-9]+)", std::regex::icase);
+      std::smatch matches;
+
+      // DEBUG_INFO(app.http_header.c_str());
+
+      if(std::regex_search(app.http_header, matches, content_length_regex)) {
+        app.file_length = stoi(matches[1].str());
+
+        DEBUG_INFO("Download started, file length: %u", app.file_length);
+
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      } else {
+        // Failed to extract the content length from the header, going into an error state
+        // TODO report the reason of the error
+
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_ERROR;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      }
+    }
+    break;
+  case APP_STATE_DOWNLOAD:
+    if(app.tcp_client->available() <= 0) { // no data available
+      break;
+    }
+    // DEBUG_INFO("reading: tot_len %6u, offset %6u", app.tcp_client->p->tot_len, app.tcp_client->pbuf_offset);
+    bytes_read = app.tcp_client->read(app.buffer, APP_BUFFER_SIZE);
+    // DEBUG_INFO("read %6u", bytes_read);
+
+    if(bytes_read > 0) {
+      app.downloaded_bytes += bytes_read;
+      app.speed_bytes += bytes_read;
+
+      // dump_buffer(app.buffer, APP_BUFFER_SIZE, 4, 128);
+#ifdef CHECK_PAYLOAD
+      // if(!verify_buffer_sequential_4B(
+      if(!verify_buffer_sequential_faster_4B(
+        app.buffer,
+        bytes_read,
+        app.payload_verify_offset,
+        app.payload_verify_excess,
+        app.payload_verify_excess_len,
+        false)) {
+
+        DEBUG_INFO("Payload verification failed");
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD_FAILED;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      }
+#endif // CHECK_PAYLOAD
+    }
+
+    if(app.downloaded_bytes == app.file_length) {
+      app.last_value =
+        *(app.buffer + bytes_read - 4) << 24 |
+        *(app.buffer + bytes_read - 3) << 16 |
+        *(app.buffer + bytes_read - 2) << 8  |
+        *(app.buffer + bytes_read - 1);
+
+      // if the download of the counter file is correct the last value should be
+      // the size of the file/4 -1
+      if(app.last_value == (app.downloaded_bytes/4 - 1)) {
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD_FINISHED;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      } else {
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD_FAILED;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      }
+    }
+    break;
+
+  case APP_STATE_DOWNLOAD_FAILED:
+    // TODO report error in file download and close the connection
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_ERROR;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_DOWNLOAD_FINISHED:
+    DEBUG_INFO("Download finished: %uMB", app.downloaded_bytes>>20);
+    DEBUG_INFO("Last value in the buffer: 0x%08X", app.last_value);
+    application_final_report();
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_RESET;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_ERROR:
+    // The app reached an expected error state
+    // TODO report this state and go in the default, status not defined handler to reset the state
+  case APP_STATE_RESET:
+    // in this state we reset the application and we start back from the beginning
+
+    reset_app(app);
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_IFACE_UP;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+  }
+}
+
+// application stats
+void application_report(bool force) {
+  if(force || app.current_state == APP_STATE_PARSE_HEADER || app.current_state == APP_STATE_DOWNLOAD) {
+
+    // float speed_conversion_factor = 1e3/(1<<10);
+    float speed_conversion_factor = 8*1e3/float(1<<20);
+    float elapsed = millis()-app.speed_start;
+
+    float speed = (app.speed_bytes / elapsed) * speed_conversion_factor;
+    DEBUG_INFO("Application layer: %12u/%12u speed: %.2f Mbit/s", app.downloaded_bytes, app.file_length, speed);
+
+    app.speed_start = millis();
+    app.speed_bytes = 0;
+  }
+}
+
+void application_final_report() {
+  // float speed_conversion_factor = 10e3/(1<<10);
+  float speed_conversion_factor = 1e3*8/float(1<<20);
+
+  float elapsed = millis()-app.start;
+  float speed = (app.downloaded_bytes / elapsed) * speed_conversion_factor;
+  DEBUG_INFO(
+    "Application layer: Downloaded %u MB in %.2fs average speed: %.2f Mbit/s",
+    app.downloaded_bytes>>20, elapsed/1000, speed);
+}
+
+// payload checking function
+bool verify_buffer_sequential_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print) {
+  size_t i=0;
+  bool res = true;
+  uint32_t value=0, first=0;
+
+  if(excess_len > 0) {
+    uint8_t j=0;
+    for(; j<excess_len; j++) {
+      value |= excess[j] << ((3-j)*8);
+    }
+
+    for(; j<4 && i<len; j++,i++) {
+      value |= buffer[i] << ((3-j)*8);
+
+      if(excess_len < 3) {
+        excess[j] = buffer[i];
+        excess_len++;
+      }
+    }
+
+    if(value != offset) {
+      DEBUG_INFO("perror %08X, %08X", value, offset);
+
+      res = false;
+    }
+    offset++;
+    first = value;
+  }
+
+  for(; i+4<=len; i+=4,offset++) {
+    // convert buffer from big endian bytearray to uint32
+    value =
+      *(buffer+i)   << 24 |
+      *(buffer+i+1) << 16 |
+      *(buffer+i+2) << 8  |
+      *(buffer+i+3);
+
+    if(first == 0) {
+      first = value;
+    }
+    // if(print) {
+    //   DEBUG_INFO("value: %X", value);
+    // }
+
+    if(value != offset && res) {
+      DEBUG_INFO("error %8X, %8X", value, offset);
+
+      res = false;
+    }
+  }
+
+  // put the bytes that exceed the modulo4 in the excess array
+  excess_len = len - i;
+  for(uint8_t j=0; i<len; j++,i++){
+    excess[j] = buffer[i];
+  }
+
+  if(print) {
+    DEBUG_INFO("packet First: %08X LAST %08X", first, value);
+  }
+
+
+  return res;
+}
+
+bool verify_buffer_sequential_faster_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print) {
+  size_t i=0;
+  bool res = true;
+  uint32_t first=0;
+
+  if(excess_len > 0) {
+    // the first value needs to be taken from the excess bytes of the previous buffer and the first of this
+    uint8_t j=0;
+    for(; j<excess_len; j++) {
+      first |= excess[j] << ((3-j)*8);
+    }
+
+    for(; j<4 && i<len; j++,i++) {
+      first |= buffer[i] << ((3-j)*8);
+    }
+  } else {
+    // the first value needs to be taken from the current buffer
+    for(; i<4; i++) {
+      first |= buffer[i] << ((3-i)*8);
+    }
+  }
+
+  // DEBUG_INFO("verify: found %08X, expected %08X, i %1u len %8u, excess_len %1u", first, offset, i, len, excess_len);
+  if(first != offset) {
+    DEBUG_INFO("perror: found %08X, expected %08X", first, offset);
+
+    res = false;
+  }
+  // offset++;
+
+  // After reconstructing the first integer, we can skip the verification of the rest of the payload,
+  // assuming that the issues are always caused by a missing section between buffers.
+  // This means that we only need to verify the first value, and update the value for offset
+
+  // The len of the returned excess is the following:
+  uint8_t new_excess_len = (len+excess_len) % 4;
+  i = len - new_excess_len;
+  offset = offset + (i+excess_len)/4;
+
+  // collect the excess for the next buffer
+  for(uint8_t j=0; i<len; j++,i++){
+    excess[j] = buffer[i];
+  }
+
+  excess_len = new_excess_len;
+
+  return res;
+}
+
+// Utility functions
+void dump_buffer(uint8_t* b, uint32_t len, uint8_t blocks, uint8_t cols) {
+
+  // TODO make sure blocks is less that cols
+  Serial.println("BUFFER >>>>>>>");
+  for(uint8_t *p=b; p<b+len; p++) {
+    if(*p < 0x10) {
+      Serial.print(0);
+    }
+    Serial.print(*p,  HEX);
+
+    if(cols != 0 && ((p-b)+1) % blocks == 0 && ((p-b)+1) % cols != 0){
+      Serial.print(" ");
+    }
+    if(cols != 0 && ((p-b)+1) % cols == 0){
+      Serial.println();
+    }
+  }
+  Serial.println("\nBUFFER <<<<<<<");
+}
+
+void dump_buffer_char(uint8_t* b, uint32_t len) {
+  Serial.println("BUFFER_CHAR >>>>>>>");
+  for(uint8_t *p=b; p<b+len; p++) {
+    Serial.print((char)*p);
+  }
+  Serial.println("\nBUFFER_CHAR <<<<<<<");
+}
diff --git a/libraries/lwIpWrapper/examples/softap_bare_lwipc33/softap_bare_lwipc33.ino b/libraries/lwIpWrapper/examples/softap_bare_lwipc33/softap_bare_lwipc33.ino
new file mode 100644
index 000000000..ec79a8051
--- /dev/null
+++ b/libraries/lwIpWrapper/examples/softap_bare_lwipc33/softap_bare_lwipc33.ino
@@ -0,0 +1,108 @@
+#include <Arduino.h>
+#include <Arduino_DebugUtils.h>
+#include <regex>
+#include <utils.h>
+
+// #define CNETIF_STATS_ENABLED
+// #include "CNetifStats.h"
+
+#ifdef CNETIF_STATS_ENABLED
+#define STATS_BUFFER_SIZE 1000
+char cnetif_stats_buffer[STATS_BUFFER_SIZE];
+// netif_stats _stats;
+#endif // CNETIF_STATS_ENABLED
+
+// #define ETHER_CFG_PARAM_CHECKING_ENABLE
+// Renesas libraries
+
+#include "IPAddress.h"
+
+// IPAddress default_ip("192.168.10.130");
+// IPAddress default_nm("255.255.255.0");
+// IPAddress default_gw("192.168.10.1");
+ip_addr_t ip;
+ip_addr_t nm;
+ip_addr_t gw;
+
+// FspTimer timer;
+
+#define CHECK_PAYLOAD
+
+#define LOOP_MIN_DURATION 100 // us
+
+/* --------------------------------------- */
+void timer_cb(timer_callback_args_t *arg);
+void application();
+void dump_buffer(uint8_t* b, uint32_t len, uint8_t blocks=4, uint8_t cols=16);
+void dump_buffer_char(uint8_t* b, uint32_t len);
+void application_report(bool force=false);
+bool verify_buffer_sequential_faster_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print= false);
+bool verify_buffer_sequential_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print=false);
+void application_final_report();
+uint64_t debug_start;
+/* --------------------------------------- */
+
+SoftAPLWIPNetworkInterface C33WifiIface;
+
+void setup() {
+  Serial.begin(115200);
+  while(!Serial);
+
+  Serial.println("Renesas file download example");
+  // lwip setup
+  lwip_init(); // TODO move this inside the network stack init
+  LWIPNetworkStack::getInstance(); // TODO make this automatic
+
+  DEBUG_INFO("Setting up netif");
+  auto res = C33WifiIface.begin();
+  DEBUG_INFO("%d", res);
+
+  DEBUG_INFO("Starting AP");
+  while((res=C33WifiIface.startSoftAp("testAP", "123456789", 5)) != ESP_CONTROL_OK) {
+    DEBUG_INFO("Connection failed retry: %d", res);
+    delay(1000);
+  }
+  DEBUG_INFO("AP started");
+
+  // setup netif
+  // IP_ADDR4(&ip, 192, 168, 10, 130);
+  // IP_ADDR4(&nm, 255, 255, 255, 0);
+  // IP_ADDR4(&gw, 192, 168, 10, 1);
+
+  DEBUG_INFO("Begin of reception\n\n");
+  debug_start = millis();
+}
+
+uint32_t counter =0;
+void loop() {
+
+  uint32_t start = micros();
+#ifndef NETWORKSTACK_USE_TIMER
+  LWIPNetworkStack::getInstance().task();
+#endif
+  if(millis() - debug_start > 3000) { // print the debug _stats every x second
+    // DEBUG_INFO("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+    DEBUG_INFO("time: %12ums", millis());
+    // DEBUG_INFO("memory: %12u bytes \tmin: %12u bytes \tmax: %12u bytes",
+    //   memory_used, memory_used_min, memory_used_max);
+    DEBUG_INFO("loop counter %u\n", counter);\
+
+#ifdef CNETIF_STATS_ENABLED
+    netif_stats_sprintf(cnetif_stats_buffer, C33WifiIface.stats, STATS_BUFFER_SIZE, (8*1e6)/(1<<20), "Mbit/s");
+    // __disable_irq();
+    arduino::lock();
+    NETIF_STATS_RESET_AVERAGES(C33WifiIface.stats);
+    // __enable_irq();
+    arduino::unlock();
+
+    DEBUG_INFO(cnetif_stats_buffer);
+#endif // CNETIF_STATS_ENABLED
+    // DEBUG_INFO("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
+
+    counter = 0;
+    // reset some counters
+    debug_start = millis();
+  }
+
+  counter++;
+}
\ No newline at end of file
diff --git a/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/arduino_secrets.h b/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/arduino_secrets.h
new file mode 100644
index 000000000..0c9fdd556
--- /dev/null
+++ b/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/arduino_secrets.h
@@ -0,0 +1,2 @@
+#define SECRET_SSID ""
+#define SECRET_PASS ""
diff --git a/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/wifi_bare_lwipc33.ino b/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/wifi_bare_lwipc33.ino
new file mode 100644
index 000000000..071f4eec3
--- /dev/null
+++ b/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/wifi_bare_lwipc33.ino
@@ -0,0 +1,547 @@
+#include <Arduino.h>
+#include <Arduino_DebugUtils.h>
+#include <regex>
+#include <utils.h>
+#include "arduino_secrets.h"
+#include <WiFiC3.h>
+
+// #define CNETIF_STATS_ENABLED
+// #include "CNetifStats.h"
+
+#ifdef CNETIF_STATS_ENABLED
+#define STATS_BUFFER_SIZE 1000
+char cnetif_stats_buffer[STATS_BUFFER_SIZE];
+// netif_stats _stats;
+#endif // CNETIF_STATS_ENABLED
+
+static char const SSID[] = SECRET_SSID;  /* your network SSID (name) */
+static char const PASS[] = SECRET_PASS;  /* your network password (use for WPA, or use as key for WEP) */
+
+#define CHECK_PAYLOAD
+
+/* --------------------------------------- */
+void timer_cb(timer_callback_args_t *arg);
+void application();
+void dump_buffer(uint8_t* b, uint32_t len, uint8_t blocks=4, uint8_t cols=16);
+void dump_buffer_char(uint8_t* b, uint32_t len);
+void application_report(bool force=false);
+bool verify_buffer_sequential_faster_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print= false);
+bool verify_buffer_sequential_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print=false);
+void application_final_report();
+uint64_t debug_start;
+/* --------------------------------------- */
+
+void setup() {
+  Serial.begin(115200);
+  while(!Serial);
+
+  Serial.println("Renesas file download example");
+
+  int res = 0;
+  DEBUG_INFO("Connecting to AP");
+  while((res=WiFi.begin(SSID, SECRET_PASS)) != 1) {
+    DEBUG_INFO("Connection failed retry: %d", res);
+    delay(1000);
+  }
+  DEBUG_INFO("Connected to AP");
+
+  DEBUG_INFO("Begin of reception\n\n");
+  debug_start = millis();
+}
+
+uint32_t counter=0;
+void loop() {
+  // __disable_irq();
+  uint32_t start = micros();
+#ifndef LWIP_USE_TIMER
+  CLwipIf::getInstance().task();
+#endif
+  // Handle application FSM
+  application();
+
+  if(millis() - debug_start > 3000) { // print the debug _stats every x second
+    // DEBUG_INFO("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+    DEBUG_INFO("time: %12ums", millis());
+    // DEBUG_INFO("memory: %12u bytes \tmin: %12u bytes \tmax: %12u bytes",
+    //   memory_used, memory_used_min, memory_used_max);
+    DEBUG_INFO("loop counter %u\n", counter);
+    application_report();
+
+
+#ifdef CNETIF_STATS_ENABLED
+    netif_stats_sprintf(cnetif_stats_buffer, WiFi.stats, STATS_BUFFER_SIZE, (8*1e6)/(1<<20), "Mbit/s");
+    // __disable_irq();
+    arduino::lock();
+    NETIF_STATS_RESET_AVERAGES(WiFi.stats);
+    // __enable_irq();
+    arduino::unlock();
+
+    DEBUG_INFO(cnetif_stats_buffer);
+#endif // CNETIF_STATS_ENABLED
+    // DEBUG_INFO("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
+
+    counter = 0;
+    // reset some counters
+    debug_start = millis();
+  }
+  counter++;
+}
+
+// Application level Stuff
+enum app_state_t: uint8_t {
+  APP_STATE_NONE = 0,
+  APP_STATE_LINK_UP,
+  APP_STATE_LINK_DOWN,
+  APP_STATE_IFACE_UP,
+  APP_STATE_CONNECTING,
+  APP_STATE_CONNECTED,
+  APP_STATE_PARSE_HEADER,
+  APP_STATE_DOWNLOAD,
+  APP_STATE_DOWNLOAD_FAILED,
+  APP_STATE_DOWNLOAD_FINISHED,
+  APP_STATE_ERROR,
+  APP_STATE_RESET
+};
+
+static const char* state_strings[] = {
+  "APP_STATE_NONE",
+  "APP_STATE_LINK_UP",
+  "APP_STATE_LINK_DOWN",
+  "APP_STATE_IFACE_UP",
+  "APP_STATE_CONNECTING",
+  "APP_STATE_CONNECTED",
+  "APP_STATE_PARSE_HEADER",
+  "APP_STATE_DOWNLOAD",
+  "APP_STATE_DOWNLOAD_FAILED",
+  "APP_STATE_DOWNLOAD_FINISHED",
+  "APP_STATE_ERROR",
+  "APP_STATE_RESET"
+};
+
+#define APP_BUFFER_SIZE 1*1024
+
+
+struct App {
+  app_state_t current_state=APP_STATE_NONE;
+  app_state_t prev_state=APP_STATE_NONE;
+
+  lwipClient *tcp_client;
+  uint16_t port = 8000;
+  IPAddress server_ip = IPAddress(192, 168, 10, 250);
+
+  uint8_t buffer[APP_BUFFER_SIZE];
+
+  size_t file_length=0;
+  size_t downloaded_bytes=0;
+  std::string http_header;
+
+  // stats related variables
+  uint32_t start = 0;
+  uint32_t speed_start = 0;
+  uint32_t speed_bytes = 0;
+
+  // payload verification parameters
+  uint32_t payload_verify_offset=0;
+  uint8_t payload_verify_excess[4]={}; // this should be 3, but there are bugs
+  uint8_t payload_verify_excess_len=0;
+  uint32_t last_value=0;
+} app;
+
+void init_app(struct App& app) {
+  app.file_length = 0;
+  app.http_header = "";
+  app.downloaded_bytes = 0;
+  app.start = 0;
+  app.payload_verify_excess_len = 0;
+  app.payload_verify_offset = 0;
+  app.last_value=0;
+  app.speed_bytes = 0;
+}
+
+void reset_app(struct App& app) {
+  init_app(app);
+
+  if(app.tcp_client != nullptr) {
+    delete app.tcp_client;
+  }
+}
+
+const char* http_request = "GET /test-4M HTTP/1.1\nHost: 192.168.10.250\nConnection: close\n\n";
+
+void application() {
+  bool found = false;
+  uint16_t bytes_read=0;
+
+  switch(app.current_state) {
+  case APP_STATE_NONE:
+    init_app(app);
+
+    // TODO we are not handling link connection and disconnection
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_LINK_UP;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_LINK_UP:
+    if(WiFiStation.isDhcpAcquired()) {
+      app.prev_state = app.current_state;
+      app.current_state = APP_STATE_IFACE_UP;
+      DEBUG_INFO("State changed: to %s, from %s",
+        state_strings[app.current_state],
+        state_strings[app.prev_state]);
+    }
+    break;
+  case APP_STATE_IFACE_UP:
+    // The link is up we connect to the server
+    app.tcp_client = new lwipClient;
+
+    // Connection details:
+    app.tcp_client->connect(app.server_ip, app.port);
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_CONNECTING;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_CONNECTING:
+    // do nothing, until the TCP connection is established
+    // TODO handle timeout for connection and go to error state
+    if(app.tcp_client->connected()) {
+      app.prev_state = app.current_state;
+      app.current_state = APP_STATE_CONNECTED;
+      DEBUG_INFO("State changed: to %s, from %s",
+        state_strings[app.current_state],
+        state_strings[app.prev_state]);
+    }
+
+    break;
+
+  case APP_STATE_CONNECTED:
+    app.tcp_client->write((uint8_t*)http_request, strlen(http_request));
+    app.start = millis();
+    app.speed_start = app.start;
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_PARSE_HEADER;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_PARSE_HEADER:
+    // FIXME
+    bytes_read = app.tcp_client->read_until_token(app.buffer, APP_BUFFER_SIZE, "\r\n\r\n", found);
+    // DEBUG_INFO("%s", app.http_header.c_str());
+
+    if(bytes_read>0) {
+      // put the buffer into an http header string
+      std::string chunk((char*)app.buffer, bytes_read);
+      app.http_header += chunk;
+      app.speed_bytes += bytes_read;
+      DEBUG_INFO("%s", app.http_header.c_str());
+    }
+
+    if(found) { // FIXME reduce indentation level
+      // we found the http terminating token, go to the next app phase if we extracted the file len
+      // otherwise go in error phase
+
+      // Parse the http header and gather information needed for the download
+      // dump_buffer_char(app.buffer, APP_BUFFER_SIZE);
+
+      std::regex content_length_regex("Content-Length: ([0-9]+)", std::regex::icase);
+      std::smatch matches;
+
+      // DEBUG_INFO(app.http_header.c_str());
+
+      if(std::regex_search(app.http_header, matches, content_length_regex)) {
+        app.file_length = stoi(matches[1].str());
+
+        DEBUG_INFO("Download started, file length: %u", app.file_length);
+
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      } else {
+        // Failed to extract the content length from the header, going into an error state
+        // TODO report the reason of the error
+
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_ERROR;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      }
+    }
+    break;
+  case APP_STATE_DOWNLOAD:
+    if(app.tcp_client->available() <= 0) { // no data available
+      break;
+    }
+    // DEBUG_INFO("reading: tot_len %6u, offset %6u", app.tcp_client->p->tot_len, app.tcp_client->pbuf_offset);
+    bytes_read = app.tcp_client->read(app.buffer, APP_BUFFER_SIZE);
+    // DEBUG_INFO("read %6u", bytes_read);
+
+    if(bytes_read > 0) {
+      app.downloaded_bytes += bytes_read;
+      app.speed_bytes += bytes_read;
+
+      // dump_buffer(app.buffer, APP_BUFFER_SIZE, 4, 128);
+#ifdef CHECK_PAYLOAD
+      // if(!verify_buffer_sequential_4B(
+      if(!verify_buffer_sequential_faster_4B(
+        app.buffer,
+        bytes_read,
+        app.payload_verify_offset,
+        app.payload_verify_excess,
+        app.payload_verify_excess_len,
+        false)) {
+
+        DEBUG_INFO("Payload verification failed");
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD_FAILED;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      }
+#endif // CHECK_PAYLOAD
+    }
+
+    if(app.downloaded_bytes == app.file_length) {
+      app.last_value =
+        *(app.buffer + bytes_read - 4) << 24 |
+        *(app.buffer + bytes_read - 3) << 16 |
+        *(app.buffer + bytes_read - 2) << 8  |
+        *(app.buffer + bytes_read - 1);
+
+      // if the download of the counter file is correct the last value should be
+      // the size of the file/4 -1
+      if(app.last_value == (app.downloaded_bytes/4 - 1)) {
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD_FINISHED;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      } else {
+        app.prev_state = app.current_state;
+        app.current_state = APP_STATE_DOWNLOAD_FAILED;
+        DEBUG_INFO("State changed: to %s, from %s",
+          state_strings[app.current_state],
+          state_strings[app.prev_state]);
+      }
+    }
+    break;
+
+  case APP_STATE_DOWNLOAD_FAILED:
+    // TODO report error in file download and close the connection
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_ERROR;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_DOWNLOAD_FINISHED:
+    DEBUG_INFO("Download finished: %uMB", app.downloaded_bytes>>20);
+    DEBUG_INFO("Last value in the buffer: 0x%08X", app.last_value);
+    application_final_report();
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_RESET;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_ERROR:
+    // The app reached an expected error state
+    // TODO report this state and go in the default, status not defined handler to reset the state
+  case APP_STATE_RESET:
+    // in this state we reset the application and we start back from the beginning
+
+    reset_app(app);
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_IFACE_UP;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+  }
+}
+
+// application stats
+void application_report(bool force) {
+  if(force || app.current_state == APP_STATE_PARSE_HEADER || app.current_state == APP_STATE_DOWNLOAD) {
+
+    // float speed_conversion_factor = 1e3/(1<<10);
+    float speed_conversion_factor = 8*1e3/float(1<<20);
+    float elapsed = millis()-app.speed_start;
+
+    float speed = (app.speed_bytes / elapsed) * speed_conversion_factor;
+    DEBUG_INFO("Application layer: %12u/%12u speed: %.2f Mbit/s", app.downloaded_bytes, app.file_length, speed);
+
+    app.speed_start = millis();
+    app.speed_bytes = 0;
+  }
+}
+
+void application_final_report() {
+  // float speed_conversion_factor = 10e3/(1<<10);
+  float speed_conversion_factor = 1e3*8/float(1<<20);
+
+  float elapsed = millis()-app.start;
+  float speed = (app.downloaded_bytes / elapsed) * speed_conversion_factor;
+  DEBUG_INFO(
+    "Application layer: Downloaded %u MB in %.2fs average speed: %.2f Mbit/s",
+    app.downloaded_bytes>>20, elapsed/1000, speed);
+}
+
+// payload checking function
+bool verify_buffer_sequential_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print) {
+  size_t i=0;
+  bool res = true;
+  uint32_t value=0, first=0;
+
+  if(excess_len > 0) {
+    uint8_t j=0;
+    for(; j<excess_len; j++) {
+      value |= excess[j] << ((3-j)*8);
+    }
+
+    for(; j<4 && i<len; j++,i++) {
+      value |= buffer[i] << ((3-j)*8);
+
+      if(excess_len < 3) {
+        excess[j] = buffer[i];
+        excess_len++;
+      }
+    }
+
+    if(value != offset) {
+      DEBUG_INFO("perror %08X, %08X", value, offset);
+
+      res = false;
+    }
+    offset++;
+    first = value;
+  }
+
+  for(; i+4<=len; i+=4,offset++) {
+    // convert buffer from big endian bytearray to uint32
+    value =
+      *(buffer+i)   << 24 |
+      *(buffer+i+1) << 16 |
+      *(buffer+i+2) << 8  |
+      *(buffer+i+3);
+
+    if(first == 0) {
+      first = value;
+    }
+    // if(print) {
+    //   DEBUG_INFO("value: %X", value);
+    // }
+
+    if(value != offset && res) {
+      DEBUG_INFO("error %8X, %8X", value, offset);
+
+      res = false;
+    }
+  }
+
+  // put the bytes that exceed the modulo4 in the excess array
+  excess_len = len - i;
+  for(uint8_t j=0; i<len; j++,i++){
+    excess[j] = buffer[i];
+  }
+
+  if(print) {
+    DEBUG_INFO("packet First: %08X LAST %08X", first, value);
+  }
+
+
+  return res;
+}
+
+bool verify_buffer_sequential_faster_4B(uint8_t *buffer, size_t len, uint32_t& offset, uint8_t *excess, uint8_t &excess_len, bool print) {
+  size_t i=0;
+  bool res = true;
+  uint32_t first=0;
+
+  if(excess_len > 0) {
+    // the first value needs to be taken from the excess bytes of the previous buffer and the first of this
+    uint8_t j=0;
+    for(; j<excess_len; j++) {
+      first |= excess[j] << ((3-j)*8);
+    }
+
+    for(; j<4 && i<len; j++,i++) {
+      first |= buffer[i] << ((3-j)*8);
+    }
+  } else {
+    // the first value needs to be taken from the current buffer
+    for(; i<4; i++) {
+      first |= buffer[i] << ((3-i)*8);
+    }
+  }
+
+  // DEBUG_INFO("verify: found %08X, expected %08X, i %1u len %8u, excess_len %1u", first, offset, i, len, excess_len);
+  if(first != offset) {
+    DEBUG_INFO("perror: found %08X, expected %08X", first, offset);
+
+    res = false;
+  }
+  // offset++;
+
+  // After reconstructing the first integer, we can skip the verification of the rest of the payload,
+  // assuming that the issues are always caused by a missing section between buffers.
+  // This means that we only need to verify the first value, and update the value for offset
+
+  // The len of the returned excess is the following:
+  uint8_t new_excess_len = (len+excess_len) % 4;
+  i = len - new_excess_len;
+  offset = offset + (i+excess_len)/4;
+
+  // collect the excess for the next buffer
+  for(uint8_t j=0; i<len; j++,i++){
+    excess[j] = buffer[i];
+  }
+
+  excess_len = new_excess_len;
+
+  return res;
+}
+
+// Utility functions
+void dump_buffer(uint8_t* b, uint32_t len, uint8_t blocks, uint8_t cols) {
+
+  // TODO make sure blocks is less that cols
+  Serial.println("BUFFER >>>>>>>");
+  for(uint8_t *p=b; p<b+len; p++) {
+    if(*p < 0x10) {
+      Serial.print(0);
+    }
+    Serial.print(*p,  HEX);
+
+    if(cols != 0 && ((p-b)+1) % blocks == 0 && ((p-b)+1) % cols != 0){
+      Serial.print(" ");
+    }
+    if(cols != 0 && ((p-b)+1) % cols == 0){
+      Serial.println();
+    }
+  }
+  Serial.println("\nBUFFER <<<<<<<");
+}
+
+void dump_buffer_char(uint8_t* b, uint32_t len) {
+  Serial.println("BUFFER_CHAR >>>>>>>");
+  for(uint8_t *p=b; p<b+len; p++) {
+    Serial.print((char)*p);
+  }
+  Serial.println("\nBUFFER_CHAR <<<<<<<");
+}
\ No newline at end of file

From 0207e39d9cff2a7f1381b27f10e158bbb0eeb429 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 23 Jan 2024 17:15:05 +0100
Subject: [PATCH 66/79] fixing connection issue on Ethernet and Wifi Client
 when host is passed

---
 libraries/Ethernet/src/EthernetClient.h | 8 ++++++++
 libraries/WiFi/src/WiFiClient.h         | 8 ++++++++
 libraries/lwIpWrapper/src/CNetIf.cpp    | 8 ++++++++
 libraries/lwIpWrapper/src/CNetIf.h      | 2 +-
 4 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/libraries/Ethernet/src/EthernetClient.h b/libraries/Ethernet/src/EthernetClient.h
index 8d648b574..fe8edb808 100644
--- a/libraries/Ethernet/src/EthernetClient.h
+++ b/libraries/Ethernet/src/EthernetClient.h
@@ -14,6 +14,14 @@ class EthernetClient : public lwipClient {
         this->bindCNetIf(Ethernet);
     }
 
+    int connect(const char* host, uint16_t port) {
+        auto res = lwipClient::connect(host, port);
+
+        this->bindCNetIf(Ethernet);
+
+        return res;
+    }
+
     int connect(IPAddress ip, uint16_t port) {
         auto res = lwipClient::connect(ip, port);
 
diff --git a/libraries/WiFi/src/WiFiClient.h b/libraries/WiFi/src/WiFiClient.h
index cad993d39..414012def 100644
--- a/libraries/WiFi/src/WiFiClient.h
+++ b/libraries/WiFi/src/WiFiClient.h
@@ -14,6 +14,14 @@ class WiFiClient: public lwipClient {
         this->bindCNetIf(WiFiStation);
     }
 
+    int connect(const char* host, uint16_t port) {
+        auto res = lwipClient::connect(host, port);
+
+        this->bindCNetIf(WiFiStation);
+
+        return res;
+    }
+
     int connect(IPAddress ip, uint16_t port) {
         auto res = lwipClient::connect(ip, port);
 
diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 1b33457cb..eee9f3de1 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -459,6 +459,14 @@ bool CNetIf::dhcpRenew() {
 
 #endif
 
+IPAddress CNetIf::dnsServerIP() {
+#if LWIP_DNS
+    return CLwipIf::getInstance().getDns(0);
+#else
+    return INADDR_NONE;
+#endif
+}
+
 /* ##########################################################################
  *                      ETHERNET NETWORK INTERFACE CLASS
  * ########################################################################## */
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index fc7bf876b..7fa0e79a2 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -167,7 +167,7 @@ class CNetIf: public NetworkInterface {
     IPAddress localIP()     { return IPAddress(this->getIpAdd()); }
     IPAddress subnetMask()  { return IPAddress(this->getNmAdd()); }
     IPAddress gatewayIP()   { return IPAddress(this->getGwAdd()); }
-    IPAddress dnsServerIP() { /* FIXME understand where dns should be managed */}
+    IPAddress dnsServerIP();
 
     void config(IPAddress _ip, IPAddress _gw, IPAddress _nm);
 

From 3bbaccd940afa541f0681cfae7b9f2c7448d0151 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 23 Jan 2024 17:29:35 +0100
Subject: [PATCH 67/79] exporting Network stack synchronization to lwipClient

---
 libraries/lwIpWrapper/src/CNetIf.cpp     | 36 ++++++++++++------------
 libraries/lwIpWrapper/src/CNetIf.h       | 10 +++----
 libraries/lwIpWrapper/src/lwipClient.cpp |  9 +++++-
 3 files changed, 31 insertions(+), 24 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index eee9f3de1..adb919317 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -342,11 +342,11 @@ int CNetIf::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw)
         this->dhcpStart();
 
 
-        CLwipIf::getInstance().sync_timer();
+        CLwipIf::getInstance().syncTimer();
         while(!this->isDhcpAcquired()) {
             CLwipIf::getInstance().task();
         }
-        CLwipIf::getInstance().enable_timer();
+        CLwipIf::getInstance().enableTimer();
     }
 
 #endif
@@ -638,9 +638,9 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
         time_num++;
     }
 
-    CLwipIf::getInstance().sync_timer();
+    CLwipIf::getInstance().syncTimer();
     rv = CEspControl::getInstance().setWifiMode(WIFI_MODE_STA);
-    CLwipIf::getInstance().enable_timer();
+    CLwipIf::getInstance().enableTimer();
 
     if((rv=this->scanForAp()) != WL_SCAN_COMPLETED) {
         rv = -2;
@@ -668,7 +668,7 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
         memset(ap.bssid, 0x00, BSSID_LENGTH);
         memcpy(ap.bssid, access_points[best_index].bssid, BSSID_LENGTH);
 
-        CLwipIf::getInstance().sync_timer();
+        CLwipIf::getInstance().syncTimer();
         rv=CEspControl::getInstance().connectAccessPoint(ap);
 
         if (rv == ESP_CONTROL_OK) {
@@ -680,7 +680,7 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
         } else {
             wifi_status = WL_CONNECT_FAILED;
         }
-        CLwipIf::getInstance().enable_timer();
+        CLwipIf::getInstance().enableTimer();
     }
 
 exit:
@@ -690,10 +690,10 @@ int CWifiStation::connectToAP(const char* ssid, const char *passphrase) {
 int CWifiStation::scanForAp() {
     access_points.clear();
 
-    CLwipIf::getInstance().sync_timer();
+    CLwipIf::getInstance().syncTimer();
 
     int res = CEspControl::getInstance().getAccessPointScanList(access_points);
-    CLwipIf::getInstance().enable_timer();
+    CLwipIf::getInstance().enableTimer();
 
     if (res == ESP_CONTROL_OK) {
         wifi_status = WL_SCAN_COMPLETED;
@@ -707,11 +707,11 @@ int CWifiStation::scanForAp() {
 
 // disconnect
 int CWifiStation::disconnectFromAp() {
-    CLwipIf::getInstance().sync_timer();
+    CLwipIf::getInstance().syncTimer();
 
     auto res = CEspControl::getInstance().disconnectAccessPoint();
 
-    CLwipIf::getInstance().enable_timer();
+    CLwipIf::getInstance().enableTimer();
 
     return res;
 }
@@ -942,9 +942,9 @@ int CWifiSoftAp::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress
         time_num++;
     }
 
-    CLwipIf::getInstance().sync_timer();
+    CLwipIf::getInstance().syncTimer();
     res = CEspControl::getInstance().setWifiMode(WIFI_MODE_AP);
-    CLwipIf::getInstance().enable_timer();
+    CLwipIf::getInstance().enableTimer();
 
     CNetIf::begin(
         default_dhcp_server_ip,
@@ -958,7 +958,7 @@ int CWifiSoftAp::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress
 // TODO scan the other access point first and then set the channel if 0
 // TODO there are requirements for ssid and password
 int CWifiSoftAp::startSoftAp(const char* ssid, const char* passphrase, uint8_t channel) {
-    CLwipIf::getInstance().sync_timer();
+    CLwipIf::getInstance().syncTimer();
     SoftApCfg_t cfg;
 
     strncpy((char*)cfg.ssid, ssid, SSID_LENGTH);
@@ -991,7 +991,7 @@ int CWifiSoftAp::startSoftAp(const char* ssid, const char* passphrase, uint8_t c
         // wifi_status = WL_AP_FAILED;
     }
 
-    CLwipIf::getInstance().enable_timer();
+    CLwipIf::getInstance().enableTimer();
     return rv;
 }
 
@@ -1131,17 +1131,17 @@ uint8_t CWifiSoftAp::getChannel() {
 }
 
 int CWifiSoftAp::setLowPowerMode() {
-    CLwipIf::getInstance().sync_timer();
+    CLwipIf::getInstance().syncTimer();
     auto res = CEspControl::getInstance().setPowerSaveMode(1);
-    CLwipIf::getInstance().enable_timer();
+    CLwipIf::getInstance().enableTimer();
 
     return res;
 }
 
 int CWifiSoftAp::resetLowPowerMode() {
-    CLwipIf::getInstance().sync_timer();
+    CLwipIf::getInstance().syncTimer();
     auto res = CEspControl::getInstance().setPowerSaveMode(1);
-    CLwipIf::getInstance().enable_timer();
+    CLwipIf::getInstance().enableTimer();
 
     return res;
 }
diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h
index 7fa0e79a2..29f37884e 100644
--- a/libraries/lwIpWrapper/src/CNetIf.h
+++ b/libraries/lwIpWrapper/src/CNetIf.h
@@ -432,24 +432,24 @@ class CLwipIf {
     friend class CNetIf;
     friend class CWifiSoftAp;
     friend class CWifiStation;
-
+public:
 #ifdef LWIP_USE_TIMER
     FspTimer timer;
 
-    inline void sync_timer() {
+    inline void syncTimer() {
         timer.disable_overflow_irq();
         this->task();
     }
 
-    inline void enable_timer() {
+    inline void enableTimer() {
         timer.enable_overflow_irq();
     }
 #else // LWIP_USE_TIMER
-    inline void sync_timer() {
+    inline void syncTimer() {
         this->task();
     }
 
-    inline void enable_timer() { }
+    inline void enableTimer() { }
 #endif // LWIP_USE_TIMER
 };
 
diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index 1147a9563..492e1dbfe 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -101,6 +101,7 @@ int lwipClient::connect(IPAddress ip, uint16_t port) {
 
     // the connect method is only connected when trying to connect a client to a server
     // and not when a client is created out of a listening socket
+    CLwipIf::getInstance().syncTimer();
     this->tcp_info->pcb = tcp_new();
 
     if(this->tcp_info->pcb == nullptr) {
@@ -124,7 +125,13 @@ int lwipClient::connect(IPAddress ip, uint16_t port) {
         this->tcp_info->pcb, &this->_ip, port, // FIXME check if _ip gets copied
         _lwip_tcp_connected_callback // FIXME we need to define a static private function
     );
-    return err;
+
+    while(!connected()) {
+        CLwipIf::getInstance().task();
+    }
+    CLwipIf::getInstance().enableTimer();
+
+    return err == ERR_OK? 1: -err;
 }
 
 err_t _lwip_tcp_connected_callback(void* arg, struct tcp_pcb* tpcb, err_t err) {

From c595cc07c680a215fcb0a76dffff9334dcab768f Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 23 Jan 2024 17:29:54 +0100
Subject: [PATCH 68/79] fixing return codes

---
 libraries/lwIpWrapper/src/lwipClient.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index 492e1dbfe..02103ba11 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -89,7 +89,7 @@ int lwipClient::connect(const char* host, uint16_t port) {
     IPAddress remote_addr;
 
     int ret = CLwipIf::getInstance().getHostByName(host, remote_addr); // TODO test this
-    if (ret == 1) {
+    if (ret == 0) {
         return connect(remote_addr, port);
     } else {
         return 0;
@@ -287,7 +287,7 @@ size_t lwipClient::write(const uint8_t* buffer, size_t size) {
     tcp_output(this->tcp_info->pcb);
 
     arduino::unlock();
-    return buffer - buffer_cursor;
+    return buffer_cursor - buffer;
 }
 
 int lwipClient::read() {

From ec5ea837dc45651008a62d491be7f4914d2ba937 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Tue, 23 Jan 2024 17:30:46 +0100
Subject: [PATCH 69/79] deleting pbuf when partially consumed

---
 libraries/lwIpWrapper/src/lwipClient.cpp | 16 +++++++++++-----
 libraries/lwIpWrapper/src/lwipClient.h   |  1 +
 2 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index 02103ba11..019ea459a 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -235,7 +235,7 @@ err_t lwipClient::recv_callback(struct tcp_pcb* tpcb, struct pbuf* p, err_t err)
 
     if (p == NULL) {
         // Remote host has closed the connection -> close from our side
-        this->stop();
+        this->close_pcb();
 
         return ERR_OK;
     }
@@ -345,7 +345,7 @@ void lwipClient::flush() {
     tcp_output(this->tcp_info->pcb);
 }
 
-void lwipClient::stop() {
+void lwipClient::close_pcb() {
     if(this->tcp_info->pcb != nullptr) {
         tcp_recv(this->tcp_info->pcb, nullptr);
         tcp_sent(this->tcp_info->pcb, nullptr);
@@ -360,12 +360,18 @@ void lwipClient::stop() {
 
         // FIXME if err != ERR_OK retry, there may be memory issues, retry?
     }
+}
 
+void lwipClient::stop() {
+    this->close_pcb();
     // reset all the other variables in this class
 
-    // if(tcp->p != nullptr) {
-    //     pbuf_free(tcp->p); // FIXME it happens that a pbuf, with ref == 0 is added for some reason
-    // }
+    if(this->tcp_info->pbuf_head != nullptr) {
+        pbuf_free(this->tcp_info->pbuf_head); // FIXME it happens that a pbuf, with ref == 0 is added for some reason
+        this->tcp_info->pbuf_head = nullptr;
+    }
+    this->tcp_info->pbuf_offset = 0;
+
     if(this->tcp_info->server != nullptr) {
         // need to first make the server point to nullptr, then remove the client, can cause infinite recursion
         auto server = this->tcp_info->server;
diff --git a/libraries/lwIpWrapper/src/lwipClient.h b/libraries/lwIpWrapper/src/lwipClient.h
index 446c4288b..d7a7c0bd6 100644
--- a/libraries/lwIpWrapper/src/lwipClient.h
+++ b/libraries/lwIpWrapper/src/lwipClient.h
@@ -108,6 +108,7 @@ class lwipClient : public arduino::Client {
 
     friend err_t _lwip_tcp_recv_callback(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err);
     friend err_t _lwip_tcp_connected_callback(void* arg, struct tcp_pcb* tpcb, err_t err);
+    void close_pcb();
 };
 
 inline const lwipClient CLIENT_NONE(nullptr, nullptr);
\ No newline at end of file

From 564fcfc4aac58539df0b9b780a32081609318681 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 24 Jan 2024 11:45:34 +0100
Subject: [PATCH 70/79] Changed memory alignemnt according to renesas
 specifications

---
 extras/net/lwipopts.h                         |   2 +-
 .../lwIpWrapper/src/cortex-m33/liblwIP.a      | Bin 277056 -> 277056 bytes
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/extras/net/lwipopts.h b/extras/net/lwipopts.h
index b6acf324a..15f22494c 100644
--- a/extras/net/lwipopts.h
+++ b/extras/net/lwipopts.h
@@ -129,7 +129,7 @@
  *    2 byte alignment -> #define MEM_ALIGNMENT 2
  */
 #ifndef MEM_ALIGNMENT
-#define MEM_ALIGNMENT                   4
+#define MEM_ALIGNMENT                   32
 #endif
 
 /**
diff --git a/libraries/lwIpWrapper/src/cortex-m33/liblwIP.a b/libraries/lwIpWrapper/src/cortex-m33/liblwIP.a
index 0009e3f547eccf14b47246cd35170fd3bcb855bf..33abbd5a46d48c79df1b7df23c543ff00a9449dd 100644
GIT binary patch
delta 2957
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

delta 2954
zcmZuz3rtg27{2G8zIX`LBD8>53-S~w3JSp~Rw!fmm=dDE)S9V7Ma8(8uxSJzo0~6^
zJJ-Yq4rhp)N}yGaIuMNW?OfH(l+9_AsYsZvY(6UUvA_5Bwg+63|F-{kzyJN8lXg5-
zc05+rM#wYtY5I)xtY+QAFe(3=bZlm8kaef#D)Io@^=_a$@{$Pf4D+BgiQJl_J?}xy
zKvP>|bY~QTnV?IOmWI>%*1U24wC<Q5ET#3p;(Sc$T;*&d!W@~M0MA~Hs=#^d?PjGD
z2j6ks>X6!6T}Sn3%wc#s+w_HB_bO0C`4+=J_LO(7yBG)48_0f@_va0zOEQW{d|=bN
z6csH`D(C%S*SlogTO}irgnPdvHC6H6-m+tWN47syS5(8hDZ@118k%e=RZPl}YoX=M
z21WZ&6IhQ5FM=m`x@367<;Z9!#=9*6Ny**L$&yYh*t4CCR_O@jUY5iKD6T*@?9A}P
zy$UY1beMGbbQXTiC>@cisMuDvzsHXiA{J<-RVxdcT}?T<Tvcwpt4aSu7w!#_H5xkX
z0lZCpY2^5JX21u6<}>jNZD;s2c3A=vC7l*e3Z;%fU9F+dzQQS&rx#{$&r2t&Li}&$
zwSx2e##Wc5GUbLlnZmMYq~KPRrNOhlEFQI6FO=P7k;-~x?n!zk+{Oh{;$BqZG3uPx
z&tAP|;xpSBt)h)#{NfeI2H6#bMJ}y=`qLMcyEkL**@F541tgmcq6YB?QG&QXM`a8%
z4!*S@VIXdNHpm{30+2|MNgyF0CJ-$*qA1B4xJVxC*$Af`tF8Kv&U-f147!FJx@|2|
za@@A1epuh;3VB!S#GHJ4*E+pTZr3?;FiMKGF)><W+XLrkmM7YEu4_0$nBGVymJZ83
zVv+VLnZ1T;OV5oBhHksNMv0M$zh`5jWx3d-8mldgbz+&(B(KS@Ib-VVVzl5ZA9(Fd
zY|h^Y%F~T^T;;WXTDPUeC^P1qK)Mia*^c0-9)3Ird)%Ayy&=9m+sNpoxvC6zWY)y(
z*`fCT`P%+2iSbz%({63uQ5%em+|+%;(Q@mueKW<f9rZZedb=@MJQ#c%;w_&@JZ<s3
z{{UGNj-L}xBTouEIc_}NJJZwbWJ)(5k48(RJBlG45*LH)vD7;fS%_8*p@GA5(5z4z
z(0+)D0tJ9T4mF58uR-D^VK#+H)Wop?ogeM70YF2}wxC}%K0r-cZ7AF0gg3EPM=VBU
z?d<`M7C2B@0Bv`x2TfMe!1x!aOho~LqhW*D{uwo`WS1baJcPX{cFLWoKyC}`PZrQY
z5d%<L6l6FjiWM(NA@7bBK$}Of-U;x2dG5=Y0bIVu8Sw-B@*e7w{?Eh1l>Q!wUC;g{
zr>Z4)Gub5Z=JiG1X#hLV9Y2n|x`oyu(wzr!z1ZBL7_h7Y_PQ72f^=adp^&Xpcv)AA
z*aGa;qGr+B@=`X>_ipY2b|WGa=0ShG{Gj<X9~e`8{JLPa8j;b9Y5uh3Zft!8d)DUz
zFe=KUA#O(aD%uM2fJzpxhW>dYL&d;rsiJuTCR<Bmf^Bj209)`^zk%+S(81nMY3z2S
zJMN?}-t6?wZ8T4aaj%w+A=svU`KIkAGi@vIe%bXVFQJa+3HepkzlmKuKx0BKnuByM
zFjeT)qC<3F1gxcz!pu>*IE?IVVrR++w0ZRjA1%|fY-d}2VMT0c2aO4(Ej;5ZP2`<8
zNArZz{4P-#_b4%p^j>CT#lJC`B&v(8QhD;qBz1khRpT{S&DF2zEQ2EPCPXf?_=31J
znFNl+=ZHkhXzbJ|yb+nBWLQSl_~D-hl79=N$%2D1N*b%X3g^uHdnooNTU7W;KL<0!
z;~>VK7*3l)j@|>qGoXt5TKuEW2k)F@#o(J1*5^KmyW+6-Ac`G$lt^PxS&1)xlpa&r
zK>xr;Upmc);sBeL*)KlNYsjW~!n~eLz~0M-&wT?T1qSTBl(Gn{Q&qBOVp*haB_0_d
zYH$&S62}_+Z;Xg|wrc)2KCf9s*r4VH+N*#`zoRhj)mGS+L%2cb^#*+_7HiKkpLzg`
zJ@`6q2=x*GIvpZv=EfGJCUq}yn?{8A4Xg^21gS-YY)_T=d=ls`j2-?e4<;X{OT44V
L;!87)l{oN!@xY>Y


From 6c5f0ea2fe32de53873291e2501cfe557785e178 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 24 Jan 2024 14:37:59 +0100
Subject: [PATCH 71/79] improving examples

---
 .../ethernet_bare_lwipc33/ethernet_bare_lwipc33.ino       | 8 ++++++--
 .../examples/wifi_bare_lwipc33/wifi_bare_lwipc33.ino      | 5 ++---
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33/ethernet_bare_lwipc33.ino b/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33/ethernet_bare_lwipc33.ino
index 521983ce0..0e5dcca4c 100644
--- a/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33/ethernet_bare_lwipc33.ino
+++ b/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33/ethernet_bare_lwipc33.ino
@@ -36,9 +36,13 @@ void setup() {
 
   Serial.println("Renesas file download example");
 
+  IPAddress ip(192, 168, 10, 130);
+  IPAddress gw(192, 168, 10, 1);
+  IPAddress nm(255, 255, 255, 0);
+
   DEBUG_INFO("Setting up netif");
-  // Ethernet.begin(&ip, &nm, &gw);
-  Ethernet.begin();
+  Ethernet.begin(ip, nm, gw);
+  // Ethernet.begin();
 
   DEBUG_INFO("Begin of reception\n\n");
   debug_start = millis();
diff --git a/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/wifi_bare_lwipc33.ino b/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/wifi_bare_lwipc33.ino
index 071f4eec3..fc9eca56d 100644
--- a/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/wifi_bare_lwipc33.ino
+++ b/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/wifi_bare_lwipc33.ino
@@ -37,10 +37,9 @@ void setup() {
 
   Serial.println("Renesas file download example");
 
-  int res = 0;
   DEBUG_INFO("Connecting to AP");
-  while((res=WiFi.begin(SSID, SECRET_PASS)) != 1) {
-    DEBUG_INFO("Connection failed retry: %d", res);
+  while(WiFi.begin(SSID, SECRET_PASS) != WL_CONNECTED) {
+    DEBUG_INFO("Connection failed retry");
     delay(1000);
   }
   DEBUG_INFO("Connected to AP");

From 6edeefe5a7e64edf9450aacafa4983e7ea427da2 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 24 Jan 2024 14:39:29 +0100
Subject: [PATCH 72/79] setting dhcp acquired when dhcp server is informed of
 static ip

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index adb919317..389e115a7 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -432,6 +432,7 @@ void CNetIf::config(IPAddress _ip, IPAddress _gw, IPAddress _nm) {
 
 void CNetIf::dhcpNotUsed() {
     dhcp_inform(&this->ni);
+    dhcp_acquired = true;
 }
 
 bool CNetIf::isDhcpAcquired() {

From 896f7caa8c286b8c74210e8a5e4ac7cb8830995f Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 24 Jan 2024 15:05:08 +0100
Subject: [PATCH 73/79] improving performances of ssl client

---
 libraries/SSLClient/src/ssl_client.cpp | 29 ++++++++++++++++----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/libraries/SSLClient/src/ssl_client.cpp b/libraries/SSLClient/src/ssl_client.cpp
index 5a334ecdf..ee254969a 100644
--- a/libraries/SSLClient/src/ssl_client.cpp
+++ b/libraries/SSLClient/src/ssl_client.cpp
@@ -52,23 +52,30 @@ static int _handle_error(int err, const char * file, int line)
  */
 static int client_net_recv( void *ctx, unsigned char *buf, size_t len ) {
     Client *client = (Client*)ctx;
-    if (!client) { 
+    if (!client) {
         log_e("Uninitialised!");
         return -1;
     }
-    
+
     //if (!client->connected()) {
     //    log_e("Not connected!");
     //    return -2;
     //}
+    if(client->available() == 0) {
+        log_d("Want to read %u", len);
+        return MBEDTLS_ERR_SSL_WANT_READ;
+    }
 
     int result = client->read(buf, len);
     log_d("SSL client RX res=%d len=%d", result, len);
 
-    if (result > 0) {
-        //esp_log_buffer_hexdump_internal("SSL.RD", buf, (uint16_t)result, ESP_LOG_VERBOSE);
+    if(result < 0) {
+        return MBEDTLS_ERR_SSL_WANT_READ;
     }
-    
+    // if (result > 0) {
+        //esp_log_buffer_hexdump_internal("SSL.RD", buf, (uint16_t)result, ESP_LOG_VERBOSE);
+    // }
+
     return result;
 }
 
@@ -121,20 +128,20 @@ int client_net_recv_timeout( void *ctx, unsigned char *buf,
  */
 static int client_net_send( void *ctx, const unsigned char *buf, size_t len ) {
     Client *client = (Client*)ctx;
-    if (!client) { 
+    if (!client) {
         log_e("Uninitialised!");
         return -1;
     }
-    
+
     //if (!client->connected()) {
     //    log_e("Not connected!");
     //    return -2;
     //}
-    
+
     //esp_log_buffer_hexdump_internal("SSL.WR", buf, (uint16_t)len, ESP_LOG_VERBOSE);
-    
+
     int result = client->write(buf, len);
-    
+
     log_d("SSL client TX res=%d len=%d", result, len);
     return result;
 }
@@ -307,7 +314,7 @@ int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t p
 
     log_v("Setting up IO callbacks...");
     mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, ssl_client->client,
-                        client_net_send, NULL, client_net_recv_timeout );
+                        client_net_send, client_net_recv, NULL );
 
     log_v("Performing the SSL/TLS handshake...");
     unsigned long handshake_start_time=millis();

From aa3c5a1a4dadbbd61eeaee0c211d70d63d300d79 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Wed, 24 Jan 2024 15:22:21 +0100
Subject: [PATCH 74/79] fixing counter overflow in tcp send

---
 libraries/lwIpWrapper/src/lwipClient.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index 019ea459a..9aeb99cd2 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -265,7 +265,7 @@ size_t lwipClient::write(const uint8_t* buffer, size_t size) {
     arduino::lock();
 
     uint8_t* buffer_cursor = (uint8_t*)buffer;
-    uint8_t bytes_to_send = 0;
+    uint16_t bytes_to_send = 0;
 
     do {
         bytes_to_send = min(size - (buffer - buffer_cursor), tcp_sndbuf(this->tcp_info->pcb));
@@ -282,7 +282,7 @@ size_t lwipClient::write(const uint8_t* buffer, size_t size) {
         } else if(res == ERR_MEM) {
             // FIXME handle this: we get into this case only if the sent data cannot be put in the send queue
         }
-    } while(buffer_cursor < buffer + size);
+    } while(buffer_cursor - buffer < size);
 
     tcp_output(this->tcp_info->pcb);
 

From 9e05471e73c4580998a1481cec1a92b639a560ab Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Thu, 25 Jan 2024 10:31:46 +0100
Subject: [PATCH 75/79] added tcp write example for tcp echo server

---
 .../ethernet_bare_lwipc33_write.ino           | 301 ++++++++++++++++++
 1 file changed, 301 insertions(+)
 create mode 100644 libraries/lwIpWrapper/examples/ethernet_bare_lwipc33_write/ethernet_bare_lwipc33_write.ino

diff --git a/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33_write/ethernet_bare_lwipc33_write.ino b/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33_write/ethernet_bare_lwipc33_write.ino
new file mode 100644
index 000000000..42ed4238e
--- /dev/null
+++ b/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33_write/ethernet_bare_lwipc33_write.ino
@@ -0,0 +1,301 @@
+#include <Arduino.h>
+#include <Arduino_DebugUtils.h>
+#include <IRQManager.h>
+#include <regex>
+#include <utils.h>
+
+// #define CNETIF_STATS_ENABLED
+// #include "CNetifStats.h"
+
+#ifdef CNETIF_STATS_ENABLED
+#define STATS_BUFFER_SIZE 1000
+char cnetif_stats_buffer[STATS_BUFFER_SIZE];
+// netif_stats _stats;
+#endif // CNETIF_STATS_ENABLED
+
+#include <EthernetC33.h>
+#include <lwipClient.h>
+
+#define CHECK_PAYLOAD
+
+/* --------------------------------------- */
+void timer_cb(timer_callback_args_t *arg);
+void application();
+void dump_buffer(uint8_t* b, uint32_t len, uint8_t blocks=4, uint8_t cols=16);
+void dump_buffer_char(uint8_t* b, uint32_t len);
+uint64_t debug_start;
+/* --------------------------------------- */
+
+void setup() {
+  Serial.begin(115200);
+  while(!Serial);
+
+  Serial.println("Renesas file download example");
+
+  IPAddress ip(192, 168, 10, 130);
+  IPAddress gw(192, 168, 10, 1);
+  IPAddress nm(255, 255, 255, 0);
+
+  DEBUG_INFO("Setting up netif");
+  Ethernet.begin(ip, nm, gw);
+  // Ethernet.begin();
+
+  DEBUG_INFO("Begin of reception\n\n");
+  debug_start = millis();
+}
+
+uint32_t counter=0;
+void loop() {
+  // __disable_irq();
+  uint32_t start = micros();
+#ifndef LWIP_USE_TIMER
+  CLwipIf::getInstance().task();
+#endif
+  // Handle application FSM
+  application();
+
+  if(millis() - debug_start > 3000) { // print the debug _stats every x second
+    // DEBUG_INFO("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+    DEBUG_INFO("time: %12ums", millis());
+    // DEBUG_INFO("memory: %12u bytes \tmin: %12u bytes \tmax: %12u bytes",
+    //   memory_used, memory_used_min, memory_used_max);
+    DEBUG_INFO("loop counter %u\n", counter);
+    // application_report();
+
+#ifdef CNETIF_STATS_ENABLED
+    netif_stats_sprintf(cnetif_stats_buffer, Ethernet.stats, STATS_BUFFER_SIZE, (8*1e6)/(1<<20), "Mbit/s");
+    // __disable_irq();
+    arduino::lock();
+    NETIF_STATS_RESET_AVERAGES(Ethernet.stats);
+    // __enable_irq();
+    arduino::unlock();
+
+    DEBUG_INFO(cnetif_stats_buffer);
+#endif // CNETIF_STATS_ENABLED
+    // DEBUG_INFO("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
+
+    counter = 0;
+    // reset some counters
+    debug_start = millis();
+  }
+  counter++;
+}
+
+// Application level Stuff
+enum app_state_t: uint8_t {
+  APP_STATE_NONE = 0,
+  APP_STATE_LINK_UP,
+  APP_STATE_LINK_DOWN,
+  APP_STATE_IFACE_UP,
+  APP_STATE_CONNECTING,
+  APP_STATE_CONNECTED,
+  APP_STATE_SEND,
+  APP_STATE_RECEIVE,
+  APP_STATE_ERROR,
+  APP_STATE_RESET
+};
+
+static const char* state_strings[] = {
+  "APP_STATE_NONE",
+  "APP_STATE_LINK_UP",
+  "APP_STATE_LINK_DOWN",
+  "APP_STATE_IFACE_UP",
+  "APP_STATE_CONNECTING",
+  "APP_STATE_CONNECTED",
+  "APP_STATE_SEND",
+  "APP_STATE_RECEIVE",
+  "APP_STATE_ERROR",
+  "APP_STATE_RESET"
+};
+
+#define APP_BUFFER_SIZE 1*1024
+
+typedef uint32_t counter_t;
+
+struct App {
+  app_state_t current_state=APP_STATE_NONE;
+  app_state_t prev_state=APP_STATE_NONE;
+
+  lwipClient *tcp_client;
+  uint16_t port = 2000;
+  IPAddress server_ip = IPAddress(192, 168, 10, 250);
+
+  counter_t counter;
+  uint8_t buffer[APP_BUFFER_SIZE];
+
+  size_t file_length=0;
+  size_t downloaded_bytes=0;
+  std::string http_header;
+
+  // stats related variables
+  uint32_t start = 0;
+  uint32_t speed_start = 0;
+  uint32_t speed_bytes = 0;
+
+  // payload verification parameters
+  uint32_t payload_verify_offset=0;
+  uint8_t payload_verify_excess[4]={}; // this should be 3, but there are bugs
+  uint8_t payload_verify_excess_len=0;
+  uint32_t last_value=0;
+} app;
+
+void init_app(struct App& app) {
+  app.file_length = 0;
+  app.http_header = "";
+  app.downloaded_bytes = 0;
+  app.start = 0;
+  app.payload_verify_excess_len = 0;
+  app.payload_verify_offset = 0;
+  app.last_value=0;
+  app.speed_bytes = 0;
+  app.counter = 0;
+}
+
+void reset_app(struct App& app) {
+  init_app(app);
+
+  if(app.tcp_client != nullptr) {
+    app.tcp_client->stop();
+    // delete app.tcp_client;
+  }
+}
+
+void application() {
+  bool found = false;
+  uint16_t bytes_read=0;
+
+  switch(app.current_state) {
+  case APP_STATE_NONE:
+    init_app(app);
+
+    // TODO we are not handling link connection and disconnection
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_LINK_UP;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_LINK_UP:
+    if(Ethernet.isDhcpAcquired()) {
+      app.prev_state = app.current_state;
+      app.current_state = APP_STATE_IFACE_UP;
+      DEBUG_INFO("State changed: to %s, from %s",
+        state_strings[app.current_state],
+        state_strings[app.prev_state]);
+    }
+    break;
+  case APP_STATE_IFACE_UP:
+    // The link is up we connect to the server
+    app.tcp_client = new lwipClient;
+
+    // Connection details:
+    app.tcp_client->connect(app.server_ip, app.port);
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_CONNECTING;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+
+  case APP_STATE_CONNECTING:
+    // do nothing, until the TCP connection is established
+    // TODO handle timeout for connection and go to error state
+    if(app.tcp_client->connected()) {
+      app.prev_state = app.current_state;
+      app.current_state = APP_STATE_SEND;
+      DEBUG_INFO("State changed: to %s, from %s",
+        state_strings[app.current_state],
+        state_strings[app.prev_state]);
+    }
+
+    break;
+
+  case APP_STATE_CONNECTED:
+    app.start = millis();
+    app.speed_start = app.start;
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_SEND;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+  case APP_STATE_SEND: {
+    int res = app.tcp_client->write((uint8_t*)&app.counter, sizeof(counter_t));
+    DEBUG_INFO("counter sent, value 0x%08X, res: %d", app.counter, res);
+
+    if(res == sizeof(counter_t)) {
+      app.counter++;
+      app.prev_state = app.current_state;
+      app.current_state = APP_STATE_RECEIVE;
+      // DEBUG_INFO("State changed: to %s, from %s",
+      //   state_strings[app.current_state],
+      //   state_strings[app.prev_state]);
+    }
+    break;
+  }
+  case APP_STATE_RECEIVE: {
+    counter_t read_counter;
+    bytes_read = app.tcp_client->read((uint8_t*)&read_counter, sizeof(counter_t));
+
+    if(bytes_read == sizeof(counter_t)) {
+      DEBUG_INFO("counter echoed, value 0x%08X", read_counter);
+
+      app.prev_state = app.current_state;
+      app.current_state = APP_STATE_SEND;
+      // DEBUG_INFO("State changed: to %s, from %s",
+      //   state_strings[app.current_state],
+      //   state_strings[app.prev_state]);
+    }
+    break;
+  }
+  case APP_STATE_ERROR:
+    // The app reached an expected error state
+    // TODO report this state and go in the default, status not defined handler to reset the state
+  case APP_STATE_RESET:
+    // in this state we reset the application and we start back from the beginning
+
+    reset_app(app);
+
+    app.prev_state = app.current_state;
+    app.current_state = APP_STATE_IFACE_UP;
+    DEBUG_INFO("State changed: to %s, from %s",
+      state_strings[app.current_state],
+      state_strings[app.prev_state]);
+    break;
+  }
+}
+
+// Utility functions
+void dump_buffer(uint8_t* b, uint32_t len, uint8_t blocks, uint8_t cols) {
+
+  // TODO make sure blocks is less that cols
+  Serial.println("BUFFER >>>>>>>");
+  for(uint8_t *p=b; p<b+len; p++) {
+    if(p == nullptr) {
+      break;
+    }
+    if(*p < 0x10) {
+      Serial.print(0);
+    }
+    Serial.print(*p,  HEX);
+
+    if(cols != 0 && ((p-b)+1) % blocks == 0 && ((p-b)+1) % cols != 0){
+      Serial.print(" ");
+    }
+    if(cols != 0 && ((p-b)+1) % cols == 0){
+      Serial.println();
+    }
+  }
+  Serial.println("\nBUFFER <<<<<<<");
+}
+
+void dump_buffer_char(uint8_t* b, uint32_t len) {
+  Serial.println("BUFFER_CHAR >>>>>>>");
+  for(uint8_t *p=b; p<b+len; p++) {
+    Serial.print((char)*p);
+  }
+  Serial.println("\nBUFFER_CHAR <<<<<<<");
+}
\ No newline at end of file

From e489e71ced9d2acd458d1cc3680c20db54dcfbd6 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 26 Jan 2024 10:09:32 +0100
Subject: [PATCH 76/79] changing synchronization to timer disable

---
 libraries/lwIpWrapper/src/CNetIf.cpp     |  2 --
 libraries/lwIpWrapper/src/lwipClient.cpp | 19 +++++++++----------
 2 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 389e115a7..9d1976907 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -126,9 +126,7 @@ void CLwipIf::task() {
         iface->task();
     }
 
-    arduino::lock();
     sys_check_timeouts();
-    arduino::unlock();
 }
 
 void CLwipIf::setDefaultIface(CNetIf* iface) {
diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index 9aeb99cd2..bd5e92551 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -239,7 +239,6 @@ err_t lwipClient::recv_callback(struct tcp_pcb* tpcb, struct pbuf* p, err_t err)
 
         return ERR_OK;
     }
-    arduino::lock();
     if(this->tcp_info->state == TCP_CONNECTED || this->tcp_info->state == TCP_ACCEPTED) {
         if (this->tcp_info->pbuf_head == nullptr) {
             // no need to increment the references of the pbuf,
@@ -252,7 +251,6 @@ err_t lwipClient::recv_callback(struct tcp_pcb* tpcb, struct pbuf* p, err_t err)
 
         ret_err = ERR_OK;
     }
-    arduino::unlock();
 
     return ret_err;
 }
@@ -262,7 +260,7 @@ size_t lwipClient::write(uint8_t b) {
 }
 
 size_t lwipClient::write(const uint8_t* buffer, size_t size) {
-    arduino::lock();
+    CLwipIf::getInstance().syncTimer();
 
     uint8_t* buffer_cursor = (uint8_t*)buffer;
     uint16_t bytes_to_send = 0;
@@ -286,7 +284,8 @@ size_t lwipClient::write(const uint8_t* buffer, size_t size) {
 
     tcp_output(this->tcp_info->pcb);
 
-    arduino::unlock();
+    CLwipIf::getInstance().enableTimer();
+
     return buffer_cursor - buffer;
 }
 
@@ -312,14 +311,14 @@ int lwipClient::read(uint8_t* buffer, size_t size) {
      * meaning that across different calls of this function a pbuf could be partially copied
      * we need to account that
      */
-    arduino::lock();
+    CLwipIf::getInstance().syncTimer();
     uint16_t copied = pbuf_copy_partial(this->tcp_info->pbuf_head, buffer, size, this->tcp_info->pbuf_offset);
 
     this->tcp_info->pbuf_head = free_pbuf_chain(this->tcp_info->pbuf_head, copied, &this->tcp_info->pbuf_offset);
 
     // acknowledge the received data
     tcp_recved(this->tcp_info->pcb, copied);
-    arduino::unlock();
+    CLwipIf::getInstance().enableTimer();
 
     return copied;
 }
@@ -331,9 +330,9 @@ int lwipClient::peek() {
         return -1;
     }
 
-    arduino::lock();
+    CLwipIf::getInstance().syncTimer();
     b = pbuf_get_at(this->tcp_info->pbuf_head, 0); // TODO test this
-    arduino::unlock();
+    CLwipIf::getInstance().enableTimer();
 
     return b;
 }
@@ -417,7 +416,7 @@ size_t lwipClient::read_until_token(
     if(buffer_size==0 || buffer==nullptr || this->tcp_info->pbuf_head==nullptr) {
         return 0; // TODO extend checks
     }
-    arduino::lock();
+    CLwipIf::getInstance().syncTimer();
     // TODO check that the buffer size is less than the token len
 
     uint16_t offset=this->tcp_info->pbuf_offset;
@@ -456,7 +455,7 @@ size_t lwipClient::read_until_token(
 
     // acknowledge the received data
     tcp_recved(this->tcp_info->pcb, copied);
-    arduino::unlock();
+    CLwipIf::getInstance().enableTimer();
 
     return copied;
 }

From e303948509363fff55b213284ade9d92e2cfe671 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 26 Jan 2024 10:11:41 +0100
Subject: [PATCH 77/79] improving write function to better handle memory errors
 and bigger buffers

---
 libraries/lwIpWrapper/src/lwipClient.cpp | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp
index bd5e92551..a2a533ce3 100644
--- a/libraries/lwIpWrapper/src/lwipClient.cpp
+++ b/libraries/lwIpWrapper/src/lwipClient.cpp
@@ -266,8 +266,7 @@ size_t lwipClient::write(const uint8_t* buffer, size_t size) {
     uint16_t bytes_to_send = 0;
 
     do {
-        bytes_to_send = min(size - (buffer - buffer_cursor), tcp_sndbuf(this->tcp_info->pcb));
-
+        bytes_to_send = min(size - (buffer_cursor - buffer), tcp_sndbuf(this->tcp_info->pcb));
         /*
          * TODO: Look into the following flags, especially for write of 1 byte
          * TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
@@ -279,7 +278,17 @@ size_t lwipClient::write(const uint8_t* buffer, size_t size) {
             buffer_cursor += bytes_to_send;
         } else if(res == ERR_MEM) {
             // FIXME handle this: we get into this case only if the sent data cannot be put in the send queue
+            CLwipIf::getInstance().task();
+            // break;
+        } else {
+            break;
         }
+
+        // FIXME blocking call
+        while(tcp_sndbuf(this->tcp_info->pcb) == 0 && buffer_cursor - buffer < size) {
+            CLwipIf::getInstance().task();
+        }
+
     } while(buffer_cursor - buffer < size);
 
     tcp_output(this->tcp_info->pcb);

From b58a521939e36cc1ada4897068604de1de370d35 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 26 Jan 2024 10:13:34 +0100
Subject: [PATCH 78/79] making dns resolution to work automatically when
 interrupts are disabled

---
 libraries/lwIpWrapper/src/CNetIf.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libraries/lwIpWrapper/src/CNetIf.cpp b/libraries/lwIpWrapper/src/CNetIf.cpp
index 9d1976907..3bb16bcad 100644
--- a/libraries/lwIpWrapper/src/CNetIf.cpp
+++ b/libraries/lwIpWrapper/src/CNetIf.cpp
@@ -226,9 +226,13 @@ int CLwipIf::getHostByName(const char* aHostname, IPAddress& aResult, bool execu
 
     while(res == 1 && !completed) { // DNS timeouts seems to be handled by lwip, no need to put one here
         delay(1);
+#ifndef LWIP_USE_TIMER
+        this->task();
+#else // LWIP_USE_TIMER
         if(execute_task) {
             this->task();
         }
+#endif // LWIP_USE_TIMER
     }
 
     return res == 1 ? 0 : res;

From c28b4bd391bac9d0ce1b53eae7d2f00e6e614fa5 Mon Sep 17 00:00:00 2001
From: Andrea Gilardoni <a.gilardoni@arduino.cc>
Date: Fri, 26 Jan 2024 11:14:11 +0100
Subject: [PATCH 79/79] improving example

---
 .../ethernet_bare_lwipc33_write.ino           | 40 +++++++++++--------
 1 file changed, 24 insertions(+), 16 deletions(-)

diff --git a/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33_write/ethernet_bare_lwipc33_write.ino b/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33_write/ethernet_bare_lwipc33_write.ino
index 42ed4238e..4057602c6 100644
--- a/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33_write/ethernet_bare_lwipc33_write.ino
+++ b/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33_write/ethernet_bare_lwipc33_write.ino
@@ -35,9 +35,10 @@ void setup() {
   IPAddress ip(192, 168, 10, 130);
   IPAddress gw(192, 168, 10, 1);
   IPAddress nm(255, 255, 255, 0);
+  IPAddress dns(8, 8, 8, 8);
 
   DEBUG_INFO("Setting up netif");
-  Ethernet.begin(ip, nm, gw);
+  Ethernet.begin(ip, nm, gw, dns);
   // Ethernet.begin();
 
   DEBUG_INFO("Begin of reception\n\n");
@@ -160,9 +161,9 @@ void reset_app(struct App& app) {
   }
 }
 
+uint16_t bytes_read=0;
 void application() {
   bool found = false;
-  uint16_t bytes_read=0;
 
   switch(app.current_state) {
   case APP_STATE_NONE:
@@ -189,8 +190,11 @@ void application() {
     // The link is up we connect to the server
     app.tcp_client = new lwipClient;
 
+    memset(app.buffer, 0x66, APP_BUFFER_SIZE);
+
     // Connection details:
     app.tcp_client->connect(app.server_ip, app.port);
+    // app.tcp_client->connect("tcpbin.com", 4242);
 
     app.prev_state = app.current_state;
     app.current_state = APP_STATE_CONNECTING;
@@ -223,31 +227,35 @@ void application() {
       state_strings[app.prev_state]);
     break;
   case APP_STATE_SEND: {
-    int res = app.tcp_client->write((uint8_t*)&app.counter, sizeof(counter_t));
-    DEBUG_INFO("counter sent, value 0x%08X, res: %d", app.counter, res);
+    int res = app.tcp_client->write(app.buffer, APP_BUFFER_SIZE);
+    DEBUG_INFO("buffer sent res: %d", res);
 
-    if(res == sizeof(counter_t)) {
-      app.counter++;
+    if(res == APP_BUFFER_SIZE) {
       app.prev_state = app.current_state;
       app.current_state = APP_STATE_RECEIVE;
-      // DEBUG_INFO("State changed: to %s, from %s",
-      //   state_strings[app.current_state],
-      //   state_strings[app.prev_state]);
+      DEBUG_INFO("State changed: to %s, from %s",
+        state_strings[app.current_state],
+        state_strings[app.prev_state]);
     }
     break;
   }
   case APP_STATE_RECEIVE: {
-    counter_t read_counter;
-    bytes_read = app.tcp_client->read((uint8_t*)&read_counter, sizeof(counter_t));
+    int res = app.tcp_client->read(app.buffer, APP_BUFFER_SIZE);
 
-    if(bytes_read == sizeof(counter_t)) {
-      DEBUG_INFO("counter echoed, value 0x%08X", read_counter);
+    if(res > 0) {
+      bytes_read += res;
+      DEBUG_INFO("received %d bytes", res);
+    }
+
+    if(bytes_read == APP_BUFFER_SIZE) {
+      DEBUG_INFO("buffer received: %d", bytes_read);
+      bytes_read = 0;
 
       app.prev_state = app.current_state;
       app.current_state = APP_STATE_SEND;
-      // DEBUG_INFO("State changed: to %s, from %s",
-      //   state_strings[app.current_state],
-      //   state_strings[app.prev_state]);
+      DEBUG_INFO("State changed: to %s, from %s",
+        state_strings[app.current_state],
+        state_strings[app.prev_state]);
     }
     break;
   }