From ee404065cefcc47dade887a5e70850880d847730 Mon Sep 17 00:00:00 2001 From: retr0 <42kdesigners@gmail.com> Date: Tue, 25 Aug 2026 13:17:40 +0200 Subject: [PATCH] ShineLAN-X, ShineWifi-X und tools/shinediag nach growatt-diagnose-tool verschoben Konsolidierung aller Hardware-Diagnose/Firmware-Projekte in einem eigenen, privaten Repo (gitea.bitfire.work/retr0/growatt-diagnose-tool). Dieses Repo bleibt reines Energiemanagement (ShineBridge). Co-Authored-By: Claude Sonnet 5 --- .gitignore | 18 +- ShineLAN-X/README.md | 119 -- ShineLAN-X/firmware/.gitignore | 5 - ShineLAN-X/firmware/.vscode/extensions.json | 10 - ShineLAN-X/firmware/CLAUDE.md | 20 - ShineLAN-X/firmware/include/config.h | 62 - ShineLAN-X/firmware/include/config_manager.h | 50 - .../firmware/include/enc28j60_bitbang.h | 29 - ShineLAN-X/firmware/lib/EthernetENC/.piopm | 1 - ShineLAN-X/firmware/lib/EthernetENC/README.md | 158 -- .../lib/EthernetENC/library.properties | 11 - .../firmware/lib/EthernetENC/src/Dhcp.cpp | 491 ----- .../firmware/lib/EthernetENC/src/Dhcp.h | 189 -- .../firmware/lib/EthernetENC/src/Dns.cpp | 416 ---- ShineLAN-X/firmware/lib/EthernetENC/src/Dns.h | 41 - .../firmware/lib/EthernetENC/src/Ethernet.cpp | 559 ----- .../firmware/lib/EthernetENC/src/Ethernet.h | 161 -- .../lib/EthernetENC/src/EthernetClient.cpp | 663 ------ .../lib/EthernetENC/src/EthernetClient.h | 110 - .../lib/EthernetENC/src/EthernetENC.h | 1 - .../lib/EthernetENC/src/EthernetServer.cpp | 112 - .../lib/EthernetENC/src/EthernetServer.h | 55 - .../lib/EthernetENC/src/EthernetUdp.cpp | 424 ---- .../lib/EthernetENC/src/EthernetUdp.h | 139 -- .../firmware/lib/EthernetENC/src/tcp_states.h | 21 - .../src/utility/Enc28J60Network.cpp | 391 ---- .../EthernetENC/src/utility/Enc28J60Network.h | 94 - .../lib/EthernetENC/src/utility/enc28j60.h | 257 --- .../lib/EthernetENC/src/utility/mempool.cpp | 168 -- .../lib/EthernetENC/src/utility/mempool.h | 54 - .../EthernetENC/src/utility/mempool_conf.h | 34 - .../lib/EthernetENC/src/utility/uip-conf.h | 185 -- .../lib/EthernetENC/src/utility/uip.c | 1897 ----------------- .../lib/EthernetENC/src/utility/uip.h | 1601 -------------- .../lib/EthernetENC/src/utility/uip_arch.h | 138 -- .../lib/EthernetENC/src/utility/uip_arp.c | 422 ---- .../lib/EthernetENC/src/utility/uip_arp.h | 144 -- .../src/utility/uipethernet-conf.h | 48 - .../lib/EthernetENC/src/utility/uipopt.h | 555 ----- .../lib/EthernetENC/src/utility/util.h | 10 - ShineLAN-X/firmware/platformio.ini | 22 - ShineLAN-X/firmware/src/main.cpp | 712 ------- ShineLAN-X/releases/.gitkeep | 0 ShineLAN-X/releases/nuttx-mbusd-shinelanx.bin | Bin 95384 -> 0 bytes .../Growatt MIC 1500 TL-X.yaml | 212 -- .../Growatt MIC 2000 TL-X.yaml | 212 -- ShineWifi-X/Growatt MIC 2000 TL-X/README.md | 50 - .../Growatt MOD 6000 TL3-XH.yaml | 331 --- ShineWifi-X/Growatt MOD 6000 TL3-XH/README.md | 67 - .../Growatt SPH 5000 TL3-BH-UP.yaml | 452 ---- ShineWifi-X/Growatt SPH 5000 TL3/README.md | 92 - tools/shinediag/diagnose.py | 278 --- tools/shinediag/setup/dhcpcd.conf | 8 - tools/shinediag/setup/dnsmasq.conf | 9 - tools/shinediag/setup/hostapd.conf | 16 - tools/shinediag/setup/install.sh | 69 - tools/shinediag/web/index.html | 601 ------ 57 files changed, 9 insertions(+), 12985 deletions(-) delete mode 100644 ShineLAN-X/README.md delete mode 100644 ShineLAN-X/firmware/.gitignore delete mode 100644 ShineLAN-X/firmware/.vscode/extensions.json delete mode 100644 ShineLAN-X/firmware/CLAUDE.md delete mode 100644 ShineLAN-X/firmware/include/config.h delete mode 100644 ShineLAN-X/firmware/include/config_manager.h delete mode 100644 ShineLAN-X/firmware/include/enc28j60_bitbang.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/.piopm delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/README.md delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/library.properties delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/Dhcp.cpp delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/Dhcp.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/Dns.cpp delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/Dns.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/Ethernet.cpp delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/Ethernet.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/EthernetClient.cpp delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/EthernetClient.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/EthernetENC.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/EthernetServer.cpp delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/EthernetServer.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/EthernetUdp.cpp delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/EthernetUdp.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/tcp_states.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/Enc28J60Network.cpp delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/Enc28J60Network.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/enc28j60.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool.cpp delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool_conf.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip-conf.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip.c delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arch.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arp.c delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arp.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/uipethernet-conf.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/uipopt.h delete mode 100644 ShineLAN-X/firmware/lib/EthernetENC/src/utility/util.h delete mode 100644 ShineLAN-X/firmware/platformio.ini delete mode 100644 ShineLAN-X/firmware/src/main.cpp delete mode 100644 ShineLAN-X/releases/.gitkeep delete mode 100755 ShineLAN-X/releases/nuttx-mbusd-shinelanx.bin delete mode 100644 ShineWifi-X/Growatt MIC 1500 TL-X/Growatt MIC 1500 TL-X.yaml delete mode 100644 ShineWifi-X/Growatt MIC 2000 TL-X/Growatt MIC 2000 TL-X.yaml delete mode 100644 ShineWifi-X/Growatt MIC 2000 TL-X/README.md delete mode 100644 ShineWifi-X/Growatt MOD 6000 TL3-XH/Growatt MOD 6000 TL3-XH.yaml delete mode 100644 ShineWifi-X/Growatt MOD 6000 TL3-XH/README.md delete mode 100644 ShineWifi-X/Growatt SPH 5000 TL3/Growatt SPH 5000 TL3-BH-UP.yaml delete mode 100644 ShineWifi-X/Growatt SPH 5000 TL3/README.md delete mode 100644 tools/shinediag/diagnose.py delete mode 100644 tools/shinediag/setup/dhcpcd.conf delete mode 100644 tools/shinediag/setup/dnsmasq.conf delete mode 100644 tools/shinediag/setup/hostapd.conf delete mode 100644 tools/shinediag/setup/install.sh delete mode 100644 tools/shinediag/web/index.html diff --git a/.gitignore b/.gitignore index 0910caf..053d712 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,13 @@ -*.log -*.txt -*.json -!haos-addon/**/*.json -shinelanx-modbus/ -__pycache__/ -*.pyc -.env +__pycache__/ +!haos-addon/**/*.json +.env +*.json +*.log +*.pyc +*.txt # versehentlich getrackte Alt-Dateien, gehoeren nicht ins Repo +growatt-diagnose-tool/ Modbus-Server-Kathrein.pdf ShineLAN-X/releases/*.bin -growatt-diagnose-tool/ +shinelanx-modbus/ diff --git a/ShineLAN-X/README.md b/ShineLAN-X/README.md deleted file mode 100644 index 6080e0a..0000000 --- a/ShineLAN-X/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# Growatt ShineLAN-X — Custom Firmware - -Custom-Firmware für den **Growatt ShineLAN-X** LAN-Stick. Liest Modbus RTU vom Wechselrichter und publiziert alle Werte per **MQTT** direkt in Home Assistant — vollständig lokal, ohne Growatt-Cloud. - ---- - -## Hardware - -| Komponente | Details | -|---|---| -| MCU | STM32F103RBT6 (LQFP-64) | -| Ethernet | ENC28J60 (Bitbang SPI2 auf PB12–PB15) | -| Wechselrichter-Anbindung | USB-CDC (virtueller COM-Port, 115200 Baud) | -| Programmier-Interface | SWD via ST-Link | - -### Pin-Belegung (vollständig verifiziert) - -Quelle: [mwalle/shinelanx-modbus](https://github.com/mwalle/shinelanx-modbus) — identische Platine. - -| Funktion | Pin | Hinweis | -|---|---|---| -| ETH CS | PB12 | SPI2 NSS | -| ETH SCK | PB13 | SPI2 SCK | -| ETH MISO | PB14 | SPI2 MISO | -| ETH MOSI | PB15 | SPI2 MOSI | -| ETH RST | PC8 | | -| ETH INT | PC6 | optional | -| USB D- | PA11 | Wechselrichter-Kommunikation | -| USB D+ | PA12 | Wechselrichter-Kommunikation | -| USB Pullup | PA8 | | -| Debug UART TX | PA9 | USART1, 115200 | -| Debug UART RX | PA10 | USART1, 115200 | -| LED Debug | PC7 | aktiv LOW | -| LED Rot | PB1 | aktiv LOW | -| LED Grün | PB0 | aktiv LOW | -| LED Blau | PC5 | aktiv LOW | -| Taster | PA3 | aktiv LOW | - -### SWD-Verkabelung (Flashen) - -| ShineLAN-X Testpunkt | ST-Link V2 | -|---|---| -| SWDIO | SWDIO | -| SWDCLK | SWCLK | -| GND | GND | -| 3.3V | 3.3V (nur wenn nicht extern versorgt) | - ---- - -## Konfiguration - -Alle Einstellungen in `firmware/include/config.h`: - -```cpp -// DHCP (1) oder statische IP (0) -#define USE_DHCP 1 - -// MQTT-Broker -#define MQTT_BROKER "192.168.1.1" -#define MQTT_USER "mqtt" -#define MQTT_PASSWORD "..." - -// Modbus Slave-Adresse (Standard: 1) -#define MODBUS_ADDR 1 -``` - ---- - -## Flashen - -```bash -cd firmware -pio run --target upload -``` - ---- - -## Wechselrichter-Kompatibilität - -Die Sensor-Liste in `main.cpp` entspricht dem **Growatt SPH 5000 TL3-BH-UP** (dreiphasig, mit Batterie). - -Beim Testen mit dem **MIC 1500 TL-X** (einphasig, ohne Batterie): - -| Unterschied | MIC 1500 | SPH 5000 | -|---|---|---| -| AC Output Power | Register **40** | Register 35 | -| PV-Strings | nur PV1 | PV1 + PV2 | -| Phasen | nur L1 | L1 / L2 / L3 | -| Batterie-Register | nicht vorhanden | 1009–1058 | - -Nicht vorhandene Register werden automatisch übersprungen (kein Absturz). Für Dauerbetrieb am MIC 1500 einfach die nicht passenden Sensoren in `main.cpp` auskommentieren und `ac_power_total` auf Register 40 ändern. - ---- - -## MQTT Topics - -Nach dem Start sendet die Firmware automatisch **MQTT Discovery** — Home Assistant erkennt das Gerät ohne manuelle Konfiguration. - -| Topic | Inhalt | -|---|---| -| `homeassistant/sensor/growatt_shinelan_*/config` | Discovery (retained) | -| `growatt/shinelan/pv1_power` | PV1 Leistung in W | -| `growatt/shinelan/ac_power_total` | AC Ausgangsleistung in W | -| `growatt/shinelan/energy_today` | Tagesenergie in kWh | -| `growatt/shinelan/energy_total` | Gesamtenergie in kWh | -| `growatt/shinelan/bat_soc` | Batterie Ladestand in % | -| `growatt/shinelan/energy_import_total` | Netzbezug gesamt in kWh | - ---- - -## LED-Status - -| LED | Bedeutung | -|---|---| -| Alle 3 kurz an | Startup | -| Grün | MQTT verbunden | -| Rot | MQTT-Verbindungsfehler | -| Debug (PC7) | Ethernet Link UP | -| Blau blinkt | Modbus-Abfrage läuft | diff --git a/ShineLAN-X/firmware/.gitignore b/ShineLAN-X/firmware/.gitignore deleted file mode 100644 index 89cc49c..0000000 --- a/ShineLAN-X/firmware/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.pio -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json -.vscode/ipch diff --git a/ShineLAN-X/firmware/.vscode/extensions.json b/ShineLAN-X/firmware/.vscode/extensions.json deleted file mode 100644 index 080e70d..0000000 --- a/ShineLAN-X/firmware/.vscode/extensions.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ], - "unwantedRecommendations": [ - "ms-vscode.cpptools-extension-pack" - ] -} diff --git a/ShineLAN-X/firmware/CLAUDE.md b/ShineLAN-X/firmware/CLAUDE.md deleted file mode 100644 index ba71fe8..0000000 --- a/ShineLAN-X/firmware/CLAUDE.md +++ /dev/null @@ -1,20 +0,0 @@ -# Growatt ShineLAN-X Firmware - -PlatformIO-Projekt fuer STM32F103RBT6. Liest Modbus RTU vom Wechselrichter via USB-CDC und publiziert via MQTT an Home Assistant. - -## Wiki Knowledge Base - -Path: /Users/retr0/Nextcloud/HomeLab/Vault - -Beim Start einer neuen Session: -1. Zuerst `wiki/hot.md` lesen (aktueller Kontext) -2. Dann `wiki/entities/Growatt ShineLAN-X Firmware.md` fuer Projektdetails -3. Nur bei Bedarf weitere Wiki-Seiten - -## Wichtige Konventionen - -- Kommentare nur wenn das WARUM nicht offensichtlich ist -- Keine trailing summaries nach Tool-Calls -- config.h ist geheimnis-frei (DEFAULT_* Werte) -- committen OK -- Echte Credentials werden nur im EEPROM gespeichert, nie im Source -- Framework-Hacks in `~/.platformio/` werden bei `pio pkg update` zurueckgesetzt diff --git a/ShineLAN-X/firmware/include/config.h b/ShineLAN-X/firmware/include/config.h deleted file mode 100644 index 255c265..0000000 --- a/ShineLAN-X/firmware/include/config.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -// ============================================================ -// Pin-Belegung — STM32F103RBT6, LQFP-64 -// Quelle: https://github.com/mwalle/shinelanx-modbus -// ============================================================ - -// ENC28J60 — SPI2 (Hardware-SPI) -#define ETH_CS_PIN PB12 -#define ETH_SCK_PIN PB13 -#define ETH_MISO_PIN PB14 -#define ETH_MOSI_PIN PB15 -#define ETH_RST_PIN PC8 - -// LEDs (aktiv LOW) -#define LED_DEBUG PC7 -#define LED_RED PB1 -#define LED_GREEN PB0 -#define LED_BLUE PC5 - -// User-Taster -#define BTN_USER PA3 - -// MAC-Adresse -#define MAC_ADDRESS 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED - -// ============================================================ -// MODBUS -// ============================================================ -#define MODBUS_BAUD 9600 -#define MODBUS_ADDR 1 - -// ============================================================ -// GERÄT -// ============================================================ -#define MQTT_CLIENT "growatt-shinelan" -#define DEVICE_ID "growatt_shinelan" -#define DEVICE_NAME "Growatt ShineLAN-X" -#define DEVICE_MODEL "ShineLAN-X" -#define DEVICE_MFR "Growatt" - -// ============================================================ -// STANDARD-WERTE (erster Boot / Werkseinstellungen) -// Werden über die Web-UI überschrieben und in EEPROM gespeichert. -// ============================================================ -#define DEFAULT_DHCP true -#define DEFAULT_STATIC_IP 192,168,1,99 -#define DEFAULT_GW 192,168,1,1 -#define DEFAULT_SUBNET 255,255,255,0 -#define DEFAULT_DNS 192,168,1,1 -#define DEFAULT_MQTT_BROKER "192.168.1.1" -#define DEFAULT_MQTT_PORT 1883 -#define DEFAULT_MQTT_USER "" -#define DEFAULT_MQTT_PASS "" -#define DEFAULT_UPDATE_MS 10000UL - -// ============================================================ -// WECHSELRICHTER-MODELL -// Genau einen einkommentieren: -// ============================================================ -#define INVERTER_MIC1500 // Growatt MIC 1500/2000 TL-X (einphasig) -// #define INVERTER_SPH5000 // Growatt SPH 5000 TL3 (dreiphasig, Hybrid) diff --git a/ShineLAN-X/firmware/include/config_manager.h b/ShineLAN-X/firmware/include/config_manager.h deleted file mode 100644 index 4af36a3..0000000 --- a/ShineLAN-X/firmware/include/config_manager.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include -#include -#include "config.h" - -#define CFG_MAGIC 0xA55A1234 - -struct NetConfig { - uint32_t magic; - bool useDhcp; - uint8_t ip[4]; - uint8_t gw[4]; - uint8_t sn[4]; - uint8_t dns[4]; - char mqttBroker[40]; - uint16_t mqttPort; - char mqttUser[32]; - char mqttPass[64]; - uint32_t updateMs; -}; - -inline void configDefaults(NetConfig& c) { - c.magic = CFG_MAGIC; - c.useDhcp = DEFAULT_DHCP; - uint8_t dip[] = {DEFAULT_STATIC_IP}; - uint8_t dgw[] = {DEFAULT_GW}; - uint8_t dsn[] = {DEFAULT_SUBNET}; - uint8_t ddn[] = {DEFAULT_DNS}; - memcpy(c.ip, dip, 4); - memcpy(c.gw, dgw, 4); - memcpy(c.sn, dsn, 4); - memcpy(c.dns, ddn, 4); - strncpy(c.mqttBroker, DEFAULT_MQTT_BROKER, sizeof(c.mqttBroker) - 1); - c.mqttBroker[sizeof(c.mqttBroker) - 1] = 0; - c.mqttPort = DEFAULT_MQTT_PORT; - strncpy(c.mqttUser, DEFAULT_MQTT_USER, sizeof(c.mqttUser) - 1); - c.mqttUser[sizeof(c.mqttUser) - 1] = 0; - strncpy(c.mqttPass, DEFAULT_MQTT_PASS, sizeof(c.mqttPass) - 1); - c.mqttPass[sizeof(c.mqttPass) - 1] = 0; - c.updateMs = DEFAULT_UPDATE_MS; -} - -inline void configLoad(NetConfig& c) { - EEPROM.get(0, c); - if (c.magic != CFG_MAGIC) configDefaults(c); -} - -inline void configSave(const NetConfig& c) { - EEPROM.put(0, c); -} diff --git a/ShineLAN-X/firmware/include/enc28j60_bitbang.h b/ShineLAN-X/firmware/include/enc28j60_bitbang.h deleted file mode 100644 index b0c6924..0000000 --- a/ShineLAN-X/firmware/include/enc28j60_bitbang.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include "config.h" - -// ============================================================ -// Bitbang-SPI für ENC28J60 auf PC6/PC7/PC8/PC9 -// ENC28J60 verwendet SPI Mode 0 (CPOL=0, CPHA=0) -// ============================================================ - -static inline void eth_spi_init() { - pinMode(ETH_SCK_PIN, OUTPUT); digitalWrite(ETH_SCK_PIN, LOW); - pinMode(ETH_MOSI_PIN, OUTPUT); digitalWrite(ETH_MOSI_PIN, LOW); - pinMode(ETH_MISO_PIN, INPUT); - pinMode(ETH_CS_PIN, OUTPUT); digitalWrite(ETH_CS_PIN, HIGH); -} - -static inline void eth_cs_low() { digitalWrite(ETH_CS_PIN, LOW); } -static inline void eth_cs_high() { digitalWrite(ETH_CS_PIN, HIGH); } - -static inline uint8_t eth_spi_transfer(uint8_t out) { - uint8_t in = 0; - for (int8_t i = 7; i >= 0; i--) { - digitalWrite(ETH_MOSI_PIN, (out >> i) & 1); - digitalWrite(ETH_SCK_PIN, HIGH); - in = (in << 1) | digitalRead(ETH_MISO_PIN); - digitalWrite(ETH_SCK_PIN, LOW); - } - return in; -} diff --git a/ShineLAN-X/firmware/lib/EthernetENC/.piopm b/ShineLAN-X/firmware/lib/EthernetENC/.piopm deleted file mode 100644 index 01c20e6..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/.piopm +++ /dev/null @@ -1 +0,0 @@ -{"type": "library", "name": "EthernetENC", "version": "2.0.5", "spec": {"owner": "jandrassy", "id": 11203, "name": "EthernetENC", "requirements": null, "uri": null}} \ No newline at end of file diff --git a/ShineLAN-X/firmware/lib/EthernetENC/README.md b/ShineLAN-X/firmware/lib/EthernetENC/README.md deleted file mode 100644 index c7d7411..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/README.md +++ /dev/null @@ -1,158 +0,0 @@ -First an important note: There are more suitable libraries for ENC28J60 with ESP8266, RP2040, ESP32 and Mbed Arduino platforms: - -* With ESP8266 and RP2040 platform you can use the EthernetCompat.h from the lwIP_enc28j60 bundled library. -* With ESP32 platform version 3 you can use the [EthernetESP32](https://github.com/Networking-for-Arduino/EthernetESP32) library which integrates with networking on the ESP32 platform. -* With Arduino Mbed Core boards you can use the [ENC28J60-EMAC](https://github.com/Networking-for-Arduino/ENC28J60-EMAC) library with the platforms Ethernet library - ---- - -# EthernetENC - -EthernetENC is the Ethernet library for ENC28J60. It is a modern version of [the UIPEthernet library](https://github.com/jandrassy/EthernetENC/wiki/UIPEthernet). - -The modernization includes: -* Ethernet 2.0.0 Arduino library functions -* compatible include file names EthernetClient.h, EthernetServer.h and EthernetUdp.h -* support of many Arduino architectures by using the SPI library -* SPI transactions to share the SPI bus with devices with different communication settings -* SPI communication at 20 MHz if the MCU supports it, else on the maximum supported by the MCU -* client.flush() to send the packet immediately -* calls yield() in blocking functions -* has UDP backlog to receive more than one message at time -* Arduino 1.5 library format built with dot_a_linkage option for optimal build result - -[The documentation of Arduino Ethernet library](https://www.arduino.cc/en/Reference/Ethernet) applies for classes and functions descriptions. - -Limitations: -* UDP.beginMulticast is not supported, because the uIP stack doesn't support multicast -* UDP broadcasts receiving is turned off on ENC to lower the processing load on the library - -This library doesn't have examples, because examples of the Arduino Ethernet library apply. You can find them in the Arduino IDE Examples menu Ethernet section. Only change `#include ` to `#include `. Some examples require [a little change](https://github.com/jandrassy/EthernetENC/wiki/Examples). - -This library is based on the Norbert Truchsess's arduino-uip original source code repository and uses experience from the development of the multi-architecture support by Cassy. Applicable fixes and enhancements from development of EthernetENC were transfered to Cassy's UIPEthernet. - -**You can find more information in project's [Wiki](https://github.com/jandrassy/EthernetENC/wiki).** - -## Original notes by Norbert Truchsess - -UIPEthernet is written as a wrapper around the mature uIP Stack by Adam Dunkels, which provides the low-level implementation for all supported protocols. To overcome the memory-constrains (a 'regular' uIP-application does all processing in RAM) the ENC28J60 internal memory is used for all stream buffers (in and out). Only 400-600 Bytes of Arduinos RAM are used (depending on the number of concurrently open connections). As of Flash-memory a ATmega368-based Arduino is the minimum requirenment. - -uIP was written by Adam Dunkels of the Networked Embedded Systems group at the Swedish Institute of Computer Science. - -This library was inspired by the SerialIP implementation by Adam Nielsen , actually I took this code as a starting point, but in the latest versions there are very few lines left. - - -## Licenses -``` -UIPEthernet.h -UIPEthernet.cpp -UIPServer.h -UIPServer.cpp -UIPClient.h -UIPClient.cpp -UIPUdp.h -UIPUdp.cpp -utility/mempool.h -utility/mempool.cpp - -Copyright (c) 2013 Norbert Truchsess -All rights reserved. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - --------------- - -utility/enc28j60.h - -Author : Pascal Stang (c)2005 -Modified by Norbert Truchsess -Copyright: GPL V2 - --------------- - -utility/Enc28J60Network.h -utility/Enc28J60Network.cpp - -Copyright (c) 2013 Norbert Truchsess -All rights reserved. - -inspired and based on enc28j60.c file from the AVRlib library by Pascal Stang. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - --------------- - -utility/uip.c -utility/uip_arp.h -utility/uip_arp.c -utility/uip_arch.h -utility/uip.h -utility/uipopt.h - -Copyright (c) 2001-2003, Adam Dunkels , . -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. Redistributions 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 the Institute nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE 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. - --------------- - -Dhcp.h -Dhcp.cpp - -DHCP Library v0.3 - April 25, 2009 -Author: Jordan Terrell - blog.jordanterrell.com -- as included in Arduinos stock Ethernet-library, no special licence mentioned here - --------------- - -Dns.h -Dns.cpp - -(c) Copyright 2009-2010 MCQN Ltd. -Released under Apache License, version 2.0 - -``` \ No newline at end of file diff --git a/ShineLAN-X/firmware/lib/EthernetENC/library.properties b/ShineLAN-X/firmware/lib/EthernetENC/library.properties deleted file mode 100644 index eecd621..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/library.properties +++ /dev/null @@ -1,11 +0,0 @@ -name=EthernetENC -author=Norbert Truchsess, Juraj Andrassy -maintainer=Juraj Andrassy -sentence=Ethernet library for ENC28J60. Only include EthernetENC.h instead of Ethernet.h -paragraph=This is a modern version of the UIPEthernet library. EthernetENC library is compatible with all Arduino architectures with Arduino SPI library with transactions support. -url=https://github.com/Networking-for-Arduino/EthernetENC/wiki -architectures=* -version=2.0.5 -category=Communication -includes=EthernetENC.h -dot_a_linkage=true diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/Dhcp.cpp b/ShineLAN-X/firmware/lib/EthernetENC/src/Dhcp.cpp deleted file mode 100644 index cfcd2f6..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/Dhcp.cpp +++ /dev/null @@ -1,491 +0,0 @@ -// DHCP Library v0.3 - April 25, 2009 -// Author: Jordan Terrell - blog.jordanterrell.com - -#include "Dhcp.h" -#include -#include "utility/util.h" - -int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) -{ - _dhcpLeaseTime=0; - _dhcpT1=0; - _dhcpT2=0; - _lastCheck=0; - _timeout = timeout; - _responseTimeout = responseTimeout; - - // zero out _dhcpMacAddr - memset(_dhcpMacAddr, 0, 6); - reset_DHCP_lease(); - - memcpy((void*)_dhcpMacAddr, (void*)mac, 6); - _dhcp_state = STATE_DHCP_START; - return request_DHCP_lease(); -} - -void DhcpClass::reset_DHCP_lease(){ - memset(_dhcpLocalIp, 0, 4); - memset(_dhcpSubnetMask, 0, 4); - memset(_dhcpGatewayIp, 0, 4); - memset(_dhcpDhcpServerIp, 0, 4); - memset(_dhcpDnsServerIp, 0, 4); -} - -//return:0 on error, 1 if request is sent and response is received -int DhcpClass::request_DHCP_lease(){ - - uint8_t messageType = 0; - - - - // Pick an initial transaction ID - _dhcpTransactionId = random(1UL, 2000UL); - _dhcpInitialTransactionId = _dhcpTransactionId; - - _dhcpUdpSocket.stop(); - if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) - { - // Couldn't get a socket - return 0; - } - - presend_DHCP(); - - int result = 0; - - unsigned long startTime = millis(); - - while(_dhcp_state != STATE_DHCP_LEASED) - { - if(_dhcp_state == STATE_DHCP_START) - { - _dhcpTransactionId++; - - send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000)); - _dhcp_state = STATE_DHCP_DISCOVER; - } - else if(_dhcp_state == STATE_DHCP_REREQUEST){ - _dhcpTransactionId++; - send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime)/1000)); - _dhcp_state = STATE_DHCP_REQUEST; - } - else if(_dhcp_state == STATE_DHCP_DISCOVER) - { - uint32_t respId; - messageType = parseDHCPResponse(_responseTimeout, respId); - if(messageType == DHCP_OFFER) - { - // We'll use the transaction ID that the offer came with, - // rather than the one we were up to - _dhcpTransactionId = respId; - send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000)); - _dhcp_state = STATE_DHCP_REQUEST; - } - } - else if(_dhcp_state == STATE_DHCP_REQUEST) - { - uint32_t respId; - messageType = parseDHCPResponse(_responseTimeout, respId); - if(messageType == DHCP_ACK) - { - _dhcp_state = STATE_DHCP_LEASED; - result = 1; - //use default lease time if we didn't get it - if(_dhcpLeaseTime == 0){ - _dhcpLeaseTime = DEFAULT_LEASE; - } - //calculate T1 & T2 if we didn't get it - if(_dhcpT1 == 0){ - //T1 should be 50% of _dhcpLeaseTime - _dhcpT1 = _dhcpLeaseTime >> 1; - } - if(_dhcpT2 == 0){ - //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime - _dhcpT2 = _dhcpT1 << 1; - } - _renewInSec = _dhcpT1; - _rebindInSec = _dhcpT2; - } - else if(messageType == DHCP_NAK) - _dhcp_state = STATE_DHCP_START; - } - - if(messageType == 255) - { - messageType = 0; - _dhcp_state = STATE_DHCP_START; - } - - if(result != 1 && ((millis() - startTime) > _timeout)) - break; - } - - // We're done with the socket now - _dhcpUdpSocket.stop(); - _dhcpTransactionId++; - - return result; -} - -void DhcpClass::presend_DHCP() -{ -} - -void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed) -{ - uint8_t buffer[32]; - memset(buffer, 0, 32); - IPAddress dest_addr( 255, 255, 255, 255 ); // Broadcast address - - if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT)) - { - // FIXME Need to return errors - return; - } - - buffer[0] = DHCP_BOOTREQUEST; // op - buffer[1] = DHCP_HTYPE10MB; // htype - buffer[2] = DHCP_HLENETHERNET; // hlen - buffer[3] = DHCP_HOPS; // hops - - // xid - unsigned long xid = htonl(_dhcpTransactionId); - memcpy(buffer + 4, &(xid), 4); - - // 8, 9 - seconds elapsed - buffer[8] = ((secondsElapsed & 0xff00) >> 8); - buffer[9] = (secondsElapsed & 0x00ff); - - // flags - the only one flag DHCP uses is - // to request the server response as broadcast, not unicast -// unsigned short flags = htons(DHCP_FLAGSBROADCAST); -// memcpy(buffer + 10, &(flags), 2); - - // ciaddr: already zeroed - // yiaddr: already zeroed - // siaddr: already zeroed - // giaddr: already zeroed - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 28); - - memset(buffer, 0, 32); // clear local buffer - - memcpy(buffer, _dhcpMacAddr, 6); // chaddr - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 16); - - memset(buffer, 0, 32); // clear local buffer - - // leave zeroed out for sname && file - // put in W5100 transmit buffer x 6 (192 bytes) - - for(int i = 0; i < 6; i++) { - _dhcpUdpSocket.write(buffer, 32); - } - - // OPT - Magic Cookie - buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24)& 0xFF); - buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16)& 0xFF); - buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8)& 0xFF); - buffer[3] = (uint8_t)(MAGIC_COOKIE& 0xFF); - - // OPT - message type - buffer[4] = dhcpMessageType; - buffer[5] = 0x01; - buffer[6] = messageType; //DHCP_REQUEST; - - // OPT - client identifier - buffer[7] = dhcpClientIdentifier; - buffer[8] = 0x07; - buffer[9] = 0x01; - memcpy(buffer + 10, _dhcpMacAddr, 6); - - // OPT - host name - buffer[16] = hostName; - if (_hostname == nullptr) { - buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address - strcpy((char*)&(buffer[18]), HOST_NAME); - - printByte((char*)&(buffer[24]), _dhcpMacAddr[3]); - printByte((char*)&(buffer[26]), _dhcpMacAddr[4]); - printByte((char*)&(buffer[28]), _dhcpMacAddr[5]); - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 30); - } else { - uint8_t len = strlen(_hostname); - buffer[17] = len; - _dhcpUdpSocket.write(buffer, 18); - _dhcpUdpSocket.write(_hostname, len); - } - - if(messageType == DHCP_REQUEST) - { - buffer[0] = dhcpRequestedIPaddr; - buffer[1] = 0x04; - buffer[2] = _dhcpLocalIp[0]; - buffer[3] = _dhcpLocalIp[1]; - buffer[4] = _dhcpLocalIp[2]; - buffer[5] = _dhcpLocalIp[3]; - - buffer[6] = dhcpServerIdentifier; - buffer[7] = 0x04; - buffer[8] = _dhcpDhcpServerIp[0]; - buffer[9] = _dhcpDhcpServerIp[1]; - buffer[10] = _dhcpDhcpServerIp[2]; - buffer[11] = _dhcpDhcpServerIp[3]; - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 12); - } - - buffer[0] = dhcpParamRequest; - buffer[1] = 0x06; - buffer[2] = subnetMask; - buffer[3] = routersOnSubnet; - buffer[4] = dns; - buffer[5] = domainName; - buffer[6] = dhcpT1value; - buffer[7] = dhcpT2value; - buffer[8] = endOption; - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 9); - - _dhcpUdpSocket.endPacket(); -} - -uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId) -{ - uint8_t type = 0; - uint8_t opt_len = 0; - - unsigned long startTime = millis(); - - while(_dhcpUdpSocket.parsePacket() <= 0) - { - if((millis() - startTime) > responseTimeout) - { - return 255; - } - delay(50); - } - // start reading in the packet - RIP_MSG_FIXED fixedMsg; - _dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED)); - - if(fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) - { - transactionId = ntohl(fixedMsg.xid); - if(memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || (transactionId < _dhcpInitialTransactionId) || (transactionId > _dhcpTransactionId)) - { - // Need to read the rest of the packet here regardless - _dhcpUdpSocket.discardReceived(); - return 0; - } - - memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4); - - // Skip to the option part - // Doing this a byte at a time so we don't have to put a big buffer - // on the stack (as we don't have lots of memory lying around) - for (int i =0; i < (240 - (int)sizeof(RIP_MSG_FIXED)); i++) - { - _dhcpUdpSocket.read(); // we don't care about the returned byte - } - - while (_dhcpUdpSocket.available() > 0) - { - switch (_dhcpUdpSocket.read()) - { - case endOption : - break; - - case padOption : - break; - - case dhcpMessageType : - opt_len = _dhcpUdpSocket.read(); - type = _dhcpUdpSocket.read(); - break; - - case subnetMask : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read(_dhcpSubnetMask, 4); - break; - - case routersOnSubnet : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read(_dhcpGatewayIp, 4); - for (int i = 0; i < opt_len-4; i++) - { - _dhcpUdpSocket.read(); - } - break; - - case dns : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); - for (int i = 0; i < opt_len-4; i++) - { - _dhcpUdpSocket.read(); - } - break; - - case dhcpServerIdentifier : - opt_len = _dhcpUdpSocket.read(); - if( _dhcpDhcpServerIp[0] == 0 || - IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP() ) - { - _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp)); - } - else - { - // Skip over the rest of this option - while (opt_len--) - { - _dhcpUdpSocket.read(); - } - } - break; - - case dhcpT1value : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1)); - _dhcpT1 = ntohl(_dhcpT1); - break; - - case dhcpT2value : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2)); - _dhcpT2 = ntohl(_dhcpT2); - break; - - case dhcpIPaddrLeaseTime : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime)); - _dhcpLeaseTime = ntohl(_dhcpLeaseTime); - _renewInSec = _dhcpLeaseTime; - break; - - default : - opt_len = _dhcpUdpSocket.read(); - // Skip over the rest of this option - while (opt_len--) - { - _dhcpUdpSocket.read(); - } - break; - } - } - } - - // Need to skip to end of the packet regardless here - _dhcpUdpSocket.discardReceived(); - - return type; -} - - -/* - 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(){ - //this uses a signed / unsigned trick to deal with millis overflow - unsigned long now = millis(); - signed long snow = (long)now; - int rc=DHCP_CHECK_NONE; - if (_lastCheck != 0){ - signed long factor; - //calc how many ms past the timeout we are - factor = snow - (long)_secTimeout; - //if on or passed the timeout, reduce the counters - if ( factor >= 0 ){ - //next timeout should be now plus 1000 ms minus parts of second in factor - _secTimeout = snow + 1000 - factor % 1000; - //how many seconds late are we, minimum 1 - factor = factor / 1000 +1; - - //reduce the counters by that mouch - //if we can assume that the cycle time (factor) is fairly constant - //and if the remainder is less than cycle time * 2 - //do it early instead of late - if(_renewInSec < factor*2 ) - _renewInSec = 0; - else - _renewInSec -= factor; - - if(_rebindInSec < factor*2 ) - _rebindInSec = 0; - else - _rebindInSec -= factor; - } - - //if we have a lease but should renew, do it - if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <=0){ - _dhcp_state = STATE_DHCP_REREQUEST; - rc = 1 + request_DHCP_lease(); - } - - //if we have a lease or is renewing but should bind, do it - if( (_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <=0){ - //this should basically restart completely - _dhcp_state = STATE_DHCP_START; - reset_DHCP_lease(); - rc = 3 + request_DHCP_lease(); - } - } - else{ - _secTimeout = snow + 1000; - } - - _lastCheck = now; - return rc; -} - -IPAddress DhcpClass::getLocalIp() -{ - return IPAddress(_dhcpLocalIp); -} - -IPAddress DhcpClass::getSubnetMask() -{ - return IPAddress(_dhcpSubnetMask); -} - -IPAddress DhcpClass::getGatewayIp() -{ - return IPAddress(_dhcpGatewayIp); -} - -IPAddress DhcpClass::getDhcpServerIp() -{ - return IPAddress(_dhcpDhcpServerIp); -} - -IPAddress DhcpClass::getDnsServerIp() -{ - return IPAddress(_dhcpDnsServerIp); -} - -void DhcpClass::setHostname(const char* hostname) { - _hostname = hostname; -} - -void DhcpClass::printByte(char * buf, uint8_t n ) { - char *str = &buf[1]; - buf[0]='0'; - do { - unsigned long m = n; - n /= 16; - char c = m - 16 * n; - *str-- = c < 10 ? c + '0' : c + 'A' - 10; - } while(n); -} diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/Dhcp.h b/ShineLAN-X/firmware/lib/EthernetENC/src/Dhcp.h deleted file mode 100644 index ec99072..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/Dhcp.h +++ /dev/null @@ -1,189 +0,0 @@ -// 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_START 0 -#define STATE_DHCP_DISCOVER 1 -#define STATE_DHCP_REQUEST 2 -#define STATE_DHCP_LEASED 3 -#define STATE_DHCP_REREQUEST 4 -#define STATE_DHCP_RELEASE 5 - -#define DHCP_FLAGSBROADCAST 0x8000 - -/* UDP port numbers for DHCP */ -#define DHCP_SERVER_PORT 67 /* from server to client */ -#define DHCP_CLIENT_PORT 68 /* from client to server */ - -/* DHCP message OP code */ -#define DHCP_BOOTREQUEST 1 -#define DHCP_BOOTREPLY 2 - -/* DHCP message type */ -#define DHCP_DISCOVER 1 -#define DHCP_OFFER 2 -#define DHCP_REQUEST 3 -#define DHCP_DECLINE 4 -#define DHCP_ACK 5 -#define DHCP_NAK 6 -#define DHCP_RELEASE 7 -#define DHCP_INFORM 8 - -#define DHCP_HTYPE10MB 1 -#define DHCP_HTYPE100MB 2 - -#define DHCP_HLENETHERNET 6 -#define DHCP_HOPS 0 -#define DHCP_SECS 0 - -#define MAGIC_COOKIE 0x63825363 -#define MAX_DHCP_OPT 16 - -#define HOST_NAME "ENC28J" -#define DEFAULT_LEASE (900) //default lease time in seconds - -#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) - -enum -{ - padOption = 0, - subnetMask = 1, - timerOffset = 2, - routersOnSubnet = 3, - /* timeServer = 4, - nameServer = 5,*/ - dns = 6, - /*logServer = 7, - cookieServer = 8, - lprServer = 9, - impressServer = 10, - resourceLocationServer = 11,*/ - hostName = 12, - /*bootFileSize = 13, - meritDumpFile = 14,*/ - domainName = 15, - /*swapServer = 16, - rootPath = 17, - extentionsPath = 18, - IPforwarding = 19, - nonLocalSourceRouting = 20, - policyFilter = 21, - maxDgramReasmSize = 22, - defaultIPTTL = 23, - pathMTUagingTimeout = 24, - pathMTUplateauTable = 25, - ifMTU = 26, - allSubnetsLocal = 27, - broadcastAddr = 28, - performMaskDiscovery = 29, - maskSupplier = 30, - performRouterDiscovery = 31, - routerSolicitationAddr = 32, - staticRoute = 33, - trailerEncapsulation = 34, - arpCacheTimeout = 35, - ethernetEncapsulation = 36, - tcpDefaultTTL = 37, - tcpKeepaliveInterval = 38, - tcpKeepaliveGarbage = 39, - nisDomainName = 40, - nisServers = 41, - ntpServers = 42, - vendorSpecificInfo = 43, - netBIOSnameServer = 44, - netBIOSdgramDistServer = 45, - netBIOSnodeType = 46, - netBIOSscope = 47, - xFontServer = 48, - xDisplayManager = 49,*/ - dhcpRequestedIPaddr = 50, - dhcpIPaddrLeaseTime = 51, - /*dhcpOptionOverload = 52,*/ - dhcpMessageType = 53, - dhcpServerIdentifier = 54, - dhcpParamRequest = 55, - /*dhcpMsg = 56, - dhcpMaxMsgSize = 57,*/ - dhcpT1value = 58, - dhcpT2value = 59, - /*dhcpClassIdentifier = 60,*/ - dhcpClientIdentifier = 61, - endOption = 255 -}; - -typedef struct _RIP_MSG_FIXED -{ - uint8_t op; - uint8_t htype; - uint8_t hlen; - uint8_t hops; - uint32_t xid; - uint16_t secs; - uint16_t flags; - uint8_t ciaddr[4]; - uint8_t yiaddr[4]; - uint8_t siaddr[4]; - uint8_t giaddr[4]; - uint8_t chaddr[6]; -}RIP_MSG_FIXED; - -class DhcpClass { -private: - uint32_t _dhcpInitialTransactionId; - uint32_t _dhcpTransactionId; - uint8_t _dhcpMacAddr[6]; - const char* _hostname = nullptr; -#ifdef __arm__ - uint8_t _dhcpLocalIp[4] __attribute__((aligned(4))); - uint8_t _dhcpSubnetMask[4] __attribute__((aligned(4))); - uint8_t _dhcpGatewayIp[4] __attribute__((aligned(4))); - uint8_t _dhcpDhcpServerIp[4] __attribute__((aligned(4))); - uint8_t _dhcpDnsServerIp[4] __attribute__((aligned(4))); -#else - uint8_t _dhcpLocalIp[4]; - uint8_t _dhcpSubnetMask[4]; - uint8_t _dhcpGatewayIp[4]; - uint8_t _dhcpDhcpServerIp[4]; - uint8_t _dhcpDnsServerIp[4]; -#endif - uint32_t _dhcpLeaseTime; - uint32_t _dhcpT1, _dhcpT2; - signed long _renewInSec; - signed long _rebindInSec; - signed long _lastCheck; - unsigned long _timeout; - unsigned long _responseTimeout; - unsigned long _secTimeout; - uint8_t _dhcp_state; - EthernetUDP _dhcpUdpSocket; - - int request_DHCP_lease(); - void reset_DHCP_lease(); - void presend_DHCP(); - void send_DHCP_MESSAGE(uint8_t, uint16_t); - void printByte(char *, uint8_t); - - uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId); -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(); - - void setHostname(const char* hostname); -}; - -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/Dns.cpp b/ShineLAN-X/firmware/lib/EthernetENC/src/Dns.cpp deleted file mode 100644 index 4d82236..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/Dns.cpp +++ /dev/null @@ -1,416 +0,0 @@ -// Arduino DNS client for Enc28J60-based Ethernet shield -// (c) Copyright 2009-2010 MCQN Ltd. -// Released under Apache License, version 2.0 - -#include "Dns.h" -#include - -#define SOCKET_NONE 255 -// Various flags and header field values for a DNS message -#define UDP_HEADER_SIZE 8 -#define DNS_HEADER_SIZE 12 -#define TTL_SIZE 4 -#define QUERY_FLAG (0) -#define RESPONSE_FLAG (1<<15) -#define QUERY_RESPONSE_MASK (1<<15) -#define OPCODE_STANDARD_QUERY (0) -#define OPCODE_INVERSE_QUERY (1<<11) -#define OPCODE_STATUS_REQUEST (2<<11) -#define OPCODE_MASK (15<<11) -#define AUTHORITATIVE_FLAG (1<<10) -#define TRUNCATION_FLAG (1<<9) -#define RECURSION_DESIRED_FLAG (1<<8) -#define RECURSION_AVAILABLE_FLAG (1<<7) -#define RESP_NO_ERROR (0) -#define RESP_FORMAT_ERROR (1) -#define RESP_SERVER_FAILURE (2) -#define RESP_NAME_ERROR (3) -#define RESP_NOT_IMPLEMENTED (4) -#define RESP_REFUSED (5) -#define RESP_MASK (15) -#define TYPE_A (0x0001) -#define CLASS_IN (0x0001) -#define LABEL_COMPRESSION_MASK (0xC0) -// Port number that DNS servers listen on -#define DNS_PORT 53 - -// Possible return codes from ProcessResponse -#define SUCCESS 1 -#define TIMED_OUT -1 -#define INVALID_SERVER -2 -#define TRUNCATED -3 -#define INVALID_RESPONSE -4 - -void DNSClient::begin(const IPAddress& aDNSServer) -{ - iDNSServer = aDNSServer; - iRequestId = 0; -} - - -int DNSClient::inet_aton(const char* aIPAddrString, IPAddress& aResult) -{ - // See if we've been given a valid IP address - const char* p =aIPAddrString; - while (*p && - ( (*p == '.') || ((*p >= '0') && (*p <= '9')) )) - { - p++; - } - - if (*p == '\0') - { - // It's looking promising, we haven't found any invalid characters - p = aIPAddrString; - int segment =0; - int segmentValue =0; - while (*p && (segment < 4)) - { - if (*p == '.') - { - // We've reached the end of a segment - if (segmentValue > 255) - { - // You can't have IP address segments that don't fit in a byte - return 0; - } - else - { - aResult[segment] = (byte)segmentValue; - segment++; - segmentValue = 0; - } - } - else - { - // Next digit - segmentValue = (segmentValue*10)+(*p - '0'); - } - p++; - } - // We've reached the end of address, but there'll still be the last - // segment to deal with - if ((segmentValue > 255) || (segment > 3)) - { - // You can't have IP address segments that don't fit in a byte, - // or more than four segments - return 0; - } - else - { - aResult[segment] = (byte)segmentValue; - return 1; - } - } - else - { - return 0; - } -} - -int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) -{ - int ret =0; - - // See if it's a numeric IP address - if (inet_aton(aHostname, aResult)) - { - // It is, our work here is done - return 1; - } - - // Check we've got a valid DNS server to use - if (iDNSServer == INADDR_NONE) - { - return INVALID_SERVER; - } - - // Find a socket to use - if (iUdp.begin(1024+(millis() & 0xF)) == 1) - { - // Try up to three times - int retries = 0; -// while ((retries < 3) && (ret <= 0)) - { - // Send DNS request - ret = iUdp.beginPacket(iDNSServer, DNS_PORT); - if (ret != 0) - { - // Now output the request data - ret = BuildRequest(aHostname); - if (ret != 0) - { - // And finally send the request - ret = iUdp.endPacket(); - if (ret != 0) - { - // Now wait for a response - int wait_retries = 0; - ret = TIMED_OUT; - while ((wait_retries < 3) && (ret == TIMED_OUT)) - { - ret = ProcessResponse(5000, aResult); - wait_retries++; - } - } - } - } - retries++; - } - - // We're done with the socket now - iUdp.stop(); - } - - return ret; -} - -uint16_t DNSClient::BuildRequest(const char* aName) -{ - // Build header - // 1 1 1 1 1 1 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - // | ID | - // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - // |QR| Opcode |AA|TC|RD|RA| Z | RCODE | - // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - // | QDCOUNT | - // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - // | ANCOUNT | - // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - // | NSCOUNT | - // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - // | ARCOUNT | - // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - // As we only support one request at a time at present, we can simplify - // some of this header - iRequestId = millis(); // generate a random ID - uint16_t twoByteBuffer; - - // FIXME We should also check that there's enough space available to write to, rather - // FIXME than assume there's enough space (as the code does at present) - iUdp.write((uint8_t*)&iRequestId, sizeof(iRequestId)); - - twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG); - iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); - - twoByteBuffer = htons(1); // One question record - iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); - - twoByteBuffer = 0; // Zero answer records - iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); - - iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); - // and zero additional records - iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); - - // Build question - const char* start =aName; - const char* end =start; - uint8_t len; - // Run through the name being requested - while (*end) - { - // Find out how long this section of the name is - end = start; - while (*end && (*end != '.') ) - { - end++; - } - - if (end-start > 0) - { - // Write out the size of this section - len = end-start; - iUdp.write(&len, sizeof(len)); - // And then write out the section - iUdp.write((uint8_t*)start, end-start); - } - start = end+1; - } - - // We've got to the end of the question name, so - // terminate it with a zero-length section - len = 0; - iUdp.write(&len, sizeof(len)); - // Finally the type and class of question - twoByteBuffer = htons(TYPE_A); - iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); - - twoByteBuffer = htons(CLASS_IN); // Internet class of question - iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); - // Success! Everything buffered okay - return 1; -} - - -uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) -{ - uint32_t startTime = millis(); - - // Wait for a response packet - while(iUdp.parsePacket() <= 0) - { - if((millis() - startTime) > aTimeout) - return TIMED_OUT; - delay(50); - } - - // We've had a reply! - // Read the UDP header - uint8_t header[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header - // Check that it's a response from the right server and the right port - if ( (iDNSServer != iUdp.remoteIP()) || - (iUdp.remotePort() != DNS_PORT) ) - { - // It's not from who we expected - return INVALID_SERVER; - } - - // Read through the rest of the response - if (iUdp.available() < DNS_HEADER_SIZE) - { - return TRUNCATED; - } - iUdp.read(header, DNS_HEADER_SIZE); - - uint16_t header_flags = htons(*((uint16_t*)&header[2])); - // Check that it's a response to this request - if ( ( iRequestId != (*((uint16_t*)&header[0])) ) || - ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t)RESPONSE_FLAG) ) - { - // Mark the entire packet as read - iUdp.discardReceived(); - return INVALID_RESPONSE; - } - // Check for any errors in the response (or in our request) - // although we don't do anything to get round these - if ( (header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK) ) - { - // Mark the entire packet as read - iUdp.discardReceived(); - return -5; //INVALID_RESPONSE; - } - - // And make sure we've got (at least) one answer - uint16_t answerCount = htons(*((uint16_t*)&header[6])); - if (answerCount == 0 ) - { - // Mark the entire packet as read - iUdp.discardReceived(); - return -6; //INVALID_RESPONSE; - } - - // Skip over any questions - for (uint16_t i =0; i < htons(*((uint16_t*)&header[4])); i++) - { - // Skip over the name - uint8_t len; - do - { - iUdp.read(&len, sizeof(len)); - if (len > 0) - { - // Don't need to actually read the data out for the string, just - // advance ptr to beyond it - while(len--) - { - iUdp.read(); // we don't care about the returned byte - } - } - } while (len != 0); - - // Now jump over the type and class - for (int i =0; i < 4; i++) - { - iUdp.read(); // we don't care about the returned byte - } - } - - // Now we're up to the bit we're interested in, the answer - // There might be more than one answer (although we'll just use the first - // type A answer) and some authority and additional resource records but - // we're going to ignore all of them. - - for (uint16_t i =0; i < answerCount; i++) - { - // Skip the name - uint8_t len; - do - { - iUdp.read(&len, sizeof(len)); - if ((len & LABEL_COMPRESSION_MASK) == 0) - { - // It's just a normal label - if (len > 0) - { - // And it's got a length - // Don't need to actually read the data out for the string, - // just advance ptr to beyond it - while(len--) - { - iUdp.read(); // we don't care about the returned byte - } - } - } - else - { - // This is a pointer to a somewhere else in the message for the - // rest of the name. We don't care about the name, and RFC1035 - // says that a name is either a sequence of labels ended with a - // 0 length octet or a pointer or a sequence of labels ending in - // a pointer. Either way, when we get here we're at the end of - // the name - // Skip over the pointer - iUdp.read(); // we don't care about the returned byte - // And set len so that we drop out of the name loop - len = 0; - } - } while (len != 0); - - // Check the type and class - uint16_t answerType; - uint16_t answerClass; - iUdp.read((uint8_t*)&answerType, sizeof(answerType)); - iUdp.read((uint8_t*)&answerClass, sizeof(answerClass)); - - // Ignore the Time-To-Live as we don't do any caching - for (int i =0; i < TTL_SIZE; i++) - { - iUdp.read(); // we don't care about the returned byte - } - - // And read out the length of this answer - // Don't need header_flags anymore, so we can reuse it here - iUdp.read((uint8_t*)&header_flags, sizeof(header_flags)); - - if ( (htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN) ) - { - if (htons(header_flags) != 4) - { - // It's a weird size - // Mark the entire packet as read - iUdp.discardReceived(); - return -9;//INVALID_RESPONSE; - } - iUdp.read(aAddress.raw_address(), 4); - return SUCCESS; - } - else - { - // This isn't an answer type we're after, move onto the next one - for (uint16_t i =0; i < htons(header_flags); i++) - { - iUdp.read(); // we don't care about the returned byte - } - } - } - - // Mark the entire packet as read - iUdp.discardReceived(); - - // If we get here then we haven't found an answer - return -10;//INVALID_RESPONSE; -} - diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/Dns.h b/ShineLAN-X/firmware/lib/EthernetENC/src/Dns.h deleted file mode 100644 index 81abc17..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/Dns.h +++ /dev/null @@ -1,41 +0,0 @@ -// Arduino DNS client for Enc28J60-based Ethernet shield -// (c) Copyright 2009-2010 MCQN Ltd. -// Released under Apache License, version 2.0 - -#ifndef DNSClient_h -#define DNSClient_h - -#include "EthernetUdp.h" - -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; - uint16_t iRequestId; - EthernetUDP iUdp; -}; - -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/Ethernet.cpp b/ShineLAN-X/firmware/lib/EthernetENC/src/Ethernet.cpp deleted file mode 100644 index df1c77f..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/Ethernet.cpp +++ /dev/null @@ -1,559 +0,0 @@ -/* - UIPEthernet.cpp - Arduino implementation of a uIP wrapper class. - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -#include -#include "Ethernet.h" -#include "Dns.h" -#include "utility/Enc28J60Network.h" - -extern "C" -{ -#include "utility/uip-conf.h" -#include "utility/uip.h" -#include "utility/uip_arp.h" -} - -#define ETH_HDR ((struct uip_eth_hdr *)&uip_buf[0]) - -bool UIPEthernetClass::initialized = false; -memhandle UIPEthernetClass::in_packet(NOBLOCK); -memhandle UIPEthernetClass::uip_packet(NOBLOCK); -uint8_t UIPEthernetClass::uip_hdrlen(0); -uint8_t UIPEthernetClass::packetstate(0); - -IPAddress UIPEthernetClass::_dnsServerAddress; -DhcpClass* UIPEthernetClass::_dhcp(NULL); - -unsigned long UIPEthernetClass::periodic_timer; -unsigned long UIPEthernetClass::arp_timer; - -// Because uIP isn't encapsulated within a class we have to use global -// variables, so we can only have one TCP/IP stack per program. - -UIPEthernetClass::UIPEthernetClass() -{ -} - -void UIPEthernetClass::init(uint8_t csPin) -{ - Enc28J60Network::setCsPin(csPin); -} - -#if UIP_UDP -void -UIPEthernetClass::setHostname(const char* hostname) -{ - if (_dhcp == NULL) { - _dhcp = new DhcpClass(); - } - _dhcp->setHostname(hostname); -} - -int -UIPEthernetClass::begin(const uint8_t* mac, unsigned long timeout, unsigned long responseTimeout) -{ - if (_dhcp == NULL) { - _dhcp = new DhcpClass(); - } - - // Initialise the basic info - init(mac); - - // Now try to get our config info from a DHCP server - int ret = _dhcp->beginWithDHCP((uint8_t*)mac, timeout, responseTimeout); - if(ret == 1) - { - // We've successfully found a DHCP server and got our configuration info, so set things - // accordingly - configure(_dhcp->getLocalIp(),_dhcp->getDnsServerIp(),_dhcp->getGatewayIp(),_dhcp->getSubnetMask()); - } - return ret; -} -#endif - -void -UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip) -{ - IPAddress dns = ip; - dns[3] = 1; - begin(mac, ip, dns); -} - -void -UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns) -{ - IPAddress gateway = ip; - gateway[3] = 1; - begin(mac, ip, dns, gateway); -} - -void -UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway) -{ - IPAddress subnet(255, 255, 255, 0); - begin(mac, ip, dns, gateway, subnet); -} - -void -UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) -{ - init(mac); - configure(ip,dns,gateway,subnet); -} - -void UIPEthernetClass::end() -{ - //close all clients - for (int i = 0; i < UIP_CONNS; i++) - { - if (EthernetClient::all_data[i].state) { - EthernetClient client(&EthernetClient::all_data[i]); - client.stop(); - } - } - // handle clients closings - uint32_t st = millis(); - while (millis() - st < 3000) - { - tick(); - } - initialized = false; - configure(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); -} - -int UIPEthernetClass::maintain(){ - if (!initialized) - return 0; - tick(); - int rc = DHCP_CHECK_NONE; -#if UIP_UDP - if(_dhcp != NULL){ - //we have a pointer to dhcp, use it - rc = _dhcp->checkLease(); - switch ( rc ){ - case DHCP_CHECK_NONE: - //nothing done - break; - case DHCP_CHECK_RENEW_OK: - case DHCP_CHECK_REBIND_OK: - //we might have got a new IP. - configure(_dhcp->getLocalIp(),_dhcp->getDnsServerIp(),_dhcp->getGatewayIp(),_dhcp->getSubnetMask()); - break; - default: - //this is actually a error, it will retry though - break; - } - } -#endif - return rc; -} - -EthernetLinkStatus UIPEthernetClass::linkStatus() -{ - if (!Enc28J60Network::getrev()) - return Unknown; - return Enc28J60Network::linkStatus() ? LinkON : LinkOFF; -} - -EthernetHardwareStatus UIPEthernetClass::hardwareStatus() -{ - if (!Enc28J60Network::getrev()) - return EthernetNoHardware; - return EthernetENC28J60; -} - -uint8_t* UIPEthernetClass::macAddress(uint8_t* mac) -{ - memcpy(mac, uip_ethaddr.addr, 6); - return mac; -} - -IPAddress UIPEthernetClass::localIP() -{ - IPAddress ret; - uip_ipaddr_t a; - uip_gethostaddr(a); - return ip_addr_uip(a); -} - -IPAddress UIPEthernetClass::subnetMask() -{ - IPAddress ret; - uip_ipaddr_t a; - uip_getnetmask(a); - return ip_addr_uip(a); -} - -IPAddress UIPEthernetClass::gatewayIP() -{ - IPAddress ret; - uip_ipaddr_t a; - uip_getdraddr(a); - return ip_addr_uip(a); -} - -IPAddress UIPEthernetClass::dnsServerIP() -{ - return _dnsServerAddress; -} - -IPAddress UIPEthernetClass::dnsIP(int n) { - return (n == 0) ? _dnsServerAddress : IPAddress(); -} - -int UIPEthernetClass::hostByName(const char* hostname, IPAddress& result) -{ - // Look up the host first - int ret = 0; -#if UIP_UDP - DNSClient dns; - dns.begin(_dnsServerAddress); - ret = dns.getHostByName(hostname, result); -#endif - return ret; -} - -void -UIPEthernetClass::tick() -{ - if (!initialized) - return; - - // run ARP table cleanup every 10 seconds as required by a comment in uip_arp.h - if (millis() - arp_timer > 10000) { - arp_timer = millis(); - uip_arp_timer(); - } - - if (in_packet == NOBLOCK) - { - in_packet = Enc28J60Network::receivePacket(); -#ifdef UIPETHERNET_DEBUG - if (in_packet != NOBLOCK) - { - Serial.println(F("--------------\npacket received")); - } -#endif - } - if (in_packet != NOBLOCK) - { - packetstate = UIPETHERNET_FREEPACKET; - uip_len = Enc28J60Network::blockSize(in_packet); - if (uip_len > 0) - { - Enc28J60Network::readPacket(in_packet,0,(uint8_t*)uip_buf,UIP_BUFSIZE); - if (ETH_HDR ->type == HTONS(UIP_ETHTYPE_IP)) - { - uip_packet = in_packet; //required for upper_layer_checksum of in_packet! -#ifdef UIPETHERNET_DEBUG - Serial.print(F("readPacket type IP, uip_len: ")); - Serial.print(uip_len); - Serial.print(F(", bits: ")); - Serial.println(BUF->flags, BIN); -#endif - uip_arp_ipin(); - uip_input(); - if (uip_len > 0) - { - uip_arp_out(); - network_send(); - } - } - else if (ETH_HDR ->type == HTONS(UIP_ETHTYPE_ARP)) - { -#ifdef UIPETHERNET_DEBUG - Serial.print(F("readPacket type ARP, uip_len: ")); - Serial.println(uip_len); -#endif - uip_arp_arpin(); - if (uip_len > 0) - { - network_send(); - } - } - } - if (in_packet != NOBLOCK && (packetstate & UIPETHERNET_FREEPACKET)) - { -#ifdef UIPETHERNET_DEBUG - Serial.println(F("freeing received packet")); -#endif - Enc28J60Network::freePacket(); - in_packet = NOBLOCK; - } - } - - unsigned long now = millis(); - - if ((long)( now - periodic_timer ) >= 0) - { - periodic_timer = now + UIP_PERIODIC_TIMER; - - for (int i = 0; i < UIP_CONNS; i++) - { - uip_periodic(i); - // If the above function invocation resulted in data that - // should be sent out on the Enc28J60Network, the global variable - // uip_len is set to a value > 0. - if (uip_len > 0) - { - uip_arp_out(); - network_send(); - } - } -#if UIP_UDP - for (int i = 0; i < UIP_UDP_CONNS; i++) - { - uip_udp_periodic(i); - // If the above function invocation resulted in data that - // should be sent out on the Enc28J60Network, the global variable - // uip_len is set to a value > 0. */ - if (uip_len > 0) - { - EthernetUDP::_send(&uip_udp_conns[i]); - } - } -#endif /* UIP_UDP */ - } -} - -boolean UIPEthernetClass::network_send() -{ - if (packetstate & UIPETHERNET_SENDPACKET) - { -#ifdef UIPETHERNET_DEBUG - Serial.print(F("Enc28J60Network_send uip_packet: ")); - Serial.print(uip_packet); - Serial.print(F(", hdrlen: ")); - Serial.println(uip_hdrlen); -#endif - Enc28J60Network::writePacket(uip_packet, UIP_SENDBUFFER_OFFSET,uip_buf,uip_hdrlen); - packetstate &= ~ UIPETHERNET_SENDPACKET; - goto sendandfree; - } - uip_packet = Enc28J60Network::allocBlock(uip_len + UIP_SENDBUFFER_OFFSET + UIP_SENDBUFFER_PADDING); - if (uip_packet != NOBLOCK) - { -#ifdef UIPETHERNET_DEBUG - Serial.print(F("Enc28J60Network_send uip_buf (uip_len): ")); - Serial.print(uip_len); - Serial.print(F(", packet: ")); - Serial.print(uip_packet); - Serial.print(F(", bits: ")); - Serial.println(BUF->flags, BIN); -#endif - Enc28J60Network::writePacket(uip_packet, UIP_SENDBUFFER_OFFSET,uip_buf,uip_len); - goto sendandfree; - } - return false; -sendandfree: - bool success = Enc28J60Network::sendPacket(uip_packet); - Enc28J60Network::freeBlock(uip_packet); - uip_packet = NOBLOCK; - return success; -} - -void UIPEthernetClass::init(const uint8_t* mac) { - periodic_timer = millis() + UIP_PERIODIC_TIMER; - - initialized = Enc28J60Network::init((uint8_t*)mac); - uip_seteth_addr(mac); - - uip_init(); - uip_arp_init(); -} - -void UIPEthernetClass::configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) { - uip_ipaddr_t ipaddr; - - uip_ip_addr(ipaddr, ip); - uip_sethostaddr(ipaddr); - - uip_ip_addr(ipaddr, gateway); - uip_setdraddr(ipaddr); - - uip_ip_addr(ipaddr, subnet); - uip_setnetmask(ipaddr); - - _dnsServerAddress = dns; -} - -void -UIPEthernetClass::call_yield() -{ - static bool in_yield; - static uint32_t last_call_millis; - if (!in_yield && millis() - last_call_millis > 0) - { - last_call_millis = millis(); - in_yield = true; - yield(); - in_yield = false; - } -} - -/*---------------------------------------------------------------------------*/ -uint16_t -UIPEthernetClass::chksum(uint16_t sum, const uint8_t *data, uint16_t len) -{ - uint16_t t; - const uint8_t *dataptr; - const uint8_t *last_byte; - - dataptr = data; - last_byte = data + len - 1; - - while(dataptr < last_byte) { /* At least two more bytes */ - t = (dataptr[0] << 8) + dataptr[1]; - sum += t; - if(sum < t) { - sum++; /* carry */ - } - dataptr += 2; - } - - if(dataptr == last_byte) { - t = (dataptr[0] << 8) + 0; - sum += t; - if(sum < t) { - sum++; /* carry */ - } - } - - /* Return sum in host byte order. */ - return sum; -} - -/*---------------------------------------------------------------------------*/ - -uint16_t -UIPEthernetClass::ipchksum(void) -{ - uint16_t sum; - - sum = chksum(0, &uip_buf[UIP_LLH_LEN], UIP_IPH_LEN); - return (sum == 0) ? 0xffff : htons(sum); -} - -/*---------------------------------------------------------------------------*/ -uint16_t -#if UIP_UDP -UIPEthernetClass::upper_layer_chksum(uint8_t proto) -#else -uip_tcpchksum(void) -#endif -{ - uint16_t upper_layer_len; - uint16_t sum; - -#if UIP_CONF_IPV6 - upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]); -#else /* UIP_CONF_IPV6 */ - upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]) - UIP_IPH_LEN; -#endif /* UIP_CONF_IPV6 */ - - /* First sum pseudoheader. */ - - /* IP protocol and length fields. This addition cannot carry. */ -#if UIP_UDP - sum = upper_layer_len + proto; -#else - sum = upper_layer_len + UIP_PROTO_TCP; -#endif - /* Sum IP source and destination addresses. */ - sum = UIPEthernetClass::chksum(sum, (u8_t *)&BUF->srcipaddr[0], 2 * sizeof(uip_ipaddr_t)); - - uint8_t upper_layer_memlen; -#if UIP_UDP - switch(proto) - { -// case UIP_PROTO_ICMP: -// case UIP_PROTO_ICMP6: -// upper_layer_memlen = upper_layer_len; -// break; - case UIP_PROTO_UDP: - upper_layer_memlen = UIP_UDPH_LEN; - break; - default: -// case UIP_PROTO_TCP: -#endif - upper_layer_memlen = (BUF->tcpoffset >> 4) << 2; -#if UIP_UDP - break; - } -#endif - sum = UIPEthernetClass::chksum(sum, &uip_buf[UIP_IPH_LEN + UIP_LLH_LEN], upper_layer_memlen); -#ifdef UIPETHERNET_DEBUG_CHKSUM - Serial.print(F("chksum uip_buf[")); - Serial.print(UIP_IPH_LEN + UIP_LLH_LEN); - Serial.print(F("-")); - Serial.print(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen); - Serial.print(F("]: ")); - Serial.println(htons(sum),HEX); -#endif - if (upper_layer_memlen < upper_layer_len) - { - sum = Enc28J60Network::chksum( - sum, - UIPEthernetClass::uip_packet, - (UIPEthernetClass::packetstate & UIPETHERNET_SENDPACKET ? UIP_IPH_LEN + UIP_LLH_LEN + UIP_SENDBUFFER_OFFSET : UIP_IPH_LEN + UIP_LLH_LEN) + upper_layer_memlen, - upper_layer_len - upper_layer_memlen - ); -#ifdef UIPETHERNET_DEBUG_CHKSUM - Serial.print(F("chksum uip_packet(")); - Serial.print(uip_packet); - Serial.print(F(")[")); - Serial.print(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen); - Serial.print(F("-")); - Serial.print(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_len); - Serial.print(F("]: ")); - Serial.println(htons(sum),HEX); -#endif - } - return (sum == 0) ? 0xffff : htons(sum); -} - -uint16_t -uip_ipchksum(void) -{ - return UIPEthernetClass::ipchksum(); -} - -#if UIP_UDP -uint16_t -uip_tcpchksum(void) -{ - uint16_t sum = UIPEthernetClass::upper_layer_chksum(UIP_PROTO_TCP); - return sum; -} - -uint16_t -uip_udpchksum(void) -{ - uint16_t sum = UIPEthernetClass::upper_layer_chksum(UIP_PROTO_UDP); - return sum; -} -#endif - -UIPEthernetClass Ethernet; - -extern "C" void serialPrint(int i); -void serialPrint(int i) { - Serial.println(i); - Serial.flush(); -} diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/Ethernet.h b/ShineLAN-X/firmware/lib/EthernetENC/src/Ethernet.h deleted file mode 100644 index 3f9ddbf..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/Ethernet.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - UIPEthernet.h - Arduino implementation of a uIP wrapper class. - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -#ifndef UIPETHERNET_H -#define UIPETHERNET_H - -//#define UIPETHERNET_DEBUG -//#define UIPETHERNET_DEBUG_CHKSUM -//#define UIPETHERNET_DEBUG_UDP -//#define UIPETHERNET_DEBUG_CLIENT - -#include -#include "Dhcp.h" -#include -#include "utility/Enc28J60Network.h" -#include "EthernetClient.h" -#include "EthernetServer.h" -#include "EthernetUdp.h" - -extern "C" -{ -#include "utility/uip.h" -} - -#define UIPETHERNET_FREEPACKET 1 -#define UIPETHERNET_SENDPACKET 2 -#define UIPETHERNET_BUFFERREAD 4 - -#define uip_ip_addr(addr, ip) do { \ - ((u16_t *)(addr))[0] = HTONS(((ip[0]) << 8) | (ip[1])); \ - ((u16_t *)(addr))[1] = HTONS(((ip[2]) << 8) | (ip[3])); \ - } while(0) - -#define ip_addr_uip(a) IPAddress(a[0] & 0xFF, a[0] >> 8 , a[1] & 0xFF, a[1] >> 8) //TODO this is not IPV6 capable - -#define uip_seteth_addr(eaddr) do {uip_ethaddr.addr[0] = eaddr[0]; \ - uip_ethaddr.addr[1] = eaddr[1];\ - uip_ethaddr.addr[2] = eaddr[2];\ - uip_ethaddr.addr[3] = eaddr[3];\ - uip_ethaddr.addr[4] = eaddr[4];\ - uip_ethaddr.addr[5] = eaddr[5];} while(0) - -#define BUF ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN]) - -enum EthernetLinkStatus { - Unknown, - LinkON, - LinkOFF -}; - -enum EthernetHardwareStatus { - EthernetNoHardware, - EthernetW5100, - EthernetW5200, - EthernetW5500, - EthernetENC28J60 = 10 -}; - -class UIPEthernetClass -{ -public: - UIPEthernetClass(); - - void init(uint8_t csPin); - - int begin(const uint8_t* mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); - void begin(const uint8_t* mac, IPAddress ip); - void begin(const uint8_t* mac, IPAddress ip, IPAddress dns); - void begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway); - void begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); - - void end(); - - // maintain() must be called at regular intervals to process the incoming serial - // data and issue IP events to the sketch. It does not return until all IP - // events have been processed. Renews dhcp-lease if required. - int maintain(); - - EthernetLinkStatus linkStatus(); - EthernetHardwareStatus hardwareStatus(); - - uint8_t* macAddress(uint8_t* mac); - void MACAddress(uint8_t *mac_address) { macAddress(mac_address); } - - IPAddress localIP(); - IPAddress subnetMask(); - IPAddress gatewayIP(); - IPAddress dnsServerIP(); - IPAddress dnsIP(int n = 0); - - void setDnsServerIP(const IPAddress dns_server) { _dnsServerAddress = dns_server; } - void setDNS(IPAddress dns_server) { _dnsServerAddress = dns_server; } - - void setHostname(const char* hostname); // only the pointer is stored! - - int hostByName(const char* hostname, IPAddress& result); - -private: - static bool initialized; - static memhandle in_packet; - static memhandle uip_packet; - static uint8_t uip_hdrlen; - static uint8_t packetstate; - - static IPAddress _dnsServerAddress; - static DhcpClass* _dhcp; - - static unsigned long periodic_timer; - static unsigned long arp_timer; - - static void init(const uint8_t* mac); - static void configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); - - static void tick(); - - static boolean network_send(); - - static void call_yield(); - - friend class EthernetServer; - - friend class EthernetClient; - - friend class EthernetUDP; - - static uint16_t chksum(uint16_t sum, const uint8_t* data, uint16_t len); - static uint16_t ipchksum(void); -#if UIP_UDP - static uint16_t upper_layer_chksum(uint8_t proto); -#endif - friend uint16_t uip_ipchksum(void); - friend uint16_t uip_tcpchksum(void); - friend uint16_t uip_udpchksum(void); - - friend void uipclient_appcall(void); - friend void uipudp_appcall(void); - -#if UIP_CONF_IPV6 - uint16_t uip_icmp6chksum(void); -#endif /* UIP_CONF_IPV6 */ -}; - -extern UIPEthernetClass Ethernet; - -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetClient.cpp b/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetClient.cpp deleted file mode 100644 index 636bc1d..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetClient.cpp +++ /dev/null @@ -1,663 +0,0 @@ -/* - UIPClient.cpp - Arduino implementation of a uIP wrapper class. - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -extern "C" -{ -#include "utility/uip-conf.h" -#include "utility/uip.h" -#include "utility/uip_arp.h" -} -#include "Ethernet.h" -#include "EthernetClient.h" - -#define UIP_TCP_PHYH_LEN UIP_LLH_LEN+UIP_IPTCPH_LEN - -#define UIPClient EthernetClient // to not pollute source code history with the rename - -uip_userdata_t UIPClient::all_data[UIP_CONNS]; - -UIPClient::UIPClient() : - data(NULL) -{ -} - -UIPClient::UIPClient(uip_userdata_t* conn_data) : - data(conn_data) -{ -} - -int -UIPClient::connect(IPAddress ip, uint16_t port) -{ - stop(); - uip_ipaddr_t ipaddr; - uip_ip_addr(ipaddr, ip); - struct uip_conn* conn = uip_connect(&ipaddr, htons(port)); - if (conn) - { -#if UIP_CONNECT_TIMEOUT > 0 - uint32_t timeout = millis() + connectTimeout; -#endif - while((conn->tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) - { - UIPEthernetClass::tick(); - if ((conn->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) - { - data = (uip_userdata_t*) conn->appstate; -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.print(F("connected, state: ")); - Serial.print(data->state); - Serial.print(F(", first packet in: ")); - Serial.println(data->packets_in[0]); -#endif - return 1; - } -#if UIP_CONNECT_TIMEOUT > 0 - if (((int32_t)(millis() - timeout)) > 0) - { - conn->tcpstateflags = UIP_CLOSED; - break; - } -#endif - } - } - return 0; -} - -int -UIPClient::connect(const char *host, uint16_t port) -{ - // Look up the host first - int ret = 0; -#if UIP_UDP - IPAddress remote_addr; - ret = Ethernet.hostByName(host, remote_addr); - if (ret == 1) { - return connect(remote_addr, port); - } -#endif - return ret; -} - -void -UIPClient::stop() -{ - if (data && data->state) - { -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.println(F("before stop(), with data")); - _dumpAllData(); -#endif - if (data->state & UIP_CLIENT_REMOTECLOSED) - { - data->state = 0; - } - else - { - flush(); - data->state |= UIP_CLIENT_CLOSE; - } - _flushBlocks(data->packets_in); -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.println(F("after stop()")); - _dumpAllData(); -#endif - } -#ifdef UIPETHERNET_DEBUG_CLIENT - else - { - Serial.println(F("stop(), data: NULL")); - } -#endif - data = NULL; - UIPEthernetClass::tick(); -} - -uint8_t -UIPClient::connected() -{ - return (data && (data->packets_in[0] != NOBLOCK || (data->state & UIP_CLIENT_CONNECTED))) ? 1 : 0; -} - -bool -UIPClient::operator==(const UIPClient& rhs) -{ - return data && rhs.data && (data == rhs.data); -} - -UIPClient::operator bool() -{ - UIPEthernetClass::tick(); - return data && (!(data->state & UIP_CLIENT_REMOTECLOSED) || data->packets_in[0] != NOBLOCK); -} - -size_t -UIPClient::write(uint8_t c) -{ - return write(&c, 1); -} - -size_t -UIPClient::write(const uint8_t *buf, size_t size) -{ - uip_userdata_t* u = data; - int remain = size; - uint16_t written; -#if UIP_WRITE_TIMEOUT > 0 - uint32_t timeout_start = millis(); -#endif - repeat: - UIPEthernetClass::tick(); - if (u && u->state && !(u->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))) - { - uint8_t p = _currentBlock(u->packets_out); - if (u->packets_out[p] == NOBLOCK) - { -newpacket: - u->packets_out[p] = Enc28J60Network::allocBlock(UIP_SOCKET_DATALEN); - if (u->packets_out[p] == NOBLOCK) - { -#if UIP_WRITE_TIMEOUT > 0 - if (millis() - timeout_start > UIP_WRITE_TIMEOUT) - { - setWriteError(); - goto ready; - } -#endif - Ethernet.call_yield(); - goto repeat; - } - u->out_pos = 0; - } -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.print(F("UIPClient.write: writePacket(")); - Serial.print(u->packets_out[p]); - Serial.print(F(") pos: ")); - Serial.print(u->out_pos); - Serial.print(F(", buf[")); - Serial.print(size-remain); - Serial.print(F("-")); - Serial.print(remain); - Serial.print(F("]: '")); - Serial.write((uint8_t*)buf+size-remain,remain); - Serial.println(F("'")); -#endif - written = Enc28J60Network::writePacket(u->packets_out[p],u->out_pos,(uint8_t*)buf+size-remain,remain); - remain -= written; - u->out_pos+=written; - if (remain > 0) - { - if (p == 0) // block 0 just filled, start sending immediately - { - flush(); - goto repeat; - } - if (p == UIP_SOCKET_NUMPACKETS-1) - { -#if UIP_WRITE_TIMEOUT > 0 - if (millis() - timeout_start > UIP_WRITE_TIMEOUT) - { - setWriteError(); - goto ready; - } -#endif - Ethernet.call_yield(); - goto repeat; - } - p++; - goto newpacket; - } -ready: - return size-remain; - } - return 0; -} - -int -UIPClient::availableForWrite() -{ - const int MAX_AVAILABLE = UIP_SOCKET_DATALEN * UIP_SOCKET_NUMPACKETS; - UIPEthernetClass::tick(); - if (data->packets_out[0] == NOBLOCK) - return MAX_AVAILABLE; - uint8_t p = _currentBlock(data->packets_out); - int used = UIP_SOCKET_DATALEN * p + data->out_pos; - return MAX_AVAILABLE - used; -} - -void -UIPClient::flush() -{ - UIPEthernetClass::tick(); - - if (data && data->packets_out[0] != NOBLOCK) - { - struct uip_conn* conn = &uip_conns[data->conn_index]; - if (!uip_outstanding(conn)) - { - uip_poll_conn(conn); - if (uip_len > 0) - { - uip_arp_out(); - UIPEthernetClass::network_send(); - } - } - } -} - -int -UIPClient::available() -{ - if (!(*this)) - return 0; - - int len = 0; - for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) - { - len += Enc28J60Network::blockSize(data->packets_in[i]); - } - - // if sketch checks for incoming data and there are unsent data, flush the transmit buffer - if (!len) - flush(); - return len; -} - -int -UIPClient::read(uint8_t *buf, size_t size) -{ - if (*this) - { - uint16_t remain = size; - if (data->packets_in[0] == NOBLOCK) - { - flush(); // if sketch checks for incoming data and there are unsent data, flush the transmit buffer - return 0; - } - uint16_t read; - do - { - read = Enc28J60Network::readPacket(data->packets_in[0],0,buf+size-remain,remain); - if (read == Enc28J60Network::blockSize(data->packets_in[0])) - { - remain -= read; - _eatBlock(data->packets_in); - if (uip_stopped(&uip_conns[data->conn_index]) && !(data->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))) - data->state |= UIP_CLIENT_RESTART; - if (data->packets_in[0] == NOBLOCK) - { - if (data->state & UIP_CLIENT_REMOTECLOSED) - { - data->state = 0; - data = NULL; - } - return size-remain; - } - } - else - { - Enc28J60Network::resizeBlock(data->packets_in[0],read); - break; - } - } - while(remain > 0); - return size; - } - return -1; -} - -int -UIPClient::read() -{ - uint8_t c; - if (read(&c,1) <= 0) - return -1; - return c; -} - -int -UIPClient::peek() -{ - if (*this) - { - if (data->packets_in[0] != NOBLOCK) - { - uint8_t c; - Enc28J60Network::readPacket(data->packets_in[0],0,&c,1); - return c; - } - } - return -1; -} - -void -UIPClient::discardReceived() -{ - if (*this) - { - _flushBlocks(data->packets_in); - } -} - -IPAddress -UIPClient::remoteIP(void) -{ - return data ? ip_addr_uip(uip_conns[data->conn_index].ripaddr) : IPAddress(); -} - -uint16_t -UIPClient::remotePort(void) -{ - return data ? ntohs(uip_conns[data->conn_index].rport) : 0; -} - -uint8_t -UIPClient::status() -{ - return !data ? UIP_CLOSED : uip_conns[data->conn_index].tcpstateflags & UIP_TS_MASK; -} - -void -uipclient_appcall(void) -{ - uint16_t send_len = 0; - uip_userdata_t *u = (uip_userdata_t*)uip_conn->appstate; - if (!u && uip_connected()) - { -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.println(F("UIPClient uip_connected")); - UIPClient::_dumpAllData(); -#endif - u = (uip_userdata_t*) UIPClient::_allocateData(); - if (u) - { - uip_conn->appstate = u; -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.print(F("UIPClient allocated state: ")); - Serial.println(u->state,BIN); -#endif - } -#ifdef UIPETHERNET_DEBUG_CLIENT - else - Serial.println(F("UIPClient allocation failed")); -#endif - } - if (u) - { - if (uip_newdata()) - { -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.print(F("UIPClient uip_newdata, uip_len:")); - Serial.println(uip_len); -#endif - if (uip_len && u->state && !(u->state & UIP_CLIENT_CLOSE)) - { - for (uint8_t i=0; i < UIP_SOCKET_NUMPACKETS; i++) - { - if (u->packets_in[i] == NOBLOCK) - { - u->packets_in[i] = Enc28J60Network::allocBlock(uip_len); - if (u->packets_in[i] != NOBLOCK) - { - Enc28J60Network::copyPacket(u->packets_in[i],0,UIPEthernetClass::in_packet,((uint8_t*)uip_appdata)-uip_buf,uip_len); - if (i == UIP_SOCKET_NUMPACKETS-1) - uip_stop(); - goto finish_newdata; - } - } - } - UIPEthernetClass::packetstate &= ~UIPETHERNET_FREEPACKET; - uip_stop(); - } - } -finish_newdata: - if (u->state & UIP_CLIENT_RESTART) - { - u->state &= ~UIP_CLIENT_RESTART; - uip_restart(); - } - // If the connection has been closed, save received but unread data. - if (uip_closed() || uip_timedout() || uip_aborted()) - { -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.println(F("UIPClient uip_closed")); - UIPClient::_dumpAllData(); -#endif - // drop outgoing packets not sent yet: - UIPClient::_flushBlocks(u->packets_out); - if (u->packets_in[0] != NOBLOCK) - { - ((uip_userdata_closed_t *)u)->lport = uip_conn->lport; - u->state |= UIP_CLIENT_REMOTECLOSED; - } - else - u->state = 0; - // disassociate appdata. -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.println(F("after UIPClient uip_closed")); - UIPClient::_dumpAllData(); -#endif - uip_conn->appstate = NULL; - goto finish; - } - if (uip_acked()) - { -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.println(F("UIPClient uip_acked")); -#endif - UIPClient::_eatBlock(u->packets_out); - goto send; - } - if (uip_poll() || uip_rexmit()) - { -#ifdef UIPETHERNET_DEBUG_CLIENT - //Serial.println(F("UIPClient uip_poll")); -#endif -send: - if (u->packets_out[0] != NOBLOCK) - { - if (u->packets_out[1] == NOBLOCK) - { - send_len = u->out_pos; - if (send_len > 0) - { - Enc28J60Network::resizeBlock(u->packets_out[0],0,send_len); - } - } - else - send_len = Enc28J60Network::blockSize(u->packets_out[0]); - if (send_len > 0) - { - UIPEthernetClass::uip_hdrlen = ((uint8_t*)uip_appdata)-uip_buf; - UIPEthernetClass::uip_packet = Enc28J60Network::allocBlock(UIPEthernetClass::uip_hdrlen+send_len + UIP_SENDBUFFER_OFFSET + UIP_SENDBUFFER_PADDING); - if (UIPEthernetClass::uip_packet != NOBLOCK) - { - Enc28J60Network::copyPacket(UIPEthernetClass::uip_packet,UIPEthernetClass::uip_hdrlen + UIP_SENDBUFFER_OFFSET,u->packets_out[0],0,send_len); - UIPEthernetClass::packetstate |= UIPETHERNET_SENDPACKET; - } - } - goto finish; - } - } - // don't close connection unless all outgoing packets are sent - if (u->state & UIP_CLIENT_CLOSE) - { -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.println(F("UIPClient state UIP_CLIENT_CLOSE")); - UIPClient::_dumpAllData(); -#endif - if (u->packets_out[0] == NOBLOCK) - { - u->state = 0; - uip_conn->appstate = NULL; - uip_close(); -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.println(F("no blocks out -> free userdata")); - UIPClient::_dumpAllData(); -#endif - } - else - { - uip_stop(); -#ifdef UIPETHERNET_DEBUG_CLIENT - Serial.println(F("blocks outstanding transfer -> uip_stop()")); -#endif - } - } - } - finish: - uip_send(uip_appdata,send_len); - uip_len = send_len; -} - -uip_userdata_t * -UIPClient::_allocateData() -{ - for ( uint8_t sock = 0; sock < UIP_CONNS; sock++ ) - { - uip_userdata_t* data = &UIPClient::all_data[sock]; - if (!data->state) - { - *data = uip_userdata_t(); - data->conn_index = uip_conn - uip_conns; // pointer arithmetics - data->state = UIP_CLIENT_CONNECTED; - return data; - } - } - return NULL; -} - -uint8_t -UIPClient::_currentBlock(memhandle* block) -{ - for (uint8_t i = 1; i < UIP_SOCKET_NUMPACKETS; i++) - { - if (block[i] == NOBLOCK) - return i-1; - } - return UIP_SOCKET_NUMPACKETS-1; -} - -void -UIPClient::_eatBlock(memhandle* block) -{ -#ifdef UIPETHERNET_DEBUG_CLIENT - memhandle* start = block; - Serial.print(F("eatblock(")); - Serial.print(*block); - Serial.print(F("): ")); - for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) - { - Serial.print(start[i]); - Serial.print(F(" ")); - } - Serial.print(F("-> ")); -#endif - Enc28J60Network::freeBlock(block[0]); - for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS-1; i++) - { - block[i] = block[i+1]; - } - block[UIP_SOCKET_NUMPACKETS-1] = NOBLOCK; -#ifdef UIPETHERNET_DEBUG_CLIENT - for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) - { - Serial.print(start[i]); - Serial.print(F(" ")); - } - Serial.println(); -#endif -} - -void -UIPClient::_flushBlocks(memhandle* block) -{ - for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) - { - Enc28J60Network::freeBlock(block[i]); - block[i] = NOBLOCK; - } -} - -#ifdef UIPETHERNET_DEBUG_CLIENT -void -UIPClient::_dumpAllData() { - for (uint8_t i=0; i < UIP_CONNS; i++) - { - if (!all_data[i].state) - continue; - Serial.print(F("UIPClient::all_data[")); - Serial.print(i); - Serial.print(F("], state:")); - Serial.println(all_data[i].state, BIN); - struct uip_conn& conn = uip_conns[all_data[i].conn_index]; - Serial.println(ip_addr_uip(conn.ripaddr)); - Serial.print(F("ix: ")); - Serial.print(all_data[i].conn_index); - Serial.print(F(" tcp flags: 0x")); - Serial.print(conn.tcpstateflags, HEX); - Serial.print(F(" retransmission: timer ")); - Serial.print(conn.timer); - Serial.print(F(" nrtx ")); - Serial.println(conn.nrtx); - Serial.print(F("packets_in: ")); - for (uint8_t j=0; j < UIP_SOCKET_NUMPACKETS; j++) - { - Serial.print(all_data[i].packets_in[j]); - Serial.print(F(" ")); - } - Serial.println(); - if (all_data[i].state & UIP_CLIENT_REMOTECLOSED) - { - Serial.print(F("state remote closed, local port: ")); - Serial.println(htons(((uip_userdata_closed_t *)(&all_data[i]))->lport)); - } - else - { - Serial.print(F("packets_out: ")); - for (uint8_t j=0; j < UIP_SOCKET_NUMPACKETS; j++) - { - Serial.print(all_data[i].packets_out[j]); - Serial.print(F(" ")); - } - Serial.println(); - Serial.print(F("out_pos: ")); - Serial.println(all_data[i].out_pos); - } - Serial.println(); - } - for (uint8_t i=0; i < UIP_CONNS; i++) - { - struct uip_conn& conn = uip_conns[i]; - Serial.print(i); - Serial.print(' '); - Serial.print(ip_addr_uip(conn.ripaddr)); - Serial.print(':'); - Serial.print(ntohs(conn.rport)); - Serial.print(' '); - Serial.print(ntohs(conn.lport)); - Serial.print(F(" tcp flags: 0x")); - Serial.print(conn.tcpstateflags, HEX); - Serial.print(F(" retransmission: timer ")); - Serial.print(conn.timer); - Serial.print(F(" nrtx ")); - Serial.println(conn.nrtx); - } -} -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetClient.h b/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetClient.h deleted file mode 100644 index 8807ece..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetClient.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - UIPClient.h - Arduino implementation of a uIP wrapper class. - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -#ifndef UIPCLIENT_H -#define UIPCLIENT_H - -#include -#include "utility/mempool.h" - -extern "C" { - #include "utility/uip.h" -} - -#define UIP_SOCKET_DATALEN UIP_TCP_MSS - -#define UIP_CLIENT_CONNECTED 0x01 -#define UIP_CLIENT_CLOSE 0x02 -#define UIP_CLIENT_REMOTECLOSED 0x04 -#define UIP_CLIENT_RESTART 0x08 -#define UIP_CLIENT_ACCEPTED 0x10 - -typedef struct { - uint8_t conn_index; - uint8_t state; - memhandle packets_in[UIP_SOCKET_NUMPACKETS]; - uint16_t lport; /**< The local TCP port, in network byte order. */ -} uip_userdata_closed_t; - -typedef struct { - uint8_t conn_index; - uint8_t state; - memhandle packets_in[UIP_SOCKET_NUMPACKETS] = {NOBLOCK}; - memhandle packets_out[UIP_SOCKET_NUMPACKETS] = {NOBLOCK}; - memaddress out_pos; -} uip_userdata_t; - -class EthernetClient : public Client { - -public: - EthernetClient(); - int connect(IPAddress ip, uint16_t port); - int connect(const char *host, uint16_t port); - int read(uint8_t *buf, size_t size); - void stop(); - uint8_t connected(); - operator bool(); - virtual bool operator==(const EthernetClient&); - virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; - - size_t write(uint8_t); - size_t write(const uint8_t *buf, size_t size); - int availableForWrite(); - void flush(); // flush sends the buffered data - - int available(); - int read(); - int peek(); - void discardReceived(); - - using Print::write; - - IPAddress remoteIP(); - uint16_t remotePort(); - - uint8_t status(); - - void setConnectionTimeout(uint16_t millis) {connectTimeout = millis;} - -private: - EthernetClient(struct uip_conn *_conn); - EthernetClient(uip_userdata_t* conn_data); - - uip_userdata_t* data; - uint16_t connectTimeout = 1000 * UIP_CONNECT_TIMEOUT; - - static uip_userdata_t all_data[UIP_CONNS]; - static uip_userdata_t* _allocateData(); - - static uint8_t _currentBlock(memhandle* blocks); - static void _eatBlock(memhandle* blocks); - static void _flushBlocks(memhandle* blocks); - -#ifdef UIPETHERNET_DEBUG_CLIENT - static void _dumpAllData(); -#endif - - friend class UIPEthernetClass; - friend class EthernetServer; - - friend void uipclient_appcall(void); - -}; - -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetENC.h b/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetENC.h deleted file mode 100644 index 01e5d5b..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetENC.h +++ /dev/null @@ -1 +0,0 @@ -#include "Ethernet.h" diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetServer.cpp b/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetServer.cpp deleted file mode 100644 index 9237ad9..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetServer.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - UIPServer.cpp - Arduino implementation of a uIP wrapper class. - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ -#include "Ethernet.h" -#include "EthernetServer.h" -extern "C" { - #include "utility/uip-conf.h" -} - -#define UIPServer EthernetServer // to not pollute source code history with the rename -#define UIPClient EthernetClient - -UIPServer::UIPServer(uint16_t port) : _port(htons(port)) -{ -} - -UIPClient UIPServer::available() -{ - UIPEthernetClass::tick(); - for ( uip_userdata_t* data = &UIPClient::all_data[0]; data < &UIPClient::all_data[UIP_CONNS]; data++ ) - { - if (data->packets_in[0] != NOBLOCK - && (((data->state & UIP_CLIENT_CONNECTED) && uip_conns[data->conn_index].lport ==_port) - || ((data->state & UIP_CLIENT_REMOTECLOSED) && ((uip_userdata_closed_t *)data)->lport == _port))) - return UIPClient(data); - } - return UIPClient(); -} - -UIPClient UIPServer::accept() -{ - UIPEthernetClass::tick(); - for ( uip_userdata_t* data = &UIPClient::all_data[0]; data < &UIPClient::all_data[UIP_CONNS]; data++ ) - { - if (!(data->state & UIP_CLIENT_ACCEPTED) - && (((data->state & UIP_CLIENT_CONNECTED) && uip_conns[data->conn_index].lport ==_port) - || ((data->state & UIP_CLIENT_REMOTECLOSED) && ((uip_userdata_closed_t *)data)->lport == _port))) { - data->state |= UIP_CLIENT_ACCEPTED; - return UIPClient(data); - } - } - return UIPClient(); -} - -void UIPServer::begin() -{ - uip_listen(_port); - UIPEthernetClass::tick(); - listening = true; -} - -void UIPServer::begin(uint16_t port) -{ - _port = port; - begin(); -} - -void UIPServer::end() { - uip_unlisten(_port); - listening = false; - for ( uip_userdata_t* data = &UIPClient::all_data[0]; data < &UIPClient::all_data[UIP_CONNS]; data++ ) - { - if ((data->state & UIP_CLIENT_CONNECTED) && uip_conns[data->conn_index].lport ==_port) - { - UIPClient client(data); - client.stop(); - } - } -} - -UIPServer::operator bool() { - return listening; -} - -size_t UIPServer::writeToAllClients(const uint8_t *buf, size_t size) -{ - size_t ret = 0; - for ( uip_userdata_t* data = &UIPClient::all_data[0]; data < &UIPClient::all_data[UIP_CONNS]; data++ ) - { - if ((data->state & UIP_CLIENT_CONNECTED) && uip_conns[data->conn_index].lport ==_port) - { - EthernetClient client(data); - ret = client.write(buf, size); - } - } - return ret; -} - -size_t EthernetServerPrint::write(uint8_t c) -{ - return write(&c,1); -} - -size_t EthernetServerPrint::write(const uint8_t *buf, size_t size) -{ - return EthernetServer::writeToAllClients(buf, size); -} diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetServer.h b/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetServer.h deleted file mode 100644 index fec7006..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetServer.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - UIPServer.h - Arduino implementation of a uIP wrapper class. - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ -#ifndef UIPSERVER_H -#define UIPSERVER_H - -#include "EthernetClient.h" - -class EthernetServer { - -public: - EthernetServer(uint16_t port = 80); - EthernetClient available(); - EthernetClient accept(); - void begin(); - void begin(uint16_t port); - void end(); - operator bool(); - -protected: - size_t writeToAllClients(const uint8_t *buf, size_t size); - -private: - uint16_t _port; - bool listening = false; -}; - -class EthernetServerPrint : public EthernetServer, public Print { - -public: - EthernetServerPrint(uint16_t port = 80) : EthernetServer(port) {} - - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - - using Print::write; - -}; - -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetUdp.cpp b/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetUdp.cpp deleted file mode 100644 index 979037b..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetUdp.cpp +++ /dev/null @@ -1,424 +0,0 @@ -/* - UIPUdp.cpp - Arduino implementation of a uIP wrapper class. - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -#include "Ethernet.h" -#include "EthernetUdp.h" - -extern "C" { -#include "utility/uip-conf.h" -#include "utility/uip.h" -#include "utility/uip_arp.h" -} - -#if UIP_UDP -#define UIP_ARPHDRSIZE 42 -#define UDPBUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN]) - -#define UIPUDP EthernetUDP // to not pollute source code history with the rename - -// Constructor -UIPUDP::UIPUDP() : - _uip_udp_conn(NULL), - appdata() -{ -} - -// initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use -uint8_t -UIPUDP::begin(uint16_t port) -{ - if (!_uip_udp_conn) - { - _uip_udp_conn = uip_udp_new(NULL, 0); - } - if (_uip_udp_conn) - { - uip_udp_bind(_uip_udp_conn,htons(port)); - _uip_udp_conn->appstate = &appdata; - return 1; - } - return 0; -} - -// Finish with the UDP socket -void -UIPUDP::stop() -{ - if (_uip_udp_conn) - { - uip_udp_remove(_uip_udp_conn); - _uip_udp_conn->appstate = NULL; - _uip_udp_conn=NULL; - Enc28J60Network::freeBlock(appdata.packet_in); - _flushBlocks(appdata.packet_next); - Enc28J60Network::freeBlock(appdata.packet_out); - appdata = uip_udp_userdata_t(); - } -} - -// 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 -int -UIPUDP::beginPacket(IPAddress ip, uint16_t port) -{ - UIPEthernetClass::tick(); - if ((ip[0] || ip[1] || ip[2] || ip[3]) && port) - { - uip_ipaddr_t ripaddr; - uip_ip_addr(&ripaddr, ip); -#ifdef UIPETHERNET_DEBUG_UDP - Serial.print(F("udp beginPacket, ")); -#endif - if (_uip_udp_conn) - { - // [J.A]: for UDP server, setting ripaddr and rport causes in uIP filtering - // of incoming messages. it would be better to store the IP and port in fields - // and set them only in endPacket(). - _uip_udp_conn->rport = htons(port); - uip_ipaddr_copy(_uip_udp_conn->ripaddr, &ripaddr); - } - else - { - _uip_udp_conn = uip_udp_new(&ripaddr,htons(port)); - if (_uip_udp_conn) - { -#ifdef UIPETHERNET_DEBUG_UDP - Serial.print(F("new connection, ")); -#endif - _uip_udp_conn->appstate = &appdata; - } - else - { -#ifdef UIPETHERNET_DEBUG_UDP - Serial.println(F("failed to allocate new connection")); -#endif - return 0; - } - } -#ifdef UIPETHERNET_DEBUG_UDP - Serial.print(F("rip: ")); - Serial.print(ip); - Serial.print(F(", port: ")); - Serial.println(port); -#endif - } - if (_uip_udp_conn) - { - if (appdata.packet_out == NOBLOCK) - { - appdata.packet_out = Enc28J60Network::allocBlock(UIP_UDP_MAXPACKETSIZE + UIP_SENDBUFFER_OFFSET + UIP_SENDBUFFER_PADDING); - appdata.out_pos = UIP_UDP_PHYH_LEN + UIP_SENDBUFFER_OFFSET; - if (appdata.packet_out != NOBLOCK) - return 1; -#ifdef UIPETHERNET_DEBUG_UDP - else - Serial.println(F("failed to allocate memory for packet")); -#endif - } -#ifdef UIPETHERNET_DEBUG_UDP - else - Serial.println(F("previous packet on that connection not sent yet")); -#endif - } - return 0; -} - -// 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 -int -UIPUDP::beginPacket(const char *host, uint16_t port) -{ - // Look up the host first - int ret = 0; - IPAddress remote_addr; - ret = Ethernet.hostByName(host, remote_addr); - if (ret == 1) { - return beginPacket(remote_addr, port); - } else { - return ret; - } -} - -// Finish off this packet and send it -// Returns 1 if the packet was sent successfully, 0 if there was an error -int -UIPUDP::endPacket() -{ - if (_uip_udp_conn && appdata.packet_out != NOBLOCK) - { - appdata.send = true; - Enc28J60Network::resizeBlock(appdata.packet_out,0,appdata.out_pos + UIP_SENDBUFFER_PADDING); - uip_udp_periodic_conn(_uip_udp_conn); - if (uip_len > 0) - { - _send(_uip_udp_conn); - return 1; - } - } - return 0; -} - -// Write a single byte into the packet -size_t -UIPUDP::write(uint8_t c) -{ - return write(&c,1); -} - -// Write size bytes from buffer into the packet -size_t -UIPUDP::write(const uint8_t *buffer, size_t size) -{ - if (appdata.packet_out != NOBLOCK) - { - size_t ret = Enc28J60Network::writePacket(appdata.packet_out,appdata.out_pos,(uint8_t*)buffer,size); - appdata.out_pos += ret; - return ret; - } - return 0; -} - -// Start processing the next available incoming packet -// Returns the size of the packet in bytes, or 0 if no packets are available -int -UIPUDP::parsePacket() -{ - UIPEthernetClass::tick(); -#ifdef UIPETHERNET_DEBUG_UDP - if (appdata.packet_in != NOBLOCK) - { - Serial.print(F("udp parsePacket freeing previous packet: ")); - Serial.println(appdata.packet_in); - } -#endif - Enc28J60Network::freeBlock(appdata.packet_in); - - appdata.packet_in = appdata.packet_next[0].packet; - uip_ipaddr_copy(appdata.remote_ip, appdata.packet_next[0].remote_ip); - appdata.remote_port = appdata.packet_next[0].remote_port; - _moveBlocks(appdata.packet_next); - -#ifdef UIPETHERNET_DEBUG_UDP - if (appdata.packet_in != NOBLOCK) - { - Serial.print(F("udp parsePacket received packet: ")); - Serial.print(appdata.packet_in); - } -#endif - int size = Enc28J60Network::blockSize(appdata.packet_in); -#ifdef UIPETHERNET_DEBUG_UDP - if (appdata.packet_in != NOBLOCK) - { - Serial.print(F(", size: ")); - Serial.println(size); - } -#endif - return size; -} - -// Number of bytes remaining in the current packet -int -UIPUDP::available() -{ - UIPEthernetClass::tick(); - return Enc28J60Network::blockSize(appdata.packet_in); -} - -// Read a single byte from the current packet -int -UIPUDP::read() -{ - unsigned char c; - if (read(&c,1) > 0) - { - return c; - } - return -1; -} - -// 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 -int -UIPUDP::read(unsigned char* buffer, size_t len) -{ - UIPEthernetClass::tick(); - if (appdata.packet_in != NOBLOCK) - { - memaddress read = Enc28J60Network::readPacket(appdata.packet_in,0,buffer,len); - if (read == Enc28J60Network::blockSize(appdata.packet_in)) - { - Enc28J60Network::freeBlock(appdata.packet_in); - appdata.packet_in = NOBLOCK; - } - else - Enc28J60Network::resizeBlock(appdata.packet_in,read); - return read; - } - return 0; -} - -// Return the next byte from the current packet without moving on to the next byte -int -UIPUDP::peek() -{ - UIPEthernetClass::tick(); - if (appdata.packet_in != NOBLOCK) - { - unsigned char c; - if (Enc28J60Network::readPacket(appdata.packet_in,0,&c,1) == 1) - return c; - } - return -1; -} - -// Finish reading the current packet -void -UIPUDP::discardReceived() -{ - UIPEthernetClass::tick(); - Enc28J60Network::freeBlock(appdata.packet_in); - appdata.packet_in = NOBLOCK; -} - -// Return the IP address of the host who sent the current incoming packet -IPAddress -UIPUDP::remoteIP() -{ - return ip_addr_uip(appdata.remote_ip); -} - -// Return the port of the host who sent the current incoming packet -uint16_t -UIPUDP::remotePort() -{ - return ntohs(appdata.remote_port); -} - -// uIP callback function - -void -uipudp_appcall(void) { - if (uip_udp_userdata_t *data = (uip_udp_userdata_t *)(uip_udp_conn->appstate)) - { - if (uip_newdata()) - { - uint8_t p = UIPUDP::_newBlock(data->packet_next); - if (data->packet_next[p].packet == NOBLOCK) - { - data->packet_next[p].remote_port = UDPBUF->srcport; - uip_ipaddr_copy( data->packet_next[p].remote_ip,UDPBUF->srcipaddr); - data->packet_next[p].packet = Enc28J60Network::allocBlock(ntohs(UDPBUF->udplen)-UIP_UDPH_LEN); - //if we are unable to allocate memory the packet is dropped. udp doesn't guarantee packet delivery - if (data->packet_next[p].packet != NOBLOCK) - { - //discard Linklevel and IP and udp-header and any trailing bytes: - Enc28J60Network::copyPacket(data->packet_next[p].packet,0,UIPEthernetClass::in_packet,UIP_UDP_PHYH_LEN,Enc28J60Network::blockSize(data->packet_next[p].packet)); - #ifdef UIPETHERNET_DEBUG_UDP - Serial.print(F("udp, uip_newdata received packet: ")); - Serial.print(data->packet_next[p].packet); - Serial.print(F(", size: ")); - Serial.println(Enc28J60Network::blockSize(data->packet_next[p].packet)); - #endif - } - } - } - if (uip_poll() && data->send) - { - //set uip_slen (uip private) by calling uip_udp_send -#ifdef UIPETHERNET_DEBUG_UDP - Serial.print(F("udp, uip_poll preparing packet to send: ")); - Serial.print(data->packet_out); - Serial.print(F(", size: ")); - Serial.println(Enc28J60Network::blockSize(data->packet_out)); -#endif - UIPEthernetClass::uip_packet = data->packet_out; - UIPEthernetClass::packetstate |= UIPETHERNET_SENDPACKET; - UIPEthernetClass::uip_hdrlen = UIP_UDP_PHYH_LEN; - uip_udp_send(data->out_pos - (UIP_UDP_PHYH_LEN + UIP_SENDBUFFER_OFFSET)); - } - } -} - -void -UIPUDP::_send(struct uip_udp_conn *uip_udp_conn) { - - uip_arp_out(); //add arp - if (uip_len == UIP_ARPHDRSIZE) - { - UIPEthernetClass::uip_packet = NOBLOCK; - UIPEthernetClass::packetstate &= ~UIPETHERNET_SENDPACKET; -#ifdef UIPETHERNET_DEBUG_UDP - Serial.println(F("udp, uip_poll results in ARP-packet")); -#endif - UIPEthernetClass::network_send(); - } - else - //arp found ethaddr for ip (otherwise packet is replaced by arp-request) - { - uip_udp_userdata_t* data = (uip_udp_userdata_t *)(uip_udp_conn->appstate); - UIPEthernetClass::network_send(); - data->send = false; - data->packet_out = NOBLOCK; - - // [J.A] a listening UDP port in uIP filters received messages - // if rport and ripaddr are set. so we better clear them - uip_udp_conn->rport = 0; - uip_udp_conn->ripaddr[0] = 0; - uip_udp_conn->ripaddr[1] = 0; - -#ifdef UIPETHERNET_DEBUG_UDP - Serial.print(F("udp, uip_packet to send: ")); - Serial.println(UIPEthernetClass::uip_packet); -#endif - } -} - -uint8_t -UIPUDP::_newBlock(uip_udp_msg_rec_t* block) -{ - for (uint8_t i = 0; i < UIP_UDP_BACKLOG; i++) - { - if (block[i].packet == NOBLOCK) - return i; - } - return UIP_UDP_BACKLOG-1; -} - -void -UIPUDP::_moveBlocks(uip_udp_msg_rec_t* block) -{ - for (uint8_t i = 0; i < UIP_UDP_BACKLOG-1; i++) - { - block[i] = block[i+1]; - } - block[UIP_UDP_BACKLOG-1].packet = NOBLOCK; -} - -void -UIPUDP::_flushBlocks(uip_udp_msg_rec_t* block) -{ - for (uint8_t i = 0; i < UIP_UDP_BACKLOG; i++) - { - Enc28J60Network::freeBlock(block[i].packet); - block[i].packet = NOBLOCK; - } -} -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetUdp.h b/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetUdp.h deleted file mode 100644 index 43d27ca..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/EthernetUdp.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - UIPUdp.h - Arduino implementation of a uIP wrapper class. - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -#ifndef UIPUDP_H -#define UIPUDP_H - -#include -#include -#include "utility/mempool.h" -extern "C" { - #include "utility/uip.h" -} - -#define UIP_UDP_MAXDATALEN 1500 -#define UIP_UDP_PHYH_LEN UIP_LLH_LEN+UIP_IPUDPH_LEN -#define UIP_UDP_MAXPACKETSIZE UIP_UDP_MAXDATALEN+UIP_UDP_PHYH_LEN - -typedef struct { - memhandle packet = NOBLOCK; - uip_ipaddr_t remote_ip; - uint16_t remote_port = 0; -} uip_udp_msg_rec_t; - -typedef struct { - memaddress out_pos; - uip_udp_msg_rec_t packet_next[UIP_UDP_BACKLOG]; - memhandle packet_in = NOBLOCK; - memhandle packet_out = NOBLOCK; - boolean send; - uip_ipaddr_t remote_ip; - uint16_t remote_port = 0; -} uip_udp_userdata_t; - -class EthernetUDP : public UDP -{ - -private: - struct uip_udp_conn *_uip_udp_conn; - - uip_udp_userdata_t appdata; - -public: - EthernetUDP(); // Constructor - uint8_t - begin(uint16_t);// initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use - void - stop(); // Finish with the UDP socket - - // 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 - 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 - 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 - int - endPacket(); - // Write a single byte into the packet - size_t - write(uint8_t); - // Write size bytes from buffer into the packet - size_t - write(const uint8_t *buffer, size_t size); - // flush() should send the data, but here endPacket does it - void - flush() {}; // in Print it is empty, UDP makes it pure virtual - - 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 - int - parsePacket(); - // Number of bytes remaining in the current packet - int - available(); - // Read a single byte from the current packet - 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 - 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 - 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 - int - peek(); - void - discardReceived(); // former flush - - // Return the IP address of the host who sent the current incoming packet - IPAddress - remoteIP(); - - // Return the port of the host who sent the current incoming packet - uint16_t - remotePort(); - -private: - - friend void uipudp_appcall(void); - - friend class UIPEthernetClass; - static void _send(struct uip_udp_conn *uip_udp_conn); - - static uint8_t _newBlock(uip_udp_msg_rec_t* blocks); - static void _moveBlocks(uip_udp_msg_rec_t* blocks); - static void _flushBlocks(uip_udp_msg_rec_t* blocks); -}; - -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/tcp_states.h b/ShineLAN-X/firmware/lib/EthernetENC/src/tcp_states.h deleted file mode 100644 index 02864da..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/tcp_states.h +++ /dev/null @@ -1,21 +0,0 @@ - -#ifndef UIP_TCP_STATES_H -#define UIP_TCP_STATES_H - -#include - -// common constants for client.state() return values -enum uip_tcp_state { - CLOSED = UIP_CLOSED, - SYN_SENT = UIP_SYN_SENT, - SYN_RCVD = UIP_SYN_RCVD, - ESTABLISHED = UIP_ESTABLISHED, - FIN_WAIT_1 = UIP_FIN_WAIT_1, - FIN_WAIT_2 = UIP_FIN_WAIT_2, - CLOSE_WAIT = 10, // not used - CLOSING = UIP_CLOSING, - LAST_ACK = UIP_LAST_ACK, - TIME_WAIT = UIP_TIME_WAIT -}; - -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/Enc28J60Network.cpp b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/Enc28J60Network.cpp deleted file mode 100644 index 6649b48..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/Enc28J60Network.cpp +++ /dev/null @@ -1,391 +0,0 @@ -/* - Enc28J60NetworkClass.cpp — Bitbang-SPI version for non-hardware-SPI pins - Based on UIPEthernet by Norbert Truchsess, GPL V2 - - Modification: Hardware SPI replaced with bitbang SPI. - Target: STM32F103RBT6 with ENC28J60 on PC6(SCK)/PC7(MISO)/PC8(CS)/PC9(MOSI) - */ - -#include "Enc28J60Network.h" -#include -#include "config.h" // ETH_SCK_PIN / ETH_MOSI_PIN / ETH_MISO_PIN -// Note: intentionally NOT included — this version uses bitbang SPI - -extern "C" { -#include "enc28j60.h" -#include "uip.h" -} - -// ============================================================ -// Bitbang SPI — SPI Mode 0 (CPOL=0, CPHA=0) -// Pin definitions come from project include/config.h via build system -// ============================================================ -#ifndef ETH_SCK_PIN - #error "ETH_SCK_PIN not defined — check include/config.h" -#endif - -static bool _spi_ready = false; - -static void bitbang_init() { - if (_spi_ready) return; - pinMode(ETH_SCK_PIN, OUTPUT); digitalWrite(ETH_SCK_PIN, LOW); - pinMode(ETH_MOSI_PIN, OUTPUT); digitalWrite(ETH_MOSI_PIN, LOW); - pinMode(ETH_MISO_PIN, INPUT); - _spi_ready = true; -} - -static inline uint8_t bitbang_transfer(uint8_t out) { - uint8_t in = 0; - for (int8_t i = 7; i >= 0; i--) { - digitalWrite(ETH_MOSI_PIN, (out >> i) & 1); - digitalWrite(ETH_SCK_PIN, HIGH); - in = (in << 1) | digitalRead(ETH_MISO_PIN); - digitalWrite(ETH_SCK_PIN, LOW); - } - return in; -} -// ============================================================ - -// set CS to 0 = active -#define CSACTIVE digitalWrite(csPin, LOW) -// set CS to 1 = passive -#define CSPASSIVE digitalWrite(csPin, HIGH) - -bool Enc28J60Network::spiInitialized = false; -uint8_t Enc28J60Network::csPin = SS; -uint16_t Enc28J60Network::nextPacketPtr; -uint8_t Enc28J60Network::bank = 0xff; - -struct memblock Enc28J60Network::receivePkt; - -void Enc28J60Network::initSPI() -{ - if (spiInitialized) - return; - bitbang_init(); - pinMode(csPin, OUTPUT); - CSPASSIVE; - spiInitialized = true; -} - -bool Enc28J60Network::init(uint8_t* macaddr) -{ - MemoryPool::init(); - initSPI(); - - // perform system reset - writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); - delay(50); - nextPacketPtr = RXSTART_INIT; - writeRegPair(ERXSTL, RXSTART_INIT); - writeRegPair(ERXRDPTL, RXSTART_INIT); - writeRegPair(ERXNDL, RXSTOP_INIT); - writeReg(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN); - writeRegPair(EPMM0, 0x303f); - writeRegPair(EPMCSL, 0xf7f9); - writeRegPair(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); - writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); - writeRegPair(MAIPGL, 0x0C12); - writeReg(MABBIPG, 0x12); - writeRegPair(MAMXFLL, MAX_FRAMELEN); - writeReg(MAADR5, macaddr[0]); - writeReg(MAADR4, macaddr[1]); - writeReg(MAADR3, macaddr[2]); - writeReg(MAADR2, macaddr[3]); - writeReg(MAADR1, macaddr[4]); - writeReg(MAADR0, macaddr[5]); - phyWrite(PHCON2, PHCON2_HDLDIS); - setBank(ECON1); - writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); - phyWrite(PHLCON, 0x476); - - return getrev(); -} - -memhandle Enc28J60Network::receivePacket() -{ - uint8_t rxstat; - uint16_t len; - - if (readReg(EPKTCNT) != 0) - { - uint16_t readPtr = nextPacketPtr+6 > RXSTOP_INIT ? - nextPacketPtr+6-((RXSTOP_INIT + 1)-RXSTART_INIT) : nextPacketPtr+6; - writeRegPair(ERDPTL, nextPacketPtr); - nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); - nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; - len = readOp(ENC28J60_READ_BUF_MEM, 0); - len |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; - len -= 4; - rxstat = readOp(ENC28J60_READ_BUF_MEM, 0); - writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); - if ((rxstat & 0x80) != 0) - { - receivePkt.begin = readPtr; - receivePkt.size = len; - return UIP_RECEIVEBUFFERHANDLE; - } - setERXRDPT(); - } - return (NOBLOCK); -} - -void Enc28J60Network::setERXRDPT() -{ - writeRegPair(ERXRDPTL, nextPacketPtr == RXSTART_INIT ? RXSTOP_INIT : nextPacketPtr-1); -} - -memaddress Enc28J60Network::blockSize(memhandle handle) -{ - return handle == NOBLOCK ? 0 : - handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; -} - -bool Enc28J60Network::sendPacket(memhandle handle) -{ - memblock *packet = &blocks[handle]; - uint16_t start = packet->begin; - uint16_t end = start + packet->size - 1 - UIP_SENDBUFFER_PADDING; - - writeByte(start, 0); - - writeRegPair(ETXSTL, start); - writeRegPair(ETXNDL, end); - - bool success = false; - for (uint8_t retry = 0; retry < TX_COLLISION_RETRY_COUNT; retry++) - { - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); - writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); - writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF | EIR_TXIF); - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); - uint8_t eir; - while (((eir = readReg(EIR)) & (EIR_TXIF | EIR_TXERIF)) == 0); - writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); - success = ((eir & EIR_TXERIF) == 0); - if (success) break; - uint8_t tsv4 = readByte(end + 4); - if (!(tsv4 & 0b00100000)) break; - } - return success; -} - -uint16_t Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) -{ - memblock *packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle]; - memaddress start = handle == UIP_RECEIVEBUFFERHANDLE && - packet->begin + position > RXSTOP_INIT ? - packet->begin + position-((RXSTOP_INIT + 1)-RXSTART_INIT) : - packet->begin + position; - writeRegPair(ERDPTL, start); - if (len > packet->size - position) len = packet->size - position; - return len; -} - -uint16_t Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) -{ - len = setReadPtr(handle, position, len); - readBuffer(len, buffer); - return len; -} - -uint16_t Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) -{ - memblock *packet = &blocks[handle]; - uint16_t start = packet->begin + position; - if (len > packet->size - position) len = packet->size - position; - if (len == 0) return 0; - writeRegPair(EWRPTL, start); - writeBuffer(len, buffer); - return len; -} - -uint8_t Enc28J60Network::readByte(uint16_t addr) -{ - writeRegPair(ERDPTL, addr); - CSACTIVE; - bitbang_transfer(ENC28J60_READ_BUF_MEM); - uint8_t c = bitbang_transfer(0x00); - CSPASSIVE; - return c; -} - -void Enc28J60Network::writeByte(uint16_t addr, uint8_t data) -{ - writeRegPair(EWRPTL, addr); - CSACTIVE; - bitbang_transfer(ENC28J60_WRITE_BUF_MEM); - bitbang_transfer(data); - CSPASSIVE; -} - -void Enc28J60Network::copyPacket(memhandle dest_pkt, memaddress dest_pos, memhandle src_pkt, memaddress src_pos, uint16_t len) -{ - memblock *dest = &blocks[dest_pkt]; - memblock *src = src_pkt == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[src_pkt]; - memaddress start = src_pkt == UIP_RECEIVEBUFFERHANDLE && src->begin + src_pos > RXSTOP_INIT ? - src->begin + src_pos - ((RXSTOP_INIT + 1) - RXSTART_INIT) : src->begin + src_pos; - enc28J60_mempool_block_move_callback(dest->begin + dest_pos, start, len); -} - -void enc28J60_mempool_block_move_callback(memaddress dest, memaddress src, memaddress len) -{ - if (len == 1) - { - Enc28J60Network::writeByte(dest, Enc28J60Network::readByte(src)); - } - else - { - len += src - 1; - Enc28J60Network::writeRegPair(EDMASTL, src); - Enc28J60Network::writeRegPair(EDMADSTL, dest); - if ((src <= RXSTOP_INIT) && (len > RXSTOP_INIT)) - len -= ((RXSTOP_INIT + 1)-RXSTART_INIT); - Enc28J60Network::writeRegPair(EDMANDL, len); - Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_CSUMEN); - Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST); - while (Enc28J60Network::readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST); - } -} - -void Enc28J60Network::freePacket() -{ - setERXRDPT(); -} - -uint8_t Enc28J60Network::readOp(uint8_t op, uint8_t address) -{ - CSACTIVE; - bitbang_transfer(op | (address & ADDR_MASK)); - if (address & 0x80) bitbang_transfer(0x00); // dummy read for MAC/MII - uint8_t c = bitbang_transfer(0x00); - CSPASSIVE; - return c; -} - -void Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) -{ - CSACTIVE; - bitbang_transfer(op | (address & ADDR_MASK)); - bitbang_transfer(data); - CSPASSIVE; -} - -void Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) -{ - CSACTIVE; - bitbang_transfer(ENC28J60_READ_BUF_MEM); - while (len--) *data++ = bitbang_transfer(0x00); - CSPASSIVE; -} - -void Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) -{ - CSACTIVE; - bitbang_transfer(ENC28J60_WRITE_BUF_MEM); - while (len--) bitbang_transfer(*data++); - CSPASSIVE; -} - -void Enc28J60Network::setBank(uint8_t address) -{ - if ((address & BANK_MASK) != bank) - { - writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0)); - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5); - bank = (address & BANK_MASK); - } -} - -uint8_t Enc28J60Network::readReg(uint8_t address) -{ - setBank(address); - return readOp(ENC28J60_READ_CTRL_REG, address); -} - -void Enc28J60Network::writeReg(uint8_t address, uint8_t data) -{ - setBank(address); - writeOp(ENC28J60_WRITE_CTRL_REG, address, data); -} - -void Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) -{ - setBank(address); - writeOp(ENC28J60_WRITE_CTRL_REG, address, (data & 0xFF)); - writeOp(ENC28J60_WRITE_CTRL_REG, address+1, (data >> 8)); -} - -void Enc28J60Network::phyWrite(uint8_t address, uint16_t data) -{ - writeReg(MIREGADR, address); - writeRegPair(MIWRL, data); - while (readReg(MISTAT) & MISTAT_BUSY) delayMicroseconds(15); -} - -uint16_t Enc28J60Network::phyRead(uint8_t address) -{ - writeReg(MIREGADR, address); - writeReg(MICMD, MICMD_MIIRD); - while (readReg(MISTAT) & MISTAT_BUSY) delayMicroseconds(15); - writeReg(MICMD, 0); - return (readReg(MIRDL) | readReg(MIRDH) << 8); -} - -void Enc28J60Network::clkout(uint8_t clk) -{ - writeReg(ECOCON, clk & 0x7); -} - -uint8_t Enc28J60Network::getrev(void) -{ - initSPI(); - return readReg(EREVID); -} - -uint16_t Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) -{ - uint16_t t; - len = setReadPtr(handle, pos, len) - 1; - CSACTIVE; - bitbang_transfer(ENC28J60_READ_BUF_MEM); - uint16_t i; - for (i = 0; i < len; i += 2) - { - t = bitbang_transfer(0x00) << 8; - t += bitbang_transfer(0x00); - sum += t; - if (sum < t) sum++; - } - if (i == len) - { - t = (bitbang_transfer(0x00) << 8) + 0; - sum += t; - if (sum < t) sum++; - } - CSPASSIVE; - return sum; -} - -void Enc28J60Network::powerOff() -{ - writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); - delay(50); - writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); - delay(50); - writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); -} - -void Enc28J60Network::powerOn() -{ - writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); - delay(50); - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); - delay(50); -} - -bool Enc28J60Network::linkStatus() -{ - return (phyRead(PHSTAT2) & 0x0400) > 0; -} diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/Enc28J60Network.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/Enc28J60Network.h deleted file mode 100644 index f35801b..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/Enc28J60Network.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - Enc28J60NetworkClass.h - UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. - - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - inspired by enc28j60.c file from the AVRlib library by Pascal Stang. - For AVRlib See http://www.procyonengineering.com/ - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -#ifndef Enc28J60Network_H_ -#define Enc28J60Network_H_ - -#include "mempool.h" - -#define UIP_RECEIVEBUFFERHANDLE 0xff - -#define UIP_SENDBUFFER_PADDING 7 -#define UIP_SENDBUFFER_OFFSET 1 - -#define TX_COLLISION_RETRY_COUNT 3 - -//#define ENC28J60DEBUG - -/* - * Empfangen von ip-header, arp etc... - * wenn tcp/udp -> tcp/udp-callback -> assign new packet to connection - */ - -class Enc28J60Network : public MemoryPool -{ - -private: - static uint8_t csPin; - static bool spiInitialized; - - static uint16_t nextPacketPtr; - static uint8_t bank; - - static struct memblock receivePkt; - - static uint8_t readOp(uint8_t op, uint8_t address); - static void writeOp(uint8_t op, uint8_t address, uint8_t data); - static uint16_t setReadPtr(memhandle handle, memaddress position, uint16_t len); - static void setERXRDPT(); - static void readBuffer(uint16_t len, uint8_t* data); - static void writeBuffer(uint16_t len, uint8_t* data); - static uint8_t readByte(uint16_t addr); - static void writeByte(uint16_t addr, uint8_t data); - static void setBank(uint8_t address); - static uint8_t readReg(uint8_t address); - static void writeReg(uint8_t address, uint8_t data); - static void writeRegPair(uint8_t address, uint16_t data); - static void phyWrite(uint8_t address, uint16_t data); - static uint16_t phyRead(uint8_t address); - static void clkout(uint8_t clk); - - friend void enc28J60_mempool_block_move_callback(memaddress,memaddress,memaddress); - -public: - - static uint8_t getrev(void); - static void powerOn(); - static void powerOff(); - static bool linkStatus(); - - static void setCsPin(uint8_t _csPin) {csPin = _csPin;} - static void initSPI(); - static bool init(uint8_t* macaddr); - static memhandle receivePacket(); - static void freePacket(); - static memaddress blockSize(memhandle handle); - static bool sendPacket(memhandle handle); - static uint16_t readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len); - static uint16_t writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len); - static void copyPacket(memhandle dest, memaddress dest_pos, memhandle src, memaddress src_pos, uint16_t len); - static uint16_t chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len); -}; - -#endif /* Enc28J60NetworkClass_H_ */ diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/enc28j60.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/enc28j60.h deleted file mode 100644 index 3a45c59..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/enc28j60.h +++ /dev/null @@ -1,257 +0,0 @@ -/***************************************************************************** -* -* Title : Microchip ENC28J60 Ethernet Interface Driver -* Author : Pascal Stang (c)2005 -* Modified by Norbert Truchsess -* Copyright: GPL V2 -* -*This driver provides initialization and transmit/receive -*functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. -*This chip is novel in that it is a full MAC+PHY interface all in a 28-pin -*chip, using an SPI interface to the host processor. -* -* -*****************************************************************************/ - -#ifndef ENC28J60_H -#define ENC28J60_H -#include - -// ENC28J60 Control Registers -// Control register definitions are a combination of address, -// bank number, and Ethernet/MAC/PHY indicator bits. -// - Register address (bits 0-4) -// - Bank number (bits 5-6) -// - MAC/PHY indicator (bit 7) -#define ADDR_MASK 0x1F -#define BANK_MASK 0x60 -#define SPRD_MASK 0x80 -// All-bank registers -#define EIE 0x1B -#define EIR 0x1C -#define ESTAT 0x1D -#define ECON2 0x1E -#define ECON1 0x1F -// Bank 0 registers -#define ERDPTL (0x00|0x00) -#define ERDPTH (0x01|0x00) -#define EWRPTL (0x02|0x00) -#define EWRPTH (0x03|0x00) -#define ETXSTL (0x04|0x00) -#define ETXSTH (0x05|0x00) -#define ETXNDL (0x06|0x00) -#define ETXNDH (0x07|0x00) -#define ERXSTL (0x08|0x00) -#define ERXSTH (0x09|0x00) -#define ERXNDL (0x0A|0x00) -#define ERXNDH (0x0B|0x00) -#define ERXRDPTL (0x0C|0x00) -#define ERXRDPTH (0x0D|0x00) -#define ERXWRPTL (0x0E|0x00) -#define ERXWRPTH (0x0F|0x00) -#define EDMASTL (0x10|0x00) -#define EDMASTH (0x11|0x00) -#define EDMANDL (0x12|0x00) -#define EDMANDH (0x13|0x00) -#define EDMADSTL (0x14|0x00) -#define EDMADSTH (0x15|0x00) -#define EDMACSL (0x16|0x00) -#define EDMACSH (0x17|0x00) -// Bank 1 registers -#define EHT0 (0x00|0x20) -#define EHT1 (0x01|0x20) -#define EHT2 (0x02|0x20) -#define EHT3 (0x03|0x20) -#define EHT4 (0x04|0x20) -#define EHT5 (0x05|0x20) -#define EHT6 (0x06|0x20) -#define EHT7 (0x07|0x20) -#define EPMM0 (0x08|0x20) -#define EPMM1 (0x09|0x20) -#define EPMM2 (0x0A|0x20) -#define EPMM3 (0x0B|0x20) -#define EPMM4 (0x0C|0x20) -#define EPMM5 (0x0D|0x20) -#define EPMM6 (0x0E|0x20) -#define EPMM7 (0x0F|0x20) -#define EPMCSL (0x10|0x20) -#define EPMCSH (0x11|0x20) -#define EPMOL (0x14|0x20) -#define EPMOH (0x15|0x20) -#define EWOLIE (0x16|0x20) -#define EWOLIR (0x17|0x20) -#define ERXFCON (0x18|0x20) -#define EPKTCNT (0x19|0x20) -// Bank 2 registers -#define MACON1 (0x00|0x40|0x80) -#define MACON2 (0x01|0x40|0x80) -#define MACON3 (0x02|0x40|0x80) -#define MACON4 (0x03|0x40|0x80) -#define MABBIPG (0x04|0x40|0x80) -#define MAIPGL (0x06|0x40|0x80) -#define MAIPGH (0x07|0x40|0x80) -#define MACLCON1 (0x08|0x40|0x80) -#define MACLCON2 (0x09|0x40|0x80) -#define MAMXFLL (0x0A|0x40|0x80) -#define MAMXFLH (0x0B|0x40|0x80) -#define MAPHSUP (0x0D|0x40|0x80) -#define MICON (0x11|0x40|0x80) -#define MICMD (0x12|0x40|0x80) -#define MIREGADR (0x14|0x40|0x80) -#define MIWRL (0x16|0x40|0x80) -#define MIWRH (0x17|0x40|0x80) -#define MIRDL (0x18|0x40|0x80) -#define MIRDH (0x19|0x40|0x80) -// Bank 3 registers -#define MAADR1 (0x00|0x60|0x80) -#define MAADR0 (0x01|0x60|0x80) -#define MAADR3 (0x02|0x60|0x80) -#define MAADR2 (0x03|0x60|0x80) -#define MAADR5 (0x04|0x60|0x80) -#define MAADR4 (0x05|0x60|0x80) -#define EBSTSD (0x06|0x60) -#define EBSTCON (0x07|0x60) -#define EBSTCSL (0x08|0x60) -#define EBSTCSH (0x09|0x60) -#define MISTAT (0x0A|0x60|0x80) -#define EREVID (0x12|0x60) -#define ECOCON (0x15|0x60) -#define EFLOCON (0x17|0x60) -#define EPAUSL (0x18|0x60) -#define EPAUSH (0x19|0x60) -// PHY registers -#define PHCON1 0x00 -#define PHSTAT1 0x01 -#define PHHID1 0x02 -#define PHHID2 0x03 -#define PHCON2 0x10 -#define PHSTAT2 0x11 -#define PHIE 0x12 -#define PHIR 0x13 -#define PHLCON 0x14 - -// ENC28J60 ERXFCON Register Bit Definitions -#define ERXFCON_UCEN 0x80 -#define ERXFCON_ANDOR 0x40 -#define ERXFCON_CRCEN 0x20 -#define ERXFCON_PMEN 0x10 -#define ERXFCON_MPEN 0x08 -#define ERXFCON_HTEN 0x04 -#define ERXFCON_MCEN 0x02 -#define ERXFCON_BCEN 0x01 -// ENC28J60 EIE Register Bit Definitions -#define EIE_INTIE 0x80 -#define EIE_PKTIE 0x40 -#define EIE_DMAIE 0x20 -#define EIE_LINKIE 0x10 -#define EIE_TXIE 0x08 -#define EIE_WOLIE 0x04 -#define EIE_TXERIE 0x02 -#define EIE_RXERIE 0x01 -// ENC28J60 EIR Register Bit Definitions -#define EIR_PKTIF 0x40 -#define EIR_DMAIF 0x20 -#define EIR_LINKIF 0x10 -#define EIR_TXIF 0x08 -#define EIR_WOLIF 0x04 -#define EIR_TXERIF 0x02 -#define EIR_RXERIF 0x01 -// ENC28J60 ESTAT Register Bit Definitions -#define ESTAT_INT 0x80 -#define ESTAT_LATECOL 0x10 -#define ESTAT_RXBUSY 0x04 -#define ESTAT_TXABRT 0x02 -#define ESTAT_CLKRDY 0x01 -// ENC28J60 ECON2 Register Bit Definitions -#define ECON2_AUTOINC 0x80 -#define ECON2_PKTDEC 0x40 -#define ECON2_PWRSV 0x20 -#define ECON2_VRPS 0x08 -// ENC28J60 ECON1 Register Bit Definitions -#define ECON1_TXRST 0x80 -#define ECON1_RXRST 0x40 -#define ECON1_DMAST 0x20 -#define ECON1_CSUMEN 0x10 -#define ECON1_TXRTS 0x08 -#define ECON1_RXEN 0x04 -#define ECON1_BSEL1 0x02 -#define ECON1_BSEL0 0x01 -// ENC28J60 MACON1 Register Bit Definitions -#define MACON1_LOOPBK 0x10 -#define MACON1_TXPAUS 0x08 -#define MACON1_RXPAUS 0x04 -#define MACON1_PASSALL 0x02 -#define MACON1_MARXEN 0x01 -// ENC28J60 MACON2 Register Bit Definitions -#define MACON2_MARST 0x80 -#define MACON2_RNDRST 0x40 -#define MACON2_MARXRST 0x08 -#define MACON2_RFUNRST 0x04 -#define MACON2_MATXRST 0x02 -#define MACON2_TFUNRST 0x01 -// ENC28J60 MACON3 Register Bit Definitions -#define MACON3_PADCFG2 0x80 -#define MACON3_PADCFG1 0x40 -#define MACON3_PADCFG0 0x20 -#define MACON3_TXCRCEN 0x10 -#define MACON3_PHDRLEN 0x08 -#define MACON3_HFRMLEN 0x04 -#define MACON3_FRMLNEN 0x02 -#define MACON3_FULDPX 0x01 -// ENC28J60 MICMD Register Bit Definitions -#define MICMD_MIISCAN 0x02 -#define MICMD_MIIRD 0x01 -// ENC28J60 MISTAT Register Bit Definitions -#define MISTAT_NVALID 0x04 -#define MISTAT_SCAN 0x02 -#define MISTAT_BUSY 0x01 -// ENC28J60 PHY PHCON1 Register Bit Definitions -#define PHCON1_PRST 0x8000 -#define PHCON1_PLOOPBK 0x4000 -#define PHCON1_PPWRSV 0x0800 -#define PHCON1_PDPXMD 0x0100 -// ENC28J60 PHY PHSTAT1 Register Bit Definitions -#define PHSTAT1_PFDPX 0x1000 -#define PHSTAT1_PHDPX 0x0800 -#define PHSTAT1_LLSTAT 0x0004 -#define PHSTAT1_JBSTAT 0x0002 -// ENC28J60 PHY PHCON2 Register Bit Definitions -#define PHCON2_FRCLINK 0x4000 -#define PHCON2_TXDIS 0x2000 -#define PHCON2_JABBER 0x0400 -#define PHCON2_HDLDIS 0x0100 - -// ENC28J60 Packet Control Byte Bit Definitions -#define PKTCTRL_PHUGEEN 0x08 -#define PKTCTRL_PPADEN 0x04 -#define PKTCTRL_PCRCEN 0x02 -#define PKTCTRL_POVERRIDE 0x01 - -// SPI operation codes -#define ENC28J60_READ_CTRL_REG 0x00 -#define ENC28J60_READ_BUF_MEM 0x3A -#define ENC28J60_WRITE_CTRL_REG 0x40 -#define ENC28J60_WRITE_BUF_MEM 0x7A -#define ENC28J60_BIT_FIELD_SET 0x80 -#define ENC28J60_BIT_FIELD_CLR 0xA0 -#define ENC28J60_SOFT_RESET 0xFF - - -// The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata -// buffer boundaries applied to internal 8K ram -// the entire available packet buffer space is allocated -// -// start with recbuf at 0/ -#define RXSTART_INIT 0x0 -// receive buffer end. make sure this is an odd value ( See Rev. B1,B4,B5,B7 Silicon Errata 'Memory (Ethernet Buffer)') -#define RXSTOP_INIT (0x1FFF-0x1800) -// start TX buffer RXSTOP_INIT+1 -#define TXSTART_INIT (RXSTOP_INIT+1) -// stp TX buffer at end of mem -#define TXSTOP_INIT 0x1FFF -// -// max frame length which the conroller will accept: -#define MAX_FRAMELEN 1500 // (note: maximum ethernet frame length would be 1518) -//#define MAX_FRAMELEN 600 - -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool.cpp b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool.cpp deleted file mode 100644 index c2f7abf..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - mempool.cpp - sleek implementation of a memory pool - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -#include "mempool.h" -#include - -#define POOLOFFSET 1 - -struct memblock MemoryPool::blocks[MEMPOOL_NUM_MEMBLOCKS+1]; - -void -MemoryPool::init() -{ - memset(&blocks[0], 0, sizeof(blocks)); - blocks[POOLSTART].begin = MEMPOOL_STARTADDRESS; - blocks[POOLSTART].size = 0; - blocks[POOLSTART].nextblock = NOBLOCK; -} - -memhandle -MemoryPool::allocBlock(memaddress size) -{ - memblock* best = NULL; - memhandle cur = POOLSTART; - memblock* block = &blocks[POOLSTART]; - memaddress bestsize = MEMPOOL_SIZE + 1; - - do - { - memhandle next = block->nextblock; - memaddress freesize = ( next == NOBLOCK ? blocks[POOLSTART].begin + MEMPOOL_SIZE : blocks[next].begin) - block->begin - block->size; - if (freesize == size) - { - best = &blocks[cur]; - goto found; - } - if (freesize > size && freesize < bestsize) - { - bestsize = freesize; - best = &blocks[cur]; - } - if (next == NOBLOCK) - { - if (best) - goto found; - else - goto collect; - } - block = &blocks[next]; - cur = next; - } - while (true); - - collect: - { - cur = POOLSTART; - block = &blocks[POOLSTART]; - memhandle next; - while ((next = block->nextblock) != NOBLOCK) - { - memaddress dest = block->begin + block->size; - memblock* nextblock = &blocks[next]; - memaddress* src = &nextblock->begin; - if (dest != *src) - { -#ifdef MEMPOOL_MEMBLOCK_MV - MEMPOOL_MEMBLOCK_MV(dest,*src,nextblock->size); -#endif - *src = dest; - } - block = nextblock; - } - if (blocks[POOLSTART].begin + MEMPOOL_SIZE - block->begin - block->size >= size) - best = block; - else - goto notfound; - } - - found: - { - block = &blocks[POOLOFFSET]; - for (cur = POOLOFFSET; cur < MEMPOOL_NUM_MEMBLOCKS + POOLOFFSET; cur++) - { - if (block->size) - { - block++; - continue; - } - memaddress address = best->begin + best->size; -#ifdef MEMBLOCK_ALLOC - MEMBLOCK_ALLOC(address,size); -#endif - block->begin = address; - block->size = size; - block->nextblock = best->nextblock; - best->nextblock = cur; - return cur; - } - } - - notfound: return NOBLOCK; -} - -void -MemoryPool::freeBlock(memhandle handle) -{ - if (handle == NOBLOCK) - return; - memblock *b = &blocks[POOLSTART]; - - do - { - memhandle next = b->nextblock; - if (next == handle) - { - memblock *f = &blocks[next]; -#ifdef MEMBLOCK_FREE - MEMBLOCK_FREE(f->begin,f->size); -#endif - b->nextblock = f->nextblock; - f->size = 0; - f->nextblock = NOBLOCK; - return; - } - if (next == NOBLOCK) - return; - b = &blocks[next]; - } - while (true); -} - -void -MemoryPool::resizeBlock(memhandle handle, memaddress position) -{ - memblock * block = &blocks[handle]; - block->begin += position; - block->size -= position; -} - -void -MemoryPool::resizeBlock(memhandle handle, memaddress position, memaddress size) -{ - memblock * block = &blocks[handle]; - block->begin += position; - block->size = size; -} - -memaddress -MemoryPool::blockSize(memhandle handle) -{ - return blocks[handle].size; -} diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool.h deleted file mode 100644 index 2a1db21..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - mempool.h - sleek implementation of a memory pool - Copyright (c) 2013 Norbert Truchsess - All rights reserved. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -#ifndef MEMPOOL_H -#define MEMPOOL_H - -#include - -#define POOLSTART 0 -#define NOBLOCK 0 - -#include "mempool_conf.h" - -struct memblock -{ - memaddress begin; - memaddress size; - memhandle nextblock; -}; - -class MemoryPool -{ -#ifdef MEMPOOLTEST_H - friend class MemoryPoolTest; -#endif - -protected: - static struct memblock blocks[MEMPOOL_NUM_MEMBLOCKS+1]; - -public: - static void init(); - static memhandle allocBlock(memaddress); - static void freeBlock(memhandle); - static void resizeBlock(memhandle handle, memaddress position); - static void resizeBlock(memhandle handle, memaddress position, memaddress size); - static memaddress blockSize(memhandle); -}; -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool_conf.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool_conf.h deleted file mode 100644 index 5f953dd..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/mempool_conf.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef MEMPOOLCONF_H -#define MEMPOOLCONF_H -#include "uipethernet-conf.h" -extern "C" { - #include "uipopt.h" - #include "enc28j60.h" -} -#include - -typedef uint16_t memaddress; -typedef uint8_t memhandle; - -#if UIP_SOCKET_NUMPACKETS and UIP_CONNS -#define NUM_TCP_MEMBLOCKS (UIP_SOCKET_NUMPACKETS*2)*UIP_CONNS -#else -#define NUM_TCP_MEMBLOCKS 0 -#endif - -#if UIP_UDP and UIP_UDP_CONNS -#define NUM_UDP_MEMBLOCKS ((2+UIP_UDP_BACKLOG)*UIP_UDP_CONNS) -#else -#define NUM_UDP_MEMBLOCKS 0 -#endif - -#define MEMPOOL_NUM_MEMBLOCKS (NUM_TCP_MEMBLOCKS+NUM_UDP_MEMBLOCKS) - -#define MEMPOOL_STARTADDRESS TXSTART_INIT+1 -#define MEMPOOL_SIZE TXSTOP_INIT-TXSTART_INIT - -void enc28J60_mempool_block_move_callback(memaddress,memaddress,memaddress); - -#define MEMPOOL_MEMBLOCK_MV(dest,src,size) enc28J60_mempool_block_move_callback(dest,src,size) - -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip-conf.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip-conf.h deleted file mode 100644 index a6242c4..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip-conf.h +++ /dev/null @@ -1,185 +0,0 @@ -/** - * UIPEthernet Project-specific configuration options - * Copyright (c) 2013 Norbert Truchsess - * @{ - * - * uIP has a number of configuration options that can be overridden - * for each project. These are kept in a project-specific uip-conf.h - * file and all configuration names have the prefix UIP_CONF. - */ - -/* - * Copyright (c) 2006, Swedish Institute of Computer Science. - * 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. Redistributions 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 the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack - * - */ - -#ifndef __UIP_CONF_H__ -#define __UIP_CONF_H__ - -#include -#include "uipethernet-conf.h" - -/** - * 8 bit datatype - * - * This typedef defines the 8-bit type used throughout uIP. - * - * \hideinitializer - */ -typedef uint8_t u8_t; - -/** - * 16 bit datatype - * - * This typedef defines the 16-bit type used throughout uIP. - * - * \hideinitializer - */ -typedef uint16_t u16_t; - -/** - * Statistics datatype - * - * This typedef defines the dataype used for keeping statistics in - * uIP. - * - * \hideinitializer - */ -typedef unsigned short uip_stats_t; - -/** - * Maximum number of TCP connections. - * (see uipethernet-conf.h) - * \hideinitializer - * - * #define UIP_CONF_MAX_CONNECTIONS 4 - */ - -/** - * Maximum number of listening TCP ports. - * - * \hideinitializer - */ -#define UIP_CONF_MAX_LISTENPORTS 4 - -/** - * uIP buffer size. - * - * \hideinitializer - */ -#define UIP_CONF_BUFFER_SIZE 98 -//#define UIP_CONF_BUFFER_SIZE 118 - -/** - * The TCP maximum segment size. - * - * This is should not be to set to more than - * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. - */ - -#define UIP_CONF_TCP_MSS 512 - -/** - * The size of the advertised receiver's window. - * - * Should be set low (i.e., to the size of the uip_buf buffer) is the - * application is slow to process incoming data, or high (32768 bytes) - * if the application processes data quickly. - * - * \hideinitializer - */ -#define UIP_CONF_RECEIVE_WINDOW 512 - -/** - * CPU byte order. - * - * \hideinitializer - */ -#define UIP_CONF_BYTE_ORDER __BYTE_ORDER__ - -/** - * Logging on or off - * - * \hideinitializer - */ -#define UIP_CONF_LOGGING 0 - -/** - * UDP support on or off - * (see uipethernet-conf.h) - * \hideinitializer - * - * #define UIP_CONF_UDP 1 - * - * #define UIP_CONF_UDP_CONNS 4 - */ - -/** - * UDP checksums on or off - * - * \hideinitializer - */ -#define UIP_CONF_UDP_CHECKSUMS 1 - -/** - * UDP Broadcast (receive) on or off - * (see uipethernet-conf.h) - * \hideinitializer - */ -#define UIP_CONF_BROADCAST 1 - - -/** - * uIP statistics on or off - * - * \hideinitializer - */ -#define UIP_CONF_STATISTICS 0 - -// SLIP -//#define UIP_CONF_LLH_LEN 0 - -typedef void* uip_tcp_appstate_t; - -void uipclient_appcall(void); - -#define UIP_APPCALL uipclient_appcall - -typedef void* uip_udp_appstate_t; - -void uipudp_appcall(void); - -#define UIP_UDP_APPCALL uipudp_appcall - -#define CC_REGISTER_ARG register - -#define UIP_ARCH_CHKSUM 1 - -#endif /* __UIP_CONF_H__ */ diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip.c b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip.c deleted file mode 100644 index 84ea8c3..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip.c +++ /dev/null @@ -1,1897 +0,0 @@ -#define DEBUG_PRINTF(...) /*printf(__VA_ARGS__)*/ - -/** - * \defgroup uip The uIP TCP/IP stack - * @{ - * - * uIP is an implementation of the TCP/IP protocol stack intended for - * small 8-bit and 16-bit microcontrollers. - * - * uIP provides the necessary protocols for Internet communication, - * with a very small code footprint and RAM requirements - the uIP - * code size is on the order of a few kilobytes and RAM usage is on - * the order of a few hundred bytes. - */ - -/** - * \file - * The uIP TCP/IP stack code. - * \author Adam Dunkels - */ - -/* - * Copyright (c) 2001-2003, Adam Dunkels. - * 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. Redistributions 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uip.c,v 1.65 2006/06/11 21:46:39 adam Exp $ - * - */ - -/* - * uIP is a small implementation of the IP, UDP and TCP protocols (as - * well as some basic ICMP stuff). The implementation couples the IP, - * UDP, TCP and the application layers very tightly. To keep the size - * of the compiled code down, this code frequently uses the goto - * statement. While it would be possible to break the uip_process() - * function into many smaller functions, this would increase the code - * size because of the overhead of parameter passing and the fact that - * the optimier would not be as efficient. - * - * The principle is that we have a small buffer, called the uip_buf, - * in which the device driver puts an incoming packet. The TCP/IP - * stack parses the headers in the packet, and calls the - * application. If the remote host has sent data to the application, - * this data is present in the uip_buf and the application read the - * data from there. It is up to the application to put this data into - * a byte stream if needed. The application will not be fed with data - * that is out of sequence. - * - * If the application whishes to send data to the peer, it should put - * its data into the uip_buf. The uip_appdata pointer points to the - * first available byte. The TCP/IP stack will calculate the - * checksums, and fill in the necessary header fields and finally send - * the packet back to the peer. -*/ - -#include "uip.h" -#include "uipopt.h" -#include "uip_arch.h" - -#if UIP_CONF_IPV6 -#include "uip-neighbor.h" -#endif /* UIP_CONF_IPV6 */ - -#include - -/*---------------------------------------------------------------------------*/ -/* Variable definitions. */ - - -/* The IP address of this host. If it is defined to be fixed (by - setting UIP_FIXEDADDR to 1 in uipopt.h), the address is set - here. Otherwise, the address */ -#if UIP_FIXEDADDR > 0 -const uip_ipaddr_t uip_hostaddr = - {HTONS((UIP_IPADDR0 << 8) | UIP_IPADDR1), - HTONS((UIP_IPADDR2 << 8) | UIP_IPADDR3)}; -const uip_ipaddr_t uip_draddr = - {HTONS((UIP_DRIPADDR0 << 8) | UIP_DRIPADDR1), - HTONS((UIP_DRIPADDR2 << 8) | UIP_DRIPADDR3)}; -const uip_ipaddr_t uip_netmask = - {HTONS((UIP_NETMASK0 << 8) | UIP_NETMASK1), - HTONS((UIP_NETMASK2 << 8) | UIP_NETMASK3)}; -#else -uip_ipaddr_t uip_hostaddr, uip_draddr, uip_netmask; -#endif /* UIP_FIXEDADDR */ - -static const uip_ipaddr_t all_ones_addr = -#if UIP_CONF_IPV6 - {0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff}; -#else /* UIP_CONF_IPV6 */ - {0xffff,0xffff}; -#endif /* UIP_CONF_IPV6 */ -static const uip_ipaddr_t all_zeroes_addr = -#if UIP_CONF_IPV6 - {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; -#else /* UIP_CONF_IPV6 */ - {0x0000,0x0000}; -#endif /* UIP_CONF_IPV6 */ - - -#if UIP_FIXEDETHADDR -const struct uip_eth_addr uip_ethaddr = {{UIP_ETHADDR0, - UIP_ETHADDR1, - UIP_ETHADDR2, - UIP_ETHADDR3, - UIP_ETHADDR4, - UIP_ETHADDR5}}; -#else -struct uip_eth_addr uip_ethaddr = {{0,0,0,0,0,0}}; -#endif - -#ifndef UIP_CONF_EXTERNAL_BUFFER -u8_t uip_buf[UIP_BUFSIZE + 2]; /* The packet buffer that contains - incoming packets. */ -#endif /* UIP_CONF_EXTERNAL_BUFFER */ - -void *uip_appdata; /* The uip_appdata pointer points to - application data. */ -void *uip_sappdata; /* The uip_appdata pointer points to - the application data which is to - be sent. */ -#if UIP_URGDATA > 0 -void *uip_urgdata; /* The uip_urgdata pointer points to - urgent data (out-of-band data), if - present. */ -u16_t uip_urglen, uip_surglen; -#endif /* UIP_URGDATA > 0 */ - -u16_t uip_len, uip_slen; - /* The uip_len is either 8 or 16 bits, - depending on the maximum packet - size. */ - -u8_t uip_flags; /* The uip_flags variable is used for - communication between the TCP/IP stack - and the application program. */ -struct uip_conn *uip_conn; /* uip_conn always points to the current - connection. */ - -struct uip_conn uip_conns[UIP_CONNS]; - /* The uip_conns array holds all TCP - connections. */ -u16_t uip_listenports[UIP_LISTENPORTS]; - /* The uip_listenports list all currently - listning ports. */ -#if UIP_UDP -struct uip_udp_conn *uip_udp_conn; -struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS]; -#endif /* UIP_UDP */ - -static u16_t ipid; /* Ths ipid variable is an increasing - number that is used for the IP ID - field. */ - -void uip_setipid(u16_t id) { ipid = id; } - -static u8_t iss[4]; /* The iss variable is used for the TCP - initial sequence number. */ - -#if UIP_ACTIVE_OPEN -static u16_t lastport; /* Keeps track of the last port used for - a new connection. */ -#endif /* UIP_ACTIVE_OPEN */ - -/* Temporary variables. */ -u8_t uip_acc32[4]; -static u8_t c, opt; -static u16_t tmp16; - -/* Structures and definitions. */ -#define TCP_FIN 0x01 -#define TCP_SYN 0x02 -#define TCP_RST 0x04 -#define TCP_PSH 0x08 -#define TCP_ACK 0x10 -#define TCP_URG 0x20 -#define TCP_CTL 0x3f - -#define TCP_OPT_END 0 /* End of TCP options list */ -#define TCP_OPT_NOOP 1 /* "No-operation" TCP option */ -#define TCP_OPT_MSS 2 /* Maximum segment size TCP option */ - -#define TCP_OPT_MSS_LEN 4 /* Length of TCP MSS option. */ - -#define ICMP_ECHO_REPLY 0 -#define ICMP_ECHO 8 - -#define ICMP6_ECHO_REPLY 129 -#define ICMP6_ECHO 128 -#define ICMP6_NEIGHBOR_SOLICITATION 135 -#define ICMP6_NEIGHBOR_ADVERTISEMENT 136 - -#define ICMP6_FLAG_S (1 << 6) - -#define ICMP6_OPTION_SOURCE_LINK_ADDRESS 1 -#define ICMP6_OPTION_TARGET_LINK_ADDRESS 2 - - -/* Macros. */ -#define BUF ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN]) -#define FBUF ((struct uip_tcpip_hdr *)&uip_reassbuf[0]) -#define ICMPBUF ((struct uip_icmpip_hdr *)&uip_buf[UIP_LLH_LEN]) -#define UDPBUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN]) - - -#if UIP_STATISTICS == 1 -struct uip_stats uip_stat; -#define UIP_STAT(s) s -#else -#define UIP_STAT(s) -#endif /* UIP_STATISTICS == 1 */ - -#if UIP_LOGGING == 1 -#include -void uip_log(char *msg); -#define UIP_LOG(m) uip_log(m) -#else -#define UIP_LOG(m) -#endif /* UIP_LOGGING == 1 */ - -#if ! UIP_ARCH_ADD32 -void -uip_add32(u8_t *op32, u16_t op16) -{ - uip_acc32[3] = op32[3] + (op16 & 0xff); - uip_acc32[2] = op32[2] + (op16 >> 8); - uip_acc32[1] = op32[1]; - uip_acc32[0] = op32[0]; - - if(uip_acc32[2] < (op16 >> 8)) { - ++uip_acc32[1]; - if(uip_acc32[1] == 0) { - ++uip_acc32[0]; - } - } - - - if(uip_acc32[3] < (op16 & 0xff)) { - ++uip_acc32[2]; - if(uip_acc32[2] == 0) { - ++uip_acc32[1]; - if(uip_acc32[1] == 0) { - ++uip_acc32[0]; - } - } - } -} - -#endif /* UIP_ARCH_ADD32 */ - -#if ! UIP_ARCH_CHKSUM -/*---------------------------------------------------------------------------*/ -static u16_t -chksum(u16_t sum, const u8_t *data, u16_t len) -{ - u16_t t; - const u8_t *dataptr; - const u8_t *last_byte; - - dataptr = data; - last_byte = data + len - 1; - - while(dataptr < last_byte) { /* At least two more bytes */ - t = (dataptr[0] << 8) + dataptr[1]; - sum += t; - if(sum < t) { - sum++; /* carry */ - } - dataptr += 2; - } - - if(dataptr == last_byte) { - t = (dataptr[0] << 8) + 0; - sum += t; - if(sum < t) { - sum++; /* carry */ - } - } - - /* Return sum in host byte order. */ - return sum; -} -/*---------------------------------------------------------------------------*/ -u16_t -uip_chksum(u16_t *data, u16_t len) -{ - return htons(chksum(0, (u8_t *)data, len)); -} -/*---------------------------------------------------------------------------*/ -#ifndef UIP_ARCH_IPCHKSUM -u16_t -uip_ipchksum(void) -{ - u16_t sum; - - sum = chksum(0, &uip_buf[UIP_LLH_LEN], UIP_IPH_LEN); - DEBUG_PRINTF("uip_ipchksum: sum 0x%04x\n", sum); - return (sum == 0) ? 0xffff : htons(sum); -} -#endif -/*---------------------------------------------------------------------------*/ -static u16_t -upper_layer_chksum(u8_t proto) -{ - u16_t upper_layer_len; - u16_t sum; - -#if UIP_CONF_IPV6 - upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]); -#else /* UIP_CONF_IPV6 */ - upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]) - UIP_IPH_LEN; -#endif /* UIP_CONF_IPV6 */ - - /* First sum pseudoheader. */ - - /* IP protocol and length fields. This addition cannot carry. */ - sum = upper_layer_len + proto; - /* Sum IP source and destination addresses. */ - sum = chksum(sum, (u8_t *)&BUF->srcipaddr[0], 2 * sizeof(uip_ipaddr_t)); - - /* Sum TCP header and data. */ - sum = chksum(sum, &uip_buf[UIP_IPH_LEN + UIP_LLH_LEN], - upper_layer_len); - - return (sum == 0) ? 0xffff : htons(sum); -} -/*---------------------------------------------------------------------------*/ -#if UIP_CONF_IPV6 -u16_t -uip_icmp6chksum(void) -{ - return upper_layer_chksum(UIP_PROTO_ICMP6); - -} -#endif /* UIP_CONF_IPV6 */ -/*---------------------------------------------------------------------------*/ -u16_t -uip_tcpchksum(void) -{ - return upper_layer_chksum(UIP_PROTO_TCP); -} -/*---------------------------------------------------------------------------*/ -#if UIP_UDP_CHECKSUMS -u16_t -uip_udpchksum(void) -{ - return upper_layer_chksum(UIP_PROTO_UDP); -} -#endif /* UIP_UDP_CHECKSUMS */ -#endif /* UIP_ARCH_CHKSUM */ -/*---------------------------------------------------------------------------*/ -void -uip_init(void) -{ - for(c = 0; c < UIP_LISTENPORTS; ++c) { - uip_listenports[c] = 0; - } - for(c = 0; c < UIP_CONNS; ++c) { - uip_conns[c].tcpstateflags = UIP_CLOSED; - } -#if UIP_ACTIVE_OPEN - lastport = 1024; -#endif /* UIP_ACTIVE_OPEN */ - -#if UIP_UDP - for(c = 0; c < UIP_UDP_CONNS; ++c) { - uip_udp_conns[c].lport = 0; - } -#endif /* UIP_UDP */ - - - /* IPv4 initialization. */ -#if UIP_FIXEDADDR == 0 - /* uip_hostaddr[0] = uip_hostaddr[1] = 0;*/ -#endif /* UIP_FIXEDADDR */ - -} -/*---------------------------------------------------------------------------*/ -#if UIP_ACTIVE_OPEN -struct uip_conn * -uip_connect(uip_ipaddr_t *ripaddr, u16_t rport) -{ - register struct uip_conn *conn, *cconn; - - /* Find an unused local port. */ - again: - ++lastport; - - if(lastport >= 32000) { - lastport = 4096; - } - - /* Check if this port is already in use, and if so try to find - another one. */ - for(c = 0; c < UIP_CONNS; ++c) { - conn = &uip_conns[c]; - if(conn->tcpstateflags != UIP_CLOSED && - conn->lport == htons(lastport)) { - goto again; - } - } - - conn = 0; - for(c = 0; c < UIP_CONNS; ++c) { - cconn = &uip_conns[c]; - if(cconn->tcpstateflags == UIP_CLOSED) { - conn = cconn; - break; - } - if(cconn->tcpstateflags == UIP_TIME_WAIT) { - if(conn == 0 || - cconn->timer > conn->timer) { - conn = cconn; - } - } - } - - if(conn == 0) { - return 0; - } - - conn->tcpstateflags = UIP_SYN_SENT; - - conn->snd_nxt[0] = iss[0]; - conn->snd_nxt[1] = iss[1]; - conn->snd_nxt[2] = iss[2]; - conn->snd_nxt[3] = iss[3]; - - conn->initialmss = conn->mss = UIP_TCP_MSS; - - conn->len = 1; /* TCP length of the SYN is one. */ - conn->nrtx = 0; - conn->timer = 1; /* Send the SYN next time around. */ - conn->rto = UIP_RTO; - conn->sa = 0; - conn->sv = 16; /* Initial value of the RTT variance. */ - conn->lport = htons(lastport); - conn->rport = rport; - uip_ipaddr_copy(&conn->ripaddr, ripaddr); - - return conn; -} -#endif /* UIP_ACTIVE_OPEN */ -/*---------------------------------------------------------------------------*/ -#if UIP_UDP -struct uip_udp_conn * -uip_udp_new(uip_ipaddr_t *ripaddr, u16_t rport) -{ - register struct uip_udp_conn *conn; - - /* Find an unused local port. */ - again: - ++lastport; - - if(lastport >= 32000) { - lastport = 4096; - } - - for(c = 0; c < UIP_UDP_CONNS; ++c) { - if(uip_udp_conns[c].lport == htons(lastport)) { - goto again; - } - } - - - conn = 0; - for(c = 0; c < UIP_UDP_CONNS; ++c) { - if(uip_udp_conns[c].lport == 0) { - conn = &uip_udp_conns[c]; - break; - } - } - - if(conn == 0) { - return 0; - } - - conn->lport = HTONS(lastport); - conn->rport = rport; - if(ripaddr == NULL) { - memset(conn->ripaddr, 0, sizeof(uip_ipaddr_t)); - } else { - uip_ipaddr_copy(&conn->ripaddr, ripaddr); - } - conn->ttl = UIP_TTL; - - return conn; -} -#endif /* UIP_UDP */ -/*---------------------------------------------------------------------------*/ -void -uip_unlisten(u16_t port) -{ - for(c = 0; c < UIP_LISTENPORTS; ++c) { - if(uip_listenports[c] == port) { - uip_listenports[c] = 0; - return; - } - } -} -/*---------------------------------------------------------------------------*/ -void -uip_listen(u16_t port) -{ - for(c = 0; c < UIP_LISTENPORTS; ++c) { - if(uip_listenports[c] == 0) { - uip_listenports[c] = port; - return; - } - } -} -/*---------------------------------------------------------------------------*/ -/* XXX: IP fragment reassembly: not well-tested. */ - -#if UIP_REASSEMBLY && !UIP_CONF_IPV6 -#define UIP_REASS_BUFSIZE (UIP_BUFSIZE - UIP_LLH_LEN) -static u8_t uip_reassbuf[UIP_REASS_BUFSIZE]; -static u8_t uip_reassbitmap[UIP_REASS_BUFSIZE / (8 * 8)]; -static const u8_t bitmap_bits[8] = {0xff, 0x7f, 0x3f, 0x1f, - 0x0f, 0x07, 0x03, 0x01}; -static u16_t uip_reasslen; -static u8_t uip_reassflags; -#define UIP_REASS_FLAG_LASTFRAG 0x01 -static u8_t uip_reasstmr; - -#define IP_MF 0x20 - -static u8_t -uip_reass(void) -{ - u16_t offset, len; - u16_t i; - - /* If ip_reasstmr is zero, no packet is present in the buffer, so we - write the IP header of the fragment into the reassembly - buffer. The timer is updated with the maximum age. */ - if(uip_reasstmr == 0) { - memcpy(uip_reassbuf, &BUF->vhl, UIP_IPH_LEN); - uip_reasstmr = UIP_REASS_MAXAGE; - uip_reassflags = 0; - /* Clear the bitmap. */ - memset(uip_reassbitmap, 0, sizeof(uip_reassbitmap)); - } - - /* Check if the incoming fragment matches the one currently present - in the reasembly buffer. If so, we proceed with copying the - fragment into the buffer. */ - if(BUF->srcipaddr[0] == FBUF->srcipaddr[0] && - BUF->srcipaddr[1] == FBUF->srcipaddr[1] && - BUF->destipaddr[0] == FBUF->destipaddr[0] && - BUF->destipaddr[1] == FBUF->destipaddr[1] && - BUF->ipid[0] == FBUF->ipid[0] && - BUF->ipid[1] == FBUF->ipid[1]) { - - len = (BUF->len[0] << 8) + BUF->len[1] - (BUF->vhl & 0x0f) * 4; - offset = (((BUF->ipoffset[0] & 0x3f) << 8) + BUF->ipoffset[1]) * 8; - - /* If the offset or the offset + fragment length overflows the - reassembly buffer, we discard the entire packet. */ - if(offset > UIP_REASS_BUFSIZE || - offset + len > UIP_REASS_BUFSIZE) { - uip_reasstmr = 0; - goto nullreturn; - } - - /* Copy the fragment into the reassembly buffer, at the right - offset. */ - memcpy(&uip_reassbuf[UIP_IPH_LEN + offset], - (char *)BUF + (int)((BUF->vhl & 0x0f) * 4), - len); - - /* Update the bitmap. */ - if(offset / (8 * 8) == (offset + len) / (8 * 8)) { - /* If the two endpoints are in the same byte, we only update - that byte. */ - - uip_reassbitmap[offset / (8 * 8)] |= - bitmap_bits[(offset / 8 ) & 7] & - ~bitmap_bits[((offset + len) / 8 ) & 7]; - } else { - /* If the two endpoints are in different bytes, we update the - bytes in the endpoints and fill the stuff inbetween with - 0xff. */ - uip_reassbitmap[offset / (8 * 8)] |= - bitmap_bits[(offset / 8 ) & 7]; - for(i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) { - uip_reassbitmap[i] = 0xff; - } - uip_reassbitmap[(offset + len) / (8 * 8)] |= - ~bitmap_bits[((offset + len) / 8 ) & 7]; - } - - /* If this fragment has the More Fragments flag set to zero, we - know that this is the last fragment, so we can calculate the - size of the entire packet. We also set the - IP_REASS_FLAG_LASTFRAG flag to indicate that we have received - the final fragment. */ - - if((BUF->ipoffset[0] & IP_MF) == 0) { - uip_reassflags |= UIP_REASS_FLAG_LASTFRAG; - uip_reasslen = offset + len; - } - - /* Finally, we check if we have a full packet in the buffer. We do - this by checking if we have the last fragment and if all bits - in the bitmap are set. */ - if(uip_reassflags & UIP_REASS_FLAG_LASTFRAG) { - /* Check all bytes up to and including all but the last byte in - the bitmap. */ - for(i = 0; i < uip_reasslen / (8 * 8) - 1; ++i) { - if(uip_reassbitmap[i] != 0xff) { - goto nullreturn; - } - } - /* Check the last byte in the bitmap. It should contain just the - right amount of bits. */ - if(uip_reassbitmap[uip_reasslen / (8 * 8)] != - (u8_t)~bitmap_bits[uip_reasslen / 8 & 7]) { - goto nullreturn; - } - - /* If we have come this far, we have a full packet in the - buffer, so we allocate a pbuf and copy the packet into it. We - also reset the timer. */ - uip_reasstmr = 0; - memcpy(BUF, FBUF, uip_reasslen); - - /* Pretend to be a "normal" (i.e., not fragmented) IP packet - from now on. */ - BUF->ipoffset[0] = BUF->ipoffset[1] = 0; - BUF->len[0] = uip_reasslen >> 8; - BUF->len[1] = uip_reasslen & 0xff; - BUF->ipchksum = 0; - BUF->ipchksum = ~(uip_ipchksum()); - - return uip_reasslen; - } - } - - nullreturn: - return 0; -} -#endif /* UIP_REASSEMBLY */ -/*---------------------------------------------------------------------------*/ -static void -uip_add_rcv_nxt(u16_t n) -{ - uip_add32(uip_conn->rcv_nxt, n); - uip_conn->rcv_nxt[0] = uip_acc32[0]; - uip_conn->rcv_nxt[1] = uip_acc32[1]; - uip_conn->rcv_nxt[2] = uip_acc32[2]; - uip_conn->rcv_nxt[3] = uip_acc32[3]; -} -/*---------------------------------------------------------------------------*/ -void -uip_process(u8_t flag) -{ - register struct uip_conn *uip_connr = uip_conn; - -#if UIP_UDP - if(flag == UIP_UDP_SEND_CONN) { - goto udp_send; - } -#endif /* UIP_UDP */ - - uip_sappdata = uip_appdata = &uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN]; - - /* Check if we were invoked because of a poll request for a - particular connection. */ - if(flag == UIP_POLL_REQUEST) { - if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED && - !uip_outstanding(uip_connr)) { - uip_flags = UIP_POLL; - UIP_APPCALL(); - goto appsend; - } - goto drop; - - /* Check if we were invoked because of the perodic timer fireing. */ - } else if(flag == UIP_TIMER) { -#if UIP_REASSEMBLY - if(uip_reasstmr != 0) { - --uip_reasstmr; - } -#endif /* UIP_REASSEMBLY */ - /* Increase the initial sequence number. */ - if(++iss[3] == 0) { - if(++iss[2] == 0) { - if(++iss[1] == 0) { - ++iss[0]; - } - } - } - - /* Reset the length variables. */ - uip_len = 0; - uip_slen = 0; - - /* Check if the connection is in a state in which we simply wait - for the connection to time out. If so, we increase the - connection's timer and remove the connection if it times - out. */ - if(uip_connr->tcpstateflags == UIP_TIME_WAIT || - uip_connr->tcpstateflags == UIP_FIN_WAIT_2) { - ++(uip_connr->timer); - if(uip_connr->timer == UIP_TIME_WAIT_TIMEOUT) { - uip_connr->tcpstateflags = UIP_CLOSED; - } - } else if(uip_connr->tcpstateflags != UIP_CLOSED) { - /* If the connection has outstanding data, we increase the - connection's timer and see if it has reached the RTO value - in which case we retransmit. */ - if(uip_outstanding(uip_connr)) { - if(uip_connr->timer-- == 0) { - if(uip_connr->nrtx == UIP_MAXRTX || - ((uip_connr->tcpstateflags == UIP_SYN_SENT || - uip_connr->tcpstateflags == UIP_SYN_RCVD) && - uip_connr->nrtx == UIP_MAXSYNRTX)) { - uip_connr->tcpstateflags = UIP_CLOSED; - - /* We call UIP_APPCALL() with uip_flags set to - UIP_TIMEDOUT to inform the application that the - connection has timed out. */ - uip_flags = UIP_TIMEDOUT; - UIP_APPCALL(); - - /* We also send a reset packet to the remote host. */ - BUF->flags = TCP_RST | TCP_ACK; - goto tcp_send_nodata; - } - - /* Exponential backoff. */ - uip_connr->timer = UIP_RTO << (uip_connr->nrtx > 4? - 4: - uip_connr->nrtx); - ++(uip_connr->nrtx); - - /* Ok, so we need to retransmit. We do this differently - depending on which state we are in. In ESTABLISHED, we - call upon the application so that it may prepare the - data for the retransmit. In SYN_RCVD, we resend the - SYNACK that we sent earlier and in LAST_ACK we have to - retransmit our FINACK. */ - UIP_STAT(++uip_stat.tcp.rexmit); - switch(uip_connr->tcpstateflags & UIP_TS_MASK) { - case UIP_SYN_RCVD: - /* In the SYN_RCVD state, we should retransmit our - SYNACK. */ - goto tcp_send_synack; - -#if UIP_ACTIVE_OPEN - case UIP_SYN_SENT: - /* In the SYN_SENT state, we retransmit out SYN. */ - BUF->flags = 0; - goto tcp_send_syn; -#endif /* UIP_ACTIVE_OPEN */ - - case UIP_ESTABLISHED: - /* In the ESTABLISHED state, we call upon the application - to do the actual retransmit after which we jump into - the code for sending out the packet (the apprexmit - label). */ - uip_flags = UIP_REXMIT; - UIP_APPCALL(); - goto apprexmit; - - case UIP_FIN_WAIT_1: - case UIP_CLOSING: - case UIP_LAST_ACK: - /* In all these states we should retransmit a FINACK. */ - goto tcp_send_finack; - - } - } - } else if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) { - /* If there was no need for a retransmission, we poll the - application for new data. */ - uip_flags = UIP_POLL; - UIP_APPCALL(); - goto appsend; - } - } - goto drop; - } -#if UIP_UDP - if(flag == UIP_UDP_TIMER) { - if(uip_udp_conn->lport != 0) { - uip_conn = NULL; - uip_sappdata = uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; - uip_len = uip_slen = 0; - uip_flags = UIP_POLL; - UIP_UDP_APPCALL(); - goto udp_send; - } else { - goto drop; - } - } -#endif - - /* This is where the input processing starts. */ - UIP_STAT(++uip_stat.ip.recv); - - /* Start of IP input header processing code. */ - -#if UIP_CONF_IPV6 - /* Check validity of the IP header. */ - if((BUF->vtc & 0xf0) != 0x60) { /* IP version and header length. */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.vhlerr); - UIP_LOG("ipv6: invalid version."); - goto drop; - } -#else /* UIP_CONF_IPV6 */ - /* Check validity of the IP header. */ - if(BUF->vhl != 0x45) { /* IP version and header length. */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.vhlerr); - UIP_LOG("ip: invalid version or header length."); - goto drop; - } -#endif /* UIP_CONF_IPV6 */ - - /* Check the size of the packet. If the size reported to us in - uip_len is smaller the size reported in the IP header, we assume - that the packet has been corrupted in transit. If the size of - uip_len is larger than the size reported in the IP packet header, - the packet has been padded and we set uip_len to the correct - value.. */ - - if((BUF->len[0] << 8) + BUF->len[1] <= uip_len) { - uip_len = (BUF->len[0] << 8) + BUF->len[1]; -#if UIP_CONF_IPV6 - uip_len += 40; /* The length reported in the IPv6 header is the - length of the payload that follows the - header. However, uIP uses the uip_len variable - for holding the size of the entire packet, - including the IP header. For IPv4 this is not a - problem as the length field in the IPv4 header - contains the length of the entire packet. But - for IPv6 we need to add the size of the IPv6 - header (40 bytes). */ -#endif /* UIP_CONF_IPV6 */ - } else { - UIP_LOG("ip: packet shorter than reported in IP header."); - goto drop; - } - -#if !UIP_CONF_IPV6 - /* Check the fragment flag. */ - if((BUF->ipoffset[0] & 0x3f) != 0 || - BUF->ipoffset[1] != 0) { -#if UIP_REASSEMBLY - uip_len = uip_reass(); - if(uip_len == 0) { - goto drop; - } -#else /* UIP_REASSEMBLY */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.fragerr); - UIP_LOG("ip: fragment dropped."); - goto drop; -#endif /* UIP_REASSEMBLY */ - } -#endif /* UIP_CONF_IPV6 */ - - if(uip_ipaddr_cmp(uip_hostaddr, all_zeroes_addr)) { - /* If we are configured to use ping IP address configuration and - hasn't been assigned an IP address yet, we accept all ICMP - packets. */ -#if UIP_PINGADDRCONF && !UIP_CONF_IPV6 - if(BUF->proto == UIP_PROTO_ICMP) { - UIP_LOG("ip: possible ping config packet received."); - goto icmp_input; - } else { - UIP_LOG("ip: packet dropped since no address assigned."); - goto drop; - } -#endif /* UIP_PINGADDRCONF */ - - } else { - /* If IP broadcast support is configured, we check for a broadcast - UDP packet, which may be destined to us. */ -#if UIP_BROADCAST - DEBUG_PRINTF("UDP IP checksum 0x%04x\n", uip_ipchksum()); - if(BUF->proto == UIP_PROTO_UDP && - uip_ipaddr_cmp(BUF->destipaddr, all_ones_addr) - /*&& - uip_ipchksum() == 0xffff*/) { - goto udp_input; - } -#endif /* UIP_BROADCAST */ - - /* Check if the packet is destined for our IP address. */ -#if !UIP_CONF_IPV6 - if(!uip_ipaddr_cmp(BUF->destipaddr, uip_hostaddr)) { - UIP_STAT(++uip_stat.ip.drop); - goto drop; - } -#else /* UIP_CONF_IPV6 */ - /* For IPv6, packet reception is a little trickier as we need to - make sure that we listen to certain multicast addresses (all - hosts multicast address, and the solicited-node multicast - address) as well. However, we will cheat here and accept all - multicast packets that are sent to the ff02::/16 addresses. */ - if(!uip_ipaddr_cmp(BUF->destipaddr, uip_hostaddr) && - BUF->destipaddr[0] != HTONS(0xff02)) { - UIP_STAT(++uip_stat.ip.drop); - goto drop; - } -#endif /* UIP_CONF_IPV6 */ - } - -#if !UIP_CONF_IPV6 - if(uip_ipchksum() != 0xffff) { /* Compute and check the IP header - checksum. */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.chkerr); - UIP_LOG("ip: bad checksum."); - goto drop; - } -#endif /* UIP_CONF_IPV6 */ - - if(BUF->proto == UIP_PROTO_TCP) { /* Check for TCP packet. If so, - proceed with TCP input - processing. */ - goto tcp_input; - } - -#if UIP_UDP - if(BUF->proto == UIP_PROTO_UDP) { - goto udp_input; - } -#endif /* UIP_UDP */ - -#if !UIP_CONF_IPV6 - /* ICMPv4 processing code follows. */ - if(BUF->proto != UIP_PROTO_ICMP) { /* We only allow ICMP packets from - here. */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.protoerr); - UIP_LOG("ip: neither tcp nor icmp."); - goto drop; - } - -#if UIP_PINGADDRCONF - icmp_input: -#endif /* UIP_PINGADDRCONF */ - UIP_STAT(++uip_stat.icmp.recv); - - /* ICMP echo (i.e., ping) processing. This is simple, we only change - the ICMP type from ECHO to ECHO_REPLY and adjust the ICMP - checksum before we return the packet. */ - if(ICMPBUF->type != ICMP_ECHO) { - UIP_STAT(++uip_stat.icmp.drop); - UIP_STAT(++uip_stat.icmp.typeerr); - UIP_LOG("icmp: not icmp echo."); - goto drop; - } - - /* If we are configured to use ping IP address assignment, we use - the destination IP address of this ping packet and assign it to - ourself. */ -#if UIP_PINGADDRCONF - if((uip_hostaddr[0] | uip_hostaddr[1]) == 0) { - uip_hostaddr[0] = BUF->destipaddr[0]; - uip_hostaddr[1] = BUF->destipaddr[1]; - } -#endif /* UIP_PINGADDRCONF */ - - ICMPBUF->type = ICMP_ECHO_REPLY; - - if(ICMPBUF->icmpchksum >= HTONS(0xffff - (ICMP_ECHO << 8))) { - ICMPBUF->icmpchksum += HTONS(ICMP_ECHO << 8) + 1; - } else { - ICMPBUF->icmpchksum += HTONS(ICMP_ECHO << 8); - } - - /* Swap IP addresses. */ - uip_ipaddr_copy(BUF->destipaddr, BUF->srcipaddr); - uip_ipaddr_copy(BUF->srcipaddr, uip_hostaddr); - - UIP_STAT(++uip_stat.icmp.sent); - goto send; - - /* End of IPv4 input header processing code. */ -#else /* !UIP_CONF_IPV6 */ - - /* This is IPv6 ICMPv6 processing code. */ - DEBUG_PRINTF("icmp6_input: length %d\n", uip_len); - - if(BUF->proto != UIP_PROTO_ICMP6) { /* We only allow ICMPv6 packets from - here. */ - UIP_STAT(++uip_stat.ip.drop); - UIP_STAT(++uip_stat.ip.protoerr); - UIP_LOG("ip: neither tcp nor icmp6."); - goto drop; - } - - UIP_STAT(++uip_stat.icmp.recv); - - /* If we get a neighbor solicitation for our address we should send - a neighbor advertisement message back. */ - if(ICMPBUF->type == ICMP6_NEIGHBOR_SOLICITATION) { - if(uip_ipaddr_cmp(ICMPBUF->icmp6data, uip_hostaddr)) { - - if(ICMPBUF->options[0] == ICMP6_OPTION_SOURCE_LINK_ADDRESS) { - /* Save the sender's address in our neighbor list. */ - uip_neighbor_add(ICMPBUF->srcipaddr, &(ICMPBUF->options[2])); - } - - /* We should now send a neighbor advertisement back to where the - neighbor solicication came from. */ - ICMPBUF->type = ICMP6_NEIGHBOR_ADVERTISEMENT; - ICMPBUF->flags = ICMP6_FLAG_S; /* Solicited flag. */ - - ICMPBUF->reserved1 = ICMPBUF->reserved2 = ICMPBUF->reserved3 = 0; - - uip_ipaddr_copy(ICMPBUF->destipaddr, ICMPBUF->srcipaddr); - uip_ipaddr_copy(ICMPBUF->srcipaddr, uip_hostaddr); - ICMPBUF->options[0] = ICMP6_OPTION_TARGET_LINK_ADDRESS; - ICMPBUF->options[1] = 1; /* Options length, 1 = 8 bytes. */ - memcpy(&(ICMPBUF->options[2]), &uip_ethaddr, sizeof(uip_ethaddr)); - ICMPBUF->icmpchksum = 0; - ICMPBUF->icmpchksum = ~uip_icmp6chksum(); - goto send; - - } - goto drop; - } else if(ICMPBUF->type == ICMP6_ECHO) { - /* ICMP echo (i.e., ping) processing. This is simple, we only - change the ICMP type from ECHO to ECHO_REPLY and update the - ICMP checksum before we return the packet. */ - - ICMPBUF->type = ICMP6_ECHO_REPLY; - - uip_ipaddr_copy(BUF->destipaddr, BUF->srcipaddr); - uip_ipaddr_copy(BUF->srcipaddr, uip_hostaddr); - ICMPBUF->icmpchksum = 0; - ICMPBUF->icmpchksum = ~uip_icmp6chksum(); - - UIP_STAT(++uip_stat.icmp.sent); - goto send; - } else { - DEBUG_PRINTF("Unknown icmp6 message type %d\n", ICMPBUF->type); - UIP_STAT(++uip_stat.icmp.drop); - UIP_STAT(++uip_stat.icmp.typeerr); - UIP_LOG("icmp: unknown ICMP message."); - goto drop; - } - - /* End of IPv6 ICMP processing. */ - -#endif /* !UIP_CONF_IPV6 */ - -#if UIP_UDP - /* UDP input processing. */ - udp_input: - /* UDP processing is really just a hack. We don't do anything to the - UDP/IP headers, but let the UDP application do all the hard - work. If the application sets uip_slen, it has a packet to - send. */ -#if UIP_UDP_CHECKSUMS - uip_len = uip_len - UIP_IPUDPH_LEN; - uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; - if(UDPBUF->udpchksum != 0 && uip_udpchksum() != 0xffff) { - UIP_STAT(++uip_stat.udp.drop); - UIP_STAT(++uip_stat.udp.chkerr); - UIP_LOG("udp: bad checksum."); - goto drop; - } -#else /* UIP_UDP_CHECKSUMS */ - uip_len = uip_len - UIP_IPUDPH_LEN; -#endif /* UIP_UDP_CHECKSUMS */ - - /* Demultiplex this UDP packet between the UDP "connections". */ - for(uip_udp_conn = &uip_udp_conns[0]; - uip_udp_conn < &uip_udp_conns[UIP_UDP_CONNS]; - ++uip_udp_conn) { - /* If the local UDP port is non-zero, the connection is considered - to be used. If so, the local port number is checked against the - destination port number in the received packet. If the two port - numbers match, the remote port number is checked if the - connection is bound to a remote port. Finally, if the - connection is bound to a remote IP address, the source IP - address of the packet is checked. */ - if(uip_udp_conn->lport != 0 && - UDPBUF->destport == uip_udp_conn->lport && - (uip_udp_conn->rport == 0 || - UDPBUF->srcport == uip_udp_conn->rport) && - (uip_ipaddr_cmp(uip_udp_conn->ripaddr, all_zeroes_addr) || - uip_ipaddr_cmp(uip_udp_conn->ripaddr, all_ones_addr) || - uip_ipaddr_cmp(BUF->srcipaddr, uip_udp_conn->ripaddr))) { - goto udp_found; - } - } - UIP_LOG("udp: no matching connection found"); - goto drop; - - udp_found: - uip_conn = NULL; - uip_flags = UIP_NEWDATA; - uip_sappdata = uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; - uip_slen = 0; - UIP_UDP_APPCALL(); - udp_send: - if(uip_slen == 0) { - goto drop; - } - uip_len = uip_slen + UIP_IPUDPH_LEN; - -#if UIP_CONF_IPV6 - /* For IPv6, the IP length field does not include the IPv6 IP header - length. */ - BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); - BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); -#else /* UIP_CONF_IPV6 */ - BUF->len[0] = (uip_len >> 8); - BUF->len[1] = (uip_len & 0xff); -#endif /* UIP_CONF_IPV6 */ - - BUF->ttl = uip_udp_conn->ttl; - BUF->proto = UIP_PROTO_UDP; - - UDPBUF->udplen = HTONS(uip_slen + UIP_UDPH_LEN); - UDPBUF->udpchksum = 0; - - BUF->srcport = uip_udp_conn->lport; - BUF->destport = uip_udp_conn->rport; - - uip_ipaddr_copy(BUF->srcipaddr, uip_hostaddr); - uip_ipaddr_copy(BUF->destipaddr, uip_udp_conn->ripaddr); - - uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPTCPH_LEN]; - -#if UIP_UDP_CHECKSUMS - /* Calculate UDP checksum. */ - UDPBUF->udpchksum = ~(uip_udpchksum()); - if(UDPBUF->udpchksum == 0) { - UDPBUF->udpchksum = 0xffff; - } -#endif /* UIP_UDP_CHECKSUMS */ - - goto ip_send_nolen; -#endif /* UIP_UDP */ - - /* TCP input processing. */ - tcp_input: - UIP_STAT(++uip_stat.tcp.recv); - - /* Start of TCP input header processing code. */ - - if(uip_tcpchksum() != 0xffff) { /* Compute and check the TCP - checksum. */ - UIP_STAT(++uip_stat.tcp.drop); - UIP_STAT(++uip_stat.tcp.chkerr); - UIP_LOG("tcp: bad checksum."); - goto drop; - } - - - /* Demultiplex this segment. */ - /* First check any active connections. */ - for(uip_connr = &uip_conns[0]; uip_connr <= &uip_conns[UIP_CONNS - 1]; - ++uip_connr) { - if(uip_connr->tcpstateflags != UIP_CLOSED && - BUF->destport == uip_connr->lport && - BUF->srcport == uip_connr->rport && - uip_ipaddr_cmp(BUF->srcipaddr, uip_connr->ripaddr)) { - goto found; - } - } - - /* If we didn't find and active connection that expected the packet, - either this packet is an old duplicate, or this is a SYN packet - destined for a connection in LISTEN. If the SYN flag isn't set, - it is an old packet and we send a RST. */ - if((BUF->flags & TCP_CTL) != TCP_SYN) { - goto reset; - } - - tmp16 = BUF->destport; - /* Next, check listening connections. */ - for(c = 0; c < UIP_LISTENPORTS; ++c) { - if(tmp16 == uip_listenports[c]) - goto found_listen; - } - - /* No matching connection found, so we send a RST packet. */ - UIP_STAT(++uip_stat.tcp.synrst); - reset: - - /* We do not send resets in response to resets. */ - if(BUF->flags & TCP_RST) { - goto drop; - } - - UIP_STAT(++uip_stat.tcp.rst); - - BUF->flags = TCP_RST | TCP_ACK; - uip_len = UIP_IPTCPH_LEN; - BUF->tcpoffset = 5 << 4; - - /* Flip the seqno and ackno fields in the TCP header. */ - c = BUF->seqno[3]; - BUF->seqno[3] = BUF->ackno[3]; - BUF->ackno[3] = c; - - c = BUF->seqno[2]; - BUF->seqno[2] = BUF->ackno[2]; - BUF->ackno[2] = c; - - c = BUF->seqno[1]; - BUF->seqno[1] = BUF->ackno[1]; - BUF->ackno[1] = c; - - c = BUF->seqno[0]; - BUF->seqno[0] = BUF->ackno[0]; - BUF->ackno[0] = c; - - /* We also have to increase the sequence number we are - acknowledging. If the least significant byte overflowed, we need - to propagate the carry to the other bytes as well. */ - if(++BUF->ackno[3] == 0) { - if(++BUF->ackno[2] == 0) { - if(++BUF->ackno[1] == 0) { - ++BUF->ackno[0]; - } - } - } - - /* Swap port numbers. */ - tmp16 = BUF->srcport; - BUF->srcport = BUF->destport; - BUF->destport = tmp16; - - /* Swap IP addresses. */ - uip_ipaddr_copy(BUF->destipaddr, BUF->srcipaddr); - uip_ipaddr_copy(BUF->srcipaddr, uip_hostaddr); - - /* And send out the RST packet! */ - goto tcp_send_noconn; - - /* This label will be jumped to if we matched the incoming packet - with a connection in LISTEN. In that case, we should create a new - connection and send a SYNACK in return. */ - found_listen: - /* First we check if there are any connections avaliable. Unused - connections are kept in the same table as used connections, but - unused ones have the tcpstate set to CLOSED. Also, connections in - TIME_WAIT are kept track of and we'll use the oldest one if no - CLOSED connections are found. Thanks to Eddie C. Dost for a very - nice algorithm for the TIME_WAIT search. */ - uip_connr = 0; - for(c = 0; c < UIP_CONNS; ++c) { - if(uip_conns[c].tcpstateflags == UIP_CLOSED) { - uip_connr = &uip_conns[c]; - break; - } - if(uip_conns[c].tcpstateflags == UIP_TIME_WAIT) { - if(uip_connr == 0 || - uip_conns[c].timer > uip_connr->timer) { - uip_connr = &uip_conns[c]; - } - } - } - - if(uip_connr == 0) { - /* All connections are used already, we drop packet and hope that - the remote end will retransmit the packet at a time when we - have more spare connections. */ - UIP_STAT(++uip_stat.tcp.syndrop); - UIP_LOG("tcp: found no unused connections."); - goto drop; - } - uip_conn = uip_connr; - - /* Fill in the necessary fields for the new connection. */ - uip_connr->rto = uip_connr->timer = UIP_RTO; - uip_connr->sa = 0; - uip_connr->sv = 4; - uip_connr->nrtx = 0; - uip_connr->lport = BUF->destport; - uip_connr->rport = BUF->srcport; - uip_ipaddr_copy(uip_connr->ripaddr, BUF->srcipaddr); - uip_connr->tcpstateflags = UIP_SYN_RCVD; - - uip_connr->snd_nxt[0] = iss[0]; - uip_connr->snd_nxt[1] = iss[1]; - uip_connr->snd_nxt[2] = iss[2]; - uip_connr->snd_nxt[3] = iss[3]; - uip_connr->len = 1; - - /* rcv_nxt should be the seqno from the incoming packet + 1. */ - uip_connr->rcv_nxt[3] = BUF->seqno[3]; - uip_connr->rcv_nxt[2] = BUF->seqno[2]; - uip_connr->rcv_nxt[1] = BUF->seqno[1]; - uip_connr->rcv_nxt[0] = BUF->seqno[0]; - uip_add_rcv_nxt(1); - - /* Parse the TCP MSS option, if present. */ - if((BUF->tcpoffset & 0xf0) > 0x50) { - for(c = 0; c < ((BUF->tcpoffset >> 4) - 5) << 2 ;) { - opt = uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + c]; - if(opt == TCP_OPT_END) { - /* End of options. */ - break; - } else if(opt == TCP_OPT_NOOP) { - ++c; - /* NOP option. */ - } else if(opt == TCP_OPT_MSS && - uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) { - /* An MSS option with the right option length. */ - tmp16 = ((u16_t)uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) | - (u16_t)uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN + 3 + c]; - uip_connr->initialmss = uip_connr->mss = - tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16; - - /* And we are done processing options. */ - break; - } else { - /* All other options have a length field, so that we easily - can skip past them. */ - if(uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) { - /* If the length field is zero, the options are malformed - and we don't process them further. */ - break; - } - c += uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c]; - } - } - } - - /* Our response will be a SYNACK. */ -#if UIP_ACTIVE_OPEN - tcp_send_synack: - BUF->flags = TCP_ACK; - - tcp_send_syn: - BUF->flags |= TCP_SYN; -#else /* UIP_ACTIVE_OPEN */ - tcp_send_synack: - BUF->flags = TCP_SYN | TCP_ACK; -#endif /* UIP_ACTIVE_OPEN */ - - /* We send out the TCP Maximum Segment Size option with our - SYNACK. */ - BUF->optdata[0] = TCP_OPT_MSS; - BUF->optdata[1] = TCP_OPT_MSS_LEN; - BUF->optdata[2] = (UIP_TCP_MSS) / 256; - BUF->optdata[3] = (UIP_TCP_MSS) & 255; - uip_len = UIP_IPTCPH_LEN + TCP_OPT_MSS_LEN; - BUF->tcpoffset = ((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4) << 4; - goto tcp_send; - - /* This label will be jumped to if we found an active connection. */ - found: - uip_conn = uip_connr; - uip_flags = 0; - /* We do a very naive form of TCP reset processing; we just accept - any RST and kill our connection. We should in fact check if the - sequence number of this reset is wihtin our advertised window - before we accept the reset. */ - if(BUF->flags & TCP_RST) { - uip_connr->tcpstateflags = UIP_CLOSED; - UIP_LOG("tcp: got reset, aborting connection."); - uip_flags = UIP_ABORT; - UIP_APPCALL(); - goto drop; - } - /* Calculated the length of the data, if the application has sent - any data to us. */ - c = (BUF->tcpoffset >> 4) << 2; - /* uip_len will contain the length of the actual TCP data. This is - calculated by subtracing the length of the TCP header (in - c) and the length of the IP header (20 bytes). */ - uip_len = uip_len - c - UIP_IPH_LEN; - - /* First, check if the sequence number of the incoming packet is - what we're expecting next. If not, we send out an ACK with the - correct numbers in. */ - if(!(((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_SENT) && - ((BUF->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)))) { - if((uip_len > 0 || ((BUF->flags & (TCP_SYN | TCP_FIN)) != 0)) && - (BUF->seqno[0] != uip_connr->rcv_nxt[0] || - BUF->seqno[1] != uip_connr->rcv_nxt[1] || - BUF->seqno[2] != uip_connr->rcv_nxt[2] || - BUF->seqno[3] != uip_connr->rcv_nxt[3])) { - goto tcp_send_ack; - } - } - - /* Next, check if the incoming segment acknowledges any outstanding - data. If so, we update the sequence number, reset the length of - the outstanding data, calculate RTT estimations, and reset the - retransmission timer. */ - if((BUF->flags & TCP_ACK) && uip_outstanding(uip_connr)) { - uip_add32(uip_connr->snd_nxt, uip_connr->len); - - if(BUF->ackno[0] == uip_acc32[0] && - BUF->ackno[1] == uip_acc32[1] && - BUF->ackno[2] == uip_acc32[2] && - BUF->ackno[3] == uip_acc32[3]) { - /* Update sequence number. */ - uip_connr->snd_nxt[0] = uip_acc32[0]; - uip_connr->snd_nxt[1] = uip_acc32[1]; - uip_connr->snd_nxt[2] = uip_acc32[2]; - uip_connr->snd_nxt[3] = uip_acc32[3]; - - - /* Do RTT estimation, unless we have done retransmissions. */ - if(uip_connr->nrtx == 0) { - signed char m; - m = uip_connr->rto - uip_connr->timer; - /* This is taken directly from VJs original code in his paper */ - m = m - (uip_connr->sa >> 3); - uip_connr->sa += m; - if(m < 0) { - m = -m; - } - m = m - (uip_connr->sv >> 2); - uip_connr->sv += m; - uip_connr->rto = (uip_connr->sa >> 3) + uip_connr->sv; - - } - /* Set the acknowledged flag. */ - uip_flags = UIP_ACKDATA; - /* Reset the retransmission timer. */ - uip_connr->timer = uip_connr->rto; - - /* Reset length of outstanding data. */ - uip_connr->len = 0; - } - - } - - /* Do different things depending on in what state the connection is. */ - switch(uip_connr->tcpstateflags & UIP_TS_MASK) { - /* CLOSED and LISTEN are not handled here. CLOSE_WAIT is not - implemented, since we force the application to close when the - peer sends a FIN (hence the application goes directly from - ESTABLISHED to LAST_ACK). */ - case UIP_SYN_RCVD: - /* In SYN_RCVD we have sent out a SYNACK in response to a SYN, and - we are waiting for an ACK that acknowledges the data we sent - out the last time. Therefore, we want to have the UIP_ACKDATA - flag set. If so, we enter the ESTABLISHED state. */ - if(uip_flags & UIP_ACKDATA) { - uip_connr->tcpstateflags = UIP_ESTABLISHED; - uip_flags = UIP_CONNECTED; - uip_connr->len = 0; - if(uip_len > 0) { - uip_flags |= UIP_NEWDATA; - uip_add_rcv_nxt(uip_len); - } - uip_slen = 0; - UIP_APPCALL(); - goto appsend; - } - goto drop; -#if UIP_ACTIVE_OPEN - case UIP_SYN_SENT: - /* In SYN_SENT, we wait for a SYNACK that is sent in response to - our SYN. The rcv_nxt is set to sequence number in the SYNACK - plus one, and we send an ACK. We move into the ESTABLISHED - state. */ - if((uip_flags & UIP_ACKDATA) && - (BUF->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)) { - - /* Parse the TCP MSS option, if present. */ - if((BUF->tcpoffset & 0xf0) > 0x50) { - for(c = 0; c < ((BUF->tcpoffset >> 4) - 5) << 2 ;) { - opt = uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN + c]; - if(opt == TCP_OPT_END) { - /* End of options. */ - break; - } else if(opt == TCP_OPT_NOOP) { - ++c; - /* NOP option. */ - } else if(opt == TCP_OPT_MSS && - uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) { - /* An MSS option with the right option length. */ - tmp16 = (uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) | - uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 3 + c]; - uip_connr->initialmss = - uip_connr->mss = tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16; - - /* And we are done processing options. */ - break; - } else { - /* All other options have a length field, so that we easily - can skip past them. */ - if(uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) { - /* If the length field is zero, the options are malformed - and we don't process them further. */ - break; - } - c += uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c]; - } - } - } - uip_connr->tcpstateflags = UIP_ESTABLISHED; - uip_connr->rcv_nxt[0] = BUF->seqno[0]; - uip_connr->rcv_nxt[1] = BUF->seqno[1]; - uip_connr->rcv_nxt[2] = BUF->seqno[2]; - uip_connr->rcv_nxt[3] = BUF->seqno[3]; - uip_add_rcv_nxt(1); - uip_flags = UIP_CONNECTED | UIP_NEWDATA; - uip_connr->len = 0; - uip_len = 0; - uip_slen = 0; - UIP_APPCALL(); - goto appsend; - } - /* Inform the application that the connection failed */ - uip_flags = UIP_ABORT; - UIP_APPCALL(); - /* The connection is closed after we send the RST */ - uip_conn->tcpstateflags = UIP_CLOSED; - goto reset; -#endif /* UIP_ACTIVE_OPEN */ - - case UIP_ESTABLISHED: - /* In the ESTABLISHED state, we call upon the application to feed - data into the uip_buf. If the UIP_ACKDATA flag is set, the - application should put new data into the buffer, otherwise we are - retransmitting an old segment, and the application should put that - data into the buffer. - - If the incoming packet is a FIN, we should close the connection on - this side as well, and we send out a FIN and enter the LAST_ACK - state. We require that there is no outstanding data; otherwise the - sequence numbers will be screwed up. */ - - if(BUF->flags & TCP_FIN && !(uip_connr->tcpstateflags & UIP_STOPPED)) { - if(uip_outstanding(uip_connr)) { - goto drop; - } - uip_add_rcv_nxt(1 + uip_len); - uip_flags |= UIP_CLOSE; - if(uip_len > 0) { - uip_flags |= UIP_NEWDATA; - } - UIP_APPCALL(); - uip_connr->len = 1; - uip_connr->tcpstateflags = UIP_LAST_ACK; - uip_connr->nrtx = 0; - tcp_send_finack: - BUF->flags = TCP_FIN | TCP_ACK; - goto tcp_send_nodata; - } - - /* Check the URG flag. If this is set, the segment carries urgent - data that we must pass to the application. */ - if((BUF->flags & TCP_URG) != 0) { -#if UIP_URGDATA > 0 - uip_urglen = (BUF->urgp[0] << 8) | BUF->urgp[1]; - if(uip_urglen > uip_len) { - /* There is more urgent data in the next segment to come. */ - uip_urglen = uip_len; - } - uip_add_rcv_nxt(uip_urglen); - uip_len -= uip_urglen; - uip_urgdata = uip_appdata; - uip_appdata += uip_urglen; - } else { - uip_urglen = 0; -#else /* UIP_URGDATA > 0 */ - uip_appdata = ((char *)uip_appdata) + ((BUF->urgp[0] << 8) | BUF->urgp[1]); - uip_len -= (BUF->urgp[0] << 8) | BUF->urgp[1]; -#endif /* UIP_URGDATA > 0 */ - } - - /* If uip_len > 0 we have TCP data in the packet, and we flag this - by setting the UIP_NEWDATA flag and update the sequence number - we acknowledge. If the application has stopped the dataflow - using uip_stop(), we must not accept any data packets from the - remote host. */ - if(uip_len > 0 && !(uip_connr->tcpstateflags & UIP_STOPPED)) { - uip_flags |= UIP_NEWDATA; - uip_add_rcv_nxt(uip_len); - } - - /* Check if the available buffer space advertised by the other end - is smaller than the initial MSS for this connection. If so, we - set the current MSS to the window size to ensure that the - application does not send more data than the other end can - handle. - - If the remote host advertises a zero window, we set the MSS to - the initial MSS so that the application will send an entire MSS - of data. This data will not be acknowledged by the receiver, - and the application will retransmit it. This is called the - "persistent timer" and uses the retransmission mechanim. - */ - tmp16 = ((u16_t)BUF->wnd[0] << 8) + (u16_t)BUF->wnd[1]; - if(tmp16 > uip_connr->initialmss || - tmp16 == 0) { - tmp16 = uip_connr->initialmss; - } - uip_connr->mss = tmp16; - - /* If this packet constitutes an ACK for outstanding data (flagged - by the UIP_ACKDATA flag, we should call the application since it - might want to send more data. If the incoming packet had data - from the peer (as flagged by the UIP_NEWDATA flag), the - application must also be notified. - - When the application is called, the global variable uip_len - contains the length of the incoming data. The application can - access the incoming data through the global pointer - uip_appdata, which usually points UIP_IPTCPH_LEN + UIP_LLH_LEN - bytes into the uip_buf array. - - If the application wishes to send any data, this data should be - put into the uip_appdata and the length of the data should be - put into uip_len. If the application don't have any data to - send, uip_len must be set to 0. */ - if(uip_flags & (UIP_NEWDATA | UIP_ACKDATA)) { - uip_slen = 0; - UIP_APPCALL(); - - appsend: - - if(uip_flags & UIP_ABORT) { - uip_slen = 0; - uip_connr->tcpstateflags = UIP_CLOSED; - BUF->flags = TCP_RST | TCP_ACK; - goto tcp_send_nodata; - } - - if(uip_flags & UIP_CLOSE) { - uip_slen = 0; - uip_connr->len = 1; - uip_connr->tcpstateflags = UIP_FIN_WAIT_1; - uip_connr->nrtx = 0; - BUF->flags = TCP_FIN | TCP_ACK; - goto tcp_send_nodata; - } - - /* If uip_slen > 0, the application has data to be sent. */ - if(uip_slen > 0) { - - /* If the connection has acknowledged data, the contents of - the ->len variable should be discarded. */ - if((uip_flags & UIP_ACKDATA) != 0) { - uip_connr->len = 0; - } - - /* If the ->len variable is non-zero the connection has - already data in transit and cannot send anymore right - now. */ - if(uip_connr->len == 0) { - - /* The application cannot send more than what is allowed by - the mss (the minumum of the MSS and the available - window). */ - if(uip_slen > uip_connr->mss) { - uip_slen = uip_connr->mss; - } - - /* Remember how much data we send out now so that we know - when everything has been acknowledged. */ - uip_connr->len = uip_slen; - } else { - - /* If the application already had unacknowledged data, we - make sure that the application does not send (i.e., - retransmit) out more than it previously sent out. */ - uip_slen = uip_connr->len; - } - } - uip_connr->nrtx = 0; - apprexmit: - uip_appdata = uip_sappdata; - - /* If the application has data to be sent, or if the incoming - packet had new data in it, we must send out a packet. */ - if(uip_slen > 0 && uip_connr->len > 0) { - /* Add the length of the IP and TCP headers. */ - uip_len = uip_connr->len + UIP_TCPIP_HLEN; - /* We always set the ACK flag in response packets. */ - BUF->flags = TCP_ACK | TCP_PSH; - /* Send the packet. */ - goto tcp_send_noopts; - } - /* If there is no data to send, just send out a pure ACK if - there is newdata. */ - if(uip_flags & UIP_NEWDATA) { - uip_len = UIP_TCPIP_HLEN; - BUF->flags = TCP_ACK; - goto tcp_send_noopts; - } - } - goto drop; - case UIP_LAST_ACK: - /* We can close this connection if the peer has acknowledged our - FIN. This is indicated by the UIP_ACKDATA flag. */ - if(uip_flags & UIP_ACKDATA) { - uip_connr->tcpstateflags = UIP_CLOSED; - uip_flags = UIP_CLOSE; - UIP_APPCALL(); - } - break; - - case UIP_FIN_WAIT_1: - /* The application has closed the connection, but the remote host - hasn't closed its end yet. Thus we do nothing but wait for a - FIN from the other side. */ - if(uip_len > 0) { - uip_add_rcv_nxt(uip_len); - } - if(BUF->flags & TCP_FIN) { - if(uip_flags & UIP_ACKDATA) { - uip_connr->tcpstateflags = UIP_TIME_WAIT; - uip_connr->timer = 0; - uip_connr->len = 0; - } else { - uip_connr->tcpstateflags = UIP_CLOSING; - } - uip_add_rcv_nxt(1); - uip_flags = UIP_CLOSE; - UIP_APPCALL(); - goto tcp_send_ack; - } else if(uip_flags & UIP_ACKDATA) { - uip_connr->tcpstateflags = UIP_FIN_WAIT_2; - uip_connr->len = 0; - goto drop; - } - if(uip_len > 0) { - goto tcp_send_ack; - } - goto drop; - - case UIP_FIN_WAIT_2: - if(uip_len > 0) { - uip_add_rcv_nxt(uip_len); - } - if(BUF->flags & TCP_FIN) { - uip_connr->tcpstateflags = UIP_TIME_WAIT; - uip_connr->timer = 0; - uip_add_rcv_nxt(1); - uip_flags = UIP_CLOSE; - UIP_APPCALL(); - goto tcp_send_ack; - } - if(uip_len > 0) { - goto tcp_send_ack; - } - goto drop; - - case UIP_TIME_WAIT: - goto tcp_send_ack; - - case UIP_CLOSING: - if(uip_flags & UIP_ACKDATA) { - uip_connr->tcpstateflags = UIP_TIME_WAIT; - uip_connr->timer = 0; - } - } - goto drop; - - - /* We jump here when we are ready to send the packet, and just want - to set the appropriate TCP sequence numbers in the TCP header. */ - tcp_send_ack: - BUF->flags = TCP_ACK; - tcp_send_nodata: - uip_len = UIP_IPTCPH_LEN; - tcp_send_noopts: - BUF->tcpoffset = (UIP_TCPH_LEN / 4) << 4; - tcp_send: - /* We're done with the input processing. We are now ready to send a - reply. Our job is to fill in all the fields of the TCP and IP - headers before calculating the checksum and finally send the - packet. */ - BUF->ackno[0] = uip_connr->rcv_nxt[0]; - BUF->ackno[1] = uip_connr->rcv_nxt[1]; - BUF->ackno[2] = uip_connr->rcv_nxt[2]; - BUF->ackno[3] = uip_connr->rcv_nxt[3]; - - BUF->seqno[0] = uip_connr->snd_nxt[0]; - BUF->seqno[1] = uip_connr->snd_nxt[1]; - BUF->seqno[2] = uip_connr->snd_nxt[2]; - BUF->seqno[3] = uip_connr->snd_nxt[3]; - - BUF->proto = UIP_PROTO_TCP; - - BUF->srcport = uip_connr->lport; - BUF->destport = uip_connr->rport; - - uip_ipaddr_copy(BUF->srcipaddr, uip_hostaddr); - uip_ipaddr_copy(BUF->destipaddr, uip_connr->ripaddr); - - if(uip_connr->tcpstateflags & UIP_STOPPED) { - /* If the connection has issued uip_stop(), we advertise a zero - window so that the remote host will stop sending data. */ - BUF->wnd[0] = BUF->wnd[1] = 0; - } else { - BUF->wnd[0] = ((UIP_RECEIVE_WINDOW) >> 8); - BUF->wnd[1] = ((UIP_RECEIVE_WINDOW) & 0xff); - } - - tcp_send_noconn: - BUF->ttl = UIP_TTL; -#if UIP_CONF_IPV6 - /* For IPv6, the IP length field does not include the IPv6 IP header - length. */ - BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); - BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); -#else /* UIP_CONF_IPV6 */ - BUF->len[0] = (uip_len >> 8); - BUF->len[1] = (uip_len & 0xff); -#endif /* UIP_CONF_IPV6 */ - - BUF->urgp[0] = BUF->urgp[1] = 0; - - /* Calculate TCP checksum. */ - BUF->tcpchksum = 0; - BUF->tcpchksum = ~(uip_tcpchksum()); - - ip_send_nolen: - -#if UIP_CONF_IPV6 - BUF->vtc = 0x60; - BUF->tcflow = 0x00; - BUF->flow = 0x00; -#else /* UIP_CONF_IPV6 */ - BUF->vhl = 0x45; - BUF->tos = 0; - BUF->ipoffset[0] = BUF->ipoffset[1] = 0; - ++ipid; - BUF->ipid[0] = ipid >> 8; - BUF->ipid[1] = ipid & 0xff; - /* Calculate IP checksum. */ - BUF->ipchksum = 0; - BUF->ipchksum = ~(uip_ipchksum()); - DEBUG_PRINTF("uip ip_send_nolen: chkecum 0x%04x\n", uip_ipchksum()); -#endif /* UIP_CONF_IPV6 */ - - UIP_STAT(++uip_stat.tcp.sent); - send: - DEBUG_PRINTF("Sending packet with length %d (%d)\n", uip_len, - (BUF->len[0] << 8) | BUF->len[1]); - - UIP_STAT(++uip_stat.ip.sent); - /* Return and let the caller do the actual transmission. */ - uip_flags = 0; - return; - drop: - uip_len = 0; - uip_flags = 0; - return; -} -/*---------------------------------------------------------------------------*/ -u16_t -htons(u16_t val) -{ - return HTONS(val); -} -/*---------------------------------------------------------------------------*/ -void -uip_send(const void *data, int len) -{ - uip_slen = len; - if(len > 0) { - if(data != uip_sappdata) { - memcpy(uip_sappdata, (data), uip_slen); - } - } -} -/** @} */ diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip.h deleted file mode 100644 index 4fc0452..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip.h +++ /dev/null @@ -1,1601 +0,0 @@ - -/** - * \addtogroup uip - * @{ - */ - -/** - * \file - * Header file for the uIP TCP/IP stack. - * \author Adam Dunkels - * - * The uIP TCP/IP stack header file contains definitions for a number - * of C macros that are used by uIP programs as well as internal uIP - * structures, TCP/IP header structures and function declarations. - * - */ - - -/* - * Copyright (c) 2001-2003, Adam Dunkels. - * 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. Redistributions 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uip.h,v 1.40 2006/06/08 07:12:07 adam Exp $ - * - */ - -#ifndef __UIP_H__ -#define __UIP_H__ - -#include "uipopt.h" - -/** - * Repressentation of an IP address. - * - */ -typedef u16_t uip_ip4addr_t[2]; -typedef u16_t uip_ip6addr_t[8]; -#if UIP_CONF_IPV6 -typedef uip_ip6addr_t uip_ipaddr_t; -#else /* UIP_CONF_IPV6 */ -typedef uip_ip4addr_t uip_ipaddr_t; -#endif /* UIP_CONF_IPV6 */ - -/*---------------------------------------------------------------------------*/ -/* First, the functions that should be called from the - * system. Initialization, the periodic timer and incoming packets are - * handled by the following three functions. - */ - -/** - * \defgroup uipconffunc uIP configuration functions - * @{ - * - * The uIP configuration functions are used for setting run-time - * parameters in uIP such as IP addresses. - */ - -/** - * Set the IP address of this host. - * - * The IP address is represented as a 4-byte array where the first - * octet of the IP address is put in the first member of the 4-byte - * array. - * - * Example: - \code - - uip_ipaddr_t addr; - - uip_ipaddr(&addr, 192,168,1,2); - uip_sethostaddr(&addr); - - \endcode - * \param addr A pointer to an IP address of type uip_ipaddr_t; - * - * \sa uip_ipaddr() - * - * \hideinitializer - */ -#define uip_sethostaddr(addr) uip_ipaddr_copy(uip_hostaddr, (addr)) - -/** - * Get the IP address of this host. - * - * The IP address is represented as a 4-byte array where the first - * octet of the IP address is put in the first member of the 4-byte - * array. - * - * Example: - \code - uip_ipaddr_t hostaddr; - - uip_gethostaddr(&hostaddr); - \endcode - * \param addr A pointer to a uip_ipaddr_t variable that will be - * filled in with the currently configured IP address. - * - * \hideinitializer - */ -#define uip_gethostaddr(addr) uip_ipaddr_copy((addr), uip_hostaddr) - -/** - * Set the default router's IP address. - * - * \param addr A pointer to a uip_ipaddr_t variable containing the IP - * address of the default router. - * - * \sa uip_ipaddr() - * - * \hideinitializer - */ -#define uip_setdraddr(addr) uip_ipaddr_copy(uip_draddr, (addr)) - -/** - * Set the netmask. - * - * \param addr A pointer to a uip_ipaddr_t variable containing the IP - * address of the netmask. - * - * \sa uip_ipaddr() - * - * \hideinitializer - */ -#define uip_setnetmask(addr) uip_ipaddr_copy(uip_netmask, (addr)) - - -/** - * Get the default router's IP address. - * - * \param addr A pointer to a uip_ipaddr_t variable that will be - * filled in with the IP address of the default router. - * - * \hideinitializer - */ -#define uip_getdraddr(addr) uip_ipaddr_copy((addr), uip_draddr) - -/** - * Get the netmask. - * - * \param addr A pointer to a uip_ipaddr_t variable that will be - * filled in with the value of the netmask. - * - * \hideinitializer - */ -#define uip_getnetmask(addr) uip_ipaddr_copy((addr), uip_netmask) - -/** @} */ - -/** - * \defgroup uipinit uIP initialization functions - * @{ - * - * The uIP initialization functions are used for booting uIP. - */ - -/** - * uIP initialization function. - * - * This function should be called at boot up to initilize the uIP - * TCP/IP stack. - */ -void uip_init(void); - -/** - * uIP initialization function. - * - * This function may be used at boot time to set the initial ip_id. - */ -void uip_setipid(u16_t id); - -/** @} */ - -/** - * \defgroup uipdevfunc uIP device driver functions - * @{ - * - * These functions are used by a network device driver for interacting - * with uIP. - */ - -/** - * Process an incoming packet. - * - * This function should be called when the device driver has received - * a packet from the network. The packet from the device driver must - * be present in the uip_buf buffer, and the length of the packet - * should be placed in the uip_len variable. - * - * When the function returns, there may be an outbound packet placed - * in the uip_buf packet buffer. If so, the uip_len variable is set to - * the length of the packet. If no packet is to be sent out, the - * uip_len variable is set to 0. - * - * The usual way of calling the function is presented by the source - * code below. - \code - uip_len = devicedriver_poll(); - if(uip_len > 0) { - uip_input(); - if(uip_len > 0) { - devicedriver_send(); - } - } - \endcode - * - * \note If you are writing a uIP device driver that needs ARP - * (Address Resolution Protocol), e.g., when running uIP over - * Ethernet, you will need to call the uIP ARP code before calling - * this function: - \code - #define BUF ((struct uip_eth_hdr *)&uip_buf[0]) - uip_len = ethernet_devicedrver_poll(); - if(uip_len > 0) { - if(BUF->type == HTONS(UIP_ETHTYPE_IP)) { - uip_arp_ipin(); - uip_input(); - if(uip_len > 0) { - uip_arp_out(); - ethernet_devicedriver_send(); - } - } else if(BUF->type == HTONS(UIP_ETHTYPE_ARP)) { - uip_arp_arpin(); - if(uip_len > 0) { - ethernet_devicedriver_send(); - } - } - \endcode - * - * \hideinitializer - */ -#define uip_input() uip_process(UIP_DATA) - -/** - * Periodic processing for a connection identified by its number. - * - * This function does the necessary periodic processing (timers, - * polling) for a uIP TCP conneciton, and should be called when the - * periodic uIP timer goes off. It should be called for every - * connection, regardless of whether they are open of closed. - * - * When the function returns, it may have an outbound packet waiting - * for service in the uIP packet buffer, and if so the uip_len - * variable is set to a value larger than zero. The device driver - * should be called to send out the packet. - * - * The ususal way of calling the function is through a for() loop like - * this: - \code - for(i = 0; i < UIP_CONNS; ++i) { - uip_periodic(i); - if(uip_len > 0) { - devicedriver_send(); - } - } - \endcode - * - * \note If you are writing a uIP device driver that needs ARP - * (Address Resolution Protocol), e.g., when running uIP over - * Ethernet, you will need to call the uip_arp_out() function before - * calling the device driver: - \code - for(i = 0; i < UIP_CONNS; ++i) { - uip_periodic(i); - if(uip_len > 0) { - uip_arp_out(); - ethernet_devicedriver_send(); - } - } - \endcode - * - * \param conn The number of the connection which is to be periodically polled. - * - * \hideinitializer - */ -#define uip_periodic(conn) do { uip_conn = &uip_conns[conn]; \ - uip_process(UIP_TIMER); } while (0) - -/** - * - * - */ -#define uip_conn_active(conn) (uip_conns[conn].tcpstateflags != UIP_CLOSED) - -/** - * Perform periodic processing for a connection identified by a pointer - * to its structure. - * - * Same as uip_periodic() but takes a pointer to the actual uip_conn - * struct instead of an integer as its argument. This function can be - * used to force periodic processing of a specific connection. - * - * \param conn A pointer to the uip_conn struct for the connection to - * be processed. - * - * \hideinitializer - */ -#define uip_periodic_conn(conn) do { uip_conn = conn; \ - uip_process(UIP_TIMER); } while (0) - -/** - * Reuqest that a particular connection should be polled. - * - * Similar to uip_periodic_conn() but does not perform any timer - * processing. The application is polled for new data. - * - * \param conn A pointer to the uip_conn struct for the connection to - * be processed. - * - * \hideinitializer - */ -#define uip_poll_conn(conn) do { uip_conn = conn; \ - uip_process(UIP_POLL_REQUEST); } while (0) - - -#if UIP_UDP -/** - * Periodic processing for a UDP connection identified by its number. - * - * This function is essentially the same as uip_periodic(), but for - * UDP connections. It is called in a similar fashion as the - * uip_periodic() function: - \code - for(i = 0; i < UIP_UDP_CONNS; i++) { - uip_udp_periodic(i); - if(uip_len > 0) { - devicedriver_send(); - } - } - \endcode - * - * \note As for the uip_periodic() function, special care has to be - * taken when using uIP together with ARP and Ethernet: - \code - for(i = 0; i < UIP_UDP_CONNS; i++) { - uip_udp_periodic(i); - if(uip_len > 0) { - uip_arp_out(); - ethernet_devicedriver_send(); - } - } - \endcode - * - * \param conn The number of the UDP connection to be processed. - * - * \hideinitializer - */ -#define uip_udp_periodic(conn) do { uip_udp_conn = &uip_udp_conns[conn]; \ - uip_process(UIP_UDP_TIMER); } while (0) - -/** - * Periodic processing for a UDP connection identified by a pointer to - * its structure. - * - * Same as uip_udp_periodic() but takes a pointer to the actual - * uip_conn struct instead of an integer as its argument. This - * function can be used to force periodic processing of a specific - * connection. - * - * \param conn A pointer to the uip_udp_conn struct for the connection - * to be processed. - * - * \hideinitializer - */ -#define uip_udp_periodic_conn(conn) do { uip_udp_conn = conn; \ - uip_process(UIP_UDP_TIMER); } while (0) - - -#endif /* UIP_UDP */ - -/** - * The uIP packet buffer. - * - * The uip_buf array is used to hold incoming and outgoing - * packets. The device driver should place incoming data into this - * buffer. When sending data, the device driver should read the link - * level headers and the TCP/IP headers from this buffer. The size of - * the link level headers is configured by the UIP_LLH_LEN define. - * - * \note The application data need not be placed in this buffer, so - * the device driver must read it from the place pointed to by the - * uip_appdata pointer as illustrated by the following example: - \code - void - devicedriver_send(void) - { - hwsend(&uip_buf[0], UIP_LLH_LEN); - if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) { - hwsend(&uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN); - } else { - hwsend(&uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN); - hwsend(uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN); - } - } - \endcode - */ -extern u8_t uip_buf[UIP_BUFSIZE+2]; - -/** @} */ - -/*---------------------------------------------------------------------------*/ -/* Functions that are used by the uIP application program. Opening and - * closing connections, sending and receiving data, etc. is all - * handled by the functions below. -*/ -/** - * \defgroup uipappfunc uIP application functions - * @{ - * - * Functions used by an application running of top of uIP. - */ - -/** - * Start listening to the specified port. - * - * \note Since this function expects the port number in network byte - * order, a conversion using HTONS() or htons() is necessary. - * - \code - uip_listen(HTONS(80)); - \endcode - * - * \param port A 16-bit port number in network byte order. - */ -void uip_listen(u16_t port); - -/** - * Stop listening to the specified port. - * - * \note Since this function expects the port number in network byte - * order, a conversion using HTONS() or htons() is necessary. - * - \code - uip_unlisten(HTONS(80)); - \endcode - * - * \param port A 16-bit port number in network byte order. - */ -void uip_unlisten(u16_t port); - -/** - * Connect to a remote host using TCP. - * - * This function is used to start a new connection to the specified - * port on the specied host. It allocates a new connection identifier, - * sets the connection to the SYN_SENT state and sets the - * retransmission timer to 0. This will cause a TCP SYN segment to be - * sent out the next time this connection is periodically processed, - * which usually is done within 0.5 seconds after the call to - * uip_connect(). - * - * \note This function is avaliable only if support for active open - * has been configured by defining UIP_ACTIVE_OPEN to 1 in uipopt.h. - * - * \note Since this function requires the port number to be in network - * byte order, a conversion using HTONS() or htons() is necessary. - * - \code - uip_ipaddr_t ipaddr; - - uip_ipaddr(&ipaddr, 192,168,1,2); - uip_connect(&ipaddr, HTONS(80)); - \endcode - * - * \param ripaddr The IP address of the remote hot. - * - * \param port A 16-bit port number in network byte order. - * - * \return A pointer to the uIP connection identifier for the new connection, - * or NULL if no connection could be allocated. - * - */ -struct uip_conn *uip_connect(uip_ipaddr_t *ripaddr, u16_t port); - - - -/** - * \internal - * - * Check if a connection has outstanding (i.e., unacknowledged) data. - * - * \param conn A pointer to the uip_conn structure for the connection. - * - * \hideinitializer - */ -#define uip_outstanding(conn) ((conn)->len) - -/** - * Send data on the current connection. - * - * This function is used to send out a single segment of TCP - * data. Only applications that have been invoked by uIP for event - * processing can send data. - * - * The amount of data that actually is sent out after a call to this - * funcion is determined by the maximum amount of data TCP allows. uIP - * will automatically crop the data so that only the appropriate - * amount of data is sent. The function uip_mss() can be used to query - * uIP for the amount of data that actually will be sent. - * - * \note This function does not guarantee that the sent data will - * arrive at the destination. If the data is lost in the network, the - * application will be invoked with the uip_rexmit() event being - * set. The application will then have to resend the data using this - * function. - * - * \param data A pointer to the data which is to be sent. - * - * \param len The maximum amount of data bytes to be sent. - * - * \hideinitializer - */ -void uip_send(const void *data, int len); - -/** - * The length of any incoming data that is currently avaliable (if avaliable) - * in the uip_appdata buffer. - * - * The test function uip_data() must first be used to check if there - * is any data available at all. - * - * \hideinitializer - */ -/*void uip_datalen(void);*/ -#define uip_datalen() uip_len - -/** - * The length of any out-of-band data (urgent data) that has arrived - * on the connection. - * - * \note The configuration parameter UIP_URGDATA must be set for this - * function to be enabled. - * - * \hideinitializer - */ -#define uip_urgdatalen() uip_urglen - -/** - * Close the current connection. - * - * This function will close the current connection in a nice way. - * - * \hideinitializer - */ -#define uip_close() (uip_flags = UIP_CLOSE) - -/** - * Abort the current connection. - * - * This function will abort (reset) the current connection, and is - * usually used when an error has occured that prevents using the - * uip_close() function. - * - * \hideinitializer - */ -#define uip_abort() (uip_flags = UIP_ABORT) - -/** - * Tell the sending host to stop sending data. - * - * This function will close our receiver's window so that we stop - * receiving data for the current connection. - * - * \hideinitializer - */ -#define uip_stop() (uip_conn->tcpstateflags |= UIP_STOPPED) - -/** - * Find out if the current connection has been previously stopped with - * uip_stop(). - * - * \hideinitializer - */ -#define uip_stopped(conn) ((conn)->tcpstateflags & UIP_STOPPED) - -/** - * Restart the current connection, if is has previously been stopped - * with uip_stop(). - * - * This function will open the receiver's window again so that we - * start receiving data for the current connection. - * - * \hideinitializer - */ -#define uip_restart() do { uip_flags |= UIP_NEWDATA; \ - uip_conn->tcpstateflags &= ~UIP_STOPPED; \ - } while(0) - - -/* uIP tests that can be made to determine in what state the current - connection is, and what the application function should do. */ - -/** - * Is the current connection a UDP connection? - * - * This function checks whether the current connection is a UDP connection. - * - * \hideinitializer - * - */ -#define uip_udpconnection() (uip_conn == NULL) - -/** - * Is new incoming data available? - * - * Will reduce to non-zero if there is new data for the application - * present at the uip_appdata pointer. The size of the data is - * avaliable through the uip_len variable. - * - * \hideinitializer - */ -#define uip_newdata() (uip_flags & UIP_NEWDATA) - -/** - * Has previously sent data been acknowledged? - * - * Will reduce to non-zero if the previously sent data has been - * acknowledged by the remote host. This means that the application - * can send new data. - * - * \hideinitializer - */ -#define uip_acked() (uip_flags & UIP_ACKDATA) - -/** - * Has the connection just been connected? - * - * Reduces to non-zero if the current connection has been connected to - * a remote host. This will happen both if the connection has been - * actively opened (with uip_connect()) or passively opened (with - * uip_listen()). - * - * \hideinitializer - */ -#define uip_connected() (uip_flags & UIP_CONNECTED) - -/** - * Has the connection been closed by the other end? - * - * Is non-zero if the connection has been closed by the remote - * host. The application may then do the necessary clean-ups. - * - * \hideinitializer - */ -#define uip_closed() (uip_flags & UIP_CLOSE) - -/** - * Has the connection been aborted by the other end? - * - * Non-zero if the current connection has been aborted (reset) by the - * remote host. - * - * \hideinitializer - */ -#define uip_aborted() (uip_flags & UIP_ABORT) - -/** - * Has the connection timed out? - * - * Non-zero if the current connection has been aborted due to too many - * retransmissions. - * - * \hideinitializer - */ -#define uip_timedout() (uip_flags & UIP_TIMEDOUT) - -/** - * Do we need to retransmit previously data? - * - * Reduces to non-zero if the previously sent data has been lost in - * the network, and the application should retransmit it. The - * application should send the exact same data as it did the last - * time, using the uip_send() function. - * - * \hideinitializer - */ -#define uip_rexmit() (uip_flags & UIP_REXMIT) - -/** - * Is the connection being polled by uIP? - * - * Is non-zero if the reason the application is invoked is that the - * current connection has been idle for a while and should be - * polled. - * - * The polling event can be used for sending data without having to - * wait for the remote host to send data. - * - * \hideinitializer - */ -#define uip_poll() (uip_flags & UIP_POLL) - -/** - * Get the initial maxium segment size (MSS) of the current - * connection. - * - * \hideinitializer - */ -#define uip_initialmss() (uip_conn->initialmss) - -/** - * Get the current maxium segment size that can be sent on the current - * connection. - * - * The current maxiumum segment size that can be sent on the - * connection is computed from the receiver's window and the MSS of - * the connection (which also is available by calling - * uip_initialmss()). - * - * \hideinitializer - */ -#define uip_mss() (uip_conn->mss) - -/** - * Set up a new UDP connection. - * - * This function sets up a new UDP connection. The function will - * automatically allocate an unused local port for the new - * connection. However, another port can be chosen by using the - * uip_udp_bind() call, after the uip_udp_new() function has been - * called. - * - * Example: - \code - uip_ipaddr_t addr; - struct uip_udp_conn *c; - - uip_ipaddr(&addr, 192,168,2,1); - c = uip_udp_new(&addr, HTONS(12345)); - if(c != NULL) { - uip_udp_bind(c, HTONS(12344)); - } - \endcode - * \param ripaddr The IP address of the remote host. - * - * \param rport The remote port number in network byte order. - * - * \return The uip_udp_conn structure for the new connection or NULL - * if no connection could be allocated. - */ -struct uip_udp_conn *uip_udp_new(uip_ipaddr_t *ripaddr, u16_t rport); - -/** - * Removed a UDP connection. - * - * \param conn A pointer to the uip_udp_conn structure for the connection. - * - * \hideinitializer - */ -#define uip_udp_remove(conn) (conn)->lport = 0 - -/** - * Bind a UDP connection to a local port. - * - * \param conn A pointer to the uip_udp_conn structure for the - * connection. - * - * \param port The local port number, in network byte order. - * - * \hideinitializer - */ -#define uip_udp_bind(conn, port) (conn)->lport = port - -/** - * Send a UDP datagram of length len on the current connection. - * - * This function can only be called in response to a UDP event (poll - * or newdata). The data must be present in the uip_buf buffer, at the - * place pointed to by the uip_appdata pointer. - * - * \param len The length of the data in the uip_buf buffer. - * - * \hideinitializer - */ -#define uip_udp_send(len) uip_send((char *)uip_appdata, len) - -/** @} */ - -/* uIP convenience and converting functions. */ - -/** - * \defgroup uipconvfunc uIP conversion functions - * @{ - * - * These functions can be used for converting between different data - * formats used by uIP. - */ - -/** - * Construct an IP address from four bytes. - * - * This function constructs an IP address of the type that uIP handles - * internally from four bytes. The function is handy for specifying IP - * addresses to use with e.g. the uip_connect() function. - * - * Example: - \code - uip_ipaddr_t ipaddr; - struct uip_conn *c; - - uip_ipaddr(&ipaddr, 192,168,1,2); - c = uip_connect(&ipaddr, HTONS(80)); - \endcode - * - * \param addr A pointer to a uip_ipaddr_t variable that will be - * filled in with the IP address. - * - * \param addr0 The first octet of the IP address. - * \param addr1 The second octet of the IP address. - * \param addr2 The third octet of the IP address. - * \param addr3 The forth octet of the IP address. - * - * \hideinitializer - */ -#define uip_ipaddr(addr, addr0,addr1,addr2,addr3) do { \ - ((u16_t *)(addr))[0] = HTONS(((addr0) << 8) | (addr1)); \ - ((u16_t *)(addr))[1] = HTONS(((addr2) << 8) | (addr3)); \ - } while(0) - -/** - * Construct an IPv6 address from eight 16-bit words. - * - * This function constructs an IPv6 address. - * - * \hideinitializer - */ -#define uip_ip6addr(addr, addr0,addr1,addr2,addr3,addr4,addr5,addr6,addr7) do { \ - ((u16_t *)(addr))[0] = HTONS((addr0)); \ - ((u16_t *)(addr))[1] = HTONS((addr1)); \ - ((u16_t *)(addr))[2] = HTONS((addr2)); \ - ((u16_t *)(addr))[3] = HTONS((addr3)); \ - ((u16_t *)(addr))[4] = HTONS((addr4)); \ - ((u16_t *)(addr))[5] = HTONS((addr5)); \ - ((u16_t *)(addr))[6] = HTONS((addr6)); \ - ((u16_t *)(addr))[7] = HTONS((addr7)); \ - } while(0) - -/** - * Copy an IP address to another IP address. - * - * Copies an IP address from one place to another. - * - * Example: - \code - uip_ipaddr_t ipaddr1, ipaddr2; - - uip_ipaddr(&ipaddr1, 192,16,1,2); - uip_ipaddr_copy(&ipaddr2, &ipaddr1); - \endcode - * - * \param dest The destination for the copy. - * \param src The source from where to copy. - * - * \hideinitializer - */ -#if !UIP_CONF_IPV6 -#define uip_ipaddr_copy(dest, src) do { \ - ((u16_t *)dest)[0] = ((u16_t *)src)[0]; \ - ((u16_t *)dest)[1] = ((u16_t *)src)[1]; \ - } while(0) -#else /* !UIP_CONF_IPV6 */ -#define uip_ipaddr_copy(dest, src) memcpy(dest, src, sizeof(uip_ip6addr_t)) -#endif /* !UIP_CONF_IPV6 */ - -/** - * Compare two IP addresses - * - * Compares two IP addresses. - * - * Example: - \code - uip_ipaddr_t ipaddr1, ipaddr2; - - uip_ipaddr(&ipaddr1, 192,16,1,2); - if(uip_ipaddr_cmp(&ipaddr2, &ipaddr1)) { - printf("They are the same"); - } - \endcode - * - * \param addr1 The first IP address. - * \param addr2 The second IP address. - * - * \hideinitializer - */ -#if !UIP_CONF_IPV6 -#define uip_ipaddr_cmp(addr1, addr2) (((u16_t *)addr1)[0] == ((u16_t *)addr2)[0] && \ - ((u16_t *)addr1)[1] == ((u16_t *)addr2)[1]) -#else /* !UIP_CONF_IPV6 */ -#define uip_ipaddr_cmp(addr1, addr2) (memcmp(addr1, addr2, sizeof(uip_ip6addr_t)) == 0) -#endif /* !UIP_CONF_IPV6 */ - -/** - * Compare two IP addresses with netmasks - * - * Compares two IP addresses with netmasks. The masks are used to mask - * out the bits that are to be compared. - * - * Example: - \code - uip_ipaddr_t ipaddr1, ipaddr2, mask; - - uip_ipaddr(&mask, 255,255,255,0); - uip_ipaddr(&ipaddr1, 192,16,1,2); - uip_ipaddr(&ipaddr2, 192,16,1,3); - if(uip_ipaddr_maskcmp(&ipaddr1, &ipaddr2, &mask)) { - printf("They are the same"); - } - \endcode - * - * \param addr1 The first IP address. - * \param addr2 The second IP address. - * \param mask The netmask. - * - * \hideinitializer - */ -#define uip_ipaddr_maskcmp(addr1, addr2, mask) \ - (((((u16_t *)addr1)[0] & ((u16_t *)mask)[0]) == \ - (((u16_t *)addr2)[0] & ((u16_t *)mask)[0])) && \ - ((((u16_t *)addr1)[1] & ((u16_t *)mask)[1]) == \ - (((u16_t *)addr2)[1] & ((u16_t *)mask)[1]))) - - -/** - * Mask out the network part of an IP address. - * - * Masks out the network part of an IP address, given the address and - * the netmask. - * - * Example: - \code - uip_ipaddr_t ipaddr1, ipaddr2, netmask; - - uip_ipaddr(&ipaddr1, 192,16,1,2); - uip_ipaddr(&netmask, 255,255,255,0); - uip_ipaddr_mask(&ipaddr2, &ipaddr1, &netmask); - \endcode - * - * In the example above, the variable "ipaddr2" will contain the IP - * address 192.168.1.0. - * - * \param dest Where the result is to be placed. - * \param src The IP address. - * \param mask The netmask. - * - * \hideinitializer - */ -#define uip_ipaddr_mask(dest, src, mask) do { \ - ((u16_t *)dest)[0] = ((u16_t *)src)[0] & ((u16_t *)mask)[0]; \ - ((u16_t *)dest)[1] = ((u16_t *)src)[1] & ((u16_t *)mask)[1]; \ - } while(0) - -/** - * Pick the first octet of an IP address. - * - * Picks out the first octet of an IP address. - * - * Example: - \code - uip_ipaddr_t ipaddr; - u8_t octet; - - uip_ipaddr(&ipaddr, 1,2,3,4); - octet = uip_ipaddr1(&ipaddr); - \endcode - * - * In the example above, the variable "octet" will contain the value 1. - * - * \hideinitializer - */ -#define uip_ipaddr1(addr) (htons(((u16_t *)(addr))[0]) >> 8) - -/** - * Pick the second octet of an IP address. - * - * Picks out the second octet of an IP address. - * - * Example: - \code - uip_ipaddr_t ipaddr; - u8_t octet; - - uip_ipaddr(&ipaddr, 1,2,3,4); - octet = uip_ipaddr2(&ipaddr); - \endcode - * - * In the example above, the variable "octet" will contain the value 2. - * - * \hideinitializer - */ -#define uip_ipaddr2(addr) (htons(((u16_t *)(addr))[0]) & 0xff) - -/** - * Pick the third octet of an IP address. - * - * Picks out the third octet of an IP address. - * - * Example: - \code - uip_ipaddr_t ipaddr; - u8_t octet; - - uip_ipaddr(&ipaddr, 1,2,3,4); - octet = uip_ipaddr3(&ipaddr); - \endcode - * - * In the example above, the variable "octet" will contain the value 3. - * - * \hideinitializer - */ -#define uip_ipaddr3(addr) (htons(((u16_t *)(addr))[1]) >> 8) - -/** - * Pick the fourth octet of an IP address. - * - * Picks out the fourth octet of an IP address. - * - * Example: - \code - uip_ipaddr_t ipaddr; - u8_t octet; - - uip_ipaddr(&ipaddr, 1,2,3,4); - octet = uip_ipaddr4(&ipaddr); - \endcode - * - * In the example above, the variable "octet" will contain the value 4. - * - * \hideinitializer - */ -#define uip_ipaddr4(addr) (htons(((u16_t *)(addr))[1]) & 0xff) - -/** - * Convert 16-bit quantity from host byte order to network byte order. - * - * This macro is primarily used for converting constants from host - * byte order to network byte order. For converting variables to - * network byte order, use the htons() function instead. - * - * \hideinitializer - */ -#ifndef HTONS -# if UIP_BYTE_ORDER == UIP_BIG_ENDIAN -# define HTONS(n) (n) -# else /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ -# define HTONS(n) (u16_t)((((u16_t) (n)) << 8) | (((u16_t) (n)) >> 8)) -# endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ -#else -#error "HTONS already defined!" -#endif /* HTONS */ - -/** - * Convert 16-bit quantity from host byte order to network byte order. - * - * This function is primarily used for converting variables from host - * byte order to network byte order. For converting constants to - * network byte order, use the HTONS() macro instead. - */ -#ifndef htons -u16_t htons(u16_t val); -#endif /* htons */ -#ifndef ntohs -#define ntohs htons -#endif - -/** @} */ - -/** - * Pointer to the application data in the packet buffer. - * - * This pointer points to the application data when the application is - * called. If the application wishes to send data, the application may - * use this space to write the data into before calling uip_send(). - */ -extern void *uip_appdata; - -#if UIP_URGDATA > 0 -/* u8_t *uip_urgdata: - * - * This pointer points to any urgent data that has been received. Only - * present if compiled with support for urgent data (UIP_URGDATA). - */ -extern void *uip_urgdata; -#endif /* UIP_URGDATA > 0 */ - - -/** - * \defgroup uipdrivervars Variables used in uIP device drivers - * @{ - * - * uIP has a few global variables that are used in device drivers for - * uIP. - */ - -/** - * The length of the packet in the uip_buf buffer. - * - * The global variable uip_len holds the length of the packet in the - * uip_buf buffer. - * - * When the network device driver calls the uIP input function, - * uip_len should be set to the length of the packet in the uip_buf - * buffer. - * - * When sending packets, the device driver should use the contents of - * the uip_len variable to determine the length of the outgoing - * packet. - * - */ -extern u16_t uip_len; - -/** @} */ - -#if UIP_URGDATA > 0 -extern u16_t uip_urglen, uip_surglen; -#endif /* UIP_URGDATA > 0 */ - - -/** - * Representation of a uIP TCP connection. - * - * The uip_conn structure is used for identifying a connection. All - * but one field in the structure are to be considered read-only by an - * application. The only exception is the appstate field whos purpose - * is to let the application store application-specific state (e.g., - * file pointers) for the connection. The type of this field is - * configured in the "uipopt.h" header file. - */ -struct uip_conn { - uip_ipaddr_t ripaddr; /**< The IP address of the remote host. */ - - u16_t lport; /**< The local TCP port, in network byte order. */ - u16_t rport; /**< The local remote TCP port, in network byte - order. */ - - u8_t rcv_nxt[4]; /**< The sequence number that we expect to - receive next. */ - u8_t snd_nxt[4]; /**< The sequence number that was last sent by - us. */ - u16_t len; /**< Length of the data that was previously sent. */ - u16_t mss; /**< Current maximum segment size for the - connection. */ - u16_t initialmss; /**< Initial maximum segment size for the - connection. */ - u8_t sa; /**< Retransmission time-out calculation state - variable. */ - u8_t sv; /**< Retransmission time-out calculation state - variable. */ - u8_t rto; /**< Retransmission time-out. */ - u8_t tcpstateflags; /**< TCP state and flags. */ - u8_t timer; /**< The retransmission timer. */ - u8_t nrtx; /**< The number of retransmissions for the last - segment sent. */ - - /** The application state. */ - uip_tcp_appstate_t appstate; -}; - - -/** - * Pointer to the current TCP connection. - * - * The uip_conn pointer can be used to access the current TCP - * connection. - */ -extern struct uip_conn *uip_conn; -/* The array containing all uIP connections. */ -extern struct uip_conn uip_conns[UIP_CONNS]; -/** - * \addtogroup uiparch - * @{ - */ - -/** - * 4-byte array used for the 32-bit sequence number calculations. - */ -extern u8_t uip_acc32[4]; - -/** @} */ - - -#if UIP_UDP -/** - * Representation of a uIP UDP connection. - */ -struct uip_udp_conn { - uip_ipaddr_t ripaddr; /**< The IP address of the remote peer. */ - u16_t lport; /**< The local port number in network byte order. */ - u16_t rport; /**< The remote port number in network byte order. */ - u8_t ttl; /**< Default time-to-live. */ - - /** The application state. */ - uip_udp_appstate_t appstate; -}; - -/** - * The current UDP connection. - */ -extern struct uip_udp_conn *uip_udp_conn; -extern struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS]; -#endif /* UIP_UDP */ - -/** - * The structure holding the TCP/IP statistics that are gathered if - * UIP_STATISTICS is set to 1. - * - */ -struct uip_stats { - struct { - uip_stats_t drop; /**< Number of dropped packets at the IP - layer. */ - uip_stats_t recv; /**< Number of received packets at the IP - layer. */ - uip_stats_t sent; /**< Number of sent packets at the IP - layer. */ - uip_stats_t vhlerr; /**< Number of packets dropped due to wrong - IP version or header length. */ - uip_stats_t hblenerr; /**< Number of packets dropped due to wrong - IP length, high byte. */ - uip_stats_t lblenerr; /**< Number of packets dropped due to wrong - IP length, low byte. */ - uip_stats_t fragerr; /**< Number of packets dropped since they - were IP fragments. */ - uip_stats_t chkerr; /**< Number of packets dropped due to IP - checksum errors. */ - uip_stats_t protoerr; /**< Number of packets dropped since they - were neither ICMP, UDP nor TCP. */ - } ip; /**< IP statistics. */ - struct { - uip_stats_t drop; /**< Number of dropped ICMP packets. */ - uip_stats_t recv; /**< Number of received ICMP packets. */ - uip_stats_t sent; /**< Number of sent ICMP packets. */ - uip_stats_t typeerr; /**< Number of ICMP packets with a wrong - type. */ - } icmp; /**< ICMP statistics. */ - struct { - uip_stats_t drop; /**< Number of dropped TCP segments. */ - uip_stats_t recv; /**< Number of recived TCP segments. */ - uip_stats_t sent; /**< Number of sent TCP segments. */ - uip_stats_t chkerr; /**< Number of TCP segments with a bad - checksum. */ - uip_stats_t ackerr; /**< Number of TCP segments with a bad ACK - number. */ - uip_stats_t rst; /**< Number of recevied TCP RST (reset) segments. */ - uip_stats_t rexmit; /**< Number of retransmitted TCP segments. */ - uip_stats_t syndrop; /**< Number of dropped SYNs due to too few - connections was avaliable. */ - uip_stats_t synrst; /**< Number of SYNs for closed ports, - triggering a RST. */ - } tcp; /**< TCP statistics. */ -#if UIP_UDP - struct { - uip_stats_t drop; /**< Number of dropped UDP segments. */ - uip_stats_t recv; /**< Number of recived UDP segments. */ - uip_stats_t sent; /**< Number of sent UDP segments. */ - uip_stats_t chkerr; /**< Number of UDP segments with a bad - checksum. */ - } udp; /**< UDP statistics. */ -#endif /* UIP_UDP */ -}; - -/** - * The uIP TCP/IP statistics. - * - * This is the variable in which the uIP TCP/IP statistics are gathered. - */ -extern struct uip_stats uip_stat; - - -/*---------------------------------------------------------------------------*/ -/* All the stuff below this point is internal to uIP and should not be - * used directly by an application or by a device driver. - */ -/*---------------------------------------------------------------------------*/ -/* u8_t uip_flags: - * - * When the application is called, uip_flags will contain the flags - * that are defined in this file. Please read below for more - * infomation. - */ -extern u8_t uip_flags; - -/* The following flags may be set in the global variable uip_flags - before calling the application callback. The UIP_ACKDATA, - UIP_NEWDATA, and UIP_CLOSE flags may both be set at the same time, - whereas the others are mutualy exclusive. Note that these flags - should *NOT* be accessed directly, but only through the uIP - functions/macros. */ - -#define UIP_ACKDATA 1 /* Signifies that the outstanding data was - acked and the application should send - out new data instead of retransmitting - the last data. */ -#define UIP_NEWDATA 2 /* Flags the fact that the peer has sent - us new data. */ -#define UIP_REXMIT 4 /* Tells the application to retransmit the - data that was last sent. */ -#define UIP_POLL 8 /* Used for polling the application, to - check if the application has data that - it wants to send. */ -#define UIP_CLOSE 16 /* The remote host has closed the - connection, thus the connection has - gone away. Or the application signals - that it wants to close the - connection. */ -#define UIP_ABORT 32 /* The remote host has aborted the - connection, thus the connection has - gone away. Or the application signals - that it wants to abort the - connection. */ -#define UIP_CONNECTED 64 /* We have got a connection from a remote - host and have set up a new connection - for it, or an active connection has - been successfully established. */ - -#define UIP_TIMEDOUT 128 /* The connection has been aborted due to - too many retransmissions. */ - -/* uip_process(flag): - * - * The actual uIP function which does all the work. - */ -void uip_process(u8_t flag); - -/* The following flags are passed as an argument to the uip_process() - function. They are used to distinguish between the two cases where - uip_process() is called. It can be called either because we have - incoming data that should be processed, or because the periodic - timer has fired. These values are never used directly, but only in - the macrose defined in this file. */ - -#define UIP_DATA 1 /* Tells uIP that there is incoming - data in the uip_buf buffer. The - length of the data is stored in the - global variable uip_len. */ -#define UIP_TIMER 2 /* Tells uIP that the periodic timer - has fired. */ -#define UIP_POLL_REQUEST 3 /* Tells uIP that a connection should - be polled. */ -#define UIP_UDP_SEND_CONN 4 /* Tells uIP that a UDP datagram - should be constructed in the - uip_buf buffer. */ -#if UIP_UDP -#define UIP_UDP_TIMER 5 -#endif /* UIP_UDP */ - -/* The TCP states used in the uip_conn->tcpstateflags. */ -#define UIP_CLOSED 0 -#define UIP_SYN_RCVD 1 -#define UIP_SYN_SENT 2 -#define UIP_ESTABLISHED 3 -#define UIP_FIN_WAIT_1 4 -#define UIP_FIN_WAIT_2 5 -#define UIP_CLOSING 6 -#define UIP_TIME_WAIT 7 -#define UIP_LAST_ACK 8 -#define UIP_TS_MASK 15 - -#define UIP_STOPPED 16 - -/* The TCP and IP headers. */ -struct uip_tcpip_hdr { -#if UIP_CONF_IPV6 - /* IPv6 header. */ - u8_t vtc, - tcflow; - u16_t flow; - u8_t len[2]; - u8_t proto, ttl; - uip_ip6addr_t srcipaddr, destipaddr; -#else /* UIP_CONF_IPV6 */ - /* IPv4 header. */ - u8_t vhl, - tos, - len[2], - ipid[2], - ipoffset[2], - ttl, - proto; - u16_t ipchksum; - u16_t srcipaddr[2], - destipaddr[2]; -#endif /* UIP_CONF_IPV6 */ - - /* TCP header. */ - u16_t srcport, - destport; - u8_t seqno[4], - ackno[4], - tcpoffset, - flags, - wnd[2]; - u16_t tcpchksum; - u8_t urgp[2]; - u8_t optdata[4]; -}; - -/* The ICMP and IP headers. */ -struct uip_icmpip_hdr { -#if UIP_CONF_IPV6 - /* IPv6 header. */ - u8_t vtc, - tcf; - u16_t flow; - u8_t len[2]; - u8_t proto, ttl; - uip_ip6addr_t srcipaddr, destipaddr; -#else /* UIP_CONF_IPV6 */ - /* IPv4 header. */ - u8_t vhl, - tos, - len[2], - ipid[2], - ipoffset[2], - ttl, - proto; - u16_t ipchksum; - u16_t srcipaddr[2], - destipaddr[2]; -#endif /* UIP_CONF_IPV6 */ - - /* ICMP (echo) header. */ - u8_t type, icode; - u16_t icmpchksum; -#if !UIP_CONF_IPV6 - u16_t id, seqno; -#else /* !UIP_CONF_IPV6 */ - u8_t flags, reserved1, reserved2, reserved3; - u8_t icmp6data[16]; - u8_t options[1]; -#endif /* !UIP_CONF_IPV6 */ -}; - - -/* The UDP and IP headers. */ -struct uip_udpip_hdr { -#if UIP_CONF_IPV6 - /* IPv6 header. */ - u8_t vtc, - tcf; - u16_t flow; - u8_t len[2]; - u8_t proto, ttl; - uip_ip6addr_t srcipaddr, destipaddr; -#else /* UIP_CONF_IPV6 */ - /* IP header. */ - u8_t vhl, - tos, - len[2], - ipid[2], - ipoffset[2], - ttl, - proto; - u16_t ipchksum; - u16_t srcipaddr[2], - destipaddr[2]; -#endif /* UIP_CONF_IPV6 */ - - /* UDP header. */ - u16_t srcport, - destport; - u16_t udplen; - u16_t udpchksum; -}; - - - -/** - * The buffer size available for user data in the \ref uip_buf buffer. - * - * This macro holds the available size for user data in the \ref - * uip_buf buffer. The macro is intended to be used for checking - * bounds of available user data. - * - * Example: - \code - snprintf(uip_appdata, UIP_APPDATA_SIZE, "%u\n", i); - \endcode - * - * \hideinitializer - */ -#define UIP_APPDATA_SIZE (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) - - -#define UIP_PROTO_ICMP 1 -#define UIP_PROTO_TCP 6 -#define UIP_PROTO_UDP 17 -#define UIP_PROTO_ICMP6 58 - -/* Header sizes. */ -#if UIP_CONF_IPV6 -#define UIP_IPH_LEN 40 -#else /* UIP_CONF_IPV6 */ -#define UIP_IPH_LEN 20 /* Size of IP header */ -#endif /* UIP_CONF_IPV6 */ -#define UIP_UDPH_LEN 8 /* Size of UDP header */ -#define UIP_TCPH_LEN 20 /* Size of TCP header */ -#define UIP_IPUDPH_LEN (UIP_UDPH_LEN + UIP_IPH_LEN) /* Size of IP + - UDP - header */ -#define UIP_IPTCPH_LEN (UIP_TCPH_LEN + UIP_IPH_LEN) /* Size of IP + - TCP - header */ -#define UIP_TCPIP_HLEN UIP_IPTCPH_LEN - - -#if UIP_FIXEDADDR -extern const uip_ipaddr_t uip_hostaddr, uip_netmask, uip_draddr; -#else /* UIP_FIXEDADDR */ -extern uip_ipaddr_t uip_hostaddr, uip_netmask, uip_draddr; -#endif /* UIP_FIXEDADDR */ - - - -/** - * Representation of a 48-bit Ethernet address. - */ -struct uip_eth_addr { - u8_t addr[6]; -}; - -/** - * Calculate the Internet checksum over a buffer. - * - * The Internet checksum is the one's complement of the one's - * complement sum of all 16-bit words in the buffer. - * - * See RFC1071. - * - * \param buf A pointer to the buffer over which the checksum is to be - * computed. - * - * \param len The length of the buffer over which the checksum is to - * be computed. - * - * \return The Internet checksum of the buffer. - */ -u16_t uip_chksum(u16_t *buf, u16_t len); - -/** - * Calculate the IP header checksum of the packet header in uip_buf. - * - * The IP header checksum is the Internet checksum of the 20 bytes of - * the IP header. - * - * \return The IP header checksum of the IP header in the uip_buf - * buffer. - */ -u16_t uip_ipchksum(void); - -/** - * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. - * - * The TCP checksum is the Internet checksum of data contents of the - * TCP segment, and a pseudo-header as defined in RFC793. - * - * \return The TCP checksum of the TCP segment in uip_buf and pointed - * to by uip_appdata. - */ -u16_t uip_tcpchksum(void); - -/** - * Calculate the UDP checksum of the packet in uip_buf and uip_appdata. - * - * The UDP checksum is the Internet checksum of data contents of the - * UDP segment, and a pseudo-header as defined in RFC768. - * - * \return The UDP checksum of the UDP segment in uip_buf and pointed - * to by uip_appdata. - */ -u16_t uip_udpchksum(void); - - -#endif /* __UIP_H__ */ - - -/** @} */ diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arch.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arch.h deleted file mode 100644 index 71fd84b..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arch.h +++ /dev/null @@ -1,138 +0,0 @@ -/** - * \addtogroup uip - * {@ - */ - -/** - * \defgroup uiparch Architecture specific uIP functions - * @{ - * - * The functions in the architecture specific module implement the IP - * check sum and 32-bit additions. - * - * The IP checksum calculation is the most computationally expensive - * operation in the TCP/IP stack and it therefore pays off to - * implement this in efficient assembler. The purpose of the uip-arch - * module is to let the checksum functions to be implemented in - * architecture specific assembler. - * - */ - -/** - * \file - * Declarations of architecture specific functions. - * \author Adam Dunkels - */ - -/* - * Copyright (c) 2001, Adam Dunkels. - * 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. Redistributions 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uip_arch.h,v 1.2 2006/06/07 09:15:19 adam Exp $ - * - */ - -#ifndef __UIP_ARCH_H__ -#define __UIP_ARCH_H__ - -#include "uip.h" - -/** - * Carry out a 32-bit addition. - * - * Because not all architectures for which uIP is intended has native - * 32-bit arithmetic, uIP uses an external C function for doing the - * required 32-bit additions in the TCP protocol processing. This - * function should add the two arguments and place the result in the - * global variable uip_acc32. - * - * \note The 32-bit integer pointed to by the op32 parameter and the - * result in the uip_acc32 variable are in network byte order (big - * endian). - * - * \param op32 A pointer to a 4-byte array representing a 32-bit - * integer in network byte order (big endian). - * - * \param op16 A 16-bit integer in host byte order. - */ -void uip_add32(u8_t *op32, u16_t op16); - -/** - * Calculate the Internet checksum over a buffer. - * - * The Internet checksum is the one's complement of the one's - * complement sum of all 16-bit words in the buffer. - * - * See RFC1071. - * - * \note This function is not called in the current version of uIP, - * but future versions might make use of it. - * - * \param buf A pointer to the buffer over which the checksum is to be - * computed. - * - * \param len The length of the buffer over which the checksum is to - * be computed. - * - * \return The Internet checksum of the buffer. - */ -u16_t uip_chksum(u16_t *buf, u16_t len); - -/** - * Calculate the IP header checksum of the packet header in uip_buf. - * - * The IP header checksum is the Internet checksum of the 20 bytes of - * the IP header. - * - * \return The IP header checksum of the IP header in the uip_buf - * buffer. - */ -u16_t uip_ipchksum(void); - -/** - * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. - * - * The TCP checksum is the Internet checksum of data contents of the - * TCP segment, and a pseudo-header as defined in RFC793. - * - * \note The uip_appdata pointer that points to the packet data may - * point anywhere in memory, so it is not possible to simply calculate - * the Internet checksum of the contents of the uip_buf buffer. - * - * \return The TCP checksum of the TCP segment in uip_buf and pointed - * to by uip_appdata. - */ -u16_t uip_tcpchksum(void); - -u16_t uip_udpchksum(void); - -/** @} */ -/** @} */ - -#endif /* __UIP_ARCH_H__ */ diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arp.c b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arp.c deleted file mode 100644 index 86b4df8..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arp.c +++ /dev/null @@ -1,422 +0,0 @@ -/** - * \addtogroup uip - * @{ - */ - -/** - * \defgroup uiparp uIP Address Resolution Protocol - * @{ - * - * The Address Resolution Protocol ARP is used for mapping between IP - * addresses and link level addresses such as the Ethernet MAC - * addresses. ARP uses broadcast queries to ask for the link level - * address of a known IP address and the host which is configured with - * the IP address for which the query was meant, will respond with its - * link level address. - * - * \note This ARP implementation only supports Ethernet. - */ - -/** - * \file - * Implementation of the ARP Address Resolution Protocol. - * \author Adam Dunkels - * - */ - -/* - * Copyright (c) 2001-2003, Adam Dunkels. - * 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. Redistributions 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uip_arp.c,v 1.8 2006/06/02 23:36:21 adam Exp $ - * - */ - - -#include "uip_arp.h" - -#include - -struct arp_hdr { - struct uip_eth_hdr ethhdr; - u16_t hwtype; - u16_t protocol; - u8_t hwlen; - u8_t protolen; - u16_t opcode; - struct uip_eth_addr shwaddr; - u16_t sipaddr[2]; - struct uip_eth_addr dhwaddr; - u16_t dipaddr[2]; -}; - -struct ethip_hdr { - struct uip_eth_hdr ethhdr; - /* IP header. */ - u8_t vhl, - tos, - len[2], - ipid[2], - ipoffset[2], - ttl, - proto; - u16_t ipchksum; - u16_t srcipaddr[2], - destipaddr[2]; -}; - -#define ARP_REQUEST 1 -#define ARP_REPLY 2 - -#define ARP_HWTYPE_ETH 1 - -struct arp_entry { - u16_t ipaddr[2]; - struct uip_eth_addr ethaddr; - u8_t time; -}; - -static const struct uip_eth_addr broadcast_ethaddr = - {{0xff,0xff,0xff,0xff,0xff,0xff}}; -static const u16_t broadcast_ipaddr[2] = {0xffff,0xffff}; - -static struct arp_entry arp_table[UIP_ARPTAB_SIZE]; -static u16_t ipaddr[2]; -static u8_t i, c; - -static u8_t arptime; -static u8_t tmpage; - -#define BUF ((struct arp_hdr *)&uip_buf[0]) -#define IPBUF ((struct ethip_hdr *)&uip_buf[0]) -/*-----------------------------------------------------------------------------------*/ -/** - * Initialize the ARP module. - * - */ -/*-----------------------------------------------------------------------------------*/ -void -uip_arp_init(void) -{ - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - memset(arp_table[i].ipaddr, 0, 4); - } -} -/*-----------------------------------------------------------------------------------*/ -/** - * Periodic ARP processing function. - * - * This function performs periodic timer processing in the ARP module - * and should be called at regular intervals. The recommended interval - * is 10 seconds between the calls. - * - */ -/*-----------------------------------------------------------------------------------*/ -void -uip_arp_timer(void) -{ - struct arp_entry *tabptr; - - ++arptime; - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - tabptr = &arp_table[i]; - if((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 && - arptime - tabptr->time >= UIP_ARP_MAXAGE) { - memset(tabptr->ipaddr, 0, 4); - } - } - -} -/*-----------------------------------------------------------------------------------*/ -static void -uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr) -{ - register struct arp_entry *tabptr; - /* Walk through the ARP mapping table and try to find an entry to - update. If none is found, the IP -> MAC address mapping is - inserted in the ARP table. */ - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - - tabptr = &arp_table[i]; - /* Only check those entries that are actually in use. */ - if(tabptr->ipaddr[0] != 0 && - tabptr->ipaddr[1] != 0) { - - /* Check if the source IP address of the incoming packet matches - the IP address in this ARP table entry. */ - if(memcmp(ipaddr, tabptr->ipaddr, 4) == 0) { - - /* An old entry found, update this and return. */ - memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); - tabptr->time = arptime; - - return; - } - } - } - - /* If we get here, no existing ARP table entry was found, so we - create one. */ - - /* First, we try to find an unused entry in the ARP table. */ - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - tabptr = &arp_table[i]; - if(tabptr->ipaddr[0] == 0 && - tabptr->ipaddr[1] == 0) { - break; - } - } - - /* If no unused entry is found, we try to find the oldest entry and - throw it away. */ - if(i == UIP_ARPTAB_SIZE) { - tmpage = 0; - c = 0; - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - tabptr = &arp_table[i]; - if(arptime - tabptr->time > tmpage) { - tmpage = arptime - tabptr->time; - c = i; - } - } - i = c; - tabptr = &arp_table[i]; - } - - /* Now, i is the ARP table entry which we will fill with the new - information. */ - memcpy(tabptr->ipaddr, ipaddr, 4); - memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); - tabptr->time = arptime; -} -/*-----------------------------------------------------------------------------------*/ -/** - * ARP processing for incoming IP packets - * - * This function should be called by the device driver when an IP - * packet has been received. The function will check if the address is - * in the ARP cache, and if so the ARP cache entry will be - * refreshed. If no ARP cache entry was found, a new one is created. - * - * This function expects an IP packet with a prepended Ethernet header - * in the uip_buf[] buffer, and the length of the packet in the global - * variable uip_len. - */ -/*-----------------------------------------------------------------------------------*/ -//#if 0 -void -uip_arp_ipin(void) -{ - uip_len -= sizeof(struct uip_eth_hdr); - - /* Only insert/update an entry if the source IP address of the - incoming IP packet comes from a host on the local network. */ - if((IPBUF->srcipaddr[0] & uip_netmask[0]) != - (uip_hostaddr[0] & uip_netmask[0])) { - return; - } - if((IPBUF->srcipaddr[1] & uip_netmask[1]) != - (uip_hostaddr[1] & uip_netmask[1])) { - return; - } - uip_arp_update(IPBUF->srcipaddr, &(IPBUF->ethhdr.src)); - - return; -} -//#endif /* 0 */ -/*-----------------------------------------------------------------------------------*/ -/** - * ARP processing for incoming ARP packets. - * - * This function should be called by the device driver when an ARP - * packet has been received. The function will act differently - * depending on the ARP packet type: if it is a reply for a request - * that we previously sent out, the ARP cache will be filled in with - * the values from the ARP reply. If the incoming ARP packet is an ARP - * request for our IP address, an ARP reply packet is created and put - * into the uip_buf[] buffer. - * - * When the function returns, the value of the global variable uip_len - * indicates whether the device driver should send out a packet or - * not. If uip_len is zero, no packet should be sent. If uip_len is - * non-zero, it contains the length of the outbound packet that is - * present in the uip_buf[] buffer. - * - * This function expects an ARP packet with a prepended Ethernet - * header in the uip_buf[] buffer, and the length of the packet in the - * global variable uip_len. - */ -/*-----------------------------------------------------------------------------------*/ -void -uip_arp_arpin(void) -{ - - if(uip_len < sizeof(struct arp_hdr)) { - uip_len = 0; - return; - } - uip_len = 0; - - switch(BUF->opcode) { - case HTONS(ARP_REQUEST): - /* ARP request. If it asked for our address, we send out a - reply. */ - if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) { - /* First, we register the one who made the request in our ARP - table, since it is likely that we will do more communication - with this host in the future. */ - uip_arp_update(BUF->sipaddr, &BUF->shwaddr); - - /* The reply opcode is 2. */ - BUF->opcode = HTONS(2); - - memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6); - memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); - memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); - memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6); - - BUF->dipaddr[0] = BUF->sipaddr[0]; - BUF->dipaddr[1] = BUF->sipaddr[1]; - BUF->sipaddr[0] = uip_hostaddr[0]; - BUF->sipaddr[1] = uip_hostaddr[1]; - - BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); - uip_len = sizeof(struct arp_hdr); - } - break; - case HTONS(ARP_REPLY): - /* ARP reply. We insert or update the ARP table if it was meant - for us. */ - if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) { - uip_arp_update(BUF->sipaddr, &BUF->shwaddr); - } - break; - } - - return; -} -/*-----------------------------------------------------------------------------------*/ -/** - * Prepend Ethernet header to an outbound IP packet and see if we need - * to send out an ARP request. - * - * This function should be called before sending out an IP packet. The - * function checks the destination IP address of the IP packet to see - * what Ethernet MAC address that should be used as a destination MAC - * address on the Ethernet. - * - * If the destination IP address is in the local network (determined - * by logical ANDing of netmask and our IP address), the function - * checks the ARP cache to see if an entry for the destination IP - * address is found. If so, an Ethernet header is prepended and the - * function returns. If no ARP cache entry is found for the - * destination IP address, the packet in the uip_buf[] is replaced by - * an ARP request packet for the IP address. The IP packet is dropped - * and it is assumed that they higher level protocols (e.g., TCP) - * eventually will retransmit the dropped packet. - * - * If the destination IP address is not on the local network, the IP - * address of the default router is used instead. - * - * When the function returns, a packet is present in the uip_buf[] - * buffer, and the length of the packet is in the global variable - * uip_len. - */ -/*-----------------------------------------------------------------------------------*/ -void -uip_arp_out(void) -{ - struct arp_entry *tabptr; - - /* Find the destination IP address in the ARP table and construct - the Ethernet header. If the destination IP addres isn't on the - local network, we use the default router's IP address instead. - - If not ARP table entry is found, we overwrite the original IP - packet with an ARP request for the IP address. */ - - /* First check if destination is a local broadcast. */ - if(uip_ipaddr_cmp(IPBUF->destipaddr, broadcast_ipaddr)) { - memcpy(IPBUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6); - } else { - /* Check if the destination address is on the local network. */ - if(!uip_ipaddr_maskcmp(IPBUF->destipaddr, uip_hostaddr, uip_netmask)) { - /* Destination address was not on the local network, so we need to - use the default router's IP address instead of the destination - address when determining the MAC address. */ - uip_ipaddr_copy(ipaddr, uip_draddr); - } else { - /* Else, we use the destination IP address. */ - uip_ipaddr_copy(ipaddr, IPBUF->destipaddr); - } - - for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { - tabptr = &arp_table[i]; - if(uip_ipaddr_cmp(ipaddr, tabptr->ipaddr)) { - break; - } - } - - if(i == UIP_ARPTAB_SIZE) { - /* The destination address was not in our ARP table, so we - overwrite the IP packet with an ARP request. */ - - memset(BUF->ethhdr.dest.addr, 0xff, 6); - memset(BUF->dhwaddr.addr, 0x00, 6); - memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); - memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); - - uip_ipaddr_copy(BUF->dipaddr, ipaddr); - uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr); - BUF->opcode = HTONS(ARP_REQUEST); /* ARP request. */ - BUF->hwtype = HTONS(ARP_HWTYPE_ETH); - BUF->protocol = HTONS(UIP_ETHTYPE_IP); - BUF->hwlen = 6; - BUF->protolen = 4; - BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); - - uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN]; - - uip_len = sizeof(struct arp_hdr); - return; - } - - /* Build an ethernet header. */ - memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6); - } - memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, 6); - - IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP); - - uip_len += sizeof(struct uip_eth_hdr); -} -/*-----------------------------------------------------------------------------------*/ - -/** @} */ -/** @} */ diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arp.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arp.h deleted file mode 100644 index ef93054..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uip_arp.h +++ /dev/null @@ -1,144 +0,0 @@ -/** - * \addtogroup uip - * @{ - */ - -/** - * \addtogroup uiparp - * @{ - */ - -/** - * \file - * Macros and definitions for the ARP module. - * \author Adam Dunkels - */ - - -/* - * Copyright (c) 2001-2003, Adam Dunkels. - * 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. Redistributions 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uip_arp.h,v 1.5 2006/06/11 21:46:39 adam Exp $ - * - */ - -#ifndef __UIP_ARP_H__ -#define __UIP_ARP_H__ - -#include "uip.h" - - -extern struct uip_eth_addr uip_ethaddr; - -/** - * The Ethernet header. - */ -struct uip_eth_hdr { - struct uip_eth_addr dest; - struct uip_eth_addr src; - u16_t type; -}; - -#define UIP_ETHTYPE_ARP 0x0806 -#define UIP_ETHTYPE_IP 0x0800 -#define UIP_ETHTYPE_IP6 0x86dd - - -/* The uip_arp_init() function must be called before any of the other - ARP functions. */ -void uip_arp_init(void); - -/* The uip_arp_ipin() function should be called whenever an IP packet - arrives from the Ethernet. This function refreshes the ARP table or - inserts a new mapping if none exists. The function assumes that an - IP packet with an Ethernet header is present in the uip_buf buffer - and that the length of the packet is in the uip_len variable. */ -void uip_arp_ipin(void); -//#define uip_arp_ipin() - -/* The uip_arp_arpin() should be called when an ARP packet is received - by the Ethernet driver. This function also assumes that the - Ethernet frame is present in the uip_buf buffer. When the - uip_arp_arpin() function returns, the contents of the uip_buf - buffer should be sent out on the Ethernet if the uip_len variable - is > 0. */ -void uip_arp_arpin(void); - -/* The uip_arp_out() function should be called when an IP packet - should be sent out on the Ethernet. This function creates an - Ethernet header before the IP header in the uip_buf buffer. The - Ethernet header will have the correct Ethernet MAC destination - address filled in if an ARP table entry for the destination IP - address (or the IP address of the default router) is present. If no - such table entry is found, the IP packet is overwritten with an ARP - request and we rely on TCP to retransmit the packet that was - overwritten. In any case, the uip_len variable holds the length of - the Ethernet frame that should be transmitted. */ -void uip_arp_out(void); - -/* The uip_arp_timer() function should be called every ten seconds. It - is responsible for flushing old entries in the ARP table. */ -void uip_arp_timer(void); - -/** @} */ - -/** - * \addtogroup uipconffunc - * @{ - */ - - -/** - * Specifiy the Ethernet MAC address. - * - * The ARP code needs to know the MAC address of the Ethernet card in - * order to be able to respond to ARP queries and to generate working - * Ethernet headers. - * - * \note This macro only specifies the Ethernet MAC address to the ARP - * code. It cannot be used to change the MAC address of the Ethernet - * card. - * - * \param eaddr A pointer to a struct uip_eth_addr containing the - * Ethernet MAC address of the Ethernet card. - * - * \hideinitializer - */ -#define uip_setethaddr(eaddr) do {uip_ethaddr.addr[0] = eaddr.addr[0]; \ - uip_ethaddr.addr[1] = eaddr.addr[1];\ - uip_ethaddr.addr[2] = eaddr.addr[2];\ - uip_ethaddr.addr[3] = eaddr.addr[3];\ - uip_ethaddr.addr[4] = eaddr.addr[4];\ - uip_ethaddr.addr[5] = eaddr.addr[5];} while(0) - -/** @} */ -/** @} */ - -#endif /* __UIP_ARP_H__ */ diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uipethernet-conf.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uipethernet-conf.h deleted file mode 100644 index 6387091..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uipethernet-conf.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef UIPETHERNET_CONF_H -#define UIPETHERNET_CONF_H - -// https://github.com/jandrassy/EthernetENC/wiki/Settings - -/* for TCP */ -#ifndef UIP_SOCKET_NUMPACKETS -#define UIP_SOCKET_NUMPACKETS 3 -#endif -#ifndef UIP_CONF_MAX_CONNECTIONS -#define UIP_CONF_MAX_CONNECTIONS 4 -#endif - -/* for UDP - * set UIP_CONF_UDP to 0 to disable UDP (saves aprox. 4kB flash) */ -#ifndef UIP_CONF_UDP -#define UIP_CONF_UDP 1 -#endif -#ifndef UIP_CONF_UDP_CONNS -#define UIP_CONF_UDP_CONNS 2 -#endif - -/** - * size of received UDP messages backlog. it must be at least 1 - */ -#ifndef UIP_UDP_BACKLOG -#define UIP_UDP_BACKLOG 2 -#endif - -/* timeout in ms for attempts to get a free memory block to write - * before returning number of bytes sent so far - * set to 0 to block until connection is closed by timeout */ -#ifndef UIP_WRITE_TIMEOUT -#define UIP_WRITE_TIMEOUT 2000 -#endif - -/* timeout after which UIPClient::connect gives up. The timeout is specified in seconds. - * if set to a number <= 0 connect will timeout when uIP does (which might be longer than you expect...) */ -#ifndef UIP_CONNECT_TIMEOUT -#define UIP_CONNECT_TIMEOUT 5 -#endif - -/* periodic timer for uip (in ms) */ -#ifndef UIP_PERIODIC_TIMER -#define UIP_PERIODIC_TIMER 100 -#endif - -#endif diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uipopt.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uipopt.h deleted file mode 100644 index 945decd..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/uipopt.h +++ /dev/null @@ -1,555 +0,0 @@ -/** - * \defgroup uipopt Configuration options for uIP - * @{ - * - * uIP is configured using the per-project configuration file - * uipopt.h. This file contains all compile-time options for uIP and - * should be tweaked to match each specific project. The uIP - * distribution contains a documented example "uipopt.h" that can be - * copied and modified for each project. - * - * \note Most of the configuration options in the uipopt.h should not - * be changed, but rather the per-project uip-conf.h file. - */ - -/** - * \file - * Configuration options for uIP. - * \author Adam Dunkels - * - * This file is used for tweaking various configuration options for - * uIP. You should make a copy of this file into one of your project's - * directories instead of editing this example "uipopt.h" file that - * comes with the uIP distribution. - */ - -/* - * Copyright (c) 2001-2003, Adam Dunkels. - * 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. Redistributions 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. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the uIP TCP/IP stack. - * - * $Id: uipopt.h,v 1.4 2006/06/12 08:00:31 adam Exp $ - * - */ - -#ifndef __UIPOPT_H__ -#define __UIPOPT_H__ - -#ifndef UIP_LITTLE_ENDIAN - #if defined(LITTLE_ENDIAN) - #define UIP_LITTLE_ENDIAN LITTLE_ENDIAN - #elif defined(__ORDER_LITTLE_ENDIAN__) - #define UIP_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ - #else - #define UIP_LITTLE_ENDIAN 1234 - #endif -#endif /* UIP_LITTLE_ENDIAN */ -#ifndef UIP_BIG_ENDIAN - #if defined(BIG_ENDIAN) - #define UIP_BIG_ENDIAN BIG_ENDIAN - #elif defined(__ORDER_BIG_ENDIAN__) - #define UIP_BIG_ENDIAN __ORDER_BIG_ENDIAN__ - #else - #define UIP_BIG_ENDIAN 4321 - #endif -#endif /* UIP_BIG_ENDIAN */ - -#include "uip-conf.h" - -/*------------------------------------------------------------------------------*/ - -/** - * \name Static configuration options - * @{ - * - * These configuration options can be used for setting the IP address - * settings statically, but only if UIP_FIXEDADDR is set to 1. The - * configuration options for a specific node includes IP address, - * netmask and default router as well as the Ethernet address. The - * netmask, default router and Ethernet address are appliciable only - * if uIP should be run over Ethernet. - * - * All of these should be changed to suit your project. -*/ - -/** - * Determines if uIP should use a fixed IP address or not. - * - * If uIP should use a fixed IP address, the settings are set in the - * uipopt.h file. If not, the macros uip_sethostaddr(), - * uip_setdraddr() and uip_setnetmask() should be used instead. - * - * \hideinitializer - */ -#define UIP_FIXEDADDR 0 - -/** - * Ping IP address asignment. - * - * uIP uses a "ping" packets for setting its own IP address if this - * option is set. If so, uIP will start with an empty IP address and - * the destination IP address of the first incoming "ping" (ICMP echo) - * packet will be used for setting the hosts IP address. - * - * \note This works only if UIP_FIXEDADDR is 0. - * - * \hideinitializer - */ -#ifdef UIP_CONF_PINGADDRCONF -#define UIP_PINGADDRCONF UIP_CONF_PINGADDRCONF -#else /* UIP_CONF_PINGADDRCONF */ -#define UIP_PINGADDRCONF 0 -#endif /* UIP_CONF_PINGADDRCONF */ - - -/** - * Specifies if the uIP ARP module should be compiled with a fixed - * Ethernet MAC address or not. - * - * If this configuration option is 0, the macro uip_setethaddr() can - * be used to specify the Ethernet address at run-time. - * - * \hideinitializer - */ -#define UIP_FIXEDETHADDR 0 - -/** @} */ -/*------------------------------------------------------------------------------*/ -/** - * \name IP configuration options - * @{ - * - */ -/** - * The IP TTL (time to live) of IP packets sent by uIP. - * - * This should normally not be changed. - */ -#define UIP_TTL 64 - -/** - * Turn on support for IP packet reassembly. - * - * uIP supports reassembly of fragmented IP packets. This features - * requires an additonal amount of RAM to hold the reassembly buffer - * and the reassembly code size is approximately 700 bytes. The - * reassembly buffer is of the same size as the uip_buf buffer - * (configured by UIP_BUFSIZE). - * - * \note IP packet reassembly is not heavily tested. - * - * \hideinitializer - */ -#define UIP_REASSEMBLY 0 - -/** - * The maximum time an IP fragment should wait in the reassembly - * buffer before it is dropped. - * - */ -#define UIP_REASS_MAXAGE 40 - -/** @} */ - -/*------------------------------------------------------------------------------*/ -/** - * \name UDP configuration options - * @{ - */ - -/** - * Toggles wether UDP support should be compiled in or not. - * - * \hideinitializer - */ -#ifdef UIP_CONF_UDP -#define UIP_UDP UIP_CONF_UDP -#else /* UIP_CONF_UDP */ -#define UIP_UDP 0 -#endif /* UIP_CONF_UDP */ - -/** - * Toggles if UDP checksums should be used or not. - * - * \note Support for UDP checksums is currently not included in uIP, - * so this option has no function. - * - * \hideinitializer - */ -#ifdef UIP_CONF_UDP_CHECKSUMS -#define UIP_UDP_CHECKSUMS UIP_CONF_UDP_CHECKSUMS -#else -#define UIP_UDP_CHECKSUMS 0 -#endif - -/** - * The maximum amount of concurrent UDP connections. - * - * \hideinitializer - */ -#ifdef UIP_CONF_UDP_CONNS -#define UIP_UDP_CONNS UIP_CONF_UDP_CONNS -#else /* UIP_CONF_UDP_CONNS */ -#define UIP_UDP_CONNS 10 -#endif /* UIP_CONF_UDP_CONNS */ - -/** - * The name of the function that should be called when UDP datagrams arrive. - * - * \hideinitializer - */ - - -/** @} */ -/*------------------------------------------------------------------------------*/ -/** - * \name TCP configuration options - * @{ - */ - -/** - * Determines if support for opening connections from uIP should be - * compiled in. - * - * If the applications that are running on top of uIP for this project - * do not need to open outgoing TCP connections, this configration - * option can be turned off to reduce the code size of uIP. - * - * \hideinitializer - */ -#define UIP_ACTIVE_OPEN 1 - -/** - * The maximum number of simultaneously open TCP connections. - * - * Since the TCP connections are statically allocated, turning this - * configuration knob down results in less RAM used. Each TCP - * connection requires approximatly 30 bytes of memory. - * - * \hideinitializer - */ -#ifndef UIP_CONF_MAX_CONNECTIONS -#define UIP_CONNS 10 -#else /* UIP_CONF_MAX_CONNECTIONS */ -#define UIP_CONNS UIP_CONF_MAX_CONNECTIONS -#endif /* UIP_CONF_MAX_CONNECTIONS */ - - -/** - * The maximum number of simultaneously listening TCP ports. - * - * Each listening TCP port requires 2 bytes of memory. - * - * \hideinitializer - */ -#ifndef UIP_CONF_MAX_LISTENPORTS -#define UIP_LISTENPORTS 20 -#else /* UIP_CONF_MAX_LISTENPORTS */ -#define UIP_LISTENPORTS UIP_CONF_MAX_LISTENPORTS -#endif /* UIP_CONF_MAX_LISTENPORTS */ - -/** - * Determines if support for TCP urgent data notification should be - * compiled in. - * - * Urgent data (out-of-band data) is a rarely used TCP feature that - * very seldom would be required. - * - * \hideinitializer - */ -#define UIP_URGDATA 0 - -/** - * The initial retransmission timeout counted in timer pulses. - * - * This should not be changed. - */ -#define UIP_RTO 3 - -/** - * The maximum number of times a segment should be retransmitted - * before the connection should be aborted. - * - * This should not be changed. - */ -#define UIP_MAXRTX 8 - -/** - * The maximum number of times a SYN segment should be retransmitted - * before a connection request should be deemed to have been - * unsuccessful. - * - * This should not need to be changed. - */ -#define UIP_MAXSYNRTX 5 - -/** - * The TCP maximum segment size. - * - * This is should not be to set to more than - * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. - */ -#ifndef UIP_CONF_TCP_MSS -#define UIP_TCP_MSS (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) -#else -#define UIP_TCP_MSS UIP_CONF_TCP_MSS -#endif - -/** - * The size of the advertised receiver's window. - * - * Should be set low (i.e., to the size of the uip_buf buffer) is the - * application is slow to process incoming data, or high (32768 bytes) - * if the application processes data quickly. - * - * \hideinitializer - */ -#ifndef UIP_CONF_RECEIVE_WINDOW -#define UIP_RECEIVE_WINDOW UIP_TCP_MSS -#else -#define UIP_RECEIVE_WINDOW UIP_CONF_RECEIVE_WINDOW -#endif - -/** - * How long a connection should stay in the TIME_WAIT state. - * - * This configiration option has no real implication, and it should be - * left untouched. - */ -#define UIP_TIME_WAIT_TIMEOUT 120 - - -/** @} */ -/*------------------------------------------------------------------------------*/ -/** - * \name ARP configuration options - * @{ - */ - -/** - * The size of the ARP table. - * - * This option should be set to a larger value if this uIP node will - * have many connections from the local network. - * - * \hideinitializer - */ -#ifdef UIP_CONF_ARPTAB_SIZE -#define UIP_ARPTAB_SIZE UIP_CONF_ARPTAB_SIZE -#else -#define UIP_ARPTAB_SIZE 8 -#endif - -/** - * The maxium age of ARP table entries measured in 10ths of seconds. - * - * An UIP_ARP_MAXAGE of 120 corresponds to 20 minutes (BSD - * default). - */ -#define UIP_ARP_MAXAGE 120 - -/** @} */ - -/*------------------------------------------------------------------------------*/ - -/** - * \name General configuration options - * @{ - */ - -/** - * The size of the uIP packet buffer. - * - * The uIP packet buffer should not be smaller than 60 bytes, and does - * not need to be larger than 1500 bytes. Lower size results in lower - * TCP throughput, larger size results in higher TCP throughput. - * - * \hideinitializer - */ -#ifndef UIP_CONF_BUFFER_SIZE -#define UIP_BUFSIZE 400 -#else /* UIP_CONF_BUFFER_SIZE */ -#define UIP_BUFSIZE UIP_CONF_BUFFER_SIZE -#endif /* UIP_CONF_BUFFER_SIZE */ - - -/** - * Determines if statistics support should be compiled in. - * - * The statistics is useful for debugging and to show the user. - * - * \hideinitializer - */ -#ifndef UIP_CONF_STATISTICS -#define UIP_STATISTICS 0 -#else /* UIP_CONF_STATISTICS */ -#define UIP_STATISTICS UIP_CONF_STATISTICS -#endif /* UIP_CONF_STATISTICS */ - -/** - * Determines if logging of certain events should be compiled in. - * - * This is useful mostly for debugging. The function uip_log() - * must be implemented to suit the architecture of the project, if - * logging is turned on. - * - * \hideinitializer - */ -#ifndef UIP_CONF_LOGGING -#define UIP_LOGGING 0 -#else /* UIP_CONF_LOGGING */ -#define UIP_LOGGING UIP_CONF_LOGGING -#endif /* UIP_CONF_LOGGING */ - -/** - * Broadcast support. - * - * This flag configures IP broadcast support. This is useful only - * together with UDP. - * - * \hideinitializer - * - */ -#if UIP_UDP && UIP_CONF_BROADCAST -#define UIP_BROADCAST UIP_CONF_BROADCAST -#else /* UIP_CONF_BROADCAST */ -#define UIP_BROADCAST 0 -#endif /* UIP_CONF_BROADCAST */ - -/** - * Print out a uIP log message. - * - * This function must be implemented by the module that uses uIP, and - * is called by uIP whenever a log message is generated. - */ -void uip_log(char *msg); - -/** - * The link level header length. - * - * This is the offset into the uip_buf where the IP header can be - * found. For Ethernet, this should be set to 14. For SLIP, this - * should be set to 0. - * - * \hideinitializer - */ -#ifdef UIP_CONF_LLH_LEN -#define UIP_LLH_LEN UIP_CONF_LLH_LEN -#else /* UIP_CONF_LLH_LEN */ -#define UIP_LLH_LEN 14 -#endif /* UIP_CONF_LLH_LEN */ - -/** @} */ -/*------------------------------------------------------------------------------*/ -/** - * \name CPU architecture configuration - * @{ - * - * The CPU architecture configuration is where the endianess of the - * CPU on which uIP is to be run is specified. Most CPUs today are - * little endian, and the most notable exception are the Motorolas - * which are big endian. The BYTE_ORDER macro should be changed to - * reflect the CPU architecture on which uIP is to be run. - */ - -/** - * The byte order of the CPU architecture on which uIP is to be run. - * - * This option can be either BIG_ENDIAN (Motorola byte order) or - * LITTLE_ENDIAN (Intel byte order). - * - * \hideinitializer - */ -#ifdef UIP_CONF_BYTE_ORDER -#define UIP_BYTE_ORDER UIP_CONF_BYTE_ORDER -#else /* UIP_CONF_BYTE_ORDER */ -#define UIP_BYTE_ORDER UIP_LITTLE_ENDIAN -#endif /* UIP_CONF_BYTE_ORDER */ - -/** @} */ -/*------------------------------------------------------------------------------*/ - -/** - * \name Appication specific configurations - * @{ - * - * An uIP application is implemented using a single application - * function that is called by uIP whenever a TCP/IP event occurs. The - * name of this function must be registered with uIP at compile time - * using the UIP_APPCALL definition. - * - * uIP applications can store the application state within the - * uip_conn structure by specifying the type of the application - * structure by typedef:ing the type uip_tcp_appstate_t and uip_udp_appstate_t. - * - * The file containing the definitions must be included in the - * uipopt.h file. - * - * The following example illustrates how this can look. - \code - -void httpd_appcall(void); -#define UIP_APPCALL httpd_appcall - -struct httpd_state { - u8_t state; - u16_t count; - char *dataptr; - char *script; -}; -typedef struct httpd_state uip_tcp_appstate_t - \endcode - */ - -/** - * \var #define UIP_APPCALL - * - * The name of the application function that uIP should call in - * response to TCP/IP events. - * - */ - -/** - * \var typedef uip_tcp_appstate_t - * - * The type of the application state that is to be stored in the - * uip_conn structure. This usually is typedef:ed to a struct holding - * application state information. - */ - -/** - * \var typedef uip_udp_appstate_t - * - * The type of the application state that is to be stored in the - * uip_conn structure. This usually is typedef:ed to a struct holding - * application state information. - */ -/** @} */ -/** @} */ - -#endif /* __UIPOPT_H__ */ diff --git a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/util.h b/ShineLAN-X/firmware/lib/EthernetENC/src/utility/util.h deleted file mode 100644 index 11e4242..0000000 --- a/ShineLAN-X/firmware/lib/EthernetENC/src/utility/util.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef UTIL_H -#define UTIL_H - -#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ - ((x)<< 8 & 0x00FF0000UL) | \ - ((x)>> 8 & 0x0000FF00UL) | \ - ((x)>>24 & 0x000000FFUL) ) -#define ntohl(x) htonl(x) - -#endif diff --git a/ShineLAN-X/firmware/platformio.ini b/ShineLAN-X/firmware/platformio.ini deleted file mode 100644 index 826935a..0000000 --- a/ShineLAN-X/firmware/platformio.ini +++ /dev/null @@ -1,22 +0,0 @@ -[env:growatt-shinelan] -platform = ststm32 -board = genericSTM32F103RB -framework = arduino -upload_protocol = stlink -debug_tool = stlink -monitor_speed = 115200 - -lib_deps = - 4-20ma/ModbusMaster@^2.0.1 - knolleary/PubSubClient@^2.8 - # EthernetENC ist lokal in lib/EthernetENC/ (Bitbang-SPI Modifikation) - -build_flags = - -DPIO_FRAMEWORK_ARDUINO_NOERRNO - -DUSBCON - -DUSBD_USE_CDC - -DHAL_PCD_MODULE_ENABLED - -DUSB_CDC_TRANSMIT_TIMEOUT=3000 - -DCUSTOM_USBD_PID=0x5740 - '-DUSB_PRODUCT_STRING="STM32 Virtual COM Port"' - -I include diff --git a/ShineLAN-X/firmware/src/main.cpp b/ShineLAN-X/firmware/src/main.cpp deleted file mode 100644 index f865faa..0000000 --- a/ShineLAN-X/firmware/src/main.cpp +++ /dev/null @@ -1,712 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "config.h" -#include "config_manager.h" - -extern "C" void SystemClock_Config(void) { - RCC_OscInitTypeDef osc = {}; - RCC_ClkInitTypeDef clk = {}; - RCC_PeriphCLKInitTypeDef periphClk = {}; - - osc.OscillatorType = RCC_OSCILLATORTYPE_HSE; - osc.HSEState = RCC_HSE_ON; - osc.PLL.PLLState = RCC_PLL_ON; - osc.PLL.PLLSource = RCC_PLLSOURCE_HSE; - osc.PLL.PLLMUL = RCC_PLL_MUL9; - if (HAL_RCC_OscConfig(&osc) == HAL_OK) { - clk.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK - | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; - clk.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; - clk.AHBCLKDivider = RCC_SYSCLK_DIV1; - clk.APB1CLKDivider = RCC_HCLK_DIV2; - clk.APB2CLKDivider = RCC_HCLK_DIV1; - HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_2); - periphClk.PeriphClockSelection = RCC_PERIPHCLK_USB; - periphClk.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5; - HAL_RCCEx_PeriphCLKConfig(&periphClk); - } else { - osc.OscillatorType = RCC_OSCILLATORTYPE_HSI; - osc.HSEState = RCC_HSE_OFF; - osc.HSIState = RCC_HSI_ON; - osc.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; - osc.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2; - osc.PLL.PLLMUL = RCC_PLL_MUL12; - HAL_RCC_OscConfig(&osc); - clk.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK - | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; - clk.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; - clk.AHBCLKDivider = RCC_SYSCLK_DIV1; - clk.APB1CLKDivider = RCC_HCLK_DIV2; - clk.APB2CLKDivider = RCC_HCLK_DIV1; - HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_1); - periphClk.PeriphClockSelection = RCC_PERIPHCLK_USB; - periphClk.UsbClockSelection = RCC_USBCLKSOURCE_PLL; - HAL_RCCEx_PeriphCLKConfig(&periphClk); - } -} - -// Hält PA8 HIGH (USB getrennt) bis setup() es manuell verbindet -extern "C" void USBD_reenumerate(void) { - __HAL_RCC_GPIOA_CLK_ENABLE(); - GPIO_InitTypeDef gpio = {}; - gpio.Pin = GPIO_PIN_8; - gpio.Mode = GPIO_MODE_OUTPUT_PP; - gpio.Pull = GPIO_NOPULL; - gpio.Speed = GPIO_SPEED_FREQ_HIGH; - HAL_GPIO_Init(GPIOA, &gpio); - HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); -} - -HardwareSerial DebugSerial(PA10, PA9); - -// USB-Diagnose: was der Wechselrichter per Control-Transfer schickt -extern __IO bool dtrState; -extern __IO bool rtsState; -struct { uint32_t baud; uint8_t format; uint8_t parity; uint8_t bits; } - g_lineCoding = {115200, 0, 0, 8}; -volatile bool g_lineCodingUpdated = false; - -// Wird vom USB-Stack aufgerufen wenn Wechselrichter SET_LINE_CODING schickt -// usbd_cdc_if.c kann nicht geändert werden → wir lesen linecoding-Struct direkt -extern "C" { - typedef struct { uint32_t bitrate; uint8_t format; uint8_t paritytype; uint8_t datatype; } - USBD_CDC_LineCodingTypeDef; - extern USBD_CDC_LineCodingTypeDef linecoding; -} - -// CDC SERIAL_STATE Notification — NuttX sendet dies beim Öffnen von /dev/ttyACM0 -// signalisiert dem Wechselrichter DCD+DSR=1 → startet Modbus-Server im Inverter -#include "usbd_core.h" -extern USBD_HandleTypeDef hUSBD_Device_CDC; - -#define CDC_SERIAL_STATE_EP 0x82U - -void sendSerialStateNotification() { - static uint8_t notif[10] = { - 0xA1, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00 - }; - USBD_LL_Transmit(&hUSBD_Device_CDC, CDC_SERIAL_STATE_EP, notif, sizeof(notif)); -} - -// ModbusMaster schreibt Byte-für-Byte. Würden wir sofort senden, käme Byte 1 als -// eigenes 1-Byte USB-Paket an — der Wechselrichter behandelt jedes Short Packet -// als Frame-Ende und sieht keinen gültigen Modbus-Frame. -// Lösung: Bytes puffern, dann in flush() als ein einziges USB-Paket senden. -class ModbusStream : public Stream { -public: - uint8_t txBuf[16]; uint8_t txLen = 0; - uint8_t rxBuf[32]; uint8_t rxLen = 0; - uint32_t rxTotal = 0; - void reset() { txLen = 0; rxLen = 0; } - - int available() override { return SerialUSB.available(); } - int read() override { - int b = SerialUSB.read(); - if (b >= 0) { rxTotal++; if (rxLen < sizeof(rxBuf)) rxBuf[rxLen++] = (uint8_t)b; } - return b; - } - int peek() override { return SerialUSB.peek(); } - size_t write(uint8_t b) override { - if (txLen < sizeof(txBuf)) txBuf[txLen++] = b; - return 1; - } - size_t write(const uint8_t* buf, size_t size) override { - for (size_t i = 0; i < size && txLen < sizeof(txBuf); i++) txBuf[txLen++] = buf[i]; - return size; - } - void flush() override { - if (txLen > 0) SerialUSB.write(txBuf, txLen); - // txLen nicht zurücksetzen — Debug-Code liest txBuf danach noch - } - using Print::write; -} modbusSerial; - -// ============================================================ -// Sensor-Definitionen -// ============================================================ -struct Sensor { - const char* id; - const char* name; - uint16_t address; - bool isDword; - float scale; - const char* unit; - const char* deviceClass; - const char* stateClass; - const char* icon; -}; - -#if defined(INVERTER_MIC1500) -const Sensor SENSORS[] = { - {"pv_voltage", "PV Voltage", 3, false, 0.1f, "V", "voltage", "measurement", "mdi:solar-panel"}, - {"pv_current", "PV Current", 4, false, 0.1f, "A", "current", "measurement", "mdi:solar-panel"}, - {"pv_power", "PV Power", 5, true, 0.1f, "W", "power", "measurement", "mdi:solar-panel"}, - {"grid_frequency", "Grid Frequency", 37, false, 0.01f, "Hz", "frequency", "measurement", "mdi:sine-wave"}, - {"grid_voltage", "Grid Voltage", 38, false, 0.1f, "V", "voltage", "measurement", "mdi:flash"}, - {"grid_current", "Grid Current", 39, false, 0.1f, "A", "current", "measurement", "mdi:flash"}, - {"ac_power", "AC Output Power", 40, true, 0.1f, "W", "power", "measurement", "mdi:flash"}, - {"energy_today", "Energy Today", 53, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:solar-power"}, - {"energy_total", "Energy Total", 55, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:solar-power"}, - {"inverter_temp", "Inverter Temperature", 93, false, 0.1f, "°C", "temperature", "measurement", "mdi:thermometer"}, -}; -#elif defined(INVERTER_SPH5000) -const Sensor SENSORS[] = { - {"pv1_voltage", "PV1 Voltage", 3, false, 0.1f, "V", "voltage", "measurement", "mdi:solar-panel"}, - {"pv1_current", "PV1 Current", 4, false, 0.1f, "A", "current", "measurement", "mdi:solar-panel"}, - {"pv1_power", "PV1 Power", 5, true, 0.1f, "W", "power", "measurement", "mdi:solar-panel"}, - {"pv2_voltage", "PV2 Voltage", 7, false, 0.1f, "V", "voltage", "measurement", "mdi:solar-panel"}, - {"pv2_current", "PV2 Current", 8, false, 0.1f, "A", "current", "measurement", "mdi:solar-panel"}, - {"pv2_power", "PV2 Power", 9, true, 0.1f, "W", "power", "measurement", "mdi:solar-panel"}, - {"ac_power_total", "AC Output Power Total", 35, true, 0.1f, "W", "power", "measurement", "mdi:flash"}, - {"grid_frequency", "Grid Frequency", 37, false, 0.01f, "Hz", "frequency", "measurement", "mdi:sine-wave"}, - {"grid_voltage_l1", "Grid Voltage L1", 38, false, 0.1f, "V", "voltage", "measurement", "mdi:flash"}, - {"grid_current_l1", "Grid Current L1", 39, false, 0.1f, "A", "current", "measurement", "mdi:flash"}, - {"grid_voltage_l2", "Grid Voltage L2", 42, false, 0.1f, "V", "voltage", "measurement", "mdi:flash"}, - {"grid_current_l2", "Grid Current L2", 43, false, 0.1f, "A", "current", "measurement", "mdi:flash"}, - {"grid_voltage_l3", "Grid Voltage L3", 46, false, 0.1f, "V", "voltage", "measurement", "mdi:flash"}, - {"grid_current_l3", "Grid Current L3", 47, false, 0.1f, "A", "current", "measurement", "mdi:flash"}, - {"energy_today", "Energy Today", 53, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:solar-power"}, - {"energy_total", "Energy Total", 55, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:solar-power"}, - {"inverter_temp", "Inverter Temperature", 93, false, 0.1f, "°C", "temperature", "measurement", "mdi:thermometer"}, - {"bat_discharge_power", "Battery Discharge Power", 1009, true, 0.1f, "W", "power", "measurement", "mdi:battery-minus"}, - {"bat_charge_power", "Battery Charge Power", 1011, true, 0.1f, "W", "power", "measurement", "mdi:battery-plus"}, - {"bat_voltage", "Battery Voltage", 1013, false, 0.1f, "V", "voltage", "measurement", "mdi:battery"}, - {"bat_soc", "Battery State of Charge", 1014, false, 1.0f, "%", "battery", "measurement", "mdi:battery"}, - {"bat_temperature", "Battery Temperature", 1040, false, 0.1f, "°C", "temperature", "measurement", "mdi:thermometer"}, - {"power_to_grid", "Power To Grid", 1021, true, 0.1f, "W", "power", "measurement", "mdi:transmission-tower-export"}, - {"power_to_user", "Power To User", 1029, true, 0.1f, "W", "power", "measurement", "mdi:transmission-tower-import"}, - {"energy_import_total", "Energy Import Total", 1046, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:transmission-tower-import"}, - {"energy_export_total", "Energy Export Total", 1050, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:transmission-tower-export"}, - {"bat_discharge_total", "Battery Discharge Total", 1054, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:battery-minus"}, - {"bat_charge_total", "Battery Charge Total", 1058, true, 0.1f, "kWh", "energy", "total_increasing", "mdi:battery-plus"}, -}; -#else - #error "Kein Inverter-Modell definiert! INVERTER_MIC1500 oder INVERTER_SPH5000 in config.h setzen." -#endif -const uint8_t SENSOR_COUNT = sizeof(SENSORS) / sizeof(SENSORS[0]); - -// ============================================================ -// Globale Objekte -// ============================================================ -NetConfig cfg; -byte mac[] = {MAC_ADDRESS}; -EthernetClient ethClient; -PubSubClient mqtt(ethClient); -ModbusMaster modbus; -EthernetServer webServer(80); - -uint32_t g_okTotal = 0; -uint32_t g_failTotal = 0; -uint8_t g_lastErr = 0; -uint32_t g_rawTotal = 0; - -// ============================================================ -// Web-Server -// ============================================================ - -static bool webReadLine(EthernetClient& c, char* buf, size_t maxLen) { - size_t i = 0; - uint32_t t = millis(); - while (millis() - t < 1000) { - while (c.available()) { - char ch = c.read(); - if (ch == '\n') { buf[i] = 0; return true; } - if (ch != '\r' && i < maxLen - 1) buf[i++] = ch; - t = millis(); - } - } - buf[i] = 0; - return false; -} - -static void ipToStr(const uint8_t ip[4], char* buf) { - snprintf(buf, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); -} - -static void urlDecode(char* s) { - char* out = s; - while (*s) { - if (*s == '+') { - *out++ = ' '; s++; - } else if (*s == '%' && s[1] && s[2]) { - char h[3] = {s[1], s[2], 0}; - *out++ = (char)strtol(h, nullptr, 16); - s += 3; - } else { - *out++ = *s++; - } - } - *out = 0; -} - -static void parseIP(const char* s, uint8_t out[4]) { - unsigned a = 0, b = 0, c = 0, d = 0; - if (sscanf(s, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) { - out[0] = a; out[1] = b; out[2] = c; out[3] = d; - } -} - -static void parseFormBody(char* body) { - cfg.useDhcp = false; // Checkbox fehlt im POST wenn nicht gesetzt - char* p = body; - while (p && *p) { - char* eq = strchr(p, '='); - char* amp = strchr(p, '&'); - if (!eq) break; - *eq = 0; - if (amp) *amp = 0; - char* key = p; - char* val = eq + 1; - urlDecode(key); - urlDecode(val); - - if (!strcmp(key, "dhcp")) cfg.useDhcp = true; - else if (!strcmp(key, "ip")) parseIP(val, cfg.ip); - else if (!strcmp(key, "gw")) parseIP(val, cfg.gw); - else if (!strcmp(key, "sn")) parseIP(val, cfg.sn); - else if (!strcmp(key, "dns")) parseIP(val, cfg.dns); - else if (!strcmp(key, "broker")) strncpy(cfg.mqttBroker, val, sizeof(cfg.mqttBroker) - 1); - else if (!strcmp(key, "port")) cfg.mqttPort = (uint16_t)atoi(val); - else if (!strcmp(key, "user")) strncpy(cfg.mqttUser, val, sizeof(cfg.mqttUser) - 1); - else if (!strcmp(key, "pass")) strncpy(cfg.mqttPass, val, sizeof(cfg.mqttPass) - 1); - else if (!strcmp(key, "interval")) cfg.updateMs = (uint32_t)atoi(val) * 1000UL; - - p = amp ? amp + 1 : nullptr; - } -} - -static void sendPage(EthernetClient& cl) { - char buf[16]; - - cl.print( - "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n" - "" - "" - "" - "ShineLAN-X" - ); - - // Header - cl.print("

☀ ShineLAN-X

" - "
Growatt Modbus → MQTT Bridge
" - "
Verbunden" : "off'>Getrennt"); - cl.print("
IP: "); - cl.print(Ethernet.localIP()); - cl.print("
"); - - // Statistik - cl.print("
" - "
"); - cl.print(g_okTotal); - cl.print("
OK
" - "
"); - cl.print(g_failTotal); - cl.print("
Fehler
" - "
0x"); - snprintf(buf, sizeof(buf), "%02X", g_lastErr); - cl.print(buf); - cl.print("
Letzter Err
"); - - // Formular — Netzwerk - cl.print("
" - "

Netzwerk

" - "
" - "
"); - - ipToStr(cfg.ip, buf); - cl.print("
" - "
"); - ipToStr(cfg.gw, buf); - cl.print("
" - "
"); - ipToStr(cfg.sn, buf); - cl.print("
" - "
"); - ipToStr(cfg.dns, buf); - cl.print("
" - "
"); - cl.print("
"); - - // Formular — MQTT - cl.print("

MQTT

" - "
" - "
" - "
" - "
" - "
" - "
" - "
" - "
" - "
" - "
"); - - // Buttons + Script - cl.print( - "" - "" - "
" - "" - ); -} - -static void handleWebRequest() { - EthernetClient client = webServer.available(); - if (!client) return; - - char line[80]; - if (!webReadLine(client, line, sizeof(line))) { client.stop(); return; } - - bool isPost = (strncmp(line, "POST", 4) == 0); - bool isReset = (strstr(line, "/reset") != nullptr); - - // Header lesen, Content-Length suchen (case-insensitive via tolower) - int contentLen = 0; - char hdr[80]; - while (webReadLine(client, hdr, sizeof(hdr)) && hdr[0] != '\0') { - for (char* p = hdr; *p; p++) *p = (char)tolower((unsigned char)*p); - if (strncmp(hdr, "content-length:", 15) == 0) - contentLen = atoi(hdr + 15); - } - - if (isReset) { - configDefaults(cfg); - configSave(cfg); - client.print("HTTP/1.1 303 See Other\r\nLocation: /\r\nConnection: close\r\n\r\n"); - client.flush(); client.stop(); - delay(300); - NVIC_SystemReset(); - return; - } - - if (isPost && contentLen > 0 && contentLen < 500) { - char body[500] = {}; - int n = 0; - uint32_t t = millis(); - while (n < contentLen && millis() - t < 3000) { - if (client.available()) { body[n++] = client.read(); t = millis(); } - } - body[n] = 0; - - parseFormBody(body); - cfg.magic = CFG_MAGIC; - configSave(cfg); - - client.print("HTTP/1.1 303 See Other\r\nLocation: /\r\nConnection: close\r\n\r\n"); - client.flush(); client.stop(); - delay(300); - NVIC_SystemReset(); - return; - } - - sendPage(client); - client.flush(); - client.stop(); -} - -// ============================================================ -// LED -// ============================================================ -void ledSet(uint8_t pin, bool on) { digitalWrite(pin, on ? LOW : HIGH); } - -// ============================================================ -// MQTT -// ============================================================ -void publishDiscovery() { - char topic[128]; - char payload[640]; - - for (uint8_t i = 0; i < SENSOR_COUNT; i++) { - const Sensor& s = SENSORS[i]; - snprintf(topic, sizeof(topic), - "homeassistant/sensor/%s_%s/config", DEVICE_ID, s.id); - snprintf(payload, sizeof(payload), - "{" - "\"name\":\"%s\"," - "\"unique_id\":\"%s_%s\"," - "\"state_topic\":\"growatt/shinelan/%s\"," - "\"unit_of_measurement\":\"%s\"," - "\"device_class\":\"%s\"," - "\"state_class\":\"%s\"," - "\"icon\":\"%s\"," - "\"device\":{" - "\"identifiers\":[\"%s\"]," - "\"name\":\"%s\"," - "\"model\":\"%s\"," - "\"manufacturer\":\"%s\"" - "}}", - s.name, - DEVICE_ID, s.id, - s.id, - s.unit, s.deviceClass, s.stateClass, s.icon, - DEVICE_ID, DEVICE_NAME, DEVICE_MODEL, DEVICE_MFR); - mqtt.publish(topic, payload, true); - } -} - -bool mqttReconnect() { - DebugSerial.print("MQTT connecting... "); - ledSet(LED_RED, true); - bool ok = (strlen(cfg.mqttUser) > 0) - ? mqtt.connect(MQTT_CLIENT, cfg.mqttUser, cfg.mqttPass) - : mqtt.connect(MQTT_CLIENT); - if (ok) { - DebugSerial.println("OK"); - ledSet(LED_RED, false); - ledSet(LED_GREEN, true); - publishDiscovery(); - } else { - DebugSerial.print("FAIL rc="); - DebugSerial.println(mqtt.state()); - } - return ok; -} - -// ============================================================ -// Setup -// ============================================================ -void setup() { - DebugSerial.begin(115200); - delay(10); - DebugSerial.println("\r\n=== Growatt ShineLAN-X ==="); - DebugSerial.println("Build: " __DATE__ " " __TIME__); - - configLoad(cfg); - - pinMode(LED_DEBUG, OUTPUT); ledSet(LED_DEBUG, false); - pinMode(LED_RED, OUTPUT); ledSet(LED_RED, false); - pinMode(LED_GREEN, OUTPUT); ledSet(LED_GREEN, false); - pinMode(LED_BLUE, OUTPUT); ledSet(LED_BLUE, false); - ledSet(LED_RED, true); ledSet(LED_GREEN, true); ledSet(LED_BLUE, true); - delay(300); - ledSet(LED_RED, false); ledSet(LED_GREEN, false); ledSet(LED_BLUE, false); - - pinMode(ETH_RST_PIN, OUTPUT); - digitalWrite(ETH_RST_PIN, LOW); delay(20); - digitalWrite(ETH_RST_PIN, HIGH); delay(200); - Ethernet.init(ETH_CS_PIN); - - if (cfg.useDhcp) { - DebugSerial.println("ETH: DHCP..."); - if (Ethernet.begin(mac) == 0) { - // DHCP fehlgeschlagen: Fallback auf gespeicherte statische IP - DebugSerial.println("ETH: DHCP failed, static fallback"); - IPAddress ip (cfg.ip[0], cfg.ip[1], cfg.ip[2], cfg.ip[3]); - IPAddress gw (cfg.gw[0], cfg.gw[1], cfg.gw[2], cfg.gw[3]); - IPAddress sn (cfg.sn[0], cfg.sn[1], cfg.sn[2], cfg.sn[3]); - IPAddress dns(cfg.dns[0], cfg.dns[1], cfg.dns[2], cfg.dns[3]); - Ethernet.begin(mac, ip, dns, gw, sn); - } - } else { - IPAddress ip (cfg.ip[0], cfg.ip[1], cfg.ip[2], cfg.ip[3]); - IPAddress gw (cfg.gw[0], cfg.gw[1], cfg.gw[2], cfg.gw[3]); - IPAddress sn (cfg.sn[0], cfg.sn[1], cfg.sn[2], cfg.sn[3]); - IPAddress dns(cfg.dns[0], cfg.dns[1], cfg.dns[2], cfg.dns[3]); - Ethernet.begin(mac, ip, dns, gw, sn); - } - - DebugSerial.print("ETH: IP="); - DebugSerial.println(Ethernet.localIP()); - if (Ethernet.linkStatus() == LinkON) ledSet(LED_DEBUG, true); - - webServer.begin(); - DebugSerial.print("Web: http://"); - DebugSerial.println(Ethernet.localIP()); - - mqtt.setServer(cfg.mqttBroker, cfg.mqttPort); - mqtt.setBufferSize(768); - - SerialUSB.begin(MODBUS_BAUD); - delay(10000); - HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); - // Warte bis Wechselrichter USB-CDC konfiguriert hat - delay(3000); - // SERIAL_STATE(DCD=1,DSR=1) senden — NuttX tut dies beim Öffnen von ttyACM0 - // signalisiert dem Wechselrichter dass das Device bereit ist - sendSerialStateNotification(); - delay(500); - modbus.begin(MODBUS_ADDR, modbusSerial); - - DebugSerial.println("Setup done."); -} - -// ============================================================ -// Loop -// ============================================================ -unsigned long lastUpdate = 0; -static bool g_usbInfoPublished = false; - -void loop() { - handleWebRequest(); - - if (!mqtt.connected()) { - if (!mqttReconnect()) { - delay(5000); - return; - } - } - mqtt.loop(); - handleWebRequest(); - - // usbinfo: initial publish + DTR-Änderungen - { - static bool lastDtr = false; - if (!g_usbInfoPublished || (bool)dtrState != lastDtr) { - g_usbInfoPublished = true; - lastDtr = (bool)dtrState; - char info[80]; - snprintf(info, sizeof(info), "baud=%lu fmt=%u par=%u bits=%u dtr=%d rts=%d", - (unsigned long)linecoding.bitrate, linecoding.format, - linecoding.paritytype, linecoding.datatype, - (int)dtrState, (int)rtsState); - mqtt.publish("growatt/shinelan/usbinfo", info, true); - } - } - - // SERIAL_STATE(DCD=1,DSR=1) alle 5s senden bis DTR vom Wechselrichter gesetzt wird - { - static uint32_t lastNotif = 0; - if (!dtrState && millis() - lastNotif > 5000) { - lastNotif = millis(); - sendSerialStateNotification(); - } - } - - // Raw-RX-Drain: jedes Byte das außerhalb von Modbus-Fenstern ankommt fangen - // Beantwortet: sendet der Inverter JEMALS irgendwas (spontan oder verspätet)? - { - static uint8_t rawBuf[32] = {}; - static uint8_t rawLen = 0; - static uint32_t rawPublish = 0; - while (SerialUSB.available()) { - uint8_t b = (uint8_t)SerialUSB.read(); - g_rawTotal++; - if (rawLen < sizeof(rawBuf)) rawBuf[rawLen++] = b; - } - if (rawLen > 0 && millis() - rawPublish > 1000) { - rawPublish = millis(); - char msg[80]; - size_t p = snprintf(msg, sizeof(msg), "raw=%lu: ", g_rawTotal); - for (uint8_t i = 0; i < rawLen && p < 74; i++) - p += snprintf(msg + p, sizeof(msg) - p, "%02X", rawBuf[i]); - mqtt.publish("growatt/shinelan/rawbytes", msg); - rawLen = 0; - } - } - - if (millis() - lastUpdate < cfg.updateMs) return; - lastUpdate = millis(); - - ledSet(LED_BLUE, true); - - uint32_t okCnt = 0, failCnt = 0; - uint8_t lastErr = 0; - uint16_t lastErrReg = 0; - - for (uint8_t i = 0; i < SENSOR_COUNT; i++) { - const Sensor& s = SENSORS[i]; - modbusSerial.reset(); - - uint8_t res = modbus.readInputRegisters(s.address, s.isDword ? 2 : 1); - - char dbg[80]; - size_t pos = snprintf(dbg, sizeof(dbg), "ok=%lu fail=%lu err=0x%02X@%u avS=%d dtr=%d raw=%lu rx=%u tx=", - g_okTotal + okCnt, g_failTotal + failCnt, - lastErr, lastErrReg, - SerialUSB.availableForWrite(), - (int)dtrState, - g_rawTotal, - modbusSerial.rxLen); - for (uint8_t j = 0; j < modbusSerial.txLen && pos < 74; j++) - pos += snprintf(dbg + pos, sizeof(dbg) - pos, "%02X", modbusSerial.txBuf[j]); - pos += snprintf(dbg + pos, sizeof(dbg) - pos, " rx="); - for (uint8_t j = 0; j < modbusSerial.rxLen && pos < 78; j++) - pos += snprintf(dbg + pos, sizeof(dbg) - pos, "%02X", modbusSerial.rxBuf[j]); - mqtt.publish("growatt/shinelan/debug", dbg); - - delay(10); // mbusd -R10: 10ms Pause zwischen Requests - - if (res != modbus.ku8MBSuccess) { - failCnt++; - lastErr = res; - lastErrReg = s.address; - continue; - } - - okCnt++; - float val; - if (s.isDword) { - uint32_t raw = ((uint32_t)modbus.getResponseBuffer(0) << 16) - | modbus.getResponseBuffer(1); - val = raw * s.scale; - } else { - val = modbus.getResponseBuffer(0) * s.scale; - } - - char topic[64], payload[24]; - snprintf(topic, sizeof(topic), "growatt/shinelan/%s", s.id); - snprintf(payload, sizeof(payload), "%.1f", (double)val); - mqtt.publish(topic, payload); - } - - g_okTotal += okCnt; - g_failTotal += failCnt; - g_lastErr = lastErr; - - ledSet(LED_BLUE, false); -} diff --git a/ShineLAN-X/releases/.gitkeep b/ShineLAN-X/releases/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/ShineLAN-X/releases/nuttx-mbusd-shinelanx.bin b/ShineLAN-X/releases/nuttx-mbusd-shinelanx.bin deleted file mode 100755 index 8e69b46c82d695bb9bf71fe7f4a80af7bd15bcc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95384 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 e 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 kV@V(B&-^&d6n9+X{ej8-X($g2);X9100 vorhanden. -- Register identisch mit MIC 1500 TL-X. Andere MIC TL-X Modelle (1000W, 1500W, 2000W) verwenden dieselbe Registerstruktur. -- Passwörter und API-Keys sind durch Platzhalter ersetzt und müssen vor dem Einsatz angepasst werden. diff --git a/ShineWifi-X/Growatt MOD 6000 TL3-XH/Growatt MOD 6000 TL3-XH.yaml b/ShineWifi-X/Growatt MOD 6000 TL3-XH/Growatt MOD 6000 TL3-XH.yaml deleted file mode 100644 index d08939e..0000000 --- a/ShineWifi-X/Growatt MOD 6000 TL3-XH/Growatt MOD 6000 TL3-XH.yaml +++ /dev/null @@ -1,331 +0,0 @@ -esphome: - name: growatt-mod-6000 - friendly_name: Growatt MOD 6000 TL3-XH - -esp8266: - board: esp07s - -logger: - -api: - encryption: - key: "HIER_API_KEY_EINTRAGEN" - -ota: - - platform: esphome - password: "HIER_OTA_PASSWORT_EINTRAGEN" - -wifi: - ssid: "HIER_WLAN_NAME_EINTRAGEN" - password: "HIER_WLAN_PASSWORT_EINTRAGEN" - ap: - ssid: "Growatt MOD 6000 Fallback Hotspot" - password: "HIER_HOTSPOT_PASSWORT_EINTRAGEN" - -captive_portal: - -time: - - platform: homeassistant - id: homeassistant_time - -output: - - id: light_bl - platform: gpio - pin: 16 - - id: light_gr - platform: gpio - pin: 0 - - id: light_rd - platform: gpio - pin: 2 - -uart: - id: mod_bus - tx_pin: 1 - rx_pin: 3 - baud_rate: 115200 - -modbus: - id: modbus1 - uart_id: mod_bus - -modbus_controller: - - id: growatt - address: 1 - modbus_id: modbus1 - setup_priority: -10 - on_online: - then: - - output.turn_on: light_gr - on_offline: - then: - - output.turn_off: light_gr - -script: - - id: blink_blue - mode: single - then: - - output.turn_on: light_bl - - delay: 200ms - - output.turn_off: light_bl - -sensor: - # --- PV Eingang --- - - - platform: modbus_controller - name: "PV1 Voltage" - address: 3003 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:solar-panel - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - on_value: - then: - - script.execute: blink_blue - - - platform: modbus_controller - name: "PV1 Current" - address: 3004 - register_type: read - unit_of_measurement: A - device_class: current - state_class: measurement - icon: mdi:solar-panel - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "PV1 Power" - address: 3005 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:solar-panel - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "PV2 Voltage" - address: 3007 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:solar-panel - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "PV2 Current" - address: 3008 - register_type: read - unit_of_measurement: A - device_class: current - state_class: measurement - icon: mdi:solar-panel - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "PV2 Power" - address: 3009 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:solar-panel - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- AC Ausgang / Netz (dreiphasig) --- - - - platform: modbus_controller - name: "AC Output Power Total" - address: 3023 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:flash - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Frequency" - address: 3025 - register_type: read - unit_of_measurement: Hz - device_class: frequency - state_class: measurement - icon: mdi:sine-wave - value_type: U_WORD - accuracy_decimals: 2 - filters: - - multiply: 0.01 - - - platform: modbus_controller - name: "Grid Voltage L1" - address: 3026 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Current L1" - address: 3027 - register_type: read - unit_of_measurement: A - device_class: current - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Voltage L2" - address: 3030 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Current L2" - address: 3031 - register_type: read - unit_of_measurement: A - device_class: current - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Voltage L3" - address: 3034 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Current L3" - address: 3035 - register_type: read - unit_of_measurement: A - device_class: current - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- Energie (PV Erzeugung) --- - - - platform: modbus_controller - name: "Energy Today" - address: 3049 - register_type: read - unit_of_measurement: kWh - device_class: energy - state_class: total_increasing - icon: mdi:solar-power - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Energy Total" - address: 3051 - register_type: read - unit_of_measurement: kWh - device_class: energy - state_class: total_increasing - icon: mdi:solar-power - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- Temperatur --- - - - platform: modbus_controller - name: "Inverter Temperature" - address: 3093 - register_type: read - unit_of_measurement: "°C" - device_class: temperature - state_class: measurement - icon: mdi:thermometer - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- Smartmeter (optional, nur wenn Smartmeter angeschlossen) --- - - - platform: modbus_controller - name: "Power To User" - address: 3041 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:transmission-tower-import - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Power To Grid" - address: 3043 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:transmission-tower-export - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 diff --git a/ShineWifi-X/Growatt MOD 6000 TL3-XH/README.md b/ShineWifi-X/Growatt MOD 6000 TL3-XH/README.md deleted file mode 100644 index 86fbd4c..0000000 --- a/ShineWifi-X/Growatt MOD 6000 TL3-XH/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# Growatt MOD 6000 TL3-XH — ESPHome Konfiguration - -Modbus-RTU Integration des Growatt MOD 6000 TL3-XH (dreiphasiger String-Wechselrichter, kein Hybrid) über einen Growatt SuniStick mit ESPHome-Firmware. - ---- - -## Modbus-Register Übersicht - -Alle Register sind **Input Register** (Modbus Funktionscode 04, `register_type: "read"`). -Modbus-Adresse des Geräts: `0x01` - -> **Wichtig:** Die MOD TL3-XH Serie verwendet 3000er-Register — nicht die kleinen Register der MIC/MIN-Serie. - -### PV Eingang - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| PV1 Voltage | 3003 | U_WORD | 0.1 | V | -| PV1 Current | 3004 | U_WORD | 0.1 | A | -| PV1 Power | 3005 | U_DWORD | 0.1 | W | -| PV2 Voltage | 3007 | U_WORD | 0.1 | V | -| PV2 Current | 3008 | U_WORD | 0.1 | A | -| PV2 Power | 3009 | U_DWORD | 0.1 | W | - -### AC Ausgang / Netz (dreiphasig) - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| AC Output Power Total | 3023 | U_DWORD | 0.1 | W | -| Grid Frequency | 3025 | U_WORD | **0.01** | Hz | -| Grid Voltage L1 | 3026 | U_WORD | 0.1 | V | -| Grid Current L1 | 3027 | U_WORD | 0.1 | A | -| Grid Voltage L2 | 3030 | U_WORD | 0.1 | V | -| Grid Current L2 | 3031 | U_WORD | 0.1 | A | -| Grid Voltage L3 | 3034 | U_WORD | 0.1 | V | -| Grid Current L3 | 3035 | U_WORD | 0.1 | A | - -### Energie (PV Erzeugung) - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| Energy Today | 3049 | U_DWORD | 0.1 | kWh | -| Energy Total | 3051 | U_DWORD | 0.1 | kWh | - -### Temperatur - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| Inverter Temperature | 3093 | U_WORD | 0.1 | °C | - -### Smartmeter (optional) - -Nur relevant wenn ein Smartmeter am Wechselrichter angeschlossen ist. - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| Power To User (Bezug) | 3041 | U_DWORD | 0.1 | W | -| Power To Grid (Einspeisung) | 3043 | U_DWORD | 0.1 | W | - ---- - -## Hinweise - -- **Dreiphasig, kein Hybrid, kein Batterie-Interface.** -- Registerstruktur verifiziert gegen Growatt MOD TL3-XH Modbus-Protokoll (gleiche Familie wie MOD 7000 TL3-XH). -- Ohne angeschlossenen Smartmeter liefern Register 3041/3043 Nullwerte — diese Sensoren können dann entfernt werden. -- Passwörter und API-Keys sind durch Platzhalter ersetzt und müssen vor dem Einsatz angepasst werden. diff --git a/ShineWifi-X/Growatt SPH 5000 TL3/Growatt SPH 5000 TL3-BH-UP.yaml b/ShineWifi-X/Growatt SPH 5000 TL3/Growatt SPH 5000 TL3-BH-UP.yaml deleted file mode 100644 index 937cf70..0000000 --- a/ShineWifi-X/Growatt SPH 5000 TL3/Growatt SPH 5000 TL3-BH-UP.yaml +++ /dev/null @@ -1,452 +0,0 @@ -esphome: - name: growatt-sph5000 - friendly_name: Growatt SPH 5000 TL3-BH-UP - -esp8266: - board: esp07s - -logger: - -api: - encryption: - key: "HIER_API_KEY_EINTRAGEN" # 32-Byte Base64-Key, wird in der ESPHome-UI automatisch generiert - -ota: - - platform: esphome - password: "HIER_OTA_PASSWORT_EINTRAGEN" # Beliebiges Passwort für Over-the-Air Updates - -wifi: - ssid: "HIER_WLAN_NAME_EINTRAGEN" # Name deines WLANs (SSID) - password: "HIER_WLAN_PASSWORT_EINTRAGEN" # Passwort deines WLANs - ap: - ssid: "Growatt-SPH5000 Fallback Hotspot" - password: "HIER_HOTSPOT_PASSWORT_EINTRAGEN" # Passwort für den Notfall-Hotspot (min. 8 Zeichen) - -captive_portal: - -time: - - platform: homeassistant - id: homeassistant_time - -output: - - id: light_bl - platform: gpio - pin: 16 - - id: light_gr - platform: gpio - pin: 0 - - id: light_rd - platform: gpio - pin: 2 - -uart: - id: mod_bus - tx_pin: 1 - rx_pin: 3 - baud_rate: 115200 - -modbus: - id: modbus1 - uart_id: mod_bus - -modbus_controller: - - id: growatt - address: 1 - modbus_id: modbus1 - setup_priority: -10 - on_online: - then: - - output.turn_on: light_gr - on_offline: - then: - - output.turn_off: light_gr - -script: - - id: blink_blue - mode: single - then: - - output.turn_on: light_bl - - delay: 200ms - - output.turn_off: light_bl - -sensor: - # --- PV Eingang --- - - - platform: modbus_controller - name: "PV1 Voltage" - address: 3 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:solar-panel - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - on_value: - then: - - script.execute: blink_blue - - - platform: modbus_controller - name: "PV1 Current" - address: 4 - register_type: read - unit_of_measurement: A - device_class: current - state_class: measurement - icon: mdi:solar-panel - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "PV1 Power" - address: 5 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:solar-panel - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "PV2 Voltage" - address: 7 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:solar-panel - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "PV2 Current" - address: 8 - register_type: read - unit_of_measurement: A - device_class: current - state_class: measurement - icon: mdi:solar-panel - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "PV2 Power" - address: 9 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:solar-panel - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- AC Ausgang / Netz --- - - - platform: modbus_controller - name: "AC Output Power Total" - address: 35 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:flash - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Frequency" - address: 37 - register_type: read - unit_of_measurement: Hz - device_class: frequency - state_class: measurement - icon: mdi:sine-wave - value_type: U_WORD - accuracy_decimals: 2 - filters: - - multiply: 0.01 - - - platform: modbus_controller - name: "Grid Voltage L1" - address: 38 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Current L1" - address: 39 - register_type: read - unit_of_measurement: A - device_class: current - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Voltage L2" - address: 42 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Current L2" - address: 43 - register_type: read - unit_of_measurement: A - device_class: current - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Voltage L3" - address: 46 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Grid Current L3" - address: 47 - register_type: read - unit_of_measurement: A - device_class: current - state_class: measurement - icon: mdi:flash - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- Energie (PV Erzeugung) --- - - - platform: modbus_controller - name: "Energy Today" - address: 53 - register_type: read - unit_of_measurement: kWh - device_class: energy - state_class: total_increasing - icon: mdi:solar-power - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Energy Total" - address: 55 - register_type: read - unit_of_measurement: kWh - device_class: energy - state_class: total_increasing - icon: mdi:solar-power - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- Temperatur --- - - - platform: modbus_controller - name: "Inverter Temperature" - address: 93 - register_type: read - unit_of_measurement: "°C" - device_class: temperature - state_class: measurement - icon: mdi:thermometer - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- Batterie --- - - - platform: modbus_controller - name: "Battery Discharge Power" - address: 1009 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:battery-minus - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Battery Charge Power" - address: 1011 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:battery-plus - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Battery Voltage" - address: 1013 - register_type: read - unit_of_measurement: V - device_class: voltage - state_class: measurement - icon: mdi:battery - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Battery State of Charge" - address: 1014 - register_type: read - unit_of_measurement: "%" - device_class: battery - state_class: measurement - icon: mdi:battery - value_type: U_WORD - accuracy_decimals: 0 - - - platform: modbus_controller - name: "Battery Temperature" - address: 1040 - register_type: read - unit_of_measurement: "°C" - device_class: temperature - state_class: measurement - icon: mdi:thermometer - value_type: U_WORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- Batterie Energie (kWh Zähler für Energie-Dashboard) --- - - - platform: modbus_controller - name: "Battery Discharge Total" - address: 1054 - register_type: read - unit_of_measurement: kWh - device_class: energy - state_class: total_increasing - icon: mdi:battery-minus - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Battery Charge Total" - address: 1058 - register_type: read - unit_of_measurement: kWh - device_class: energy - state_class: total_increasing - icon: mdi:battery-plus - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- Smartmeter Momentanleistung --- - - - platform: modbus_controller - name: "Power To Grid" - address: 1021 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:transmission-tower-export - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Power To User" - address: 1029 - register_type: read - unit_of_measurement: W - device_class: power - state_class: measurement - icon: mdi:transmission-tower-import - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - # --- Netzenergie Import / Export (kWh Zähler für Energie-Dashboard) --- - - - platform: modbus_controller - name: "Energy Import Total" - address: 1046 - register_type: read - unit_of_measurement: kWh - device_class: energy - state_class: total_increasing - icon: mdi:transmission-tower-import - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 - - - platform: modbus_controller - name: "Energy Export Total" - address: 1050 - register_type: read - unit_of_measurement: kWh - device_class: energy - state_class: total_increasing - icon: mdi:transmission-tower-export - value_type: U_DWORD - accuracy_decimals: 1 - filters: - - multiply: 0.1 diff --git a/ShineWifi-X/Growatt SPH 5000 TL3/README.md b/ShineWifi-X/Growatt SPH 5000 TL3/README.md deleted file mode 100644 index e85b6aa..0000000 --- a/ShineWifi-X/Growatt SPH 5000 TL3/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# Growatt SPH 5000 TL3-BH-UP — ESPHome Konfiguration - -Modbus-RTU Integration des Growatt SPH 5000 TL3-BH-UP (3-phasiger Hybrid-Wechselrichter mit Batterie) über einen Growatt SuniStick mit ESPHome-Firmware. - ---- - -## Modbus-Register Übersicht - -Alle Register sind **Input Register** (Modbus Funktionscode 04, `register_type: "read"`). -Modbus-Adresse des Geräts: `0x01` - -### PV Eingang - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| PV1 Voltage | 3 | U_WORD | 0.1 | V | -| PV1 Current | 4 | U_WORD | 0.1 | A | -| PV1 Power | 5 | U_DWORD | 0.1 | W | -| PV2 Voltage | 7 | U_WORD | 0.1 | V | -| PV2 Current | 8 | U_WORD | 0.1 | A | -| PV2 Power | 9 | U_DWORD | 0.1 | W | - -### AC Ausgang / Netz - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| AC Output Power Total | 35 | U_DWORD | 0.1 | W | -| Grid Frequency | 37 | U_WORD | **0.01** | Hz | -| Grid Voltage L1 | 38 | U_WORD | 0.1 | V | -| Grid Current L1 | 39 | U_WORD | 0.1 | A | -| Grid Voltage L2 | 42 | U_WORD | 0.1 | V | -| Grid Current L2 | 43 | U_WORD | 0.1 | A | -| Grid Voltage L3 | 46 | U_WORD | 0.1 | V | -| Grid Current L3 | 47 | U_WORD | 0.1 | A | - -> **Wichtig:** Grid Frequency hat Multiplikator `0.01` — der Rohwert ist z.B. `5000` = 50,00 Hz. - -### Energie (PV Erzeugung) - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| Energy Today | 53 | U_DWORD | 0.1 | kWh | -| Energy Total | 55 | U_DWORD | 0.1 | kWh | - -### Temperatur - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| Inverter Temperature | 93 | U_WORD | 0.1 | °C | - -### Batterie — Momentanleistung - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| Battery Discharge Power | 1009 | U_DWORD | 0.1 | W | -| Battery Charge Power | 1011 | U_DWORD | 0.1 | W | -| Battery Voltage | 1013 | U_WORD | 0.1 | V | -| Battery State of Charge | 1014 | U_WORD | 1 | % | -| Battery Temperature | 1040 | U_WORD | 0.1 | °C | - -### Batterie — Energie (für Energie-Dashboard) - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| Battery Discharge Total | 1054 | U_DWORD | 0.1 | kWh | -| Battery Charge Total | 1058 | U_DWORD | 0.1 | kWh | - -> **Wichtig:** 1009/1011 = Momentanleistung in **Watt**. 1054/1058 = kumulierte Energie in **kWh** für das HA Energie-Dashboard. - -### Smartmeter — Momentanleistung - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| Power To Grid | 1021 | U_DWORD | 0.1 | W | -| Power To User | 1029 | U_DWORD | 0.1 | W | - -> **Hinweis:** Beide Werte sind immer positiv (unsigned). "To Grid" = Einspeisung, "To User" = Bezug vom Netz. - -### Netzenergie Import / Export (für Energie-Dashboard) - -| Name | Adresse | Typ | Multiplikator | Einheit | -|------|---------|-----|---------------|---------| -| Energy Import Total | 1046 | U_DWORD | 0.1 | kWh | -| Energy Export Total | 1050 | U_DWORD | 0.1 | kWh | - ---- - -## Hinweise - -- **U_DWORD** = 32-Bit-Wert über 2 aufeinanderfolgende Register. ESPHome liest diese automatisch korrekt, wenn `value_type: U_DWORD` gesetzt ist. -- Passwörter und API-Keys sind durch `#` ersetzt und müssen vor dem Einsatz angepasst werden. -- Diese Config wurde für den SPH 5000 TL3-BH-UP verifiziert. Andere SPH-Modelle (SPH 3000, SPH 6000 usw.) verwenden dieselbe Registerstruktur. diff --git a/tools/shinediag/diagnose.py b/tools/shinediag/diagnose.py deleted file mode 100644 index 6c86f02..0000000 --- a/tools/shinediag/diagnose.py +++ /dev/null @@ -1,278 +0,0 @@ -#!/usr/bin/env python3 -"""ShineDiag — Vor-Ort-Diagnose für Growatt-Wechselrichter via ShineLAN-X.""" - -import json -import logging -import os -import sqlite3 -import sys -import threading -import time -from typing import Dict, List, Optional - -from flask import Flask, jsonify, request, send_from_directory - -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../haos-addon/src")) -from inverters import INVERTERS -from modbus_client import ModbusReader - -logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s") -log = logging.getLogger(__name__) - -WEB_DIR = os.path.join(os.path.dirname(__file__), "web") -DB_PATH = os.environ.get("DB_PATH", "/var/lib/shinediag/history.db") -POLL_SEC = int(os.environ.get("POLL_SEC", "30")) - -app = Flask(__name__, static_folder=WEB_DIR) - -# ── SQLite ──────────────────────────────────────────────────── - -_db_lock = threading.Lock() -_db_conn: Optional[sqlite3.Connection] = None - - -def _db() -> sqlite3.Connection: - global _db_conn - if _db_conn is None: - os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) - _db_conn = sqlite3.connect(DB_PATH, check_same_thread=False) - _db_conn.execute("PRAGMA journal_mode=WAL") - _db_conn.execute("PRAGMA synchronous=NORMAL") - _db_conn.execute(""" - CREATE TABLE IF NOT EXISTS measurements ( - inv_key TEXT NOT NULL, - sensor_id TEXT NOT NULL, - ts REAL NOT NULL, - value REAL NOT NULL - )""") - _db_conn.execute(""" - CREATE INDEX IF NOT EXISTS idx_inv_sensor_ts - ON measurements(inv_key, sensor_id, ts)""") - _db_conn.commit() - return _db_conn - - -def db_write(inv_key: str, ts: float, values: Dict[str, float]): - rows = [(inv_key, sid, ts, v) for sid, v in values.items()] - with _db_lock: - _db().executemany( - "INSERT INTO measurements(inv_key,sensor_id,ts,value) VALUES(?,?,?,?)", rows) - _db().commit() - - -def db_query(inv_key: str, sensor_ids: List[str], - from_ts: float, to_ts: float) -> Dict[str, List]: - result = {sid: [] for sid in sensor_ids} - placeholders = ",".join("?" * len(sensor_ids)) - with _db_lock: - rows = _db().execute(f""" - SELECT sensor_id, ts, value FROM measurements - WHERE inv_key=? AND sensor_id IN ({placeholders}) - AND ts BETWEEN ? AND ? - ORDER BY ts ASC - """, [inv_key] + sensor_ids + [from_ts, to_ts]).fetchall() - for sid, ts, val in rows: - if sid in result: - result[sid].append({"ts": ts, "v": val}) - return result - - -def db_cleanup(days: int = 7): - cutoff = time.time() - days * 86400 - with _db_lock: - n = _db().execute("DELETE FROM measurements WHERE ts> /etc/dhcpcd.conf - -# DHCP-Server für eth0 -cp setup/dnsmasq.conf /etc/dnsmasq.conf -systemctl enable dnsmasq - -# WiFi-Hotspot -cp setup/hostapd.conf /etc/hostapd/hostapd.conf -echo 'DAEMON_CONF="/etc/hostapd/hostapd.conf"' >> /etc/default/hostapd -systemctl unmask hostapd -systemctl enable hostapd - -# Wlan0 IP -cat >> /etc/dhcpcd.conf << 'EOF' - -interface wlan0 -static ip_address=10.0.1.1/24 -nohook wpa_supplicant -EOF - -# DHCP auch für wlan0 (MacBook-Verbindung) -cat >> /etc/dnsmasq.conf << 'EOF' - -interface=wlan0 -dhcp-range=10.0.1.100,10.0.1.200,1h -EOF - -# systemd-Service für ShineDiag -INSTALL_DIR="$(pwd)" -cat > /etc/systemd/system/shinediag.service << EOF -[Unit] -Description=ShineDiag Diagnose-Tool -After=network.target - -[Service] -ExecStart=/usr/bin/python3 ${INSTALL_DIR}/diagnose.py -WorkingDirectory=${INSTALL_DIR} -Environment=PORT=80 -Environment=SHINELANX_IP=10.0.0.100 -Restart=always -User=root - -[Install] -WantedBy=multi-user.target -EOF - -systemctl daemon-reload -systemctl enable shinediag - -echo "" -echo "=== Fertig! Bitte neu starten: sudo reboot ===" -echo "" -echo "Nach dem Neustart:" -echo " WiFi: 'ShineDiag' | Passwort: shinelanx" -echo " Browser: http://10.0.1.1" diff --git a/tools/shinediag/web/index.html b/tools/shinediag/web/index.html deleted file mode 100644 index 9aa23ce..0000000 --- a/tools/shinediag/web/index.html +++ /dev/null @@ -1,601 +0,0 @@ - - - - - -ShineDiag - - - - -
-
- - - - - - - -

ShineDiag

Nicht verbunden

-
-
-
- -
- - - - -
- -
- - -
- -
-
Verbindung
- - -
-
-
-
- - - -
- - - -
- - - - - - - - - - -
- - - -