├── src ├── EthernetENC.h ├── utility │ ├── util.h │ ├── mempool_conf.h │ ├── uipethernet-conf.h │ ├── mempool.h │ ├── Enc28J60Network.h │ ├── mempool.cpp │ ├── uip_arch.h │ ├── uip-conf.h │ ├── uip_arp.h │ ├── enc28j60.h │ ├── uip_arp.c │ ├── uipopt.h │ └── Enc28J60Network.cpp ├── tcp_states.h ├── Dns.h ├── EthernetServer.h ├── EthernetClient.h ├── EthernetServer.cpp ├── EthernetUdp.h ├── Ethernet.h ├── Dhcp.h ├── EthernetUdp.cpp ├── Dns.cpp ├── Ethernet.cpp ├── Dhcp.cpp └── EthernetClient.cpp ├── library.properties └── README.md /src/EthernetENC.h: -------------------------------------------------------------------------------- 1 | #include "Ethernet.h" 2 | -------------------------------------------------------------------------------- /src/utility/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ 5 | ((x)<< 8 & 0x00FF0000UL) | \ 6 | ((x)>> 8 & 0x0000FF00UL) | \ 7 | ((x)>>24 & 0x000000FFUL) ) 8 | #define ntohl(x) htonl(x) 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/tcp_states.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef UIP_TCP_STATES_H 3 | #define UIP_TCP_STATES_H 4 | 5 | #include 6 | 7 | // common constants for client.state() return values 8 | enum uip_tcp_state { 9 | CLOSED = UIP_CLOSED, 10 | SYN_SENT = UIP_SYN_SENT, 11 | SYN_RCVD = UIP_SYN_RCVD, 12 | ESTABLISHED = UIP_ESTABLISHED, 13 | FIN_WAIT_1 = UIP_FIN_WAIT_1, 14 | FIN_WAIT_2 = UIP_FIN_WAIT_2, 15 | CLOSE_WAIT = 10, // not used 16 | CLOSING = UIP_CLOSING, 17 | LAST_ACK = UIP_LAST_ACK, 18 | TIME_WAIT = UIP_TIME_WAIT 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=EthernetENC 2 | author=Norbert Truchsess, Juraj Andrassy 3 | maintainer=Juraj Andrassy 4 | sentence=Ethernet library for ENC28J60. Only include EthernetENC.h instead of Ethernet.h 5 | 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. 6 | url=https://github.com/Networking-for-Arduino/EthernetENC/wiki 7 | architectures=* 8 | version=2.0.5 9 | category=Communication 10 | includes=EthernetENC.h 11 | dot_a_linkage=true 12 | -------------------------------------------------------------------------------- /src/utility/mempool_conf.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMPOOLCONF_H 2 | #define MEMPOOLCONF_H 3 | #include "uipethernet-conf.h" 4 | extern "C" { 5 | #include "uipopt.h" 6 | #include "enc28j60.h" 7 | } 8 | #include 9 | 10 | typedef uint16_t memaddress; 11 | typedef uint8_t memhandle; 12 | 13 | #if UIP_SOCKET_NUMPACKETS and UIP_CONNS 14 | #define NUM_TCP_MEMBLOCKS (UIP_SOCKET_NUMPACKETS*2)*UIP_CONNS 15 | #else 16 | #define NUM_TCP_MEMBLOCKS 0 17 | #endif 18 | 19 | #if UIP_UDP and UIP_UDP_CONNS 20 | #define NUM_UDP_MEMBLOCKS ((2+UIP_UDP_BACKLOG)*UIP_UDP_CONNS) 21 | #else 22 | #define NUM_UDP_MEMBLOCKS 0 23 | #endif 24 | 25 | #define MEMPOOL_NUM_MEMBLOCKS (NUM_TCP_MEMBLOCKS+NUM_UDP_MEMBLOCKS) 26 | 27 | #define MEMPOOL_STARTADDRESS TXSTART_INIT+1 28 | #define MEMPOOL_SIZE TXSTOP_INIT-TXSTART_INIT 29 | 30 | void enc28J60_mempool_block_move_callback(memaddress,memaddress,memaddress); 31 | 32 | #define MEMPOOL_MEMBLOCK_MV(dest,src,size) enc28J60_mempool_block_move_callback(dest,src,size) 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/Dns.h: -------------------------------------------------------------------------------- 1 | // Arduino DNS client for Enc28J60-based Ethernet shield 2 | // (c) Copyright 2009-2010 MCQN Ltd. 3 | // Released under Apache License, version 2.0 4 | 5 | #ifndef DNSClient_h 6 | #define DNSClient_h 7 | 8 | #include "EthernetUdp.h" 9 | 10 | class DNSClient 11 | { 12 | public: 13 | // ctor 14 | void begin(const IPAddress& aDNSServer); 15 | 16 | /** Convert a numeric IP address string into a four-byte IP address. 17 | @param aIPAddrString IP address to convert 18 | @param aResult IPAddress structure to store the returned IP address 19 | @result 1 if aIPAddrString was successfully converted to an IP address, 20 | else error code 21 | */ 22 | int inet_aton(const char *aIPAddrString, IPAddress& aResult); 23 | 24 | /** Resolve the given hostname to an IP address. 25 | @param aHostname Name to be resolved 26 | @param aResult IPAddress structure to store the returned IP address 27 | @result 1 if aIPAddrString was successfully converted to an IP address, 28 | else error code 29 | */ 30 | int getHostByName(const char* aHostname, IPAddress& aResult); 31 | 32 | protected: 33 | uint16_t BuildRequest(const char* aName); 34 | uint16_t ProcessResponse(uint16_t aTimeout, IPAddress& aAddress); 35 | 36 | IPAddress iDNSServer; 37 | uint16_t iRequestId; 38 | EthernetUDP iUdp; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/utility/uipethernet-conf.h: -------------------------------------------------------------------------------- 1 | #ifndef UIPETHERNET_CONF_H 2 | #define UIPETHERNET_CONF_H 3 | 4 | // https://github.com/jandrassy/EthernetENC/wiki/Settings 5 | 6 | /* for TCP */ 7 | #ifndef UIP_SOCKET_NUMPACKETS 8 | #define UIP_SOCKET_NUMPACKETS 3 9 | #endif 10 | #ifndef UIP_CONF_MAX_CONNECTIONS 11 | #define UIP_CONF_MAX_CONNECTIONS 4 12 | #endif 13 | 14 | /* for UDP 15 | * set UIP_CONF_UDP to 0 to disable UDP (saves aprox. 4kB flash) */ 16 | #ifndef UIP_CONF_UDP 17 | #define UIP_CONF_UDP 1 18 | #endif 19 | #ifndef UIP_CONF_UDP_CONNS 20 | #define UIP_CONF_UDP_CONNS 2 21 | #endif 22 | 23 | /** 24 | * size of received UDP messages backlog. it must be at least 1 25 | */ 26 | #ifndef UIP_UDP_BACKLOG 27 | #define UIP_UDP_BACKLOG 2 28 | #endif 29 | 30 | /* timeout in ms for attempts to get a free memory block to write 31 | * before returning number of bytes sent so far 32 | * set to 0 to block until connection is closed by timeout */ 33 | #ifndef UIP_WRITE_TIMEOUT 34 | #define UIP_WRITE_TIMEOUT 2000 35 | #endif 36 | 37 | /* timeout after which UIPClient::connect gives up. The timeout is specified in seconds. 38 | * if set to a number <= 0 connect will timeout when uIP does (which might be longer than you expect...) */ 39 | #ifndef UIP_CONNECT_TIMEOUT 40 | #define UIP_CONNECT_TIMEOUT 5 41 | #endif 42 | 43 | /* periodic timer for uip (in ms) */ 44 | #ifndef UIP_PERIODIC_TIMER 45 | #define UIP_PERIODIC_TIMER 100 46 | #endif 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/utility/mempool.h: -------------------------------------------------------------------------------- 1 | /* 2 | mempool.h - sleek implementation of a memory pool 3 | Copyright (c) 2013 Norbert Truchsess 4 | All rights reserved. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifndef MEMPOOL_H 21 | #define MEMPOOL_H 22 | 23 | #include 24 | 25 | #define POOLSTART 0 26 | #define NOBLOCK 0 27 | 28 | #include "mempool_conf.h" 29 | 30 | struct memblock 31 | { 32 | memaddress begin; 33 | memaddress size; 34 | memhandle nextblock; 35 | }; 36 | 37 | class MemoryPool 38 | { 39 | #ifdef MEMPOOLTEST_H 40 | friend class MemoryPoolTest; 41 | #endif 42 | 43 | protected: 44 | static struct memblock blocks[MEMPOOL_NUM_MEMBLOCKS+1]; 45 | 46 | public: 47 | static void init(); 48 | static memhandle allocBlock(memaddress); 49 | static void freeBlock(memhandle); 50 | static void resizeBlock(memhandle handle, memaddress position); 51 | static void resizeBlock(memhandle handle, memaddress position, memaddress size); 52 | static memaddress blockSize(memhandle); 53 | }; 54 | #endif 55 | -------------------------------------------------------------------------------- /src/EthernetServer.h: -------------------------------------------------------------------------------- 1 | /* 2 | UIPServer.h - Arduino implementation of a uIP wrapper class. 3 | Copyright (c) 2013 Norbert Truchsess 4 | All rights reserved. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | #ifndef UIPSERVER_H 20 | #define UIPSERVER_H 21 | 22 | #include "EthernetClient.h" 23 | 24 | class EthernetServer { 25 | 26 | public: 27 | EthernetServer(uint16_t port = 80); 28 | EthernetClient available(); 29 | EthernetClient accept(); 30 | void begin(); 31 | void begin(uint16_t port); 32 | void end(); 33 | operator bool(); 34 | 35 | protected: 36 | size_t writeToAllClients(const uint8_t *buf, size_t size); 37 | 38 | private: 39 | uint16_t _port; 40 | bool listening = false; 41 | }; 42 | 43 | class EthernetServerPrint : public EthernetServer, public Print { 44 | 45 | public: 46 | EthernetServerPrint(uint16_t port = 80) : EthernetServer(port) {} 47 | 48 | virtual size_t write(uint8_t); 49 | virtual size_t write(const uint8_t *buf, size_t size); 50 | 51 | using Print::write; 52 | 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/EthernetClient.h: -------------------------------------------------------------------------------- 1 | /* 2 | UIPClient.h - Arduino implementation of a uIP wrapper class. 3 | Copyright (c) 2013 Norbert Truchsess 4 | All rights reserved. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifndef UIPCLIENT_H 21 | #define UIPCLIENT_H 22 | 23 | #include 24 | #include "utility/mempool.h" 25 | 26 | extern "C" { 27 | #include "utility/uip.h" 28 | } 29 | 30 | #define UIP_SOCKET_DATALEN UIP_TCP_MSS 31 | 32 | #define UIP_CLIENT_CONNECTED 0x01 33 | #define UIP_CLIENT_CLOSE 0x02 34 | #define UIP_CLIENT_REMOTECLOSED 0x04 35 | #define UIP_CLIENT_RESTART 0x08 36 | #define UIP_CLIENT_ACCEPTED 0x10 37 | 38 | typedef struct { 39 | uint8_t conn_index; 40 | uint8_t state; 41 | memhandle packets_in[UIP_SOCKET_NUMPACKETS]; 42 | uint16_t lport; /**< The local TCP port, in network byte order. */ 43 | } uip_userdata_closed_t; 44 | 45 | typedef struct { 46 | uint8_t conn_index; 47 | uint8_t state; 48 | memhandle packets_in[UIP_SOCKET_NUMPACKETS] = {NOBLOCK}; 49 | memhandle packets_out[UIP_SOCKET_NUMPACKETS] = {NOBLOCK}; 50 | memaddress out_pos; 51 | } uip_userdata_t; 52 | 53 | class EthernetClient : public Client { 54 | 55 | public: 56 | EthernetClient(); 57 | int connect(IPAddress ip, uint16_t port); 58 | int connect(const char *host, uint16_t port); 59 | int read(uint8_t *buf, size_t size); 60 | void stop(); 61 | uint8_t connected(); 62 | operator bool(); 63 | virtual bool operator==(const EthernetClient&); 64 | virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; 65 | 66 | size_t write(uint8_t); 67 | size_t write(const uint8_t *buf, size_t size); 68 | int availableForWrite(); 69 | void flush(); // flush sends the buffered data 70 | 71 | int available(); 72 | int read(); 73 | int peek(); 74 | void discardReceived(); 75 | 76 | using Print::write; 77 | 78 | IPAddress remoteIP(); 79 | uint16_t remotePort(); 80 | 81 | uint8_t status(); 82 | 83 | void setConnectionTimeout(uint16_t millis) {connectTimeout = millis;} 84 | 85 | private: 86 | EthernetClient(struct uip_conn *_conn); 87 | EthernetClient(uip_userdata_t* conn_data); 88 | 89 | uip_userdata_t* data; 90 | uint16_t connectTimeout = 1000 * UIP_CONNECT_TIMEOUT; 91 | 92 | static uip_userdata_t all_data[UIP_CONNS]; 93 | static uip_userdata_t* _allocateData(); 94 | 95 | static uint8_t _currentBlock(memhandle* blocks); 96 | static void _eatBlock(memhandle* blocks); 97 | static void _flushBlocks(memhandle* blocks); 98 | 99 | #ifdef UIPETHERNET_DEBUG_CLIENT 100 | static void _dumpAllData(); 101 | #endif 102 | 103 | friend class UIPEthernetClass; 104 | friend class EthernetServer; 105 | 106 | friend void uipclient_appcall(void); 107 | 108 | }; 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /src/utility/Enc28J60Network.h: -------------------------------------------------------------------------------- 1 | /* 2 | Enc28J60NetworkClass.h 3 | UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. 4 | 5 | Copyright (c) 2013 Norbert Truchsess 6 | All rights reserved. 7 | 8 | inspired by enc28j60.c file from the AVRlib library by Pascal Stang. 9 | For AVRlib See http://www.procyonengineering.com/ 10 | 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License as published by 13 | the Free Software Foundation, either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | 21 | You should have received a copy of the GNU General Public License 22 | along with this program. If not, see . 23 | */ 24 | 25 | #ifndef Enc28J60Network_H_ 26 | #define Enc28J60Network_H_ 27 | 28 | #include "mempool.h" 29 | 30 | #define UIP_RECEIVEBUFFERHANDLE 0xff 31 | 32 | #define UIP_SENDBUFFER_PADDING 7 33 | #define UIP_SENDBUFFER_OFFSET 1 34 | 35 | #define TX_COLLISION_RETRY_COUNT 3 36 | 37 | //#define ENC28J60DEBUG 38 | 39 | /* 40 | * Empfangen von ip-header, arp etc... 41 | * wenn tcp/udp -> tcp/udp-callback -> assign new packet to connection 42 | */ 43 | 44 | class Enc28J60Network : public MemoryPool 45 | { 46 | 47 | private: 48 | static uint8_t csPin; 49 | static bool spiInitialized; 50 | 51 | static uint16_t nextPacketPtr; 52 | static uint8_t bank; 53 | 54 | static struct memblock receivePkt; 55 | 56 | static uint8_t readOp(uint8_t op, uint8_t address); 57 | static void writeOp(uint8_t op, uint8_t address, uint8_t data); 58 | static uint16_t setReadPtr(memhandle handle, memaddress position, uint16_t len); 59 | static void setERXRDPT(); 60 | static void readBuffer(uint16_t len, uint8_t* data); 61 | static void writeBuffer(uint16_t len, uint8_t* data); 62 | static uint8_t readByte(uint16_t addr); 63 | static void writeByte(uint16_t addr, uint8_t data); 64 | static void setBank(uint8_t address); 65 | static uint8_t readReg(uint8_t address); 66 | static void writeReg(uint8_t address, uint8_t data); 67 | static void writeRegPair(uint8_t address, uint16_t data); 68 | static void phyWrite(uint8_t address, uint16_t data); 69 | static uint16_t phyRead(uint8_t address); 70 | static void clkout(uint8_t clk); 71 | 72 | friend void enc28J60_mempool_block_move_callback(memaddress,memaddress,memaddress); 73 | 74 | public: 75 | 76 | static uint8_t getrev(void); 77 | static void powerOn(); 78 | static void powerOff(); 79 | static bool linkStatus(); 80 | 81 | static void setCsPin(uint8_t _csPin) {csPin = _csPin;} 82 | static void initSPI(); 83 | static bool init(uint8_t* macaddr); 84 | static memhandle receivePacket(); 85 | static void freePacket(); 86 | static memaddress blockSize(memhandle handle); 87 | static bool sendPacket(memhandle handle); 88 | static uint16_t readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len); 89 | static uint16_t writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len); 90 | static void copyPacket(memhandle dest, memaddress dest_pos, memhandle src, memaddress src_pos, uint16_t len); 91 | static uint16_t chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len); 92 | }; 93 | 94 | #endif /* Enc28J60NetworkClass_H_ */ 95 | -------------------------------------------------------------------------------- /src/EthernetServer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | UIPServer.cpp - Arduino implementation of a uIP wrapper class. 3 | Copyright (c) 2013 Norbert Truchsess 4 | All rights reserved. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | #include "Ethernet.h" 20 | #include "EthernetServer.h" 21 | extern "C" { 22 | #include "utility/uip-conf.h" 23 | } 24 | 25 | #define UIPServer EthernetServer // to not pollute source code history with the rename 26 | #define UIPClient EthernetClient 27 | 28 | UIPServer::UIPServer(uint16_t port) : _port(htons(port)) 29 | { 30 | } 31 | 32 | UIPClient UIPServer::available() 33 | { 34 | UIPEthernetClass::tick(); 35 | for ( uip_userdata_t* data = &UIPClient::all_data[0]; data < &UIPClient::all_data[UIP_CONNS]; data++ ) 36 | { 37 | if (data->packets_in[0] != NOBLOCK 38 | && (((data->state & UIP_CLIENT_CONNECTED) && uip_conns[data->conn_index].lport ==_port) 39 | || ((data->state & UIP_CLIENT_REMOTECLOSED) && ((uip_userdata_closed_t *)data)->lport == _port))) 40 | return UIPClient(data); 41 | } 42 | return UIPClient(); 43 | } 44 | 45 | UIPClient UIPServer::accept() 46 | { 47 | UIPEthernetClass::tick(); 48 | for ( uip_userdata_t* data = &UIPClient::all_data[0]; data < &UIPClient::all_data[UIP_CONNS]; data++ ) 49 | { 50 | if (!(data->state & UIP_CLIENT_ACCEPTED) 51 | && (((data->state & UIP_CLIENT_CONNECTED) && uip_conns[data->conn_index].lport ==_port) 52 | || ((data->state & UIP_CLIENT_REMOTECLOSED) && ((uip_userdata_closed_t *)data)->lport == _port))) { 53 | data->state |= UIP_CLIENT_ACCEPTED; 54 | return UIPClient(data); 55 | } 56 | } 57 | return UIPClient(); 58 | } 59 | 60 | void UIPServer::begin() 61 | { 62 | uip_listen(_port); 63 | UIPEthernetClass::tick(); 64 | listening = true; 65 | } 66 | 67 | void UIPServer::begin(uint16_t port) 68 | { 69 | _port = port; 70 | begin(); 71 | } 72 | 73 | void UIPServer::end() { 74 | uip_unlisten(_port); 75 | listening = false; 76 | for ( uip_userdata_t* data = &UIPClient::all_data[0]; data < &UIPClient::all_data[UIP_CONNS]; data++ ) 77 | { 78 | if ((data->state & UIP_CLIENT_CONNECTED) && uip_conns[data->conn_index].lport ==_port) 79 | { 80 | UIPClient client(data); 81 | client.stop(); 82 | } 83 | } 84 | } 85 | 86 | UIPServer::operator bool() { 87 | return listening; 88 | } 89 | 90 | size_t UIPServer::writeToAllClients(const uint8_t *buf, size_t size) 91 | { 92 | size_t ret = 0; 93 | for ( uip_userdata_t* data = &UIPClient::all_data[0]; data < &UIPClient::all_data[UIP_CONNS]; data++ ) 94 | { 95 | if ((data->state & UIP_CLIENT_CONNECTED) && uip_conns[data->conn_index].lport ==_port) 96 | { 97 | EthernetClient client(data); 98 | ret = client.write(buf, size); 99 | } 100 | } 101 | return ret; 102 | } 103 | 104 | size_t EthernetServerPrint::write(uint8_t c) 105 | { 106 | return write(&c,1); 107 | } 108 | 109 | size_t EthernetServerPrint::write(const uint8_t *buf, size_t size) 110 | { 111 | return EthernetServer::writeToAllClients(buf, size); 112 | } 113 | -------------------------------------------------------------------------------- /src/EthernetUdp.h: -------------------------------------------------------------------------------- 1 | /* 2 | UIPUdp.h - Arduino implementation of a uIP wrapper class. 3 | Copyright (c) 2013 Norbert Truchsess 4 | All rights reserved. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifndef UIPUDP_H 21 | #define UIPUDP_H 22 | 23 | #include 24 | #include 25 | #include "utility/mempool.h" 26 | extern "C" { 27 | #include "utility/uip.h" 28 | } 29 | 30 | #define UIP_UDP_MAXDATALEN 1500 31 | #define UIP_UDP_PHYH_LEN UIP_LLH_LEN+UIP_IPUDPH_LEN 32 | #define UIP_UDP_MAXPACKETSIZE UIP_UDP_MAXDATALEN+UIP_UDP_PHYH_LEN 33 | 34 | typedef struct { 35 | memhandle packet = NOBLOCK; 36 | uip_ipaddr_t remote_ip; 37 | uint16_t remote_port = 0; 38 | } uip_udp_msg_rec_t; 39 | 40 | typedef struct { 41 | memaddress out_pos; 42 | uip_udp_msg_rec_t packet_next[UIP_UDP_BACKLOG]; 43 | memhandle packet_in = NOBLOCK; 44 | memhandle packet_out = NOBLOCK; 45 | boolean send; 46 | uip_ipaddr_t remote_ip; 47 | uint16_t remote_port = 0; 48 | } uip_udp_userdata_t; 49 | 50 | class EthernetUDP : public UDP 51 | { 52 | 53 | private: 54 | struct uip_udp_conn *_uip_udp_conn; 55 | 56 | uip_udp_userdata_t appdata; 57 | 58 | public: 59 | EthernetUDP(); // Constructor 60 | uint8_t 61 | begin(uint16_t);// initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use 62 | void 63 | stop(); // Finish with the UDP socket 64 | 65 | // Sending UDP packets 66 | 67 | // Start building up a packet to send to the remote host specific in ip and port 68 | // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port 69 | int 70 | beginPacket(IPAddress ip, uint16_t port); 71 | // Start building up a packet to send to the remote host specific in host and port 72 | // Returns 1 if successful, 0 if there was a problem resolving the hostname or port 73 | int 74 | beginPacket(const char *host, uint16_t port); 75 | // Finish off this packet and send it 76 | // Returns 1 if the packet was sent successfully, 0 if there was an error 77 | int 78 | endPacket(); 79 | // Write a single byte into the packet 80 | size_t 81 | write(uint8_t); 82 | // Write size bytes from buffer into the packet 83 | size_t 84 | write(const uint8_t *buffer, size_t size); 85 | // flush() should send the data, but here endPacket does it 86 | void 87 | flush() {}; // in Print it is empty, UDP makes it pure virtual 88 | 89 | using Print::write; 90 | 91 | // Start processing the next available incoming packet 92 | // Returns the size of the packet in bytes, or 0 if no packets are available 93 | int 94 | parsePacket(); 95 | // Number of bytes remaining in the current packet 96 | int 97 | available(); 98 | // Read a single byte from the current packet 99 | int 100 | read(); 101 | // Read up to len bytes from the current packet and place them into buffer 102 | // Returns the number of bytes read, or 0 if none are available 103 | int 104 | read(unsigned char* buffer, size_t len); 105 | // Read up to len characters from the current packet and place them into buffer 106 | // Returns the number of characters read, or 0 if none are available 107 | int 108 | read(char* buffer, size_t len) 109 | { 110 | return read((unsigned char*) buffer, len); 111 | } 112 | ; 113 | // Return the next byte from the current packet without moving on to the next byte 114 | int 115 | peek(); 116 | void 117 | discardReceived(); // former flush 118 | 119 | // Return the IP address of the host who sent the current incoming packet 120 | IPAddress 121 | remoteIP(); 122 | 123 | // Return the port of the host who sent the current incoming packet 124 | uint16_t 125 | remotePort(); 126 | 127 | private: 128 | 129 | friend void uipudp_appcall(void); 130 | 131 | friend class UIPEthernetClass; 132 | static void _send(struct uip_udp_conn *uip_udp_conn); 133 | 134 | static uint8_t _newBlock(uip_udp_msg_rec_t* blocks); 135 | static void _moveBlocks(uip_udp_msg_rec_t* blocks); 136 | static void _flushBlocks(uip_udp_msg_rec_t* blocks); 137 | }; 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /src/utility/mempool.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | mempool.cpp - sleek implementation of a memory pool 3 | Copyright (c) 2013 Norbert Truchsess 4 | All rights reserved. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #include "mempool.h" 21 | #include 22 | 23 | #define POOLOFFSET 1 24 | 25 | struct memblock MemoryPool::blocks[MEMPOOL_NUM_MEMBLOCKS+1]; 26 | 27 | void 28 | MemoryPool::init() 29 | { 30 | memset(&blocks[0], 0, sizeof(blocks)); 31 | blocks[POOLSTART].begin = MEMPOOL_STARTADDRESS; 32 | blocks[POOLSTART].size = 0; 33 | blocks[POOLSTART].nextblock = NOBLOCK; 34 | } 35 | 36 | memhandle 37 | MemoryPool::allocBlock(memaddress size) 38 | { 39 | memblock* best = NULL; 40 | memhandle cur = POOLSTART; 41 | memblock* block = &blocks[POOLSTART]; 42 | memaddress bestsize = MEMPOOL_SIZE + 1; 43 | 44 | do 45 | { 46 | memhandle next = block->nextblock; 47 | memaddress freesize = ( next == NOBLOCK ? blocks[POOLSTART].begin + MEMPOOL_SIZE : blocks[next].begin) - block->begin - block->size; 48 | if (freesize == size) 49 | { 50 | best = &blocks[cur]; 51 | goto found; 52 | } 53 | if (freesize > size && freesize < bestsize) 54 | { 55 | bestsize = freesize; 56 | best = &blocks[cur]; 57 | } 58 | if (next == NOBLOCK) 59 | { 60 | if (best) 61 | goto found; 62 | else 63 | goto collect; 64 | } 65 | block = &blocks[next]; 66 | cur = next; 67 | } 68 | while (true); 69 | 70 | collect: 71 | { 72 | cur = POOLSTART; 73 | block = &blocks[POOLSTART]; 74 | memhandle next; 75 | while ((next = block->nextblock) != NOBLOCK) 76 | { 77 | memaddress dest = block->begin + block->size; 78 | memblock* nextblock = &blocks[next]; 79 | memaddress* src = &nextblock->begin; 80 | if (dest != *src) 81 | { 82 | #ifdef MEMPOOL_MEMBLOCK_MV 83 | MEMPOOL_MEMBLOCK_MV(dest,*src,nextblock->size); 84 | #endif 85 | *src = dest; 86 | } 87 | block = nextblock; 88 | } 89 | if (blocks[POOLSTART].begin + MEMPOOL_SIZE - block->begin - block->size >= size) 90 | best = block; 91 | else 92 | goto notfound; 93 | } 94 | 95 | found: 96 | { 97 | block = &blocks[POOLOFFSET]; 98 | for (cur = POOLOFFSET; cur < MEMPOOL_NUM_MEMBLOCKS + POOLOFFSET; cur++) 99 | { 100 | if (block->size) 101 | { 102 | block++; 103 | continue; 104 | } 105 | memaddress address = best->begin + best->size; 106 | #ifdef MEMBLOCK_ALLOC 107 | MEMBLOCK_ALLOC(address,size); 108 | #endif 109 | block->begin = address; 110 | block->size = size; 111 | block->nextblock = best->nextblock; 112 | best->nextblock = cur; 113 | return cur; 114 | } 115 | } 116 | 117 | notfound: return NOBLOCK; 118 | } 119 | 120 | void 121 | MemoryPool::freeBlock(memhandle handle) 122 | { 123 | if (handle == NOBLOCK) 124 | return; 125 | memblock *b = &blocks[POOLSTART]; 126 | 127 | do 128 | { 129 | memhandle next = b->nextblock; 130 | if (next == handle) 131 | { 132 | memblock *f = &blocks[next]; 133 | #ifdef MEMBLOCK_FREE 134 | MEMBLOCK_FREE(f->begin,f->size); 135 | #endif 136 | b->nextblock = f->nextblock; 137 | f->size = 0; 138 | f->nextblock = NOBLOCK; 139 | return; 140 | } 141 | if (next == NOBLOCK) 142 | return; 143 | b = &blocks[next]; 144 | } 145 | while (true); 146 | } 147 | 148 | void 149 | MemoryPool::resizeBlock(memhandle handle, memaddress position) 150 | { 151 | memblock * block = &blocks[handle]; 152 | block->begin += position; 153 | block->size -= position; 154 | } 155 | 156 | void 157 | MemoryPool::resizeBlock(memhandle handle, memaddress position, memaddress size) 158 | { 159 | memblock * block = &blocks[handle]; 160 | block->begin += position; 161 | block->size = size; 162 | } 163 | 164 | memaddress 165 | MemoryPool::blockSize(memhandle handle) 166 | { 167 | return blocks[handle].size; 168 | } 169 | -------------------------------------------------------------------------------- /src/utility/uip_arch.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \addtogroup uip 3 | * {@ 4 | */ 5 | 6 | /** 7 | * \defgroup uiparch Architecture specific uIP functions 8 | * @{ 9 | * 10 | * The functions in the architecture specific module implement the IP 11 | * check sum and 32-bit additions. 12 | * 13 | * The IP checksum calculation is the most computationally expensive 14 | * operation in the TCP/IP stack and it therefore pays off to 15 | * implement this in efficient assembler. The purpose of the uip-arch 16 | * module is to let the checksum functions to be implemented in 17 | * architecture specific assembler. 18 | * 19 | */ 20 | 21 | /** 22 | * \file 23 | * Declarations of architecture specific functions. 24 | * \author Adam Dunkels 25 | */ 26 | 27 | /* 28 | * Copyright (c) 2001, Adam Dunkels. 29 | * All rights reserved. 30 | * 31 | * Redistribution and use in source and binary forms, with or without 32 | * modification, are permitted provided that the following conditions 33 | * are met: 34 | * 1. Redistributions of source code must retain the above copyright 35 | * notice, this list of conditions and the following disclaimer. 36 | * 2. Redistributions in binary form must reproduce the above copyright 37 | * notice, this list of conditions and the following disclaimer in the 38 | * documentation and/or other materials provided with the distribution. 39 | * 3. The name of the author may not be used to endorse or promote 40 | * products derived from this software without specific prior 41 | * written permission. 42 | * 43 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 44 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 45 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 47 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 49 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 50 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 51 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 52 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 53 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 54 | * 55 | * This file is part of the uIP TCP/IP stack. 56 | * 57 | * $Id: uip_arch.h,v 1.2 2006/06/07 09:15:19 adam Exp $ 58 | * 59 | */ 60 | 61 | #ifndef __UIP_ARCH_H__ 62 | #define __UIP_ARCH_H__ 63 | 64 | #include "uip.h" 65 | 66 | /** 67 | * Carry out a 32-bit addition. 68 | * 69 | * Because not all architectures for which uIP is intended has native 70 | * 32-bit arithmetic, uIP uses an external C function for doing the 71 | * required 32-bit additions in the TCP protocol processing. This 72 | * function should add the two arguments and place the result in the 73 | * global variable uip_acc32. 74 | * 75 | * \note The 32-bit integer pointed to by the op32 parameter and the 76 | * result in the uip_acc32 variable are in network byte order (big 77 | * endian). 78 | * 79 | * \param op32 A pointer to a 4-byte array representing a 32-bit 80 | * integer in network byte order (big endian). 81 | * 82 | * \param op16 A 16-bit integer in host byte order. 83 | */ 84 | void uip_add32(u8_t *op32, u16_t op16); 85 | 86 | /** 87 | * Calculate the Internet checksum over a buffer. 88 | * 89 | * The Internet checksum is the one's complement of the one's 90 | * complement sum of all 16-bit words in the buffer. 91 | * 92 | * See RFC1071. 93 | * 94 | * \note This function is not called in the current version of uIP, 95 | * but future versions might make use of it. 96 | * 97 | * \param buf A pointer to the buffer over which the checksum is to be 98 | * computed. 99 | * 100 | * \param len The length of the buffer over which the checksum is to 101 | * be computed. 102 | * 103 | * \return The Internet checksum of the buffer. 104 | */ 105 | u16_t uip_chksum(u16_t *buf, u16_t len); 106 | 107 | /** 108 | * Calculate the IP header checksum of the packet header in uip_buf. 109 | * 110 | * The IP header checksum is the Internet checksum of the 20 bytes of 111 | * the IP header. 112 | * 113 | * \return The IP header checksum of the IP header in the uip_buf 114 | * buffer. 115 | */ 116 | u16_t uip_ipchksum(void); 117 | 118 | /** 119 | * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. 120 | * 121 | * The TCP checksum is the Internet checksum of data contents of the 122 | * TCP segment, and a pseudo-header as defined in RFC793. 123 | * 124 | * \note The uip_appdata pointer that points to the packet data may 125 | * point anywhere in memory, so it is not possible to simply calculate 126 | * the Internet checksum of the contents of the uip_buf buffer. 127 | * 128 | * \return The TCP checksum of the TCP segment in uip_buf and pointed 129 | * to by uip_appdata. 130 | */ 131 | u16_t uip_tcpchksum(void); 132 | 133 | u16_t uip_udpchksum(void); 134 | 135 | /** @} */ 136 | /** @} */ 137 | 138 | #endif /* __UIP_ARCH_H__ */ 139 | -------------------------------------------------------------------------------- /src/utility/uip-conf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * UIPEthernet Project-specific configuration options 3 | * Copyright (c) 2013 Norbert Truchsess 4 | * @{ 5 | * 6 | * uIP has a number of configuration options that can be overridden 7 | * for each project. These are kept in a project-specific uip-conf.h 8 | * file and all configuration names have the prefix UIP_CONF. 9 | */ 10 | 11 | /* 12 | * Copyright (c) 2006, Swedish Institute of Computer Science. 13 | * All rights reserved. 14 | * 15 | * Redistribution and use in source and binary forms, with or without 16 | * modification, are permitted provided that the following conditions 17 | * are met: 18 | * 1. Redistributions of source code must retain the above copyright 19 | * notice, this list of conditions and the following disclaimer. 20 | * 2. Redistributions in binary form must reproduce the above copyright 21 | * notice, this list of conditions and the following disclaimer in the 22 | * documentation and/or other materials provided with the distribution. 23 | * 3. Neither the name of the Institute nor the names of its contributors 24 | * may be used to endorse or promote products derived from this software 25 | * without specific prior written permission. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 28 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 31 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 | * SUCH DAMAGE. 38 | * 39 | * This file is part of the uIP TCP/IP stack 40 | * 41 | */ 42 | 43 | #ifndef __UIP_CONF_H__ 44 | #define __UIP_CONF_H__ 45 | 46 | #include 47 | #include "uipethernet-conf.h" 48 | 49 | /** 50 | * 8 bit datatype 51 | * 52 | * This typedef defines the 8-bit type used throughout uIP. 53 | * 54 | * \hideinitializer 55 | */ 56 | typedef uint8_t u8_t; 57 | 58 | /** 59 | * 16 bit datatype 60 | * 61 | * This typedef defines the 16-bit type used throughout uIP. 62 | * 63 | * \hideinitializer 64 | */ 65 | typedef uint16_t u16_t; 66 | 67 | /** 68 | * Statistics datatype 69 | * 70 | * This typedef defines the dataype used for keeping statistics in 71 | * uIP. 72 | * 73 | * \hideinitializer 74 | */ 75 | typedef unsigned short uip_stats_t; 76 | 77 | /** 78 | * Maximum number of TCP connections. 79 | * (see uipethernet-conf.h) 80 | * \hideinitializer 81 | * 82 | * #define UIP_CONF_MAX_CONNECTIONS 4 83 | */ 84 | 85 | /** 86 | * Maximum number of listening TCP ports. 87 | * 88 | * \hideinitializer 89 | */ 90 | #define UIP_CONF_MAX_LISTENPORTS 4 91 | 92 | /** 93 | * uIP buffer size. 94 | * 95 | * \hideinitializer 96 | */ 97 | #define UIP_CONF_BUFFER_SIZE 98 98 | //#define UIP_CONF_BUFFER_SIZE 118 99 | 100 | /** 101 | * The TCP maximum segment size. 102 | * 103 | * This is should not be to set to more than 104 | * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. 105 | */ 106 | 107 | #define UIP_CONF_TCP_MSS 512 108 | 109 | /** 110 | * The size of the advertised receiver's window. 111 | * 112 | * Should be set low (i.e., to the size of the uip_buf buffer) is the 113 | * application is slow to process incoming data, or high (32768 bytes) 114 | * if the application processes data quickly. 115 | * 116 | * \hideinitializer 117 | */ 118 | #define UIP_CONF_RECEIVE_WINDOW 512 119 | 120 | /** 121 | * CPU byte order. 122 | * 123 | * \hideinitializer 124 | */ 125 | #define UIP_CONF_BYTE_ORDER __BYTE_ORDER__ 126 | 127 | /** 128 | * Logging on or off 129 | * 130 | * \hideinitializer 131 | */ 132 | #define UIP_CONF_LOGGING 0 133 | 134 | /** 135 | * UDP support on or off 136 | * (see uipethernet-conf.h) 137 | * \hideinitializer 138 | * 139 | * #define UIP_CONF_UDP 1 140 | * 141 | * #define UIP_CONF_UDP_CONNS 4 142 | */ 143 | 144 | /** 145 | * UDP checksums on or off 146 | * 147 | * \hideinitializer 148 | */ 149 | #define UIP_CONF_UDP_CHECKSUMS 1 150 | 151 | /** 152 | * UDP Broadcast (receive) on or off 153 | * (see uipethernet-conf.h) 154 | * \hideinitializer 155 | */ 156 | #define UIP_CONF_BROADCAST 1 157 | 158 | 159 | /** 160 | * uIP statistics on or off 161 | * 162 | * \hideinitializer 163 | */ 164 | #define UIP_CONF_STATISTICS 0 165 | 166 | // SLIP 167 | //#define UIP_CONF_LLH_LEN 0 168 | 169 | typedef void* uip_tcp_appstate_t; 170 | 171 | void uipclient_appcall(void); 172 | 173 | #define UIP_APPCALL uipclient_appcall 174 | 175 | typedef void* uip_udp_appstate_t; 176 | 177 | void uipudp_appcall(void); 178 | 179 | #define UIP_UDP_APPCALL uipudp_appcall 180 | 181 | #define CC_REGISTER_ARG register 182 | 183 | #define UIP_ARCH_CHKSUM 1 184 | 185 | #endif /* __UIP_CONF_H__ */ 186 | -------------------------------------------------------------------------------- /src/Ethernet.h: -------------------------------------------------------------------------------- 1 | /* 2 | UIPEthernet.h - Arduino implementation of a uIP wrapper class. 3 | Copyright (c) 2013 Norbert Truchsess 4 | All rights reserved. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifndef UIPETHERNET_H 21 | #define UIPETHERNET_H 22 | 23 | //#define UIPETHERNET_DEBUG 24 | //#define UIPETHERNET_DEBUG_CHKSUM 25 | //#define UIPETHERNET_DEBUG_UDP 26 | //#define UIPETHERNET_DEBUG_CLIENT 27 | 28 | #include 29 | #include "Dhcp.h" 30 | #include 31 | #include "utility/Enc28J60Network.h" 32 | #include "EthernetClient.h" 33 | #include "EthernetServer.h" 34 | #include "EthernetUdp.h" 35 | 36 | extern "C" 37 | { 38 | #include "utility/uip.h" 39 | } 40 | 41 | #define UIPETHERNET_FREEPACKET 1 42 | #define UIPETHERNET_SENDPACKET 2 43 | #define UIPETHERNET_BUFFERREAD 4 44 | 45 | #define uip_ip_addr(addr, ip) do { \ 46 | ((u16_t *)(addr))[0] = HTONS(((ip[0]) << 8) | (ip[1])); \ 47 | ((u16_t *)(addr))[1] = HTONS(((ip[2]) << 8) | (ip[3])); \ 48 | } while(0) 49 | 50 | #define ip_addr_uip(a) IPAddress(a[0] & 0xFF, a[0] >> 8 , a[1] & 0xFF, a[1] >> 8) //TODO this is not IPV6 capable 51 | 52 | #define uip_seteth_addr(eaddr) do {uip_ethaddr.addr[0] = eaddr[0]; \ 53 | uip_ethaddr.addr[1] = eaddr[1];\ 54 | uip_ethaddr.addr[2] = eaddr[2];\ 55 | uip_ethaddr.addr[3] = eaddr[3];\ 56 | uip_ethaddr.addr[4] = eaddr[4];\ 57 | uip_ethaddr.addr[5] = eaddr[5];} while(0) 58 | 59 | #define BUF ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN]) 60 | 61 | enum EthernetLinkStatus { 62 | Unknown, 63 | LinkON, 64 | LinkOFF 65 | }; 66 | 67 | enum EthernetHardwareStatus { 68 | EthernetNoHardware, 69 | EthernetW5100, 70 | EthernetW5200, 71 | EthernetW5500, 72 | EthernetENC28J60 = 10 73 | }; 74 | 75 | class UIPEthernetClass 76 | { 77 | public: 78 | UIPEthernetClass(); 79 | 80 | void init(uint8_t csPin); 81 | 82 | int begin(const uint8_t* mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); 83 | void begin(const uint8_t* mac, IPAddress ip); 84 | void begin(const uint8_t* mac, IPAddress ip, IPAddress dns); 85 | void begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway); 86 | void begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); 87 | 88 | void end(); 89 | 90 | // maintain() must be called at regular intervals to process the incoming serial 91 | // data and issue IP events to the sketch. It does not return until all IP 92 | // events have been processed. Renews dhcp-lease if required. 93 | int maintain(); 94 | 95 | EthernetLinkStatus linkStatus(); 96 | EthernetHardwareStatus hardwareStatus(); 97 | 98 | uint8_t* macAddress(uint8_t* mac); 99 | void MACAddress(uint8_t *mac_address) { macAddress(mac_address); } 100 | 101 | IPAddress localIP(); 102 | IPAddress subnetMask(); 103 | IPAddress gatewayIP(); 104 | IPAddress dnsServerIP(); 105 | IPAddress dnsIP(int n = 0); 106 | 107 | void setDnsServerIP(const IPAddress dns_server) { _dnsServerAddress = dns_server; } 108 | void setDNS(IPAddress dns_server) { _dnsServerAddress = dns_server; } 109 | 110 | void setHostname(const char* hostname); // only the pointer is stored! 111 | 112 | int hostByName(const char* hostname, IPAddress& result); 113 | 114 | private: 115 | static bool initialized; 116 | static memhandle in_packet; 117 | static memhandle uip_packet; 118 | static uint8_t uip_hdrlen; 119 | static uint8_t packetstate; 120 | 121 | static IPAddress _dnsServerAddress; 122 | static DhcpClass* _dhcp; 123 | 124 | static unsigned long periodic_timer; 125 | static unsigned long arp_timer; 126 | 127 | static void init(const uint8_t* mac); 128 | static void configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); 129 | 130 | static void tick(); 131 | 132 | static boolean network_send(); 133 | 134 | static void call_yield(); 135 | 136 | friend class EthernetServer; 137 | 138 | friend class EthernetClient; 139 | 140 | friend class EthernetUDP; 141 | 142 | static uint16_t chksum(uint16_t sum, const uint8_t* data, uint16_t len); 143 | static uint16_t ipchksum(void); 144 | #if UIP_UDP 145 | static uint16_t upper_layer_chksum(uint8_t proto); 146 | #endif 147 | friend uint16_t uip_ipchksum(void); 148 | friend uint16_t uip_tcpchksum(void); 149 | friend uint16_t uip_udpchksum(void); 150 | 151 | friend void uipclient_appcall(void); 152 | friend void uipudp_appcall(void); 153 | 154 | #if UIP_CONF_IPV6 155 | uint16_t uip_icmp6chksum(void); 156 | #endif /* UIP_CONF_IPV6 */ 157 | }; 158 | 159 | extern UIPEthernetClass Ethernet; 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /src/utility/uip_arp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \addtogroup uip 3 | * @{ 4 | */ 5 | 6 | /** 7 | * \addtogroup uiparp 8 | * @{ 9 | */ 10 | 11 | /** 12 | * \file 13 | * Macros and definitions for the ARP module. 14 | * \author Adam Dunkels 15 | */ 16 | 17 | 18 | /* 19 | * Copyright (c) 2001-2003, Adam Dunkels. 20 | * All rights reserved. 21 | * 22 | * Redistribution and use in source and binary forms, with or without 23 | * modification, are permitted provided that the following conditions 24 | * are met: 25 | * 1. Redistributions of source code must retain the above copyright 26 | * notice, this list of conditions and the following disclaimer. 27 | * 2. Redistributions in binary form must reproduce the above copyright 28 | * notice, this list of conditions and the following disclaimer in the 29 | * documentation and/or other materials provided with the distribution. 30 | * 3. The name of the author may not be used to endorse or promote 31 | * products derived from this software without specific prior 32 | * written permission. 33 | * 34 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 35 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 36 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 37 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 38 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 39 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 40 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 41 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 42 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 43 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 44 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 45 | * 46 | * This file is part of the uIP TCP/IP stack. 47 | * 48 | * $Id: uip_arp.h,v 1.5 2006/06/11 21:46:39 adam Exp $ 49 | * 50 | */ 51 | 52 | #ifndef __UIP_ARP_H__ 53 | #define __UIP_ARP_H__ 54 | 55 | #include "uip.h" 56 | 57 | 58 | extern struct uip_eth_addr uip_ethaddr; 59 | 60 | /** 61 | * The Ethernet header. 62 | */ 63 | struct uip_eth_hdr { 64 | struct uip_eth_addr dest; 65 | struct uip_eth_addr src; 66 | u16_t type; 67 | }; 68 | 69 | #define UIP_ETHTYPE_ARP 0x0806 70 | #define UIP_ETHTYPE_IP 0x0800 71 | #define UIP_ETHTYPE_IP6 0x86dd 72 | 73 | 74 | /* The uip_arp_init() function must be called before any of the other 75 | ARP functions. */ 76 | void uip_arp_init(void); 77 | 78 | /* The uip_arp_ipin() function should be called whenever an IP packet 79 | arrives from the Ethernet. This function refreshes the ARP table or 80 | inserts a new mapping if none exists. The function assumes that an 81 | IP packet with an Ethernet header is present in the uip_buf buffer 82 | and that the length of the packet is in the uip_len variable. */ 83 | void uip_arp_ipin(void); 84 | //#define uip_arp_ipin() 85 | 86 | /* The uip_arp_arpin() should be called when an ARP packet is received 87 | by the Ethernet driver. This function also assumes that the 88 | Ethernet frame is present in the uip_buf buffer. When the 89 | uip_arp_arpin() function returns, the contents of the uip_buf 90 | buffer should be sent out on the Ethernet if the uip_len variable 91 | is > 0. */ 92 | void uip_arp_arpin(void); 93 | 94 | /* The uip_arp_out() function should be called when an IP packet 95 | should be sent out on the Ethernet. This function creates an 96 | Ethernet header before the IP header in the uip_buf buffer. The 97 | Ethernet header will have the correct Ethernet MAC destination 98 | address filled in if an ARP table entry for the destination IP 99 | address (or the IP address of the default router) is present. If no 100 | such table entry is found, the IP packet is overwritten with an ARP 101 | request and we rely on TCP to retransmit the packet that was 102 | overwritten. In any case, the uip_len variable holds the length of 103 | the Ethernet frame that should be transmitted. */ 104 | void uip_arp_out(void); 105 | 106 | /* The uip_arp_timer() function should be called every ten seconds. It 107 | is responsible for flushing old entries in the ARP table. */ 108 | void uip_arp_timer(void); 109 | 110 | /** @} */ 111 | 112 | /** 113 | * \addtogroup uipconffunc 114 | * @{ 115 | */ 116 | 117 | 118 | /** 119 | * Specifiy the Ethernet MAC address. 120 | * 121 | * The ARP code needs to know the MAC address of the Ethernet card in 122 | * order to be able to respond to ARP queries and to generate working 123 | * Ethernet headers. 124 | * 125 | * \note This macro only specifies the Ethernet MAC address to the ARP 126 | * code. It cannot be used to change the MAC address of the Ethernet 127 | * card. 128 | * 129 | * \param eaddr A pointer to a struct uip_eth_addr containing the 130 | * Ethernet MAC address of the Ethernet card. 131 | * 132 | * \hideinitializer 133 | */ 134 | #define uip_setethaddr(eaddr) do {uip_ethaddr.addr[0] = eaddr.addr[0]; \ 135 | uip_ethaddr.addr[1] = eaddr.addr[1];\ 136 | uip_ethaddr.addr[2] = eaddr.addr[2];\ 137 | uip_ethaddr.addr[3] = eaddr.addr[3];\ 138 | uip_ethaddr.addr[4] = eaddr.addr[4];\ 139 | uip_ethaddr.addr[5] = eaddr.addr[5];} while(0) 140 | 141 | /** @} */ 142 | /** @} */ 143 | 144 | #endif /* __UIP_ARP_H__ */ 145 | -------------------------------------------------------------------------------- /src/Dhcp.h: -------------------------------------------------------------------------------- 1 | // DHCP Library v0.3 - April 25, 2009 2 | // Author: Jordan Terrell - blog.jordanterrell.com 3 | 4 | #ifndef Dhcp_h 5 | #define Dhcp_h 6 | 7 | #include "EthernetUdp.h" 8 | 9 | /* DHCP state machine. */ 10 | #define STATE_DHCP_START 0 11 | #define STATE_DHCP_DISCOVER 1 12 | #define STATE_DHCP_REQUEST 2 13 | #define STATE_DHCP_LEASED 3 14 | #define STATE_DHCP_REREQUEST 4 15 | #define STATE_DHCP_RELEASE 5 16 | 17 | #define DHCP_FLAGSBROADCAST 0x8000 18 | 19 | /* UDP port numbers for DHCP */ 20 | #define DHCP_SERVER_PORT 67 /* from server to client */ 21 | #define DHCP_CLIENT_PORT 68 /* from client to server */ 22 | 23 | /* DHCP message OP code */ 24 | #define DHCP_BOOTREQUEST 1 25 | #define DHCP_BOOTREPLY 2 26 | 27 | /* DHCP message type */ 28 | #define DHCP_DISCOVER 1 29 | #define DHCP_OFFER 2 30 | #define DHCP_REQUEST 3 31 | #define DHCP_DECLINE 4 32 | #define DHCP_ACK 5 33 | #define DHCP_NAK 6 34 | #define DHCP_RELEASE 7 35 | #define DHCP_INFORM 8 36 | 37 | #define DHCP_HTYPE10MB 1 38 | #define DHCP_HTYPE100MB 2 39 | 40 | #define DHCP_HLENETHERNET 6 41 | #define DHCP_HOPS 0 42 | #define DHCP_SECS 0 43 | 44 | #define MAGIC_COOKIE 0x63825363 45 | #define MAX_DHCP_OPT 16 46 | 47 | #define HOST_NAME "ENC28J" 48 | #define DEFAULT_LEASE (900) //default lease time in seconds 49 | 50 | #define DHCP_CHECK_NONE (0) 51 | #define DHCP_CHECK_RENEW_FAIL (1) 52 | #define DHCP_CHECK_RENEW_OK (2) 53 | #define DHCP_CHECK_REBIND_FAIL (3) 54 | #define DHCP_CHECK_REBIND_OK (4) 55 | 56 | enum 57 | { 58 | padOption = 0, 59 | subnetMask = 1, 60 | timerOffset = 2, 61 | routersOnSubnet = 3, 62 | /* timeServer = 4, 63 | nameServer = 5,*/ 64 | dns = 6, 65 | /*logServer = 7, 66 | cookieServer = 8, 67 | lprServer = 9, 68 | impressServer = 10, 69 | resourceLocationServer = 11,*/ 70 | hostName = 12, 71 | /*bootFileSize = 13, 72 | meritDumpFile = 14,*/ 73 | domainName = 15, 74 | /*swapServer = 16, 75 | rootPath = 17, 76 | extentionsPath = 18, 77 | IPforwarding = 19, 78 | nonLocalSourceRouting = 20, 79 | policyFilter = 21, 80 | maxDgramReasmSize = 22, 81 | defaultIPTTL = 23, 82 | pathMTUagingTimeout = 24, 83 | pathMTUplateauTable = 25, 84 | ifMTU = 26, 85 | allSubnetsLocal = 27, 86 | broadcastAddr = 28, 87 | performMaskDiscovery = 29, 88 | maskSupplier = 30, 89 | performRouterDiscovery = 31, 90 | routerSolicitationAddr = 32, 91 | staticRoute = 33, 92 | trailerEncapsulation = 34, 93 | arpCacheTimeout = 35, 94 | ethernetEncapsulation = 36, 95 | tcpDefaultTTL = 37, 96 | tcpKeepaliveInterval = 38, 97 | tcpKeepaliveGarbage = 39, 98 | nisDomainName = 40, 99 | nisServers = 41, 100 | ntpServers = 42, 101 | vendorSpecificInfo = 43, 102 | netBIOSnameServer = 44, 103 | netBIOSdgramDistServer = 45, 104 | netBIOSnodeType = 46, 105 | netBIOSscope = 47, 106 | xFontServer = 48, 107 | xDisplayManager = 49,*/ 108 | dhcpRequestedIPaddr = 50, 109 | dhcpIPaddrLeaseTime = 51, 110 | /*dhcpOptionOverload = 52,*/ 111 | dhcpMessageType = 53, 112 | dhcpServerIdentifier = 54, 113 | dhcpParamRequest = 55, 114 | /*dhcpMsg = 56, 115 | dhcpMaxMsgSize = 57,*/ 116 | dhcpT1value = 58, 117 | dhcpT2value = 59, 118 | /*dhcpClassIdentifier = 60,*/ 119 | dhcpClientIdentifier = 61, 120 | endOption = 255 121 | }; 122 | 123 | typedef struct _RIP_MSG_FIXED 124 | { 125 | uint8_t op; 126 | uint8_t htype; 127 | uint8_t hlen; 128 | uint8_t hops; 129 | uint32_t xid; 130 | uint16_t secs; 131 | uint16_t flags; 132 | uint8_t ciaddr[4]; 133 | uint8_t yiaddr[4]; 134 | uint8_t siaddr[4]; 135 | uint8_t giaddr[4]; 136 | uint8_t chaddr[6]; 137 | }RIP_MSG_FIXED; 138 | 139 | class DhcpClass { 140 | private: 141 | uint32_t _dhcpInitialTransactionId; 142 | uint32_t _dhcpTransactionId; 143 | uint8_t _dhcpMacAddr[6]; 144 | const char* _hostname = nullptr; 145 | #ifdef __arm__ 146 | uint8_t _dhcpLocalIp[4] __attribute__((aligned(4))); 147 | uint8_t _dhcpSubnetMask[4] __attribute__((aligned(4))); 148 | uint8_t _dhcpGatewayIp[4] __attribute__((aligned(4))); 149 | uint8_t _dhcpDhcpServerIp[4] __attribute__((aligned(4))); 150 | uint8_t _dhcpDnsServerIp[4] __attribute__((aligned(4))); 151 | #else 152 | uint8_t _dhcpLocalIp[4]; 153 | uint8_t _dhcpSubnetMask[4]; 154 | uint8_t _dhcpGatewayIp[4]; 155 | uint8_t _dhcpDhcpServerIp[4]; 156 | uint8_t _dhcpDnsServerIp[4]; 157 | #endif 158 | uint32_t _dhcpLeaseTime; 159 | uint32_t _dhcpT1, _dhcpT2; 160 | signed long _renewInSec; 161 | signed long _rebindInSec; 162 | signed long _lastCheck; 163 | unsigned long _timeout; 164 | unsigned long _responseTimeout; 165 | unsigned long _secTimeout; 166 | uint8_t _dhcp_state; 167 | EthernetUDP _dhcpUdpSocket; 168 | 169 | int request_DHCP_lease(); 170 | void reset_DHCP_lease(); 171 | void presend_DHCP(); 172 | void send_DHCP_MESSAGE(uint8_t, uint16_t); 173 | void printByte(char *, uint8_t); 174 | 175 | uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId); 176 | public: 177 | IPAddress getLocalIp(); 178 | IPAddress getSubnetMask(); 179 | IPAddress getGatewayIp(); 180 | IPAddress getDhcpServerIp(); 181 | IPAddress getDnsServerIp(); 182 | 183 | int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); 184 | int checkLease(); 185 | 186 | void setHostname(const char* hostname); 187 | }; 188 | 189 | #endif 190 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | First an important note: There are more suitable libraries for ENC28J60 with ESP8266, RP2040, ESP32 and Mbed Arduino platforms: 2 | 3 | * With ESP8266 and RP2040 platform you can use the EthernetCompat.h from the lwIP_enc28j60 bundled library. 4 | * 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. 5 | * 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 6 | 7 | --- 8 | 9 | # EthernetENC 10 | 11 | EthernetENC is the Ethernet library for ENC28J60. It is a modern version of [the UIPEthernet library](https://github.com/jandrassy/EthernetENC/wiki/UIPEthernet). 12 | 13 | The modernization includes: 14 | * Ethernet 2.0.0 Arduino library functions 15 | * compatible include file names EthernetClient.h, EthernetServer.h and EthernetUdp.h 16 | * support of many Arduino architectures by using the SPI library 17 | * SPI transactions to share the SPI bus with devices with different communication settings 18 | * SPI communication at 20 MHz if the MCU supports it, else on the maximum supported by the MCU 19 | * client.flush() to send the packet immediately 20 | * calls yield() in blocking functions 21 | * has UDP backlog to receive more than one message at time 22 | * Arduino 1.5 library format built with dot_a_linkage option for optimal build result 23 | 24 | [The documentation of Arduino Ethernet library](https://www.arduino.cc/en/Reference/Ethernet) applies for classes and functions descriptions. 25 | 26 | Limitations: 27 | * UDP.beginMulticast is not supported, because the uIP stack doesn't support multicast 28 | * UDP broadcasts receiving is turned off on ENC to lower the processing load on the library 29 | 30 | This library doesn't have examples, because examples of the Arduino Ethernet library apply. You can find them in the Arduino IDE Examples menu Ethernet section. Only change `#include ` to `#include `. Some examples require [a little change](https://github.com/jandrassy/EthernetENC/wiki/Examples). 31 | 32 | 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. 33 | 34 | **You can find more information in project's [Wiki](https://github.com/jandrassy/EthernetENC/wiki).** 35 | 36 | ## Original notes by Norbert Truchsess 37 | 38 | 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. 39 | 40 | uIP was written by Adam Dunkels of the Networked Embedded Systems group at the Swedish Institute of Computer Science. 41 | 42 | This library was inspired by the SerialIP implementation by Adam Nielsen , actually I took this code as a starting point, but in the latest versions there are very few lines left. 43 | 44 | 45 | ## Licenses 46 | ``` 47 | UIPEthernet.h 48 | UIPEthernet.cpp 49 | UIPServer.h 50 | UIPServer.cpp 51 | UIPClient.h 52 | UIPClient.cpp 53 | UIPUdp.h 54 | UIPUdp.cpp 55 | utility/mempool.h 56 | utility/mempool.cpp 57 | 58 | Copyright (c) 2013 Norbert Truchsess 59 | All rights reserved. 60 | 61 | This program is free software: you can redistribute it and/or modify 62 | it under the terms of the GNU General Public License as published by 63 | the Free Software Foundation, either version 3 of the License, or 64 | (at your option) any later version. 65 | 66 | This program is distributed in the hope that it will be useful, 67 | but WITHOUT ANY WARRANTY; without even the implied warranty of 68 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 69 | GNU General Public License for more details. 70 | 71 | You should have received a copy of the GNU General Public License 72 | along with this program. If not, see . 73 | 74 | -------------- 75 | 76 | utility/enc28j60.h 77 | 78 | Author : Pascal Stang (c)2005 79 | Modified by Norbert Truchsess 80 | Copyright: GPL V2 81 | 82 | -------------- 83 | 84 | utility/Enc28J60Network.h 85 | utility/Enc28J60Network.cpp 86 | 87 | Copyright (c) 2013 Norbert Truchsess 88 | All rights reserved. 89 | 90 | inspired and based on enc28j60.c file from the AVRlib library by Pascal Stang. 91 | 92 | This program is free software: you can redistribute it and/or modify 93 | it under the terms of the GNU General Public License as published by 94 | the Free Software Foundation, either version 3 of the License, or 95 | (at your option) any later version. 96 | 97 | This program is distributed in the hope that it will be useful, 98 | but WITHOUT ANY WARRANTY; without even the implied warranty of 99 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 100 | GNU General Public License for more details. 101 | 102 | You should have received a copy of the GNU General Public License 103 | along with this program. If not, see . 104 | 105 | -------------- 106 | 107 | utility/uip.c 108 | utility/uip_arp.h 109 | utility/uip_arp.c 110 | utility/uip_arch.h 111 | utility/uip.h 112 | utility/uipopt.h 113 | 114 | Copyright (c) 2001-2003, Adam Dunkels , . 115 | All rights reserved. 116 | 117 | Redistribution and use in source and binary forms, with or without 118 | modification, are permitted provided that the following conditions 119 | are met: 120 | 1. Redistributions of source code must retain the above copyright 121 | notice, this list of conditions and the following disclaimer. 122 | 2. Redistributions in binary form must reproduce the above copyright 123 | notice, this list of conditions and the following disclaimer in the 124 | documentation and/or other materials provided with the distribution. 125 | 3. Neither the name of the Institute nor the names of its contributors 126 | may be used to endorse or promote products derived from this software 127 | without specific prior written permission. 128 | 129 | THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 130 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 131 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 132 | ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 133 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 134 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 135 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 136 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 137 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 138 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 139 | SUCH DAMAGE. 140 | 141 | -------------- 142 | 143 | Dhcp.h 144 | Dhcp.cpp 145 | 146 | DHCP Library v0.3 - April 25, 2009 147 | Author: Jordan Terrell - blog.jordanterrell.com 148 | - as included in Arduinos stock Ethernet-library, no special licence mentioned here 149 | 150 | -------------- 151 | 152 | Dns.h 153 | Dns.cpp 154 | 155 | (c) Copyright 2009-2010 MCQN Ltd. 156 | Released under Apache License, version 2.0 157 | 158 | ``` -------------------------------------------------------------------------------- /src/utility/enc28j60.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * 3 | * Title : Microchip ENC28J60 Ethernet Interface Driver 4 | * Author : Pascal Stang (c)2005 5 | * Modified by Norbert Truchsess 6 | * Copyright: GPL V2 7 | * 8 | *This driver provides initialization and transmit/receive 9 | *functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. 10 | *This chip is novel in that it is a full MAC+PHY interface all in a 28-pin 11 | *chip, using an SPI interface to the host processor. 12 | * 13 | * 14 | *****************************************************************************/ 15 | 16 | #ifndef ENC28J60_H 17 | #define ENC28J60_H 18 | #include 19 | 20 | // ENC28J60 Control Registers 21 | // Control register definitions are a combination of address, 22 | // bank number, and Ethernet/MAC/PHY indicator bits. 23 | // - Register address (bits 0-4) 24 | // - Bank number (bits 5-6) 25 | // - MAC/PHY indicator (bit 7) 26 | #define ADDR_MASK 0x1F 27 | #define BANK_MASK 0x60 28 | #define SPRD_MASK 0x80 29 | // All-bank registers 30 | #define EIE 0x1B 31 | #define EIR 0x1C 32 | #define ESTAT 0x1D 33 | #define ECON2 0x1E 34 | #define ECON1 0x1F 35 | // Bank 0 registers 36 | #define ERDPTL (0x00|0x00) 37 | #define ERDPTH (0x01|0x00) 38 | #define EWRPTL (0x02|0x00) 39 | #define EWRPTH (0x03|0x00) 40 | #define ETXSTL (0x04|0x00) 41 | #define ETXSTH (0x05|0x00) 42 | #define ETXNDL (0x06|0x00) 43 | #define ETXNDH (0x07|0x00) 44 | #define ERXSTL (0x08|0x00) 45 | #define ERXSTH (0x09|0x00) 46 | #define ERXNDL (0x0A|0x00) 47 | #define ERXNDH (0x0B|0x00) 48 | #define ERXRDPTL (0x0C|0x00) 49 | #define ERXRDPTH (0x0D|0x00) 50 | #define ERXWRPTL (0x0E|0x00) 51 | #define ERXWRPTH (0x0F|0x00) 52 | #define EDMASTL (0x10|0x00) 53 | #define EDMASTH (0x11|0x00) 54 | #define EDMANDL (0x12|0x00) 55 | #define EDMANDH (0x13|0x00) 56 | #define EDMADSTL (0x14|0x00) 57 | #define EDMADSTH (0x15|0x00) 58 | #define EDMACSL (0x16|0x00) 59 | #define EDMACSH (0x17|0x00) 60 | // Bank 1 registers 61 | #define EHT0 (0x00|0x20) 62 | #define EHT1 (0x01|0x20) 63 | #define EHT2 (0x02|0x20) 64 | #define EHT3 (0x03|0x20) 65 | #define EHT4 (0x04|0x20) 66 | #define EHT5 (0x05|0x20) 67 | #define EHT6 (0x06|0x20) 68 | #define EHT7 (0x07|0x20) 69 | #define EPMM0 (0x08|0x20) 70 | #define EPMM1 (0x09|0x20) 71 | #define EPMM2 (0x0A|0x20) 72 | #define EPMM3 (0x0B|0x20) 73 | #define EPMM4 (0x0C|0x20) 74 | #define EPMM5 (0x0D|0x20) 75 | #define EPMM6 (0x0E|0x20) 76 | #define EPMM7 (0x0F|0x20) 77 | #define EPMCSL (0x10|0x20) 78 | #define EPMCSH (0x11|0x20) 79 | #define EPMOL (0x14|0x20) 80 | #define EPMOH (0x15|0x20) 81 | #define EWOLIE (0x16|0x20) 82 | #define EWOLIR (0x17|0x20) 83 | #define ERXFCON (0x18|0x20) 84 | #define EPKTCNT (0x19|0x20) 85 | // Bank 2 registers 86 | #define MACON1 (0x00|0x40|0x80) 87 | #define MACON2 (0x01|0x40|0x80) 88 | #define MACON3 (0x02|0x40|0x80) 89 | #define MACON4 (0x03|0x40|0x80) 90 | #define MABBIPG (0x04|0x40|0x80) 91 | #define MAIPGL (0x06|0x40|0x80) 92 | #define MAIPGH (0x07|0x40|0x80) 93 | #define MACLCON1 (0x08|0x40|0x80) 94 | #define MACLCON2 (0x09|0x40|0x80) 95 | #define MAMXFLL (0x0A|0x40|0x80) 96 | #define MAMXFLH (0x0B|0x40|0x80) 97 | #define MAPHSUP (0x0D|0x40|0x80) 98 | #define MICON (0x11|0x40|0x80) 99 | #define MICMD (0x12|0x40|0x80) 100 | #define MIREGADR (0x14|0x40|0x80) 101 | #define MIWRL (0x16|0x40|0x80) 102 | #define MIWRH (0x17|0x40|0x80) 103 | #define MIRDL (0x18|0x40|0x80) 104 | #define MIRDH (0x19|0x40|0x80) 105 | // Bank 3 registers 106 | #define MAADR1 (0x00|0x60|0x80) 107 | #define MAADR0 (0x01|0x60|0x80) 108 | #define MAADR3 (0x02|0x60|0x80) 109 | #define MAADR2 (0x03|0x60|0x80) 110 | #define MAADR5 (0x04|0x60|0x80) 111 | #define MAADR4 (0x05|0x60|0x80) 112 | #define EBSTSD (0x06|0x60) 113 | #define EBSTCON (0x07|0x60) 114 | #define EBSTCSL (0x08|0x60) 115 | #define EBSTCSH (0x09|0x60) 116 | #define MISTAT (0x0A|0x60|0x80) 117 | #define EREVID (0x12|0x60) 118 | #define ECOCON (0x15|0x60) 119 | #define EFLOCON (0x17|0x60) 120 | #define EPAUSL (0x18|0x60) 121 | #define EPAUSH (0x19|0x60) 122 | // PHY registers 123 | #define PHCON1 0x00 124 | #define PHSTAT1 0x01 125 | #define PHHID1 0x02 126 | #define PHHID2 0x03 127 | #define PHCON2 0x10 128 | #define PHSTAT2 0x11 129 | #define PHIE 0x12 130 | #define PHIR 0x13 131 | #define PHLCON 0x14 132 | 133 | // ENC28J60 ERXFCON Register Bit Definitions 134 | #define ERXFCON_UCEN 0x80 135 | #define ERXFCON_ANDOR 0x40 136 | #define ERXFCON_CRCEN 0x20 137 | #define ERXFCON_PMEN 0x10 138 | #define ERXFCON_MPEN 0x08 139 | #define ERXFCON_HTEN 0x04 140 | #define ERXFCON_MCEN 0x02 141 | #define ERXFCON_BCEN 0x01 142 | // ENC28J60 EIE Register Bit Definitions 143 | #define EIE_INTIE 0x80 144 | #define EIE_PKTIE 0x40 145 | #define EIE_DMAIE 0x20 146 | #define EIE_LINKIE 0x10 147 | #define EIE_TXIE 0x08 148 | #define EIE_WOLIE 0x04 149 | #define EIE_TXERIE 0x02 150 | #define EIE_RXERIE 0x01 151 | // ENC28J60 EIR Register Bit Definitions 152 | #define EIR_PKTIF 0x40 153 | #define EIR_DMAIF 0x20 154 | #define EIR_LINKIF 0x10 155 | #define EIR_TXIF 0x08 156 | #define EIR_WOLIF 0x04 157 | #define EIR_TXERIF 0x02 158 | #define EIR_RXERIF 0x01 159 | // ENC28J60 ESTAT Register Bit Definitions 160 | #define ESTAT_INT 0x80 161 | #define ESTAT_LATECOL 0x10 162 | #define ESTAT_RXBUSY 0x04 163 | #define ESTAT_TXABRT 0x02 164 | #define ESTAT_CLKRDY 0x01 165 | // ENC28J60 ECON2 Register Bit Definitions 166 | #define ECON2_AUTOINC 0x80 167 | #define ECON2_PKTDEC 0x40 168 | #define ECON2_PWRSV 0x20 169 | #define ECON2_VRPS 0x08 170 | // ENC28J60 ECON1 Register Bit Definitions 171 | #define ECON1_TXRST 0x80 172 | #define ECON1_RXRST 0x40 173 | #define ECON1_DMAST 0x20 174 | #define ECON1_CSUMEN 0x10 175 | #define ECON1_TXRTS 0x08 176 | #define ECON1_RXEN 0x04 177 | #define ECON1_BSEL1 0x02 178 | #define ECON1_BSEL0 0x01 179 | // ENC28J60 MACON1 Register Bit Definitions 180 | #define MACON1_LOOPBK 0x10 181 | #define MACON1_TXPAUS 0x08 182 | #define MACON1_RXPAUS 0x04 183 | #define MACON1_PASSALL 0x02 184 | #define MACON1_MARXEN 0x01 185 | // ENC28J60 MACON2 Register Bit Definitions 186 | #define MACON2_MARST 0x80 187 | #define MACON2_RNDRST 0x40 188 | #define MACON2_MARXRST 0x08 189 | #define MACON2_RFUNRST 0x04 190 | #define MACON2_MATXRST 0x02 191 | #define MACON2_TFUNRST 0x01 192 | // ENC28J60 MACON3 Register Bit Definitions 193 | #define MACON3_PADCFG2 0x80 194 | #define MACON3_PADCFG1 0x40 195 | #define MACON3_PADCFG0 0x20 196 | #define MACON3_TXCRCEN 0x10 197 | #define MACON3_PHDRLEN 0x08 198 | #define MACON3_HFRMLEN 0x04 199 | #define MACON3_FRMLNEN 0x02 200 | #define MACON3_FULDPX 0x01 201 | // ENC28J60 MICMD Register Bit Definitions 202 | #define MICMD_MIISCAN 0x02 203 | #define MICMD_MIIRD 0x01 204 | // ENC28J60 MISTAT Register Bit Definitions 205 | #define MISTAT_NVALID 0x04 206 | #define MISTAT_SCAN 0x02 207 | #define MISTAT_BUSY 0x01 208 | // ENC28J60 PHY PHCON1 Register Bit Definitions 209 | #define PHCON1_PRST 0x8000 210 | #define PHCON1_PLOOPBK 0x4000 211 | #define PHCON1_PPWRSV 0x0800 212 | #define PHCON1_PDPXMD 0x0100 213 | // ENC28J60 PHY PHSTAT1 Register Bit Definitions 214 | #define PHSTAT1_PFDPX 0x1000 215 | #define PHSTAT1_PHDPX 0x0800 216 | #define PHSTAT1_LLSTAT 0x0004 217 | #define PHSTAT1_JBSTAT 0x0002 218 | // ENC28J60 PHY PHCON2 Register Bit Definitions 219 | #define PHCON2_FRCLINK 0x4000 220 | #define PHCON2_TXDIS 0x2000 221 | #define PHCON2_JABBER 0x0400 222 | #define PHCON2_HDLDIS 0x0100 223 | 224 | // ENC28J60 Packet Control Byte Bit Definitions 225 | #define PKTCTRL_PHUGEEN 0x08 226 | #define PKTCTRL_PPADEN 0x04 227 | #define PKTCTRL_PCRCEN 0x02 228 | #define PKTCTRL_POVERRIDE 0x01 229 | 230 | // SPI operation codes 231 | #define ENC28J60_READ_CTRL_REG 0x00 232 | #define ENC28J60_READ_BUF_MEM 0x3A 233 | #define ENC28J60_WRITE_CTRL_REG 0x40 234 | #define ENC28J60_WRITE_BUF_MEM 0x7A 235 | #define ENC28J60_BIT_FIELD_SET 0x80 236 | #define ENC28J60_BIT_FIELD_CLR 0xA0 237 | #define ENC28J60_SOFT_RESET 0xFF 238 | 239 | 240 | // The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata 241 | // buffer boundaries applied to internal 8K ram 242 | // the entire available packet buffer space is allocated 243 | // 244 | // start with recbuf at 0/ 245 | #define RXSTART_INIT 0x0 246 | // receive buffer end. make sure this is an odd value ( See Rev. B1,B4,B5,B7 Silicon Errata 'Memory (Ethernet Buffer)') 247 | #define RXSTOP_INIT (0x1FFF-0x1800) 248 | // start TX buffer RXSTOP_INIT+1 249 | #define TXSTART_INIT (RXSTOP_INIT+1) 250 | // stp TX buffer at end of mem 251 | #define TXSTOP_INIT 0x1FFF 252 | // 253 | // max frame length which the conroller will accept: 254 | #define MAX_FRAMELEN 1500 // (note: maximum ethernet frame length would be 1518) 255 | //#define MAX_FRAMELEN 600 256 | 257 | #endif 258 | -------------------------------------------------------------------------------- /src/EthernetUdp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | UIPUdp.cpp - Arduino implementation of a uIP wrapper class. 3 | Copyright (c) 2013 Norbert Truchsess 4 | All rights reserved. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #include "Ethernet.h" 21 | #include "EthernetUdp.h" 22 | 23 | extern "C" { 24 | #include "utility/uip-conf.h" 25 | #include "utility/uip.h" 26 | #include "utility/uip_arp.h" 27 | } 28 | 29 | #if UIP_UDP 30 | #define UIP_ARPHDRSIZE 42 31 | #define UDPBUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN]) 32 | 33 | #define UIPUDP EthernetUDP // to not pollute source code history with the rename 34 | 35 | // Constructor 36 | UIPUDP::UIPUDP() : 37 | _uip_udp_conn(NULL), 38 | appdata() 39 | { 40 | } 41 | 42 | // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use 43 | uint8_t 44 | UIPUDP::begin(uint16_t port) 45 | { 46 | if (!_uip_udp_conn) 47 | { 48 | _uip_udp_conn = uip_udp_new(NULL, 0); 49 | } 50 | if (_uip_udp_conn) 51 | { 52 | uip_udp_bind(_uip_udp_conn,htons(port)); 53 | _uip_udp_conn->appstate = &appdata; 54 | return 1; 55 | } 56 | return 0; 57 | } 58 | 59 | // Finish with the UDP socket 60 | void 61 | UIPUDP::stop() 62 | { 63 | if (_uip_udp_conn) 64 | { 65 | uip_udp_remove(_uip_udp_conn); 66 | _uip_udp_conn->appstate = NULL; 67 | _uip_udp_conn=NULL; 68 | Enc28J60Network::freeBlock(appdata.packet_in); 69 | _flushBlocks(appdata.packet_next); 70 | Enc28J60Network::freeBlock(appdata.packet_out); 71 | appdata = uip_udp_userdata_t(); 72 | } 73 | } 74 | 75 | // Sending UDP packets 76 | 77 | // Start building up a packet to send to the remote host specific in ip and port 78 | // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port 79 | int 80 | UIPUDP::beginPacket(IPAddress ip, uint16_t port) 81 | { 82 | UIPEthernetClass::tick(); 83 | if ((ip[0] || ip[1] || ip[2] || ip[3]) && port) 84 | { 85 | uip_ipaddr_t ripaddr; 86 | uip_ip_addr(&ripaddr, ip); 87 | #ifdef UIPETHERNET_DEBUG_UDP 88 | Serial.print(F("udp beginPacket, ")); 89 | #endif 90 | if (_uip_udp_conn) 91 | { 92 | // [J.A]: for UDP server, setting ripaddr and rport causes in uIP filtering 93 | // of incoming messages. it would be better to store the IP and port in fields 94 | // and set them only in endPacket(). 95 | _uip_udp_conn->rport = htons(port); 96 | uip_ipaddr_copy(_uip_udp_conn->ripaddr, &ripaddr); 97 | } 98 | else 99 | { 100 | _uip_udp_conn = uip_udp_new(&ripaddr,htons(port)); 101 | if (_uip_udp_conn) 102 | { 103 | #ifdef UIPETHERNET_DEBUG_UDP 104 | Serial.print(F("new connection, ")); 105 | #endif 106 | _uip_udp_conn->appstate = &appdata; 107 | } 108 | else 109 | { 110 | #ifdef UIPETHERNET_DEBUG_UDP 111 | Serial.println(F("failed to allocate new connection")); 112 | #endif 113 | return 0; 114 | } 115 | } 116 | #ifdef UIPETHERNET_DEBUG_UDP 117 | Serial.print(F("rip: ")); 118 | Serial.print(ip); 119 | Serial.print(F(", port: ")); 120 | Serial.println(port); 121 | #endif 122 | } 123 | if (_uip_udp_conn) 124 | { 125 | if (appdata.packet_out == NOBLOCK) 126 | { 127 | appdata.packet_out = Enc28J60Network::allocBlock(UIP_UDP_MAXPACKETSIZE + UIP_SENDBUFFER_OFFSET + UIP_SENDBUFFER_PADDING); 128 | appdata.out_pos = UIP_UDP_PHYH_LEN + UIP_SENDBUFFER_OFFSET; 129 | if (appdata.packet_out != NOBLOCK) 130 | return 1; 131 | #ifdef UIPETHERNET_DEBUG_UDP 132 | else 133 | Serial.println(F("failed to allocate memory for packet")); 134 | #endif 135 | } 136 | #ifdef UIPETHERNET_DEBUG_UDP 137 | else 138 | Serial.println(F("previous packet on that connection not sent yet")); 139 | #endif 140 | } 141 | return 0; 142 | } 143 | 144 | // Start building up a packet to send to the remote host specific in host and port 145 | // Returns 1 if successful, 0 if there was a problem resolving the hostname or port 146 | int 147 | UIPUDP::beginPacket(const char *host, uint16_t port) 148 | { 149 | // Look up the host first 150 | int ret = 0; 151 | IPAddress remote_addr; 152 | ret = Ethernet.hostByName(host, remote_addr); 153 | if (ret == 1) { 154 | return beginPacket(remote_addr, port); 155 | } else { 156 | return ret; 157 | } 158 | } 159 | 160 | // Finish off this packet and send it 161 | // Returns 1 if the packet was sent successfully, 0 if there was an error 162 | int 163 | UIPUDP::endPacket() 164 | { 165 | if (_uip_udp_conn && appdata.packet_out != NOBLOCK) 166 | { 167 | appdata.send = true; 168 | Enc28J60Network::resizeBlock(appdata.packet_out,0,appdata.out_pos + UIP_SENDBUFFER_PADDING); 169 | uip_udp_periodic_conn(_uip_udp_conn); 170 | if (uip_len > 0) 171 | { 172 | _send(_uip_udp_conn); 173 | return 1; 174 | } 175 | } 176 | return 0; 177 | } 178 | 179 | // Write a single byte into the packet 180 | size_t 181 | UIPUDP::write(uint8_t c) 182 | { 183 | return write(&c,1); 184 | } 185 | 186 | // Write size bytes from buffer into the packet 187 | size_t 188 | UIPUDP::write(const uint8_t *buffer, size_t size) 189 | { 190 | if (appdata.packet_out != NOBLOCK) 191 | { 192 | size_t ret = Enc28J60Network::writePacket(appdata.packet_out,appdata.out_pos,(uint8_t*)buffer,size); 193 | appdata.out_pos += ret; 194 | return ret; 195 | } 196 | return 0; 197 | } 198 | 199 | // Start processing the next available incoming packet 200 | // Returns the size of the packet in bytes, or 0 if no packets are available 201 | int 202 | UIPUDP::parsePacket() 203 | { 204 | UIPEthernetClass::tick(); 205 | #ifdef UIPETHERNET_DEBUG_UDP 206 | if (appdata.packet_in != NOBLOCK) 207 | { 208 | Serial.print(F("udp parsePacket freeing previous packet: ")); 209 | Serial.println(appdata.packet_in); 210 | } 211 | #endif 212 | Enc28J60Network::freeBlock(appdata.packet_in); 213 | 214 | appdata.packet_in = appdata.packet_next[0].packet; 215 | uip_ipaddr_copy(appdata.remote_ip, appdata.packet_next[0].remote_ip); 216 | appdata.remote_port = appdata.packet_next[0].remote_port; 217 | _moveBlocks(appdata.packet_next); 218 | 219 | #ifdef UIPETHERNET_DEBUG_UDP 220 | if (appdata.packet_in != NOBLOCK) 221 | { 222 | Serial.print(F("udp parsePacket received packet: ")); 223 | Serial.print(appdata.packet_in); 224 | } 225 | #endif 226 | int size = Enc28J60Network::blockSize(appdata.packet_in); 227 | #ifdef UIPETHERNET_DEBUG_UDP 228 | if (appdata.packet_in != NOBLOCK) 229 | { 230 | Serial.print(F(", size: ")); 231 | Serial.println(size); 232 | } 233 | #endif 234 | return size; 235 | } 236 | 237 | // Number of bytes remaining in the current packet 238 | int 239 | UIPUDP::available() 240 | { 241 | UIPEthernetClass::tick(); 242 | return Enc28J60Network::blockSize(appdata.packet_in); 243 | } 244 | 245 | // Read a single byte from the current packet 246 | int 247 | UIPUDP::read() 248 | { 249 | unsigned char c; 250 | if (read(&c,1) > 0) 251 | { 252 | return c; 253 | } 254 | return -1; 255 | } 256 | 257 | // Read up to len bytes from the current packet and place them into buffer 258 | // Returns the number of bytes read, or 0 if none are available 259 | int 260 | UIPUDP::read(unsigned char* buffer, size_t len) 261 | { 262 | UIPEthernetClass::tick(); 263 | if (appdata.packet_in != NOBLOCK) 264 | { 265 | memaddress read = Enc28J60Network::readPacket(appdata.packet_in,0,buffer,len); 266 | if (read == Enc28J60Network::blockSize(appdata.packet_in)) 267 | { 268 | Enc28J60Network::freeBlock(appdata.packet_in); 269 | appdata.packet_in = NOBLOCK; 270 | } 271 | else 272 | Enc28J60Network::resizeBlock(appdata.packet_in,read); 273 | return read; 274 | } 275 | return 0; 276 | } 277 | 278 | // Return the next byte from the current packet without moving on to the next byte 279 | int 280 | UIPUDP::peek() 281 | { 282 | UIPEthernetClass::tick(); 283 | if (appdata.packet_in != NOBLOCK) 284 | { 285 | unsigned char c; 286 | if (Enc28J60Network::readPacket(appdata.packet_in,0,&c,1) == 1) 287 | return c; 288 | } 289 | return -1; 290 | } 291 | 292 | // Finish reading the current packet 293 | void 294 | UIPUDP::discardReceived() 295 | { 296 | UIPEthernetClass::tick(); 297 | Enc28J60Network::freeBlock(appdata.packet_in); 298 | appdata.packet_in = NOBLOCK; 299 | } 300 | 301 | // Return the IP address of the host who sent the current incoming packet 302 | IPAddress 303 | UIPUDP::remoteIP() 304 | { 305 | return ip_addr_uip(appdata.remote_ip); 306 | } 307 | 308 | // Return the port of the host who sent the current incoming packet 309 | uint16_t 310 | UIPUDP::remotePort() 311 | { 312 | return ntohs(appdata.remote_port); 313 | } 314 | 315 | // uIP callback function 316 | 317 | void 318 | uipudp_appcall(void) { 319 | if (uip_udp_userdata_t *data = (uip_udp_userdata_t *)(uip_udp_conn->appstate)) 320 | { 321 | if (uip_newdata()) 322 | { 323 | uint8_t p = UIPUDP::_newBlock(data->packet_next); 324 | if (data->packet_next[p].packet == NOBLOCK) 325 | { 326 | data->packet_next[p].remote_port = UDPBUF->srcport; 327 | uip_ipaddr_copy( data->packet_next[p].remote_ip,UDPBUF->srcipaddr); 328 | data->packet_next[p].packet = Enc28J60Network::allocBlock(ntohs(UDPBUF->udplen)-UIP_UDPH_LEN); 329 | //if we are unable to allocate memory the packet is dropped. udp doesn't guarantee packet delivery 330 | if (data->packet_next[p].packet != NOBLOCK) 331 | { 332 | //discard Linklevel and IP and udp-header and any trailing bytes: 333 | Enc28J60Network::copyPacket(data->packet_next[p].packet,0,UIPEthernetClass::in_packet,UIP_UDP_PHYH_LEN,Enc28J60Network::blockSize(data->packet_next[p].packet)); 334 | #ifdef UIPETHERNET_DEBUG_UDP 335 | Serial.print(F("udp, uip_newdata received packet: ")); 336 | Serial.print(data->packet_next[p].packet); 337 | Serial.print(F(", size: ")); 338 | Serial.println(Enc28J60Network::blockSize(data->packet_next[p].packet)); 339 | #endif 340 | } 341 | } 342 | } 343 | if (uip_poll() && data->send) 344 | { 345 | //set uip_slen (uip private) by calling uip_udp_send 346 | #ifdef UIPETHERNET_DEBUG_UDP 347 | Serial.print(F("udp, uip_poll preparing packet to send: ")); 348 | Serial.print(data->packet_out); 349 | Serial.print(F(", size: ")); 350 | Serial.println(Enc28J60Network::blockSize(data->packet_out)); 351 | #endif 352 | UIPEthernetClass::uip_packet = data->packet_out; 353 | UIPEthernetClass::packetstate |= UIPETHERNET_SENDPACKET; 354 | UIPEthernetClass::uip_hdrlen = UIP_UDP_PHYH_LEN; 355 | uip_udp_send(data->out_pos - (UIP_UDP_PHYH_LEN + UIP_SENDBUFFER_OFFSET)); 356 | } 357 | } 358 | } 359 | 360 | void 361 | UIPUDP::_send(struct uip_udp_conn *uip_udp_conn) { 362 | 363 | uip_arp_out(); //add arp 364 | if (uip_len == UIP_ARPHDRSIZE) 365 | { 366 | UIPEthernetClass::uip_packet = NOBLOCK; 367 | UIPEthernetClass::packetstate &= ~UIPETHERNET_SENDPACKET; 368 | #ifdef UIPETHERNET_DEBUG_UDP 369 | Serial.println(F("udp, uip_poll results in ARP-packet")); 370 | #endif 371 | UIPEthernetClass::network_send(); 372 | } 373 | else 374 | //arp found ethaddr for ip (otherwise packet is replaced by arp-request) 375 | { 376 | uip_udp_userdata_t* data = (uip_udp_userdata_t *)(uip_udp_conn->appstate); 377 | UIPEthernetClass::network_send(); 378 | data->send = false; 379 | data->packet_out = NOBLOCK; 380 | 381 | // [J.A] a listening UDP port in uIP filters received messages 382 | // if rport and ripaddr are set. so we better clear them 383 | uip_udp_conn->rport = 0; 384 | uip_udp_conn->ripaddr[0] = 0; 385 | uip_udp_conn->ripaddr[1] = 0; 386 | 387 | #ifdef UIPETHERNET_DEBUG_UDP 388 | Serial.print(F("udp, uip_packet to send: ")); 389 | Serial.println(UIPEthernetClass::uip_packet); 390 | #endif 391 | } 392 | } 393 | 394 | uint8_t 395 | UIPUDP::_newBlock(uip_udp_msg_rec_t* block) 396 | { 397 | for (uint8_t i = 0; i < UIP_UDP_BACKLOG; i++) 398 | { 399 | if (block[i].packet == NOBLOCK) 400 | return i; 401 | } 402 | return UIP_UDP_BACKLOG-1; 403 | } 404 | 405 | void 406 | UIPUDP::_moveBlocks(uip_udp_msg_rec_t* block) 407 | { 408 | for (uint8_t i = 0; i < UIP_UDP_BACKLOG-1; i++) 409 | { 410 | block[i] = block[i+1]; 411 | } 412 | block[UIP_UDP_BACKLOG-1].packet = NOBLOCK; 413 | } 414 | 415 | void 416 | UIPUDP::_flushBlocks(uip_udp_msg_rec_t* block) 417 | { 418 | for (uint8_t i = 0; i < UIP_UDP_BACKLOG; i++) 419 | { 420 | Enc28J60Network::freeBlock(block[i].packet); 421 | block[i].packet = NOBLOCK; 422 | } 423 | } 424 | #endif 425 | -------------------------------------------------------------------------------- /src/Dns.cpp: -------------------------------------------------------------------------------- 1 | // Arduino DNS client for Enc28J60-based Ethernet shield 2 | // (c) Copyright 2009-2010 MCQN Ltd. 3 | // Released under Apache License, version 2.0 4 | 5 | #include "Dns.h" 6 | #include 7 | 8 | #define SOCKET_NONE 255 9 | // Various flags and header field values for a DNS message 10 | #define UDP_HEADER_SIZE 8 11 | #define DNS_HEADER_SIZE 12 12 | #define TTL_SIZE 4 13 | #define QUERY_FLAG (0) 14 | #define RESPONSE_FLAG (1<<15) 15 | #define QUERY_RESPONSE_MASK (1<<15) 16 | #define OPCODE_STANDARD_QUERY (0) 17 | #define OPCODE_INVERSE_QUERY (1<<11) 18 | #define OPCODE_STATUS_REQUEST (2<<11) 19 | #define OPCODE_MASK (15<<11) 20 | #define AUTHORITATIVE_FLAG (1<<10) 21 | #define TRUNCATION_FLAG (1<<9) 22 | #define RECURSION_DESIRED_FLAG (1<<8) 23 | #define RECURSION_AVAILABLE_FLAG (1<<7) 24 | #define RESP_NO_ERROR (0) 25 | #define RESP_FORMAT_ERROR (1) 26 | #define RESP_SERVER_FAILURE (2) 27 | #define RESP_NAME_ERROR (3) 28 | #define RESP_NOT_IMPLEMENTED (4) 29 | #define RESP_REFUSED (5) 30 | #define RESP_MASK (15) 31 | #define TYPE_A (0x0001) 32 | #define CLASS_IN (0x0001) 33 | #define LABEL_COMPRESSION_MASK (0xC0) 34 | // Port number that DNS servers listen on 35 | #define DNS_PORT 53 36 | 37 | // Possible return codes from ProcessResponse 38 | #define SUCCESS 1 39 | #define TIMED_OUT -1 40 | #define INVALID_SERVER -2 41 | #define TRUNCATED -3 42 | #define INVALID_RESPONSE -4 43 | 44 | void DNSClient::begin(const IPAddress& aDNSServer) 45 | { 46 | iDNSServer = aDNSServer; 47 | iRequestId = 0; 48 | } 49 | 50 | 51 | int DNSClient::inet_aton(const char* aIPAddrString, IPAddress& aResult) 52 | { 53 | // See if we've been given a valid IP address 54 | const char* p =aIPAddrString; 55 | while (*p && 56 | ( (*p == '.') || ((*p >= '0') && (*p <= '9')) )) 57 | { 58 | p++; 59 | } 60 | 61 | if (*p == '\0') 62 | { 63 | // It's looking promising, we haven't found any invalid characters 64 | p = aIPAddrString; 65 | int segment =0; 66 | int segmentValue =0; 67 | while (*p && (segment < 4)) 68 | { 69 | if (*p == '.') 70 | { 71 | // We've reached the end of a segment 72 | if (segmentValue > 255) 73 | { 74 | // You can't have IP address segments that don't fit in a byte 75 | return 0; 76 | } 77 | else 78 | { 79 | aResult[segment] = (byte)segmentValue; 80 | segment++; 81 | segmentValue = 0; 82 | } 83 | } 84 | else 85 | { 86 | // Next digit 87 | segmentValue = (segmentValue*10)+(*p - '0'); 88 | } 89 | p++; 90 | } 91 | // We've reached the end of address, but there'll still be the last 92 | // segment to deal with 93 | if ((segmentValue > 255) || (segment > 3)) 94 | { 95 | // You can't have IP address segments that don't fit in a byte, 96 | // or more than four segments 97 | return 0; 98 | } 99 | else 100 | { 101 | aResult[segment] = (byte)segmentValue; 102 | return 1; 103 | } 104 | } 105 | else 106 | { 107 | return 0; 108 | } 109 | } 110 | 111 | int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) 112 | { 113 | int ret =0; 114 | 115 | // See if it's a numeric IP address 116 | if (inet_aton(aHostname, aResult)) 117 | { 118 | // It is, our work here is done 119 | return 1; 120 | } 121 | 122 | // Check we've got a valid DNS server to use 123 | if (iDNSServer == INADDR_NONE) 124 | { 125 | return INVALID_SERVER; 126 | } 127 | 128 | // Find a socket to use 129 | if (iUdp.begin(1024+(millis() & 0xF)) == 1) 130 | { 131 | // Try up to three times 132 | int retries = 0; 133 | // while ((retries < 3) && (ret <= 0)) 134 | { 135 | // Send DNS request 136 | ret = iUdp.beginPacket(iDNSServer, DNS_PORT); 137 | if (ret != 0) 138 | { 139 | // Now output the request data 140 | ret = BuildRequest(aHostname); 141 | if (ret != 0) 142 | { 143 | // And finally send the request 144 | ret = iUdp.endPacket(); 145 | if (ret != 0) 146 | { 147 | // Now wait for a response 148 | int wait_retries = 0; 149 | ret = TIMED_OUT; 150 | while ((wait_retries < 3) && (ret == TIMED_OUT)) 151 | { 152 | ret = ProcessResponse(5000, aResult); 153 | wait_retries++; 154 | } 155 | } 156 | } 157 | } 158 | retries++; 159 | } 160 | 161 | // We're done with the socket now 162 | iUdp.stop(); 163 | } 164 | 165 | return ret; 166 | } 167 | 168 | uint16_t DNSClient::BuildRequest(const char* aName) 169 | { 170 | // Build header 171 | // 1 1 1 1 1 1 172 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 173 | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 174 | // | ID | 175 | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 176 | // |QR| Opcode |AA|TC|RD|RA| Z | RCODE | 177 | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 178 | // | QDCOUNT | 179 | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 180 | // | ANCOUNT | 181 | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 182 | // | NSCOUNT | 183 | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 184 | // | ARCOUNT | 185 | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 186 | // As we only support one request at a time at present, we can simplify 187 | // some of this header 188 | iRequestId = millis(); // generate a random ID 189 | uint16_t twoByteBuffer; 190 | 191 | // FIXME We should also check that there's enough space available to write to, rather 192 | // FIXME than assume there's enough space (as the code does at present) 193 | iUdp.write((uint8_t*)&iRequestId, sizeof(iRequestId)); 194 | 195 | twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG); 196 | iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); 197 | 198 | twoByteBuffer = htons(1); // One question record 199 | iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); 200 | 201 | twoByteBuffer = 0; // Zero answer records 202 | iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); 203 | 204 | iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); 205 | // and zero additional records 206 | iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); 207 | 208 | // Build question 209 | const char* start =aName; 210 | const char* end =start; 211 | uint8_t len; 212 | // Run through the name being requested 213 | while (*end) 214 | { 215 | // Find out how long this section of the name is 216 | end = start; 217 | while (*end && (*end != '.') ) 218 | { 219 | end++; 220 | } 221 | 222 | if (end-start > 0) 223 | { 224 | // Write out the size of this section 225 | len = end-start; 226 | iUdp.write(&len, sizeof(len)); 227 | // And then write out the section 228 | iUdp.write((uint8_t*)start, end-start); 229 | } 230 | start = end+1; 231 | } 232 | 233 | // We've got to the end of the question name, so 234 | // terminate it with a zero-length section 235 | len = 0; 236 | iUdp.write(&len, sizeof(len)); 237 | // Finally the type and class of question 238 | twoByteBuffer = htons(TYPE_A); 239 | iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); 240 | 241 | twoByteBuffer = htons(CLASS_IN); // Internet class of question 242 | iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); 243 | // Success! Everything buffered okay 244 | return 1; 245 | } 246 | 247 | 248 | uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) 249 | { 250 | uint32_t startTime = millis(); 251 | 252 | // Wait for a response packet 253 | while(iUdp.parsePacket() <= 0) 254 | { 255 | if((millis() - startTime) > aTimeout) 256 | return TIMED_OUT; 257 | delay(50); 258 | } 259 | 260 | // We've had a reply! 261 | // Read the UDP header 262 | uint8_t header[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header 263 | // Check that it's a response from the right server and the right port 264 | if ( (iDNSServer != iUdp.remoteIP()) || 265 | (iUdp.remotePort() != DNS_PORT) ) 266 | { 267 | // It's not from who we expected 268 | return INVALID_SERVER; 269 | } 270 | 271 | // Read through the rest of the response 272 | if (iUdp.available() < DNS_HEADER_SIZE) 273 | { 274 | return TRUNCATED; 275 | } 276 | iUdp.read(header, DNS_HEADER_SIZE); 277 | 278 | uint16_t header_flags = htons(*((uint16_t*)&header[2])); 279 | // Check that it's a response to this request 280 | if ( ( iRequestId != (*((uint16_t*)&header[0])) ) || 281 | ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t)RESPONSE_FLAG) ) 282 | { 283 | // Mark the entire packet as read 284 | iUdp.discardReceived(); 285 | return INVALID_RESPONSE; 286 | } 287 | // Check for any errors in the response (or in our request) 288 | // although we don't do anything to get round these 289 | if ( (header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK) ) 290 | { 291 | // Mark the entire packet as read 292 | iUdp.discardReceived(); 293 | return -5; //INVALID_RESPONSE; 294 | } 295 | 296 | // And make sure we've got (at least) one answer 297 | uint16_t answerCount = htons(*((uint16_t*)&header[6])); 298 | if (answerCount == 0 ) 299 | { 300 | // Mark the entire packet as read 301 | iUdp.discardReceived(); 302 | return -6; //INVALID_RESPONSE; 303 | } 304 | 305 | // Skip over any questions 306 | for (uint16_t i =0; i < htons(*((uint16_t*)&header[4])); i++) 307 | { 308 | // Skip over the name 309 | uint8_t len; 310 | do 311 | { 312 | iUdp.read(&len, sizeof(len)); 313 | if (len > 0) 314 | { 315 | // Don't need to actually read the data out for the string, just 316 | // advance ptr to beyond it 317 | while(len--) 318 | { 319 | iUdp.read(); // we don't care about the returned byte 320 | } 321 | } 322 | } while (len != 0); 323 | 324 | // Now jump over the type and class 325 | for (int i =0; i < 4; i++) 326 | { 327 | iUdp.read(); // we don't care about the returned byte 328 | } 329 | } 330 | 331 | // Now we're up to the bit we're interested in, the answer 332 | // There might be more than one answer (although we'll just use the first 333 | // type A answer) and some authority and additional resource records but 334 | // we're going to ignore all of them. 335 | 336 | for (uint16_t i =0; i < answerCount; i++) 337 | { 338 | // Skip the name 339 | uint8_t len; 340 | do 341 | { 342 | iUdp.read(&len, sizeof(len)); 343 | if ((len & LABEL_COMPRESSION_MASK) == 0) 344 | { 345 | // It's just a normal label 346 | if (len > 0) 347 | { 348 | // And it's got a length 349 | // Don't need to actually read the data out for the string, 350 | // just advance ptr to beyond it 351 | while(len--) 352 | { 353 | iUdp.read(); // we don't care about the returned byte 354 | } 355 | } 356 | } 357 | else 358 | { 359 | // This is a pointer to a somewhere else in the message for the 360 | // rest of the name. We don't care about the name, and RFC1035 361 | // says that a name is either a sequence of labels ended with a 362 | // 0 length octet or a pointer or a sequence of labels ending in 363 | // a pointer. Either way, when we get here we're at the end of 364 | // the name 365 | // Skip over the pointer 366 | iUdp.read(); // we don't care about the returned byte 367 | // And set len so that we drop out of the name loop 368 | len = 0; 369 | } 370 | } while (len != 0); 371 | 372 | // Check the type and class 373 | uint16_t answerType; 374 | uint16_t answerClass; 375 | iUdp.read((uint8_t*)&answerType, sizeof(answerType)); 376 | iUdp.read((uint8_t*)&answerClass, sizeof(answerClass)); 377 | 378 | // Ignore the Time-To-Live as we don't do any caching 379 | for (int i =0; i < TTL_SIZE; i++) 380 | { 381 | iUdp.read(); // we don't care about the returned byte 382 | } 383 | 384 | // And read out the length of this answer 385 | // Don't need header_flags anymore, so we can reuse it here 386 | iUdp.read((uint8_t*)&header_flags, sizeof(header_flags)); 387 | 388 | if ( (htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN) ) 389 | { 390 | if (htons(header_flags) != 4) 391 | { 392 | // It's a weird size 393 | // Mark the entire packet as read 394 | iUdp.discardReceived(); 395 | return -9;//INVALID_RESPONSE; 396 | } 397 | iUdp.read(aAddress.raw_address(), 4); 398 | return SUCCESS; 399 | } 400 | else 401 | { 402 | // This isn't an answer type we're after, move onto the next one 403 | for (uint16_t i =0; i < htons(header_flags); i++) 404 | { 405 | iUdp.read(); // we don't care about the returned byte 406 | } 407 | } 408 | } 409 | 410 | // Mark the entire packet as read 411 | iUdp.discardReceived(); 412 | 413 | // If we get here then we haven't found an answer 414 | return -10;//INVALID_RESPONSE; 415 | } 416 | 417 | -------------------------------------------------------------------------------- /src/utility/uip_arp.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \addtogroup uip 3 | * @{ 4 | */ 5 | 6 | /** 7 | * \defgroup uiparp uIP Address Resolution Protocol 8 | * @{ 9 | * 10 | * The Address Resolution Protocol ARP is used for mapping between IP 11 | * addresses and link level addresses such as the Ethernet MAC 12 | * addresses. ARP uses broadcast queries to ask for the link level 13 | * address of a known IP address and the host which is configured with 14 | * the IP address for which the query was meant, will respond with its 15 | * link level address. 16 | * 17 | * \note This ARP implementation only supports Ethernet. 18 | */ 19 | 20 | /** 21 | * \file 22 | * Implementation of the ARP Address Resolution Protocol. 23 | * \author Adam Dunkels 24 | * 25 | */ 26 | 27 | /* 28 | * Copyright (c) 2001-2003, Adam Dunkels. 29 | * All rights reserved. 30 | * 31 | * Redistribution and use in source and binary forms, with or without 32 | * modification, are permitted provided that the following conditions 33 | * are met: 34 | * 1. Redistributions of source code must retain the above copyright 35 | * notice, this list of conditions and the following disclaimer. 36 | * 2. Redistributions in binary form must reproduce the above copyright 37 | * notice, this list of conditions and the following disclaimer in the 38 | * documentation and/or other materials provided with the distribution. 39 | * 3. The name of the author may not be used to endorse or promote 40 | * products derived from this software without specific prior 41 | * written permission. 42 | * 43 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 44 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 45 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 47 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 49 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 50 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 51 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 52 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 53 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 54 | * 55 | * This file is part of the uIP TCP/IP stack. 56 | * 57 | * $Id: uip_arp.c,v 1.8 2006/06/02 23:36:21 adam Exp $ 58 | * 59 | */ 60 | 61 | 62 | #include "uip_arp.h" 63 | 64 | #include 65 | 66 | struct arp_hdr { 67 | struct uip_eth_hdr ethhdr; 68 | u16_t hwtype; 69 | u16_t protocol; 70 | u8_t hwlen; 71 | u8_t protolen; 72 | u16_t opcode; 73 | struct uip_eth_addr shwaddr; 74 | u16_t sipaddr[2]; 75 | struct uip_eth_addr dhwaddr; 76 | u16_t dipaddr[2]; 77 | }; 78 | 79 | struct ethip_hdr { 80 | struct uip_eth_hdr ethhdr; 81 | /* IP header. */ 82 | u8_t vhl, 83 | tos, 84 | len[2], 85 | ipid[2], 86 | ipoffset[2], 87 | ttl, 88 | proto; 89 | u16_t ipchksum; 90 | u16_t srcipaddr[2], 91 | destipaddr[2]; 92 | }; 93 | 94 | #define ARP_REQUEST 1 95 | #define ARP_REPLY 2 96 | 97 | #define ARP_HWTYPE_ETH 1 98 | 99 | struct arp_entry { 100 | u16_t ipaddr[2]; 101 | struct uip_eth_addr ethaddr; 102 | u8_t time; 103 | }; 104 | 105 | static const struct uip_eth_addr broadcast_ethaddr = 106 | {{0xff,0xff,0xff,0xff,0xff,0xff}}; 107 | static const u16_t broadcast_ipaddr[2] = {0xffff,0xffff}; 108 | 109 | static struct arp_entry arp_table[UIP_ARPTAB_SIZE]; 110 | static u16_t ipaddr[2]; 111 | static u8_t i, c; 112 | 113 | static u8_t arptime; 114 | static u8_t tmpage; 115 | 116 | #define BUF ((struct arp_hdr *)&uip_buf[0]) 117 | #define IPBUF ((struct ethip_hdr *)&uip_buf[0]) 118 | /*-----------------------------------------------------------------------------------*/ 119 | /** 120 | * Initialize the ARP module. 121 | * 122 | */ 123 | /*-----------------------------------------------------------------------------------*/ 124 | void 125 | uip_arp_init(void) 126 | { 127 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 128 | memset(arp_table[i].ipaddr, 0, 4); 129 | } 130 | } 131 | /*-----------------------------------------------------------------------------------*/ 132 | /** 133 | * Periodic ARP processing function. 134 | * 135 | * This function performs periodic timer processing in the ARP module 136 | * and should be called at regular intervals. The recommended interval 137 | * is 10 seconds between the calls. 138 | * 139 | */ 140 | /*-----------------------------------------------------------------------------------*/ 141 | void 142 | uip_arp_timer(void) 143 | { 144 | struct arp_entry *tabptr; 145 | 146 | ++arptime; 147 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 148 | tabptr = &arp_table[i]; 149 | if((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 && 150 | arptime - tabptr->time >= UIP_ARP_MAXAGE) { 151 | memset(tabptr->ipaddr, 0, 4); 152 | } 153 | } 154 | 155 | } 156 | /*-----------------------------------------------------------------------------------*/ 157 | static void 158 | uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr) 159 | { 160 | register struct arp_entry *tabptr; 161 | /* Walk through the ARP mapping table and try to find an entry to 162 | update. If none is found, the IP -> MAC address mapping is 163 | inserted in the ARP table. */ 164 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 165 | 166 | tabptr = &arp_table[i]; 167 | /* Only check those entries that are actually in use. */ 168 | if(tabptr->ipaddr[0] != 0 && 169 | tabptr->ipaddr[1] != 0) { 170 | 171 | /* Check if the source IP address of the incoming packet matches 172 | the IP address in this ARP table entry. */ 173 | if(memcmp(ipaddr, tabptr->ipaddr, 4) == 0) { 174 | 175 | /* An old entry found, update this and return. */ 176 | memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); 177 | tabptr->time = arptime; 178 | 179 | return; 180 | } 181 | } 182 | } 183 | 184 | /* If we get here, no existing ARP table entry was found, so we 185 | create one. */ 186 | 187 | /* First, we try to find an unused entry in the ARP table. */ 188 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 189 | tabptr = &arp_table[i]; 190 | if(tabptr->ipaddr[0] == 0 && 191 | tabptr->ipaddr[1] == 0) { 192 | break; 193 | } 194 | } 195 | 196 | /* If no unused entry is found, we try to find the oldest entry and 197 | throw it away. */ 198 | if(i == UIP_ARPTAB_SIZE) { 199 | tmpage = 0; 200 | c = 0; 201 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 202 | tabptr = &arp_table[i]; 203 | if(arptime - tabptr->time > tmpage) { 204 | tmpage = arptime - tabptr->time; 205 | c = i; 206 | } 207 | } 208 | i = c; 209 | tabptr = &arp_table[i]; 210 | } 211 | 212 | /* Now, i is the ARP table entry which we will fill with the new 213 | information. */ 214 | memcpy(tabptr->ipaddr, ipaddr, 4); 215 | memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); 216 | tabptr->time = arptime; 217 | } 218 | /*-----------------------------------------------------------------------------------*/ 219 | /** 220 | * ARP processing for incoming IP packets 221 | * 222 | * This function should be called by the device driver when an IP 223 | * packet has been received. The function will check if the address is 224 | * in the ARP cache, and if so the ARP cache entry will be 225 | * refreshed. If no ARP cache entry was found, a new one is created. 226 | * 227 | * This function expects an IP packet with a prepended Ethernet header 228 | * in the uip_buf[] buffer, and the length of the packet in the global 229 | * variable uip_len. 230 | */ 231 | /*-----------------------------------------------------------------------------------*/ 232 | //#if 0 233 | void 234 | uip_arp_ipin(void) 235 | { 236 | uip_len -= sizeof(struct uip_eth_hdr); 237 | 238 | /* Only insert/update an entry if the source IP address of the 239 | incoming IP packet comes from a host on the local network. */ 240 | if((IPBUF->srcipaddr[0] & uip_netmask[0]) != 241 | (uip_hostaddr[0] & uip_netmask[0])) { 242 | return; 243 | } 244 | if((IPBUF->srcipaddr[1] & uip_netmask[1]) != 245 | (uip_hostaddr[1] & uip_netmask[1])) { 246 | return; 247 | } 248 | uip_arp_update(IPBUF->srcipaddr, &(IPBUF->ethhdr.src)); 249 | 250 | return; 251 | } 252 | //#endif /* 0 */ 253 | /*-----------------------------------------------------------------------------------*/ 254 | /** 255 | * ARP processing for incoming ARP packets. 256 | * 257 | * This function should be called by the device driver when an ARP 258 | * packet has been received. The function will act differently 259 | * depending on the ARP packet type: if it is a reply for a request 260 | * that we previously sent out, the ARP cache will be filled in with 261 | * the values from the ARP reply. If the incoming ARP packet is an ARP 262 | * request for our IP address, an ARP reply packet is created and put 263 | * into the uip_buf[] buffer. 264 | * 265 | * When the function returns, the value of the global variable uip_len 266 | * indicates whether the device driver should send out a packet or 267 | * not. If uip_len is zero, no packet should be sent. If uip_len is 268 | * non-zero, it contains the length of the outbound packet that is 269 | * present in the uip_buf[] buffer. 270 | * 271 | * This function expects an ARP packet with a prepended Ethernet 272 | * header in the uip_buf[] buffer, and the length of the packet in the 273 | * global variable uip_len. 274 | */ 275 | /*-----------------------------------------------------------------------------------*/ 276 | void 277 | uip_arp_arpin(void) 278 | { 279 | 280 | if(uip_len < sizeof(struct arp_hdr)) { 281 | uip_len = 0; 282 | return; 283 | } 284 | uip_len = 0; 285 | 286 | switch(BUF->opcode) { 287 | case HTONS(ARP_REQUEST): 288 | /* ARP request. If it asked for our address, we send out a 289 | reply. */ 290 | if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) { 291 | /* First, we register the one who made the request in our ARP 292 | table, since it is likely that we will do more communication 293 | with this host in the future. */ 294 | uip_arp_update(BUF->sipaddr, &BUF->shwaddr); 295 | 296 | /* The reply opcode is 2. */ 297 | BUF->opcode = HTONS(2); 298 | 299 | memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6); 300 | memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); 301 | memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); 302 | memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6); 303 | 304 | BUF->dipaddr[0] = BUF->sipaddr[0]; 305 | BUF->dipaddr[1] = BUF->sipaddr[1]; 306 | BUF->sipaddr[0] = uip_hostaddr[0]; 307 | BUF->sipaddr[1] = uip_hostaddr[1]; 308 | 309 | BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); 310 | uip_len = sizeof(struct arp_hdr); 311 | } 312 | break; 313 | case HTONS(ARP_REPLY): 314 | /* ARP reply. We insert or update the ARP table if it was meant 315 | for us. */ 316 | if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) { 317 | uip_arp_update(BUF->sipaddr, &BUF->shwaddr); 318 | } 319 | break; 320 | } 321 | 322 | return; 323 | } 324 | /*-----------------------------------------------------------------------------------*/ 325 | /** 326 | * Prepend Ethernet header to an outbound IP packet and see if we need 327 | * to send out an ARP request. 328 | * 329 | * This function should be called before sending out an IP packet. The 330 | * function checks the destination IP address of the IP packet to see 331 | * what Ethernet MAC address that should be used as a destination MAC 332 | * address on the Ethernet. 333 | * 334 | * If the destination IP address is in the local network (determined 335 | * by logical ANDing of netmask and our IP address), the function 336 | * checks the ARP cache to see if an entry for the destination IP 337 | * address is found. If so, an Ethernet header is prepended and the 338 | * function returns. If no ARP cache entry is found for the 339 | * destination IP address, the packet in the uip_buf[] is replaced by 340 | * an ARP request packet for the IP address. The IP packet is dropped 341 | * and it is assumed that they higher level protocols (e.g., TCP) 342 | * eventually will retransmit the dropped packet. 343 | * 344 | * If the destination IP address is not on the local network, the IP 345 | * address of the default router is used instead. 346 | * 347 | * When the function returns, a packet is present in the uip_buf[] 348 | * buffer, and the length of the packet is in the global variable 349 | * uip_len. 350 | */ 351 | /*-----------------------------------------------------------------------------------*/ 352 | void 353 | uip_arp_out(void) 354 | { 355 | struct arp_entry *tabptr; 356 | 357 | /* Find the destination IP address in the ARP table and construct 358 | the Ethernet header. If the destination IP addres isn't on the 359 | local network, we use the default router's IP address instead. 360 | 361 | If not ARP table entry is found, we overwrite the original IP 362 | packet with an ARP request for the IP address. */ 363 | 364 | /* First check if destination is a local broadcast. */ 365 | if(uip_ipaddr_cmp(IPBUF->destipaddr, broadcast_ipaddr)) { 366 | memcpy(IPBUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6); 367 | } else { 368 | /* Check if the destination address is on the local network. */ 369 | if(!uip_ipaddr_maskcmp(IPBUF->destipaddr, uip_hostaddr, uip_netmask)) { 370 | /* Destination address was not on the local network, so we need to 371 | use the default router's IP address instead of the destination 372 | address when determining the MAC address. */ 373 | uip_ipaddr_copy(ipaddr, uip_draddr); 374 | } else { 375 | /* Else, we use the destination IP address. */ 376 | uip_ipaddr_copy(ipaddr, IPBUF->destipaddr); 377 | } 378 | 379 | for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { 380 | tabptr = &arp_table[i]; 381 | if(uip_ipaddr_cmp(ipaddr, tabptr->ipaddr)) { 382 | break; 383 | } 384 | } 385 | 386 | if(i == UIP_ARPTAB_SIZE) { 387 | /* The destination address was not in our ARP table, so we 388 | overwrite the IP packet with an ARP request. */ 389 | 390 | memset(BUF->ethhdr.dest.addr, 0xff, 6); 391 | memset(BUF->dhwaddr.addr, 0x00, 6); 392 | memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6); 393 | memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6); 394 | 395 | uip_ipaddr_copy(BUF->dipaddr, ipaddr); 396 | uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr); 397 | BUF->opcode = HTONS(ARP_REQUEST); /* ARP request. */ 398 | BUF->hwtype = HTONS(ARP_HWTYPE_ETH); 399 | BUF->protocol = HTONS(UIP_ETHTYPE_IP); 400 | BUF->hwlen = 6; 401 | BUF->protolen = 4; 402 | BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP); 403 | 404 | uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN]; 405 | 406 | uip_len = sizeof(struct arp_hdr); 407 | return; 408 | } 409 | 410 | /* Build an ethernet header. */ 411 | memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6); 412 | } 413 | memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, 6); 414 | 415 | IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP); 416 | 417 | uip_len += sizeof(struct uip_eth_hdr); 418 | } 419 | /*-----------------------------------------------------------------------------------*/ 420 | 421 | /** @} */ 422 | /** @} */ 423 | -------------------------------------------------------------------------------- /src/Ethernet.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | UIPEthernet.cpp - Arduino implementation of a uIP wrapper class. 3 | Copyright (c) 2013 Norbert Truchsess 4 | All rights reserved. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include "Ethernet.h" 22 | #include "Dns.h" 23 | #include "utility/Enc28J60Network.h" 24 | 25 | extern "C" 26 | { 27 | #include "utility/uip-conf.h" 28 | #include "utility/uip.h" 29 | #include "utility/uip_arp.h" 30 | } 31 | 32 | #define ETH_HDR ((struct uip_eth_hdr *)&uip_buf[0]) 33 | 34 | bool UIPEthernetClass::initialized = false; 35 | memhandle UIPEthernetClass::in_packet(NOBLOCK); 36 | memhandle UIPEthernetClass::uip_packet(NOBLOCK); 37 | uint8_t UIPEthernetClass::uip_hdrlen(0); 38 | uint8_t UIPEthernetClass::packetstate(0); 39 | 40 | IPAddress UIPEthernetClass::_dnsServerAddress; 41 | DhcpClass* UIPEthernetClass::_dhcp(NULL); 42 | 43 | unsigned long UIPEthernetClass::periodic_timer; 44 | unsigned long UIPEthernetClass::arp_timer; 45 | 46 | // Because uIP isn't encapsulated within a class we have to use global 47 | // variables, so we can only have one TCP/IP stack per program. 48 | 49 | UIPEthernetClass::UIPEthernetClass() 50 | { 51 | } 52 | 53 | void UIPEthernetClass::init(uint8_t csPin) 54 | { 55 | Enc28J60Network::setCsPin(csPin); 56 | } 57 | 58 | #if UIP_UDP 59 | void 60 | UIPEthernetClass::setHostname(const char* hostname) 61 | { 62 | if (_dhcp == NULL) { 63 | _dhcp = new DhcpClass(); 64 | } 65 | _dhcp->setHostname(hostname); 66 | } 67 | 68 | int 69 | UIPEthernetClass::begin(const uint8_t* mac, unsigned long timeout, unsigned long responseTimeout) 70 | { 71 | if (_dhcp == NULL) { 72 | _dhcp = new DhcpClass(); 73 | } 74 | 75 | // Initialise the basic info 76 | init(mac); 77 | 78 | // Now try to get our config info from a DHCP server 79 | int ret = _dhcp->beginWithDHCP((uint8_t*)mac, timeout, responseTimeout); 80 | if(ret == 1) 81 | { 82 | // We've successfully found a DHCP server and got our configuration info, so set things 83 | // accordingly 84 | configure(_dhcp->getLocalIp(),_dhcp->getDnsServerIp(),_dhcp->getGatewayIp(),_dhcp->getSubnetMask()); 85 | } 86 | return ret; 87 | } 88 | #endif 89 | 90 | void 91 | UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip) 92 | { 93 | IPAddress dns = ip; 94 | dns[3] = 1; 95 | begin(mac, ip, dns); 96 | } 97 | 98 | void 99 | UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns) 100 | { 101 | IPAddress gateway = ip; 102 | gateway[3] = 1; 103 | begin(mac, ip, dns, gateway); 104 | } 105 | 106 | void 107 | UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway) 108 | { 109 | IPAddress subnet(255, 255, 255, 0); 110 | begin(mac, ip, dns, gateway, subnet); 111 | } 112 | 113 | void 114 | UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) 115 | { 116 | init(mac); 117 | configure(ip,dns,gateway,subnet); 118 | } 119 | 120 | void UIPEthernetClass::end() 121 | { 122 | //close all clients 123 | for (int i = 0; i < UIP_CONNS; i++) 124 | { 125 | if (EthernetClient::all_data[i].state) { 126 | EthernetClient client(&EthernetClient::all_data[i]); 127 | client.stop(); 128 | } 129 | } 130 | // handle clients closings 131 | uint32_t st = millis(); 132 | while (millis() - st < 3000) 133 | { 134 | tick(); 135 | } 136 | initialized = false; 137 | configure(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); 138 | } 139 | 140 | int UIPEthernetClass::maintain(){ 141 | if (!initialized) 142 | return 0; 143 | tick(); 144 | int rc = DHCP_CHECK_NONE; 145 | #if UIP_UDP 146 | if(_dhcp != NULL){ 147 | //we have a pointer to dhcp, use it 148 | rc = _dhcp->checkLease(); 149 | switch ( rc ){ 150 | case DHCP_CHECK_NONE: 151 | //nothing done 152 | break; 153 | case DHCP_CHECK_RENEW_OK: 154 | case DHCP_CHECK_REBIND_OK: 155 | //we might have got a new IP. 156 | configure(_dhcp->getLocalIp(),_dhcp->getDnsServerIp(),_dhcp->getGatewayIp(),_dhcp->getSubnetMask()); 157 | break; 158 | default: 159 | //this is actually a error, it will retry though 160 | break; 161 | } 162 | } 163 | #endif 164 | return rc; 165 | } 166 | 167 | EthernetLinkStatus UIPEthernetClass::linkStatus() 168 | { 169 | if (!Enc28J60Network::getrev()) 170 | return Unknown; 171 | return Enc28J60Network::linkStatus() ? LinkON : LinkOFF; 172 | } 173 | 174 | EthernetHardwareStatus UIPEthernetClass::hardwareStatus() 175 | { 176 | if (!Enc28J60Network::getrev()) 177 | return EthernetNoHardware; 178 | return EthernetENC28J60; 179 | } 180 | 181 | uint8_t* UIPEthernetClass::macAddress(uint8_t* mac) 182 | { 183 | memcpy(mac, uip_ethaddr.addr, 6); 184 | return mac; 185 | } 186 | 187 | IPAddress UIPEthernetClass::localIP() 188 | { 189 | IPAddress ret; 190 | uip_ipaddr_t a; 191 | uip_gethostaddr(a); 192 | return ip_addr_uip(a); 193 | } 194 | 195 | IPAddress UIPEthernetClass::subnetMask() 196 | { 197 | IPAddress ret; 198 | uip_ipaddr_t a; 199 | uip_getnetmask(a); 200 | return ip_addr_uip(a); 201 | } 202 | 203 | IPAddress UIPEthernetClass::gatewayIP() 204 | { 205 | IPAddress ret; 206 | uip_ipaddr_t a; 207 | uip_getdraddr(a); 208 | return ip_addr_uip(a); 209 | } 210 | 211 | IPAddress UIPEthernetClass::dnsServerIP() 212 | { 213 | return _dnsServerAddress; 214 | } 215 | 216 | IPAddress UIPEthernetClass::dnsIP(int n) { 217 | return (n == 0) ? _dnsServerAddress : IPAddress(); 218 | } 219 | 220 | int UIPEthernetClass::hostByName(const char* hostname, IPAddress& result) 221 | { 222 | // Look up the host first 223 | int ret = 0; 224 | #if UIP_UDP 225 | DNSClient dns; 226 | dns.begin(_dnsServerAddress); 227 | ret = dns.getHostByName(hostname, result); 228 | #endif 229 | return ret; 230 | } 231 | 232 | void 233 | UIPEthernetClass::tick() 234 | { 235 | if (!initialized) 236 | return; 237 | 238 | // run ARP table cleanup every 10 seconds as required by a comment in uip_arp.h 239 | if (millis() - arp_timer > 10000) { 240 | arp_timer = millis(); 241 | uip_arp_timer(); 242 | } 243 | 244 | if (in_packet == NOBLOCK) 245 | { 246 | in_packet = Enc28J60Network::receivePacket(); 247 | #ifdef UIPETHERNET_DEBUG 248 | if (in_packet != NOBLOCK) 249 | { 250 | Serial.println(F("--------------\npacket received")); 251 | } 252 | #endif 253 | } 254 | if (in_packet != NOBLOCK) 255 | { 256 | packetstate = UIPETHERNET_FREEPACKET; 257 | uip_len = Enc28J60Network::blockSize(in_packet); 258 | if (uip_len > 0) 259 | { 260 | Enc28J60Network::readPacket(in_packet,0,(uint8_t*)uip_buf,UIP_BUFSIZE); 261 | if (ETH_HDR ->type == HTONS(UIP_ETHTYPE_IP)) 262 | { 263 | uip_packet = in_packet; //required for upper_layer_checksum of in_packet! 264 | #ifdef UIPETHERNET_DEBUG 265 | Serial.print(F("readPacket type IP, uip_len: ")); 266 | Serial.print(uip_len); 267 | Serial.print(F(", bits: ")); 268 | Serial.println(BUF->flags, BIN); 269 | #endif 270 | uip_arp_ipin(); 271 | uip_input(); 272 | if (uip_len > 0) 273 | { 274 | uip_arp_out(); 275 | network_send(); 276 | } 277 | } 278 | else if (ETH_HDR ->type == HTONS(UIP_ETHTYPE_ARP)) 279 | { 280 | #ifdef UIPETHERNET_DEBUG 281 | Serial.print(F("readPacket type ARP, uip_len: ")); 282 | Serial.println(uip_len); 283 | #endif 284 | uip_arp_arpin(); 285 | if (uip_len > 0) 286 | { 287 | network_send(); 288 | } 289 | } 290 | } 291 | if (in_packet != NOBLOCK && (packetstate & UIPETHERNET_FREEPACKET)) 292 | { 293 | #ifdef UIPETHERNET_DEBUG 294 | Serial.println(F("freeing received packet")); 295 | #endif 296 | Enc28J60Network::freePacket(); 297 | in_packet = NOBLOCK; 298 | } 299 | } 300 | 301 | unsigned long now = millis(); 302 | 303 | if ((long)( now - periodic_timer ) >= 0) 304 | { 305 | periodic_timer = now + UIP_PERIODIC_TIMER; 306 | 307 | for (int i = 0; i < UIP_CONNS; i++) 308 | { 309 | uip_periodic(i); 310 | // If the above function invocation resulted in data that 311 | // should be sent out on the Enc28J60Network, the global variable 312 | // uip_len is set to a value > 0. 313 | if (uip_len > 0) 314 | { 315 | uip_arp_out(); 316 | network_send(); 317 | } 318 | } 319 | #if UIP_UDP 320 | for (int i = 0; i < UIP_UDP_CONNS; i++) 321 | { 322 | uip_udp_periodic(i); 323 | // If the above function invocation resulted in data that 324 | // should be sent out on the Enc28J60Network, the global variable 325 | // uip_len is set to a value > 0. */ 326 | if (uip_len > 0) 327 | { 328 | EthernetUDP::_send(&uip_udp_conns[i]); 329 | } 330 | } 331 | #endif /* UIP_UDP */ 332 | } 333 | } 334 | 335 | boolean UIPEthernetClass::network_send() 336 | { 337 | if (packetstate & UIPETHERNET_SENDPACKET) 338 | { 339 | #ifdef UIPETHERNET_DEBUG 340 | Serial.print(F("Enc28J60Network_send uip_packet: ")); 341 | Serial.print(uip_packet); 342 | Serial.print(F(", hdrlen: ")); 343 | Serial.println(uip_hdrlen); 344 | #endif 345 | Enc28J60Network::writePacket(uip_packet, UIP_SENDBUFFER_OFFSET,uip_buf,uip_hdrlen); 346 | packetstate &= ~ UIPETHERNET_SENDPACKET; 347 | goto sendandfree; 348 | } 349 | uip_packet = Enc28J60Network::allocBlock(uip_len + UIP_SENDBUFFER_OFFSET + UIP_SENDBUFFER_PADDING); 350 | if (uip_packet != NOBLOCK) 351 | { 352 | #ifdef UIPETHERNET_DEBUG 353 | Serial.print(F("Enc28J60Network_send uip_buf (uip_len): ")); 354 | Serial.print(uip_len); 355 | Serial.print(F(", packet: ")); 356 | Serial.print(uip_packet); 357 | Serial.print(F(", bits: ")); 358 | Serial.println(BUF->flags, BIN); 359 | #endif 360 | Enc28J60Network::writePacket(uip_packet, UIP_SENDBUFFER_OFFSET,uip_buf,uip_len); 361 | goto sendandfree; 362 | } 363 | return false; 364 | sendandfree: 365 | bool success = Enc28J60Network::sendPacket(uip_packet); 366 | Enc28J60Network::freeBlock(uip_packet); 367 | uip_packet = NOBLOCK; 368 | return success; 369 | } 370 | 371 | void UIPEthernetClass::init(const uint8_t* mac) { 372 | periodic_timer = millis() + UIP_PERIODIC_TIMER; 373 | 374 | initialized = Enc28J60Network::init((uint8_t*)mac); 375 | uip_seteth_addr(mac); 376 | 377 | uip_init(); 378 | uip_arp_init(); 379 | } 380 | 381 | void UIPEthernetClass::configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) { 382 | uip_ipaddr_t ipaddr; 383 | 384 | uip_ip_addr(ipaddr, ip); 385 | uip_sethostaddr(ipaddr); 386 | 387 | uip_ip_addr(ipaddr, gateway); 388 | uip_setdraddr(ipaddr); 389 | 390 | uip_ip_addr(ipaddr, subnet); 391 | uip_setnetmask(ipaddr); 392 | 393 | _dnsServerAddress = dns; 394 | } 395 | 396 | void 397 | UIPEthernetClass::call_yield() 398 | { 399 | static bool in_yield; 400 | static uint32_t last_call_millis; 401 | if (!in_yield && millis() - last_call_millis > 0) 402 | { 403 | last_call_millis = millis(); 404 | in_yield = true; 405 | yield(); 406 | in_yield = false; 407 | } 408 | } 409 | 410 | /*---------------------------------------------------------------------------*/ 411 | uint16_t 412 | UIPEthernetClass::chksum(uint16_t sum, const uint8_t *data, uint16_t len) 413 | { 414 | uint16_t t; 415 | const uint8_t *dataptr; 416 | const uint8_t *last_byte; 417 | 418 | dataptr = data; 419 | last_byte = data + len - 1; 420 | 421 | while(dataptr < last_byte) { /* At least two more bytes */ 422 | t = (dataptr[0] << 8) + dataptr[1]; 423 | sum += t; 424 | if(sum < t) { 425 | sum++; /* carry */ 426 | } 427 | dataptr += 2; 428 | } 429 | 430 | if(dataptr == last_byte) { 431 | t = (dataptr[0] << 8) + 0; 432 | sum += t; 433 | if(sum < t) { 434 | sum++; /* carry */ 435 | } 436 | } 437 | 438 | /* Return sum in host byte order. */ 439 | return sum; 440 | } 441 | 442 | /*---------------------------------------------------------------------------*/ 443 | 444 | uint16_t 445 | UIPEthernetClass::ipchksum(void) 446 | { 447 | uint16_t sum; 448 | 449 | sum = chksum(0, &uip_buf[UIP_LLH_LEN], UIP_IPH_LEN); 450 | return (sum == 0) ? 0xffff : htons(sum); 451 | } 452 | 453 | /*---------------------------------------------------------------------------*/ 454 | uint16_t 455 | #if UIP_UDP 456 | UIPEthernetClass::upper_layer_chksum(uint8_t proto) 457 | #else 458 | uip_tcpchksum(void) 459 | #endif 460 | { 461 | uint16_t upper_layer_len; 462 | uint16_t sum; 463 | 464 | #if UIP_CONF_IPV6 465 | upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]); 466 | #else /* UIP_CONF_IPV6 */ 467 | upper_layer_len = (((u16_t)(BUF->len[0]) << 8) + BUF->len[1]) - UIP_IPH_LEN; 468 | #endif /* UIP_CONF_IPV6 */ 469 | 470 | /* First sum pseudoheader. */ 471 | 472 | /* IP protocol and length fields. This addition cannot carry. */ 473 | #if UIP_UDP 474 | sum = upper_layer_len + proto; 475 | #else 476 | sum = upper_layer_len + UIP_PROTO_TCP; 477 | #endif 478 | /* Sum IP source and destination addresses. */ 479 | sum = UIPEthernetClass::chksum(sum, (u8_t *)&BUF->srcipaddr[0], 2 * sizeof(uip_ipaddr_t)); 480 | 481 | uint8_t upper_layer_memlen; 482 | #if UIP_UDP 483 | switch(proto) 484 | { 485 | // case UIP_PROTO_ICMP: 486 | // case UIP_PROTO_ICMP6: 487 | // upper_layer_memlen = upper_layer_len; 488 | // break; 489 | case UIP_PROTO_UDP: 490 | upper_layer_memlen = UIP_UDPH_LEN; 491 | break; 492 | default: 493 | // case UIP_PROTO_TCP: 494 | #endif 495 | upper_layer_memlen = (BUF->tcpoffset >> 4) << 2; 496 | #if UIP_UDP 497 | break; 498 | } 499 | #endif 500 | sum = UIPEthernetClass::chksum(sum, &uip_buf[UIP_IPH_LEN + UIP_LLH_LEN], upper_layer_memlen); 501 | #ifdef UIPETHERNET_DEBUG_CHKSUM 502 | Serial.print(F("chksum uip_buf[")); 503 | Serial.print(UIP_IPH_LEN + UIP_LLH_LEN); 504 | Serial.print(F("-")); 505 | Serial.print(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen); 506 | Serial.print(F("]: ")); 507 | Serial.println(htons(sum),HEX); 508 | #endif 509 | if (upper_layer_memlen < upper_layer_len) 510 | { 511 | sum = Enc28J60Network::chksum( 512 | sum, 513 | UIPEthernetClass::uip_packet, 514 | (UIPEthernetClass::packetstate & UIPETHERNET_SENDPACKET ? UIP_IPH_LEN + UIP_LLH_LEN + UIP_SENDBUFFER_OFFSET : UIP_IPH_LEN + UIP_LLH_LEN) + upper_layer_memlen, 515 | upper_layer_len - upper_layer_memlen 516 | ); 517 | #ifdef UIPETHERNET_DEBUG_CHKSUM 518 | Serial.print(F("chksum uip_packet(")); 519 | Serial.print(uip_packet); 520 | Serial.print(F(")[")); 521 | Serial.print(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen); 522 | Serial.print(F("-")); 523 | Serial.print(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_len); 524 | Serial.print(F("]: ")); 525 | Serial.println(htons(sum),HEX); 526 | #endif 527 | } 528 | return (sum == 0) ? 0xffff : htons(sum); 529 | } 530 | 531 | uint16_t 532 | uip_ipchksum(void) 533 | { 534 | return UIPEthernetClass::ipchksum(); 535 | } 536 | 537 | #if UIP_UDP 538 | uint16_t 539 | uip_tcpchksum(void) 540 | { 541 | uint16_t sum = UIPEthernetClass::upper_layer_chksum(UIP_PROTO_TCP); 542 | return sum; 543 | } 544 | 545 | uint16_t 546 | uip_udpchksum(void) 547 | { 548 | uint16_t sum = UIPEthernetClass::upper_layer_chksum(UIP_PROTO_UDP); 549 | return sum; 550 | } 551 | #endif 552 | 553 | UIPEthernetClass Ethernet; 554 | 555 | extern "C" void serialPrint(int i); 556 | void serialPrint(int i) { 557 | Serial.println(i); 558 | Serial.flush(); 559 | } 560 | -------------------------------------------------------------------------------- /src/utility/uipopt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \defgroup uipopt Configuration options for uIP 3 | * @{ 4 | * 5 | * uIP is configured using the per-project configuration file 6 | * uipopt.h. This file contains all compile-time options for uIP and 7 | * should be tweaked to match each specific project. The uIP 8 | * distribution contains a documented example "uipopt.h" that can be 9 | * copied and modified for each project. 10 | * 11 | * \note Most of the configuration options in the uipopt.h should not 12 | * be changed, but rather the per-project uip-conf.h file. 13 | */ 14 | 15 | /** 16 | * \file 17 | * Configuration options for uIP. 18 | * \author Adam Dunkels 19 | * 20 | * This file is used for tweaking various configuration options for 21 | * uIP. You should make a copy of this file into one of your project's 22 | * directories instead of editing this example "uipopt.h" file that 23 | * comes with the uIP distribution. 24 | */ 25 | 26 | /* 27 | * Copyright (c) 2001-2003, Adam Dunkels. 28 | * All rights reserved. 29 | * 30 | * Redistribution and use in source and binary forms, with or without 31 | * modification, are permitted provided that the following conditions 32 | * are met: 33 | * 1. Redistributions of source code must retain the above copyright 34 | * notice, this list of conditions and the following disclaimer. 35 | * 2. Redistributions in binary form must reproduce the above copyright 36 | * notice, this list of conditions and the following disclaimer in the 37 | * documentation and/or other materials provided with the distribution. 38 | * 3. The name of the author may not be used to endorse or promote 39 | * products derived from this software without specific prior 40 | * written permission. 41 | * 42 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 43 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 44 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 45 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 46 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 47 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 48 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 49 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 50 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 51 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 52 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | * 54 | * This file is part of the uIP TCP/IP stack. 55 | * 56 | * $Id: uipopt.h,v 1.4 2006/06/12 08:00:31 adam Exp $ 57 | * 58 | */ 59 | 60 | #ifndef __UIPOPT_H__ 61 | #define __UIPOPT_H__ 62 | 63 | #ifndef UIP_LITTLE_ENDIAN 64 | #if defined(LITTLE_ENDIAN) 65 | #define UIP_LITTLE_ENDIAN LITTLE_ENDIAN 66 | #elif defined(__ORDER_LITTLE_ENDIAN__) 67 | #define UIP_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ 68 | #else 69 | #define UIP_LITTLE_ENDIAN 1234 70 | #endif 71 | #endif /* UIP_LITTLE_ENDIAN */ 72 | #ifndef UIP_BIG_ENDIAN 73 | #if defined(BIG_ENDIAN) 74 | #define UIP_BIG_ENDIAN BIG_ENDIAN 75 | #elif defined(__ORDER_BIG_ENDIAN__) 76 | #define UIP_BIG_ENDIAN __ORDER_BIG_ENDIAN__ 77 | #else 78 | #define UIP_BIG_ENDIAN 4321 79 | #endif 80 | #endif /* UIP_BIG_ENDIAN */ 81 | 82 | #include "uip-conf.h" 83 | 84 | /*------------------------------------------------------------------------------*/ 85 | 86 | /** 87 | * \name Static configuration options 88 | * @{ 89 | * 90 | * These configuration options can be used for setting the IP address 91 | * settings statically, but only if UIP_FIXEDADDR is set to 1. The 92 | * configuration options for a specific node includes IP address, 93 | * netmask and default router as well as the Ethernet address. The 94 | * netmask, default router and Ethernet address are appliciable only 95 | * if uIP should be run over Ethernet. 96 | * 97 | * All of these should be changed to suit your project. 98 | */ 99 | 100 | /** 101 | * Determines if uIP should use a fixed IP address or not. 102 | * 103 | * If uIP should use a fixed IP address, the settings are set in the 104 | * uipopt.h file. If not, the macros uip_sethostaddr(), 105 | * uip_setdraddr() and uip_setnetmask() should be used instead. 106 | * 107 | * \hideinitializer 108 | */ 109 | #define UIP_FIXEDADDR 0 110 | 111 | /** 112 | * Ping IP address asignment. 113 | * 114 | * uIP uses a "ping" packets for setting its own IP address if this 115 | * option is set. If so, uIP will start with an empty IP address and 116 | * the destination IP address of the first incoming "ping" (ICMP echo) 117 | * packet will be used for setting the hosts IP address. 118 | * 119 | * \note This works only if UIP_FIXEDADDR is 0. 120 | * 121 | * \hideinitializer 122 | */ 123 | #ifdef UIP_CONF_PINGADDRCONF 124 | #define UIP_PINGADDRCONF UIP_CONF_PINGADDRCONF 125 | #else /* UIP_CONF_PINGADDRCONF */ 126 | #define UIP_PINGADDRCONF 0 127 | #endif /* UIP_CONF_PINGADDRCONF */ 128 | 129 | 130 | /** 131 | * Specifies if the uIP ARP module should be compiled with a fixed 132 | * Ethernet MAC address or not. 133 | * 134 | * If this configuration option is 0, the macro uip_setethaddr() can 135 | * be used to specify the Ethernet address at run-time. 136 | * 137 | * \hideinitializer 138 | */ 139 | #define UIP_FIXEDETHADDR 0 140 | 141 | /** @} */ 142 | /*------------------------------------------------------------------------------*/ 143 | /** 144 | * \name IP configuration options 145 | * @{ 146 | * 147 | */ 148 | /** 149 | * The IP TTL (time to live) of IP packets sent by uIP. 150 | * 151 | * This should normally not be changed. 152 | */ 153 | #define UIP_TTL 64 154 | 155 | /** 156 | * Turn on support for IP packet reassembly. 157 | * 158 | * uIP supports reassembly of fragmented IP packets. This features 159 | * requires an additonal amount of RAM to hold the reassembly buffer 160 | * and the reassembly code size is approximately 700 bytes. The 161 | * reassembly buffer is of the same size as the uip_buf buffer 162 | * (configured by UIP_BUFSIZE). 163 | * 164 | * \note IP packet reassembly is not heavily tested. 165 | * 166 | * \hideinitializer 167 | */ 168 | #define UIP_REASSEMBLY 0 169 | 170 | /** 171 | * The maximum time an IP fragment should wait in the reassembly 172 | * buffer before it is dropped. 173 | * 174 | */ 175 | #define UIP_REASS_MAXAGE 40 176 | 177 | /** @} */ 178 | 179 | /*------------------------------------------------------------------------------*/ 180 | /** 181 | * \name UDP configuration options 182 | * @{ 183 | */ 184 | 185 | /** 186 | * Toggles wether UDP support should be compiled in or not. 187 | * 188 | * \hideinitializer 189 | */ 190 | #ifdef UIP_CONF_UDP 191 | #define UIP_UDP UIP_CONF_UDP 192 | #else /* UIP_CONF_UDP */ 193 | #define UIP_UDP 0 194 | #endif /* UIP_CONF_UDP */ 195 | 196 | /** 197 | * Toggles if UDP checksums should be used or not. 198 | * 199 | * \note Support for UDP checksums is currently not included in uIP, 200 | * so this option has no function. 201 | * 202 | * \hideinitializer 203 | */ 204 | #ifdef UIP_CONF_UDP_CHECKSUMS 205 | #define UIP_UDP_CHECKSUMS UIP_CONF_UDP_CHECKSUMS 206 | #else 207 | #define UIP_UDP_CHECKSUMS 0 208 | #endif 209 | 210 | /** 211 | * The maximum amount of concurrent UDP connections. 212 | * 213 | * \hideinitializer 214 | */ 215 | #ifdef UIP_CONF_UDP_CONNS 216 | #define UIP_UDP_CONNS UIP_CONF_UDP_CONNS 217 | #else /* UIP_CONF_UDP_CONNS */ 218 | #define UIP_UDP_CONNS 10 219 | #endif /* UIP_CONF_UDP_CONNS */ 220 | 221 | /** 222 | * The name of the function that should be called when UDP datagrams arrive. 223 | * 224 | * \hideinitializer 225 | */ 226 | 227 | 228 | /** @} */ 229 | /*------------------------------------------------------------------------------*/ 230 | /** 231 | * \name TCP configuration options 232 | * @{ 233 | */ 234 | 235 | /** 236 | * Determines if support for opening connections from uIP should be 237 | * compiled in. 238 | * 239 | * If the applications that are running on top of uIP for this project 240 | * do not need to open outgoing TCP connections, this configration 241 | * option can be turned off to reduce the code size of uIP. 242 | * 243 | * \hideinitializer 244 | */ 245 | #define UIP_ACTIVE_OPEN 1 246 | 247 | /** 248 | * The maximum number of simultaneously open TCP connections. 249 | * 250 | * Since the TCP connections are statically allocated, turning this 251 | * configuration knob down results in less RAM used. Each TCP 252 | * connection requires approximatly 30 bytes of memory. 253 | * 254 | * \hideinitializer 255 | */ 256 | #ifndef UIP_CONF_MAX_CONNECTIONS 257 | #define UIP_CONNS 10 258 | #else /* UIP_CONF_MAX_CONNECTIONS */ 259 | #define UIP_CONNS UIP_CONF_MAX_CONNECTIONS 260 | #endif /* UIP_CONF_MAX_CONNECTIONS */ 261 | 262 | 263 | /** 264 | * The maximum number of simultaneously listening TCP ports. 265 | * 266 | * Each listening TCP port requires 2 bytes of memory. 267 | * 268 | * \hideinitializer 269 | */ 270 | #ifndef UIP_CONF_MAX_LISTENPORTS 271 | #define UIP_LISTENPORTS 20 272 | #else /* UIP_CONF_MAX_LISTENPORTS */ 273 | #define UIP_LISTENPORTS UIP_CONF_MAX_LISTENPORTS 274 | #endif /* UIP_CONF_MAX_LISTENPORTS */ 275 | 276 | /** 277 | * Determines if support for TCP urgent data notification should be 278 | * compiled in. 279 | * 280 | * Urgent data (out-of-band data) is a rarely used TCP feature that 281 | * very seldom would be required. 282 | * 283 | * \hideinitializer 284 | */ 285 | #define UIP_URGDATA 0 286 | 287 | /** 288 | * The initial retransmission timeout counted in timer pulses. 289 | * 290 | * This should not be changed. 291 | */ 292 | #define UIP_RTO 3 293 | 294 | /** 295 | * The maximum number of times a segment should be retransmitted 296 | * before the connection should be aborted. 297 | * 298 | * This should not be changed. 299 | */ 300 | #define UIP_MAXRTX 8 301 | 302 | /** 303 | * The maximum number of times a SYN segment should be retransmitted 304 | * before a connection request should be deemed to have been 305 | * unsuccessful. 306 | * 307 | * This should not need to be changed. 308 | */ 309 | #define UIP_MAXSYNRTX 5 310 | 311 | /** 312 | * The TCP maximum segment size. 313 | * 314 | * This is should not be to set to more than 315 | * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. 316 | */ 317 | #ifndef UIP_CONF_TCP_MSS 318 | #define UIP_TCP_MSS (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) 319 | #else 320 | #define UIP_TCP_MSS UIP_CONF_TCP_MSS 321 | #endif 322 | 323 | /** 324 | * The size of the advertised receiver's window. 325 | * 326 | * Should be set low (i.e., to the size of the uip_buf buffer) is the 327 | * application is slow to process incoming data, or high (32768 bytes) 328 | * if the application processes data quickly. 329 | * 330 | * \hideinitializer 331 | */ 332 | #ifndef UIP_CONF_RECEIVE_WINDOW 333 | #define UIP_RECEIVE_WINDOW UIP_TCP_MSS 334 | #else 335 | #define UIP_RECEIVE_WINDOW UIP_CONF_RECEIVE_WINDOW 336 | #endif 337 | 338 | /** 339 | * How long a connection should stay in the TIME_WAIT state. 340 | * 341 | * This configiration option has no real implication, and it should be 342 | * left untouched. 343 | */ 344 | #define UIP_TIME_WAIT_TIMEOUT 120 345 | 346 | 347 | /** @} */ 348 | /*------------------------------------------------------------------------------*/ 349 | /** 350 | * \name ARP configuration options 351 | * @{ 352 | */ 353 | 354 | /** 355 | * The size of the ARP table. 356 | * 357 | * This option should be set to a larger value if this uIP node will 358 | * have many connections from the local network. 359 | * 360 | * \hideinitializer 361 | */ 362 | #ifdef UIP_CONF_ARPTAB_SIZE 363 | #define UIP_ARPTAB_SIZE UIP_CONF_ARPTAB_SIZE 364 | #else 365 | #define UIP_ARPTAB_SIZE 8 366 | #endif 367 | 368 | /** 369 | * The maxium age of ARP table entries measured in 10ths of seconds. 370 | * 371 | * An UIP_ARP_MAXAGE of 120 corresponds to 20 minutes (BSD 372 | * default). 373 | */ 374 | #define UIP_ARP_MAXAGE 120 375 | 376 | /** @} */ 377 | 378 | /*------------------------------------------------------------------------------*/ 379 | 380 | /** 381 | * \name General configuration options 382 | * @{ 383 | */ 384 | 385 | /** 386 | * The size of the uIP packet buffer. 387 | * 388 | * The uIP packet buffer should not be smaller than 60 bytes, and does 389 | * not need to be larger than 1500 bytes. Lower size results in lower 390 | * TCP throughput, larger size results in higher TCP throughput. 391 | * 392 | * \hideinitializer 393 | */ 394 | #ifndef UIP_CONF_BUFFER_SIZE 395 | #define UIP_BUFSIZE 400 396 | #else /* UIP_CONF_BUFFER_SIZE */ 397 | #define UIP_BUFSIZE UIP_CONF_BUFFER_SIZE 398 | #endif /* UIP_CONF_BUFFER_SIZE */ 399 | 400 | 401 | /** 402 | * Determines if statistics support should be compiled in. 403 | * 404 | * The statistics is useful for debugging and to show the user. 405 | * 406 | * \hideinitializer 407 | */ 408 | #ifndef UIP_CONF_STATISTICS 409 | #define UIP_STATISTICS 0 410 | #else /* UIP_CONF_STATISTICS */ 411 | #define UIP_STATISTICS UIP_CONF_STATISTICS 412 | #endif /* UIP_CONF_STATISTICS */ 413 | 414 | /** 415 | * Determines if logging of certain events should be compiled in. 416 | * 417 | * This is useful mostly for debugging. The function uip_log() 418 | * must be implemented to suit the architecture of the project, if 419 | * logging is turned on. 420 | * 421 | * \hideinitializer 422 | */ 423 | #ifndef UIP_CONF_LOGGING 424 | #define UIP_LOGGING 0 425 | #else /* UIP_CONF_LOGGING */ 426 | #define UIP_LOGGING UIP_CONF_LOGGING 427 | #endif /* UIP_CONF_LOGGING */ 428 | 429 | /** 430 | * Broadcast support. 431 | * 432 | * This flag configures IP broadcast support. This is useful only 433 | * together with UDP. 434 | * 435 | * \hideinitializer 436 | * 437 | */ 438 | #if UIP_UDP && UIP_CONF_BROADCAST 439 | #define UIP_BROADCAST UIP_CONF_BROADCAST 440 | #else /* UIP_CONF_BROADCAST */ 441 | #define UIP_BROADCAST 0 442 | #endif /* UIP_CONF_BROADCAST */ 443 | 444 | /** 445 | * Print out a uIP log message. 446 | * 447 | * This function must be implemented by the module that uses uIP, and 448 | * is called by uIP whenever a log message is generated. 449 | */ 450 | void uip_log(char *msg); 451 | 452 | /** 453 | * The link level header length. 454 | * 455 | * This is the offset into the uip_buf where the IP header can be 456 | * found. For Ethernet, this should be set to 14. For SLIP, this 457 | * should be set to 0. 458 | * 459 | * \hideinitializer 460 | */ 461 | #ifdef UIP_CONF_LLH_LEN 462 | #define UIP_LLH_LEN UIP_CONF_LLH_LEN 463 | #else /* UIP_CONF_LLH_LEN */ 464 | #define UIP_LLH_LEN 14 465 | #endif /* UIP_CONF_LLH_LEN */ 466 | 467 | /** @} */ 468 | /*------------------------------------------------------------------------------*/ 469 | /** 470 | * \name CPU architecture configuration 471 | * @{ 472 | * 473 | * The CPU architecture configuration is where the endianess of the 474 | * CPU on which uIP is to be run is specified. Most CPUs today are 475 | * little endian, and the most notable exception are the Motorolas 476 | * which are big endian. The BYTE_ORDER macro should be changed to 477 | * reflect the CPU architecture on which uIP is to be run. 478 | */ 479 | 480 | /** 481 | * The byte order of the CPU architecture on which uIP is to be run. 482 | * 483 | * This option can be either BIG_ENDIAN (Motorola byte order) or 484 | * LITTLE_ENDIAN (Intel byte order). 485 | * 486 | * \hideinitializer 487 | */ 488 | #ifdef UIP_CONF_BYTE_ORDER 489 | #define UIP_BYTE_ORDER UIP_CONF_BYTE_ORDER 490 | #else /* UIP_CONF_BYTE_ORDER */ 491 | #define UIP_BYTE_ORDER UIP_LITTLE_ENDIAN 492 | #endif /* UIP_CONF_BYTE_ORDER */ 493 | 494 | /** @} */ 495 | /*------------------------------------------------------------------------------*/ 496 | 497 | /** 498 | * \name Appication specific configurations 499 | * @{ 500 | * 501 | * An uIP application is implemented using a single application 502 | * function that is called by uIP whenever a TCP/IP event occurs. The 503 | * name of this function must be registered with uIP at compile time 504 | * using the UIP_APPCALL definition. 505 | * 506 | * uIP applications can store the application state within the 507 | * uip_conn structure by specifying the type of the application 508 | * structure by typedef:ing the type uip_tcp_appstate_t and uip_udp_appstate_t. 509 | * 510 | * The file containing the definitions must be included in the 511 | * uipopt.h file. 512 | * 513 | * The following example illustrates how this can look. 514 | \code 515 | 516 | void httpd_appcall(void); 517 | #define UIP_APPCALL httpd_appcall 518 | 519 | struct httpd_state { 520 | u8_t state; 521 | u16_t count; 522 | char *dataptr; 523 | char *script; 524 | }; 525 | typedef struct httpd_state uip_tcp_appstate_t 526 | \endcode 527 | */ 528 | 529 | /** 530 | * \var #define UIP_APPCALL 531 | * 532 | * The name of the application function that uIP should call in 533 | * response to TCP/IP events. 534 | * 535 | */ 536 | 537 | /** 538 | * \var typedef uip_tcp_appstate_t 539 | * 540 | * The type of the application state that is to be stored in the 541 | * uip_conn structure. This usually is typedef:ed to a struct holding 542 | * application state information. 543 | */ 544 | 545 | /** 546 | * \var typedef uip_udp_appstate_t 547 | * 548 | * The type of the application state that is to be stored in the 549 | * uip_conn structure. This usually is typedef:ed to a struct holding 550 | * application state information. 551 | */ 552 | /** @} */ 553 | /** @} */ 554 | 555 | #endif /* __UIPOPT_H__ */ 556 | -------------------------------------------------------------------------------- /src/Dhcp.cpp: -------------------------------------------------------------------------------- 1 | // DHCP Library v0.3 - April 25, 2009 2 | // Author: Jordan Terrell - blog.jordanterrell.com 3 | 4 | #include "Dhcp.h" 5 | #include 6 | #include "utility/util.h" 7 | 8 | int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) 9 | { 10 | _dhcpLeaseTime=0; 11 | _dhcpT1=0; 12 | _dhcpT2=0; 13 | _lastCheck=0; 14 | _timeout = timeout; 15 | _responseTimeout = responseTimeout; 16 | 17 | // zero out _dhcpMacAddr 18 | memset(_dhcpMacAddr, 0, 6); 19 | reset_DHCP_lease(); 20 | 21 | memcpy((void*)_dhcpMacAddr, (void*)mac, 6); 22 | _dhcp_state = STATE_DHCP_START; 23 | return request_DHCP_lease(); 24 | } 25 | 26 | void DhcpClass::reset_DHCP_lease(){ 27 | memset(_dhcpLocalIp, 0, 4); 28 | memset(_dhcpSubnetMask, 0, 4); 29 | memset(_dhcpGatewayIp, 0, 4); 30 | memset(_dhcpDhcpServerIp, 0, 4); 31 | memset(_dhcpDnsServerIp, 0, 4); 32 | } 33 | 34 | //return:0 on error, 1 if request is sent and response is received 35 | int DhcpClass::request_DHCP_lease(){ 36 | 37 | uint8_t messageType = 0; 38 | 39 | 40 | 41 | // Pick an initial transaction ID 42 | _dhcpTransactionId = random(1UL, 2000UL); 43 | _dhcpInitialTransactionId = _dhcpTransactionId; 44 | 45 | _dhcpUdpSocket.stop(); 46 | if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) 47 | { 48 | // Couldn't get a socket 49 | return 0; 50 | } 51 | 52 | presend_DHCP(); 53 | 54 | int result = 0; 55 | 56 | unsigned long startTime = millis(); 57 | 58 | while(_dhcp_state != STATE_DHCP_LEASED) 59 | { 60 | if(_dhcp_state == STATE_DHCP_START) 61 | { 62 | _dhcpTransactionId++; 63 | 64 | send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000)); 65 | _dhcp_state = STATE_DHCP_DISCOVER; 66 | } 67 | else if(_dhcp_state == STATE_DHCP_REREQUEST){ 68 | _dhcpTransactionId++; 69 | send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime)/1000)); 70 | _dhcp_state = STATE_DHCP_REQUEST; 71 | } 72 | else if(_dhcp_state == STATE_DHCP_DISCOVER) 73 | { 74 | uint32_t respId; 75 | messageType = parseDHCPResponse(_responseTimeout, respId); 76 | if(messageType == DHCP_OFFER) 77 | { 78 | // We'll use the transaction ID that the offer came with, 79 | // rather than the one we were up to 80 | _dhcpTransactionId = respId; 81 | send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000)); 82 | _dhcp_state = STATE_DHCP_REQUEST; 83 | } 84 | } 85 | else if(_dhcp_state == STATE_DHCP_REQUEST) 86 | { 87 | uint32_t respId; 88 | messageType = parseDHCPResponse(_responseTimeout, respId); 89 | if(messageType == DHCP_ACK) 90 | { 91 | _dhcp_state = STATE_DHCP_LEASED; 92 | result = 1; 93 | //use default lease time if we didn't get it 94 | if(_dhcpLeaseTime == 0){ 95 | _dhcpLeaseTime = DEFAULT_LEASE; 96 | } 97 | //calculate T1 & T2 if we didn't get it 98 | if(_dhcpT1 == 0){ 99 | //T1 should be 50% of _dhcpLeaseTime 100 | _dhcpT1 = _dhcpLeaseTime >> 1; 101 | } 102 | if(_dhcpT2 == 0){ 103 | //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime 104 | _dhcpT2 = _dhcpT1 << 1; 105 | } 106 | _renewInSec = _dhcpT1; 107 | _rebindInSec = _dhcpT2; 108 | } 109 | else if(messageType == DHCP_NAK) 110 | _dhcp_state = STATE_DHCP_START; 111 | } 112 | 113 | if(messageType == 255) 114 | { 115 | messageType = 0; 116 | _dhcp_state = STATE_DHCP_START; 117 | } 118 | 119 | if(result != 1 && ((millis() - startTime) > _timeout)) 120 | break; 121 | } 122 | 123 | // We're done with the socket now 124 | _dhcpUdpSocket.stop(); 125 | _dhcpTransactionId++; 126 | 127 | return result; 128 | } 129 | 130 | void DhcpClass::presend_DHCP() 131 | { 132 | } 133 | 134 | void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed) 135 | { 136 | uint8_t buffer[32]; 137 | memset(buffer, 0, 32); 138 | IPAddress dest_addr( 255, 255, 255, 255 ); // Broadcast address 139 | 140 | if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT)) 141 | { 142 | // FIXME Need to return errors 143 | return; 144 | } 145 | 146 | buffer[0] = DHCP_BOOTREQUEST; // op 147 | buffer[1] = DHCP_HTYPE10MB; // htype 148 | buffer[2] = DHCP_HLENETHERNET; // hlen 149 | buffer[3] = DHCP_HOPS; // hops 150 | 151 | // xid 152 | unsigned long xid = htonl(_dhcpTransactionId); 153 | memcpy(buffer + 4, &(xid), 4); 154 | 155 | // 8, 9 - seconds elapsed 156 | buffer[8] = ((secondsElapsed & 0xff00) >> 8); 157 | buffer[9] = (secondsElapsed & 0x00ff); 158 | 159 | // flags - the only one flag DHCP uses is 160 | // to request the server response as broadcast, not unicast 161 | // unsigned short flags = htons(DHCP_FLAGSBROADCAST); 162 | // memcpy(buffer + 10, &(flags), 2); 163 | 164 | // ciaddr: already zeroed 165 | // yiaddr: already zeroed 166 | // siaddr: already zeroed 167 | // giaddr: already zeroed 168 | 169 | //put data in W5100 transmit buffer 170 | _dhcpUdpSocket.write(buffer, 28); 171 | 172 | memset(buffer, 0, 32); // clear local buffer 173 | 174 | memcpy(buffer, _dhcpMacAddr, 6); // chaddr 175 | 176 | //put data in W5100 transmit buffer 177 | _dhcpUdpSocket.write(buffer, 16); 178 | 179 | memset(buffer, 0, 32); // clear local buffer 180 | 181 | // leave zeroed out for sname && file 182 | // put in W5100 transmit buffer x 6 (192 bytes) 183 | 184 | for(int i = 0; i < 6; i++) { 185 | _dhcpUdpSocket.write(buffer, 32); 186 | } 187 | 188 | // OPT - Magic Cookie 189 | buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24)& 0xFF); 190 | buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16)& 0xFF); 191 | buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8)& 0xFF); 192 | buffer[3] = (uint8_t)(MAGIC_COOKIE& 0xFF); 193 | 194 | // OPT - message type 195 | buffer[4] = dhcpMessageType; 196 | buffer[5] = 0x01; 197 | buffer[6] = messageType; //DHCP_REQUEST; 198 | 199 | // OPT - client identifier 200 | buffer[7] = dhcpClientIdentifier; 201 | buffer[8] = 0x07; 202 | buffer[9] = 0x01; 203 | memcpy(buffer + 10, _dhcpMacAddr, 6); 204 | 205 | // OPT - host name 206 | buffer[16] = hostName; 207 | if (_hostname == nullptr) { 208 | buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address 209 | strcpy((char*)&(buffer[18]), HOST_NAME); 210 | 211 | printByte((char*)&(buffer[24]), _dhcpMacAddr[3]); 212 | printByte((char*)&(buffer[26]), _dhcpMacAddr[4]); 213 | printByte((char*)&(buffer[28]), _dhcpMacAddr[5]); 214 | 215 | //put data in W5100 transmit buffer 216 | _dhcpUdpSocket.write(buffer, 30); 217 | } else { 218 | uint8_t len = strlen(_hostname); 219 | buffer[17] = len; 220 | _dhcpUdpSocket.write(buffer, 18); 221 | _dhcpUdpSocket.write(_hostname, len); 222 | } 223 | 224 | if(messageType == DHCP_REQUEST) 225 | { 226 | buffer[0] = dhcpRequestedIPaddr; 227 | buffer[1] = 0x04; 228 | buffer[2] = _dhcpLocalIp[0]; 229 | buffer[3] = _dhcpLocalIp[1]; 230 | buffer[4] = _dhcpLocalIp[2]; 231 | buffer[5] = _dhcpLocalIp[3]; 232 | 233 | buffer[6] = dhcpServerIdentifier; 234 | buffer[7] = 0x04; 235 | buffer[8] = _dhcpDhcpServerIp[0]; 236 | buffer[9] = _dhcpDhcpServerIp[1]; 237 | buffer[10] = _dhcpDhcpServerIp[2]; 238 | buffer[11] = _dhcpDhcpServerIp[3]; 239 | 240 | //put data in W5100 transmit buffer 241 | _dhcpUdpSocket.write(buffer, 12); 242 | } 243 | 244 | buffer[0] = dhcpParamRequest; 245 | buffer[1] = 0x06; 246 | buffer[2] = subnetMask; 247 | buffer[3] = routersOnSubnet; 248 | buffer[4] = dns; 249 | buffer[5] = domainName; 250 | buffer[6] = dhcpT1value; 251 | buffer[7] = dhcpT2value; 252 | buffer[8] = endOption; 253 | 254 | //put data in W5100 transmit buffer 255 | _dhcpUdpSocket.write(buffer, 9); 256 | 257 | _dhcpUdpSocket.endPacket(); 258 | } 259 | 260 | uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId) 261 | { 262 | uint8_t type = 0; 263 | uint8_t opt_len = 0; 264 | 265 | unsigned long startTime = millis(); 266 | 267 | while(_dhcpUdpSocket.parsePacket() <= 0) 268 | { 269 | if((millis() - startTime) > responseTimeout) 270 | { 271 | return 255; 272 | } 273 | delay(50); 274 | } 275 | // start reading in the packet 276 | RIP_MSG_FIXED fixedMsg; 277 | _dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED)); 278 | 279 | if(fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) 280 | { 281 | transactionId = ntohl(fixedMsg.xid); 282 | if(memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || (transactionId < _dhcpInitialTransactionId) || (transactionId > _dhcpTransactionId)) 283 | { 284 | // Need to read the rest of the packet here regardless 285 | _dhcpUdpSocket.discardReceived(); 286 | return 0; 287 | } 288 | 289 | memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4); 290 | 291 | // Skip to the option part 292 | // Doing this a byte at a time so we don't have to put a big buffer 293 | // on the stack (as we don't have lots of memory lying around) 294 | for (int i =0; i < (240 - (int)sizeof(RIP_MSG_FIXED)); i++) 295 | { 296 | _dhcpUdpSocket.read(); // we don't care about the returned byte 297 | } 298 | 299 | while (_dhcpUdpSocket.available() > 0) 300 | { 301 | switch (_dhcpUdpSocket.read()) 302 | { 303 | case endOption : 304 | break; 305 | 306 | case padOption : 307 | break; 308 | 309 | case dhcpMessageType : 310 | opt_len = _dhcpUdpSocket.read(); 311 | type = _dhcpUdpSocket.read(); 312 | break; 313 | 314 | case subnetMask : 315 | opt_len = _dhcpUdpSocket.read(); 316 | _dhcpUdpSocket.read(_dhcpSubnetMask, 4); 317 | break; 318 | 319 | case routersOnSubnet : 320 | opt_len = _dhcpUdpSocket.read(); 321 | _dhcpUdpSocket.read(_dhcpGatewayIp, 4); 322 | for (int i = 0; i < opt_len-4; i++) 323 | { 324 | _dhcpUdpSocket.read(); 325 | } 326 | break; 327 | 328 | case dns : 329 | opt_len = _dhcpUdpSocket.read(); 330 | _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); 331 | for (int i = 0; i < opt_len-4; i++) 332 | { 333 | _dhcpUdpSocket.read(); 334 | } 335 | break; 336 | 337 | case dhcpServerIdentifier : 338 | opt_len = _dhcpUdpSocket.read(); 339 | if( _dhcpDhcpServerIp[0] == 0 || 340 | IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP() ) 341 | { 342 | _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp)); 343 | } 344 | else 345 | { 346 | // Skip over the rest of this option 347 | while (opt_len--) 348 | { 349 | _dhcpUdpSocket.read(); 350 | } 351 | } 352 | break; 353 | 354 | case dhcpT1value : 355 | opt_len = _dhcpUdpSocket.read(); 356 | _dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1)); 357 | _dhcpT1 = ntohl(_dhcpT1); 358 | break; 359 | 360 | case dhcpT2value : 361 | opt_len = _dhcpUdpSocket.read(); 362 | _dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2)); 363 | _dhcpT2 = ntohl(_dhcpT2); 364 | break; 365 | 366 | case dhcpIPaddrLeaseTime : 367 | opt_len = _dhcpUdpSocket.read(); 368 | _dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime)); 369 | _dhcpLeaseTime = ntohl(_dhcpLeaseTime); 370 | _renewInSec = _dhcpLeaseTime; 371 | break; 372 | 373 | default : 374 | opt_len = _dhcpUdpSocket.read(); 375 | // Skip over the rest of this option 376 | while (opt_len--) 377 | { 378 | _dhcpUdpSocket.read(); 379 | } 380 | break; 381 | } 382 | } 383 | } 384 | 385 | // Need to skip to end of the packet regardless here 386 | _dhcpUdpSocket.discardReceived(); 387 | 388 | return type; 389 | } 390 | 391 | 392 | /* 393 | returns: 394 | 0/DHCP_CHECK_NONE: nothing happened 395 | 1/DHCP_CHECK_RENEW_FAIL: renew failed 396 | 2/DHCP_CHECK_RENEW_OK: renew success 397 | 3/DHCP_CHECK_REBIND_FAIL: rebind fail 398 | 4/DHCP_CHECK_REBIND_OK: rebind success 399 | */ 400 | int DhcpClass::checkLease(){ 401 | //this uses a signed / unsigned trick to deal with millis overflow 402 | unsigned long now = millis(); 403 | signed long snow = (long)now; 404 | int rc=DHCP_CHECK_NONE; 405 | if (_lastCheck != 0){ 406 | signed long factor; 407 | //calc how many ms past the timeout we are 408 | factor = snow - (long)_secTimeout; 409 | //if on or passed the timeout, reduce the counters 410 | if ( factor >= 0 ){ 411 | //next timeout should be now plus 1000 ms minus parts of second in factor 412 | _secTimeout = snow + 1000 - factor % 1000; 413 | //how many seconds late are we, minimum 1 414 | factor = factor / 1000 +1; 415 | 416 | //reduce the counters by that mouch 417 | //if we can assume that the cycle time (factor) is fairly constant 418 | //and if the remainder is less than cycle time * 2 419 | //do it early instead of late 420 | if(_renewInSec < factor*2 ) 421 | _renewInSec = 0; 422 | else 423 | _renewInSec -= factor; 424 | 425 | if(_rebindInSec < factor*2 ) 426 | _rebindInSec = 0; 427 | else 428 | _rebindInSec -= factor; 429 | } 430 | 431 | //if we have a lease but should renew, do it 432 | if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <=0){ 433 | _dhcp_state = STATE_DHCP_REREQUEST; 434 | rc = 1 + request_DHCP_lease(); 435 | } 436 | 437 | //if we have a lease or is renewing but should bind, do it 438 | if( (_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <=0){ 439 | //this should basically restart completely 440 | _dhcp_state = STATE_DHCP_START; 441 | reset_DHCP_lease(); 442 | rc = 3 + request_DHCP_lease(); 443 | } 444 | } 445 | else{ 446 | _secTimeout = snow + 1000; 447 | } 448 | 449 | _lastCheck = now; 450 | return rc; 451 | } 452 | 453 | IPAddress DhcpClass::getLocalIp() 454 | { 455 | return IPAddress(_dhcpLocalIp); 456 | } 457 | 458 | IPAddress DhcpClass::getSubnetMask() 459 | { 460 | return IPAddress(_dhcpSubnetMask); 461 | } 462 | 463 | IPAddress DhcpClass::getGatewayIp() 464 | { 465 | return IPAddress(_dhcpGatewayIp); 466 | } 467 | 468 | IPAddress DhcpClass::getDhcpServerIp() 469 | { 470 | return IPAddress(_dhcpDhcpServerIp); 471 | } 472 | 473 | IPAddress DhcpClass::getDnsServerIp() 474 | { 475 | return IPAddress(_dhcpDnsServerIp); 476 | } 477 | 478 | void DhcpClass::setHostname(const char* hostname) { 479 | _hostname = hostname; 480 | } 481 | 482 | void DhcpClass::printByte(char * buf, uint8_t n ) { 483 | char *str = &buf[1]; 484 | buf[0]='0'; 485 | do { 486 | unsigned long m = n; 487 | n /= 16; 488 | char c = m - 16 * n; 489 | *str-- = c < 10 ? c + '0' : c + 'A' - 10; 490 | } while(n); 491 | } 492 | -------------------------------------------------------------------------------- /src/utility/Enc28J60Network.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Enc28J60NetworkClass.h 3 | UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. 4 | 5 | Copyright (c) 2013 Norbert Truchsess 6 | All rights reserved. 7 | 8 | based on enc28j60.c file from the AVRlib library by Pascal Stang. 9 | For AVRlib See http://www.procyonengineering.com/ 10 | 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License as published by 13 | the Free Software Foundation, either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU General Public License for more details. 20 | 21 | You should have received a copy of the GNU General Public License 22 | along with this program. If not, see . 23 | */ 24 | 25 | #include "Enc28J60Network.h" 26 | #include 27 | #include 28 | 29 | extern "C" { 30 | #include "enc28j60.h" 31 | #include "uip.h" 32 | } 33 | 34 | // set CS to 0 = active 35 | #define CSACTIVE digitalWrite(csPin, LOW) 36 | // set CS to 1 = passive 37 | #define CSPASSIVE digitalWrite(csPin, HIGH) 38 | 39 | #define SPI_ETHERNET_SETTINGS SPISettings(20000000, MSBFIRST, SPI_MODE0) 40 | // 41 | bool Enc28J60Network::spiInitialized=false; 42 | uint8_t Enc28J60Network::csPin=SS; 43 | uint16_t Enc28J60Network::nextPacketPtr; 44 | uint8_t Enc28J60Network::bank=0xff; 45 | 46 | struct memblock Enc28J60Network::receivePkt; 47 | 48 | void Enc28J60Network::initSPI() 49 | { 50 | if (spiInitialized) 51 | return; 52 | pinMode(csPin, OUTPUT); 53 | CSPASSIVE; 54 | SPI.begin(); 55 | spiInitialized = true; 56 | } 57 | 58 | bool Enc28J60Network::init(uint8_t* macaddr) 59 | { 60 | 61 | MemoryPool::init(); // 1 byte in between RX_STOP_INIT and pool to allow prepending of controlbyte 62 | 63 | initSPI(); 64 | 65 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 66 | 67 | // perform system reset 68 | writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); 69 | delay(50); 70 | // check CLKRDY bit to see if reset is complete 71 | // The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait. 72 | //while(!(readReg(ESTAT) & ESTAT_CLKRDY)); 73 | // do bank 0 stuff 74 | // initialize receive buffer 75 | // 16-bit transfers, must write low byte first 76 | // set receive buffer start address 77 | nextPacketPtr = RXSTART_INIT; 78 | // Rx start 79 | writeRegPair(ERXSTL, RXSTART_INIT); 80 | // set receive pointer address 81 | writeRegPair(ERXRDPTL, RXSTART_INIT); 82 | // RX end 83 | writeRegPair(ERXNDL, RXSTOP_INIT); 84 | // TX start 85 | //writeRegPair(ETXSTL, TXSTART_INIT); 86 | // TX end 87 | //writeRegPair(ETXNDL, TXSTOP_INIT); 88 | // do bank 1 stuff, packet filter: 89 | // For broadcast packets we allow only ARP packtets 90 | // All other packets should be unicast only for our mac (MAADR) 91 | // 92 | // The pattern to match on is therefore 93 | // Type ETH.DST 94 | // ARP BROADCAST 95 | // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9 96 | // in binary these poitions are:11 0000 0011 1111 97 | // This is hex 303F->EPMM0=0x3f,EPMM1=0x30 98 | writeReg(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN); 99 | writeRegPair(EPMM0, 0x303f); 100 | writeRegPair(EPMCSL, 0xf7f9); 101 | // 102 | // 103 | // do bank 2 stuff 104 | // enable MAC receive 105 | // and bring MAC out of reset (writes 0x00 to MACON2) 106 | writeRegPair(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); 107 | // enable automatic padding to 60bytes and CRC operations 108 | writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); 109 | // set inter-frame gap (non-back-to-back) 110 | writeRegPair(MAIPGL, 0x0C12); 111 | // set inter-frame gap (back-to-back) 112 | writeReg(MABBIPG, 0x12); 113 | // Set the maximum packet size which the controller will accept 114 | // Do not send packets longer than MAX_FRAMELEN: 115 | writeRegPair(MAMXFLL, MAX_FRAMELEN); 116 | // do bank 3 stuff 117 | // write MAC address 118 | // NOTE: MAC address in ENC28J60 is byte-backward 119 | writeReg(MAADR5, macaddr[0]); 120 | writeReg(MAADR4, macaddr[1]); 121 | writeReg(MAADR3, macaddr[2]); 122 | writeReg(MAADR2, macaddr[3]); 123 | writeReg(MAADR1, macaddr[4]); 124 | writeReg(MAADR0, macaddr[5]); 125 | // no loopback of transmitted frames 126 | phyWrite(PHCON2, PHCON2_HDLDIS); 127 | // switch to bank 0 128 | setBank(ECON1); 129 | // enable interrutps 130 | writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); 131 | // enable packet reception 132 | writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); 133 | //Configure leds 134 | phyWrite(PHLCON,0x476); 135 | 136 | SPI.endTransaction(); 137 | 138 | return getrev(); 139 | } 140 | 141 | memhandle 142 | Enc28J60Network::receivePacket() 143 | { 144 | uint8_t rxstat; 145 | uint16_t len; 146 | 147 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 148 | 149 | // check if a packet has been received and buffered 150 | //if( !(readReg(EIR) & EIR_PKTIF) ){ 151 | // The above does not work. See Rev. B4 Silicon Errata point 6. 152 | if (readReg(EPKTCNT) != 0) 153 | { 154 | uint16_t readPtr = nextPacketPtr+6 > RXSTOP_INIT ? nextPacketPtr+6-((RXSTOP_INIT + 1)-RXSTART_INIT) : nextPacketPtr+6; 155 | // Set the read pointer to the start of the received packet 156 | writeRegPair(ERDPTL, nextPacketPtr); 157 | // read the next packet pointer 158 | nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); 159 | nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; 160 | // read the packet length (see datasheet page 43) 161 | len = readOp(ENC28J60_READ_BUF_MEM, 0); 162 | len |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; 163 | len -= 4; //remove the CRC count 164 | // read the receive status (see datasheet page 43) 165 | rxstat = readOp(ENC28J60_READ_BUF_MEM, 0); 166 | //rxstat |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; 167 | #ifdef ENC28J60DEBUG 168 | Serial.print("receivePacket ["); 169 | Serial.print(readPtr,HEX); 170 | Serial.print("-"); 171 | Serial.print((readPtr+len) % (RXSTOP_INIT+1),HEX); 172 | Serial.print("], next: "); 173 | Serial.print(nextPacketPtr,HEX); 174 | Serial.print(", stat: "); 175 | Serial.print(rxstat,HEX); 176 | Serial.print(", count: "); 177 | Serial.print(readReg(EPKTCNT)); 178 | Serial.print(" -> "); 179 | Serial.println((rxstat & 0x80)!=0 ? "OK" : "failed"); 180 | #endif 181 | // decrement the packet counter indicate we are done with this packet 182 | writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); 183 | // check CRC and symbol errors (see datasheet page 44, table 7-3): 184 | // The ERXFCON.CRCEN is set by default. Normally we should not 185 | // need to check this. 186 | if ((rxstat & 0x80) != 0) 187 | { 188 | receivePkt.begin = readPtr; 189 | receivePkt.size = len; 190 | SPI.endTransaction(); 191 | return UIP_RECEIVEBUFFERHANDLE; 192 | } 193 | // Move the RX read pointer to the start of the next received packet 194 | // This frees the memory we just read out 195 | setERXRDPT(); 196 | } 197 | SPI.endTransaction(); 198 | return (NOBLOCK); 199 | } 200 | 201 | void 202 | Enc28J60Network::setERXRDPT() 203 | { 204 | writeRegPair(ERXRDPTL, nextPacketPtr == RXSTART_INIT ? RXSTOP_INIT : nextPacketPtr-1); 205 | } 206 | 207 | memaddress 208 | Enc28J60Network::blockSize(memhandle handle) 209 | { 210 | return handle == NOBLOCK ? 0 : handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; 211 | } 212 | 213 | bool 214 | Enc28J60Network::sendPacket(memhandle handle) 215 | { 216 | memblock *packet = &blocks[handle]; 217 | uint16_t start = packet->begin; // includes the UIP_SENDBUFFER_OFFSET for control byte 218 | uint16_t end = start + packet->size - 1 - UIP_SENDBUFFER_PADDING; // end = start + size - 1 and padding for TSV is no included 219 | 220 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 221 | 222 | // write control-byte (if not 0 anyway) 223 | writeByte(start, 0); 224 | 225 | #ifdef ENC28J60DEBUG 226 | Serial.print("sendPacket("); 227 | Serial.print(handle); 228 | Serial.print(") ["); 229 | Serial.print(start,HEX); 230 | Serial.print("-"); 231 | Serial.print(end,HEX); 232 | Serial.print("]: "); 233 | for (uint16_t i=start; i<=end; i++) 234 | { 235 | Serial.print(readByte(i),HEX); 236 | Serial.print(" "); 237 | } 238 | Serial.println(); 239 | #endif 240 | 241 | // TX start 242 | writeRegPair(ETXSTL, start); 243 | // Set the TXND pointer to correspond to the packet size given 244 | writeRegPair(ETXNDL, end); 245 | 246 | bool success = false; 247 | // See Rev. B7 Silicon Errata issues 12 and 13 248 | for (uint8_t retry = 0; retry < TX_COLLISION_RETRY_COUNT; retry++) 249 | { 250 | // Reset the transmit logic problem. Errata 12 251 | writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); 252 | writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); 253 | writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF | EIR_TXIF); 254 | 255 | // send the contents of the transmit buffer onto the network 256 | writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); 257 | 258 | // wait for transmission to complete or fail 259 | uint8_t eir; 260 | while (((eir = readReg(EIR)) & (EIR_TXIF | EIR_TXERIF)) == 0); 261 | writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); 262 | success = ((eir & EIR_TXERIF) == 0); 263 | if (success) 264 | break; // usual exit of the for(retry) loop 265 | 266 | // Errata 13 detection 267 | uint8_t tsv4 = readByte(end + 4); 268 | if (!(tsv4 & 0b00100000)) // is it "late collision" indicated in bit 29 of TSV? 269 | break; // other fail, not the Errata 13 situation 270 | } 271 | 272 | SPI.endTransaction(); 273 | return success; 274 | } 275 | 276 | uint16_t 277 | Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) 278 | { 279 | memblock *packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle]; 280 | memaddress start = handle == UIP_RECEIVEBUFFERHANDLE && packet->begin + position > RXSTOP_INIT ? packet->begin + position-((RXSTOP_INIT + 1)-RXSTART_INIT) : packet->begin + position; 281 | 282 | writeRegPair(ERDPTL, start); 283 | 284 | if (len > packet->size - position) 285 | len = packet->size - position; 286 | return len; 287 | } 288 | 289 | uint16_t 290 | Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) 291 | { 292 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 293 | len = setReadPtr(handle, position, len); 294 | readBuffer(len, buffer); 295 | SPI.endTransaction(); 296 | return len; 297 | } 298 | 299 | uint16_t 300 | Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) 301 | { 302 | memblock *packet = &blocks[handle]; 303 | uint16_t start = packet->begin + position; 304 | 305 | if (len > packet->size - position) 306 | len = packet->size - position; 307 | 308 | if (len == 0) 309 | return 0; 310 | 311 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 312 | 313 | writeRegPair(EWRPTL, start); 314 | 315 | writeBuffer(len, buffer); 316 | 317 | SPI.endTransaction(); 318 | return len; 319 | } 320 | 321 | uint8_t Enc28J60Network::readByte(uint16_t addr) 322 | { 323 | writeRegPair(ERDPTL, addr); 324 | 325 | CSACTIVE; 326 | // issue read command 327 | SPI.transfer(ENC28J60_READ_BUF_MEM); 328 | // read data 329 | uint8_t c = SPI.transfer(0x00); 330 | CSPASSIVE; 331 | return c; 332 | } 333 | 334 | void Enc28J60Network::writeByte(uint16_t addr, uint8_t data) 335 | { 336 | writeRegPair(EWRPTL, addr); 337 | 338 | CSACTIVE; 339 | // issue write command 340 | SPI.transfer(ENC28J60_WRITE_BUF_MEM); 341 | // write data 342 | SPI.transfer(data); 343 | CSPASSIVE; 344 | } 345 | 346 | void 347 | Enc28J60Network::copyPacket(memhandle dest_pkt, memaddress dest_pos, memhandle src_pkt, memaddress src_pos, uint16_t len) 348 | { 349 | memblock *dest = &blocks[dest_pkt]; 350 | memblock *src = src_pkt == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[src_pkt]; 351 | 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; 352 | enc28J60_mempool_block_move_callback(dest->begin+dest_pos,start,len); 353 | // setERXRDPT(); let it to freePacket after all packets are saved 354 | } 355 | 356 | void 357 | enc28J60_mempool_block_move_callback(memaddress dest, memaddress src, memaddress len) 358 | { 359 | //void 360 | //Enc28J60Network::memblock_mv_cb(uint16_t dest, uint16_t src, uint16_t len) 361 | //{ 362 | 363 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 364 | 365 | //as ENC28J60 DMA is unable to copy single bytes: 366 | if (len == 1) 367 | { 368 | Enc28J60Network::writeByte(dest,Enc28J60Network::readByte(src)); 369 | } 370 | else 371 | { 372 | // calculate address of last byte 373 | len += src - 1; 374 | 375 | /* 1. Appropriately program the EDMAST, EDMAND 376 | and EDMADST register pairs. The EDMAST 377 | registers should point to the first byte to copy 378 | from, the EDMAND registers should point to the 379 | last byte to copy and the EDMADST registers 380 | should point to the first byte in the destination 381 | range. The destination range will always be 382 | linear, never wrapping at any values except from 383 | 8191 to 0 (the 8-Kbyte memory boundary). 384 | Extreme care should be taken when 385 | programming the start and end pointers to 386 | prevent a never ending DMA operation which 387 | would overwrite the entire 8-Kbyte buffer. 388 | */ 389 | Enc28J60Network::writeRegPair(EDMASTL, src); 390 | Enc28J60Network::writeRegPair(EDMADSTL, dest); 391 | 392 | if ((src <= RXSTOP_INIT)&& (len > RXSTOP_INIT))len -= ((RXSTOP_INIT + 1)-RXSTART_INIT); 393 | Enc28J60Network::writeRegPair(EDMANDL, len); 394 | 395 | /* 396 | 2. If an interrupt at the end of the copy process is 397 | desired, set EIE.DMAIE and EIE.INTIE and 398 | clear EIR.DMAIF. 399 | 400 | 3. Verify that ECON1.CSUMEN is clear. */ 401 | Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_CSUMEN); 402 | 403 | /* 4. Start the DMA copy by setting ECON1.DMAST. */ 404 | Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST); 405 | 406 | // wait until runnig DMA is completed 407 | while (Enc28J60Network::readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST); 408 | } 409 | SPI.endTransaction(); 410 | } 411 | 412 | void 413 | Enc28J60Network::freePacket() 414 | { 415 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 416 | setERXRDPT(); 417 | SPI.endTransaction(); 418 | } 419 | 420 | uint8_t 421 | Enc28J60Network::readOp(uint8_t op, uint8_t address) 422 | { 423 | CSACTIVE; 424 | // issue read command 425 | SPI.transfer(op | (address & ADDR_MASK)); 426 | // read data 427 | if(address & 0x80) 428 | { 429 | // do dummy read if needed (for mac and mii, see datasheet page 29) 430 | SPI.transfer(0x00); 431 | } 432 | uint8_t c = SPI.transfer(0x00); 433 | CSPASSIVE; 434 | return c; 435 | } 436 | 437 | void 438 | Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) 439 | { 440 | CSACTIVE; 441 | // issue write command 442 | SPI.transfer(op | (address & ADDR_MASK)); 443 | // write data 444 | SPI.transfer(data); 445 | CSPASSIVE; 446 | } 447 | 448 | void 449 | Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) 450 | { 451 | CSACTIVE; 452 | // issue read command 453 | SPI.transfer(ENC28J60_READ_BUF_MEM); 454 | while(len) 455 | { 456 | len--; 457 | // read data 458 | *data = SPI.transfer(0x00); 459 | data++; 460 | } 461 | //*data='\0'; 462 | CSPASSIVE; 463 | } 464 | 465 | void 466 | Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) 467 | { 468 | CSACTIVE; 469 | // issue write command 470 | SPI.transfer(ENC28J60_WRITE_BUF_MEM); 471 | while(len) 472 | { 473 | len--; 474 | // write data 475 | SPI.transfer(*data); 476 | data++; 477 | } 478 | CSPASSIVE; 479 | } 480 | 481 | void 482 | Enc28J60Network::setBank(uint8_t address) 483 | { 484 | // set the bank (if needed) 485 | if((address & BANK_MASK) != bank) 486 | { 487 | // set the bank 488 | writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0)); 489 | writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5); 490 | bank = (address & BANK_MASK); 491 | } 492 | } 493 | 494 | uint8_t 495 | Enc28J60Network::readReg(uint8_t address) 496 | { 497 | // set the bank 498 | setBank(address); 499 | // do the read 500 | return readOp(ENC28J60_READ_CTRL_REG, address); 501 | } 502 | 503 | void 504 | Enc28J60Network::writeReg(uint8_t address, uint8_t data) 505 | { 506 | // set the bank 507 | setBank(address); 508 | // do the write 509 | writeOp(ENC28J60_WRITE_CTRL_REG, address, data); 510 | } 511 | 512 | void 513 | Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) 514 | { 515 | // set the bank 516 | setBank(address); 517 | // do the write 518 | writeOp(ENC28J60_WRITE_CTRL_REG, address, (data&0xFF)); 519 | writeOp(ENC28J60_WRITE_CTRL_REG, address+1, (data) >> 8); 520 | } 521 | 522 | void 523 | Enc28J60Network::phyWrite(uint8_t address, uint16_t data) 524 | { 525 | // set the PHY register address 526 | writeReg(MIREGADR, address); 527 | // write the PHY data 528 | writeRegPair(MIWRL, data); 529 | // wait until the PHY write completes 530 | while(readReg(MISTAT) & MISTAT_BUSY){ 531 | delayMicroseconds(15); 532 | } 533 | } 534 | 535 | uint16_t 536 | Enc28J60Network::phyRead(uint8_t address) 537 | { 538 | writeReg(MIREGADR,address); 539 | writeReg(MICMD, MICMD_MIIRD); 540 | // wait until the PHY read completes 541 | while(readReg(MISTAT) & MISTAT_BUSY){ 542 | delayMicroseconds(15); 543 | } //and MIRDH 544 | writeReg(MICMD, 0); 545 | return (readReg(MIRDL) | readReg(MIRDH) << 8); 546 | } 547 | 548 | void 549 | Enc28J60Network::clkout(uint8_t clk) 550 | { 551 | //setup clkout: 2 is 12.5MHz: 552 | writeReg(ECOCON, clk & 0x7); 553 | } 554 | 555 | // read the revision of the chip: 556 | uint8_t 557 | Enc28J60Network::getrev(void) 558 | { 559 | initSPI(); 560 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 561 | uint8_t res = readReg(EREVID); 562 | if (res == 0xFF) { 563 | res = 0; 564 | } 565 | SPI.endTransaction(); 566 | return res; 567 | } 568 | 569 | uint16_t 570 | Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) 571 | { 572 | uint16_t t; 573 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 574 | len = setReadPtr(handle, pos, len)-1; 575 | CSACTIVE; 576 | // issue read command 577 | SPI.transfer(ENC28J60_READ_BUF_MEM); 578 | uint16_t i; 579 | for (i = 0; i < len; i+=2) 580 | { 581 | // read data 582 | t = SPI.transfer(0x00) << 8; 583 | t += SPI.transfer(0x00); 584 | sum += t; 585 | if(sum < t) { 586 | sum++; /* carry */ 587 | } 588 | } 589 | if(i == len) { 590 | t = (SPI.transfer(0x00) << 8) + 0; 591 | sum += t; 592 | if(sum < t) { 593 | sum++; /* carry */ 594 | } 595 | } 596 | CSPASSIVE; 597 | SPI.endTransaction(); 598 | 599 | /* Return sum in host byte order. */ 600 | return sum; 601 | } 602 | 603 | void 604 | Enc28J60Network::powerOff() 605 | { 606 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 607 | writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); 608 | delay(50); 609 | writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); 610 | delay(50); 611 | writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); 612 | SPI.endTransaction(); 613 | } 614 | 615 | void 616 | Enc28J60Network::powerOn() 617 | { 618 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 619 | writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); 620 | delay(50); 621 | writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); 622 | delay(50); 623 | SPI.endTransaction(); 624 | } 625 | 626 | bool 627 | Enc28J60Network::linkStatus() 628 | { 629 | SPI.beginTransaction(SPI_ETHERNET_SETTINGS); 630 | bool res = (phyRead(PHSTAT2) & 0x0400) > 0; 631 | SPI.endTransaction(); 632 | return res; 633 | } 634 | -------------------------------------------------------------------------------- /src/EthernetClient.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | UIPClient.cpp - Arduino implementation of a uIP wrapper class. 3 | Copyright (c) 2013 Norbert Truchsess 4 | All rights reserved. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | extern "C" 21 | { 22 | #include "utility/uip-conf.h" 23 | #include "utility/uip.h" 24 | #include "utility/uip_arp.h" 25 | } 26 | #include "Ethernet.h" 27 | #include "EthernetClient.h" 28 | 29 | #define UIP_TCP_PHYH_LEN UIP_LLH_LEN+UIP_IPTCPH_LEN 30 | 31 | #define UIPClient EthernetClient // to not pollute source code history with the rename 32 | 33 | uip_userdata_t UIPClient::all_data[UIP_CONNS]; 34 | 35 | UIPClient::UIPClient() : 36 | data(NULL) 37 | { 38 | } 39 | 40 | UIPClient::UIPClient(uip_userdata_t* conn_data) : 41 | data(conn_data) 42 | { 43 | } 44 | 45 | int 46 | UIPClient::connect(IPAddress ip, uint16_t port) 47 | { 48 | stop(); 49 | uip_ipaddr_t ipaddr; 50 | uip_ip_addr(ipaddr, ip); 51 | struct uip_conn* conn = uip_connect(&ipaddr, htons(port)); 52 | if (conn) 53 | { 54 | #if UIP_CONNECT_TIMEOUT > 0 55 | uint32_t timeout = millis() + connectTimeout; 56 | #endif 57 | while((conn->tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) 58 | { 59 | UIPEthernetClass::tick(); 60 | if ((conn->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) 61 | { 62 | data = (uip_userdata_t*) conn->appstate; 63 | #ifdef UIPETHERNET_DEBUG_CLIENT 64 | Serial.print(F("connected, state: ")); 65 | Serial.print(data->state); 66 | Serial.print(F(", first packet in: ")); 67 | Serial.println(data->packets_in[0]); 68 | #endif 69 | return 1; 70 | } 71 | #if UIP_CONNECT_TIMEOUT > 0 72 | if (((int32_t)(millis() - timeout)) > 0) 73 | { 74 | conn->tcpstateflags = UIP_CLOSED; 75 | break; 76 | } 77 | #endif 78 | } 79 | } 80 | return 0; 81 | } 82 | 83 | int 84 | UIPClient::connect(const char *host, uint16_t port) 85 | { 86 | // Look up the host first 87 | int ret = 0; 88 | #if UIP_UDP 89 | IPAddress remote_addr; 90 | ret = Ethernet.hostByName(host, remote_addr); 91 | if (ret == 1) { 92 | return connect(remote_addr, port); 93 | } 94 | #endif 95 | return ret; 96 | } 97 | 98 | void 99 | UIPClient::stop() 100 | { 101 | if (data && data->state) 102 | { 103 | #ifdef UIPETHERNET_DEBUG_CLIENT 104 | Serial.println(F("before stop(), with data")); 105 | _dumpAllData(); 106 | #endif 107 | if (data->state & UIP_CLIENT_REMOTECLOSED) 108 | { 109 | data->state = 0; 110 | } 111 | else 112 | { 113 | flush(); 114 | data->state |= UIP_CLIENT_CLOSE; 115 | } 116 | _flushBlocks(data->packets_in); 117 | #ifdef UIPETHERNET_DEBUG_CLIENT 118 | Serial.println(F("after stop()")); 119 | _dumpAllData(); 120 | #endif 121 | } 122 | #ifdef UIPETHERNET_DEBUG_CLIENT 123 | else 124 | { 125 | Serial.println(F("stop(), data: NULL")); 126 | } 127 | #endif 128 | data = NULL; 129 | UIPEthernetClass::tick(); 130 | } 131 | 132 | uint8_t 133 | UIPClient::connected() 134 | { 135 | return (data && (data->packets_in[0] != NOBLOCK || (data->state & UIP_CLIENT_CONNECTED))) ? 1 : 0; 136 | } 137 | 138 | bool 139 | UIPClient::operator==(const UIPClient& rhs) 140 | { 141 | return data && rhs.data && (data == rhs.data); 142 | } 143 | 144 | UIPClient::operator bool() 145 | { 146 | UIPEthernetClass::tick(); 147 | return data && (!(data->state & UIP_CLIENT_REMOTECLOSED) || data->packets_in[0] != NOBLOCK); 148 | } 149 | 150 | size_t 151 | UIPClient::write(uint8_t c) 152 | { 153 | return write(&c, 1); 154 | } 155 | 156 | size_t 157 | UIPClient::write(const uint8_t *buf, size_t size) 158 | { 159 | uip_userdata_t* u = data; 160 | int remain = size; 161 | uint16_t written; 162 | #if UIP_WRITE_TIMEOUT > 0 163 | uint32_t timeout_start = millis(); 164 | #endif 165 | repeat: 166 | UIPEthernetClass::tick(); 167 | if (u && u->state && !(u->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))) 168 | { 169 | uint8_t p = _currentBlock(u->packets_out); 170 | if (u->packets_out[p] == NOBLOCK) 171 | { 172 | newpacket: 173 | u->packets_out[p] = Enc28J60Network::allocBlock(UIP_SOCKET_DATALEN); 174 | if (u->packets_out[p] == NOBLOCK) 175 | { 176 | #if UIP_WRITE_TIMEOUT > 0 177 | if (millis() - timeout_start > UIP_WRITE_TIMEOUT) 178 | { 179 | setWriteError(); 180 | goto ready; 181 | } 182 | #endif 183 | Ethernet.call_yield(); 184 | goto repeat; 185 | } 186 | u->out_pos = 0; 187 | } 188 | #ifdef UIPETHERNET_DEBUG_CLIENT 189 | Serial.print(F("UIPClient.write: writePacket(")); 190 | Serial.print(u->packets_out[p]); 191 | Serial.print(F(") pos: ")); 192 | Serial.print(u->out_pos); 193 | Serial.print(F(", buf[")); 194 | Serial.print(size-remain); 195 | Serial.print(F("-")); 196 | Serial.print(remain); 197 | Serial.print(F("]: '")); 198 | Serial.write((uint8_t*)buf+size-remain,remain); 199 | Serial.println(F("'")); 200 | #endif 201 | written = Enc28J60Network::writePacket(u->packets_out[p],u->out_pos,(uint8_t*)buf+size-remain,remain); 202 | remain -= written; 203 | u->out_pos+=written; 204 | if (remain > 0) 205 | { 206 | if (p == 0) // block 0 just filled, start sending immediately 207 | { 208 | flush(); 209 | goto repeat; 210 | } 211 | if (p == UIP_SOCKET_NUMPACKETS-1) 212 | { 213 | #if UIP_WRITE_TIMEOUT > 0 214 | if (millis() - timeout_start > UIP_WRITE_TIMEOUT) 215 | { 216 | setWriteError(); 217 | goto ready; 218 | } 219 | #endif 220 | Ethernet.call_yield(); 221 | goto repeat; 222 | } 223 | p++; 224 | goto newpacket; 225 | } 226 | ready: 227 | return size-remain; 228 | } 229 | return 0; 230 | } 231 | 232 | int 233 | UIPClient::availableForWrite() 234 | { 235 | const int MAX_AVAILABLE = UIP_SOCKET_DATALEN * UIP_SOCKET_NUMPACKETS; 236 | UIPEthernetClass::tick(); 237 | if (data->packets_out[0] == NOBLOCK) 238 | return MAX_AVAILABLE; 239 | uint8_t p = _currentBlock(data->packets_out); 240 | int used = UIP_SOCKET_DATALEN * p + data->out_pos; 241 | return MAX_AVAILABLE - used; 242 | } 243 | 244 | void 245 | UIPClient::flush() 246 | { 247 | UIPEthernetClass::tick(); 248 | 249 | if (data && data->packets_out[0] != NOBLOCK) 250 | { 251 | struct uip_conn* conn = &uip_conns[data->conn_index]; 252 | if (!uip_outstanding(conn)) 253 | { 254 | uip_poll_conn(conn); 255 | if (uip_len > 0) 256 | { 257 | uip_arp_out(); 258 | UIPEthernetClass::network_send(); 259 | } 260 | } 261 | } 262 | } 263 | 264 | int 265 | UIPClient::available() 266 | { 267 | if (!(*this)) 268 | return 0; 269 | 270 | int len = 0; 271 | for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) 272 | { 273 | len += Enc28J60Network::blockSize(data->packets_in[i]); 274 | } 275 | 276 | // if sketch checks for incoming data and there are unsent data, flush the transmit buffer 277 | if (!len) 278 | flush(); 279 | return len; 280 | } 281 | 282 | int 283 | UIPClient::read(uint8_t *buf, size_t size) 284 | { 285 | if (*this) 286 | { 287 | uint16_t remain = size; 288 | if (data->packets_in[0] == NOBLOCK) 289 | { 290 | flush(); // if sketch checks for incoming data and there are unsent data, flush the transmit buffer 291 | return 0; 292 | } 293 | uint16_t read; 294 | do 295 | { 296 | read = Enc28J60Network::readPacket(data->packets_in[0],0,buf+size-remain,remain); 297 | if (read == Enc28J60Network::blockSize(data->packets_in[0])) 298 | { 299 | remain -= read; 300 | _eatBlock(data->packets_in); 301 | if (uip_stopped(&uip_conns[data->conn_index]) && !(data->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))) 302 | data->state |= UIP_CLIENT_RESTART; 303 | if (data->packets_in[0] == NOBLOCK) 304 | { 305 | if (data->state & UIP_CLIENT_REMOTECLOSED) 306 | { 307 | data->state = 0; 308 | data = NULL; 309 | } 310 | return size-remain; 311 | } 312 | } 313 | else 314 | { 315 | Enc28J60Network::resizeBlock(data->packets_in[0],read); 316 | break; 317 | } 318 | } 319 | while(remain > 0); 320 | return size; 321 | } 322 | return -1; 323 | } 324 | 325 | int 326 | UIPClient::read() 327 | { 328 | uint8_t c; 329 | if (read(&c,1) <= 0) 330 | return -1; 331 | return c; 332 | } 333 | 334 | int 335 | UIPClient::peek() 336 | { 337 | if (*this) 338 | { 339 | if (data->packets_in[0] != NOBLOCK) 340 | { 341 | uint8_t c; 342 | Enc28J60Network::readPacket(data->packets_in[0],0,&c,1); 343 | return c; 344 | } 345 | } 346 | return -1; 347 | } 348 | 349 | void 350 | UIPClient::discardReceived() 351 | { 352 | if (*this) 353 | { 354 | _flushBlocks(data->packets_in); 355 | } 356 | } 357 | 358 | IPAddress 359 | UIPClient::remoteIP(void) 360 | { 361 | return data ? ip_addr_uip(uip_conns[data->conn_index].ripaddr) : IPAddress(); 362 | } 363 | 364 | uint16_t 365 | UIPClient::remotePort(void) 366 | { 367 | return data ? ntohs(uip_conns[data->conn_index].rport) : 0; 368 | } 369 | 370 | uint8_t 371 | UIPClient::status() 372 | { 373 | return !data ? UIP_CLOSED : uip_conns[data->conn_index].tcpstateflags & UIP_TS_MASK; 374 | } 375 | 376 | void 377 | uipclient_appcall(void) 378 | { 379 | uint16_t send_len = 0; 380 | uip_userdata_t *u = (uip_userdata_t*)uip_conn->appstate; 381 | if (!u && uip_connected()) 382 | { 383 | #ifdef UIPETHERNET_DEBUG_CLIENT 384 | Serial.println(F("UIPClient uip_connected")); 385 | UIPClient::_dumpAllData(); 386 | #endif 387 | u = (uip_userdata_t*) UIPClient::_allocateData(); 388 | if (u) 389 | { 390 | uip_conn->appstate = u; 391 | #ifdef UIPETHERNET_DEBUG_CLIENT 392 | Serial.print(F("UIPClient allocated state: ")); 393 | Serial.println(u->state,BIN); 394 | #endif 395 | } 396 | #ifdef UIPETHERNET_DEBUG_CLIENT 397 | else 398 | Serial.println(F("UIPClient allocation failed")); 399 | #endif 400 | } 401 | if (u) 402 | { 403 | if (uip_newdata()) 404 | { 405 | #ifdef UIPETHERNET_DEBUG_CLIENT 406 | Serial.print(F("UIPClient uip_newdata, uip_len:")); 407 | Serial.println(uip_len); 408 | #endif 409 | if (uip_len && u->state && !(u->state & UIP_CLIENT_CLOSE)) 410 | { 411 | for (uint8_t i=0; i < UIP_SOCKET_NUMPACKETS; i++) 412 | { 413 | if (u->packets_in[i] == NOBLOCK) 414 | { 415 | u->packets_in[i] = Enc28J60Network::allocBlock(uip_len); 416 | if (u->packets_in[i] != NOBLOCK) 417 | { 418 | Enc28J60Network::copyPacket(u->packets_in[i],0,UIPEthernetClass::in_packet,((uint8_t*)uip_appdata)-uip_buf,uip_len); 419 | if (i == UIP_SOCKET_NUMPACKETS-1) 420 | uip_stop(); 421 | goto finish_newdata; 422 | } 423 | } 424 | } 425 | UIPEthernetClass::packetstate &= ~UIPETHERNET_FREEPACKET; 426 | uip_stop(); 427 | } 428 | } 429 | finish_newdata: 430 | if (u->state & UIP_CLIENT_RESTART) 431 | { 432 | u->state &= ~UIP_CLIENT_RESTART; 433 | uip_restart(); 434 | } 435 | // If the connection has been closed, save received but unread data. 436 | if (uip_closed() || uip_timedout() || uip_aborted()) 437 | { 438 | #ifdef UIPETHERNET_DEBUG_CLIENT 439 | Serial.println(F("UIPClient uip_closed")); 440 | UIPClient::_dumpAllData(); 441 | #endif 442 | // drop outgoing packets not sent yet: 443 | UIPClient::_flushBlocks(u->packets_out); 444 | if (u->packets_in[0] != NOBLOCK) 445 | { 446 | ((uip_userdata_closed_t *)u)->lport = uip_conn->lport; 447 | u->state |= UIP_CLIENT_REMOTECLOSED; 448 | } 449 | else 450 | u->state = 0; 451 | // disassociate appdata. 452 | #ifdef UIPETHERNET_DEBUG_CLIENT 453 | Serial.println(F("after UIPClient uip_closed")); 454 | UIPClient::_dumpAllData(); 455 | #endif 456 | uip_conn->appstate = NULL; 457 | goto finish; 458 | } 459 | if (uip_acked()) 460 | { 461 | #ifdef UIPETHERNET_DEBUG_CLIENT 462 | Serial.println(F("UIPClient uip_acked")); 463 | #endif 464 | UIPClient::_eatBlock(u->packets_out); 465 | goto send; 466 | } 467 | if (uip_poll() || uip_rexmit()) 468 | { 469 | #ifdef UIPETHERNET_DEBUG_CLIENT 470 | //Serial.println(F("UIPClient uip_poll")); 471 | #endif 472 | send: 473 | if (u->packets_out[0] != NOBLOCK) 474 | { 475 | if (u->packets_out[1] == NOBLOCK) 476 | { 477 | send_len = u->out_pos; 478 | if (send_len > 0) 479 | { 480 | Enc28J60Network::resizeBlock(u->packets_out[0],0,send_len); 481 | } 482 | } 483 | else 484 | send_len = Enc28J60Network::blockSize(u->packets_out[0]); 485 | if (send_len > 0) 486 | { 487 | UIPEthernetClass::uip_hdrlen = ((uint8_t*)uip_appdata)-uip_buf; 488 | UIPEthernetClass::uip_packet = Enc28J60Network::allocBlock(UIPEthernetClass::uip_hdrlen+send_len + UIP_SENDBUFFER_OFFSET + UIP_SENDBUFFER_PADDING); 489 | if (UIPEthernetClass::uip_packet != NOBLOCK) 490 | { 491 | Enc28J60Network::copyPacket(UIPEthernetClass::uip_packet,UIPEthernetClass::uip_hdrlen + UIP_SENDBUFFER_OFFSET,u->packets_out[0],0,send_len); 492 | UIPEthernetClass::packetstate |= UIPETHERNET_SENDPACKET; 493 | } 494 | } 495 | goto finish; 496 | } 497 | } 498 | // don't close connection unless all outgoing packets are sent 499 | if (u->state & UIP_CLIENT_CLOSE) 500 | { 501 | #ifdef UIPETHERNET_DEBUG_CLIENT 502 | Serial.println(F("UIPClient state UIP_CLIENT_CLOSE")); 503 | UIPClient::_dumpAllData(); 504 | #endif 505 | if (u->packets_out[0] == NOBLOCK) 506 | { 507 | u->state = 0; 508 | uip_conn->appstate = NULL; 509 | uip_close(); 510 | #ifdef UIPETHERNET_DEBUG_CLIENT 511 | Serial.println(F("no blocks out -> free userdata")); 512 | UIPClient::_dumpAllData(); 513 | #endif 514 | } 515 | else 516 | { 517 | uip_stop(); 518 | #ifdef UIPETHERNET_DEBUG_CLIENT 519 | Serial.println(F("blocks outstanding transfer -> uip_stop()")); 520 | #endif 521 | } 522 | } 523 | } 524 | finish: 525 | uip_send(uip_appdata,send_len); 526 | uip_len = send_len; 527 | } 528 | 529 | uip_userdata_t * 530 | UIPClient::_allocateData() 531 | { 532 | for ( uint8_t sock = 0; sock < UIP_CONNS; sock++ ) 533 | { 534 | uip_userdata_t* data = &UIPClient::all_data[sock]; 535 | if (!data->state) 536 | { 537 | *data = uip_userdata_t(); 538 | data->conn_index = uip_conn - uip_conns; // pointer arithmetics 539 | data->state = UIP_CLIENT_CONNECTED; 540 | return data; 541 | } 542 | } 543 | return NULL; 544 | } 545 | 546 | uint8_t 547 | UIPClient::_currentBlock(memhandle* block) 548 | { 549 | for (uint8_t i = 1; i < UIP_SOCKET_NUMPACKETS; i++) 550 | { 551 | if (block[i] == NOBLOCK) 552 | return i-1; 553 | } 554 | return UIP_SOCKET_NUMPACKETS-1; 555 | } 556 | 557 | void 558 | UIPClient::_eatBlock(memhandle* block) 559 | { 560 | #ifdef UIPETHERNET_DEBUG_CLIENT 561 | memhandle* start = block; 562 | Serial.print(F("eatblock(")); 563 | Serial.print(*block); 564 | Serial.print(F("): ")); 565 | for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) 566 | { 567 | Serial.print(start[i]); 568 | Serial.print(F(" ")); 569 | } 570 | Serial.print(F("-> ")); 571 | #endif 572 | Enc28J60Network::freeBlock(block[0]); 573 | for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS-1; i++) 574 | { 575 | block[i] = block[i+1]; 576 | } 577 | block[UIP_SOCKET_NUMPACKETS-1] = NOBLOCK; 578 | #ifdef UIPETHERNET_DEBUG_CLIENT 579 | for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) 580 | { 581 | Serial.print(start[i]); 582 | Serial.print(F(" ")); 583 | } 584 | Serial.println(); 585 | #endif 586 | } 587 | 588 | void 589 | UIPClient::_flushBlocks(memhandle* block) 590 | { 591 | for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) 592 | { 593 | Enc28J60Network::freeBlock(block[i]); 594 | block[i] = NOBLOCK; 595 | } 596 | } 597 | 598 | #ifdef UIPETHERNET_DEBUG_CLIENT 599 | void 600 | UIPClient::_dumpAllData() { 601 | for (uint8_t i=0; i < UIP_CONNS; i++) 602 | { 603 | if (!all_data[i].state) 604 | continue; 605 | Serial.print(F("UIPClient::all_data[")); 606 | Serial.print(i); 607 | Serial.print(F("], state:")); 608 | Serial.println(all_data[i].state, BIN); 609 | struct uip_conn& conn = uip_conns[all_data[i].conn_index]; 610 | Serial.println(ip_addr_uip(conn.ripaddr)); 611 | Serial.print(F("ix: ")); 612 | Serial.print(all_data[i].conn_index); 613 | Serial.print(F(" tcp flags: 0x")); 614 | Serial.print(conn.tcpstateflags, HEX); 615 | Serial.print(F(" retransmission: timer ")); 616 | Serial.print(conn.timer); 617 | Serial.print(F(" nrtx ")); 618 | Serial.println(conn.nrtx); 619 | Serial.print(F("packets_in: ")); 620 | for (uint8_t j=0; j < UIP_SOCKET_NUMPACKETS; j++) 621 | { 622 | Serial.print(all_data[i].packets_in[j]); 623 | Serial.print(F(" ")); 624 | } 625 | Serial.println(); 626 | if (all_data[i].state & UIP_CLIENT_REMOTECLOSED) 627 | { 628 | Serial.print(F("state remote closed, local port: ")); 629 | Serial.println(htons(((uip_userdata_closed_t *)(&all_data[i]))->lport)); 630 | } 631 | else 632 | { 633 | Serial.print(F("packets_out: ")); 634 | for (uint8_t j=0; j < UIP_SOCKET_NUMPACKETS; j++) 635 | { 636 | Serial.print(all_data[i].packets_out[j]); 637 | Serial.print(F(" ")); 638 | } 639 | Serial.println(); 640 | Serial.print(F("out_pos: ")); 641 | Serial.println(all_data[i].out_pos); 642 | } 643 | Serial.println(); 644 | } 645 | for (uint8_t i=0; i < UIP_CONNS; i++) 646 | { 647 | struct uip_conn& conn = uip_conns[i]; 648 | Serial.print(i); 649 | Serial.print(' '); 650 | Serial.print(ip_addr_uip(conn.ripaddr)); 651 | Serial.print(':'); 652 | Serial.print(ntohs(conn.rport)); 653 | Serial.print(' '); 654 | Serial.print(ntohs(conn.lport)); 655 | Serial.print(F(" tcp flags: 0x")); 656 | Serial.print(conn.tcpstateflags, HEX); 657 | Serial.print(F(" retransmission: timer ")); 658 | Serial.print(conn.timer); 659 | Serial.print(F(" nrtx ")); 660 | Serial.println(conn.nrtx); 661 | } 662 | } 663 | #endif 664 | --------------------------------------------------------------------------------