From a1db02b751b67c2751cf4fa46d9d45aa92cfeabd Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Mon, 26 Nov 2018 09:52:44 +0100 Subject: [PATCH 01/16] Use bool instead of boolean https://github.com/arduino/Arduino/issues/4673 Signed-off-by: Frederic.Pillon --- examples/AdvancedChatServer/AdvancedChatServer.ino | 2 +- .../BarometricPressureWebServer/BarometricPressureWebServer.ino | 2 +- examples/ChatServer/ChatServer.ino | 2 +- examples/DhcpChatServer/DhcpChatServer.ino | 2 +- examples/WebServer/WebServer.ino | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/AdvancedChatServer/AdvancedChatServer.ino b/examples/AdvancedChatServer/AdvancedChatServer.ino index 1680233..cdb236a 100644 --- a/examples/AdvancedChatServer/AdvancedChatServer.ino +++ b/examples/AdvancedChatServer/AdvancedChatServer.ino @@ -63,7 +63,7 @@ void loop() { // when the client sends the first byte, say hello: if (client) { - boolean newClient = true; + bool newClient = true; for (byte i = 0; i < 4; i++) { //check whether this client refers to the same socket as one of the existing instances: if (clients[i] == client) { diff --git a/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino b/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino index 96dbcbc..666f527 100644 --- a/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino +++ b/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino @@ -128,7 +128,7 @@ void listenForEthernetClients() { if (client) { Serial.println("Got a client"); // an http request ends with a blank line - boolean currentLineIsBlank = true; + bool currentLineIsBlank = true; while (client.connected()) { if (client.available()) { char c = client.read(); diff --git a/examples/ChatServer/ChatServer.ino b/examples/ChatServer/ChatServer.ino index 5c3aa3d..cffbabf 100644 --- a/examples/ChatServer/ChatServer.ino +++ b/examples/ChatServer/ChatServer.ino @@ -33,7 +33,7 @@ IPAddress subnet(255, 255, 0, 0); // telnet defaults to port 23 EthernetServer server(23); -boolean alreadyConnected = false; // whether or not the client was connected previously +bool alreadyConnected = false; // whether or not the client was connected previously void setup() { // initialize the ethernet device diff --git a/examples/DhcpChatServer/DhcpChatServer.ino b/examples/DhcpChatServer/DhcpChatServer.ino index 50ac63f..d2561fb 100644 --- a/examples/DhcpChatServer/DhcpChatServer.ino +++ b/examples/DhcpChatServer/DhcpChatServer.ino @@ -35,7 +35,7 @@ IPAddress subnet(255, 255, 0, 0); // telnet defaults to port 23 EthernetServer server(23); -boolean gotAMessage = false; // whether or not you got a message from the client yet +bool gotAMessage = false; // whether or not you got a message from the client yet void setup() { // Open serial communications and wait for port to open: diff --git a/examples/WebServer/WebServer.ino b/examples/WebServer/WebServer.ino index e47900c..9030ee4 100644 --- a/examples/WebServer/WebServer.ino +++ b/examples/WebServer/WebServer.ino @@ -53,7 +53,7 @@ void loop() { if (client) { Serial.println("new client"); // an http request ends with a blank line - boolean currentLineIsBlank = true; + bool currentLineIsBlank = true; while (client.connected()) { if (client.available()) { char c = client.read(); From be067d9c0a30ed211b92bae9ebbf46b0bbf48454 Mon Sep 17 00:00:00 2001 From: Philippe Coval Date: Fri, 4 Jan 2019 11:14:14 +0100 Subject: [PATCH 02/16] client: Fix bool operator on init and ongoing use When iterating we make sure client's socket is not closing since _tcp_client remains after close. This was tested on nuleo-f767zi using project webthing-arduino Change-Id: Ie465fe59009c33957dad97f1c70b4dc3af3b2ebe Forwarded: https://github.com/stm32duino/STM32Ethernet/pull/17 Signed-off-by: Philippe Coval --- src/EthernetClient.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/EthernetClient.cpp b/src/EthernetClient.cpp index f02c486..349ffb6 100644 --- a/src/EthernetClient.cpp +++ b/src/EthernetClient.cpp @@ -9,12 +9,14 @@ extern "C" { #include "EthernetServer.h" #include "Dns.h" -EthernetClient::EthernetClient() { +EthernetClient::EthernetClient() + :_tcp_client(NULL) { } /* Deprecated constructor. Keeps compatibility with W5100 architecture sketches but sock is ignored. */ -EthernetClient::EthernetClient(uint8_t sock) { +EthernetClient::EthernetClient(uint8_t sock) + :_tcp_client(NULL) { UNUSED(sock); } @@ -181,7 +183,7 @@ uint8_t EthernetClient::status() { // EthernetServer::available() as the condition in an if-statement. EthernetClient::operator bool() { - return _tcp_client != NULL; + return (_tcp_client && (_tcp_client->state != TCP_CLOSING)); } bool EthernetClient::operator==(const EthernetClient& rhs) { From a443221fb53d196cdd399d5193ec6f05177aa18a Mon Sep 17 00:00:00 2001 From: Philippe Coval Date: Fri, 4 Jan 2019 13:59:40 +0100 Subject: [PATCH 03/16] server: Initialize class member on dynamic alloc Also replace default constructor to use port 80 by default. I noticed that server worked on global instance. but not if used in class (allocated with new). This change makes both use cases working. Forwarded: https://github.com/stm32duino/STM32Ethernet/pull/17 Relate-to: https://github.com/rzr/webthing-iotjs/wiki/MCU Change-Id: I2e21e34a43f3cadb7981f2e359a5960735daa5d3 Signed-off-by: Philippe Coval --- src/EthernetServer.cpp | 2 ++ src/EthernetServer.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EthernetServer.cpp b/src/EthernetServer.cpp index f5353d8..18ff147 100644 --- a/src/EthernetServer.cpp +++ b/src/EthernetServer.cpp @@ -9,6 +9,8 @@ extern "C" { EthernetServer::EthernetServer(uint16_t port) { _port = port; + _tcp_client[MAX_CLIENT] = {}; + _tcp_server = {}; } void EthernetServer::begin() diff --git a/src/EthernetServer.h b/src/EthernetServer.h index 317bf41..fb42c43 100644 --- a/src/EthernetServer.h +++ b/src/EthernetServer.h @@ -14,7 +14,7 @@ public Server { void accept(void); public: - EthernetServer(uint16_t); + EthernetServer(uint16_t port = 80); EthernetClient available(); virtual void begin(); virtual size_t write(uint8_t); From 13a0adf9ece8038458c94d560af2708cc5346cff Mon Sep 17 00:00:00 2001 From: Stefan Staub Date: Sat, 22 Dec 2018 18:48:47 +0100 Subject: [PATCH 04/16] Create library.json --- library.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 library.json diff --git a/library.json b/library.json new file mode 100755 index 0000000..f1a6d3d --- /dev/null +++ b/library.json @@ -0,0 +1,13 @@ +{ + "name": "STM32Ethernet", + "keywords": "Ethernet", + "description": "Arduino library to support Ethernet for STM32 based board.", + "repository": + { + "type": "git", + "url": "https://github.com/stm32duino/STM32Ethernet" + }, + "version": "1.0.4", + "frameworks": "arduino", + "platforms": "ststm32" +} From ad95c45afa8e9c727c70826d39d93d81d8c62b33 Mon Sep 17 00:00:00 2001 From: Stefan Staub Date: Fri, 10 May 2019 16:52:40 +0200 Subject: [PATCH 05/16] Update library.json --- library.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index f1a6d3d..b93a2b7 100755 --- a/library.json +++ b/library.json @@ -7,7 +7,10 @@ "type": "git", "url": "https://github.com/stm32duino/STM32Ethernet" }, - "version": "1.0.4", + "version": "1.0.5", "frameworks": "arduino", - "platforms": "ststm32" + "platforms": "ststm32", + "build": { + "libArchive": false + } } From 72c849f248985a676267686ffa719dfecd0a7532 Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Fri, 10 May 2019 16:40:39 +0200 Subject: [PATCH 06/16] Remove useless variant.h include variant.h is now included thanks stm32_def.h See https://github.com/stm32duino/Arduino_Core_STM32/pull/518 Signed-off-by: Frederic.Pillon --- src/utility/ethernetif.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utility/ethernetif.c b/src/utility/ethernetif.c index eeb966c..3658064 100644 --- a/src/utility/ethernetif.c +++ b/src/utility/ethernetif.c @@ -52,7 +52,9 @@ #include #include "PeripheralPins.h" #include "stm32_eth.h" +#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01050000) #include "variant.h" +#endif #ifdef __cplusplus extern "C" { From 71208860cf0ba177a64190fbbde34f0205902e4e Mon Sep 17 00:00:00 2001 From: Alexandre Bourdiol Date: Fri, 26 Jul 2019 15:38:50 +0200 Subject: [PATCH 07/16] Update STM32duino_STM32Ethernet library to use HardwareTimer library --- src/utility/{stm32_eth.c => stm32_eth.cpp} | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) rename src/utility/{stm32_eth.c => stm32_eth.cpp} (98%) diff --git a/src/utility/stm32_eth.c b/src/utility/stm32_eth.cpp similarity index 98% rename from src/utility/stm32_eth.c rename to src/utility/stm32_eth.cpp index 4365b17..c64725d 100644 --- a/src/utility/stm32_eth.c +++ b/src/utility/stm32_eth.cpp @@ -1,6 +1,6 @@ /** ****************************************************************************** - * @file stm32_eth.c + * @file stm32_eth.cpp * @author WI6LABS * @version V1.0.0 * @date 24-May-2017 @@ -96,15 +96,12 @@ static uint8_t DHCP_Started_by_user = 0; /* Ethernet link status periodic timer */ static uint32_t gEhtLinkTickStart = 0; -/* Handler for stimer */ -static stimer_t TimHandle; - /*************************** Function prototype *******************************/ static void Netif_Config(void); 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); -static void scheduler_callback(stimer_t *htim); +static void scheduler_callback(HardwareTimer *HT); static void TIM_scheduler_Config(void); /** @@ -139,12 +136,12 @@ static void Netif_Config(void) /** * @brief Scheduler callback. Call by a timer interrupt. -* @param htim: pointer to stimer_t +* @param htim: pointer to HardwareTimer * @retval None */ -static void scheduler_callback(stimer_t *htim) +static void scheduler_callback(HardwareTimer *HT) { - UNUSED(htim); + UNUSED(HT); stm32_eth_scheduler(); } @@ -156,13 +153,14 @@ static void scheduler_callback(stimer_t *htim) */ static void TIM_scheduler_Config(void) { - /* Set TIMx instance. */ - TimHandle.timer = DEFAULT_ETHERNET_TIMER; + /* Configure HardwareTimer */ + HardwareTimer *EthTim = new HardwareTimer(DEFAULT_ETHERNET_TIMER); + EthTim->setMode(1, TIMER_OUTPUT_COMPARE); /* Timer set to 1ms */ - TimerHandleInit(&TimHandle, (uint16_t)(1000 - 1), ((uint32_t)(getTimerClkFreq(DEFAULT_ETHERNET_TIMER) / (1000000)) - 1)); - - attachIntHandle(&TimHandle, scheduler_callback); + EthTim->setOverflow(1000, MICROSEC_FORMAT); + EthTim->attachInterrupt(scheduler_callback); + EthTim->resume(); } void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, const uint8_t *netmask) From 568478e0f4ddc5622c7123a6761eb8193433c440 Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Wed, 21 Aug 2019 16:02:58 +0200 Subject: [PATCH 08/16] Ensure backward compatibility depending of the STM32 Core Version Signed-off-by: Frederic.Pillon --- src/utility/stm32_eth.cpp | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/utility/stm32_eth.cpp b/src/utility/stm32_eth.cpp index c64725d..2f4db14 100644 --- a/src/utility/stm32_eth.cpp +++ b/src/utility/stm32_eth.cpp @@ -96,12 +96,16 @@ static uint8_t DHCP_Started_by_user = 0; /* Ethernet link status periodic timer */ static uint32_t gEhtLinkTickStart = 0; +#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01060100) +/* Handler for stimer */ +static stimer_t TimHandle; +#endif + /*************************** Function prototype *******************************/ static void Netif_Config(void); 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); -static void scheduler_callback(HardwareTimer *HT); static void TIM_scheduler_Config(void); /** @@ -136,15 +140,35 @@ static void Netif_Config(void) /** * @brief Scheduler callback. Call by a timer interrupt. -* @param htim: pointer to HardwareTimer +* @param htim: pointer to stimer_t or Hardware Timer * @retval None */ -static void scheduler_callback(HardwareTimer *HT) +#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01060100) +static void scheduler_callback(stimer_t *htim) +#else +static void scheduler_callback(HardwareTimer *htim) +#endif { - UNUSED(HT); + UNUSED(htim); stm32_eth_scheduler(); } +#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01060100) +/** +* @brief Enable the timer used to call ethernet scheduler function at regular +* interval. +* @param None +* @retval None +*/ +static void TIM_scheduler_Config(void) +{ + /* Set TIMx instance. */ + TimHandle.timer = DEFAULT_ETHERNET_TIMER; + /* Timer set to 1ms */ + TimerHandleInit(&TimHandle, (uint16_t)(1000 - 1), ((uint32_t)(getTimerClkFreq(DEFAULT_ETHERNET_TIMER) / (1000000)) - 1)); + attachIntHandle(&TimHandle, scheduler_callback); +} +#else /** * @brief Enable the timer used to call ethernet scheduler function at regular * interval. @@ -162,6 +186,7 @@ static void TIM_scheduler_Config(void) EthTim->attachInterrupt(scheduler_callback); EthTim->resume(); } +#endif void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, const uint8_t *netmask) { From 3ebfc7be9946b8cdfb9d0b3b6ce13e99c276aa77 Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Thu, 22 Aug 2019 10:13:03 +0200 Subject: [PATCH 09/16] Allow to add extra options to the default one by adding a 'lwipopts_extra.h' Signed-off-by: Frederic.Pillon --- README.md | 4 ++- src/lwipopts.h | 3 ++ src/lwipopts_default.h | 62 ++++++++++++++++++------------------------ 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 9106f6e..78e5755 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,9 @@ The LwIP has several user defined options, which is specified from within the `l This library provides a default user defined options file named `lwipopts_default.h`. -User can provide his own defined options at sketch level by adding his configuration in a file named `STM32lwipopts.h`. +User can provide his own defined options at sketch level by adding his configuration in a file named `STM32lwipopts.h` or +extend the default one by adding some extra configuration in a file named `lwipopts_extra.h`. + ## New alternative init procedure **!!!** diff --git a/src/lwipopts.h b/src/lwipopts.h index 56f805b..e3fb5f3 100644 --- a/src/lwipopts.h +++ b/src/lwipopts.h @@ -11,6 +11,9 @@ #if __has_include("STM32lwipopts.h") #include "STM32lwipopts.h" #else +#if __has_include("lwipopts_extra.h") +#include "lwipopts_extra.h" +#endif #include "lwipopts_default.h" #endif diff --git a/src/lwipopts_default.h b/src/lwipopts_default.h index 7a988f5..0a03e31 100644 --- a/src/lwipopts_default.h +++ b/src/lwipopts_default.h @@ -9,36 +9,10 @@ *

© Copyright (c) 2017 STMicroelectronics International N.V. * All rights reserved.

* - * Redistribution and use in source and binary forms, with or without - * modification, are permitted, provided that the following conditions are met: - * - * 1. Redistribution of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of other - * contributors to this software may be used to endorse or promote products - * derived from this software without specific written permission. - * 4. This software, including modifications and/or derivative works of this - * software, must execute solely and exclusively on microcontroller or - * microprocessor devices manufactured by or for STMicroelectronics. - * 5. Redistribution and use of this software other than as permitted under - * this license is void and will automatically terminate your rights under - * this license. - * - * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY - * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT - * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 * ****************************************************************************** */ @@ -58,6 +32,8 @@ */ #define SYS_LIGHTWEIGHT_PROT 0 +#define LWIP_NOASSERT + /* ---------- Memory options ---------- */ /* MEM_ALIGNMENT: should be set to the alignment of the CPU for which lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 @@ -100,9 +76,13 @@ a lot of data that needs to be copied, this should be set high. */ /* ---------- TCP options ---------- */ #define LWIP_TCP 1 #define TCP_TTL 255 +#define LWIP_SO_RCVTIMEO 1 +#define LWIP_SO_RCVRCVTIMEO_NONSTANDARD 1 /* Pass an integer number of ms instead of a timeval struct. */ +#define LWIP_SO_SNDTIMEO 1 +#define LWIP_SO_SNDRCVTIMEO_NONSTANDARD 1 /* Pass an integer number of ms instead of a timeval struct. */ /* Controls if TCP should queue segments that arrive out of - order. Define to 0 if your device is low on memory. */ + order. Define to 0 if your device is low on memory and you are not scared by TCP congestion and latencies. */ #define TCP_QUEUE_OOSEQ 0 /* TCP Maximum segment size. */ @@ -117,19 +97,25 @@ a lot of data that needs to be copied, this should be set high. */ #define TCP_SND_QUEUELEN (2* TCP_SND_BUF/TCP_MSS) /* TCP receive window. */ -#define TCP_WND (2*TCP_MSS) +#define TCP_WND (3*TCP_MSS) + +#define LWIP_TCP_KEEPALIVE 1 /* Keep the TCP link active. Important for MQTT/TLS */ +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 1 /* Prevent the same port to be used after reset. + Otherwise, the remote host may be confused if the port was not explicitly closed before the reset. */ /* ---------- ICMP options ---------- */ #define LWIP_ICMP 1 +#define LWIP_RAW 1 /* PING changed to 1 */ +#define DEFAULT_RAW_RECVMBOX_SIZE 3 /* for ICMP PING */ /* ---------- DHCP options ---------- */ +/* Define LWIP_DHCP to 1 if you want DHCP configuration of + interfaces. DHCP is not implemented in lwIP 0.5.1, however, so + turning this on does currently not work. */ #define LWIP_DHCP 1 -/* ---------- DNS options ---------- */ -#define LWIP_DNS 1 - /* ---------- UDP options ---------- */ #define LWIP_UDP 1 @@ -138,12 +124,17 @@ a lot of data that needs to be copied, this should be set high. */ /* ---------- Statistics options ---------- */ #define LWIP_STATS 0 +#define LWIP_PROVIDE_ERRNO /* ---------- link callback options ---------- */ /* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface * whenever the link changes (i.e., link down) */ +// need for building net_ip.c +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETIF_STATUS_CALLBACK 1 #define LWIP_NETIF_LINK_CALLBACK 1 +#define LWIP_DHCP_CHECK_LINK_UP 1 /* -------------------------------------- @@ -211,6 +202,7 @@ The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) */ #define LWIP_SOCKET 0 +#define LWIP_DNS 1 /* ------------------------------------ From 75fefe628c4d438463cc05373774f6d29d27f7f2 Mon Sep 17 00:00:00 2001 From: gdsports Date: Wed, 16 Oct 2019 14:27:13 -0700 Subject: [PATCH 10/16] Add EthernetClient functions: localPort, remoteIP, remotePort SSLClient needs these functions --- src/EthernetClient.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/EthernetClient.h b/src/EthernetClient.h index 1cf73d8..6734b6d 100644 --- a/src/EthernetClient.h +++ b/src/EthernetClient.h @@ -31,6 +31,9 @@ class EthernetClient : public Client { virtual bool operator==(const EthernetClient&); virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; uint8_t getSocketNumber(); + virtual uint16_t localPort() { return (_tcp_client->pcb->local_port); }; + virtual IPAddress remoteIP() { return (IPAddress(_tcp_client->pcb->remote_ip.addr)); }; + virtual uint16_t remotePort() { return (_tcp_client->pcb->remote_port); }; friend class EthernetServer; From cfe4f6496d7e5deb974498d0d6c4011819d0574c Mon Sep 17 00:00:00 2001 From: gdsports Date: Mon, 21 Oct 2019 15:31:59 -0700 Subject: [PATCH 11/16] Allocate the TCP client when needed. This patch allows the WebClientRepeating example to work. --- src/EthernetClient.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/EthernetClient.cpp b/src/EthernetClient.cpp index 349ffb6..f6978fb 100644 --- a/src/EthernetClient.cpp +++ b/src/EthernetClient.cpp @@ -40,16 +40,13 @@ int EthernetClient::connect(const char* host, uint16_t port) { } int EthernetClient::connect(IPAddress ip, uint16_t port) { - /* Can't create twice the same client */ - if(_tcp_client != NULL) { - return 0; - } - - /* Allocates memory for client */ - _tcp_client = (struct tcp_struct *)mem_malloc(sizeof(struct tcp_struct)); - if(_tcp_client == NULL) { - return 0; + /* Allocates memory for client */ + _tcp_client = (struct tcp_struct *)mem_malloc(sizeof(struct tcp_struct)); + + if(_tcp_client == NULL) { + return 0; + } } /* Creates a new TCP protocol control block */ From 193a5131ea79feb277712db40ffcd54a4fd6ab41 Mon Sep 17 00:00:00 2001 From: gdsports Date: Tue, 22 Oct 2019 16:11:02 -0700 Subject: [PATCH 12/16] Add Ethernet.linkStatus Also remove 2 printf debug. --- src/STM32Ethernet.cpp | 5 +++++ src/STM32Ethernet.h | 7 +++++++ src/utility/stm32_eth.cpp | 13 +++++++++---- src/utility/stm32_eth.h | 1 + 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/STM32Ethernet.cpp b/src/STM32Ethernet.cpp index e406df5..7a79a38 100644 --- a/src/STM32Ethernet.cpp +++ b/src/STM32Ethernet.cpp @@ -101,6 +101,11 @@ void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server macAddress(mac); } +EthernetLinkStatus EthernetClass::linkStatus() +{ + return (stm32_eth_link_up() ? LinkON : LinkOFF); +} + int EthernetClass::maintain(){ int rc = DHCP_CHECK_NONE; diff --git a/src/STM32Ethernet.h b/src/STM32Ethernet.h index c392275..5d814cd 100644 --- a/src/STM32Ethernet.h +++ b/src/STM32Ethernet.h @@ -7,6 +7,12 @@ #include "EthernetServer.h" #include "Dhcp.h" +enum EthernetLinkStatus { + Unknown, + LinkON, + LinkOFF +}; + class EthernetClass { private: IPAddress _dnsServerAddress; @@ -19,6 +25,7 @@ class EthernetClass { // 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(); void begin(IPAddress local_ip); void begin(IPAddress local_ip, IPAddress subnet); void begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway); diff --git a/src/utility/stm32_eth.cpp b/src/utility/stm32_eth.cpp index 2f4db14..08b0c3f 100644 --- a/src/utility/stm32_eth.cpp +++ b/src/utility/stm32_eth.cpp @@ -246,6 +246,15 @@ void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, co stm32_eth_scheduler(); } +/** + * @brief Return Ethernet link status + * @param None + * @retval 1 for link up, 0 for link down + */ +uint8_t stm32_eth_link_up(void) { + return netif_is_link_up(&gnetif); +} + /** * @brief This function must be called in main loop in standalone mode. * @param None @@ -479,8 +488,6 @@ void ethernetif_notify_conn_changed(struct netif *netif) { if(netif_is_link_up(netif)) { - printf("Link up\n"); - /* Update DHCP state machine if DHCP used */ if(DHCP_Started_by_user == 1) { DHCP_state = DHCP_START; @@ -498,8 +505,6 @@ void ethernetif_notify_conn_changed(struct netif *netif) /* When the netif link is down this function must be called.*/ netif_set_down(netif); - - printf("Link down\n"); } } diff --git a/src/utility/stm32_eth.h b/src/utility/stm32_eth.h index bf1bb28..e537005 100644 --- a/src/utility/stm32_eth.h +++ b/src/utility/stm32_eth.h @@ -121,6 +121,7 @@ extern struct netif gnetif; /* Exported functions ------------------------------------------------------- */ void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, const uint8_t *netmask); +uint8_t stm32_eth_link_up(void); void stm32_eth_scheduler(void); void User_notification(struct netif *netif); From 5f6c5b670bc42fc778603d8abd88573c33fa5341 Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Fri, 25 Oct 2019 16:32:40 +0200 Subject: [PATCH 13/16] Update Ethernet.linkStatus to handle Unknown status Signed-off-by: Frederic Pillon --- src/STM32Ethernet.cpp | 2 +- src/utility/ethernetif.c | 11 +++++++++++ src/utility/ethernetif.h | 1 + src/utility/stm32_eth.cpp | 9 +++++++++ src/utility/stm32_eth.h | 1 + 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/STM32Ethernet.cpp b/src/STM32Ethernet.cpp index 7a79a38..1a72ed8 100644 --- a/src/STM32Ethernet.cpp +++ b/src/STM32Ethernet.cpp @@ -103,7 +103,7 @@ void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server EthernetLinkStatus EthernetClass::linkStatus() { - return (stm32_eth_link_up() ? LinkON : LinkOFF); + return (!stm32_eth_is_init()) ? Unknown : (stm32_eth_link_up() ? LinkON : LinkOFF); } int EthernetClass::maintain(){ diff --git a/src/utility/ethernetif.c b/src/utility/ethernetif.c index 3658064..a60a269 100644 --- a/src/utility/ethernetif.c +++ b/src/utility/ethernetif.c @@ -431,6 +431,17 @@ void ethernetif_input(struct netif *netif) } } +/** + * @brief Returns the current state + * + * @param None + * @return 0 not initialized else 1 + */ +uint8_t ethernetif_is_init(void) +{ + return (EthHandle.State != HAL_ETH_STATE_RESET); +} + /** * @brief Should be called at the beginning of the program to set up the * network interface. It calls the function low_level_init() to do the diff --git a/src/utility/ethernetif.h b/src/utility/ethernetif.h index e851c19..8f4c782 100644 --- a/src/utility/ethernetif.h +++ b/src/utility/ethernetif.h @@ -56,6 +56,7 @@ #endif /* Exported types ------------------------------------------------------------*/ +uint8_t ethernetif_is_init(void); err_t ethernetif_init(struct netif *netif); void ethernetif_input(struct netif *netif); void ethernetif_set_link(struct netif *netif); diff --git a/src/utility/stm32_eth.cpp b/src/utility/stm32_eth.cpp index 08b0c3f..f63a26d 100644 --- a/src/utility/stm32_eth.cpp +++ b/src/utility/stm32_eth.cpp @@ -246,6 +246,15 @@ void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, co stm32_eth_scheduler(); } +/** + * @brief Return Ethernet init status + * @param None + * @retval 1 for initialized, 0 for not initialized + */ +uint8_t stm32_eth_is_init(void) { + return ethernetif_is_init(); +} + /** * @brief Return Ethernet link status * @param None diff --git a/src/utility/stm32_eth.h b/src/utility/stm32_eth.h index e537005..e90c7f8 100644 --- a/src/utility/stm32_eth.h +++ b/src/utility/stm32_eth.h @@ -121,6 +121,7 @@ extern struct netif gnetif; /* Exported functions ------------------------------------------------------- */ void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, const uint8_t *netmask); +uint8_t stm32_eth_is_init(void); uint8_t stm32_eth_link_up(void); void stm32_eth_scheduler(void); From 07b379706b1b8442d4421792c183b8146c44a096 Mon Sep 17 00:00:00 2001 From: gdsports Date: Tue, 22 Oct 2019 21:11:28 -0700 Subject: [PATCH 14/16] Use UDP unconnected sockets UDP connected sockets are limited to commuinicating with one host and port. --- src/EthernetUdp.cpp | 13 ++++--------- src/EthernetUdp.h | 2 ++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/EthernetUdp.cpp b/src/EthernetUdp.cpp index c5ae45c..057fa69 100644 --- a/src/EthernetUdp.cpp +++ b/src/EthernetUdp.cpp @@ -106,9 +106,8 @@ int EthernetUDP::beginPacket(IPAddress ip, uint16_t port) ip_addr_t ipaddr; - if(ERR_OK != udp_connect( _udp.pcb, u8_to_ip_addr(rawIPAddress(ip), &ipaddr), port)) { - return 0; - } + _sendtoIP = ip; + _sendtoPort = port; udp_recv(_udp.pcb, &udp_receive_callback, &_udp); stm32_eth_scheduler(); @@ -122,12 +121,8 @@ int EthernetUDP::endPacket() return 0; } - // A remote IP & port must be connected to udp pcb. Call ::beginPacket before. - if((udp_flags(_udp.pcb) & UDP_FLAGS_CONNECTED) != UDP_FLAGS_CONNECTED) { - return 0; - } - - if(ERR_OK != udp_send(_udp.pcb, _data)) { + ip_addr_t ipaddr; + if(ERR_OK != udp_sendto(_udp.pcb, _data, u8_to_ip_addr(rawIPAddress(_sendtoIP), &ipaddr), _sendtoPort)) { _data = stm32_free_data(_data); return 0; } diff --git a/src/EthernetUdp.h b/src/EthernetUdp.h index 8d7d878..f7f22e3 100644 --- a/src/EthernetUdp.h +++ b/src/EthernetUdp.h @@ -50,6 +50,8 @@ class EthernetUDP : public UDP { uint16_t _port; // local port to listen on IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed + IPAddress _sendtoIP; // the remote IP address set by beginPacket + uint16_t _sendtoPort; // the remote port set by beginPacket struct pbuf *_data; //pbuf for data to send struct udp_struct _udp; //udp settings From b956c4b29cef0f96893fc2bb73d067c7d2c6aa51 Mon Sep 17 00:00:00 2001 From: straccio Date: Tue, 16 Jan 2018 17:44:02 +0100 Subject: [PATCH 15/16] Added multicast udp, added callback for interrupt data arrival Implemented begin multicast for receiving multicast udp. Added ability to add a callback from the interrupt when udp receive data (Usefull with FreeRTOS). ``` void EthernetUDP::onDataArrival( std::function onDataArrival_fn){ _udp.onDataArrival = onDataArrival_fn; } ``` --- src/EthernetUdp.cpp | 35 ++++++-- src/EthernetUdp.h | 5 +- src/utility/{ethernetif.c => ethernetif.cpp} | 87 +++++++++++++++++++- src/utility/ethernetif.h | 12 +-- src/utility/stm32_eth.cpp | 12 +-- src/utility/stm32_eth.h | 10 +-- 6 files changed, 130 insertions(+), 31 deletions(-) mode change 100644 => 100755 src/EthernetUdp.cpp rename src/utility/{ethernetif.c => ethernetif.cpp} (89%) mode change 100644 => 100755 src/utility/ethernetif.h diff --git a/src/EthernetUdp.cpp b/src/EthernetUdp.cpp old mode 100644 new mode 100755 index 057fa69..c4bfff7 --- a/src/EthernetUdp.cpp +++ b/src/EthernetUdp.cpp @@ -25,16 +25,23 @@ * * bjoern@cs.stanford.edu 12/30/2008 */ - #include "STM32Ethernet.h" #include "Udp.h" #include "Dns.h" +#include "lwip/igmp.h" +#include "lwip/ip_addr.h" + /* Constructor */ EthernetUDP::EthernetUDP() {} /* Start EthernetUDP socket, listening at local port PORT */ uint8_t EthernetUDP::begin(uint16_t port) { + return begin(Ethernet.localIP(), port); +} + +/* Start EthernetUDP socket, listening at local IP ip and port PORT */ +uint8_t EthernetUDP::begin(IPAddress ip, uint16_t port, bool multicast) { // Can create a single udp connection per socket if(_udp.pcb != NULL) { return 0; @@ -46,14 +53,25 @@ uint8_t EthernetUDP::begin(uint16_t port) { return 0; } - IPAddress ip = Ethernet.localIP(); ip_addr_t ipaddr; + err_t err; + u8_to_ip_addr(rawIPAddress(ip), &ipaddr); + if (multicast) { + err = udp_bind(_udp.pcb, IP_ADDR_ANY, port); + } else { + err = udp_bind(_udp.pcb, &ipaddr, port); + } - if(ERR_OK != udp_bind(_udp.pcb, u8_to_ip_addr(rawIPAddress(ip), &ipaddr), port)) { + if(ERR_OK != err) { stop(); return 0; } +#if LWIP_IGMP + if ((multicast) && (ERR_OK != igmp_joingroup(IP_ADDR_ANY, &ipaddr))) { + return 0; + } +#endif udp_recv(_udp.pcb, &udp_receive_callback, &_udp); _port = port; @@ -104,8 +122,6 @@ int EthernetUDP::beginPacket(IPAddress ip, uint16_t port) return 0; } - ip_addr_t ipaddr; - _sendtoIP = ip; _sendtoPort = port; @@ -245,6 +261,11 @@ void EthernetUDP::flush() /* Start EthernetUDP socket, listening at local port PORT */ uint8_t EthernetUDP::beginMulticast(IPAddress ip, uint16_t port) { - UNUSED(ip); - return begin(port); + return begin(ip, port, true); +} + +#if LWIP_UDP +void EthernetUDP::onDataArrival( std::function onDataArrival_fn){ + _udp.onDataArrival = onDataArrival_fn; } +#endif diff --git a/src/EthernetUdp.h b/src/EthernetUdp.h index f7f22e3..0a1d2f9 100644 --- a/src/EthernetUdp.h +++ b/src/EthernetUdp.h @@ -38,10 +38,9 @@ #define ethernetudp_h #include +#include -extern "C" { #include "utility/stm32_eth.h" -} #define UDP_TX_PACKET_MAX_SIZE 24 @@ -62,6 +61,7 @@ class EthernetUDP : public UDP { public: EthernetUDP(); // Constructor virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual uint8_t begin(IPAddress, uint16_t, bool multicast = false); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use virtual uint8_t beginMulticast(IPAddress, uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use virtual void stop(); // Finish with the UDP socket @@ -104,6 +104,7 @@ class EthernetUDP : public UDP { virtual IPAddress remoteIP() { return _remoteIP; }; // Return the port of the host who sent the current incoming packet virtual uint16_t remotePort() { return _remotePort; }; + virtual void onDataArrival( std::function onDataArrival_fn); }; #endif diff --git a/src/utility/ethernetif.c b/src/utility/ethernetif.cpp similarity index 89% rename from src/utility/ethernetif.c rename to src/utility/ethernetif.cpp index a60a269..6ad4407 100644 --- a/src/utility/ethernetif.c +++ b/src/utility/ethernetif.cpp @@ -51,6 +51,7 @@ #include "ethernetif.h" #include #include "PeripheralPins.h" +#include "lwip/igmp.h" #include "stm32_eth.h" #if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01050000) #include "variant.h" @@ -92,6 +93,11 @@ static ETH_HandleTypeDef EthHandle; static uint8_t macaddress[6]= { MAC_ADDR0, MAC_ADDR1, MAC_ADDR2, MAC_ADDR3, MAC_ADDR4, MAC_ADDR5 }; +#if LWIP_IGMP +uint32_t ETH_HashTableHigh=0x0; +uint32_t ETH_HashTableLow=0x0; +#endif + /* Private function prototypes -----------------------------------------------*/ /* Private functions ---------------------------------------------------------*/ /******************************************************************************* @@ -205,7 +211,9 @@ static void low_level_init(struct netif *netif) /* Enable MAC and DMA transmission and reception */ HAL_ETH_Start(&EthHandle); - +#if LWIP_IGMP + netif_set_igmp_mac_filter(netif, igmp_mac_filter); +#endif /**** Configure PHY to generate an interrupt when Eth Link state changes ****/ /* Read Register Configuration */ HAL_ETH_ReadPHYRegister(&EthHandle, PHY_IMR, ®value); @@ -214,6 +222,10 @@ static void low_level_init(struct netif *netif) /* 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 } /** @@ -510,6 +522,13 @@ void ethernetif_set_link(struct netif *netif) HAL_ETH_ReadPHYRegister(&EthHandle, PHY_BSR, ®value); if((regvalue & PHY_LINKED_STATUS) != (uint16_t)RESET) { +#if LWIP_IGMP + if (!(netif->flags & NETIF_FLAG_IGMP)) { + netif->flags |= NETIF_FLAG_IGMP; + igmp_init(); + igmp_start(netif); + } +#endif netif_set_link_up(netif); } } @@ -628,6 +647,72 @@ void ethernetif_set_mac_addr(const uint8_t *mac) { } } +#if LWIP_IGMP +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; +} + +#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; + + 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; + } +} +#endif /* LWIP_IGMP */ + + #ifdef ETH_INPUT_USE_IT /** * @brief Ethernet Rx Transfer completed callback diff --git a/src/utility/ethernetif.h b/src/utility/ethernetif.h old mode 100644 new mode 100755 index 8f4c782..b0976fb --- a/src/utility/ethernetif.h +++ b/src/utility/ethernetif.h @@ -47,14 +47,11 @@ #ifndef __ETHERNETIF_H__ #define __ETHERNETIF_H__ - -#include "lwip/err.h" -#include "lwip/netif.h" - #ifdef __cplusplus extern "C" { #endif - +#include "lwip/err.h" +#include "lwip/netif.h" /* Exported types ------------------------------------------------------------*/ uint8_t ethernetif_is_init(void); err_t ethernetif_init(struct netif *netif); @@ -65,6 +62,11 @@ void ethernetif_notify_conn_changed(struct netif *netif); void ethernetif_set_mac_addr(const uint8_t *mac); +#if LWIP_IGMP +err_t igmp_mac_filter( struct netif *netif, const ip4_addr_t *ip4_addr, netif_mac_filter_action action ); +void register_multicast_address(const uint8_t *mac); +#endif + #ifdef __cplusplus } #endif diff --git a/src/utility/stm32_eth.cpp b/src/utility/stm32_eth.cpp index f63a26d..c7210dd 100644 --- a/src/utility/stm32_eth.cpp +++ b/src/utility/stm32_eth.cpp @@ -47,10 +47,6 @@ #include "lwip/prot/dhcp.h" #include "lwip/dns.h" -#ifdef __cplusplus - extern "C" { -#endif - /* Check ethernet link status every seconds */ #define TIME_CHECK_ETH_LINK_STATE 500U @@ -798,6 +794,10 @@ void udp_receive_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_copy(udp_arg->ip, *addr); udp_arg->port = port; + + if(udp_arg->onDataArrival != NULL){ + udp_arg->onDataArrival(); + } } else { pbuf_free(p); } @@ -1049,7 +1049,3 @@ void tcp_connection_close(struct tcp_pcb *tpcb, struct tcp_struct *tcp) } #endif /* LWIP_TCP */ - -#ifdef __cplusplus -} -#endif diff --git a/src/utility/stm32_eth.h b/src/utility/stm32_eth.h index e90c7f8..fd1cad6 100644 --- a/src/utility/stm32_eth.h +++ b/src/utility/stm32_eth.h @@ -38,10 +38,6 @@ #ifndef __STM32_ETH_H__ #define __STM32_ETH_H__ -#ifdef __cplusplus - extern "C" { -#endif - /* Includes ------------------------------------------------------------------*/ #include "stm32_def.h" #include "lwip/ip_addr.h" @@ -49,6 +45,7 @@ #include "lwip/udp.h" #include "lwip/tcp.h" #include "lwip/opt.h" +#include /* Exported types ------------------------------------------------------------*/ /* TCP connection state */ @@ -74,6 +71,7 @@ struct udp_struct { 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 */ @@ -174,8 +172,4 @@ void tcp_connection_close(struct tcp_pcb *tpcb, struct tcp_struct *tcp); #error "LWIP_TCP must be enabled in lwipopts.h" #endif -#ifdef __cplusplus -} -#endif - #endif /* __STM32_ETH_H__ */ From adf5ac4c9c42f01acc46fe49be6e6511f1e5bce6 Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Sun, 10 Nov 2019 07:19:18 +0100 Subject: [PATCH 16/16] Update library.properties Signed-off-by: Frederic Pillon --- library.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 69e5105..2ad82c6 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=STM32duino STM32Ethernet -version=1.0.4 +version=1.0.5 author=Various maintainer=STMicroelectronics sentence=Enables network connection (local and Internet) using the STM32 Board. @@ -7,3 +7,4 @@ paragraph=With this library you can use the STM32 board to connect to Internet. category=Communication url=https://github.com/stm32duino/STM32Ethernet architectures=stm32 +depends=STM32duino LwIP \ No newline at end of file