ShineLAN-X: Initiale Firmware + Hardware-Diagnose
- STM32F103RBT6 Firmware für Growatt ShineLAN-X - Bitbang-SPI (EthernetENC) auf Port C (PC6/PC7/PC8/PC9) - UART-Debug auf USART1 (PA9/PA10), Modbus temporär deaktiviert - SO-Aktivitätstest und ESTAT-Register-Scan bestätigen: ENC28J60 läuft (SO aktiv), SI/MOSI-Verbindung unterbrochen - Nächster Schritt: Pin 5 ENC28J60 nachlöten oder Bodge-Draht Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
# 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** in Home Assistant — vollständig lokal, ohne Growatt-Cloud.
|
||||
|
||||
---
|
||||
|
||||
## Hardware
|
||||
|
||||
| Komponente | Details |
|
||||
|---|---|
|
||||
| MCU | STM32F103 (LQFP48) |
|
||||
| Ethernet | Unbekannt — vermutlich W5500 (SPI) — **TODO: PCB prüfen** |
|
||||
| RS485 | Transceiver auf Platine — **TODO: Chip-Bezeichnung prüfen** |
|
||||
| Programmier-Interface | SWD (SWDIO, SWDCLK, GND, 3.3V) |
|
||||
| Anschluss | RJ45 (Ethernet) + RS485-Port am Wechselrichter |
|
||||
|
||||
### Pin-Belegung (TODO — muss durch PCB-Analyse bestätigt werden)
|
||||
|
||||
| Funktion | STM32 Pin | Hinweis |
|
||||
|---|---|---|
|
||||
| SPI SCK | PA5 | SPI1, fest |
|
||||
| SPI MISO | PA6 | SPI1, fest |
|
||||
| SPI MOSI | PA7 | SPI1, fest |
|
||||
| ETH CS | PA4 | TODO: nachmessen |
|
||||
| ETH RST | PB1 | TODO: nachmessen |
|
||||
| RS485 DE/RE | PB0 | TODO: nachmessen |
|
||||
| Modbus TX | PA9 | USART1, fest |
|
||||
| Modbus RX | PA10 | USART1, fest |
|
||||
| Debug TX | PA2 | USART2 (optional) |
|
||||
|
||||
> **Wichtig:** Die mit TODO markierten Pins müssen durch Nachmessen mit einem Multimeter auf der PCB bestätigt werden, bevor die Firmware geflasht wird. Falsche Pins beschädigen nichts, aber die Firmware funktioniert nicht.
|
||||
|
||||
---
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- [PlatformIO](https://platformio.org/) (VS Code Extension oder CLI)
|
||||
- ST-Link V2 Adapter (zum Flashen via SWD)
|
||||
- ShineLAN-X geöffnet und SWD-Pins angelötet oder angeklemmt
|
||||
|
||||
### SWD-Verkabelung
|
||||
|
||||
| ShineLAN-X | 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` anpassen:
|
||||
|
||||
```cpp
|
||||
// MQTT-Broker IP
|
||||
#define MQTT_BROKER "192.168.1.x"
|
||||
|
||||
// DHCP (1) oder statische IP (0)
|
||||
#define USE_DHCP 1
|
||||
|
||||
// Modbus Slave-Adresse des Wechselrichters (Standard: 1)
|
||||
#define MODBUS_ADDR 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Flashen
|
||||
|
||||
```bash
|
||||
cd firmware
|
||||
pio run --target upload
|
||||
```
|
||||
|
||||
Nach dem ersten Flash sind weitere Updates ebenfalls per ST-Link möglich, solange die SWD-Pins zugänglich sind.
|
||||
|
||||
---
|
||||
|
||||
## MQTT Topics
|
||||
|
||||
Nach dem Start sendet die Firmware automatisch **MQTT Discovery** Pakete — Home Assistant erkennt das Gerät sofort ohne manuelle Konfiguration.
|
||||
|
||||
| Topic | Inhalt |
|
||||
|---|---|
|
||||
| `homeassistant/sensor/growatt_shinelan_*/config` | Discovery (retained) |
|
||||
| `growatt/shinelan/pv1_power` | PV1 Leistung in W |
|
||||
| `growatt/shinelan/energy_total` | PV Gesamtenergie in kWh |
|
||||
| `growatt/shinelan/bat_soc` | Batterie Ladestand in % |
|
||||
| `growatt/shinelan/energy_import_total` | Netzbezug gesamt in kWh |
|
||||
| … | alle weiteren Sensoren analog |
|
||||
|
||||
---
|
||||
|
||||
## Unterstützte Sensoren
|
||||
|
||||
Alle Sensoren entsprechen den Modbus-Registern des **Growatt SPH 5000 TL3-BH-UP**.
|
||||
Für andere Modelle (z.B. MIC 1500 ohne Batterie) können nicht vorhandene Sensoren in `main.cpp` auskommentiert werden — fehlerhafte Modbus-Reads werden automatisch übersprungen.
|
||||
|
||||
| Sensor | Register | Einheit | state_class |
|
||||
|---|---|---|---|
|
||||
| PV1/PV2 Voltage | 3, 7 | V | measurement |
|
||||
| PV1/PV2 Current | 4, 8 | A | measurement |
|
||||
| PV1/PV2 Power | 5, 9 | W | measurement |
|
||||
| AC Output Power Total | 35 | W | measurement |
|
||||
| Grid Frequency | 37 | Hz | measurement |
|
||||
| Grid Voltage L1/L2/L3 | 38, 42, 46 | V | measurement |
|
||||
| Grid Current L1/L2/L3 | 39, 43, 47 | A | measurement |
|
||||
| Energy Today | 53 | kWh | total_increasing |
|
||||
| Energy Total | 55 | kWh | total_increasing |
|
||||
| Inverter Temperature | 93 | °C | measurement |
|
||||
| Battery Discharge/Charge Power | 1009, 1011 | W | measurement |
|
||||
| Battery Voltage | 1013 | V | measurement |
|
||||
| Battery State of Charge | 1014 | % | measurement |
|
||||
| Battery Temperature | 1040 | °C | measurement |
|
||||
| Energy Import/Export Total | 1046, 1050 | kWh | total_increasing |
|
||||
| Battery Discharge/Charge Total | 1054, 1058 | kWh | total_increasing |
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================
|
||||
// NETZWERK — ENC28J60 Ethernet (Bitbang-SPI auf Port C)
|
||||
// STM32F103RBT6, LQFP-64
|
||||
// Hardware-SPI nicht nutzbar — alle SPI-Pins liegen auf Port C
|
||||
// ============================================================
|
||||
|
||||
// Bitbang-SPI Pins (alle gemessen)
|
||||
#define ETH_CS_PIN PC7 // ENC28J60 Pin 7 /CS → STM32 Pin 36 (gemessen)
|
||||
#define ETH_SCK_PIN PC6 // ENC28J60 Pin 6 SCK → STM32 Pin 35 (gemessen)
|
||||
#define ETH_MISO_PIN PC8 // ENC28J60 Pin 4 SO → STM32 Pin 37 (gemessen)
|
||||
#define ETH_MOSI_PIN PC9 // ENC28J60 Pin 5 SI → STM32 Pin ?? (noch unbekannt, Scan läuft)
|
||||
|
||||
// ENC28J60 Reset
|
||||
#define ETH_RST_PIN PB13 // ENC28J60 Pin 18 /RESET → STM32 Pin 32, 500Ω
|
||||
|
||||
// 0 = DHCP, 1 = Statische IP
|
||||
#define USE_DHCP 0
|
||||
|
||||
// Nur relevant wenn USE_DHCP = 0
|
||||
#define STATIC_IP 192,168,2,15
|
||||
#define STATIC_GW 192,168,2,1
|
||||
#define STATIC_SUBNET 255,255,255,0
|
||||
#define STATIC_DNS 192,168,2,1
|
||||
|
||||
// MAC-Adresse — muss im Netzwerk eindeutig sein
|
||||
#define MAC_ADDRESS 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
|
||||
|
||||
// ============================================================
|
||||
// MQTT
|
||||
// ============================================================
|
||||
#define MQTT_BROKER "192.168.2.84"
|
||||
#define MQTT_PORT 1883
|
||||
#define MQTT_USER "" // Leer lassen wenn kein Auth
|
||||
#define MQTT_PASSWORD ""
|
||||
#define MQTT_CLIENT "growatt-shinelan"
|
||||
|
||||
// ============================================================
|
||||
// RS485 / MODBUS
|
||||
// RS485 DE/RE → STM32 Pin 25 = PB1 (LQFP-64 neu gemessen)
|
||||
// USART1: TX=PA9 (Pin 40), RX=PA10 (Pin 41)
|
||||
// ============================================================
|
||||
#define RS485_DE_PIN PB1 // RE/DE Steuerpin → STM32 Pin 25
|
||||
#define MODBUS_BAUD 9600 // Growatt Standard-Baudrate
|
||||
#define MODBUS_ADDR 1 // Modbus Slave-Adresse des Wechselrichters
|
||||
|
||||
// ============================================================
|
||||
// GERÄT
|
||||
// ============================================================
|
||||
#define DEVICE_ID "growatt_shinelan"
|
||||
#define DEVICE_NAME "Growatt ShineLAN-X"
|
||||
#define DEVICE_MODEL "ShineLAN-X"
|
||||
#define DEVICE_MFR "Growatt"
|
||||
|
||||
// Abfrageintervall in Millisekunden
|
||||
#define UPDATE_INTERVAL 10000UL
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"type": "library", "name": "EthernetENC", "version": "2.0.5", "spec": {"owner": "jandrassy", "id": 11203, "name": "EthernetENC", "requirements": null, "uri": null}}
|
||||
@@ -0,0 +1,158 @@
|
||||
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 <Ethernet.h>` to `#include <EthernetENC.h>`. 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 <malvineous@shikadi.net>, 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 <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
--------------
|
||||
|
||||
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 <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
--------------
|
||||
|
||||
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 <adam@sics.se>, <adam@dunkels.com>.
|
||||
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
|
||||
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
name=EthernetENC
|
||||
author=Norbert Truchsess, Juraj Andrassy
|
||||
maintainer=Juraj Andrassy <juraj.andrassy@gmail.com>
|
||||
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
|
||||
@@ -0,0 +1,491 @@
|
||||
// DHCP Library v0.3 - April 25, 2009
|
||||
// Author: Jordan Terrell - blog.jordanterrell.com
|
||||
|
||||
#include "Dhcp.h"
|
||||
#include <Arduino.h>
|
||||
#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);
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
// 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
|
||||
@@ -0,0 +1,416 @@
|
||||
// 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 <Arduino.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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
|
||||
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
UIPEthernet.cpp - Arduino implementation of a uIP wrapper class.
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#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();
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
UIPEthernet.h - Arduino implementation of a uIP wrapper class.
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UIPETHERNET_H
|
||||
#define UIPETHERNET_H
|
||||
|
||||
//#define UIPETHERNET_DEBUG
|
||||
//#define UIPETHERNET_DEBUG_CHKSUM
|
||||
//#define UIPETHERNET_DEBUG_UDP
|
||||
//#define UIPETHERNET_DEBUG_CLIENT
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Dhcp.h"
|
||||
#include <IPAddress.h>
|
||||
#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
|
||||
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
UIPClient.cpp - Arduino implementation of a uIP wrapper class.
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
UIPClient.h - Arduino implementation of a uIP wrapper class.
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UIPCLIENT_H
|
||||
#define UIPCLIENT_H
|
||||
|
||||
#include <Client.h>
|
||||
#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
|
||||
@@ -0,0 +1 @@
|
||||
#include "Ethernet.h"
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
UIPServer.cpp - Arduino implementation of a uIP wrapper class.
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
UIPServer.h - Arduino implementation of a uIP wrapper class.
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
UIPUdp.cpp - Arduino implementation of a uIP wrapper class.
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
UIPUdp.h - Arduino implementation of a uIP wrapper class.
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UIPUDP_H
|
||||
#define UIPUDP_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Udp.h>
|
||||
#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
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
#ifndef UIP_TCP_STATES_H
|
||||
#define UIP_TCP_STATES_H
|
||||
|
||||
#include <utility/uip.h>
|
||||
|
||||
// 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
|
||||
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
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 <Arduino.h>
|
||||
#include "config.h" // ETH_SCK_PIN / ETH_MOSI_PIN / ETH_MISO_PIN
|
||||
// Note: <SPI.h> 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;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
Enc28J60NetworkClass.h
|
||||
UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface.
|
||||
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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_ */
|
||||
@@ -0,0 +1,257 @@
|
||||
/*****************************************************************************
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
|
||||
// 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
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
mempool.cpp - sleek implementation of a memory pool
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mempool.h"
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
mempool.h - sleek implementation of a memory pool
|
||||
Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MEMPOOL_H
|
||||
#define MEMPOOL_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef MEMPOOLCONF_H
|
||||
#define MEMPOOLCONF_H
|
||||
#include "uipethernet-conf.h"
|
||||
extern "C" {
|
||||
#include "uipopt.h"
|
||||
#include "enc28j60.h"
|
||||
}
|
||||
#include <inttypes.h>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* UIPEthernet Project-specific configuration options
|
||||
* Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
|
||||
* @{
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#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__ */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* \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 <adam@dunkels.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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__ */
|
||||
@@ -0,0 +1,422 @@
|
||||
/**
|
||||
* \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 <adam@dunkels.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* \addtogroup uip
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \addtogroup uiparp
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Macros and definitions for the ARP module.
|
||||
* \author Adam Dunkels <adam@dunkels.com>
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* 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__ */
|
||||
@@ -0,0 +1,48 @@
|
||||
#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
|
||||
@@ -0,0 +1,555 @@
|
||||
/**
|
||||
* \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 <adam@dunkels.com>
|
||||
*
|
||||
* 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__ */
|
||||
@@ -0,0 +1,10 @@
|
||||
#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
|
||||
@@ -0,0 +1,16 @@
|
||||
[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
|
||||
-I include
|
||||
@@ -0,0 +1,292 @@
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include <EthernetENC.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <ModbusMaster.h>
|
||||
#include "config.h"
|
||||
|
||||
// Debug-UART: USART1, TX=PA9, RX=PA10 (Testpunkt auf Platine)
|
||||
// HINWEIS: Modbus ist temporär deaktiviert — erst Ethernet/MQTT bestätigen,
|
||||
// dann Debug entfernen und Modbus wieder aktivieren.
|
||||
HardwareSerial DebugSerial(PA10, PA9);
|
||||
|
||||
// ============================================================
|
||||
// Sensor-Definition
|
||||
// ============================================================
|
||||
struct Sensor {
|
||||
const char* id;
|
||||
const char* name;
|
||||
uint16_t address;
|
||||
bool isDword; // true = 2 Register (32 bit), false = 1 Register (16 bit)
|
||||
float scale;
|
||||
const char* unit;
|
||||
const char* deviceClass;
|
||||
const char* stateClass;
|
||||
const char* icon;
|
||||
};
|
||||
|
||||
// Sensor-Liste — entspricht den Modbus-Registern des SPH 5000 TL3-BH-UP
|
||||
// Für andere Modelle nicht zutreffende Sensoren auskommentieren.
|
||||
const Sensor SENSORS[] = {
|
||||
// --- PV Eingang ---
|
||||
{"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 Ausgang / Netz ---
|
||||
{"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"},
|
||||
// --- Energie PV ---
|
||||
{"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"},
|
||||
// --- Temperatur ---
|
||||
{"inverter_temp", "Inverter Temperature", 93, false, 0.1f, "°C", "temperature", "measurement", "mdi:thermometer"},
|
||||
// --- Batterie ---
|
||||
{"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"},
|
||||
// --- Netz- und Batterie-Energiezähler (für Energie-Dashboard) ---
|
||||
{"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"},
|
||||
};
|
||||
const uint8_t SENSOR_COUNT = sizeof(SENSORS) / sizeof(SENSORS[0]);
|
||||
|
||||
// ============================================================
|
||||
// Globale Objekte
|
||||
// ============================================================
|
||||
// HardwareSerial modbusSerial(PA10, PA9); // USART1 — temporär deaktiviert (teilt sich UART mit DebugSerial)
|
||||
|
||||
byte mac[] = {MAC_ADDRESS};
|
||||
EthernetClient ethClient;
|
||||
PubSubClient mqtt(ethClient);
|
||||
// ModbusMaster modbus; // temporär deaktiviert
|
||||
|
||||
// ============================================================
|
||||
// RS485 Richtungssteuerung (Callbacks für ModbusMaster)
|
||||
// ============================================================
|
||||
void preTransmission() { digitalWrite(RS485_DE_PIN, HIGH); }
|
||||
void postTransmission() { digitalWrite(RS485_DE_PIN, LOW); }
|
||||
|
||||
// ============================================================
|
||||
// MQTT Hilfsfunktionen
|
||||
// ============================================================
|
||||
|
||||
// Veröffentlicht alle Sensor-Discovery-Pakete für Home Assistant
|
||||
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); // retained = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool mqttReconnect() {
|
||||
DebugSerial.print("MQTT connecting... ");
|
||||
bool ok;
|
||||
if (strlen(MQTT_USER) > 0) {
|
||||
ok = mqtt.connect(MQTT_CLIENT, MQTT_USER, MQTT_PASSWORD);
|
||||
} else {
|
||||
ok = mqtt.connect(MQTT_CLIENT);
|
||||
}
|
||||
if (ok) {
|
||||
DebugSerial.println("OK");
|
||||
publishDiscovery();
|
||||
} else {
|
||||
DebugSerial.print("FAIL rc=");
|
||||
DebugSerial.println(mqtt.state());
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Setup
|
||||
// ============================================================
|
||||
void setup() {
|
||||
// Debug-UART zuerst starten (USART1: TX=PA9 = Testpunkt auf Platine)
|
||||
DebugSerial.begin(115200);
|
||||
delay(10);
|
||||
DebugSerial.println("\r\n=== Growatt ShineLAN-X ===");
|
||||
DebugSerial.println("Build: " __DATE__ " " __TIME__);
|
||||
|
||||
// RS485 DE/RE Pin
|
||||
pinMode(RS485_DE_PIN, OUTPUT);
|
||||
digitalWrite(RS485_DE_PIN, LOW); // Empfangsmodus
|
||||
|
||||
// ENC28J60 Reset (Hardware-Reset vor init)
|
||||
DebugSerial.println("ETH: reset...");
|
||||
pinMode(ETH_RST_PIN, OUTPUT);
|
||||
digitalWrite(ETH_RST_PIN, LOW);
|
||||
delay(50);
|
||||
digitalWrite(ETH_RST_PIN, HIGH);
|
||||
delay(200);
|
||||
|
||||
// SO-Aktivitätstest: Ist der ENC28J60 überhaupt am Leben?
|
||||
// PC8 mit Pull-Down → wenn ENC28J60 SO aktiv HIGH treibt = Chip antwortet
|
||||
// Wenn PC8 bleibt LOW = SO ist High-Z = Chip tot/kein Takt/keine Power
|
||||
pinMode(ETH_CS_PIN, OUTPUT); digitalWrite(ETH_CS_PIN, HIGH);
|
||||
pinMode(ETH_SCK_PIN, OUTPUT); digitalWrite(ETH_SCK_PIN, LOW);
|
||||
pinMode(PC8, INPUT_PULLDOWN);
|
||||
|
||||
DebugSerial.println("SO-Aktivitaetstest (PC8 = INPUT_PULLDOWN):");
|
||||
|
||||
// Test 1: CS hoch (SO sollte High-Z sein → LOW erwartet)
|
||||
uint8_t so_cs_high = digitalRead(PC8);
|
||||
DebugSerial.print(" CS=H SO="); DebugSerial.print(so_cs_high);
|
||||
DebugSerial.println(so_cs_high == 0 ? " (High-Z, erwartet)" : " (getrieben! unerwartet)");
|
||||
|
||||
// Test 2: CS runter (ENC28J60 soll SO aktivieren)
|
||||
digitalWrite(ETH_CS_PIN, LOW);
|
||||
delayMicroseconds(50);
|
||||
uint8_t so_cs_low = digitalRead(PC8);
|
||||
digitalWrite(ETH_CS_PIN, HIGH);
|
||||
DebugSerial.print(" CS=L SO="); DebugSerial.print(so_cs_low);
|
||||
if (so_cs_low == 1) DebugSerial.println(" --> Chip treibt SO! Chip ist am Leben.");
|
||||
else DebugSerial.println(" --> SO bleibt LOW = Chip antwortet nicht (kein Takt? kein Strom?)");
|
||||
|
||||
// Test 3: Reset-Zyklus, dann CS low
|
||||
digitalWrite(ETH_RST_PIN, LOW); delay(10);
|
||||
digitalWrite(ETH_RST_PIN, HIGH); delay(100);
|
||||
digitalWrite(ETH_CS_PIN, LOW);
|
||||
delayMicroseconds(50);
|
||||
uint8_t so_after_reset = digitalRead(PC8);
|
||||
digitalWrite(ETH_CS_PIN, HIGH);
|
||||
DebugSerial.print(" nach Reset, CS=L SO="); DebugSerial.print(so_after_reset);
|
||||
if (so_after_reset == 1) DebugSerial.println(" --> Chip lebt!");
|
||||
else DebugSerial.println(" --> immer noch kein Leben");
|
||||
|
||||
// ============================================================
|
||||
// Roher SPI-Test: ESTAT-Register lesen (kein EthernetENC)
|
||||
// ENC28J60 ESTAT = Bank0, Addr 0x1D
|
||||
// Read Control Register: opcode 000 | addr 11101 = 0x1D
|
||||
// Erwarteter Wert nach Reset: 0x01 (CLKRDY-Bit gesetzt)
|
||||
// ============================================================
|
||||
|
||||
// Langer Reset-Zyklus und Wartezeit für CLKRDY
|
||||
pinMode(ETH_RST_PIN, OUTPUT);
|
||||
digitalWrite(ETH_RST_PIN, LOW); delay(20);
|
||||
digitalWrite(ETH_RST_PIN, HIGH); delay(800); // 25 MHz Quarz braucht <1 ms
|
||||
|
||||
pinMode(ETH_CS_PIN, OUTPUT); digitalWrite(ETH_CS_PIN, HIGH);
|
||||
pinMode(ETH_SCK_PIN, OUTPUT); digitalWrite(ETH_SCK_PIN, LOW);
|
||||
pinMode(ETH_MISO_PIN, INPUT); // kein Pull — Chip treibt SO direkt
|
||||
|
||||
// Inline-Hilfsfunktion: 1 Byte über SPI senden und empfangen
|
||||
// MOSI-Pin wird als Parameter übergeben (Kandidaten-Scan)
|
||||
auto spiXfer = [](uint8_t mosiPin, uint8_t out) -> uint8_t {
|
||||
uint8_t in = 0;
|
||||
for (int8_t i = 7; i >= 0; i--) {
|
||||
digitalWrite(mosiPin, (out >> i) & 1);
|
||||
digitalWrite(ETH_SCK_PIN, HIGH);
|
||||
in = (in << 1) | digitalRead(ETH_MISO_PIN);
|
||||
digitalWrite(ETH_SCK_PIN, LOW);
|
||||
}
|
||||
return in;
|
||||
};
|
||||
|
||||
auto readESTAT = [&](uint8_t mosiPin) -> uint8_t {
|
||||
digitalWrite(ETH_CS_PIN, LOW);
|
||||
spiXfer(mosiPin, 0x1D); // RCR ESTAT
|
||||
uint8_t val = spiXfer(mosiPin, 0x00);
|
||||
digitalWrite(ETH_CS_PIN, HIGH);
|
||||
return val;
|
||||
};
|
||||
|
||||
// MOSI-Kandidaten
|
||||
const uint8_t MOSI_CANDIDATES[] = { PC9, PB14, PB15, PB10, PB11, PA5, PA7 };
|
||||
const char* MOSI_NAMES[] = {"PC9","PB14","PB15","PB10","PB11","PA5","PA7"};
|
||||
const uint8_t NUM_CAND = sizeof(MOSI_CANDIDATES) / sizeof(MOSI_CANDIDATES[0]);
|
||||
|
||||
DebugSerial.println("SPI ESTAT-Scan (Erwartung: 0x01):");
|
||||
for (uint8_t c = 0; c < NUM_CAND; c++) {
|
||||
uint8_t pin = MOSI_CANDIDATES[c];
|
||||
pinMode(pin, OUTPUT); digitalWrite(pin, LOW);
|
||||
|
||||
// Chip-Reset zwischen Kandidaten
|
||||
digitalWrite(ETH_RST_PIN, LOW); delay(5);
|
||||
digitalWrite(ETH_RST_PIN, HIGH); delay(50);
|
||||
|
||||
uint8_t estat = readESTAT(pin);
|
||||
|
||||
DebugSerial.print(" MOSI="); DebugSerial.print(MOSI_NAMES[c]);
|
||||
DebugSerial.print(" -> ESTAT=0x"); DebugSerial.print(estat, HEX);
|
||||
if (estat == 0x01) DebugSerial.println(" <-- TREFFER! SPI OK");
|
||||
else if (estat == 0xFF) DebugSerial.println(" (kein Kontakt)");
|
||||
else DebugSerial.println(" (unbekannt)");
|
||||
|
||||
pinMode(pin, INPUT); // danach wieder floating lassen
|
||||
}
|
||||
|
||||
DebugSerial.println("Scan fertig.");
|
||||
DebugSerial.println("Setup done (Ethernet deaktiviert bis MOSI gefunden).");
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Loop
|
||||
// ============================================================
|
||||
unsigned long lastUpdate = 0;
|
||||
|
||||
void loop() {
|
||||
// MQTT Verbindung halten
|
||||
if (!mqtt.connected()) {
|
||||
if (!mqttReconnect()) {
|
||||
delay(5000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mqtt.loop();
|
||||
|
||||
if (millis() - lastUpdate < UPDATE_INTERVAL) return;
|
||||
lastUpdate = millis();
|
||||
|
||||
char stateTopic[64];
|
||||
char valueStr[16];
|
||||
|
||||
// Modbus temporär deaktiviert — Sensor-Loop übersprungen
|
||||
DebugSerial.println("loop: MQTT ok, Modbus disabled");
|
||||
(void)stateTopic;
|
||||
(void)valueStr;
|
||||
}
|
||||
Reference in New Issue
Block a user