diff --git a/.github/workflows/Continuous-Integration.yml b/.github/workflows/Continuous-Integration.yml new file mode 100644 index 0000000..259d70f --- /dev/null +++ b/.github/workflows/Continuous-Integration.yml @@ -0,0 +1,69 @@ +name: STM32Ethernet Continuous Integration +on: + push: + branches: + - main + paths-ignore: + - '*' + - '**.md' + - '**.txt' + pull_request: + paths-ignore: + - '*' + - '**.md' + - '**.txt' +jobs: + astyle_check: + runs-on: ubuntu-latest + name: AStyle check + steps: + # First of all, clone the repo using the checkout action. + - name: Checkout + uses: actions/checkout@main + + - name: Astyle check + id: Astyle + uses: stm32duino/actions/astyle-check@main + + # Use the output from the `Astyle` step + - name: Astyle Errors + if: failure() + run: | + cat ${{ steps.Astyle.outputs.astyle-result }} + exit 1 + codespell: + name: Check for spelling errors + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@main + + # See: https://github.com/codespell-project/actions-codespell/blob/master/README.md + - name: Spell check + uses: codespell-project/actions-codespell@master + with: + check_filenames: true + check_hidden: true + # In the event of a false positive, add the word in all lower case to this file: + # ignore_words_file: ./extras/codespell-ignore-words-list.txt + lib_build: + runs-on: ubuntu-latest + name: Library compilation + steps: + # First of all, clone the repo using the checkout action. + - name: Checkout + uses: actions/checkout@main + + - name: Compilation + id: Compile + uses: stm32duino/actions/compile-examples@main + with: + board-pattern: "DISCO_F746NG|NUCLEO_F429ZI|NUCLEO_F767ZI" + libraries: "STM32duino LwIP, STM32duino FreeRTOS" + + # Use the output from the `Compile` step + - name: Compilation Errors + if: failure() + run: | + cat ${{ steps.Compile.outputs.compile-result }} + exit 1 diff --git a/AUTHORS b/AUTHORS index e9f8f6b..8f8eb4f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -31,6 +31,7 @@ Paul Stoffregen https://github.com/PaulStoffregen per1234 https://github.com/per1234 Richard Sim Scott Fitzgerald https://github.com/shfitz +Stefan Staub https://github.com/sstaub STMicroelectronics https://github.com/stm32duino Thibaut Viard https://github.com/aethaniel Tom Igoe https://github.com/tigoe diff --git a/README.md b/README.md index 60c268d..0256ceb 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,34 @@ 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 **!!!** + +There are alternative inits of the Ethernet interface with following orders: + + Ethernet.begin(); + Ethernet.begin(ip); + Ethernet.begin(ip, subnet); + Ethernet.begin(ip, subnet, gateway); + Ethernet.begin(ip, subnet, gateway, dns); + +This is more logical. A MAC address is no more needed and will retrieved internally by the mbed MAC address! + +You can get the MAC address with following function, this must be done after Ethernet.Begin() + + uint8_t *mac; + Ethernet.begin(); + Ethernet.MACAddress(mac); + +You can also set a new user based MAC address, this must be done before Ethernet.begin() + + uint8_t newMAC[] = {0x00, 0x80, 0xE1, 0x01, 0x01, 0x01}; + Ethernet.setMACAddress(newMAC); + Ethernet.begin(); ## Note @@ -39,4 +66,4 @@ Call `Ethernet::schedule()` performs an update of the LwIP stack.
## Wiki -You can find information at https://github.com/stm32duino/wiki/wiki/STM32Ethernet +You can find information at https://github.com/stm32duino/Arduino_Core_STM32/wiki/STM32Ethernet diff --git a/examples/AdvancedChatServer/AdvancedChatServer.ino b/examples/AdvancedChatServer/AdvancedChatServer.ino index e3719a6..451e9a9 100644 --- a/examples/AdvancedChatServer/AdvancedChatServer.ino +++ b/examples/AdvancedChatServer/AdvancedChatServer.ino @@ -17,18 +17,18 @@ by Norbert Truchsess modified 23 Jun 2017 by Wi6Labs + modified 1 Jun 2018 + by sstaub */ #include #include -// Enter a MAC address and IP address for your controller below. +// Enter an IP address for your controller below. // The IP address will be dependent on your local network. // gateway and subnet are optional: -byte mac[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED -}; + IPAddress ip(192, 168, 1, 177); IPAddress myDns(192, 168, 1, 1); IPAddress gateway(192, 168, 1, 1); @@ -42,7 +42,7 @@ EthernetClient clients[4]; void setup() { // initialize the Ethernet device - Ethernet.begin(mac, ip, myDns, gateway, subnet); + Ethernet.begin(ip, subnet, gateway, myDns); // start listening for clients server.begin(); // Open serial communications and wait for port to open: @@ -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) { @@ -103,7 +103,7 @@ void loop() { } for (byte i = 0; i < 4; i++) { if (!(clients[i].connected())) { - // client.stop() invalidates the internal socket-descriptor, so next use of == will allways return false; + // client.stop() invalidates the internal socket-descriptor, so next use of == will always return false; clients[i].stop(); } } diff --git a/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino b/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino index 32516cd..13d06bc 100644 --- a/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino +++ b/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino @@ -20,6 +20,8 @@ by Tom Igoe modified 23 Jun 2017 by Wi6Labs + modified 1 Jun 2018 + by sstaub */ #include @@ -28,11 +30,6 @@ #include -// assign a MAC address for the Ethernet controller. -// fill in your address here: -byte mac[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED -}; // assign an IP address for the controller: IPAddress ip(192, 168, 1, 20); @@ -62,10 +59,10 @@ void setup() { SPI.begin(); // start the Ethernet connection and the server: - Ethernet.begin(mac, ip); + Ethernet.begin(ip); server.begin(); - // initalize the data ready and chip select pins: + // initialize the data ready and chip select pins: pinMode(dataReadyPin, INPUT); pinMode(chipSelectPin, OUTPUT); @@ -131,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 8ee5126..cffbabf 100644 --- a/examples/ChatServer/ChatServer.ino +++ b/examples/ChatServer/ChatServer.ino @@ -14,17 +14,17 @@ by Tom Igoe modified 23 Jun 2017 by Wi6Labs + modified 1 Jun 2018 + by sstaub */ #include #include -// Enter a MAC address and IP address for your controller below. +// Enter an IP address for your controller below. // The IP address will be dependent on your local network. // gateway and subnet are optional: -byte mac[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED -}; + IPAddress ip(192, 168, 1, 177); IPAddress myDns(192,168,1, 1); IPAddress gateway(192, 168, 1, 1); @@ -33,11 +33,11 @@ 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 - Ethernet.begin(mac, ip, myDns, gateway, subnet); + Ethernet.begin(ip, subnet, gateway, myDns); // start listening for clients server.begin(); // Open serial communications and wait for port to open: diff --git a/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino b/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino index d17d97c..7f5c702 100644 --- a/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino +++ b/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino @@ -14,15 +14,13 @@ by Arturo Guadalupi modified 23 Jun 2017 by Wi6Labs + modified 1 Jun 2018 + by sstaub */ #include #include -// Enter a MAC address for your controller below. -byte mac[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED -}; // Initialize the Ethernet client library // with the IP address and port of the server @@ -38,7 +36,7 @@ void setup() { } // start the Ethernet connection: - if (Ethernet.begin(mac) == 0) { + if (Ethernet.begin() == 0) { Serial.println("Failed to configure Ethernet using DHCP"); // no point in carrying on, so do nothing forevermore: for (;;) diff --git a/examples/DhcpChatServer/DhcpChatServer.ino b/examples/DhcpChatServer/DhcpChatServer.ino index 842bf4d..d2561fb 100644 --- a/examples/DhcpChatServer/DhcpChatServer.ino +++ b/examples/DhcpChatServer/DhcpChatServer.ino @@ -18,18 +18,16 @@ Based on ChatServer example by David A. Mellis modified 23 Jun 2017 by Wi6Labs - + modified 1 Jun 2018 + by sstaub */ #include #include -// Enter a MAC address and IP address for your controller below. +// Enter an IP address for your controller below. // The IP address will be dependent on your local network. // gateway and subnet are optional: -byte mac[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED -}; IPAddress ip(192, 168, 1, 177); IPAddress myDns(192,168,1, 1); IPAddress gateway(192, 168, 1, 1); @@ -37,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: @@ -50,10 +48,10 @@ void setup() { // start the Ethernet connection: Serial.println("Trying to get an IP address using DHCP"); - if (Ethernet.begin(mac) == 0) { + if (Ethernet.begin() == 0) { Serial.println("Failed to configure Ethernet using DHCP"); // initialize the Ethernet device not using DHCP: - Ethernet.begin(mac, ip, myDns, gateway, subnet); + Ethernet.begin(ip, subnet, gateway, myDns); } // print your local IP address: Serial.print("My IP address: "); diff --git a/examples/TelnetClient/TelnetClient.ino b/examples/TelnetClient/TelnetClient.ino index e503504..48fafe8 100644 --- a/examples/TelnetClient/TelnetClient.ino +++ b/examples/TelnetClient/TelnetClient.ino @@ -17,17 +17,15 @@ by Tom Igoe modified 23 Jun 2017 by Wi6Labs - + modified 1 Jun 2018 + by sstaub */ #include #include -// Enter a MAC address and IP address for your controller below. +// Enter an IP address for your controller below. // The IP address will be dependent on your local network: -byte mac[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED -}; IPAddress ip(192, 168, 1, 177); // Enter the IP address of the server you're connecting to: @@ -41,7 +39,7 @@ EthernetClient client; void setup() { // start the Ethernet connection: - Ethernet.begin(mac, ip); + Ethernet.begin(ip); // Open serial communications and wait for port to open: Serial.begin(9600); while (!Serial) { diff --git a/examples/UDPSendReceiveString/UDPSendReceiveString.ino b/examples/UDPSendReceiveString/UDPSendReceiveString.ino index 00e040b..7665fd9 100644 --- a/examples/UDPSendReceiveString/UDPSendReceiveString.ino +++ b/examples/UDPSendReceiveString/UDPSendReceiveString.ino @@ -10,7 +10,8 @@ by Michael Margolis modified 23 Jun 2017 by Wi6Labs - + modified 1 Jun 2018 + by sstaub This code is in the public domain. */ @@ -19,11 +20,8 @@ #include // UDP library from: bjoern@cs.stanford.edu 12/30/2008 -// Enter a MAC address and IP address for your controller below. +// Enter an IP address for your controller below. // The IP address will be dependent on your local network: -byte mac[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED -}; IPAddress ip(192, 168, 1, 177); unsigned int localPort = 8888; // local port to listen on @@ -37,7 +35,7 @@ EthernetUDP Udp; void setup() { // start the Ethernet and UDP: - Ethernet.begin(mac, ip); + Ethernet.begin(ip); Udp.begin(localPort); Serial.begin(9600); diff --git a/examples/UdpNtpClient/UdpNtpClient.ino b/examples/UdpNtpClient/UdpNtpClient.ino index 716c365..3ce84de 100644 --- a/examples/UdpNtpClient/UdpNtpClient.ino +++ b/examples/UdpNtpClient/UdpNtpClient.ino @@ -15,7 +15,8 @@ by Arturo Guadalupi modified 23 Jun 2017 by Wi6Labs - + modified 1 Jun 2018 + by sstaub This code is in the public domain. */ @@ -24,11 +25,6 @@ #include #include -// Enter a MAC address for your controller below. -byte mac[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED -}; - unsigned int localPort = 8888; // local port to listen for UDP packets char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server @@ -49,7 +45,7 @@ void setup() { // start Ethernet and UDP - if (Ethernet.begin(mac) == 0) { + if (Ethernet.begin() == 0) { Serial.println("Failed to configure Ethernet using DHCP"); // no point in carrying on, so do nothing forevermore: for (;;) diff --git a/examples/WebClient/WebClient.ino b/examples/WebClient/WebClient.ino index 18be08d..3592889 100644 --- a/examples/WebClient/WebClient.ino +++ b/examples/WebClient/WebClient.ino @@ -12,14 +12,13 @@ by Tom Igoe, based on work by Adrian McEwen modified 23 Jun 2017 by Wi6Labs - + modified 1 Jun 2018 + by sstaub */ #include #include -// Enter a MAC address for your controller below. -byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // if you don't want to use DNS (and reduce your sketch size) // use the numeric IP instead of the name for the server: //IPAddress server(74,125,232,128); // numeric IP for Google (no DNS) @@ -41,10 +40,10 @@ void setup() { } // start the Ethernet connection: - if (Ethernet.begin(mac) == 0) { + if (Ethernet.begin() == 0) { Serial.println("Failed to configure Ethernet using DHCP"); - // try to congifure using IP address instead of DHCP: - Ethernet.begin(mac, ip); + // try to configure using IP address instead of DHCP: + Ethernet.begin(ip); } // give the Ethernet shield a second to initialize: delay(1000); diff --git a/examples/WebClientFreeRTOS/WebClientFreeRTOS.ino b/examples/WebClientFreeRTOS/WebClientFreeRTOS.ino new file mode 100644 index 0000000..842f244 --- /dev/null +++ b/examples/WebClientFreeRTOS/WebClientFreeRTOS.ino @@ -0,0 +1,104 @@ +/* + Port of WebClient on FreeRTOS + + This sketch connects to a website (http://www.google.com) + + Circuit: + * STM32 board with Ethernet support + + created 18 Dec 2009 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe, based on work by Adrian McEwen + modified 23 Jun 2017 + by Wi6Labs + modified 1 Jun 2018 + by sstaub + */ + +#include +#include +#include + +// if you don't want to use DNS (and reduce your sketch size) +// use the numeric IP instead of the name for the server: +//IPAddress server(74,125,232,128); // numeric IP for Google (no DNS) +char server[] = "www.google.com"; // name address for Google (using DNS) + +// Set the static IP address to use if the DHCP fails to assign +IPAddress ip(192, 168, 0, 177); + +// Initialize the Ethernet client library +// with the IP address and port of the server +// that you want to connect to (port 80 is default for HTTP): +EthernetClient client; + + +// task code +void taskETH(void* arg) { + UNUSED(arg); + // start the Ethernet connection: + if (Ethernet.begin() == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // try to configure using IP address instead of DHCP: + Ethernet.begin(ip); + } + // give the Ethernet shield a second to initialize: + delay(1000); + Serial.println("connecting..."); + + // if you get a connection, report back via serial: + if (client.connect(server, 80)) { + Serial.println("connected"); + // Make a HTTP request: + client.println("GET /search?q=arduino HTTP/1.1"); + client.println("Host: www.google.com"); + client.println("Connection: close"); + client.println(); + } else { + // if you didn't get a connection to the server: + Serial.println("connection failed"); + } + + while(1){ + // if there are incoming bytes available + // from the server, read them and print them: + if (client.available()) { + char c = client.read(); + Serial.print(c); + } + + // if the server's disconnected, stop the client: + if (!client.connected()) { + Serial.println(); + Serial.println("disconnecting."); + client.stop(); + + // do nothing forevermore: + while (true); + } + } +} + + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + portBASE_TYPE s = xTaskCreate(taskETH, NULL, 200, NULL, 1, NULL); + if (s != pdPASS) { + printf("Ethernet task creation failed\n"); + while (1); + } + + vTaskStartScheduler(); + Serial.println("Scheduler failed"); + while (1); +} + +void loop() { + // Not used. +} diff --git a/examples/WebClientRepeating/WebClientRepeating.ino b/examples/WebClientRepeating/WebClientRepeating.ino index 61919d7..38482de 100644 --- a/examples/WebClientRepeating/WebClientRepeating.ino +++ b/examples/WebClientRepeating/WebClientRepeating.ino @@ -15,7 +15,8 @@ by Federico Vanzati modified 23 Jun 2017 by Wi6Labs - + modified 1 Jun 2018 + by sstaub http://www.arduino.cc/en/Tutorial/WebClientRepeating This code is in the public domain. @@ -24,15 +25,11 @@ #include #include -// assign a MAC address for the ethernet controller. -// fill in your address here: -byte mac[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED -}; // fill in an available IP address on your network here, // for manual configuration: IPAddress ip(192, 168, 1, 177); - +IPAddress gateway(192, 168, 1, 1); +IPAddress subnet(255, 255, 0, 0); // fill in your Domain Name Server address here: IPAddress myDns(1, 1, 1, 1); @@ -56,7 +53,7 @@ void setup() { // give the ethernet module time to boot up: delay(1000); // start the Ethernet connection using a fixed IP address and DNS server: - Ethernet.begin(mac, ip, myDns); + Ethernet.begin(ip, subnet, gateway, myDns); // print the Ethernet board/shield's IP address: Serial.print("My IP address: "); Serial.println(Ethernet.localIP()); diff --git a/examples/WebServer/WebServer.ino b/examples/WebServer/WebServer.ino index 3e6b318..9030ee4 100644 --- a/examples/WebServer/WebServer.ino +++ b/examples/WebServer/WebServer.ino @@ -15,17 +15,15 @@ by Arturo Guadalupi modified 23 Jun 2017 by Wi6Labs - + modified 1 Jun 2018 + by sstaub */ #include #include -// Enter a MAC address and IP address for your controller below. +// Enter an IP address for your controller below. // The IP address will be dependent on your local network: -byte mac[] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED -}; IPAddress ip(192, 168, 1, 177); // Initialize the Ethernet server library @@ -42,7 +40,7 @@ void setup() { // start the Ethernet connection and the server: - Ethernet.begin(mac, ip); + Ethernet.begin(ip); server.begin(); Serial.print("server is at "); Serial.println(Ethernet.localIP()); @@ -55,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(); diff --git a/examples/WebServerFreeRTOS/WebServerFreeRTOS.ino b/examples/WebServerFreeRTOS/WebServerFreeRTOS.ino new file mode 100644 index 0000000..f69b68b --- /dev/null +++ b/examples/WebServerFreeRTOS/WebServerFreeRTOS.ino @@ -0,0 +1,139 @@ +/* + Port of WebServer on FreeRTOS + + A simple web server that shows the value of the analog input pins. + 2 task are created: + Ethernet: to manage the server + Analog to read input values + + Circuit: + STM32 board with Ethernet support + Analog inputs attached to pins A0 through A5 (optional) + + created 18 Dec 2009 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe + modified 02 Sept 2015 + by Arturo Guadalupi + modified 23 Jun 2017 + by Wi6Labs + modified 1 Jun 2018 + by sstaub +*/ + +#include +#include +#include + +// Enter an IP address for your controller below. +// The IP address will be dependent on your local network: +IPAddress ip(192, 168, 0, 177); + +// Initialize the Ethernet server library +// with the IP address and port you want to use +// (port 80 is default for HTTP): +EthernetServer server(80); + +#define ANALOG_CHANEL_NUMBER 6 +int sensorReading[ANALOG_CHANEL_NUMBER]; + +void taskAnalog(void* arg) { + UNUSED(arg); + while (1) { + for (int analogChannel = 0; analogChannel < ANALOG_CHANEL_NUMBER; analogChannel++) { + sensorReading[analogChannel] = analogRead(analogChannel); + } + vTaskDelay(1000); // read Analog every seconds + } +} + +// task code +void taskETH(void* arg) { + UNUSED(arg); + // start the Ethernet connection and the server: + Ethernet.begin(ip); + server.begin(); + Serial.print("server is at "); + Serial.println(Ethernet.localIP()); + + while (1) { + // listen for incoming clients + EthernetClient client = server.available(); + if (client) { + Serial.println("new client"); + // an http request ends with a blank line + bool currentLineIsBlank = true; + while (client.connected()) { + if (client.available()) { + char c = client.read(); + Serial.write(c); + // if you've gotten to the end of the line (received a newline + // character) and the line is blank, the http request has ended, + // so you can send a reply + if (c == '\n' && currentLineIsBlank) { + // send a standard http response header + client.println("HTTP/1.1 200 OK"); + client.println("Content-Type: text/html"); + client.println("Connection: close"); // the connection will be closed after completion of the response + client.println("Refresh: 5"); // refresh the page automatically every 5 sec + client.println(); + client.println(""); + client.println(""); + // output the value of each analog input pin + for (int analogChannel = 0; analogChannel < ANALOG_CHANEL_NUMBER; analogChannel++) { + client.print("analog input "); + client.print(analogChannel); + client.print(" is "); + client.print(sensorReading[analogChannel]); + client.println("
"); + } + client.println(""); + break; + } + if (c == '\n') { + // you're starting a new line + currentLineIsBlank = true; + } else if (c != '\r') { + // you've gotten a character on the current line + currentLineIsBlank = false; + } + } + } + // give the web browser time to receive the data + delay(1); + // close the connection: + client.stop(); + Serial.println("client disconnected"); + } + } +} + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + portBASE_TYPE s = xTaskCreate(taskETH, NULL, 200, NULL, 1, NULL); + if (s != pdPASS) { + printf("Ethernet task creation failed\n"); + while (1); + } + + s = xTaskCreate(taskAnalog, NULL, 200, NULL, 2, NULL); + if (s != pdPASS) { + printf("Analog task creation failed\n"); + while (1); + } + + vTaskStartScheduler(); + Serial.println("Scheduler failed"); + while (1); +} + + +void loop() { + // Not used. +} \ No newline at end of file diff --git a/keywords.txt b/keywords.txt index 1f19b2a..469aa86 100644 --- a/keywords.txt +++ b/keywords.txt @@ -24,7 +24,9 @@ peek KEYWORD2 flush KEYWORD2 stop KEYWORD2 connected KEYWORD2 +accept KEYWORD2 begin KEYWORD2 +beginMulticast KEYWORD2 beginPacket KEYWORD2 endPacket KEYWORD2 parsePacket KEYWORD2 @@ -32,9 +34,19 @@ remoteIP KEYWORD2 remotePort KEYWORD2 getSocketNumber KEYWORD2 localIP KEYWORD2 +localPort KEYWORD2 maintain KEYWORD2 +linkStatus KEYWORD2 +MACAddress KEYWORD2 +setMACAddress KEYWORD2 +subnetMask KEYWORD2 +gatewayIP KEYWORD2 +dnsServerIP KEYWORD2 +setDnsServerIP KEYWORD2 +setConnectionTimeout KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### - +LinkON LITERAL1 +LinkOFF LITERAL1 diff --git a/library.json b/library.json new file mode 100755 index 0000000..f8fe867 --- /dev/null +++ b/library.json @@ -0,0 +1,16 @@ +{ + "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.4.0", + "frameworks": "arduino", + "platforms": "ststm32", + "build": { + "libArchive": false + } +} diff --git a/library.properties b/library.properties index 0cbb04a..a7e954a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=STM32duino STM32Ethernet -version=1.0.3 +version=1.4.0 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 diff --git a/src/Dhcp.cpp b/src/Dhcp.cpp index ac75c71..19c7705 100644 --- a/src/Dhcp.cpp +++ b/src/Dhcp.cpp @@ -1,129 +1,134 @@ -// DHCP Library v0.3 - April 25, 2009 -// Author: Jordan Terrell - blog.jordanterrell.com - -#include -#include -#include "Dhcp.h" -#include "Arduino.h" -#include "utility/stm32_eth.h" - -int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) -{ - UNUSED(responseTimeout); - _timeout = timeout; - _dhcp_lease_state = DHCP_CHECK_NONE; - - // zero out _dhcpMacAddr - memset(_dhcpMacAddr, 0, 6); - reset_DHCP_lease(); - - memcpy((void*)_dhcpMacAddr, (void*)mac, 6); - _dhcp_state = STATE_DHCP_START; - stm32_set_DHCP_state(_dhcp_state); - return request_DHCP_lease(); -} - -void DhcpClass::reset_DHCP_lease(){ - _dhcp_state = STATE_DHCP_RELEASE; - stm32_set_DHCP_state(_dhcp_state); - stm32_eth_scheduler(); - _dhcp_state = stm32_get_DHCP_state(); -} - -//return:0 on error, 1 if request is sent and response is received -int DhcpClass::request_DHCP_lease(){ - - int result = 0; - unsigned long startTime = millis(); - - while(_dhcp_state != STATE_DHCP_LEASED) - { - stm32_eth_scheduler(); - _dhcp_state = stm32_get_DHCP_state(); - - if(result != 1 && ((millis() - startTime) > _timeout)) { - reset_DHCP_lease(); - break; - } - } - - if(_dhcp_state == STATE_DHCP_LEASED) { - result = 1; - } - - return result; -} - -/* - returns: - 0/DHCP_CHECK_NONE: nothing happened - 1/DHCP_CHECK_RENEW_FAIL: renew failed - 2/DHCP_CHECK_RENEW_OK: renew success - 3/DHCP_CHECK_REBIND_FAIL: rebind fail - 4/DHCP_CHECK_REBIND_OK: rebind success -*/ -int DhcpClass::checkLease(){ - int rc = DHCP_CHECK_NONE; - - stm32_eth_scheduler(); - rc = stm32_get_DHCP_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; - } - } - - return rc; -} - -IPAddress DhcpClass::getLocalIp() -{ - return IPAddress(stm32_eth_get_ipaddr()); -} - -IPAddress DhcpClass::getSubnetMask() -{ - return IPAddress(stm32_eth_get_netmaskaddr()); -} - -IPAddress DhcpClass::getGatewayIp() -{ - return IPAddress(stm32_eth_get_gwaddr()); -} - -IPAddress DhcpClass::getDhcpServerIp() -{ - return IPAddress(stm32_eth_get_dhcpaddr()); -} - -IPAddress DhcpClass::getDnsServerIp() -{ - return IPAddress(stm32_eth_get_dnsaddr()); -} +// DHCP Library v0.3 - April 25, 2009 +// Author: Jordan Terrell - blog.jordanterrell.com + +#include +#include +#include "Dhcp.h" +#include "Arduino.h" +#include "utility/stm32_eth.h" + +int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) +{ + UNUSED(responseTimeout); + _timeout = timeout; + _dhcp_lease_state = DHCP_CHECK_NONE; + + // zero out _dhcpMacAddr + memset(_dhcpMacAddr, 0, 6); + reset_DHCP_lease(); + if (mac == NULL) { + // use mac from Ethernet chip + stm32_eth_get_macaddr(_dhcpMacAddr); + } else { + memcpy((void *)_dhcpMacAddr, (void *)mac, 6); + } + _dhcp_state = STATE_DHCP_START; + stm32_set_DHCP_state(_dhcp_state); + return request_DHCP_lease(); +} + +void DhcpClass::reset_DHCP_lease() +{ + _dhcp_state = STATE_DHCP_RELEASE; + stm32_set_DHCP_state(_dhcp_state); + stm32_eth_scheduler(); + _dhcp_state = stm32_get_DHCP_state(); +} + +//return:0 on error, 1 if request is sent and response is received +int DhcpClass::request_DHCP_lease() +{ + + int result = 0; + unsigned long startTime = millis(); + + while (_dhcp_state != STATE_DHCP_LEASED) { + stm32_eth_scheduler(); + _dhcp_state = stm32_get_DHCP_state(); + + if (result != 1 && ((millis() - startTime) > _timeout)) { + reset_DHCP_lease(); + break; + } + } + + if (_dhcp_state == STATE_DHCP_LEASED) { + result = 1; + } + + return result; +} + +/* + returns: + 0/DHCP_CHECK_NONE: nothing happened + 1/DHCP_CHECK_RENEW_FAIL: renew failed + 2/DHCP_CHECK_RENEW_OK: renew success + 3/DHCP_CHECK_REBIND_FAIL: rebind fail + 4/DHCP_CHECK_REBIND_OK: rebind success +*/ +int DhcpClass::checkLease() +{ + int rc = DHCP_CHECK_NONE; + + stm32_eth_scheduler(); + rc = stm32_get_DHCP_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; + } + } + + return rc; +} + +IPAddress DhcpClass::getLocalIp() +{ + return IPAddress(stm32_eth_get_ipaddr()); +} + +IPAddress DhcpClass::getSubnetMask() +{ + return IPAddress(stm32_eth_get_netmaskaddr()); +} + +IPAddress DhcpClass::getGatewayIp() +{ + return IPAddress(stm32_eth_get_gwaddr()); +} + +IPAddress DhcpClass::getDhcpServerIp() +{ + return IPAddress(stm32_eth_get_dhcpaddr()); +} + +IPAddress DhcpClass::getDnsServerIp() +{ + return IPAddress(stm32_eth_get_dnsaddr()); +} diff --git a/src/Dhcp.h b/src/Dhcp.h index 089fbde..1467b18 100644 --- a/src/Dhcp.h +++ b/src/Dhcp.h @@ -1,45 +1,45 @@ -// DHCP Library v0.3 - April 25, 2009 -// Author: Jordan Terrell - blog.jordanterrell.com - -#ifndef Dhcp_h -#define Dhcp_h - -#include "EthernetUdp.h" - -/* DHCP state machine. */ -#define STATE_DHCP_STOP DHCP_OFF -#define STATE_DHCP_START DHCP_START -#define STATE_DHCP_DISCOVER DHCP_WAIT_ADDRESS -#define STATE_DHCP_REQUEST 0 -#define STATE_DHCP_LEASED DHCP_ADDRESS_ASSIGNED -#define STATE_DHCP_REREQUEST 0 -#define STATE_DHCP_RELEASE DHCP_ASK_RELEASE - -#define DHCP_CHECK_NONE (0) -#define DHCP_CHECK_RENEW_FAIL (1) -#define DHCP_CHECK_RENEW_OK (2) -#define DHCP_CHECK_REBIND_FAIL (3) -#define DHCP_CHECK_REBIND_OK (4) - -class DhcpClass { -private: - uint8_t _dhcpMacAddr[6]; - unsigned long _timeout; - uint8_t _dhcp_lease_state; - uint8_t _dhcp_state; - - int request_DHCP_lease(); - void reset_DHCP_lease(); - -public: - IPAddress getLocalIp(); - IPAddress getSubnetMask(); - IPAddress getGatewayIp(); - IPAddress getDhcpServerIp(); - IPAddress getDnsServerIp(); - - int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); - int checkLease(); -}; - -#endif +// DHCP Library v0.3 - April 25, 2009 +// Author: Jordan Terrell - blog.jordanterrell.com + +#ifndef Dhcp_h +#define Dhcp_h + +#include "EthernetUdp.h" + +/* DHCP state machine. */ +#define STATE_DHCP_STOP DHCP_OFF +#define STATE_DHCP_START DHCP_START +#define STATE_DHCP_DISCOVER DHCP_WAIT_ADDRESS +#define STATE_DHCP_REQUEST 0 +#define STATE_DHCP_LEASED DHCP_ADDRESS_ASSIGNED +#define STATE_DHCP_REREQUEST 0 +#define STATE_DHCP_RELEASE DHCP_ASK_RELEASE + +#define DHCP_CHECK_NONE (0) +#define DHCP_CHECK_RENEW_FAIL (1) +#define DHCP_CHECK_RENEW_OK (2) +#define DHCP_CHECK_REBIND_FAIL (3) +#define DHCP_CHECK_REBIND_OK (4) + +class DhcpClass { + private: + uint8_t _dhcpMacAddr[6]; + unsigned long _timeout; + uint8_t _dhcp_lease_state; + uint8_t _dhcp_state; + + int request_DHCP_lease(); + void reset_DHCP_lease(); + + public: + IPAddress getLocalIp(); + IPAddress getSubnetMask(); + IPAddress getGatewayIp(); + IPAddress getDhcpServerIp(); + IPAddress getDnsServerIp(); + + int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + int checkLease(); +}; + +#endif diff --git a/src/Dns.cpp b/src/Dns.cpp index 434c139..717b24b 100644 --- a/src/Dns.cpp +++ b/src/Dns.cpp @@ -16,89 +16,82 @@ #define TRUNCATED -3 #define INVALID_RESPONSE -4 -void DNSClient::begin(const IPAddress& aDNSServer) +void DNSClient::begin(const IPAddress &aDNSServer) { - iDNSServer = aDNSServer; - stm32_dns_init(iDNSServer.raw_address()); + iDNSServer = aDNSServer; + stm32_dns_init(iDNSServer.raw_address()); } -int DNSClient::inet_aton(const char* address, IPAddress& result) +int DNSClient::inet_aton(const char *address, IPAddress &result) { - uint16_t acc = 0; // Accumulator - uint8_t dots = 0; - - if(address == NULL) - return 0; - - 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; - } - } - - if (dots != 3) { - // Too few dots (there must be 3 dots) + uint16_t acc = 0; // Accumulator + uint8_t dots = 0; + + if (address == NULL) { + return 0; + } + + 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; } - result[3] = acc; - return 1; + } + + if (dots != 3) { + // Too few dots (there must be 3 dots) + return 0; + } + result[3] = acc; + return 1; } -int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) +int DNSClient::getHostByName(const char *aHostname, IPAddress &aResult) { - int ret = 0; - uint32_t ipResult = 0; - - // See if it's a numeric IP address - if (inet_aton(aHostname, aResult)) - { - // It is, our work here is done - return SUCCESS; - } + int ret = 0; + uint32_t ipResult = 0; - // Check we've got a valid DNS server to use - if (iDNSServer == INADDR_NONE) - { - return INVALID_SERVER; - } + // See if it's a numeric IP address + if (inet_aton(aHostname, aResult)) { + // It is, our work here is done + return SUCCESS; + } + + // Check we've got a valid DNS server to use + if (iDNSServer == INADDR_NONE) { + return INVALID_SERVER; + } - ret = stm32_dns_gethostbyname(aHostname, &ipResult); - aResult = IPAddress(ipResult); + ret = stm32_dns_gethostbyname(aHostname, &ipResult); + aResult = IPAddress(ipResult); - return ret; + return ret; } /* Deprecated function. Do not use anymore. */ -uint16_t DNSClient::BuildRequest(const char* aName) +uint16_t DNSClient::BuildRequest(const char *aName) { UNUSED(aName); return 0; } /* Deprecated function. Do not use anymore. */ -uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) +uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress &aAddress) { UNUSED(aTimeout); UNUSED(aAddress); diff --git a/src/Dns.h b/src/Dns.h index 6f2773f..5de2d4f 100644 --- a/src/Dns.h +++ b/src/Dns.h @@ -1,39 +1,38 @@ -// Arduino DNS client for WizNet5100-based Ethernet shield -// (c) Copyright 2009-2010 MCQN Ltd. -// Released under Apache License, version 2.0 - -#ifndef DNSClient_h -#define DNSClient_h - -#include - -class DNSClient -{ -public: - // ctor - void begin(const IPAddress& aDNSServer); - - /** Convert a numeric IP address string into a four-byte IP address. - @param aIPAddrString IP address to convert - @param aResult IPAddress structure to store the returned IP address - @result 1 if aIPAddrString was successfully converted to an IP address, - else error code - */ - int inet_aton(const char *aIPAddrString, IPAddress& aResult); - - /** Resolve the given hostname to an IP address. - @param aHostname Name to be resolved - @param aResult IPAddress structure to store the returned IP address - @result 1 if aIPAddrString was successfully converted to an IP address, - else error code - */ - int getHostByName(const char* aHostname, IPAddress& aResult); - -protected: - uint16_t BuildRequest(const char* aName); - uint16_t ProcessResponse(uint16_t aTimeout, IPAddress& aAddress); - - IPAddress iDNSServer; -}; - -#endif +// Arduino DNS client for WizNet5100-based Ethernet shield +// (c) Copyright 2009-2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#ifndef DNSClient_h +#define DNSClient_h + +#include + +class DNSClient { + public: + // ctor + void begin(const IPAddress &aDNSServer); + + /** Convert a numeric IP address string into a four-byte IP address. + @param aIPAddrString IP address to convert + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, + else error code + */ + int inet_aton(const char *aIPAddrString, IPAddress &aResult); + + /** Resolve the given hostname to an IP address. + @param aHostname Name to be resolved + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, + else error code + */ + int getHostByName(const char *aHostname, IPAddress &aResult); + + protected: + uint16_t BuildRequest(const char *aName); + uint16_t ProcessResponse(uint16_t aTimeout, IPAddress &aAddress); + + IPAddress iDNSServer; +}; + +#endif diff --git a/src/EthernetClient.cpp b/src/EthernetClient.cpp index f02c486..5941afd 100644 --- a/src/EthernetClient.cpp +++ b/src/EthernetClient.cpp @@ -1,5 +1,5 @@ extern "C" { - #include "string.h" +#include "string.h" } #include "Arduino.h" @@ -9,20 +9,26 @@ 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); } -EthernetClient::EthernetClient(struct tcp_struct *tcpClient) { +EthernetClient::EthernetClient(struct tcp_struct *tcpClient) +{ _tcp_client = tcpClient; } -int EthernetClient::connect(const char* host, uint16_t port) { +int EthernetClient::connect(const char *host, uint16_t port) +{ // Look up the host first int ret = 0; DNSClient dns; @@ -33,27 +39,25 @@ int EthernetClient::connect(const char* host, uint16_t port) { if (ret == 1) { return connect(remote_addr, port); } else { - return ret; - } -} - -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)); +int EthernetClient::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)); - if(_tcp_client == NULL) { - return 0; + if (_tcp_client == NULL) { + return 0; + } } /* Creates a new TCP protocol control block */ _tcp_client->pcb = tcp_new(); - if(_tcp_client->pcb == NULL) { + if (_tcp_client->pcb == NULL) { return 0; } @@ -61,17 +65,18 @@ int EthernetClient::connect(IPAddress ip, uint16_t port) { _tcp_client->data.available = 0; _tcp_client->state = TCP_NONE; + 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)) { + if (ERR_OK != tcp_connect(_tcp_client->pcb, u8_to_ip_addr(rawIPAddress(ip), &ipaddr), port, &tcp_connected_callback)) { stop(); return 0; } - uint32_t startTime = millis(); - while(_tcp_client->state == TCP_NONE) { + startTime = millis(); + while (_tcp_client->state == TCP_NONE) { stm32_eth_scheduler(); - if((_tcp_client->state == TCP_CLOSING) || ((millis() - startTime) >= 10000)) { + if ((_tcp_client->state == TCP_CLOSING) || ((millis() - startTime) >= _connectionTimeout)) { stop(); return 0; } @@ -80,48 +85,70 @@ int EthernetClient::connect(IPAddress ip, uint16_t port) { return 1; } -size_t EthernetClient::write(uint8_t b) { +size_t EthernetClient::write(uint8_t b) +{ return write(&b, 1); } -size_t EthernetClient::write(const uint8_t *buf, size_t size) { - if( (_tcp_client == NULL) || (_tcp_client->pcb == NULL) || +size_t EthernetClient::write(const uint8_t *buf, size_t size) +{ + if ((_tcp_client == NULL) || (_tcp_client->pcb == NULL) || (buf == NULL) || (size == 0)) { return 0; } /* 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)) { + if ((_tcp_client->state != TCP_ACCEPTED) && + (_tcp_client->state != TCP_CONNECTED)) { return 0; } - if(ERR_OK != tcp_write(_tcp_client->pcb, buf, size, TCP_WRITE_FLAG_COPY)) { - return 0; - } + size_t max_send_size, bytes_to_send; + size_t bytes_sent = 0; + size_t bytes_left = size; + err_t res; - //Force to send data right now! - if(ERR_OK != tcp_output(_tcp_client->pcb)) { - return 0; - } + do { + max_send_size = tcp_sndbuf(_tcp_client->pcb); + bytes_to_send = bytes_left > max_send_size ? max_send_size : bytes_left; - stm32_eth_scheduler(); + 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; + } + } + + //Force to send data right now! + if (ERR_OK != tcp_output(_tcp_client->pcb)) { + return 0; + } + stm32_eth_scheduler(); + + } while (bytes_sent != size); return size; } -int EthernetClient::available() { +int EthernetClient::available() +{ stm32_eth_scheduler(); - if(_tcp_client != NULL) { + if (_tcp_client != NULL) { return _tcp_client->data.available; } return 0; } -int EthernetClient::read() { +int EthernetClient::read() +{ uint8_t b; - if((_tcp_client != NULL) && (_tcp_client->data.p != NULL)) { + if ((_tcp_client != NULL) && (_tcp_client->data.p != NULL)) { stm32_get_data(&(_tcp_client->data), &b, 1); return b; } @@ -129,49 +156,58 @@ int EthernetClient::read() { return -1; } -int EthernetClient::read(uint8_t *buf, size_t size) { - if((_tcp_client != NULL) && (_tcp_client->data.p != NULL)) { +int EthernetClient::read(uint8_t *buf, size_t size) +{ + if ((_tcp_client != NULL) && (_tcp_client->data.p != NULL)) { return stm32_get_data(&(_tcp_client->data), buf, size); } return -1; } -int EthernetClient::peek() { +int EthernetClient::peek() +{ uint8_t b; // Unlike recv, peek doesn't check to see if there's any data available, so we must - if (!available()) + if (!available()) { return -1; - b = pbuf_get_at(_tcp_client->data.p, 0); + } + b = pbuf_get_at(_tcp_client->data.p, _tcp_client->data.p->tot_len - _tcp_client->data.available); return b; } -void EthernetClient::flush() { - if((_tcp_client == NULL) || (_tcp_client->pcb == NULL)) { +void EthernetClient::flush() +{ + if ((_tcp_client == NULL) || (_tcp_client->pcb == NULL)) { return; } tcp_output(_tcp_client->pcb); stm32_eth_scheduler(); } -void EthernetClient::stop() { - if(_tcp_client == NULL) { +void EthernetClient::stop() +{ + if (_tcp_client == NULL) { return; } // close tcp connection if not closed yet - if(status() != TCP_CLOSING) { + if (status() != TCP_CLOSING) { tcp_connection_close(_tcp_client->pcb, _tcp_client); } + mem_free(_tcp_client); + _tcp_client = NULL; } -uint8_t EthernetClient::connected() { +uint8_t EthernetClient::connected() +{ uint8_t s = status(); return ((available() && (s == TCP_CLOSING)) || (s == TCP_CONNECTED) || (s == TCP_ACCEPTED)); } -uint8_t EthernetClient::status() { - if(_tcp_client == NULL) { +uint8_t EthernetClient::status() +{ + if (_tcp_client == NULL) { return TCP_NONE; } return _tcp_client->state; @@ -180,17 +216,20 @@ uint8_t EthernetClient::status() { // the next function allows us to use the client returned by // EthernetServer::available() as the condition in an if-statement. -EthernetClient::operator bool() { - return _tcp_client != NULL; +EthernetClient::operator bool() +{ + return (_tcp_client && (_tcp_client->state != TCP_CLOSING)); } -bool EthernetClient::operator==(const EthernetClient& rhs) { +bool EthernetClient::operator==(const EthernetClient &rhs) +{ return _tcp_client == rhs._tcp_client && _tcp_client->pcb == rhs._tcp_client->pcb; } /* This function is not a function defined by Arduino. This is a function specific to the W5100 architecture. To keep the compatibility we leave it and returns always 0. */ -uint8_t EthernetClient::getSocketNumber() { +uint8_t EthernetClient::getSocketNumber() +{ return 0; } diff --git a/src/EthernetClient.h b/src/EthernetClient.h index 1cf73d8..5afb160 100644 --- a/src/EthernetClient.h +++ b/src/EthernetClient.h @@ -8,36 +8,62 @@ class EthernetClient : public Client { -public: - EthernetClient(); - EthernetClient(uint8_t sock); - EthernetClient(struct tcp_struct *tcpClient); + public: + EthernetClient(); + EthernetClient(uint8_t sock); + EthernetClient(struct tcp_struct *tcpClient); - 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(); - virtual int read(); - virtual int read(uint8_t *buf, size_t size); - virtual int peek(); - virtual void flush(); - virtual void stop(); - virtual uint8_t connected(); - virtual operator bool(); - virtual bool operator==(const bool value) { return bool() == value; } - virtual bool operator!=(const bool value) { return bool() != value; } - virtual bool operator==(const EthernetClient&); - virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; - uint8_t getSocketNumber(); + 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(); + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual int peek(); + virtual void flush(); + virtual void stop(); + virtual uint8_t connected(); + virtual operator bool(); + virtual bool operator==(const bool value) + { + return bool() == value; + } + virtual bool operator!=(const bool value) + { + return bool() != value; + } + 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); + }; + void setConnectionTimeout(uint16_t timeout) + { + _connectionTimeout = timeout; + } - friend class EthernetServer; + friend class EthernetServer; - using Print::write; + using Print::write; -private: - struct tcp_struct *_tcp_client; + private: + struct tcp_struct *_tcp_client; + uint16_t _connectionTimeout = 10000; }; #endif diff --git a/src/EthernetServer.cpp b/src/EthernetServer.cpp index f5353d8..2082498 100644 --- a/src/EthernetServer.cpp +++ b/src/EthernetServer.cpp @@ -9,24 +9,28 @@ extern "C" { EthernetServer::EthernetServer(uint16_t port) { _port = port; + for (int i = 0; i < MAX_CLIENT; i++) { + _tcp_client[i] = {}; + } + _tcp_server = {}; } void EthernetServer::begin() { - if(_tcp_server.pcb != NULL) { + if (_tcp_server.pcb != NULL) { return; } _tcp_server.pcb = tcp_new(); - if(_tcp_server.pcb == NULL) { + if (_tcp_server.pcb == NULL) { return; } tcp_arg(_tcp_server.pcb, &_tcp_client); _tcp_server.state = TCP_NONE; - if(ERR_OK != tcp_bind(_tcp_server.pcb, IP_ADDR_ANY, _port)) { + if (ERR_OK != tcp_bind(_tcp_server.pcb, IP_ADDR_ANY, _port)) { memp_free(MEMP_TCP_PCB, _tcp_server.pcb); _tcp_server.pcb = NULL; return; @@ -36,17 +40,39 @@ void EthernetServer::begin() tcp_accept(_tcp_server.pcb, tcp_accept_callback); } +void EthernetServer::begin(uint16_t port) +{ + _port = port; + begin(); +} + +void EthernetServer::end(void) +{ + /* Free client */ + for (int n = 0; n < MAX_CLIENT; n++) { + if (_tcp_client[n] != NULL) { + EthernetClient client(_tcp_client[n]); + client.stop(); + _tcp_client[n] = NULL; + } + } + if (_tcp_server.pcb != NULL) { + tcp_close(_tcp_server.pcb); + _tcp_server.pcb = NULL; + } +} + void EthernetServer::accept() { /* Free client if disconnected */ for (int n = 0; n < MAX_CLIENT; n++) { - if(_tcp_client[n] != NULL) { - EthernetClient client(_tcp_client[n]); - if (client.status() == TCP_CLOSING) { - mem_free(_tcp_client[n]); - _tcp_client[n] = NULL; - } + if (_tcp_client[n] != NULL) { + EthernetClient client(_tcp_client[n]); + if (client.status() == TCP_CLOSING) { + mem_free(_tcp_client[n]); + _tcp_client[n] = NULL; } + } } } @@ -55,8 +81,8 @@ EthernetClient EthernetServer::available() accept(); for (int n = 0; n < MAX_CLIENT; n++) { - if(_tcp_client[n] != NULL) { - if(_tcp_client[n]->pcb != NULL) { + 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) { @@ -83,10 +109,10 @@ size_t EthernetServer::write(const uint8_t *buffer, size_t size) 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]); + for (int i = 0; i < MAX_CLIENT; i++) { + if (_tcp_client[i] != NULL) { + if (_tcp_client[i]->pcb != NULL) { + EthernetClient client(_tcp_client[i]); uint8_t s = client.status(); if (s == TCP_ACCEPTED) { n += client.write(buffer, size); @@ -97,3 +123,9 @@ size_t EthernetServer::write(const uint8_t *buffer, size_t size) return n; } + +EthernetServer::operator bool() +{ + // server is listening for incoming clients + return ((_tcp_server.pcb != NULL) && (_tcp_server.pcb->state == LISTEN)); +} diff --git a/src/EthernetServer.h b/src/EthernetServer.h index 317bf41..10c306c 100644 --- a/src/EthernetServer.h +++ b/src/EthernetServer.h @@ -6,20 +6,23 @@ class EthernetClient; class EthernetServer : -public Server { -private: - uint16_t _port; - struct tcp_struct _tcp_server; - struct tcp_struct *_tcp_client[MAX_CLIENT]; - - void accept(void); -public: - EthernetServer(uint16_t); - EthernetClient available(); - virtual void begin(); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - using Print::write; + public Server { + private: + uint16_t _port; + struct tcp_struct _tcp_server; + struct tcp_struct *_tcp_client[MAX_CLIENT]; + + void accept(void); + public: + EthernetServer(uint16_t port = 80); + EthernetClient available(); + virtual void begin(); + virtual void begin(uint16_t port); + void end(void); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual operator bool(); + using Print::write; }; #endif diff --git a/src/EthernetUdp.cpp b/src/EthernetUdp.cpp index c5ae45c..443f898 100644 --- a/src/EthernetUdp.cpp +++ b/src/EthernetUdp.cpp @@ -25,35 +25,55 @@ * * 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) { +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) { + if (_udp.pcb != NULL) { return 0; } _udp.pcb = udp_new(); - if(_udp.pcb == NULL) { + if (_udp.pcb == NULL) { 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; @@ -66,14 +86,15 @@ uint8_t EthernetUDP::begin(uint16_t port) { /* return number of bytes available in the current packet, will return zero if parsePacket hasn't been called yet */ -int EthernetUDP::available() { +int EthernetUDP::available() +{ return _remaining; } /* Release any resources being used by this EthernetUDP instance */ void EthernetUDP::stop() { - if(_udp.pcb != NULL) { + if (_udp.pcb != NULL) { udp_disconnect(_udp.pcb); udp_remove(_udp.pcb); _udp.pcb = NULL; @@ -100,15 +121,12 @@ int EthernetUDP::beginPacket(const char *host, uint16_t port) int EthernetUDP::beginPacket(IPAddress ip, uint16_t port) { - if(_udp.pcb == NULL) { + if (_udp.pcb == NULL) { return 0; } - 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(); @@ -118,16 +136,12 @@ int EthernetUDP::beginPacket(IPAddress ip, uint16_t port) int EthernetUDP::endPacket() { - if((_udp.pcb == NULL) || (_data == NULL)) { - 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) { + if ((_udp.pcb == NULL) || (_data == NULL)) { 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; } @@ -146,7 +160,7 @@ size_t EthernetUDP::write(uint8_t byte) size_t EthernetUDP::write(const uint8_t *buffer, size_t size) { _data = stm32_new_data(_data, buffer, size); - if(_data == NULL) { + if (_data == NULL) { return 0; } @@ -165,8 +179,7 @@ int EthernetUDP::parsePacket() stm32_eth_scheduler(); - if (_udp.data.available > 0) - { + if (_udp.data.available > 0) { _remoteIP = IPAddress(ip_addr_to_u32(&(_udp.ip))); _remotePort = _udp.port; _remaining = _udp.data.available; @@ -181,44 +194,39 @@ int EthernetUDP::read() { uint8_t byte; - if(_udp.data.p == NULL) { + if (_udp.data.p == NULL) { return -1; } - if (_remaining > 0) - { - if(read(&byte, 1) > 0) + if (_remaining > 0) { + if (read(&byte, 1) > 0) { return byte; + } } // If we get here, there's no data available return -1; } -int EthernetUDP::read(unsigned char* buffer, size_t len) +int EthernetUDP::read(unsigned char *buffer, size_t len) { - if(_udp.data.p == NULL) { + if (_udp.data.p == NULL) { return -1; } - if (_remaining > 0) - { + if (_remaining > 0) { int got; - if (_remaining <= len) - { + if (_remaining <= len) { // data should fit in the buffer got = (int)stm32_get_data(&(_udp.data), (uint8_t *)buffer, _remaining); - } - else - { + } else { // too much data for the buffer, // grab as much as will fit got = (int)stm32_get_data(&(_udp.data), (uint8_t *)buffer, len); } - if (got > 0) - { + if (got > 0) { _remaining -= got; return got; } @@ -236,9 +244,10 @@ int EthernetUDP::peek() // Unlike recv, peek doesn't check to see if there's any data available, so we must. // If the user hasn't called parsePacket yet then return nothing otherwise they // may get the UDP header - if (!_remaining) + if (!_remaining) { return -1; - b = pbuf_get_at(_udp.data.p, 0); + } + b = pbuf_get_at(_udp.data.p, _udp.data.p->tot_len - _udp.data.available); return b; } @@ -250,6 +259,12 @@ 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 8d7d878..a9864cb 100644 --- a/src/EthernetUdp.h +++ b/src/EthernetUdp.h @@ -38,70 +38,82 @@ #define ethernetudp_h #include +#include -extern "C" { #include "utility/stm32_eth.h" -} #define UDP_TX_PACKET_MAX_SIZE 24 class EthernetUDP : public UDP { -private: - 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 + private: + 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 + struct pbuf *_data; //pbuf for data to send + struct udp_struct _udp; //udp settings -protected: - uint16_t _remaining; // remaining bytes of incoming packet yet to be processed + protected: + uint16_t _remaining; // remaining bytes of incoming packet yet to be processed -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 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 + 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 - // Sending UDP packets + // Sending UDP packets - // Start building up a packet to send to the remote host specific in ip and port - // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port - virtual int beginPacket(IPAddress ip, uint16_t port); - // Start building up a packet to send to the remote host specific in host and port - // Returns 1 if successful, 0 if there was a problem resolving the hostname or port - virtual int beginPacket(const char *host, uint16_t port); - // Finish off this packet and send it - // Returns 1 if the packet was sent successfully, 0 if there was an error - virtual int endPacket(); - // Write a single byte into the packet - virtual size_t write(uint8_t); - // Write size bytes from buffer into the packet - virtual size_t write(const uint8_t *buffer, size_t size); + // Start building up a packet to send to the remote host specific in ip and port + // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port + virtual int beginPacket(IPAddress ip, uint16_t port); + // Start building up a packet to send to the remote host specific in host and port + // Returns 1 if successful, 0 if there was a problem resolving the hostname or port + virtual int beginPacket(const char *host, uint16_t port); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + virtual int endPacket(); + // Write a single byte into the packet + virtual size_t write(uint8_t); + // Write size bytes from buffer into the packet + virtual size_t write(const uint8_t *buffer, size_t size); - using Print::write; + using Print::write; - // Start processing the next available incoming packet - // Returns the size of the packet in bytes, or 0 if no packets are available - virtual int parsePacket(); - // Number of bytes remaining in the current packet - virtual int available(); - // Read a single byte from the current packet - virtual int read(); - // Read up to len bytes from the current packet and place them into buffer - // Returns the number of bytes read, or 0 if none are available - virtual int read(unsigned char* buffer, size_t len); - // Read up to len characters from the current packet and place them into buffer - // Returns the number of characters read, or 0 if none are available - virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); }; - // Return the next byte from the current packet without moving on to the next byte - virtual int peek(); - virtual void flush(); // Finish reading the current packet + // Start processing the next available incoming packet + // Returns the size of the packet in bytes, or 0 if no packets are available + virtual int parsePacket(); + // Number of bytes remaining in the current packet + virtual int available(); + // Read a single byte from the current packet + virtual int read(); + // Read up to len bytes from the current packet and place them into buffer + // Returns the number of bytes read, or 0 if none are available + virtual int read(unsigned char *buffer, size_t len); + // Read up to len characters from the current packet and place them into buffer + // Returns the number of characters read, or 0 if none are available + virtual int read(char *buffer, size_t len) + { + return read((unsigned char *)buffer, len); + }; + // Return the next byte from the current packet without moving on to the next byte + virtual int peek(); + virtual void flush(); // Finish reading the current packet - // Return the IP address of the host who sent the current incoming packet - virtual IPAddress remoteIP() { return _remoteIP; }; - // Return the port of the host who sent the current incoming packet - virtual uint16_t remotePort() { return _remotePort; }; + // Return the IP address of the host who sent the current incoming packet + 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/STM32Ethernet.cpp b/src/STM32Ethernet.cpp index 0f806c5..1e98284 100644 --- a/src/STM32Ethernet.cpp +++ b/src/STM32Ethernet.cpp @@ -1,6 +1,51 @@ #include "STM32Ethernet.h" #include "Dhcp.h" +int EthernetClass::begin(unsigned long timeout, unsigned long responseTimeout) +{ + static DhcpClass s_dhcp; + _dhcp = &s_dhcp; + stm32_eth_init(NULL, NULL, NULL, NULL); + + // Now try to get our config info from a DHCP server + int ret = _dhcp->beginWithDHCP(NULL, timeout, responseTimeout); + if (ret == 1) { + _dnsServerAddress = _dhcp->getDnsServerIp(); + } + + return ret; +} + +void EthernetClass::begin(IPAddress local_ip) +{ + IPAddress subnet(255, 255, 255, 0); + begin(local_ip, subnet); +} + +void EthernetClass::begin(IPAddress local_ip, IPAddress subnet) +{ + // 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; + begin(local_ip, subnet, gateway); +} + +void EthernetClass::begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway) +{ + // Assume the DNS server will be the same machine than gateway + begin(local_ip, subnet, gateway, gateway); +} + +void EthernetClass::begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway, IPAddress dns_server) +{ + stm32_eth_init(NULL, local_ip.raw_address(), gateway.raw_address(), subnet.raw_address()); + /* If there is a local DHCP informs it of our manual IP configuration to + prevent IP conflict */ + stm32_DHCP_manual_config(); + _dnsServerAddress = dns_server; +} + int EthernetClass::begin(uint8_t *mac_address, unsigned long timeout, unsigned long responseTimeout) { static DhcpClass s_dhcp; @@ -10,11 +55,9 @@ int EthernetClass::begin(uint8_t *mac_address, unsigned long timeout, unsigned l // Now try to get our config info from a DHCP server int ret = _dhcp->beginWithDHCP(mac_address, timeout, responseTimeout); - if(ret == 1) - { + if (ret == 1) { _dnsServerAddress = _dhcp->getDnsServerIp(); } - return ret; } @@ -42,22 +85,28 @@ void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dn begin(mac_address, local_ip, dns_server, gateway, subnet); } -void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) { - stm32_eth_init(mac, local_ip.raw_address(), gateway.raw_address(), subnet.raw_address()); + stm32_eth_init(mac_address, local_ip.raw_address(), gateway.raw_address(), subnet.raw_address()); /* If there is a local DHCP informs it of our manual IP configuration to prevent IP conflict */ stm32_DHCP_manual_config(); _dnsServerAddress = dns_server; } -int EthernetClass::maintain(){ +EthernetLinkStatus EthernetClass::linkStatus() +{ + return (!stm32_eth_is_init()) ? Unknown : (stm32_eth_link_up() ? LinkON : LinkOFF); +} + +int EthernetClass::maintain() +{ int rc = DHCP_CHECK_NONE; - if(_dhcp != NULL){ + if (_dhcp != NULL) { //we have a pointer to dhcp, use it rc = _dhcp->checkLease(); - switch ( rc ){ + switch (rc) { case DHCP_CHECK_NONE: //nothing done break; @@ -82,6 +131,16 @@ void EthernetClass::schedule(void) stm32_eth_scheduler(); } +void EthernetClass::setMACAddress(const uint8_t *mac_address) +{ + stm32_eth_set_macaddr(mac_address); +} + +void EthernetClass::MACAddress(uint8_t *mac_address) +{ + stm32_eth_get_macaddr(mac_address); +} + IPAddress EthernetClass::localIP() { return IPAddress(stm32_eth_get_ipaddr()); @@ -102,4 +161,9 @@ IPAddress EthernetClass::dnsServerIP() return _dnsServerAddress; } +void EthernetClass::setDnsServerIP(const IPAddress dns_server) +{ + _dnsServerAddress = dns_server; +} + EthernetClass Ethernet; diff --git a/src/STM32Ethernet.h b/src/STM32Ethernet.h index 78afbb7..f0ecfff 100644 --- a/src/STM32Ethernet.h +++ b/src/STM32Ethernet.h @@ -7,29 +7,51 @@ #include "EthernetServer.h" #include "Dhcp.h" +enum EthernetLinkStatus { + Unknown, + LinkON, + LinkOFF +}; + class EthernetClass { -private: - IPAddress _dnsServerAddress; - DhcpClass* _dhcp; -public: - // 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); - void begin(uint8_t *mac_address, IPAddress local_ip); - void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server); - void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); - void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); - int maintain(); - void schedule(void); - - IPAddress localIP(); - IPAddress subnetMask(); - IPAddress gatewayIP(); - IPAddress dnsServerIP(); - - friend class EthernetClient; - friend class EthernetServer; + private: + IPAddress _dnsServerAddress; + DhcpClass *_dhcp; + + 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(); + void begin(IPAddress local_ip); + void begin(IPAddress local_ip, IPAddress subnet); + void begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway); + void begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway, IPAddress dns_server); + // 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); + void begin(uint8_t *mac_address, IPAddress local_ip); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); + + + int maintain(); + void schedule(void); + + void MACAddress(uint8_t *mac_address); + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP(); + + void setMACAddress(const uint8_t *mac_address); + void setDnsServerIP(const IPAddress dns_server); + + friend class EthernetClient; + friend class EthernetServer; }; extern EthernetClass Ethernet; diff --git a/src/lwipopts.h b/src/lwipopts.h index 56f805b..0cf3278 100644 --- a/src/lwipopts.h +++ b/src/lwipopts.h @@ -1,18 +1,21 @@ -/* - * @file lwipopts.h - * @author Frederic Pillon for STMicroelectronics. - * @brief Include header file to match Arduino library format - */ - -#ifndef _ARDUINO_LWIPOPTS_H -#define _ARDUINO_LWIPOPTS_H - -/* LwIP specific configuration options. */ -#if __has_include("STM32lwipopts.h") -#include "STM32lwipopts.h" -#else -#include "lwipopts_default.h" -#endif - -#endif /* _ARDUINO_LWIPOPTS_H */ - +/* + * @file lwipopts.h + * @author Frederic Pillon for STMicroelectronics. + * @brief Include header file to match Arduino library format + */ + +#ifndef _ARDUINO_LWIPOPTS_H +#define _ARDUINO_LWIPOPTS_H + +/* LwIP specific configuration options. */ +#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 + +#endif /* _ARDUINO_LWIPOPTS_H */ + diff --git a/src/lwipopts_default.h b/src/lwipopts_default.h index 7a988f5..f605098 100644 --- a/src/lwipopts_default.h +++ b/src/lwipopts_default.h @@ -1,249 +1,241 @@ -/** - ****************************************************************************** - * @file lwipopts_default.h - * @author MCD Application Team - * @brief lwIP Options Configuration. - ****************************************************************************** - * @attention - * - *

© 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. - * - ****************************************************************************** - */ -#ifndef __LWIPOPTS_H__ -#define __LWIPOPTS_H__ - -/** - * NO_SYS==1: Provides VERY minimal functionality. Otherwise, - * use lwIP facilities. - */ -#define NO_SYS 1 - -/** - * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain - * critical regions during buffer allocation, deallocation and memory - * allocation and deallocation. - */ -#define SYS_LIGHTWEIGHT_PROT 0 - -/* ---------- 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 - byte alignment -> define MEM_ALIGNMENT to 2. */ -#define MEM_ALIGNMENT 4 - -/* MEM_SIZE: the size of the heap memory. If the application will send -a lot of data that needs to be copied, this should be set high. */ -#define MEM_SIZE (10*1024) - -/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application - sends a lot of data out of ROM (or other static memory), this - should be set high. */ -#define MEMP_NUM_PBUF 10 -/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One - per active UDP "connection". */ -#define MEMP_NUM_UDP_PCB 6 -/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP - connections. */ -#define MEMP_NUM_TCP_PCB 10 -/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP - connections. */ -#define MEMP_NUM_TCP_PCB_LISTEN 6 -/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP - segments. */ -#define MEMP_NUM_TCP_SEG 8 -/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active - timeouts. */ -#define MEMP_NUM_SYS_TIMEOUT 10 - - -/* ---------- Pbuf options ---------- */ -/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ -#define PBUF_POOL_SIZE 8 - -/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ -#define PBUF_POOL_BUFSIZE 1524 - - -/* ---------- TCP options ---------- */ -#define LWIP_TCP 1 -#define TCP_TTL 255 - -/* Controls if TCP should queue segments that arrive out of - order. Define to 0 if your device is low on memory. */ -#define TCP_QUEUE_OOSEQ 0 - -/* TCP Maximum segment size. */ -#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */ - -/* TCP sender buffer space (bytes). */ -#define TCP_SND_BUF (4*TCP_MSS) - -/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least - as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */ - -#define TCP_SND_QUEUELEN (2* TCP_SND_BUF/TCP_MSS) - -/* TCP receive window. */ -#define TCP_WND (2*TCP_MSS) - - -/* ---------- ICMP options ---------- */ -#define LWIP_ICMP 1 - - -/* ---------- DHCP options ---------- */ -#define LWIP_DHCP 1 - -/* ---------- DNS options ---------- */ -#define LWIP_DNS 1 - - -/* ---------- UDP options ---------- */ -#define LWIP_UDP 1 -#define UDP_TTL 255 - - -/* ---------- Statistics options ---------- */ -#define LWIP_STATS 0 - -/* ---------- link callback options ---------- */ -/* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface - * whenever the link changes (i.e., link down) - */ -#define LWIP_NETIF_LINK_CALLBACK 1 - -/* - -------------------------------------- - ---------- Checksum options ---------- - -------------------------------------- -*/ - -/* -The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware: - - To use this feature let the following define uncommented. - - To disable it and process by CPU comment the the checksum. -*/ -#define CHECKSUM_BY_HARDWARE - - -#ifdef CHECKSUM_BY_HARDWARE - /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/ - #define CHECKSUM_GEN_IP 0 - /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/ - #define CHECKSUM_GEN_UDP 0 - /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/ - #define CHECKSUM_GEN_TCP 0 - /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/ - #define CHECKSUM_CHECK_IP 0 - /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/ - #define CHECKSUM_CHECK_UDP 0 - /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/ - #define CHECKSUM_CHECK_TCP 0 - /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/ - #define CHECKSUM_GEN_ICMP 0 -#else - /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/ - #define CHECKSUM_GEN_IP 1 - /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/ - #define CHECKSUM_GEN_UDP 1 - /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/ - #define CHECKSUM_GEN_TCP 1 - /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/ - #define CHECKSUM_CHECK_IP 1 - /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/ - #define CHECKSUM_CHECK_UDP 1 - /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/ - #define CHECKSUM_CHECK_TCP 1 - /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/ - #define CHECKSUM_GEN_ICMP 1 -#endif - - -/* - ---------------------------------------------- - ---------- Sequential layer options ---------- - ---------------------------------------------- -*/ -/** - * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) - */ -#define LWIP_NETCONN 0 - -/* - ------------------------------------ - ---------- Socket options ---------- - ------------------------------------ -*/ -/** - * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) - */ -#define LWIP_SOCKET 0 - -/* - ------------------------------------ - ---------- httpd options ---------- - ------------------------------------ -*/ - -/** Set this to 1 to support CGI */ -#define LWIP_HTTPD_CGI 1 - -/** Set this to 1 to support SSI (Server-Side-Includes) */ -#define LWIP_HTTPD_SSI 1 - -/** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the - * file system (to prevent changing the file included in CVS) */ -#define HTTPD_USE_CUSTOM_FSDATA 1 - -/* - ------------------------------------ - ---------- Custom options ---------- - ------------------------------------ -*/ - -/** Enables the Ethernet peripheral in RMII mode. If not defined, MII mode will - be enabled. Pin mapping must be configured for the selected mode - (see PinMap_Ethernet in PeripheralPins.c). */ -#define ETHERNET_RMII_MODE_CONFIGURATION 1 - -/** Uncomment this line to use the ethernet input in interrupt mode. - * NOTE: LwIP stack documentation recommends to use the polling mode without - * an operating system. */ -//#define ETH_INPUT_USE_IT 1 - -#endif /* __LWIPOPTS_H__ */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +/** + ****************************************************************************** + * @file lwipopts_default.h + * @author MCD Application Team + * @brief lwIP Options Configuration. + ****************************************************************************** + * @attention + * + *

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

+ * + * 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 + * + ****************************************************************************** + */ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ +#define NO_SYS 1 + +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#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 + byte alignment -> define MEM_ALIGNMENT to 2. */ +#define MEM_ALIGNMENT 4 + +/* MEM_SIZE: the size of the heap memory. If the application will send +a lot of data that needs to be copied, this should be set high. */ +#define MEM_SIZE (10*1024) + +/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application + sends a lot of data out of ROM (or other static memory), this + should be set high. */ +#define MEMP_NUM_PBUF 10 +/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + per active UDP "connection". */ +#define MEMP_NUM_UDP_PCB 6 +/* MEMP_NUM_TCP_PCB: the number of simultaneously active TCP + connections. */ +#define MEMP_NUM_TCP_PCB 10 +/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP + connections. */ +#define MEMP_NUM_TCP_PCB_LISTEN 6 +/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP + segments. */ +#define MEMP_NUM_TCP_SEG 8 +/* MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active + timeouts. */ +#define MEMP_NUM_SYS_TIMEOUT 10 + + +/* ---------- Pbuf options ---------- */ +/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ +#define PBUF_POOL_SIZE 8 + +/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ +#define PBUF_POOL_BUFSIZE 1524 + + +/* ---------- 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 and you are not scared by TCP congestion and latencies. */ +#define TCP_QUEUE_OOSEQ 0 + +/* TCP Maximum segment size. */ +#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */ + +/* TCP sender buffer space (bytes). */ +#define TCP_SND_BUF (4*TCP_MSS) + +/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */ + +#define TCP_SND_QUEUELEN (2* TCP_SND_BUF/TCP_MSS) + +/* TCP receive window. */ +#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 + + +/* ---------- UDP options ---------- */ +#define LWIP_UDP 1 +#define UDP_TTL 255 + + +/* ---------- 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 + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ + +/* +The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware: + - To use this feature let the following define uncommented. + - To disable it and process by CPU comment the the checksum. +*/ +#define CHECKSUM_BY_HARDWARE + + +#ifdef CHECKSUM_BY_HARDWARE + /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/ + #define CHECKSUM_GEN_IP 0 + /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/ + #define CHECKSUM_GEN_UDP 0 + /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/ + #define CHECKSUM_GEN_TCP 0 + /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/ + #define CHECKSUM_CHECK_IP 0 + /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/ + #define CHECKSUM_CHECK_UDP 0 + /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/ + #define CHECKSUM_CHECK_TCP 0 + /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/ + #define CHECKSUM_GEN_ICMP 0 +#else + /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/ + #define CHECKSUM_GEN_IP 1 + /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/ + #define CHECKSUM_GEN_UDP 1 + /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/ + #define CHECKSUM_GEN_TCP 1 + /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/ + #define CHECKSUM_CHECK_IP 1 + /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/ + #define CHECKSUM_CHECK_UDP 1 + /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/ + #define CHECKSUM_CHECK_TCP 1 + /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/ + #define CHECKSUM_GEN_ICMP 1 +#endif + + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#define LWIP_NETCONN 0 + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#define LWIP_SOCKET 0 +#define LWIP_DNS 1 + +/* + ------------------------------------ + ---------- httpd options ---------- + ------------------------------------ +*/ + +/** Set this to 1 to support CGI */ +#define LWIP_HTTPD_CGI 1 + +/** Set this to 1 to support SSI (Server-Side-Includes) */ +#define LWIP_HTTPD_SSI 1 + +/** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the + * file system (to prevent changing the file included in CVS) */ +#define HTTPD_USE_CUSTOM_FSDATA 1 + +/* + ------------------------------------ + ---------- Custom options ---------- + ------------------------------------ +*/ + +/** Enables the Ethernet peripheral in RMII mode. If not defined, MII mode will + be enabled. Pin mapping must be configured for the selected mode + (see PinMap_Ethernet in PeripheralPins.c). */ +#define ETHERNET_RMII_MODE_CONFIGURATION 1 + +/** Uncomment this line to use the ethernet input in interrupt mode. + * NOTE: LwIP stack documentation recommends to use the polling mode without + * an operating system. */ +//#define ETH_INPUT_USE_IT 1 + +#endif /* __LWIPOPTS_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/utility/ethernetif.c b/src/utility/ethernetif.cpp similarity index 76% rename from src/utility/ethernetif.c rename to src/utility/ethernetif.cpp index eeb966c..9cb2695 100644 --- a/src/utility/ethernetif.c +++ b/src/utility/ethernetif.cpp @@ -1,643 +1,738 @@ -/** - ****************************************************************************** - * @file ethernetif.c - * @author MCD Application Team & Wi6Labs - * @version V1.5.0 - * @date 20-june-2017 - * @brief This file implements Ethernet network interface drivers for lwIP - ****************************************************************************** - * @attention - * - *

© 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. - * - ****************************************************************************** - */ -/* Includes ------------------------------------------------------------------*/ -#include "stm32_def.h" -#include "lwip/timeouts.h" -#include "netif/etharp.h" -#include "ethernetif.h" -#include -#include "PeripheralPins.h" -#include "stm32_eth.h" -#include "variant.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/* Private typedef -----------------------------------------------------------*/ -/* Private define ------------------------------------------------------------*/ -/* Network interface name */ -#define IFNAME0 's' -#define IFNAME1 't' - -/* Private macro -------------------------------------------------------------*/ -/* Private variables ---------------------------------------------------------*/ -#if defined ( __ICCARM__ ) /*!< IAR Compiler */ - #pragma data_alignment=4 -#endif -__ALIGN_BEGIN ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB] __ALIGN_END;/* Ethernet Rx MA Descriptor */ - -#if defined ( __ICCARM__ ) /*!< IAR Compiler */ - #pragma data_alignment=4 -#endif -__ALIGN_BEGIN ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB] __ALIGN_END;/* Ethernet Tx DMA Descriptor */ - -#if defined ( __ICCARM__ ) /*!< IAR Compiler */ - #pragma data_alignment=4 -#endif -__ALIGN_BEGIN uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __ALIGN_END; /* Ethernet Receive Buffer */ - -#if defined ( __ICCARM__ ) /*!< IAR Compiler */ - #pragma data_alignment=4 -#endif -__ALIGN_BEGIN uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __ALIGN_END; /* Ethernet Transmit Buffer */ - -static ETH_HandleTypeDef EthHandle; - -static uint8_t macaddress[6]= { MAC_ADDR0, MAC_ADDR1, MAC_ADDR2, MAC_ADDR3, MAC_ADDR4, MAC_ADDR5 }; - -/* Private function prototypes -----------------------------------------------*/ -/* Private functions ---------------------------------------------------------*/ -/******************************************************************************* - Ethernet MSP Routines -*******************************************************************************/ -/** - * @brief Initializes the ETH MSP. - * @param heth: ETH handle - * @retval None - */ -void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) -{ - GPIO_InitTypeDef GPIO_InitStructure; - const PinMap* map = PinMap_Ethernet; - PinName pin = pin_pinName(map); - GPIO_TypeDef* port; - - UNUSED(heth); - -/* Ethernet pins configuration ************************************************/ - - if(map != NULL) { - while(pin != NC) { - /* Set port clock */ - port = set_GPIO_Port_Clock(STM_PORT(pin)); - - /* pin configuration */ - GPIO_InitStructure.Pin = STM_GPIO_PIN(pin); - GPIO_InitStructure.Mode = STM_PIN_MODE(pinmap_function(pin, PinMap_Ethernet)); - GPIO_InitStructure.Pull = STM_PIN_PUPD(pinmap_function(pin, PinMap_Ethernet)); - GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStructure.Alternate = STM_PIN_AFNUM(pinmap_function(pin, PinMap_Ethernet)); - HAL_GPIO_Init(port, &GPIO_InitStructure); - - pin = pin_pinName(++map); - } - } - -#ifdef ETH_INPUT_USE_IT - /* Enable the Ethernet global Interrupt */ - HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); - HAL_NVIC_EnableIRQ(ETH_IRQn); -#endif /* ETH_INPUT_USE_IT */ - - /* Enable ETHERNET clock */ - __HAL_RCC_ETH_CLK_ENABLE(); -} - -/******************************************************************************* - LL Driver Interface ( LwIP stack --> ETH) -*******************************************************************************/ -/** - * @brief In this function, the hardware should be initialized. - * Called from ethernetif_init(). - * - * @param netif the already initialized lwip network interface structure - * for this ethernetif - */ -static void low_level_init(struct netif *netif) -{ - uint32_t regvalue; - - EthHandle.Instance = ETH; - EthHandle.Init.MACAddr = macaddress; - EthHandle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; - EthHandle.Init.Speed = ETH_SPEED_100M; - EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; -#ifdef ETHERNET_RMII_MODE_CONFIGURATION - EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; -#else - EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_MII; -#endif /* ETHERNET_RMII_MODE_CONFIGURATION */ -#ifdef ETH_INPUT_USE_IT - EthHandle.Init.RxMode = ETH_RXINTERRUPT_MODE; -#else - EthHandle.Init.RxMode = ETH_RXPOLLING_MODE; -#endif /* ETH_INPUT_USE_IT */ - EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; - EthHandle.Init.PhyAddress = LAN8742A_PHY_ADDRESS; - - /* configure ethernet peripheral (GPIOs, clocks, MAC, DMA) */ - if (HAL_ETH_Init(&EthHandle) == HAL_OK) - { - /* Set netif link flag */ - netif->flags |= NETIF_FLAG_LINK_UP; - } - - /* Initialize Tx Descriptors list: Chain Mode */ - HAL_ETH_DMATxDescListInit(&EthHandle, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); - - /* Initialize Rx Descriptors list: Chain Mode */ - HAL_ETH_DMARxDescListInit(&EthHandle, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); - - /* set MAC hardware address length */ - netif->hwaddr_len = ETH_HWADDR_LEN; - - /* set MAC hardware address */ - netif->hwaddr[0] = macaddress[0]; - netif->hwaddr[1] = macaddress[1]; - netif->hwaddr[2] = macaddress[2]; - netif->hwaddr[3] = macaddress[3]; - netif->hwaddr[4] = macaddress[4]; - netif->hwaddr[5] = macaddress[5]; - - /* maximum transfer unit */ - netif->mtu = 1500; - - /* device capabilities */ - /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ - netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; - - /* Enable MAC and DMA transmission and reception */ - HAL_ETH_Start(&EthHandle); - - /**** Configure PHY to generate an interrupt when Eth Link state changes ****/ - /* Read Register Configuration */ - 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 ); -} - -/** - * @brief This function should do the actual transmission of the packet. The packet is - * contained in the pbuf that is passed to the function. This pbuf - * might be chained. - * - * @param netif the lwip network interface structure for this ethernetif - * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) - * @return ERR_OK if the packet could be sent - * an err_t value if the packet couldn't be sent - * - * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to - * strange results. You might consider waiting for space in the DMA queue - * to become availale since the stack doesn't retry to send a packet - * dropped because of memory failure (except for the TCP timers). - */ -static err_t low_level_output(struct netif *netif, struct pbuf *p) -{ - err_t errval; - struct pbuf *q; - uint8_t *buffer = (uint8_t *)(EthHandle.TxDesc->Buffer1Addr); - __IO ETH_DMADescTypeDef *DmaTxDesc; - uint32_t framelength = 0; - uint32_t bufferoffset = 0; - uint32_t byteslefttocopy = 0; - uint32_t payloadoffset = 0; - - UNUSED(netif); - - DmaTxDesc = EthHandle.TxDesc; - bufferoffset = 0; - - /* copy frame from pbufs to driver buffers */ - for(q = p; q != NULL; q = q->next) - { - /* Is this buffer available? If not, goto error */ - if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) - { - errval = ERR_USE; - goto error; - } - - /* Get bytes in current lwIP buffer */ - byteslefttocopy = q->len; - payloadoffset = 0; - - /* Check if the length of data to copy is bigger than Tx buffer size*/ - while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE ) - { - /* Copy data to Tx buffer*/ - memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) ); - - /* Point to next descriptor */ - DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr); - - /* Check if the buffer is available */ - if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) - { - errval = ERR_USE; - goto error; - } - - buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr); - - byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset); - payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset); - framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset); - bufferoffset = 0; - } - - /* Copy the remaining bytes */ - memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), byteslefttocopy ); - bufferoffset = bufferoffset + byteslefttocopy; - framelength = framelength + byteslefttocopy; - } - - /* Prepare transmit descriptors to give to DMA */ - HAL_ETH_TransmitFrame(&EthHandle, framelength); - - errval = ERR_OK; - -error: - - /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */ - if ((EthHandle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET) - { - /* Clear TUS ETHERNET DMA flag */ - EthHandle.Instance->DMASR = ETH_DMASR_TUS; - - /* Resume DMA transmission*/ - EthHandle.Instance->DMATPDR = 0; - } - return errval; -} - -/** - * @brief Should allocate a pbuf and transfer the bytes of the incoming - * packet from the interface into the pbuf. - * - * @param netif the lwip network interface structure for this ethernetif - * @return a pbuf filled with the received packet (including MAC header) - * NULL on memory error - */ -static struct pbuf * low_level_input(struct netif *netif) -{ - struct pbuf *p = NULL; - struct pbuf *q; - uint16_t len; - uint8_t *buffer; - __IO ETH_DMADescTypeDef *dmarxdesc; - uint32_t bufferoffset = 0; - uint32_t payloadoffset = 0; - uint32_t byteslefttocopy = 0; - uint32_t i=0; - - UNUSED(netif); - - if (HAL_ETH_GetReceivedFrame_IT(&EthHandle) != HAL_OK) - return NULL; - - /* Obtain the size of the packet and put it into the "len" variable. */ - len = EthHandle.RxFrameInfos.length; - buffer = (uint8_t *)EthHandle.RxFrameInfos.buffer; - - if (len > 0) - { - /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ - p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); - } - - if (p != NULL) - { - dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; - bufferoffset = 0; - - for(q = p; q != NULL; q = q->next) - { - byteslefttocopy = q->len; - payloadoffset = 0; - - /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size */ - while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE ) - { - /* Copy data to pbuf */ - memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset)); - - /* Point to next descriptor */ - dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); - buffer = (uint8_t *)(dmarxdesc->Buffer1Addr); - - byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset); - payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset); - bufferoffset = 0; - } - - /* Copy remaining data in pbuf */ - memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy); - bufferoffset = bufferoffset + byteslefttocopy; - } - } - - /* Release descriptors to DMA */ - /* Point to first descriptor */ - dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; - /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ - for (i=0; i< EthHandle.RxFrameInfos.SegCount; i++) - { - dmarxdesc->Status |= ETH_DMARXDESC_OWN; - dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); - } - - /* Clear Segment_Count */ - EthHandle.RxFrameInfos.SegCount =0; - - /* When Rx Buffer unavailable flag is set: clear it and resume reception */ - if ((EthHandle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) - { - /* Clear RBUS ETHERNET DMA flag */ - EthHandle.Instance->DMASR = ETH_DMASR_RBUS; - /* Resume DMA reception */ - EthHandle.Instance->DMARPDR = 0; - } - return p; -} - -/** - * @brief This function should be called when a packet is ready to be read - * from the interface. It uses the function low_level_input() that - * should handle the actual reception of bytes from the network - * interface. Then the type of the received packet is determined and - * the appropriate input function is called. - * - * @param netif the lwip network interface structure for this ethernetif - */ -void ethernetif_input(struct netif *netif) -{ - err_t err; - struct pbuf *p; - - /* move received packet into a new pbuf */ - p = low_level_input(netif); - - /* no packet could be read, silently ignore this */ - if (p == NULL) return; - - /* entry point to the LwIP stack */ - err = netif->input(p, netif); - - if (err != ERR_OK) - { - LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); - pbuf_free(p); - p = NULL; - } -} - -/** - * @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 - * actual setup of the hardware. - * - * This function should be passed as a parameter to netif_add(). - * - * @param netif the lwip network interface structure for this ethernetif - * @return ERR_OK if the loopif is initialized - * ERR_MEM if private data couldn't be allocated - * any other err_t on error - */ -err_t ethernetif_init(struct netif *netif) -{ - LWIP_ASSERT("netif != NULL", (netif != NULL)); - -#if LWIP_NETIF_HOSTNAME - /* Initialize interface hostname */ - netif->hostname = "lwip"; -#endif /* LWIP_NETIF_HOSTNAME */ - - netif->name[0] = IFNAME0; - netif->name[1] = 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...) */ - netif->output = etharp_output; - netif->linkoutput = low_level_output; - - /* initialize the hardware */ - low_level_init(netif); - - return ERR_OK; -} - -/** - * @brief Returns the current time in milliseconds - * when LWIP_TIMERS == 1 and NO_SYS == 1 - * @param None - * @retval Current Time value - */ -u32_t sys_now(void) -{ - return HAL_GetTick(); -} - -/** - * @brief This function sets the netif link status. - * @param netif: the network interface - * @retval None - */ -void ethernetif_set_link(struct netif *netif) -{ - uint32_t regvalue = 0; - - /* Read PHY_MISR*/ - HAL_ETH_ReadPHYRegister(&EthHandle, PHY_ISFR, ®value); - - /* Check whether the link interrupt has occurred or not */ - if((regvalue & PHY_ISFR_INT4) != (uint16_t)RESET) - { - netif_set_link_down(netif); - } - - HAL_ETH_ReadPHYRegister(&EthHandle, PHY_BSR, ®value); - - if((regvalue & PHY_LINKED_STATUS) != (uint16_t)RESET) { - netif_set_link_up(netif); - } -} - -/** - * @brief Link callback function, this function is called on change of link status - * to update low level driver configuration. - * @param netif: The network interface - * @retval None - */ -void ethernetif_update_config(struct netif *netif) -{ - __IO uint32_t tickstart = 0; - uint32_t regvalue = 0; - - if(netif_is_link_up(netif)) - { - /* Restart the auto-negotiation */ - if(EthHandle.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE) - { - /* Enable Auto-Negotiation */ - HAL_ETH_WritePHYRegister(&EthHandle, PHY_BCR, PHY_AUTONEGOTIATION); - - /* Get tick */ - tickstart = HAL_GetTick(); - - /* Wait until the auto-negotiation will be completed */ - do - { - HAL_ETH_ReadPHYRegister(&EthHandle, PHY_BSR, ®value); - - /* Check for the Timeout ( 1s ) */ - if((HAL_GetTick() - tickstart ) > 1000) - { - /* In case of timeout */ - goto error; - } - - } while (((regvalue & PHY_AUTONEGO_COMPLETE) != PHY_AUTONEGO_COMPLETE)); - - /* Read the result of the auto-negotiation */ - HAL_ETH_ReadPHYRegister(&EthHandle, PHY_SR, ®value); - - /* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */ - if((regvalue & PHY_DUPLEX_STATUS) != (uint32_t)RESET) - { - /* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */ - EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; - } - else - { - /* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */ - EthHandle.Init.DuplexMode = ETH_MODE_HALFDUPLEX; - } - /* Configure the MAC with the speed fixed by the auto-negotiation process */ - if(regvalue & PHY_SPEED_STATUS) - { - /* Set Ethernet speed to 10M following the auto-negotiation */ - EthHandle.Init.Speed = ETH_SPEED_10M; - } - else - { - /* Set Ethernet speed to 100M following the auto-negotiation */ - EthHandle.Init.Speed = ETH_SPEED_100M; - } - } - else /* AutoNegotiation Disable */ - { - error : - /* Check parameters */ - assert_param(IS_ETH_SPEED(EthHandle.Init.Speed)); - assert_param(IS_ETH_DUPLEX_MODE(EthHandle.Init.DuplexMode)); - - /* Set MAC Speed and Duplex Mode to PHY */ - HAL_ETH_WritePHYRegister(&EthHandle, PHY_BCR, ((uint16_t)(EthHandle.Init.DuplexMode >> 3) | - (uint16_t)(EthHandle.Init.Speed >> 1))); - } - - /* ETHERNET MAC Re-Configuration */ - HAL_ETH_ConfigMAC(&EthHandle, (ETH_MACInitTypeDef *) NULL); - - /* Restart MAC interface */ - HAL_ETH_Start(&EthHandle); - } - else - { - /* Stop MAC interface */ - HAL_ETH_Stop(&EthHandle); - } - - ethernetif_notify_conn_changed(netif); -} - -/** - * @brief This function notify user about link status changement. - * @param netif: the network interface - * @retval None - */ -__weak void ethernetif_notify_conn_changed(struct netif *netif) -{ - /* NOTE : This is function clould be implemented in user file - when the callback is needed, - */ - UNUSED(netif); -} - -/** - * @brief This function set a custom MAC address. This function must be called - * before ethernetif_init(). - * @param mac: mac address - * @retval None - */ -void ethernetif_set_mac_addr(const uint8_t *mac) { - if(mac != NULL) { - memcpy(macaddress,mac,6); - } -} - -#ifdef ETH_INPUT_USE_IT -/** - * @brief Ethernet Rx Transfer completed callback - * @param heth: ETH handle - * @retval None - */ -void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth) -{ - ethernetif_input(&gnetif); -} - -/** - * @brief This function handles Ethernet interrupt request. - * @param None - * @retval None - */ -void ETH_IRQHandler(void) -{ - HAL_ETH_IRQHandler(&EthHandle); -} -#endif /* ETH_INPUT_USE_IT */ - -#ifdef __cplusplus -} -#endif -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +/** + ****************************************************************************** + * @file ethernetif.c + * @author MCD Application Team & Wi6Labs + * @version V1.5.0 + * @date 20-june-2017 + * @brief This file implements Ethernet network interface drivers for lwIP + ****************************************************************************** + * @attention + * + *

© 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. + * + ****************************************************************************** + */ +/* Includes ------------------------------------------------------------------*/ +#include "stm32_def.h" +#include "lwip/timeouts.h" +#include "netif/etharp.h" +#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" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Network interface name */ +#define IFNAME0 's' +#define IFNAME1 't' + +/* Definition of PHY SPECIAL CONTROL/STATUS REGISTER bitfield Auto-negotiation done indication */ +/* Placed in STM32Ethernet library instead of HAL conf to avoid compatibility dependence with Arduino_Core_STM32 */ +/* Could be moved from this file once Generic PHY is implemented */ +#define PHY_SR_AUTODONE ((uint16_t)0x1000) + +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ +#pragma data_alignment=4 +#endif +__ALIGN_BEGIN ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB] __ALIGN_END;/* Ethernet Rx MA Descriptor */ + +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ +#pragma data_alignment=4 +#endif +__ALIGN_BEGIN ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB] __ALIGN_END;/* Ethernet Tx DMA Descriptor */ + +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ +#pragma data_alignment=4 +#endif +__ALIGN_BEGIN uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __ALIGN_END; /* Ethernet Receive Buffer */ + +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ +#pragma data_alignment=4 +#endif +__ALIGN_BEGIN uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __ALIGN_END; /* Ethernet Transmit Buffer */ + +static ETH_HandleTypeDef EthHandle; + +/* If default MAC fields is not defined use default values based on UID */ +#if !defined(MAC_ADDR0) +#define MAC_ADDR0 0x00 +#endif +#if !defined(MAC_ADDR1) +#define MAC_ADDR1 0x80 +#endif +#if !defined(MAC_ADDR2) +#define MAC_ADDR2 0xE1 +#endif +#if !defined(MAC_ADDR3) +#define MAC_ADDR3 ((uint8_t)(((*(uint32_t *)UID_BASE) & 0x00FF0000) >> 16)) +#endif +#if !defined(MAC_ADDR4) +#define MAC_ADDR4 ((uint8_t)(((*(uint32_t *)UID_BASE) & 0x0000FF00) >> 8)) +#endif +#if !defined(MAC_ADDR5) +#define MAC_ADDR5 ((uint8_t)((*(uint32_t *)UID_BASE) & 0x000000FF)) +#endif +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 ---------------------------------------------------------*/ +/******************************************************************************* + Ethernet MSP Routines +*******************************************************************************/ +/** + * @brief Initializes the ETH MSP. + * @param heth: ETH handle + * @retval None + */ +void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +{ + GPIO_InitTypeDef GPIO_InitStructure; + const PinMap *map = PinMap_Ethernet; + PinName pin = pin_pinName(map); + GPIO_TypeDef *port; + + UNUSED(heth); + + /* Ethernet pins configuration ************************************************/ + + if (map != NULL) { + while (pin != NC) { + /* Set port clock */ + port = set_GPIO_Port_Clock(STM_PORT(pin)); + + /* pin configuration */ + GPIO_InitStructure.Pin = STM_GPIO_PIN(pin); + GPIO_InitStructure.Mode = STM_PIN_MODE(pinmap_function(pin, PinMap_Ethernet)); + GPIO_InitStructure.Pull = STM_PIN_PUPD(pinmap_function(pin, PinMap_Ethernet)); + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStructure.Alternate = STM_PIN_AFNUM(pinmap_function(pin, PinMap_Ethernet)); + HAL_GPIO_Init(port, &GPIO_InitStructure); + + pin = pin_pinName(++map); + } + } + +#ifdef ETH_INPUT_USE_IT + /* Enable the Ethernet global Interrupt */ + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); +#endif /* ETH_INPUT_USE_IT */ + + /* Enable ETHERNET clock */ + __HAL_RCC_ETH_CLK_ENABLE(); +} + +/******************************************************************************* + LL Driver Interface ( LwIP stack --> ETH) +*******************************************************************************/ +/** + * @brief In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void low_level_init(struct netif *netif) +{ + uint32_t regvalue; + + EthHandle.Instance = ETH; + EthHandle.Init.MACAddr = macaddress; + EthHandle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; + EthHandle.Init.Speed = ETH_SPEED_100M; + EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; +#ifdef ETHERNET_RMII_MODE_CONFIGURATION + EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; +#else + EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_MII; +#endif /* ETHERNET_RMII_MODE_CONFIGURATION */ +#ifdef ETH_INPUT_USE_IT + EthHandle.Init.RxMode = ETH_RXINTERRUPT_MODE; +#else + EthHandle.Init.RxMode = ETH_RXPOLLING_MODE; +#endif /* ETH_INPUT_USE_IT */ + EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; + EthHandle.Init.PhyAddress = LAN8742A_PHY_ADDRESS; + + /* configure ethernet peripheral (GPIOs, clocks, MAC, DMA) */ + if (HAL_ETH_Init(&EthHandle) == HAL_OK) { + /* Set netif link flag */ + netif->flags |= NETIF_FLAG_LINK_UP; + } + + /* Initialize Tx Descriptors list: Chain Mode */ + HAL_ETH_DMATxDescListInit(&EthHandle, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); + + /* Initialize Rx Descriptors list: Chain Mode */ + HAL_ETH_DMARxDescListInit(&EthHandle, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); + + /* set MAC hardware address length */ + netif->hwaddr_len = ETH_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = macaddress[0]; + netif->hwaddr[1] = macaddress[1]; + netif->hwaddr[2] = macaddress[2]; + netif->hwaddr[3] = macaddress[3]; + netif->hwaddr[4] = macaddress[4]; + netif->hwaddr[5] = macaddress[5]; + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + /* 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); + + 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 +} + +/** + * @brief This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become available since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ +static err_t low_level_output(struct netif *netif, struct pbuf *p) +{ + err_t errval; + struct pbuf *q; + uint8_t *buffer = (uint8_t *)(EthHandle.TxDesc->Buffer1Addr); + __IO ETH_DMADescTypeDef *DmaTxDesc; + uint32_t framelength = 0; + uint32_t bufferoffset = 0; + uint32_t byteslefttocopy = 0; + uint32_t payloadoffset = 0; + + UNUSED(netif); + + DmaTxDesc = EthHandle.TxDesc; + bufferoffset = 0; + + /* copy frame from pbufs to driver buffers */ + for (q = p; q != NULL; q = q->next) { + /* Is this buffer available? If not, goto error */ + if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) { + errval = ERR_USE; + goto error; + } + + /* Get bytes in current lwIP buffer */ + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of data to copy is bigger than Tx buffer size*/ + while ((byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE) { + /* Copy data to Tx buffer*/ + memcpy((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset)); + + /* Point to next descriptor */ + DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr); + + /* Check if the buffer is available */ + if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) { + errval = ERR_USE; + goto error; + } + + buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset); + framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + + /* Copy the remaining bytes */ + memcpy((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)q->payload + payloadoffset), byteslefttocopy); + bufferoffset = bufferoffset + byteslefttocopy; + framelength = framelength + byteslefttocopy; + } + + /* Prepare transmit descriptors to give to DMA */ + HAL_ETH_TransmitFrame(&EthHandle, framelength); + + errval = ERR_OK; + +error: + + /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */ + if ((EthHandle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET) { + /* Clear TUS ETHERNET DMA flag */ + EthHandle.Instance->DMASR = ETH_DMASR_TUS; + + /* Resume DMA transmission*/ + EthHandle.Instance->DMATPDR = 0; + } + return errval; +} + +/** + * @brief Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf *low_level_input(struct netif *netif) +{ + struct pbuf *p = NULL; + struct pbuf *q; + uint16_t len; + uint8_t *buffer; + __IO ETH_DMADescTypeDef *dmarxdesc; + uint32_t bufferoffset = 0; + uint32_t payloadoffset = 0; + uint32_t byteslefttocopy = 0; + uint32_t i = 0; + + UNUSED(netif); + + if (HAL_ETH_GetReceivedFrame_IT(&EthHandle) != HAL_OK) { + return NULL; + } + + /* Obtain the size of the packet and put it into the "len" variable. */ + len = EthHandle.RxFrameInfos.length; + buffer = (uint8_t *)EthHandle.RxFrameInfos.buffer; + + if (len > 0) { + /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + } + + if (p != NULL) { + dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; + bufferoffset = 0; + + for (q = p; q != NULL; q = q->next) { + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size */ + while ((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE) { + /* Copy data to pbuf */ + memcpy((uint8_t *)((uint8_t *)q->payload + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset)); + + /* Point to next descriptor */ + dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); + buffer = (uint8_t *)(dmarxdesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + + /* Copy remaining data in pbuf */ + memcpy((uint8_t *)((uint8_t *)q->payload + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), byteslefttocopy); + bufferoffset = bufferoffset + byteslefttocopy; + } + } + + /* Release descriptors to DMA */ + /* Point to first descriptor */ + dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i = 0; i < EthHandle.RxFrameInfos.SegCount; i++) { + dmarxdesc->Status |= ETH_DMARXDESC_OWN; + dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + EthHandle.RxFrameInfos.SegCount = 0; + + /* When Rx Buffer unavailable flag is set: clear it and resume reception */ + if ((EthHandle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) { + /* Clear RBUS ETHERNET DMA flag */ + EthHandle.Instance->DMASR = ETH_DMASR_RBUS; + /* Resume DMA reception */ + EthHandle.Instance->DMARPDR = 0; + } + return p; +} + +/** + * @brief This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +void ethernetif_input(struct netif *netif) +{ + err_t err; + struct pbuf *p; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + + /* no packet could be read, silently ignore this */ + if (p == NULL) { + return; + } + + /* entry point to the LwIP stack */ + err = netif->input(p, netif); + + if (err != ERR_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + p = NULL; + } +} + +/** + * @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 + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + netif->name[0] = IFNAME0; + netif->name[1] = 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...) */ + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} + +/** + * @brief Returns the current time in milliseconds + * when LWIP_TIMERS == 1 and NO_SYS == 1 + * @param None + * @retval Current Time value + */ +u32_t sys_now(void) +{ + return HAL_GetTick(); +} + +/** + * @brief This function sets the netif link status. + * @param netif: the network interface + * @retval None + */ +void ethernetif_set_link(struct netif *netif) +{ + uint32_t regvalue = 0; + + /* Read PHY_MISR*/ + HAL_ETH_ReadPHYRegister(&EthHandle, PHY_ISFR, ®value); + + /* Check whether the link interrupt has occurred or not */ + if ((regvalue & PHY_ISFR_INT4) != (uint16_t)RESET) { + netif_set_link_down(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); + } +} + +/** + * @brief Link callback function, this function is called on change of link status + * to update low level driver configuration. + * @param netif: The network interface + * @retval None + */ +void ethernetif_update_config(struct netif *netif) +{ + uint32_t regvalue = 0; + + if (netif_is_link_up(netif)) { + /* Restart the auto-negotiation */ + if (EthHandle.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE) { + + /* Check Auto negotiation */ + HAL_ETH_ReadPHYRegister(&EthHandle, PHY_SR, ®value); + if ((regvalue & PHY_SR_AUTODONE) != PHY_SR_AUTODONE) { + goto error; + } + + /* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */ + if ((regvalue & PHY_DUPLEX_STATUS) != (uint32_t)RESET) { + /* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */ + EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; + } else { + /* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */ + EthHandle.Init.DuplexMode = ETH_MODE_HALFDUPLEX; + } + /* Configure the MAC with the speed fixed by the auto-negotiation process */ + if (regvalue & PHY_SPEED_STATUS) { + /* Set Ethernet speed to 10M following the auto-negotiation */ + EthHandle.Init.Speed = ETH_SPEED_10M; + } else { + /* Set Ethernet speed to 100M following the auto-negotiation */ + EthHandle.Init.Speed = ETH_SPEED_100M; + } + } else { /* AutoNegotiation Disable */ +error : + /* Check parameters */ + assert_param(IS_ETH_SPEED(EthHandle.Init.Speed)); + assert_param(IS_ETH_DUPLEX_MODE(EthHandle.Init.DuplexMode)); + + /* Set MAC Speed and Duplex Mode to PHY */ + HAL_ETH_WritePHYRegister(&EthHandle, PHY_BCR, ((uint16_t)(EthHandle.Init.DuplexMode >> 3) | + (uint16_t)(EthHandle.Init.Speed >> 1))); + } + + /* ETHERNET MAC Re-Configuration */ + HAL_ETH_ConfigMAC(&EthHandle, (ETH_MACInitTypeDef *) NULL); + + /* Restart MAC interface */ + HAL_ETH_Start(&EthHandle); + } else { + /* Stop MAC interface */ + HAL_ETH_Stop(&EthHandle); + } + + ethernetif_notify_conn_changed(netif); +} + +/** + * @brief This function notify user about link status changement. + * @param netif: the network interface + * @retval None + */ +__weak void ethernetif_notify_conn_changed(struct netif *netif) +{ + /* NOTE : This is function clould be implemented in user file + when the callback is needed, + */ + UNUSED(netif); +} + +/** + * @brief This function set a custom MAC address. This function must be called + * before ethernetif_init(). + * @param mac: mac address + * @retval None + */ +void ethernetif_set_mac_addr(const uint8_t *mac) +{ + if ((mac != NULL) && !(ethernetif_is_init())) { + memcpy(macaddress, mac, 6); + } +} + +/** + * @brief This function get the current MAC address. + * @param mac: mac address + * @retval None + */ +void ethernetif_get_mac_addr(uint8_t *mac) +{ + if (mac != NULL) { + memcpy(mac, macaddress, 6); + } +} + +#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 + * @param heth: ETH handle + * @retval None + */ +void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth) +{ + ethernetif_input(&gnetif); +} + +/** + * @brief This function handles Ethernet interrupt request. + * @param None + * @retval None + */ +void ETH_IRQHandler(void) +{ + HAL_ETH_IRQHandler(&EthHandle); +} +#endif /* ETH_INPUT_USE_IT */ + +#ifdef __cplusplus +} +#endif +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/utility/ethernetif.h b/src/utility/ethernetif.h index e851c19..ec7027b 100644 --- a/src/utility/ethernetif.h +++ b/src/utility/ethernetif.h @@ -1,71 +1,75 @@ -/** - ****************************************************************************** - * @file ethernetif.h - * @author MCD Application Team & Wi6Labs - * @version V1.5.0 - * @date 20-June-2017 - * @brief Ethernet interface header file. - ****************************************************************************** - * @attention - * - *

© 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. - * - ****************************************************************************** - */ - -#ifndef __ETHERNETIF_H__ -#define __ETHERNETIF_H__ - -#include "lwip/err.h" -#include "lwip/netif.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/* Exported types ------------------------------------------------------------*/ -err_t ethernetif_init(struct netif *netif); -void ethernetif_input(struct netif *netif); -void ethernetif_set_link(struct netif *netif); -void ethernetif_update_config(struct netif *netif); -void ethernetif_notify_conn_changed(struct netif *netif); - -void ethernetif_set_mac_addr(const uint8_t *mac); - -#ifdef __cplusplus -} -#endif - -#endif +/** + ****************************************************************************** + * @file ethernetif.h + * @author MCD Application Team & Wi6Labs + * @version V1.5.0 + * @date 20-June-2017 + * @brief Ethernet interface header file. + ****************************************************************************** + * @attention + * + *

© 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. + * + ****************************************************************************** + */ + +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_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); +void ethernetif_input(struct netif *netif); +void ethernetif_set_link(struct netif *netif); +void ethernetif_update_config(struct netif *netif); +void ethernetif_notify_conn_changed(struct netif *netif); + +void ethernetif_set_mac_addr(const uint8_t *mac); +void ethernetif_get_mac_addr(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 + +#endif diff --git a/src/utility/stm32_eth.c b/src/utility/stm32_eth.cpp similarity index 71% rename from src/utility/stm32_eth.c rename to src/utility/stm32_eth.cpp index 4365b17..3a79595 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 @@ -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 @@ -67,10 +63,17 @@ * They could be used for this library when available */ #ifndef DEFAULT_ETHERNET_TIMER -#define DEFAULT_ETHERNET_TIMER TIM14 -#warning "Default timer used to call ethernet scheduler at regular interval: TIM14" + #define DEFAULT_ETHERNET_TIMER TIM14 + #warning "Default timer used to call ethernet scheduler at regular interval: TIM14" #endif +/* Interrupt priority */ +#ifndef ETH_TIM_IRQ_PRIO + #define ETH_TIM_IRQ_PRIO 15 // Warning: it should be lower prio (higher value) than Systick +#endif +#ifndef ETH_TIM_IRQ_SUBPRIO + #define ETH_TIM_IRQ_SUBPRIO 0 +#endif /* Ethernet configuration: user parameters */ struct stm32_eth_config { ip_addr_t ipaddr; @@ -96,37 +99,41 @@ 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; +#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01060100) + /* Handler for stimer */ + static stimer_t TimHandle; +#else + HardwareTimer *EthTim = NULL; +#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(stimer_t *htim); static void TIM_scheduler_Config(void); +#if defined(STM32_CORE_VERSION) && (STM32_CORE_VERSION > 0x01060100) + void _stm32_eth_scheduler(void); +#endif /** -* @brief Configurates the network interface +* @brief Configure the network interface * @param None * @retval None */ static void Netif_Config(void) { + netif_remove(&gnetif); /* Add the network interface */ netif_add(&gnetif, &(gconfig.ipaddr), &(gconfig.netmask), &(gconfig.gw), NULL, ðernetif_init, ðernet_input); /* Registers the default network interface */ netif_set_default(&gnetif); - if (netif_is_link_up(&gnetif)) - { + if (netif_is_link_up(&gnetif)) { /* When the netif is fully configured this function must be called */ netif_set_up(&gnetif); - } - else - { + } else { /* When the netif link is down this function must be called */ netif_set_down(&gnetif); } @@ -139,15 +146,24 @@ static void Netif_Config(void) /** * @brief Scheduler callback. Call by a timer interrupt. -* @param htim: pointer to stimer_t +* @param htim: pointer to stimer_t or Hardware Timer * @retval None */ -static void scheduler_callback(stimer_t *htim) +#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01060100) + static void scheduler_callback(stimer_t *htim) +#elif (STM32_CORE_VERSION <= 0x01080000) + static void scheduler_callback(HardwareTimer *htim) +#else + static void scheduler_callback(void) +#endif { +#if (STM32_CORE_VERSION <= 0x01080000) UNUSED(htim); - stm32_eth_scheduler(); +#endif + _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. @@ -158,61 +174,80 @@ 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. +* @param None +* @retval None +*/ +static void TIM_scheduler_Config(void) +{ + /* Configure HardwareTimer */ + EthTim = new HardwareTimer(DEFAULT_ETHERNET_TIMER); + EthTim->setInterruptPriority(ETH_TIM_IRQ_PRIO, ETH_TIM_IRQ_SUBPRIO); + EthTim->setMode(1, TIMER_OUTPUT_COMPARE); + + /* Timer set to 1ms */ + EthTim->setOverflow(1000, MICROSEC_FORMAT); + 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) { static uint8_t initDone = 0; - if(!initDone) { + if (!initDone) { /* Initialize the LwIP stack */ lwip_init(); + } - if(mac != NULL) { - ethernetif_set_mac_addr(mac); - } // else default value is used: MAC_ADDR0 ... MAC_ADDR5 + if (mac != NULL) { + ethernetif_set_mac_addr(mac); + } // else default value is used: MAC_ADDR0 ... MAC_ADDR5 - if(ip != NULL) { - IP_ADDR4(&(gconfig.ipaddr),ip[0],ip[1],ip[2],ip[3]); - } else { - #if LWIP_DHCP - ip_addr_set_zero_ip4(&(gconfig.ipaddr)); - #else - IP_ADDR4(&(gconfig.ipaddr),IP_ADDR0,IP_ADDR1,IP_ADDR2,IP_ADDR3); - #endif /* LWIP_DHCP */ - } + if (ip != NULL) { + IP_ADDR4(&(gconfig.ipaddr), ip[0], ip[1], ip[2], ip[3]); + } else { +#if LWIP_DHCP + ip_addr_set_zero_ip4(&(gconfig.ipaddr)); +#else + IP_ADDR4(&(gconfig.ipaddr), IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3); +#endif /* LWIP_DHCP */ + } - if(gw != NULL) { - IP_ADDR4(&(gconfig.gw),gw[0],gw[1],gw[2],gw[3]); - } else { - #if LWIP_DHCP - ip_addr_set_zero_ip4(&(gconfig.gw)); - #else - IP_ADDR4(&(gconfig.gw),GW_ADDR0,GW_ADDR1,GW_ADDR2,GW_ADDR3); - #endif /* LWIP_DHCP */ - } + if (gw != NULL) { + IP_ADDR4(&(gconfig.gw), gw[0], gw[1], gw[2], gw[3]); + } else { +#if LWIP_DHCP + ip_addr_set_zero_ip4(&(gconfig.gw)); +#else + IP_ADDR4(&(gconfig.gw), GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); +#endif /* LWIP_DHCP */ + } - if(netmask != NULL) { - IP_ADDR4(&(gconfig.netmask),netmask[0],netmask[1],netmask[2],netmask[3]); - } else { - #if LWIP_DHCP - ip_addr_set_zero_ip4(&(gconfig.netmask)); - #else - IP_ADDR4(&(gconfig.netmask),NETMASK_ADDR0,NETMASK_ADDR1,NETMASK_ADDR2,NETMASK_ADDR3); - #endif /* LWIP_DHCP */ - } + if (netmask != NULL) { + IP_ADDR4(&(gconfig.netmask), netmask[0], netmask[1], netmask[2], netmask[3]); + } else { +#if LWIP_DHCP + ip_addr_set_zero_ip4(&(gconfig.netmask)); +#else + IP_ADDR4(&(gconfig.netmask), NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3); +#endif /* LWIP_DHCP */ + } - /* Configure the Network interface */ - Netif_Config(); + /* Configure the Network interface */ + Netif_Config(); + if (!initDone) { // stm32_eth_scheduler() will be called every 1ms. TIM_scheduler_Config(); - initDone = 1; } @@ -224,11 +259,72 @@ void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, co } /** - * @brief This function must be called in main loop in standalone mode. + * @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 Set Ethernet MAC address + * @param mac: mac address + * @retval None + */ +void stm32_eth_set_macaddr(const uint8_t *mac) +{ + ethernetif_set_mac_addr(mac); +} +/** + * @brief Return Ethernet MAC address + * @param mac: mac address + * @retval None + */ +void stm32_eth_get_macaddr(uint8_t *mac) +{ + return ethernetif_get_mac_addr(mac); +} + +/** + * @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); +} + +#if defined(STM32_CORE_VERSION) && (STM32_CORE_VERSION > 0x01060100) +/** + * @brief This function generates Timer Update event to force call to _stm32_eth_scheduler(). * @param None * @retval None */ -void stm32_eth_scheduler(void) { +void stm32_eth_scheduler(void) +{ + if (EthTim != NULL) { + EthTim->refresh(); + } +} + +/** + * @brief This function is called solely by Timer callback to avoid race condition. + * @param None + * @retval None + */ +void _stm32_eth_scheduler(void) +#else +/** + * @brief This function is called solely by Timer callback to avoid race condition. + * @param None + * @retval None + */ +void stm32_eth_scheduler(void) +#endif +{ /* Read a received packet from the Ethernet buffers and send it to the lwIP for handling */ #ifndef ETH_INPUT_USE_IT @@ -236,7 +332,7 @@ void stm32_eth_scheduler(void) { #endif /* ETH_INPUT_USE_IT */ /* Check ethernet link status */ - if((HAL_GetTick() - gEhtLinkTickStart) >= TIME_CHECK_ETH_LINK_STATE) { + if ((HAL_GetTick() - gEhtLinkTickStart) >= TIME_CHECK_ETH_LINK_STATE) { ethernetif_set_link(&gnetif); gEhtLinkTickStart = HAL_GetTick(); } @@ -256,7 +352,8 @@ void stm32_eth_scheduler(void) { * @param None * @retval true if DHCP enabled or false if not used */ -uint8_t stm32_dhcp_started(void) { +uint8_t stm32_dhcp_started(void) +{ return DHCP_Started_by_user; } @@ -265,63 +362,55 @@ uint8_t stm32_dhcp_started(void) { * @param netif pointer to generic data structure used for all lwIP network interfaces * @retval None */ -void stm32_DHCP_process(struct netif *netif) { +void stm32_DHCP_process(struct netif *netif) +{ struct dhcp *dhcp; - if(netif_is_link_up(netif)){ - switch (DHCP_state) - { - case DHCP_START: - { - ip_addr_set_zero_ip4(&netif->ip_addr); - ip_addr_set_zero_ip4(&netif->netmask); - ip_addr_set_zero_ip4(&netif->gw); - DHCP_state = DHCP_WAIT_ADDRESS; - dhcp_start(netif); - DHCP_Started_by_user = 1; - } - break; - - case DHCP_WAIT_ADDRESS: - { - if (dhcp_supplied_address(netif)) - { - DHCP_state = DHCP_ADDRESS_ASSIGNED; + if (netif_is_link_up(netif)) { + switch (DHCP_state) { + case DHCP_START: { + ip_addr_set_zero_ip4(&netif->ip_addr); + ip_addr_set_zero_ip4(&netif->netmask); + ip_addr_set_zero_ip4(&netif->gw); + DHCP_state = DHCP_WAIT_ADDRESS; + dhcp_start(netif); + DHCP_Started_by_user = 1; } - else - { - dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); + break; - /* DHCP timeout */ - if (dhcp->tries > MAX_DHCP_TRIES) - { - DHCP_state = DHCP_TIMEOUT; + case DHCP_WAIT_ADDRESS: { + if (dhcp_supplied_address(netif)) { + DHCP_state = DHCP_ADDRESS_ASSIGNED; + } else { + dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); - // If DHCP address not bind, keep DHCP stopped - DHCP_Started_by_user = 0; + /* DHCP timeout */ + if (dhcp->tries > MAX_DHCP_TRIES) { + DHCP_state = DHCP_TIMEOUT; - /* Stop DHCP */ - dhcp_stop(netif); + // If DHCP address not bind, keep DHCP stopped + DHCP_Started_by_user = 0; + + /* Stop DHCP */ + dhcp_stop(netif); + } } } - } - break; - case DHCP_ASK_RELEASE: - { - /* Force release */ - dhcp_release(netif); - dhcp_stop(netif); - DHCP_state = DHCP_OFF; - } - break; - case DHCP_LINK_DOWN: - { - /* Stop DHCP */ - dhcp_stop(netif); - DHCP_state = DHCP_OFF; - } - break; - default: break; + break; + case DHCP_ASK_RELEASE: { + /* Force release */ + dhcp_release(netif); + dhcp_stop(netif); + DHCP_state = DHCP_OFF; + } + break; + case DHCP_LINK_DOWN: { + /* Stop DHCP */ + dhcp_stop(netif); + DHCP_state = DHCP_OFF; + } + break; + default: break; } } else { DHCP_state = DHCP_OFF; @@ -336,8 +425,7 @@ void stm32_DHCP_process(struct netif *netif) { void stm32_DHCP_Periodic_Handle(struct netif *netif) { /* Fine DHCP periodic process every 500ms */ - if (HAL_GetTick() - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS) - { + if (HAL_GetTick() - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS) { DHCPfineTimer = HAL_GetTick(); /* process DHCP state machine */ stm32_DHCP_process(netif); @@ -349,7 +437,8 @@ void stm32_DHCP_Periodic_Handle(struct netif *netif) * @param None * @retval None */ -void stm32_DHCP_manual_config(void) { +void stm32_DHCP_manual_config(void) +{ dhcp_inform(&gnetif); } @@ -358,13 +447,14 @@ void stm32_DHCP_manual_config(void) { * @param None * @retval Renew or rebind. Adapted from Arduino Ethernet library. */ -uint8_t stm32_get_DHCP_lease_state(void) { +uint8_t stm32_get_DHCP_lease_state(void) +{ uint8_t res = 0; - struct dhcp* dhcp = (struct dhcp *)netif_get_client_data(&gnetif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); + struct dhcp *dhcp = (struct dhcp *)netif_get_client_data(&gnetif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); - if(dhcp->state == DHCP_STATE_RENEWING) { + if (dhcp->state == DHCP_STATE_RENEWING) { res = 2; - } else if(dhcp->state == DHCP_STATE_REBINDING) { + } else if (dhcp->state == DHCP_STATE_REBINDING) { res = 4; } @@ -376,7 +466,8 @@ uint8_t stm32_get_DHCP_lease_state(void) { * @param state: DHCP_START, DHCP_ASK_RELEASE or DHCP_STOP. Others should not be used. * @retval None */ -void stm32_set_DHCP_state(uint8_t state) { +void stm32_set_DHCP_state(uint8_t state) +{ DHCP_state = state; } @@ -392,7 +483,8 @@ void stm32_set_DHCP_state(uint8_t state) { DHCP_LINK_DOWN DHCP_ASK_RELEASE */ -uint8_t stm32_get_DHCP_state(void) { +uint8_t stm32_get_DHCP_state(void) +{ return DHCP_state; } @@ -403,7 +495,8 @@ uint8_t stm32_get_DHCP_state(void) { * @param None * @retval address in uint32_t format */ -uint32_t stm32_eth_get_ipaddr(void) { +uint32_t stm32_eth_get_ipaddr(void) +{ return ip4_addr_get_u32(&(gnetif.ip_addr)); } @@ -412,7 +505,8 @@ uint32_t stm32_eth_get_ipaddr(void) { * @param None * @retval address in uint32_t format */ -uint32_t stm32_eth_get_gwaddr(void) { +uint32_t stm32_eth_get_gwaddr(void) +{ return ip4_addr_get_u32(&(gnetif.gw)); } @@ -421,7 +515,8 @@ uint32_t stm32_eth_get_gwaddr(void) { * @param None * @retval address in uint32_t format */ -uint32_t stm32_eth_get_netmaskaddr(void) { +uint32_t stm32_eth_get_netmaskaddr(void) +{ return ip4_addr_get_u32(&(gnetif.netmask)); } @@ -430,7 +525,8 @@ uint32_t stm32_eth_get_netmaskaddr(void) { * @param None * @retval address in uint32_t format */ -uint32_t stm32_eth_get_dnsaddr(void) { +uint32_t stm32_eth_get_dnsaddr(void) +{ const ip_addr_t *tmp = dns_getserver(0); return ip4_addr_get_u32(tmp); } @@ -440,7 +536,8 @@ uint32_t stm32_eth_get_dnsaddr(void) { * @param None * @retval address in uint32_t format */ -uint32_t stm32_eth_get_dhcpaddr(void) { +uint32_t stm32_eth_get_dhcpaddr(void) +{ struct dhcp *dhcp = (struct dhcp *)netif_get_client_data(&gnetif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); return ip4_addr_get_u32(&(dhcp->server_ip_addr)); } @@ -454,49 +551,39 @@ uint32_t stm32_eth_get_dhcpaddr(void) { */ void ethernetif_notify_conn_changed(struct netif *netif) { - if(netif_is_link_up(netif)) - { - printf("Link up\n"); - + if (netif_is_link_up(netif)) { /* Update DHCP state machine if DHCP used */ - if(DHCP_Started_by_user == 1) { + if (DHCP_Started_by_user == 1) { DHCP_state = DHCP_START; } /* When the netif is fully configured this function must be called.*/ netif_set_up(netif); - } - else - { + } else { /* Update DHCP state machine if DHCP used */ - if(DHCP_Started_by_user == 1) { + if (DHCP_Started_by_user == 1) { DHCP_state = DHCP_LINK_DOWN; } /* When the netif link is down this function must be called.*/ netif_set_down(netif); - - printf("Link down\n"); } } #endif /* LWIP_NETIF_LINK_CALLBACK */ /** - * @brief Notify the User about the nework interface config status + * @brief Notify the User about the network interface config status * @param netif: the network interface * @retval None */ void User_notification(struct netif *netif) { - if (netif_is_up(netif)) - { + if (netif_is_up(netif)) { //Link up - } - else - { + } else { /* Update DHCP state machine */ - if(DHCP_Started_by_user == 1) { + if (DHCP_Started_by_user == 1) { DHCP_state = DHCP_LINK_DOWN; } } @@ -514,9 +601,9 @@ void stm32_dns_init(const uint8_t *dnsaddr) ip_addr_t ip; /* DNS initialized by DHCP when call dhcp_start() */ - if(!stm32_dhcp_started()) { + if (!stm32_dhcp_started()) { dns_init(); - IP_ADDR4(&ip,dnsaddr[0],dnsaddr[1],dnsaddr[2],dnsaddr[3]); + IP_ADDR4(&ip, dnsaddr[0], dnsaddr[1], dnsaddr[2], dnsaddr[3]); dns_setserver(0, &ip); } } @@ -532,7 +619,7 @@ void dns_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) { UNUSED(name); - if(ipaddr != NULL) { + if (ipaddr != NULL) { *((uint32_t *)callback_arg) = ip4_addr_get_u32(ipaddr); } else { *((uint32_t *)callback_arg) = 0; @@ -556,38 +643,38 @@ int8_t stm32_dns_gethostbyname(const char *hostname, uint32_t *ipaddr) *ipaddr = 0; err = dns_gethostbyname(hostname, &iphost, &dns_callback, ipaddr); - switch(err) { + switch (err) { case ERR_OK: *ipaddr = ip4_addr_get_u32(&iphost); ret = 1; - break; + break; case ERR_INPROGRESS: tickstart = HAL_GetTick(); - while(*ipaddr == 0) { + while (*ipaddr == 0) { stm32_eth_scheduler(); - if((HAL_GetTick() - tickstart) >= TIMEOUT_DNS_REQUEST) { + if ((HAL_GetTick() - tickstart) >= TIMEOUT_DNS_REQUEST) { ret = -1; break; } } - if(ret == 0) { - if(*ipaddr == 0) { + if (ret == 0) { + if (*ipaddr == 0) { ret = -2; } else { ret = 1; } } - break; + break; case ERR_ARG: ret = -4; - break; + break; default: ret = -4; - break; + break; } return ret; @@ -603,7 +690,7 @@ int8_t stm32_dns_gethostbyname(const char *hostname, uint32_t *ipaddr) */ 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]); + IP_ADDR4(ipaddr, ipu8[0], ipu8[1], ipu8[2], ipu8[3]); return ipaddr; } @@ -627,12 +714,12 @@ uint32_t ip_addr_to_u32(ip_addr_t *ipaddr) struct pbuf *stm32_new_data(struct pbuf *p, const uint8_t *buffer, size_t size) { // Allocate memory if pbuf doesn't exit yet. - if(p == NULL) { + if (p == NULL) { p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); - if(p != NULL) { + if (p != NULL) { // Copy data inside pbuf - if(ERR_OK == pbuf_take(p, (uint8_t *)buffer, size)) { + if (ERR_OK == pbuf_take(p, (uint8_t *)buffer, size)) { return p; } else { pbuf_free(p); @@ -644,9 +731,9 @@ struct pbuf *stm32_new_data(struct pbuf *p, const uint8_t *buffer, size_t size) 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)) { + 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; @@ -669,10 +756,10 @@ struct pbuf *stm32_free_data(struct pbuf *p) { uint16_t n; - if(p != NULL) { + if (p != NULL) { do { n = pbuf_free(p); - } while(n == 0); + } while (n == 0); } return NULL; @@ -691,31 +778,31 @@ uint16_t stm32_get_data(struct pbuf_data *data, uint8_t *buffer, size_t size) uint16_t i; uint16_t offset; uint16_t nb; - struct pbuf * ptr; + struct pbuf *ptr; - if((data->p == NULL) || (buffer == NULL) || (size == 0) || + 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)) { + 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++) { + 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) { + if (nb < size) { /* continue with next pbuf in chain (if any) */ data->p = ptr->next; - if(data->p != NULL) { + if (data->p != NULL) { /* increment reference count for p */ pbuf_ref(data->p); } @@ -725,7 +812,7 @@ uint16_t stm32_get_data(struct pbuf_data *data, uint8_t *buffer, size_t size) } } - if(data->available == 0) { + if (data->available == 0) { data->p = stm32_free_data(data->p); } @@ -750,9 +837,9 @@ void udp_receive_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct udp_struct *udp_arg = (struct udp_struct *)arg; /* Send data to the application layer */ - if((udp_arg != NULL) && (udp_arg->pcb == pcb)) { + if ((udp_arg != NULL) && (udp_arg->pcb == pcb)) { // Free the old p buffer if not read - if(udp_arg->data.p != NULL) { + if (udp_arg->data.p != NULL) { pbuf_free(udp_arg->data.p); } @@ -761,6 +848,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); } @@ -773,7 +864,7 @@ void udp_receive_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, /** * @brief Function called when TCP connection established * @param arg: user supplied argument - * @param tpcb: pointer on the connection contol block + * @param tpcb: pointer on the connection control block * @param err: when connection correctly established err should be ERR_OK * @retval err_t: returned error */ @@ -781,10 +872,8 @@ 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)) - { + if (err == ERR_OK) { + if ((tcp_arg != NULL) && (tcp_arg->pcb == tpcb)) { tcp_arg->state = TCP_CONNECTED; /* initialize LwIP tcp_recv callback function */ @@ -797,17 +886,13 @@ err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err) tcp_err(tpcb, tcp_err_callback); return ERR_OK; - } - else - { + } else { /* close connection */ tcp_connection_close(tpcb, tcp_arg); return ERR_ARG; } - } - else - { + } else { /* close connection */ tcp_connection_close(tpcb, tcp_arg); } @@ -830,26 +915,25 @@ err_t tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err) /* set priority for the newly accepted tcp connection newpcb */ tcp_setprio(newpcb, TCP_PRIO_MIN); - if((tcpClient != NULL) && (ERR_OK == err)) { + if ((tcpClient != NULL) && (ERR_OK == err)) { struct tcp_struct *client = (struct tcp_struct *)mem_malloc(sizeof(struct tcp_struct)); - if (client != NULL) - { + if (client != NULL) { client->state = TCP_ACCEPTED; client->pcb = newpcb; client->data.p = NULL; client->data.available = 0; - /* Looking for an empty soket */ - for(uint16_t i = 0; i < MAX_CLIENT; i++) { - if(tcpClient[i] == NULL) { + /* 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) { + if (accepted) { /* pass newly allocated client structure as argument to newpcb */ tcp_arg(newpcb, client); @@ -871,9 +955,7 @@ err_t tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err) /* return memory error */ ret_err = ERR_MEM; } - } - else - { + } else { /* close tcp connection */ tcp_connection_close(newpcb, client); mem_free(client); @@ -893,7 +975,7 @@ err_t tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err) * @param arg: argument to be passed to receive callback * @param tpcb: tcp connection control block * @param err: receive error code - * @retval err_t: retuned error + * @retval err_t: returned error */ static err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { @@ -901,31 +983,25 @@ static err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t ret_err; /* if we receive an empty tcp frame from server => close connection */ - if (p == NULL) - { + 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) - { + else if (err != ERR_OK) { /* free received pbuf*/ - if (p != NULL) - { + if (p != NULL) { pbuf_free(p); } ret_err = err; - } - else if((tcp_arg->state == TCP_CONNECTED) || (tcp_arg->state == TCP_ACCEPTED)) - { + } 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) { + if (tcp_arg->data.p == NULL) { tcp_arg->data.p = p; - } - else { + } else { pbuf_chain(tcp_arg->data.p, p); } @@ -933,8 +1009,7 @@ static err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, ret_err = ERR_OK; } /* data received when connection already closed */ - else - { + else { /* Acknowledge data reception */ tcp_recved(tpcb, p->tot_len); @@ -959,8 +1034,7 @@ static err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len) LWIP_UNUSED_ARG(len); - if((tcp_arg != NULL) && (tcp_arg->pcb == tpcb)) - { + if ((tcp_arg != NULL) && (tcp_arg->pcb == tpcb)) { return ERR_OK; } @@ -981,8 +1055,8 @@ 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) { + if (tcp_arg != NULL) { + if (ERR_OK != err) { tcp_arg->pcb = NULL; tcp_arg->state = TCP_CLOSING; } @@ -1000,7 +1074,7 @@ 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_poll(tpcb, NULL, 0); tcp_err(tpcb, NULL); tcp_accept(tpcb, NULL); @@ -1012,7 +1086,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 bf1bb28..753e06c 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,18 +45,18 @@ #include "lwip/udp.h" #include "lwip/tcp.h" #include "lwip/opt.h" +#include /* Exported types ------------------------------------------------------------*/ /* TCP connection state */ -typedef enum -{ +typedef enum { TCP_NONE = 0, TCP_CONNECTED, TCP_RECEIVED, TCP_SENT, TCP_ACCEPTED, TCP_CLOSING, -}tcp_client_states; +} tcp_client_states; /* Struct to store received data */ struct pbuf_data { @@ -74,6 +70,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 */ @@ -115,33 +112,37 @@ struct tcp_struct { #define MAX_CLIENT 32 #ifdef ETH_INPUT_USE_IT -extern struct netif gnetif; + extern struct netif gnetif; #endif /* 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); +void stm32_eth_get_macaddr(uint8_t *mac); +void stm32_eth_set_macaddr(const uint8_t *mac); +uint8_t stm32_eth_link_up(void); void stm32_eth_scheduler(void); void User_notification(struct netif *netif); #if LWIP_DHCP -void stm32_DHCP_Process(struct netif *netif); -void stm32_DHCP_Periodic_Handle(struct netif *netif); -void stm32_DHCP_manual_config(void); -uint8_t stm32_get_DHCP_lease_state(void); -void stm32_set_DHCP_state(uint8_t state); -uint8_t stm32_get_DHCP_state(void); -uint8_t stm32_dhcp_started(void); + void stm32_DHCP_Process(struct netif *netif); + void stm32_DHCP_Periodic_Handle(struct netif *netif); + void stm32_DHCP_manual_config(void); + uint8_t stm32_get_DHCP_lease_state(void); + void stm32_set_DHCP_state(uint8_t state); + uint8_t stm32_get_DHCP_state(void); + uint8_t stm32_dhcp_started(void); #else -#error "LWIP_DHCP must be enabled in lwipopts.h" + #error "LWIP_DHCP must be enabled in lwipopts.h" #endif #if LWIP_DNS -void stm32_dns_init(const uint8_t *dnsaddr); -int8_t stm32_dns_gethostbyname(const char *hostname, uint32_t *ipaddr); + void stm32_dns_init(const uint8_t *dnsaddr); + int8_t stm32_dns_gethostbyname(const char *hostname, uint32_t *ipaddr); #else -#error "LWIP_DNS must be enabled in lwipopts.h" + #error "LWIP_DNS must be enabled in lwipopts.h" #endif #if LWIP_UDP @@ -165,15 +166,11 @@ ip_addr_t *u8_to_ip_addr(uint8_t *ipu8, ip_addr_t *ipaddr); uint32_t ip_addr_to_u32(ip_addr_t *ipaddr); #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); + 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 - -#ifdef __cplusplus -} + #error "LWIP_TCP must be enabled in lwipopts.h" #endif #endif /* __STM32_ETH_H__ */