diff --git a/extras/net/lwipopts.h b/extras/net/lwipopts.h index 87d0d9a5..15f22494 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 @@ -129,7 +129,7 @@ * 2 byte alignment -> #define MEM_ALIGNMENT 2 */ #ifndef MEM_ALIGNMENT -#define MEM_ALIGNMENT 4 +#define MEM_ALIGNMENT 32 #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/ESPhost/src/CEspCbks.h b/libraries/ESPhost/src/CEspCbks.h index 892fc71d..203ce996 100644 --- a/libraries/ESPhost/src/CEspCbks.h +++ b/libraries/ESPhost/src/CEspCbks.h @@ -32,13 +32,14 @@ #include "CCtrlWrapper.h" +#include #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; diff --git a/libraries/Ethernet/src/Ethernet.cpp b/libraries/Ethernet/src/Ethernet.cpp index 22c90ff6..0311f913 100644 --- a/libraries/Ethernet/src/Ethernet.cpp +++ b/libraries/Ethernet/src/Ethernet.cpp @@ -1,208 +1 @@ #include - -/* - * 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 97fa5451..41288040 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,16 @@ #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 +inline EthernetC33Driver EthernetDriver(2, 2, mem_malloc, 1536); -class CEthernet { +// FIXME Instantiate a global variable from CEth, calling it Ethernet +inline CEth Ethernet(&EthernetDriver); - 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; - -#endif +#endif // ARDUINO_PORTENTA_C33 diff --git a/libraries/Ethernet/src/EthernetClient.h b/libraries/Ethernet/src/EthernetClient.h index dbfa0012..fe8edb80 100644 --- a/libraries/Ethernet/src/EthernetClient.h +++ b/libraries/Ethernet/src/EthernetClient.h @@ -1,13 +1,32 @@ -#ifndef ARDUINO_LWIP_ETHERNET_CLIENT_H -#define ARDUINO_LWIP_ETHERNET_CLIENT_H - +#pragma once #include "lwipClient.h" class EthernetClient : public lwipClient { - public: - EthernetClient() {} - EthernetClient(struct tcp_struct *tcpClient) : lwipClient(tcpClient) {} -}; +public: + EthernetClient() { + } + EthernetClient(struct tcp_pcb *pcb, lwipServer *server) + : lwipClient(pcb, server) { + } + EthernetClient(const lwipClient &c) + : lwipClient(c) { + this->bindCNetIf(Ethernet); + } + + int connect(const char* host, uint16_t port) { + auto res = lwipClient::connect(host, port); + + this->bindCNetIf(Ethernet); -#endif \ No newline at end of file + return res; + } + + int connect(IPAddress ip, uint16_t port) { + auto res = lwipClient::connect(ip, port); + + this->bindCNetIf(Ethernet); + + return res; + } +}; diff --git a/libraries/Ethernet/src/EthernetDriver.cpp b/libraries/Ethernet/src/EthernetDriver.cpp index 70eaf43d..6c6b2fb2 100644 --- a/libraries/Ethernet/src/EthernetDriver.cpp +++ b/libraries/Ethernet/src/EthernetDriver.cpp @@ -1,145 +1,73 @@ #include "EthernetDriver.h" -#include "IRQManager.h" +#include +#include +#include "utils.h" +#include -/* 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, ®value); - - 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(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 18cf68f9..64cde03b 100644 --- a/libraries/Ethernet/src/EthernetDriver.h +++ b/libraries/Ethernet/src/EthernetDriver.h @@ -1,38 +1,97 @@ -#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 +#include -using EtherCallback_f = std::function; +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: -#endif + // extend the callbacks and add the Driver specific callbacks + std::function wake_lan_cbk; + std::function 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); +}; + +extern EthernetC33Driver C33EthernetDriver; \ No newline at end of file diff --git a/libraries/Ethernet/src/EthernetServer.h b/libraries/Ethernet/src/EthernetServer.h index 2afb5060..bb48d426 100644 --- a/libraries/Ethernet/src/EthernetServer.h +++ b/libraries/Ethernet/src/EthernetServer.h @@ -1,36 +1,18 @@ -#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(); - - 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); - } +class EthernetServer: public lwipServer { +public: + EthernetServer(uint16_t port) : lwipServer(port) {} + void begin() { + lwipServer::begin(); + this->bindCNetIf(Ethernet); + } + + EthernetClient available() { + lwipClient* res = available_ptr(); + return res != nullptr ? EthernetClient(*res) : EthernetClient(CLIENT_NONE); + } }; - -#endif \ No newline at end of file diff --git a/libraries/Ethernet/src/EthernetUdp.h b/libraries/Ethernet/src/EthernetUdp.h index 0d450b76..21f5f6b7 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/NetworkAPI/library.properties b/libraries/NetworkAPI/library.properties new file mode 100644 index 00000000..1f161d41 --- /dev/null +++ b/libraries/NetworkAPI/library.properties @@ -0,0 +1,9 @@ +name=NetworkAPI +version=1.0.0 +author=Arduino, Andrea Gilardoni +maintainer=Arduino +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 00000000..3b347f77 --- /dev/null +++ b/libraries/NetworkAPI/src/driver.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +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 consume_cbk) {this->consume_cbk = consume_cbk;} + virtual void setLinkUpCallback(std::function link_up_cbk) {this->link_up_cbk = link_up_cbk;} + virtual void setLinkDownCallback(std::function 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 consume_cbk; // TODO move in callbacks + + std::function tx_frame_cbk; + std::function link_up_cbk; + std::function 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 00000000..7cf72e92 --- /dev/null +++ b/libraries/NetworkAPI/src/interface.h @@ -0,0 +1,14 @@ +#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 diff --git a/libraries/SSLClient/src/ssl_client.cpp b/libraries/SSLClient/src/ssl_client.cpp index 5a334ecd..ee254969 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(); diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp index 59e742a0..99de072c 100644 --- a/libraries/WiFi/src/WiFi.cpp +++ b/libraries/WiFi/src/WiFi.cpp @@ -6,326 +6,272 @@ extern "C" void dhcps_start(struct netif *netif); /* -------------------------------------------------------------------------- */ -CWifi::CWifi() : _timeout(50000), ni(nullptr) { +CWifi::CWifi() : _timeout(50000) { } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ 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(); +/* -------------------------------------------------------------------------- */ + WiFiStation.connectToAP(ssid, nullptr); + return WiFiStation.begin(); } /* -------------------------------------------------------------------------- */ 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(); +/* -------------------------------------------------------------------------- */ + WiFiStation.connectToAP(ssid, passphrase); + WiFiStation.begin(); + return WiFiStation.status(); } /* 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); - CLwipIf::getInstance().startSoftAp(ssid,passphrase,channel); - if(ni != nullptr) { - - dhcps_start(ni->getNi()); - } - - return CLwipIf::getInstance().getWifiStatus(); +/* -------------------------------------------------------------------------- */ + WiFiSoftAP.begin(); + return WiFiSoftAP.startSoftAp(ssid, passphrase, channel); } /* -------------------------------------------------------------------------- */ 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; - } +/* -------------------------------------------------------------------------- */ + WiFiStation.config(local_ip, gateway, subnet); } /* -------------------------------------------------------------------------- */ void CWifi::config(IPAddress local_ip, IPAddress dns_server) { -/* -------------------------------------------------------------------------- */ - config(local_ip); - CLwipIf::getInstance().addDns(dns_server); +/* -------------------------------------------------------------------------- */ + config(local_ip); + CLwipIf::getInstance().addDnsServer(dns_server, 0); } /* -------------------------------------------------------------------------- */ 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); } /* -------------------------------------------------------------------------- */ 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); } /* -------------------------------------------------------------------------- */ 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); } /* -------------------------------------------------------------------------- */ void CWifi::setHostname(const char* name) { -/* -------------------------------------------------------------------------- */ - if(ni != nullptr) { - ni->setHostname(name); - } +/* -------------------------------------------------------------------------- */ +// if(ni != nullptr) { +// ni->setHostname(name); +// } } /* -------------------------------------------------------------------------- */ int CWifi::disconnect() { -/* -------------------------------------------------------------------------- */ - CLwipIf::getInstance().disconnectFromAp(); +/* -------------------------------------------------------------------------- */ + WiFiStation.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; +/* -------------------------------------------------------------------------- */ + if(WiFiStation.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; -} - -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + return WiFiStation.scanForAp();; +} + +/* -------------------------------------------------------------------------- */ IPAddress CWifi::localIP() { -/* -------------------------------------------------------------------------- */ - if(ni != nullptr) { - return IPAddress(ni->getIpAdd()); - } - return IPAddress((uint32_t)0); +/* -------------------------------------------------------------------------- */ + return WiFiStation.localIP(); } /* -------------------------------------------------------------------------- */ IPAddress CWifi::subnetMask() { /* -------------------------------------------------------------------------- */ - if(ni != nullptr) { - return IPAddress(ni->getNmAdd()); - } - return IPAddress((uint32_t)0); + return WiFiStation.subnetMask(); } /* -------------------------------------------------------------------------- */ IPAddress CWifi::gatewayIP() { -/* -------------------------------------------------------------------------- */ - if(ni != nullptr) { - return IPAddress(ni->getGwAdd()); - } - return IPAddress((uint32_t)0); +/* -------------------------------------------------------------------------- */ + return WiFiStation.gatewayIP(); } /* -------------------------------------------------------------------------- */ IPAddress CWifi::dnsIP(int n) { - return CLwipIf::getInstance().getDns(n); + return CLwipIf::getInstance().getDns(n); } /* -------------------------------------------------------------------------- */ -const char* CWifi::SSID(uint8_t networkItem) { - return CLwipIf::getInstance().getSSID(networkItem); +const char* CWifi::SSID() { + return WiFiStation.getSSID(); } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int32_t CWifi::RSSI(uint8_t networkItem) { - return CLwipIf::getInstance().getRSSI(networkItem); +int32_t CWifi::RSSI() { + return WiFiStation.getRSSI(); } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -uint8_t CWifi::encryptionType(uint8_t networkItem) { - return CLwipIf::getInstance().getEncrType(networkItem); +uint8_t CWifi::encryptionType() { + return WiFiStation.getEncryptionType(); } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -uint8_t* CWifi::BSSID(uint8_t networkItem, uint8_t* bssid) { - return CLwipIf::getInstance().getBSSID(networkItem,bssid); +uint8_t* CWifi::BSSID(uint8_t* bssid) { + return WiFiStation.getBSSID(bssid); } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -uint8_t CWifi::channel(uint8_t networkItem) { - return CLwipIf::getInstance().getChannel(networkItem); +uint8_t CWifi::channel() { + return WiFiStation.getChannel(); } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -const char* CWifi::SSID() { -/* -------------------------------------------------------------------------- */ - if(ni != nullptr) { - return ni->getSSID(); - } - return ""; +/* -------------------------------------------------------------------------- */ +const char* CWifi::SSID(uint8_t networkItem) { + return WiFiStation.getSSID(networkItem); } +/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -uint8_t* CWifi::BSSID(uint8_t* bssid) { -/* -------------------------------------------------------------------------- */ - if(ni != nullptr) { - return ni->getBSSID(bssid); - } - return nullptr; +/* -------------------------------------------------------------------------- */ +int32_t CWifi::RSSI(uint8_t networkItem) { + return WiFiStation.getRSSI(networkItem); } +/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -int32_t CWifi::RSSI() { -/* -------------------------------------------------------------------------- */ - if(ni != nullptr) { - return ni->getRSSI(); - } - return 0; +/* -------------------------------------------------------------------------- */ +uint8_t CWifi::encryptionType(uint8_t networkItem) { + return WiFiStation.getEncrType(networkItem); } +/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -uint8_t CWifi::encryptionType() { -/* -------------------------------------------------------------------------- */ - if(ni != nullptr) { - return ni->getEncryptionType(); - } - return 0; +/* -------------------------------------------------------------------------- */ +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() { -/* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getWifiStatus(); +uint8_t CWifi::status() { // FIXME +/* -------------------------------------------------------------------------- */ + // return CLwipIf::getInstance().getWifiStatus(); + return WiFiStation.status(); } /* -------------------------------------------------------------------------- */ int CWifi::hostByName(const char* aHostname, IPAddress& aResult) { -/* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getHostByName(aHostname,aResult); +/* -------------------------------------------------------------------------- */ + return CLwipIf::getInstance().getHostByName(aHostname, aResult); } /* -------------------------------------------------------------------------- */ void CWifi::lowPowerMode() { -/* -------------------------------------------------------------------------- */ - CLwipIf::getInstance().setLowPowerMode(); +/* -------------------------------------------------------------------------- */ + WiFiStation.setLowPowerMode(); } /* -------------------------------------------------------------------------- */ void CWifi::noLowPowerMode() { -/* -------------------------------------------------------------------------- */ - CLwipIf::getInstance().resetLowPowerMode(); +/* -------------------------------------------------------------------------- */ + WiFiStation.resetLowPowerMode(); } 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 2995cdcf..ae5112f9 100644 --- a/libraries/WiFi/src/WiFiC3.h +++ b/libraries/WiFi/src/WiFiC3.h @@ -1,17 +1,22 @@ -#ifndef C_ARDUINO_WIFI_H -#define C_ARDUINO_WIFI_H +#pragma once #include "CNetIf.h" #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); - 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 +26,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 +54,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 +112,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 +138,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 @@ -168,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(); /* @@ -180,12 +183,11 @@ 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); @@ -194,20 +196,17 @@ 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); @@ -218,11 +217,14 @@ class CWifi { * 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. * @@ -251,17 +253,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 diff --git a/libraries/WiFi/src/WiFiClient.h b/libraries/WiFi/src/WiFiClient.h index 19c7e83d..414012de 100644 --- a/libraries/WiFi/src/WiFiClient.h +++ b/libraries/WiFi/src/WiFiClient.h @@ -1,14 +1,32 @@ -#ifndef ARDUINO_LWIP_WIFI_CLIENT_H -#define ARDUINO_LWIP_WIFI_CLIENT_H - +#pragma once #include "lwipClient.h" -class WiFiClient : public lwipClient { - public: - WiFiClient() {} - WiFiClient(struct tcp_struct *tcpClient) : lwipClient(tcpClient) {} -}; +class WiFiClient: public lwipClient { +public: + WiFiClient() { + } + WiFiClient(struct tcp_pcb *pcb, lwipServer *server) + : lwipClient(pcb, server) { + } + WiFiClient(const lwipClient &c) + : lwipClient(c) { + this->bindCNetIf(WiFiStation); + } + + int connect(const char* host, uint16_t port) { + auto res = lwipClient::connect(host, port); + + this->bindCNetIf(WiFiStation); -#endif + return res; + } + int connect(IPAddress ip, uint16_t port) { + auto res = lwipClient::connect(ip, port); + + this->bindCNetIf(WiFiStation); + + return res; + } +}; diff --git a/libraries/WiFi/src/WiFiServer.h b/libraries/WiFi/src/WiFiServer.h index 1199814f..f06d8f77 100644 --- a/libraries/WiFi/src/WiFiServer.h +++ b/libraries/WiFi/src/WiFiServer.h @@ -1,35 +1,18 @@ -#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: + WiFiServer(uint16_t port) : lwipServer(port) {} + void begin() { + lwipServer::begin(); + this->bindCNetIf(WiFiStation); + } - 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() { + lwipClient* res = available_ptr(); + return res != nullptr ? WiFiClient(*res) : WiFiClient(CLIENT_NONE); + } }; - -#endif \ No newline at end of file diff --git a/libraries/WiFi/src/WiFiUdp.h b/libraries/WiFi/src/WiFiUdp.h index 4b0f3531..f9eb8c3d 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); } 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 00000000..0c9fdd55 --- /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 00000000..7459a02e --- /dev/null +++ b/libraries/lwIpWrapper/examples/dns_resolution/dns_resolution.ino @@ -0,0 +1,119 @@ +#include +#include +#include +#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; + } + +} + 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 00000000..0e5dcca4 --- /dev/null +++ b/libraries/lwIpWrapper/examples/ethernet_bare_lwipc33/ethernet_bare_lwipc33.ino @@ -0,0 +1,555 @@ +#include +#include +#include +#include +#include + +// #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 +#include + +#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"); + + 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_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 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>>>>>>"); + for(uint8_t *p=b; p>>>>>>"); + for(uint8_t *p=b; p +#include +#include +#include +#include + +// #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 +#include + +#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); + IPAddress dns(8, 8, 8, 8); + + DEBUG_INFO("Setting up netif"); + Ethernet.begin(ip, nm, gw, dns); + // 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; + } +} + +uint16_t bytes_read=0; +void application() { + bool found = false; + + 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; + + 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; + 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(app.buffer, APP_BUFFER_SIZE); + DEBUG_INFO("buffer sent res: %d", res); + + 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]); + } + break; + } + case APP_STATE_RECEIVE: { + int res = app.tcp_client->read(app.buffer, APP_BUFFER_SIZE); + + 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]); + } + 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>>>>>>"); + for(uint8_t *p=b; p +#include +#include +#include +#include "arduino_secrets.h" +#include +#include + +// #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 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>>>>>>"); + for(uint8_t *p=b; p>>>>>>"); + for(uint8_t *p=b; p +#include +#include +#include + +// #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 00000000..0c9fdd55 --- /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 00000000..fc9eca56 --- /dev/null +++ b/libraries/lwIpWrapper/examples/wifi_bare_lwipc33/wifi_bare_lwipc33.ino @@ -0,0 +1,546 @@ +#include +#include +#include +#include +#include "arduino_secrets.h" +#include + +// #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"); + + DEBUG_INFO("Connecting to AP"); + while(WiFi.begin(SSID, SECRET_PASS) != WL_CONNECTED) { + DEBUG_INFO("Connection failed retry"); + 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 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>>>>>>"); + for(uint8_t *p=b; p>>>>>>"); + for(uint8_t *p=b; p +#include "utils.h" -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 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 -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; +extern "C" void dhcps_start(struct netif *netif); -FspTimer CLwipIf::timer; +err_t _netif_init(struct netif* ni); +err_t _netif_output(struct netif* ni, struct pbuf* p); -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; +#if LWIP_DNS +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 // LWIP_USE_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; + +static void zerocopy_pbuf_mem_free(struct pbuf *p) { + // SYS_ARCH_DECL_PROTECT(zerocopy_pbuf_free); + zerocopy_pbuf_t* zcpbuf = (zerocopy_pbuf_t*) p; + + // 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); } -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,19 +79,12 @@ 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 uint8_t type = 8; int8_t ch = FspTimer::get_available_timer(type); @@ -70,6 +94,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: @@ -84,1465 +109,1049 @@ 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(); - } +#ifdef LWIP_USE_TIMER +static void timer_cb(timer_callback_args_t* arg) { + CLwipIf* context = (CLwipIf*)arg->p_context; - if (net_ifs[NI_WIFI_STATION] != nullptr) { - net_ifs[NI_WIFI_STATION]->task(); - } + context->task(); +} +#endif - if (net_ifs[NI_WIFI_SOFTAP] != nullptr) { - net_ifs[NI_WIFI_SOFTAP]->task(); +void CLwipIf::task() { + for(CNetIf* iface: this->ifaces) { + iface->task(); } - /* Handle LwIP timeouts */ sys_check_timeouts(); - - if (willing_to_start_sync_req) { - timer.disable_overflow_irq(); - willing_to_start_sync_req = false; - async_requests_ongoing = false; - } } -/* -------------------------------------------------------------------------- */ -/* 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 + + netif_set_default(&iface->ni); } -/* -------------------------------------------------------------------------- */ -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; - } +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); + +#ifdef LWIP_USE_TIMER + timer.setup_overflow_irq(); + timer.open(); + timer.start(); +#endif } + + // add the interface if not already present in the vector + this->ifaces.push_back(iface); } -/* -------------------------------------------------------------------------- */ -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(); - } - } - return ESP_CONTROL_OK; +CLwipIf::~CLwipIf() { } +/* *************************************************************************** + * DNS related functions + * ****************************************************************************/ + +#if LWIP_DNS + +struct dns_callback { + std::function cbk; +}; -/* -------------------------------------------------------------------------- */ -int CLwipIf::initEventCb(CCtrlMsgWrapper *resp) { - (void)resp; - CLwipIf::wifi_hw_initialized = true; - return ESP_CONTROL_OK; -} +static void _getHostByNameCBK(const char *name, const ip_addr_t *ipaddr, void *callback_arg) { + dns_callback* cbk = (dns_callback*)callback_arg; + cbk->cbk(toArduinoIP(ipaddr)); -/* -------------------------------------------------------------------------- */ -int CLwipIf::setWifiMode(WifiMode_t mode) { -/* -------------------------------------------------------------------------- */ - CLwipIf::getInstance().startSyncRequest(); - int rv = CEspControl::getInstance().setWifiMode(mode); - CLwipIf::getInstance().restartAsyncRequest(); - return rv; + delete cbk; } -/* -------------------------------------------------------------------------- */ -bool CLwipIf::initWifiHw(bool asStation) -{ - /* -------------------------------------------------------------------------- */ - bool rv = true; +// 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 - if (!CLwipIf::wifi_hw_initialized) { + for(priority=0; + priority= DNS_MAX_SERVERS) { + // unable to add another dns server, because priority is more than the dns server available space + return -1; + } - 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++; - } + ip_addr_t ip = fromArduinoIP(aDNSServer); - if (asStation) { - int res = CLwipIf::getInstance().setWifiMode(WIFI_MODE_STA); + dns_setserver(priority, &ip); +} - if (res == ESP_CONTROL_OK) { - CLwipIf::getInstance().scanForAp(); - } - } else { - CEspControl::getInstance().setWifiMode(WIFI_MODE_AP); - } - } +void CLwipIf::clearDnsServers() { + for(uint8_t i=0; i= 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; - - 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; - } - } +// 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 + */ + volatile bool completed = false; + + uint8_t res = this->getHostByName(aHostname, [&aResult, &completed](const IPAddress& ip){ + aResult = ip; + completed = true; + }); + + 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(); } - rv = net_ifs[type]; +#endif // LWIP_USE_TIMER } - return rv; + + return res == 1 ? 0 : res; } -/* -------------------------------------------------------------------------- */ -void CEth::handleEthRx() -{ +// TODO instead of returning int return an enum value +int CLwipIf::getHostByName(const char* aHostname, std::function cbk) { /* - * 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 + * 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 */ - __disable_irq(); + 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); + + switch(err) { + case ERR_OK: + // the address was already present in the local cache + cbk(toArduinoIP(&addr)); + 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 res; +} +#endif + +/* ########################################################################## + * BASE NETWORK INTERFACE CLASS + * ########################################################################## */ + +CNetIf::CNetIf(NetworkDriver *driver) +: driver(driver) +#ifdef LWIP_DHCP + , dhcp_acquired(false) +#endif +{ + // NETIF_STATS_INIT(this->stats); - 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; + if(driver != nullptr) { + // 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)); + } +} - p = pbuf_alloc(PBUF_RAW, rx_frame_dim, PBUF_RAM); +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 - if (p != NULL) { - /* Copy ethernet frame into pbuf */ - pbuf_take((struct pbuf*)p, (uint8_t*)rx_frame_buf, (uint32_t)rx_frame_dim); + if(driver != nullptr) { + driver->begin(); + } - if (ni.input((struct pbuf*)p, &ni) != ERR_OK) { - pbuf_free((struct pbuf*)p); - } + ip_addr_t _ip = fromArduinoIP(ip); + ip_addr_t _nm = fromArduinoIP(nm); + ip_addr_t _gw = fromArduinoIP(gw); + + 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); } - eth_release_rx_buffer(); + // TODO check for any changes detected and update the iface } - __enable_irq(); + 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().syncTimer(); + while(!this->isDhcpAcquired()) { + CLwipIf::getInstance().task(); + } + CLwipIf::getInstance().enableTimer(); + } + +#endif + + return this->isDhcpAcquired()? 1 : 0; } -/* -------------------------------------------------------------------------- */ -err_t CEth::init(struct netif* _ni) -{ - /* -------------------------------------------------------------------------- */ -#if LWIP_NETIF_HOSTNAME - /* Initialize interface hostname */ - _ni->hostname = "C33-onEth"; -#endif /* LWIP_NETIF_HOSTNAME */ +void CNetIf::task() { +#ifdef LWIP_DHCP + // TODO add timeout + if(!this->dhcp_acquired && dhcp_supplied_address(&this->ni)) { + dhcp_acquired = true; + } + +#endif + if(driver != nullptr) { + driver->poll(); + } +} - _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); +err_t _netif_init(struct netif* ni) { + CNetIf *iface = (CNetIf*)ni->state; - /* maximum transfer unit */ - _ni->mtu = 1500; + return iface->init(ni); // This function call can be a jmp instruction +} - /* device capabilities */ - /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ - _ni->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; +err_t _netif_output(struct netif* ni, struct pbuf* p) { + CNetIf *iface = (CNetIf*)ni->state; - return ERR_OK; + return iface->output(ni, p); // This function call can be a jmp instruction } -/* -------------------------------------------------------------------------- */ -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. - */ - (void)_ni; +void CNetIf::up() { + netif_set_up(&this->ni); +} - err_t errval = ERR_OK; +void CNetIf::down() { + netif_set_down(&this->ni); +} - if(eth_output_can_transimit()) { - uint16_t tx_buf_dim = p->tot_len; +void CNetIf::setLinkUp() { + this->up(); +} - // 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) { +void CNetIf::setLinkDown() { + this->down(); +} - uint16_t bytes_actually_copied = pbuf_copy_partial(p, tx_buf, p->tot_len, 0); +void CNetIf::linkUpCallback() { + netif_set_link_up(&this->ni); +} - if (bytes_actually_copied > 0 && !eth_output(tx_buf, bytes_actually_copied)) { - errval = ERR_IF; - } - } else { - errval = ERR_MEM; - } - } else { - errval = ERR_INPROGRESS; - } - return errval; +void CNetIf::linkDownCallback() { + netif_set_link_down(&this->ni); } -/* -------------------------------------------------------------------------- */ -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(); - } - -#ifdef DEBUG_OUTPUT_DISABLED - Serial.println("Bytes LWIP wants to send: "); - - for (int i = 0; i < bytes_actually_copied; i++) { - Serial.print(buf[i], HEX); - Serial.print(" "); - } - Serial.println(); +/* ########################################################################## + * DHCP related functions + * ########################################################################## */ + +void CNetIf::config(IPAddress _ip, IPAddress _gw, IPAddress _nm) { +#ifdef LWIP_DHCP + dhcpStop(); + dhcpStart(); + dhcpNotUsed(); #endif - if (CEspControl::getInstance().sendBuffer(ESP_STA_IF, ifn, buf, bytes_actually_copied) == ESP_CONTROL_OK) { - errval = ERR_OK; - } - } - delete[] buf; + 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); } +} - return errval; + +#ifdef LWIP_DHCP + +void CNetIf::dhcpNotUsed() { + dhcp_inform(&this->ni); + dhcp_acquired = true; } -/* -------------------------------------------------------------------------- */ -err_t CWifiStation::init(struct netif* _ni) -{ - /* -------------------------------------------------------------------------- */ -#if LWIP_NETIF_HOSTNAME - /* Initialize interface hostname */ - _ni->hostname = "C33-WifiSta"; -#endif /* LWIP_NETIF_HOSTNAME */ +bool CNetIf::isDhcpAcquired() { + // if(dhcp_acquired) { + // Serial.println(ip_2_ip4(ni.ip_addr).addr, HEX); + // } + return dhcp_acquired; +} - _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; +bool CNetIf::dhcpStart() { + return dhcp_start(&this->ni) == ERR_OK; +} - /* maximum transfer unit */ - _ni->mtu = 1500; +void CNetIf::dhcpStop() { + this->dhcpRelease(); + dhcp_stop(&this->ni); +} +bool CNetIf::dhcpRelease() { + return dhcp_release(&this->ni) == ERR_OK; +} - /* device capabilities */ - /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ - _ni->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; +bool CNetIf::dhcpRenew() { + return dhcp_renew(&this->ni) == ERR_OK; +} - /* set MAC hardware address */ - _ni->hwaddr_len = CLwipIf::getInstance().getMacAddress(NI_WIFI_STATION, _ni->hwaddr); +#endif - return ERR_OK; +IPAddress CNetIf::dnsServerIP() { +#if LWIP_DNS + return CLwipIf::getInstance().getDns(0); +#else + return INADDR_NONE; +#endif } -/* -------------------------------------------------------------------------- */ -err_t CWifiSoftAp::output(struct netif* _ni, struct pbuf* p) -{ - /* -------------------------------------------------------------------------- */ - (void)_ni; - err_t errval = ERR_IF; +/* ########################################################################## + * ETHERNET NETWORK INTERFACE CLASS + * ########################################################################## */ +const char CEth::eth_ifname[] = "en"; - uint8_t* buf = new uint8_t[p->tot_len]; +CEth::CEth(NetworkDriver *driver) +: CNetIf(driver) { + // driver.stats = &this->stats; +} - 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(); - } +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, + this, std::placeholders:: _1, std::placeholders::_2)); - if (CEspControl::getInstance().sendBuffer(ESP_AP_IF, ifn, buf, bytes_actually_copied) == ESP_CONTROL_OK) { - errval = ERR_OK; - } - } - delete[] buf; - } + // Call the begin function on the Parent class to init the interface + // netif_set_link_up(&this->ni); + auto res = CNetIf::begin(ip, nm, gw); - return errval; + CLwipIf::getInstance().addDnsServer(dns); + + return res; } -/* -------------------------------------------------------------------------- */ -err_t CWifiSoftAp::init(struct netif* _ni) -{ - /* -------------------------------------------------------------------------- */ -#if LWIP_NETIF_HOSTNAME - /* Initialize interface hostname */ - _ni->hostname = "C33-WifiSta"; -#endif /* LWIP_NETIF_HOSTNAME */ +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) { - _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; + this->setMacAddress(mac_address); - /* maximum transfer unit */ - _ni->mtu = 1500; + return this->begin(local_ip, subnet, gateway, dns_server); +} - /* device capabilities */ - /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ - _ni->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; +int CEth::begin( + uint8_t *mac_address, + const unsigned long timeout, + const unsigned long responseTimeout) { - /* set MAC hardware address */ - _ni->hwaddr_len = CLwipIf::getInstance().getMacAddress(NI_WIFI_SOFTAP, _ni->hwaddr); + this->setMacAddress(mac_address); - return ERR_OK; + return this->begin(); } -/* -------------------------------------------------------------------------- */ -bool CLwipIf::setMacAddress(NetIfType_t type, uint8_t* mac) -{ - /* -------------------------------------------------------------------------- */ - CLwipIf::getInstance().startSyncRequest(); - WifiMac_t MAC; - CNetUtilities::macArray2macStr(MAC.mac, mac); +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[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; - if (type == NI_WIFI_STATION) { - MAC.mode = WIFI_MODE_STA; - if (CEspControl::getInstance().setWifiMacAddress(MAC) != ESP_CONTROL_OK) { - return false; - } + memcpy(ni->hwaddr, this->driver->getMacAddress(), 6); // FIXME handle this using a constant + ni->hwaddr_len = 6; - } 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); - } + ni->output = etharp_output; + ni->linkoutput = _netif_output; - CLwipIf::getInstance().restartAsyncRequest(); - return true; + return ERR_OK; } -/* -------------------------------------------------------------------------- */ -int CLwipIf::getMacAddress(NetIfType_t type, uint8_t* mac) -{ - /* -------------------------------------------------------------------------- */ - int rv = 0; - WifiMac_t MAC; - - CLwipIf::getInstance().startSyncRequest(); +err_t CEth::output(struct netif* ni, struct pbuf* p) { + err_t errval = ERR_OK; - 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; + /* 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 = 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); + errval = ERR_IF; + break; } - } else { - eth_get_mac_address(mac); - rv = MAC_ADDRESS_DIM; - } + // NETIF_STATS_INCREMENT_TX_BYTES(this->stats, q->len); + // NETIF_STATS_TX_TIME_AVERAGE(this->stats); + q = q->next; + } while(q != nullptr && errval != ERR_OK); - CLwipIf::getInstance().restartAsyncRequest(); - return rv; + return errval; } -/* -------------------------------------------------------------------------- */ -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; +void CEth::consume_callback(uint8_t* buffer, uint32_t len) { + // TODO understand if this callback can be moved into the base class + + const uint16_t trimmed_size = len; + + // zerocopy_pbuf_t *custom_pbuf = get_zerocopy_pbuf(buffer, 1536); + zerocopy_pbuf_t *custom_pbuf = get_zerocopy_pbuf(buffer, trimmed_size); + + // mem_trim should be passed as an argument, since it depends on the kind of allocation performed + void* buf = mem_trim(buffer, trimmed_size); + + // 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); + + 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 { - wifi_status = WL_NO_SSID_AVAIL; + // NETIF_STATS_INCREMENT_RX_BYTES(this->stats, p->len); } - return res; } -/* -------------------------------------------------------------------------- */ -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; - } - return nullptr; -} +/* ########################################################################## */ +/* CWifiStation NETWORK INTERFACE CLASS */ +/* ########################################################################## */ +const char CWifiStation::wifistation_ifname[] = "ws"; -/* -------------------------------------------------------------------------- */ -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; +CWifiStation::CWifiStation() +: hw_init(false) { } -/* -------------------------------------------------------------------------- */ -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; -} +CWifiStation::~CWifiStation() { -/* -------------------------------------------------------------------------- */ -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; } -/* -------------------------------------------------------------------------- */ -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; +int CWifiStation::begin(const IPAddress &ip, const IPAddress &nm, const IPAddress &gw) { + return CNetIf::begin(ip, nm, gw); } -/* -------------------------------------------------------------------------- */ -int CLwipIf::connectToAp(const char* ssid, const char* pwd) -{ - /* -------------------------------------------------------------------------- */ + +int CWifiStation::connectToAP(const char* ssid, const char *passphrase) { WifiApCfg_t ap; - int rv = ESP_CONTROL_CTRL_ERROR; + int rv = ESP_CONTROL_CTRL_ERROR; // FIXME this should be set with an error meaning AP not found 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; + 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 { + // Serial.println("init"); + this->hw_init = true; + return ESP_CONTROL_OK; + }); + + 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(); + R_BSP_SoftwareDelay(100, BSP_DELAY_UNITS_MILLISECONDS); + time_num++; + } + + CLwipIf::getInstance().syncTimer(); + rv = CEspControl::getInstance().setWifiMode(WIFI_MODE_STA); + CLwipIf::getInstance().enableTimer(); + + if((rv=this->scanForAp()) != WL_SCAN_COMPLETED) { + rv = -2; + goto exit; + } + + // 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) { + strncpy((char*)ap.ssid, ssid, SSID_LENGTH); - 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); + if(passphrase != nullptr) { + auto slen = strlen(passphrase)+1; + strncpy((char*)ap.pwd, passphrase, (slen < PASSWORD_LENGTH) ? slen : PASSWORD_LENGTH); + } else { + ap.pwd[0] = '\0'; } + memset(ap.bssid, 0x00, BSSID_LENGTH); - memcpy(ap.bssid, access_points[index].bssid, BSSID_LENGTH); + memcpy(ap.bssid, access_points[best_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; + CLwipIf::getInstance().syncTimer(); + rv=CEspControl::getInstance().connectAccessPoint(ap); + + if (rv == ESP_CONTROL_OK) { CEspControl::getInstance().getAccessPointConfig(access_point_cfg); + wifi_status = WL_CONNECTED; - 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; -} - -/* -------------------------------------------------------------------------- */ -const char* CLwipIf::getSSID() -{ - /* -------------------------------------------------------------------------- */ - return (const char*)access_point_cfg.ssid; -} -/* -------------------------------------------------------------------------- */ -uint8_t* CLwipIf::getBSSID(uint8_t* bssid) -{ - /* -------------------------------------------------------------------------- */ - CNetUtilities::macStr2macArray(bssid, (const char*)access_point_cfg.bssid); - return bssid; -} + netif_set_link_up(&this->ni); + } else { + wifi_status = WL_CONNECT_FAILED; + } + CLwipIf::getInstance().enableTimer(); + } -/* -------------------------------------------------------------------------- */ -uint32_t CLwipIf::getRSSI() -{ - /* -------------------------------------------------------------------------- */ - return (uint32_t)access_point_cfg.rssi; +exit: + return rv; } -/* -------------------------------------------------------------------------- */ -uint8_t CLwipIf::getEncrType() -{ - /* -------------------------------------------------------------------------- */ - return Encr2wl_enc(access_point_cfg.encryption_mode); -} +int CWifiStation::scanForAp() { + access_points.clear(); -/* -------------------------------------------------------------------------- */ -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; -} + CLwipIf::getInstance().syncTimer(); -/* -------------------------------------------------------------------------- */ -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; + int res = CEspControl::getInstance().getAccessPointScanList(access_points); + CLwipIf::getInstance().enableTimer(); - 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(); - } + if (res == ESP_CONTROL_OK) { + wifi_status = WL_SCAN_COMPLETED; } else { - wifi_status = WL_AP_FAILED; + wifi_status = WL_NO_SSID_AVAIL; } - CLwipIf::getInstance().restartAsyncRequest(); - return rv; -} -/* -------------------------------------------------------------------------- */ -int CLwipIf::setLowPowerMode() -{ - /* -------------------------------------------------------------------------- */ - CLwipIf::getInstance().startSyncRequest(); - int rv = CEspControl::getInstance().setPowerSaveMode(1); - CLwipIf::getInstance().restartAsyncRequest(); - return rv; + return wifi_status; } -/* -------------------------------------------------------------------------- */ -int CLwipIf::resetLowPowerMode() -{ - /* -------------------------------------------------------------------------- */ - CLwipIf::getInstance().startSyncRequest(); - int rv = CEspControl::getInstance().setPowerSaveMode(0); - CLwipIf::getInstance().restartAsyncRequest(); - return rv; -} +// disconnect +int CWifiStation::disconnectFromAp() { + CLwipIf::getInstance().syncTimer(); -#ifdef LWIP_USE_TIMER -/* -------------------------------------------------------------------------- */ -void CLwipIf::timer_cb(timer_callback_args_t *arg) { -/* -------------------------------------------------------------------------- */ - (void)arg; - CLwipIf::getInstance().lwip_task(); -} -#endif + auto res = CEspControl::getInstance().disconnectAccessPoint(); -/* *************************************************************************** - * DNS related functions - * ****************************************************************************/ + CLwipIf::getInstance().enableTimer(); -/* -------------------------------------------------------------------------- */ -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 res; } -/* -------------------------------------------------------------------------- */ -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; - - *ipaddr = 0; - err = dns_gethostbyname(hostname, &iphost, &dns_callback, ipaddr); - - switch (err) { - case ERR_OK: - *ipaddr = ip4_addr_get_u32(&iphost); - ret = 1; - break; - - case ERR_INPROGRESS: - dns_request_sent = millis(); - while (*ipaddr == 0) { - lwip_task(); - if ((millis() - dns_request_sent) >= TIMEOUT_DNS_REQUEST) { - ret = -1; - break; - } - } - - if (ret == 0) { - if (*ipaddr == 0) { - ret = -2; - } else { - ret = 1; - } - } - break; +err_t CWifiStation::init(struct netif* ni) { + // Setting up netif +#if LWIP_NETIF_HOSTNAME + ni->hostname = "C33-WifiSta"; +#endif + 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; - case ERR_ARG: - ret = -4; - break; + 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); - default: - ret = -4; - break; - } + ni->output = etharp_output; + ni->linkoutput = _netif_output; - return ret; + return ERR_OK; } -/* -------------------------------------------------------------------------- */ -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; - } - - if (getDns(0) == IPAddress(0, 0, 0, 0)) { - return INVALID_SERVER; - } +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; -#if LWIP_DNS - ret = get_ip_address_from_hostname(aHostname, &ipResult); - aResult = IPAddress(ipResult); -#endif - return ret; -} + // NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats); + // NETIF_STATS_TX_TIME_START(this->stats); -/* -------------------------------------------------------------------------- */ -int CLwipIf::inet2aton(const char* address, IPAddress& result) -{ - /* -------------------------------------------------------------------------- */ - uint16_t acc = 0; // Accumulator - uint8_t dots = 0; + // 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; + } - if (address == NULL) { - return 0; + // copy the content of pbuf + assert(pbuf_copy_partial(p, buf, size, 0) == size); + } else { + buf = (uint8_t*)p->payload; } - 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; - } else { - // Invalid char - return 0; - } + // 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 { + // NETIF_STATS_INCREMENT_ERROR(this->stats, err); + // NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats); } - if (dots != 3) { - // Too few dots (there must be 3 dots) - return 0; +exit: + if(p->next != nullptr && buf != nullptr) { + free(buf); } - result[3] = acc; - return 1; + return errval; } -/* -------------------------------------------------------------------------- */ -void CLwipIf::beginDns(IPAddress aDNSServer) -{ - /* -------------------------------------------------------------------------- */ - addDns(aDNSServer); -} +void CWifiStation::task() { + // calling the base class task, in order to make thigs work + CNetIf::task(); + uint8_t if_num = 0; + uint16_t dim = 0; + uint8_t* buffer = nullptr; + struct pbuf* p = nullptr; + // NETIF_STATS_RX_TIME_START(this->stats); + // TODO do not perform this when not connected to an AP + if(hw_init) { + CEspControl::getInstance().communicateWithEsp(); -/* -------------------------------------------------------------------------- */ -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; - } - } + // TODO handling buffer this way may be harmful for the memory + buffer = CEspControl::getInstance().getStationRx(if_num, dim); } - if (!dhcp_started) { - dns_init(); - IP_ADDR4(&ip, aDNSServer[0], aDNSServer[1], aDNSServer[2], aDNSServer[3]); - dns_setserver(dns_num, &ip); - } -#endif -} + // empty the ESP32 queue + while(buffer != nullptr) { + // FIXME this section is redundant and should be generalized toghether with CEth::consume_callback + // NETIF_STATS_INCREMENT_RX_INTERRUPT_CALLS(this->stats); -/* -------------------------------------------------------------------------- */ -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 -} + 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); + } -/* -------------------------------------------------------------------------- */ -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; + buffer = CEspControl::getInstance().getStationRx(if_num, dim); } + // NETIF_STATS_RX_TIME_AVERAGE(this->stats); } -/* -------------------------------------------------------------------------- */ -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; - } else { - return nullptr; - } -} +// void CWifiStation::consume_callback(uint8_t* buffer, uint32_t len) { +// // FIXME take what is written in task and put it in here +// } -/* -------------------------------------------------------------------------- */ -int32_t CLwipIf::getRSSI(NetIfType_t type) -{ - /* -------------------------------------------------------------------------- */ - if (type == NI_WIFI_STATION) { - return access_point_cfg.rssi; - } else { - return 0; - } +const char* CWifiStation::getSSID() { + return (const char*)access_point_cfg.ssid; } -/* -------------------------------------------------------------------------- */ -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; - } +uint8_t* CWifiStation::getBSSID(uint8_t* bssid){ + CNetUtilities::macStr2macArray(bssid, (const char*)access_point_cfg.bssid); + return bssid; } -/* ########################################################################## */ -/* 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 +int32_t CWifiStation::getRSSI() { + // TODO update the rssi on request of this method + return (uint32_t)access_point_cfg.rssi; } -/* -------------------------------------------------------------------------- */ -CNetIf::~CNetIf() -{ - /* -------------------------------------------------------------------------- */ -#ifdef CNETWORK_INTERFACE_DEBUG - Serial.println("[CNET]: CNetIf destructor"); -#endif +uint8_t CWifiStation::getEncryptionType() { + return Encr2wl_enc(access_point_cfg.encryption_mode); } -/* *************************************************************************** - * DHCP related functions - * ****************************************************************************/ +// int CWifiStation::getMacAddress(uint8_t* mac) { +// } -/* -------------------------------------------------------------------------- */ -void CNetIf::DhcpNotUsed() -{ - /* -------------------------------------------------------------------------- */ - DhcpStop(); - dhcp_inform(getNi()); +uint8_t CWifiStation::getChannel() { + return (uint8_t)access_point_cfg.channel; } -/* -------------------------------------------------------------------------- */ -int CNetIf::checkLease() +const char* CWifiStation::getSSID(uint8_t i) { - /* -------------------------------------------------------------------------- */ - int rc = DHCP_CHECK_NONE; - - task(); - rc = dhcp_get_lease_state(); - - if (rc != _dhcp_lease_state) { - switch (_dhcp_lease_state) { - case DHCP_CHECK_NONE: - _dhcp_lease_state = rc; - rc = DHCP_CHECK_NONE; - break; - - 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; - - 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; - - default: - _dhcp_lease_state = DHCP_CHECK_NONE; - break; - } + if (access_points.size() > 0 && i < access_points.size()) { + return (const char*)access_points[i].ssid; } - - return rc; + return nullptr; } -/* -------------------------------------------------------------------------- */ -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; +int32_t CWifiStation::getRSSI(uint8_t i) { + if (access_points.size() > 0 && i < access_points.size()) { + return (int32_t)access_points[i].rssi; } - return res; -} - -/* -------------------------------------------------------------------------- */ -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; + return 0; } -/* -------------------------------------------------------------------------- */ -void CNetIf::dhcp_reset() -{ - /* -------------------------------------------------------------------------- */ - /* it resets the DHCP status to IDLE */ - while (dhcp_st != DHCP_IDLE_STATUS) { - task(); +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; } -/* -------------------------------------------------------------------------- */ -void CNetIf::DhcpSetTimeout(unsigned long t) -{ - /* -------------------------------------------------------------------------- */ - dhcp_timeout = t; -} - -/* -------------------------------------------------------------------------- */ -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; +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; } - dhcp_reset(); + return nullptr; } -/* -------------------------------------------------------------------------- */ -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::getChannel(uint8_t i) { + if (access_points.size() > 0 && i < access_points.size()) { + return (uint8_t)access_points[i].channel; } + return 0; } -/* -------------------------------------------------------------------------- */ -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::setLowPowerMode() { + return CEspControl::getInstance().setPowerSaveMode(1); } -/* -------------------------------------------------------------------------- */ -void CNetIf::setLinkDown() -{ - /* -------------------------------------------------------------------------- */ - netif_set_link_down(&ni); - /* When the netif is fully configured this function must be called.*/ - netif_set_down(&ni); +int CWifiStation::resetLowPowerMode() { + return CEspControl::getInstance().setPowerSaveMode(1); } /* ########################################################################## */ -/* 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]); +const char CWifiSoftAp::softap_ifname[] = "sa"; - netif_add(&ni, &ip, &nm, &gw, NULL, CEth::init, ethernet_input); - netif_set_default(&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"); - 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); - } +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(ð0if, 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() -{ - /* -------------------------------------------------------------------------- */ +CWifiSoftAp::~CWifiSoftAp() { } + - eth_execute_link_process(); +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; -#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(); + CEspControl::getInstance().listenForInitEvent([this] (CCtrlMsgWrapper *resp) -> int { + // Serial.println("init"); + this->hw_init = true; + return ESP_CONTROL_OK; + }); + + if ((res=CEspControl::getInstance().initSpiDriver()) != 0 && !hw_init) { + // res = -1; // FIXME put a proper error code + goto exit; } -#endif + + 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++; + } + + CLwipIf::getInstance().syncTimer(); + res = CEspControl::getInstance().setWifiMode(WIFI_MODE_AP); + CLwipIf::getInstance().enableTimer(); + + CNetIf::begin( + default_dhcp_server_ip, + default_nm, + default_dhcp_server_ip + ); +exit: + return res; } -/* ########################################################################## */ -/* CWifiStation NETWORK INTERFACE CLASS */ -/* ########################################################################## */ +// 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().syncTimer(); + SoftApCfg_t cfg; -CWifiStation::CWifiStation() { } -CWifiStation::~CWifiStation() { } + strncpy((char*)cfg.ssid, ssid, SSID_LENGTH); -void CWifiStation::begin(IPAddress _ip, IPAddress _gw, IPAddress _nm) -{ - if (_ip == INADDR_NONE) { - _ip = default_ip; - _nm = default_nm; - _gw = default_gw; + if (passphrase == nullptr) { + cfg.pwd[0] = '\0'; + cfg.encryption_mode = WIFI_AUTH_OPEN; + } else { + auto slen = strlen(passphrase)+1; + strncpy((char*)cfg.pwd, passphrase, (slen < PASSWORD_LENGTH) ? slen : PASSWORD_LENGTH); + + cfg.encryption_mode = WIFI_AUTH_WPA_WPA2_PSK; } - 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, CWifiStation::init, ethernet_input); - netif_set_default(&ni); + channel = (channel == 0) ? 1 : channel; + cfg.channel = (channel > MAX_CHNL_NO) ? MAX_CHNL_NO : channel; + cfg.max_connections = MAX_SOFAT_CONNECTION_DEF; // FIXME make user decide this parameter + cfg.bandwidth = WIFI_BW_HT40; + cfg.ssid_hidden = false; - if (netif_is_link_up(&ni)) { - /* When the netif is fully configured this function must be called */ - netif_set_up(&ni); + 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); + + // FIXME the dhcp server should be started somewhere else + dhcps_start(&(this->ni)); } else { - /* When the netif link is down this function must be called */ - netif_set_down(&ni); + // wifi_status = WL_AP_FAILED; } -#if LWIP_NETIF_LINK_CALLBACK - /* Set the link callback function, this function is called on change of link status */ - // netif_set_link_callback(ð0if, eht0if_link_toggle_cbk); -#endif /* LWIP_NETIF_LINK_CALLBACK */ + CLwipIf::getInstance().enableTimer(); + return rv; } -/* -------------------------------------------------------------------------- */ -void CWifiStation::task() -{ - /* -------------------------------------------------------------------------- */ - /* get messages and process it */ - uint8_t if_num; - uint16_t dim = 0; - uint8_t* buf = nullptr; - struct pbuf* p = nullptr; +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[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; - /* 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); + 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 + ni->output = etharp_output; + ni->linkoutput = _netif_output; -#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 ERR_OK; } -/* -------------------------------------------------------------------------- */ -int CWifiStation::getMacAddress(uint8_t* mac) { -/* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getMacAddress(NI_WIFI_STATION, mac); -} +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; -/* -------------------------------------------------------------------------- */ -const char* CWifiStation::getSSID() -{ - /* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getSSID(NI_WIFI_STATION); -} + // NETIF_STATS_INCREMENT_TX_TRANSMIT_CALLS(this->stats); + // NETIF_STATS_TX_TIME_START(this->stats); -/* -------------------------------------------------------------------------- */ -uint8_t* CWifiStation::getBSSID(uint8_t* bssid) -{ - /* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getBSSID(NI_WIFI_STATION, bssid); -} + // 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; + } -/* -------------------------------------------------------------------------- */ -int32_t CWifiStation::getRSSI() -{ - /* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getRSSI(NI_WIFI_STATION); -} + // copy the content of pbuf + assert(pbuf_copy_partial(p, buf, size, 0) == size); + } else { + buf = (uint8_t*)p->payload; + } -/* -------------------------------------------------------------------------- */ -uint8_t CWifiStation::getEncryptionType() -{ - /* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getEncryptionType(NI_WIFI_STATION); + // 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 { + // NETIF_STATS_INCREMENT_ERROR(this->stats, err); + // NETIF_STATS_INCREMENT_TX_TRANSMIT_FAILED_CALLS(this->stats); + } + +exit: + if(p->next != nullptr && buf != nullptr) { + free(buf); + } + return errval; } -/* ########################################################################## */ -/* CWifiSoftAp NETWORK INTERFACE CLASS */ -/* ########################################################################## */ +void CWifiSoftAp::task() { + // calling the base class task, in order to make thigs work + CNetIf::task(); -CWifiSoftAp::CWifiSoftAp() { } -CWifiSoftAp::~CWifiSoftAp() { } + // TODO in order to make things easier this should be implemented inside of Wifi driver + // and not override LWIPInterface method -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; - } - 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]); + uint8_t if_num = 0; + uint16_t dim = 0; + uint8_t* buffer = nullptr; + struct pbuf* p = nullptr; - 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); - } else { - /* When the netif link is down this function must be called */ - netif_set_down(&ni); + // NETIF_STATS_RX_TIME_START(this->stats); + // TODO do not perform this when not connected to an AP + if(hw_init) { + CEspControl::getInstance().communicateWithEsp(); + + // TODO handling buffer this way may be harmful for the memory + buffer = CEspControl::getInstance().getSoftApRx(if_num, dim); } -#if LWIP_NETIF_LINK_CALLBACK - /* Set the link callback function, this function is called on change of link status */ - // netif_set_link_callback(ð0if, eht0if_link_toggle_cbk); -#endif /* LWIP_NETIF_LINK_CALLBACK */ -} + // 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); -/* -------------------------------------------------------------------------- */ -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 { + zerocopy_pbuf_t *custom_pbuf = get_zerocopy_pbuf(buffer, dim, free); - buf = CEspControl::getInstance().getSoftApRx(if_num, dim); + // 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); - 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; + err_t err = this->ni.input((struct pbuf*)p, &this->ni); + if (err != ERR_OK) { + // NETIF_STATS_INCREMENT_ERROR(this->stats, err); - if (ni.input(p, &ni) != ERR_OK) { - pbuf_free(p); - } - } + // 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); } - } while(buf != nullptr); -#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(); + buffer = CEspControl::getInstance().getStationRx(if_num, dim); } -#endif + // NETIF_STATS_RX_TIME_AVERAGE(this->stats); } -/* -------------------------------------------------------------------------- */ -int CWifiSoftAp::getMacAddress(uint8_t* mac) -{ - /* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getMacAddress(NI_WIFI_SOFTAP, mac); +const char* CWifiSoftAp::getSSID() { + return (const char*)soft_ap_cfg.ssid; } -/* -------------------------------------------------------------------------- */ -const char* CWifiSoftAp::getSSID() -{ - /* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getSSID(NI_WIFI_SOFTAP); +uint8_t* CWifiSoftAp::getBSSID(uint8_t* bssid){ + // CNetUtilities::macStr2macArray(bssid, (const char*)soft_ap_cfg.bssid); + // return bssid; } -/* -------------------------------------------------------------------------- */ -uint8_t* CWifiSoftAp::getBSSID(uint8_t* bssid) -{ - /* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getBSSID(NI_WIFI_SOFTAP, bssid); +uint8_t CWifiSoftAp::getEncryptionType() { + return Encr2wl_enc(soft_ap_cfg.encryption_mode); } -/* -------------------------------------------------------------------------- */ -int32_t CWifiSoftAp::getRSSI() -{ - /* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getRSSI(NI_WIFI_SOFTAP); +uint8_t CWifiSoftAp::getChannel() { + return (uint8_t)soft_ap_cfg.channel; } -/* -------------------------------------------------------------------------- */ -uint8_t CWifiSoftAp::getEncryptionType() -{ - /* -------------------------------------------------------------------------- */ - return CLwipIf::getInstance().getEncryptionType(NI_WIFI_SOFTAP); +int CWifiSoftAp::setLowPowerMode() { + CLwipIf::getInstance().syncTimer(); + auto res = CEspControl::getInstance().setPowerSaveMode(1); + CLwipIf::getInstance().enableTimer(); + + return res; +} + +int CWifiSoftAp::resetLowPowerMode() { + CLwipIf::getInstance().syncTimer(); + auto res = CEspControl::getInstance().setPowerSaveMode(1); + CLwipIf::getInstance().enableTimer(); + + return res; } +/* ########################################################################## + * DEBUG UTILS + * ########################################################################## */ #if DHCPS_DEBUG == 1 char b_dbg[512]; diff --git a/libraries/lwIpWrapper/src/CNetIf.h b/libraries/lwIpWrapper/src/CNetIf.h index 0b805d20..29f37884 100644 --- a/libraries/lwIpWrapper/src/CNetIf.h +++ b/libraries/lwIpWrapper/src/CNetIf.h @@ -1,7 +1,5 @@ -#ifndef _ARDUINO_LWIP_NETIF_H_ -#define _ARDUINO_LWIP_NETIF_H_ +#pragma once -// #define LWIP_USE_TIMER #define UNUSED(x) (void)(x) #define USE_LWIP_AS_LIBRARY @@ -12,6 +10,8 @@ #include "IPAddress.h" #include "EthernetDriver.h" #include +#include + #ifdef USE_LWIP_AS_LIBRARY #include "lwip/include/lwip/dhcp.h" #include "lwip/include/lwip/dns.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, @@ -90,15 +79,21 @@ typedef enum { NI_ETHERNET } NetIfType_t; -#define MAX_CLIENT 32 +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 -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) @@ -111,77 +106,56 @@ using LwipInput_f = err_t (*)(struct pbuf* p, struct netif* inp); #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); +class CLwipIf; /* 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(); +class CNetIf: public NetworkInterface { +public: + 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 int begin( + const IPAddress &ip = INADDR_NONE, + const IPAddress &nm = INADDR_NONE, + const IPAddress &gw = INADDR_NONE); - IPAddress _dnsServerAddress; + /* + * This method performs interface specific tasks (if any) + */ + virtual void task(); -public: - CNetIf(); - virtual ~CNetIf(); +#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(); + + inline int disconnect() { this->down(); return 0; } + + inline LinkStatus linkStatus() { return netif_is_link_up(&ni) ? LinkON : LinkOFF; } + + bool isLinkUp() { return (bool)netif_is_link_up(&ni); } struct netif* getNi() { return ∋ } @@ -189,244 +163,296 @@ 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); - } + // 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(); - /* add */ - virtual void begin(IPAddress _ip, - IPAddress _gw, - IPAddress _nm) - = 0; - virtual void task() = 0; + void config(IPAddress _ip, IPAddress _gw, IPAddress _nm); virtual int getMacAddress(uint8_t* mac) = 0; + virtual int setMacAddress(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 CLwipIf; +protected: + struct netif ni; - friend class CWifi; -}; +#ifdef LWIP_DHCP + volatile bool dhcp_acquired; +#endif -/* -------------------------------------------------------------------------- */ -class CEth : public CNetIf { - /* -------------------------------------------------------------------------- */ -protected: /* * this function is used to initialize the netif structure of lwip */ - static err_t init(struct netif* ni); + 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); + 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(IPAddress _ip, - IPAddress _gw, - IPAddress _nm) override; - virtual void task() override; + CEth(NetworkDriver *driver=nullptr); + // virtual ~CEth(); + virtual int begin( + const IPAddress &ip = INADDR_NONE, + const IPAddress &nm = INADDR_NONE, + 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 getMacAddress(uint8_t* mac) - { - UNUSED(mac); + virtual int setMacAddress(uint8_t* mac) override { + UNUSED(mac); // FIXME not implemented return 1; } - virtual void handleEthRx(); -}; -/* -------------------------------------------------------------------------- */ -class CWifiStation : public CNetIf { - /* -------------------------------------------------------------------------- */ + 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 */ - 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; + + static const char eth_ifname[]; +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 { public: CWifiStation(); virtual ~CWifiStation(); - virtual void begin(IPAddress _ip, - IPAddress _gw, - IPAddress _nm) override; + virtual int 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 int getMacAddress(uint8_t* mac) override { + // FIXME not implemented + } - virtual const char* getSSID() override; - virtual uint8_t* getBSSID(uint8_t* bssid) override; - virtual int32_t getRSSI() override; - virtual uint8_t getEncryptionType() override; -}; + virtual int setMacAddress(uint8_t* mac) override { + UNUSED(mac); // FIXME not implemented + return 1; + } -/* -------------------------------------------------------------------------- */ -class CWifiSoftAp : public CNetIf { - /* -------------------------------------------------------------------------- */ + virtual const char* getSSID(); + virtual uint8_t* getBSSID(uint8_t* bssid); + virtual int32_t getRSSI(); + 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(); + + inline WifiStatus_t status() { + return wifi_status; + } protected: + static const char wifistation_ifname[]; + /* * 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; + +private: + std::vector 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 { public: CWifiSoftAp(); virtual ~CWifiSoftAp(); - virtual void begin(IPAddress _ip, - IPAddress _gw, - IPAddress _nm) override; + 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; - - virtual const char* getSSID() override; - virtual uint8_t* getBSSID(uint8_t* bssid) override; - virtual int32_t getRSSI() override; - virtual uint8_t getEncryptionType() override; -}; + int startSoftAp(const char* ssid, const char* passphrase=nullptr, uint8_t channel=0); + int stopSoftAp(); -/* -------------------------------------------------------------------------- */ -class CLwipIf { - /* -------------------------------------------------------------------------- */ -private: - bool eth_initialized; + virtual int getMacAddress(uint8_t* mac) override { + // FIXME not implemented + } - int dns_num; - bool willing_to_start_sync_req; - bool async_requests_ongoing; + virtual int setMacAddress(uint8_t* mac) override { + UNUSED(mac); // FIXME not implemented + return 1; + } - friend CWifiStation; - friend CWifiSoftAp; - static CNetIf* net_ifs[NETWORK_INTERFACES_MAX_NUM]; - static WifiStatus_t wifi_status; + virtual const char* getSSID(); + virtual uint8_t* getBSSID(uint8_t* bssid); + virtual uint8_t getEncryptionType(); + virtual uint8_t getChannel(); - /* initialize lwIP and timer */ - CLwipIf(); + int setLowPowerMode(); + int resetLowPowerMode(); +protected: + static const char softap_ifname[]; + /* + * this function is used to initialize the netif structure of lwip + */ + err_t init(struct netif* ni); -/* timer */ -#ifdef LWIP_USE_TIMER - static FspTimer timer; - static void timer_cb(timer_callback_args_t* arg); -#endif + /* + * 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 access_points; - WifiApCfg_t access_point_cfg; - SoftApCfg_t soft_ap_cfg; + bool hw_init; // TODO this should be moved to the wifi driver class +}; - 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); - +class CLwipIf { 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); - } - } - } - void restartAsyncRequest() - { - async_requests_ongoing = true; - delay(10); - timer.enable_overflow_irq(); + 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; } - /* -------------- - * DNS functions - * -------------- */ + // 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(); - int getHostByName(const char* aHostname, IPAddress& aResult); - void beginDns(IPAddress aDNSServer); - void addDns(IPAddress aDNSServer); - IPAddress getDns(int _num = 0); + // 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); - /* 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 */ + // function for setting an iface as default + void setDefaultIface(CNetIf* iface); + // TODO get iface method - CNetIf* get(NetIfType_t type, - IPAddress _ip = INADDR_NONE, - IPAddress _gw = INADDR_NONE, - IPAddress _nm = INADDR_NONE); + // 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 queried, -1 means the last + uint8_t addDnsServer(const IPAddress& aDNSServer, int8_t priority=-1); + void clearDnsServers(); - static void ethLinkUp(); - static void ethLinkDown(); + IPAddress getDns(int n); - /* 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 cbk); // callback version +#endif +private: + CLwipIf(); + ~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 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; + friend class CWifiSoftAp; + friend class CWifiStation; +public: +#ifdef LWIP_USE_TIMER + FspTimer timer; + + inline void syncTimer() { + timer.disable_overflow_irq(); + this->task(); + } + + inline void enableTimer() { + timer.enable_overflow_irq(); + } +#else // LWIP_USE_TIMER + inline void syncTimer() { + this->task(); + } - void lwip_task(); + inline void enableTimer() { } +#endif // LWIP_USE_TIMER }; -#endif +extern CEth Ethernet; +extern CWifiStation WiFiStation; +extern CWifiSoftAp WiFiSoftAP; diff --git a/libraries/lwIpWrapper/src/cortex-m33/liblwIP.a b/libraries/lwIpWrapper/src/cortex-m33/liblwIP.a index adaabfb1..33abbd5a 100644 Binary files a/libraries/lwIpWrapper/src/cortex-m33/liblwIP.a and b/libraries/lwIpWrapper/src/cortex-m33/liblwIP.a differ diff --git a/libraries/lwIpWrapper/src/lwipClient.cpp b/libraries/lwIpWrapper/src/lwipClient.cpp index 745f4d50..a2a533ce 100644 --- a/libraries/lwIpWrapper/src/lwipClient.cpp +++ b/libraries/lwIpWrapper/src/lwipClient.cpp @@ -5,260 +5,466 @@ 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" +// 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) +: tcp_info(new tcp_info_t) { + // tcp_info = std::shared_ptr(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) - : _tcp_client(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(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_struct* tcpClient) -{ - _tcp_client = tcpClient; +lwipClient::lwipClient(const lwipClient& c) +: tcp_info(c.tcp_info), _timeout(c._timeout), _ip(c._ip) { } -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -int lwipClient::connect(const char* host, uint16_t port) -{ - /* -------------------------------------------------------------------------- */ +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() { + if(this->tcp_info->state != TCP_CLOSING) { + this->stop(); + } +} + +int lwipClient::connect(const char* host, uint16_t port) { IPAddress remote_addr; - int ret = CLwipIf::getInstance().getHostByName(host, remote_addr); - if (ret == 1) { + int ret = CLwipIf::getInstance().getHostByName(host, remote_addr); // TODO test this + if (ret == 0) { return connect(remote_addr, port); } else { return 0; } } -/* -------------------------------------------------------------------------- */ -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; - if (_tcp_client == NULL) { - return 0; - } + // 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) { + // return ; // TODO find the proper error code + return err; } - /* Creates a new TCP protocol control block */ - _tcp_client->pcb = tcp_new(); + tcp_err(this->tcp_info->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->tcp_info->state = TCP_NONE; + + 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->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 + ); + + while(!connected()) { + CLwipIf::getInstance().task(); } + CLwipIf::getInstance().enableTimer(); - _tcp_client->data.p = NULL; - _tcp_client->data.available = 0; - _tcp_client->state = TCP_NONE; + return err == ERR_OK? 1: -err; +} - 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; +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; } - startTime = millis(); - while (_tcp_client->state == TCP_NONE) { - CLwipIf::getInstance().lwip_task(); - if ((_tcp_client->state == TCP_CLOSING) || ((millis() - startTime) >= _timeout)) { - stop(); - return 0; - } + lwipClient* client = (lwipClient*)arg; + + 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; + } + + 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->tcp_info->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; + + 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; + } + + if (p == NULL) { + // Remote host has closed the connection -> close from our side + this->close_pcb(); + + return ERR_OK; + } + 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 + 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->tcp_info->pbuf_head->next + pbuf_cat(this->tcp_info->pbuf_head, p); + } + + ret_err = ERR_OK; + } + + return ret_err; +} + +size_t lwipClient::write(uint8_t b) { + return write(&b, 1); +} + +size_t lwipClient::write(const uint8_t* buffer, size_t size) { + CLwipIf::getInstance().syncTimer(); + + uint8_t* buffer_cursor = (uint8_t*)buffer; + uint16_t bytes_to_send = 0; 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; - } + 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 + * TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will not be set on last segment sent + */ + 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; + } 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; } - // Force to send data right now! - if (ERR_OK != tcp_output(_tcp_client->pcb)) { - return 0; + // FIXME blocking call + while(tcp_sndbuf(this->tcp_info->pcb) == 0 && buffer_cursor - buffer < size) { + CLwipIf::getInstance().task(); } - CLwipIf::getInstance().lwip_task(); - } while (bytes_sent != size); + } while(buffer_cursor - buffer < size); - return size; -} + tcp_output(this->tcp_info->pcb); -/* -------------------------------------------------------------------------- */ -int lwipClient::available() -{ - /* -------------------------------------------------------------------------- */ - CLwipIf::getInstance().lwip_task(); - if (_tcp_client != NULL) { - return _tcp_client->data.available; - } - return 0; + CLwipIf::getInstance().enableTimer(); + + return buffer_cursor - buffer; } -/* -------------------------------------------------------------------------- */ -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; +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* buffer, size_t size) { + if(size==0 || buffer==nullptr || this->tcp_info->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->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 + + /* + * 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 + */ + 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); + CLwipIf::getInstance().enableTimer(); + + 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(); + + CLwipIf::getInstance().syncTimer(); + b = pbuf_get_at(this->tcp_info->pbuf_head, 0); // TODO test this + CLwipIf::getInstance().enableTimer(); + return b; } -/* -------------------------------------------------------------------------- */ -void lwipClient::flush() -{ - /* -------------------------------------------------------------------------- */ - if ((_tcp_client == NULL) || (_tcp_client->pcb == NULL)) { +void lwipClient::flush() { + if ((this->tcp_info->pcb == NULL)) { return; } - tcp_output(_tcp_client->pcb); - CLwipIf::getInstance().lwip_task(); + tcp_output(this->tcp_info->pcb); } -/* -------------------------------------------------------------------------- */ -void lwipClient::stop() -{ - /* -------------------------------------------------------------------------- */ - if (_tcp_client == NULL) { - return; +void lwipClient::close_pcb() { + 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; + + this->tcp_info->pcb = nullptr; + + // 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 - // close tcp connection if not closed yet - if (status() != TCP_CLOSING) { - tcp_connection_close(_tcp_client->pcb, _tcp_client); + 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; + this->tcp_info->server = nullptr; + server->remove(this); } } -/* -------------------------------------------------------------------------- */ -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->tcp_info->state == TCP_CONNECTED || this->tcp_info->state == TCP_ACCEPTED; } -/* -------------------------------------------------------------------------- */ -uint8_t lwipClient::status() -{ - if (_tcp_client == NULL) { +uint8_t lwipClient::status() { + if (this == nullptr) { return TCP_NONE; } - return _tcp_client->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 (_tcp_client != nullptr); +lwipClient::operator bool() { + return (this->tcp_info->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->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 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->tcp_info->pbuf_head==nullptr) { + return 0; // TODO extend checks + } + CLwipIf::getInstance().syncTimer(); + // TODO check that the buffer size is less than the token len + + uint16_t offset=this->tcp_info->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->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 + 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->tcp_info->pbuf_head, (uint8_t*)buffer, buf_copy_len, 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); + CLwipIf::getInstance().enableTimer(); + + return copied; +} diff --git a/libraries/lwIpWrapper/src/lwipClient.h b/libraries/lwIpWrapper/src/lwipClient.h index 511b3c7f..d7a7c0bd 100644 --- a/libraries/lwIpWrapper/src/lwipClient.h +++ b/libraries/lwIpWrapper/src/lwipClient.h @@ -1,73 +1,114 @@ -#ifndef ARDUINO_LWIP_CLIENT_H -#define ARDUINO_LWIP_CLIENT_H - -#include "Arduino.h" +#pragma once +#include +#include +#include +#include #include "CNetIf.h" -#include "Client.h" -#include "IPAddress.h" -#include "Print.h" -#include "lwipMem.h" -#include "lwipTcp.h" -#include "lwipTypes.h" +#include "lwipServer.h" +#include + +// TODO improve documentation -class lwipClient : public Client { +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_struct* tcpClient); + lwipClient(struct tcp_pcb* tcpClient, lwipServer *server); // FIXME this should be a private constructor, friend of Server + + // disable copy constructor + lwipClient(const lwipClient&); + lwipClient& operator=(const lwipClient&); + + // keep move constructor + lwipClient(lwipClient&&); + lwipClient& operator=(lwipClient&&); - uint8_t status(); + virtual ~lwipClient(); + + 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->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); + 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() - { - return (_tcp_client->pcb->local_port); + virtual uint16_t localPort() { + return (this->tcp_info->pcb->local_port); }; - virtual IPAddress remoteIP() - { - return (IPAddress(_tcp_client->pcb->remote_ip.addr)); + virtual IPAddress remoteIP() { + return (IPAddress(this->tcp_info->pcb->remote_ip.addr)); }; - virtual uint16_t remotePort() - { - return (_tcp_client->pcb->remote_port); + virtual uint16_t remotePort() { + return (this->tcp_info->pcb->remote_port); }; - void setConnectionTimeout(uint16_t timeout) - { + void setConnectionTimeout(uint16_t timeout) { _timeout = timeout; } + void bindCNetIf(CNetIf &n) { + tcp_bind_netif(this->tcp_info->pcb, n.getNi()); + } + friend class lwipServer; using Print::write; private: - struct tcp_struct* _tcp_client; + // 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; + }; + + std::shared_ptr tcp_info; + uint16_t _timeout = 10000; + ip_addr_t _ip; + + err_t connected_callback(struct tcp_pcb* tpcb, err_t err); + 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); + friend err_t _lwip_tcp_connected_callback(void* arg, struct tcp_pcb* tpcb, err_t err); + void close_pcb(); }; -#endif +inline const lwipClient CLIENT_NONE(nullptr, nullptr); \ No newline at end of file diff --git a/libraries/lwIpWrapper/src/lwipMem.cpp b/libraries/lwIpWrapper/src/lwipMem.cpp deleted file mode 100644 index bf255e63..00000000 --- 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 8dc8be64..00000000 --- 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 diff --git a/libraries/lwIpWrapper/src/lwipServer.cpp b/libraries/lwIpWrapper/src/lwipServer.cpp index be42b2a4..360c33fa 100644 --- a/libraries/lwIpWrapper/src/lwipServer.cpp +++ b/libraries/lwIpWrapper/src/lwipServer.cpp @@ -5,39 +5,40 @@ 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); + +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,40 +47,83 @@ 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]); - 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) { + 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; } } + + delete clients[--size]; + clients[size] = nullptr; + + arduino::unlock(); +} + +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() { - 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) { - if (client.available()) { - return client; - } - } - } - } + 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) { + res = clients[size-clients_available--]; // TODO verify index } + arduino::unlock(); - struct tcp_struct* default_client = NULL; - return lwipClient(default_client); + return res; } size_t lwipServer::write(uint8_t b) @@ -87,23 +131,31 @@ 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) { + 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 n; + 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_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)) { + 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 e7d4456b..2a744692 100644 --- a/libraries/lwIpWrapper/src/lwipServer.h +++ b/libraries/lwIpWrapper/src/lwipServer.h @@ -1,27 +1,54 @@ -#ifndef _ARDUINO_LWIP_SERVER_H -#define _ARDUINO_LWIP_SERVER_H +#pragma once -#include "Server.h" -#include "lwipTcp.h" +#include +#include +#include +// #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. + */ + 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; + 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; +}; diff --git a/libraries/lwIpWrapper/src/lwipTcp.cpp b/libraries/lwIpWrapper/src/lwipTcp.cpp deleted file mode 100644 index 68026279..00000000 --- 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 3c4e689b..00000000 --- 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 diff --git a/libraries/lwIpWrapper/src/lwipTypes.h b/libraries/lwIpWrapper/src/lwipTypes.h deleted file mode 100644 index c108e15c..00000000 --- a/libraries/lwIpWrapper/src/lwipTypes.h +++ /dev/null @@ -1,41 +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 - -/* 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 - 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 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 diff --git a/libraries/lwIpWrapper/src/lwipUDP.cpp b/libraries/lwIpWrapper/src/lwipUDP.cpp index 0ea680f8..5385236c 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); + 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)) { + /* + * 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(); - _data = pbuffer_free_data(_data); + 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))); + 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; } diff --git a/libraries/lwIpWrapper/src/lwipUDP.h b/libraries/lwIpWrapper/src/lwipUDP.h index 2b0ee8c1..a023e9a6 100644 --- a/libraries/lwIpWrapper/src/lwipUDP.h +++ b/libraries/lwIpWrapper/src/lwipUDP.h @@ -5,11 +5,19 @@ #include #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 onDataArrival; +}; + class lwipUDP : public UDP { private: uint16_t _port; // local port to listen on diff --git a/libraries/lwIpWrapper/src/lwippbuf.cpp b/libraries/lwIpWrapper/src/lwippbuf.cpp new file mode 100644 index 00000000..24c6c71d --- /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 00000000..498d6f9a --- /dev/null +++ b/libraries/lwIpWrapper/src/lwippbuf.h @@ -0,0 +1,12 @@ +#pragma once +#include + +/** + * 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 diff --git a/libraries/lwIpWrapper/src/utils.h b/libraries/lwIpWrapper/src/utils.h new file mode 100644 index 00000000..e4a2ac1b --- /dev/null +++ b/libraries/lwIpWrapper/src/utils.h @@ -0,0 +1,84 @@ +#pragma once +#include +#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