diff --git a/libraries/Ethernet/Client.cpp b/libraries/Ethernet/Client.cpp index 96d5c7eedbd..7ae55d08d5f 100644 --- a/libraries/Ethernet/Client.cpp +++ b/libraries/Ethernet/Client.cpp @@ -16,7 +16,7 @@ uint16_t Client::_srcport = 1024; Client::Client(uint8_t sock) : _sock(sock) { } -Client::Client(uint8_t *ip, uint16_t port) : _ip(ip), _port(port), _sock(MAX_SOCK_NUM) { +Client::Client(IPAddress& ip, uint16_t port) : _ip(ip), _port(port), _sock(MAX_SOCK_NUM) { } uint8_t Client::connect() { @@ -38,7 +38,7 @@ uint8_t Client::connect() { if (_srcport == 0) _srcport = 1024; socket(_sock, SnMR::TCP, _srcport, 0); - if (!::connect(_sock, _ip, _port)) { + if (!::connect(_sock, _ip.raw_address(), _port)) { _sock = MAX_SOCK_NUM; return 0; } diff --git a/libraries/Ethernet/Client.h b/libraries/Ethernet/Client.h index 8725f158a9c..e92e90e33f1 100644 --- a/libraries/Ethernet/Client.h +++ b/libraries/Ethernet/Client.h @@ -7,8 +7,8 @@ class Client : public Stream { public: Client(); - Client(uint8_t); - Client(uint8_t *, uint16_t); + Client(uint8_t sock); + Client(IPAddress& ip, uint16_t port); uint8_t status(); uint8_t connect(); @@ -31,7 +31,7 @@ class Client : public Stream { private: static uint16_t _srcport; uint8_t _sock; - uint8_t *_ip; + IPAddress _ip; uint16_t _port; }; diff --git a/libraries/Ethernet/Dhcp.cpp b/libraries/Ethernet/Dhcp.cpp new file mode 100755 index 00000000000..8264b435bd1 --- /dev/null +++ b/libraries/Ethernet/Dhcp.cpp @@ -0,0 +1,341 @@ +// DHCP Library v0.3 - April 25, 2009 +// Author: Jordan Terrell - blog.jordanterrell.com + +#include "w5100.h" + +#include +#include +#include "Dhcp.h" +#include "wiring.h" +#include "util.h" + +int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) +{ + uint8_t dhcp_state = STATE_DHCP_START; + uint8_t messageType = 0; + + // zero out _dhcpMacAddr, _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp + memset(_dhcpMacAddr, 0, 26); + + memcpy((void*)_dhcpMacAddr, (void*)mac, 6); + + // Pick an initial transaction ID + _dhcpTransactionId = random(1UL, 2000UL); + _dhcpInitialTransactionId = _dhcpTransactionId; + + if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) + { + // Couldn't get a socket + return 0; + } + + presend_DHCP(); + + int result = 0; + + unsigned long startTime = millis(); + + while(dhcp_state != STATE_DHCP_LEASED) + { + if(dhcp_state == STATE_DHCP_START) + { + _dhcpTransactionId++; + + send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000)); + dhcp_state = STATE_DHCP_DISCOVER; + } + else if(dhcp_state == STATE_DHCP_DISCOVER) + { + uint32_t respId; + messageType = parseDHCPResponse(responseTimeout, respId); + if(messageType == DHCP_OFFER) + { + // We'll use the transaction ID that the offer came with, + // rather than the one we were up to + _dhcpTransactionId = respId; + send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000)); + dhcp_state = STATE_DHCP_REQUEST; + } + } + else if(dhcp_state == STATE_DHCP_REQUEST) + { + uint32_t respId; + messageType = parseDHCPResponse(responseTimeout, respId); + if(messageType == DHCP_ACK) + { + dhcp_state = STATE_DHCP_LEASED; + result = 1; + } + else if(messageType == DHCP_NAK) + dhcp_state = STATE_DHCP_START; + } + + if(messageType == 255) + { + messageType = 0; + dhcp_state = STATE_DHCP_START; + } + + if(result != 1 && ((millis() - startTime) > timeout)) + break; + } + + // We're done with the socket now + _dhcpUdpSocket.stop(); + _dhcpTransactionId++; + + return result; +} + +void DhcpClass::presend_DHCP() +{ +} + +void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed) +{ + uint8_t buffer[32]; + memset(buffer, 0, 32); + IPAddress dest_addr( 255, 255, 255, 255 ); // Broadcast address + + if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT)) + { + // FIXME Need to return errors + return; + } + + buffer[0] = DHCP_BOOTREQUEST; // op + buffer[1] = DHCP_HTYPE10MB; // htype + buffer[2] = DHCP_HLENETHERNET; // hlen + buffer[3] = DHCP_HOPS; // hops + + // xid + unsigned long xid = htonl(_dhcpTransactionId); + memcpy(buffer + 4, &(xid), 4); + + // 8, 9 - seconds elapsed + buffer[8] = ((secondsElapsed & 0xff00) >> 8); + buffer[9] = (secondsElapsed & 0x00ff); + + // flags + unsigned short flags = htons(DHCP_FLAGSBROADCAST); + memcpy(buffer + 10, &(flags), 2); + + // ciaddr: already zeroed + // yiaddr: already zeroed + // siaddr: already zeroed + // giaddr: already zeroed + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 28); + + memset(buffer, 0, 32); // clear local buffer + + memcpy(buffer, _dhcpMacAddr, 6); // chaddr + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 16); + + memset(buffer, 0, 32); // clear local buffer + + // leave zeroed out for sname && file + // put in W5100 transmit buffer x 6 (192 bytes) + + for(int i = 0; i < 6; i++) { + _dhcpUdpSocket.write(buffer, 32); + } + + // OPT - Magic Cookie + buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24)& 0xFF); + buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16)& 0xFF); + buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8)& 0xFF); + buffer[3] = (uint8_t)(MAGIC_COOKIE& 0xFF); + + // OPT - message type + buffer[4] = dhcpMessageType; + buffer[5] = 0x01; + buffer[6] = messageType; //DHCP_REQUEST; + + // OPT - client identifier + buffer[7] = dhcpClientIdentifier; + buffer[8] = 0x07; + buffer[9] = 0x01; + memcpy(buffer + 10, _dhcpMacAddr, 6); + + // OPT - host name + buffer[16] = hostName; + buffer[17] = strlen(HOST_NAME) + 3; // length of hostname + last 3 bytes of mac address + strcpy((char*)&(buffer[18]), HOST_NAME); + + buffer[24] = _dhcpMacAddr[3]; + buffer[25] = _dhcpMacAddr[4]; + buffer[26] = _dhcpMacAddr[5]; + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 27); + + if(messageType == DHCP_REQUEST) + { + buffer[0] = dhcpRequestedIPaddr; + buffer[1] = 0x04; + buffer[2] = _dhcpLocalIp[0]; + buffer[3] = _dhcpLocalIp[1]; + buffer[4] = _dhcpLocalIp[2]; + buffer[5] = _dhcpLocalIp[3]; + + buffer[6] = dhcpServerIdentifier; + buffer[7] = 0x04; + buffer[8] = _dhcpDhcpServerIp[0]; + buffer[9] = _dhcpDhcpServerIp[1]; + buffer[10] = _dhcpDhcpServerIp[2]; + buffer[11] = _dhcpDhcpServerIp[3]; + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 12); + } + + buffer[0] = dhcpParamRequest; + buffer[1] = 0x06; + buffer[2] = subnetMask; + buffer[3] = routersOnSubnet; + buffer[4] = dns; + buffer[5] = domainName; + buffer[6] = dhcpT1value; + buffer[7] = dhcpT2value; + buffer[8] = endOption; + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 9); + + _dhcpUdpSocket.endPacket(); +} + +uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId) +{ + uint8_t type = 0; + uint8_t opt_len = 0; + + unsigned long startTime = millis(); + + while(_dhcpUdpSocket.parsePacket() <= 0) + { + if((millis() - startTime) > responseTimeout) + { + return 255; + } + delay(50); + } + // start reading in the packet + RIP_MSG_FIXED fixedMsg; + _dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED)); + + if(fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) + { + transactionId = ntohl(fixedMsg.xid); + if(memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || (transactionId < _dhcpInitialTransactionId) || (transactionId > _dhcpTransactionId)) + { + // Need to read the rest of the packet here regardless + _dhcpUdpSocket.flush(); + return 0; + } + + memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4); + + // Skip to the option part + // Doing this a byte at a time so we don't have to put a big buffer + // on the stack (as we don't have lots of memory lying around) + for (int i =0; i < (240 - sizeof(RIP_MSG_FIXED)); i++) + { + _dhcpUdpSocket.read(); // we don't care about the returned byte + } + + while (_dhcpUdpSocket.available() > 0) + { + switch (_dhcpUdpSocket.read()) + { + case endOption : + break; + + case padOption : + break; + + case dhcpMessageType : + opt_len = _dhcpUdpSocket.read(); + type = _dhcpUdpSocket.read(); + break; + + case subnetMask : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read(_dhcpSubnetMask, 4); + break; + + case routersOnSubnet : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read(_dhcpGatewayIp, 4); + break; + + case dns : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); + break; + + case dhcpServerIdentifier : + opt_len = _dhcpUdpSocket.read(); + if( *((uint32_t*)_dhcpDhcpServerIp) == 0 || + IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP() ) + { + _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp)); + } + else + { + // Skip over the rest of this option + while (opt_len--) + { + _dhcpUdpSocket.read(); + } + } + break; + + case dhcpIPaddrLeaseTime : + default : + opt_len = _dhcpUdpSocket.read(); + // Skip over the rest of this option + while (opt_len--) + { + _dhcpUdpSocket.read(); + } + break; + } + } + } + + // Need to skip to end of the packet regardless here + _dhcpUdpSocket.flush(); + + return type; +} + +IPAddress DhcpClass::getLocalIp() +{ + return IPAddress(_dhcpLocalIp); +} + +IPAddress DhcpClass::getSubnetMask() +{ + return IPAddress(_dhcpSubnetMask); +} + +IPAddress DhcpClass::getGatewayIp() +{ + return IPAddress(_dhcpGatewayIp); +} + +IPAddress DhcpClass::getDhcpServerIp() +{ + return IPAddress(_dhcpDhcpServerIp); +} + +IPAddress DhcpClass::getDnsServerIp() +{ + return IPAddress(_dhcpDnsServerIp); +} + diff --git a/libraries/Ethernet/Dhcp.h b/libraries/Ethernet/Dhcp.h new file mode 100755 index 00000000000..c0034940c27 --- /dev/null +++ b/libraries/Ethernet/Dhcp.h @@ -0,0 +1,158 @@ +// DHCP Library v0.3 - April 25, 2009 +// Author: Jordan Terrell - blog.jordanterrell.com + +#ifndef Dhcp_h +#define Dhcp_h + +#include "Udp.h" + +/* DHCP state machine. */ +#define STATE_DHCP_START 0 +#define STATE_DHCP_DISCOVER 1 +#define STATE_DHCP_REQUEST 2 +#define STATE_DHCP_LEASED 3 +#define STATE_DHCP_REREQUEST 4 +#define STATE_DHCP_RELEASE 5 + +#define DHCP_FLAGSBROADCAST 0x8000 + +/* UDP port numbers for DHCP */ +#define DHCP_SERVER_PORT 67 /* from server to client */ +#define DHCP_CLIENT_PORT 68 /* from client to server */ + +/* DHCP message OP code */ +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/* DHCP message type */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +#define DHCP_HTYPE10MB 1 +#define DHCP_HTYPE100MB 2 + +#define DHCP_HLENETHERNET 6 +#define DHCP_HOPS 0 +#define DHCP_SECS 0 + +#define MAGIC_COOKIE 0x63825363 +#define MAX_DHCP_OPT 16 + +#define HOST_NAME "WIZnet" + +enum +{ + padOption = 0, + subnetMask = 1, + timerOffset = 2, + routersOnSubnet = 3, + /* timeServer = 4, + nameServer = 5,*/ + dns = 6, + /*logServer = 7, + cookieServer = 8, + lprServer = 9, + impressServer = 10, + resourceLocationServer = 11,*/ + hostName = 12, + /*bootFileSize = 13, + meritDumpFile = 14,*/ + domainName = 15, + /*swapServer = 16, + rootPath = 17, + extentionsPath = 18, + IPforwarding = 19, + nonLocalSourceRouting = 20, + policyFilter = 21, + maxDgramReasmSize = 22, + defaultIPTTL = 23, + pathMTUagingTimeout = 24, + pathMTUplateauTable = 25, + ifMTU = 26, + allSubnetsLocal = 27, + broadcastAddr = 28, + performMaskDiscovery = 29, + maskSupplier = 30, + performRouterDiscovery = 31, + routerSolicitationAddr = 32, + staticRoute = 33, + trailerEncapsulation = 34, + arpCacheTimeout = 35, + ethernetEncapsulation = 36, + tcpDefaultTTL = 37, + tcpKeepaliveInterval = 38, + tcpKeepaliveGarbage = 39, + nisDomainName = 40, + nisServers = 41, + ntpServers = 42, + vendorSpecificInfo = 43, + netBIOSnameServer = 44, + netBIOSdgramDistServer = 45, + netBIOSnodeType = 46, + netBIOSscope = 47, + xFontServer = 48, + xDisplayManager = 49,*/ + dhcpRequestedIPaddr = 50, + dhcpIPaddrLeaseTime = 51, + /*dhcpOptionOverload = 52,*/ + dhcpMessageType = 53, + dhcpServerIdentifier = 54, + dhcpParamRequest = 55, + /*dhcpMsg = 56, + dhcpMaxMsgSize = 57,*/ + dhcpT1value = 58, + dhcpT2value = 59, + /*dhcpClassIdentifier = 60,*/ + dhcpClientIdentifier = 61, + endOption = 255 +}; + +typedef struct _RIP_MSG_FIXED +{ + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint8_t ciaddr[4]; + uint8_t yiaddr[4]; + uint8_t siaddr[4]; + uint8_t giaddr[4]; + uint8_t chaddr[6]; +}RIP_MSG_FIXED; + +class DhcpClass { +private: + uint32_t _dhcpInitialTransactionId; + uint32_t _dhcpTransactionId; + uint8_t _dhcpMacAddr[6]; + uint8_t _dhcpLocalIp[4]; + uint8_t _dhcpSubnetMask[4]; + uint8_t _dhcpGatewayIp[4]; + uint8_t _dhcpDhcpServerIp[4]; + uint8_t _dhcpDnsServerIp[4]; + UDP _dhcpUdpSocket; + + void presend_DHCP(); + void send_DHCP_MESSAGE(uint8_t, uint16_t); + + uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId); +public: + IPAddress getLocalIp(); + IPAddress getSubnetMask(); + IPAddress getGatewayIp(); + IPAddress getDhcpServerIp(); + IPAddress getDnsServerIp(); + + int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); +}; + +#endif diff --git a/libraries/Ethernet/Ethernet.cpp b/libraries/Ethernet/Ethernet.cpp index 91cbacd3966..937f5b4f9a1 100644 --- a/libraries/Ethernet/Ethernet.cpp +++ b/libraries/Ethernet/Ethernet.cpp @@ -1,5 +1,6 @@ #include "w5100.h" #include "Ethernet.h" +#include "Dhcp.h" // XXX: don't make assumptions about the value of MAX_SOCK_NUM. uint8_t EthernetClass::_state[MAX_SOCK_NUM] = { @@ -7,30 +8,78 @@ uint8_t EthernetClass::_state[MAX_SOCK_NUM] = { uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = { 0, 0, 0, 0 }; -void EthernetClass::begin(uint8_t *mac, uint8_t *ip) +int EthernetClass::begin(uint8_t *mac_address) { - uint8_t gateway[4]; - gateway[0] = ip[0]; - gateway[1] = ip[1]; - gateway[2] = ip[2]; + DhcpClass dhcp; + + // Initialise the basic info + W5100.init(); + W5100.setMACAddress(mac_address); + W5100.setIPAddress(IPAddress(0,0,0,0).raw_address()); + + // Now try to get our config info from a DHCP server + int ret = dhcp.beginWithDHCP(mac_address); + if(ret == 1) + { + // We've successfully found a DHCP server and got our configuration info, so set things + // accordingly + W5100.setIPAddress(dhcp.getLocalIp().raw_address()); + W5100.setGatewayIp(dhcp.getGatewayIp().raw_address()); + W5100.setSubnetMask(dhcp.getSubnetMask().raw_address()); + _dnsServerAddress = dhcp.getDnsServerIp(); + } + + return ret; +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip) +{ + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = local_ip; gateway[3] = 1; - begin(mac, ip, gateway); + begin(mac_address, local_ip, gateway); } -void EthernetClass::begin(uint8_t *mac, uint8_t *ip, uint8_t *gateway) +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress gateway) { - uint8_t subnet[] = { - 255, 255, 255, 0 }; - begin(mac, ip, gateway, subnet); + IPAddress subnet(255, 255, 255, 0); + begin(mac_address, local_ip, gateway, subnet); } -void EthernetClass::begin(uint8_t *mac, uint8_t *ip, uint8_t *gateway, uint8_t *subnet) +void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress gateway, IPAddress subnet) { W5100.init(); W5100.setMACAddress(mac); - W5100.setIPAddress(ip); - W5100.setGatewayIp(gateway); - W5100.setSubnetMask(subnet); + W5100.setIPAddress(local_ip._address); + W5100.setGatewayIp(gateway._address); + W5100.setSubnetMask(subnet._address); +} + +IPAddress EthernetClass::localIP() +{ + IPAddress ret; + W5100.getIPAddress(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::subnetMask() +{ + IPAddress ret; + W5100.getSubnetMask(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::gatewayIP() +{ + IPAddress ret; + W5100.getGatewayIp(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::dnsServerIP() +{ + return _dnsServerAddress; } EthernetClass Ethernet; diff --git a/libraries/Ethernet/Ethernet.h b/libraries/Ethernet/Ethernet.h index a91f1aa177b..fdf0b7f39b7 100644 --- a/libraries/Ethernet/Ethernet.h +++ b/libraries/Ethernet/Ethernet.h @@ -3,6 +3,7 @@ #include //#include "w5100.h" +#include "IPAddress.h" #include "Client.h" #include "Server.h" @@ -10,12 +11,23 @@ class EthernetClass { private: + IPAddress _dnsServerAddress; public: static uint8_t _state[MAX_SOCK_NUM]; static uint16_t _server_port[MAX_SOCK_NUM]; - void begin(uint8_t *, uint8_t *); - void begin(uint8_t *, uint8_t *, uint8_t *); - void begin(uint8_t *, uint8_t *, uint8_t *, uint8_t *); + // Initialise the Ethernet shield to use the provided MAC address and gain the rest of the + // configuration through DHCP. + // Returns 0 if the DHCP configuration failed, and 1 if it succeeded + int begin(uint8_t *mac_address); + void begin(uint8_t *mac_address, IPAddress local_ip); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress gateway); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress gateway, IPAddress subnet); + + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP(); + friend class Client; friend class Server; }; diff --git a/libraries/Ethernet/IPAddress.cpp b/libraries/Ethernet/IPAddress.cpp new file mode 100644 index 00000000000..408d518a0e7 --- /dev/null +++ b/libraries/Ethernet/IPAddress.cpp @@ -0,0 +1,44 @@ + +#include +#include + +IPAddress::IPAddress() +{ + memset(_address, 0, sizeof(_address)); +} + +IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) +{ + _address[0] = first_octet; + _address[1] = second_octet; + _address[2] = third_octet; + _address[3] = fourth_octet; +} + +IPAddress::IPAddress(uint32_t address) +{ + memcpy(_address, &address, sizeof(_address)); +} + +IPAddress::IPAddress(const uint8_t *address) +{ + memcpy(_address, address, sizeof(_address)); +} + +IPAddress& IPAddress::operator=(const uint8_t *address) +{ + memcpy(_address, address, sizeof(_address)); + return *this; +} + +IPAddress& IPAddress::operator=(uint32_t address) +{ + memcpy(_address, (const uint8_t *)&address, sizeof(_address)); + return *this; +} + +bool IPAddress::operator==(const uint8_t* addr) +{ + return memcmp(addr, _address, sizeof(_address)) == 0; +} + diff --git a/libraries/Ethernet/IPAddress.h b/libraries/Ethernet/IPAddress.h new file mode 100644 index 00000000000..1d19bbd222b --- /dev/null +++ b/libraries/Ethernet/IPAddress.h @@ -0,0 +1,68 @@ +/* + * + * MIT License: + * Copyright (c) 2011 Adrian McEwen + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * adrianm@mcqn.com 1/1/2011 + */ + +#ifndef IPAddress_h +#define IPAddress_h + +// A class to make it easier to handle and pass around IP addresses + +class IPAddress { +private: + uint8_t _address[4]; // IPv4 address + // Access the raw byte array containing the address. Because this returns a pointer + // to the internal structure rather than a copy of the address this function should only + // be used when you know that the usage of the returned uint8_t* will be transient and not + // stored. + uint8_t* raw_address() { return _address; }; + +public: + // Constructors + IPAddress(); + IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); + IPAddress(uint32_t address); + IPAddress(const uint8_t *address); + + // Overloaded cast operator to allow IPAddress objects to be used where a pointer + // to a four-byte uint8_t array is expected + operator uint32_t() { return *((uint32_t*)_address); }; + bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); }; + bool operator==(const uint8_t* addr); + + // Overloaded index operator to allow getting and setting individual octets of the address + uint8_t operator[](int index) const { return _address[index]; }; + uint8_t& operator[](int index) { return _address[index]; }; + + // Overloaded copy operators to allow initialisation of IPAddress objects from other types + IPAddress& operator=(const uint8_t *address); + IPAddress& operator=(uint32_t address); + + friend class EthernetClass; + friend class UDP; + friend class Client; + friend class Server; + friend class DhcpClass; +}; + +#endif diff --git a/libraries/Ethernet/Udp.cpp b/libraries/Ethernet/Udp.cpp index 07c454df228..a8c98c3f49b 100644 --- a/libraries/Ethernet/Udp.cpp +++ b/libraries/Ethernet/Udp.cpp @@ -56,106 +56,111 @@ uint8_t UDP::begin(uint16_t port) { return 1; } -/* Send packet contained in buf of length len to peer at specified ip, and port */ -/* Use this function to transmit binary data that might contain 0x00 bytes*/ -/* This function returns sent data size for success else -1. */ -uint16_t UDP::sendPacket(uint8_t * buf, uint16_t len, uint8_t * ip, uint16_t port){ - return sendto(_sock,(const uint8_t *)buf,len,ip,port); -} - -/* Send zero-terminated string str as packet to peer at specified ip, and port */ -/* This function returns sent data size for success else -1. */ -uint16_t UDP::sendPacket(const char str[], uint8_t * ip, uint16_t port){ - // compute strlen - const char *s; - for(s = str; *s; ++s); - uint16_t len = (s-str); - // send packet - return sendto(_sock,(const uint8_t *)str,len,ip,port); -} /* Is data available in rx buffer? Returns 0 if no, number of available bytes if yes. * returned value includes 8 byte UDP header!*/ int UDP::available() { return W5100.getRXReceivedSize(_sock); } +/* Release any resources being used by this UDP instance */ +void UDP::stop() +{ + if (_sock == MAX_SOCK_NUM) + return; -/* Read a received packet into buffer buf (which is of maximum length len); */ -/* store calling ip and port as well. Call available() to make sure data is ready first. */ -/* NOTE: I don't believe len is ever checked in implementation of recvfrom(),*/ -/* so it's easy to overflow buffer. so we check and truncate. */ -/* returns number of bytes read, or negative number of bytes we would have needed if we truncated */ -int UDP::readPacket(uint8_t * buf, uint16_t bufLen, uint8_t *ip, uint16_t *port) { - int packetLen = available()-8; //skip UDP header; - if(packetLen < 0 ) return 0; // no real data here - if(packetLen > (int)bufLen) { - //packet is too large - truncate - //HACK - hand-parse the UDP packet using TCP recv method - uint8_t tmpBuf[8]; - int i; - //read 8 header bytes and get IP and port from it - recv(_sock,tmpBuf,8); - ip[0] = tmpBuf[0]; - ip[1] = tmpBuf[1]; - ip[2] = tmpBuf[2]; - ip[3] = tmpBuf[3]; - *port = tmpBuf[4]; - *port = (*port << 8) + tmpBuf[5]; - - //now copy first (bufLen) bytes into buf - for(i=0;i<(int)bufLen;i++) { - recv(_sock,tmpBuf,1); - buf[i]=tmpBuf[0]; - } + close(_sock); - //and just read the rest byte by byte and throw it away - while(available()) { - recv(_sock,tmpBuf,1); - } + EthernetClass::_server_port[_sock] = 0; + _sock = MAX_SOCK_NUM; +} - return (-1*packetLen); +int UDP::beginPacket(IPAddress ip, uint16_t port) +{ + _offset = 0; + return startUDP(_sock, ip.raw_address(), port); +} - //ALTERNATIVE: requires stdlib - takes a bunch of space - /*//create new buffer and read everything into it - uint8_t * tmpBuf = (uint8_t *)malloc(packetLen); - recvfrom(_sock,tmpBuf,packetLen,ip,port); - if(!tmpBuf) return 0; //couldn't allocate - // copy first bufLen bytes - for(unsigned int i=0; i 0) + { + _remoteIP = tmpBuf; + _remotePort = tmpBuf[4]; + _remotePort = (_remotePort << 8) + tmpBuf[5]; + // When we get here, any remaining bytes are the data + ret = available(); + } + return ret; } -/* Release any resources being used by this UDP instance */ -void UDP::stop() +int UDP::read() { - if (_sock == MAX_SOCK_NUM) - return; + uint8_t byte; + if (recv(_sock, &byte, 1) > 0) + { + // We read things without any problems + return byte; + } + // If we get here, there's no data available + return -1; +} - close(_sock); +int UDP::read(unsigned char* buffer, size_t len) +{ + /* In the readPacket that copes with truncating packets, the buffer was + filled with this code. Not sure why it loops round reading out a byte + at a time. + int i; + for(i=0;i<(int)bufLen;i++) { + recv(_sock,tmpBuf,1); + buf[i]=tmpBuf[0]; + } + */ + return recv(_sock, buffer, len); +} - EthernetClass::_server_port[_sock] = 0; - _sock = MAX_SOCK_NUM; +int UDP::peek() +{ + uint8_t b; + // Unlike recv, peek doesn't check to see if there's any data available, so we must + if (!available()) + return -1; + ::peek(_sock, &b); + return b; +} + +void UDP::flush() +{ + while (available()) + { + read(); + } } diff --git a/libraries/Ethernet/Udp.h b/libraries/Ethernet/Udp.h index c801ee277cd..8cd3b065a1b 100644 --- a/libraries/Ethernet/Udp.h +++ b/libraries/Ethernet/Udp.h @@ -37,28 +37,60 @@ #ifndef udp_h #define udp_h +#include +#include + #define UDP_TX_PACKET_MAX_SIZE 24 -class UDP { +class UDP : public Stream { private: uint8_t _sock; // socket ID for Wiz5100 uint16_t _port; // local port to listen on + IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed + uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed + uint16_t _offset; // offset into the packet being sent public: - UDP(); + UDP(); // Constructor uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use - int available(); // has data been received? + void stop(); // Finish with the UDP socket + + // Sending UDP packets + + // Start building up a packet to send to the remote host specific in ip and port + // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port + int beginPacket(IPAddress ip, uint16_t port); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + int endPacket(); + // Write a single byte into the packet + virtual void write(uint8_t); + // Write a string of characters into the packet + virtual void write(const char *str); + // Write size bytes from buffer into the packet + virtual void write(const uint8_t *buffer, size_t size); - // C-style buffer-oriented functions - uint16_t sendPacket(uint8_t *, uint16_t, uint8_t *, uint16_t); //send a packet to specified peer - uint16_t sendPacket(const char[], uint8_t *, uint16_t); //send a string as a packet to specified peer - int readPacket(uint8_t *, uint16_t); // read a received packet - int readPacket(uint8_t *, uint16_t, uint8_t *, uint16_t *); // read a received packet, also return sender's ip and port - // readPacket that fills a character string buffer - int readPacket(char *, uint16_t, uint8_t *, uint16_t &); + // Start processing the next available incoming packet + // Returns the size of the packet in bytes, or 0 if no packets are available + int parsePacket(); + // Number of bytes remaining in the current packet + virtual int available(); + // Read a single byte from the current packet + virtual int read(); + // Read up to len bytes from the current packet and place them into buffer + // Returns the number of bytes read, or 0 if none are available + virtual int read(unsigned char* buffer, size_t len); + // Read up to len characters from the current packet and place them into buffer + // Returns the number of characters read, or 0 if none are available + virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); }; + // Return the next byte from the current packet without moving on to the next byte + virtual int peek(); + virtual void flush(); // Finish reading the current packet - // Finish with the UDP socket - void stop(); + // Return the IP address of the host who sent the current incoming packet + IPAddress remoteIP() { return _remoteIP; }; + // Return the port of the host who sent the current incoming packet + uint16_t remotePort() { return _remotePort; }; }; #endif diff --git a/libraries/Ethernet/examples/BarometricPressureWebServer/BarometricPressureWebServer.pde b/libraries/Ethernet/examples/BarometricPressureWebServer/BarometricPressureWebServer.pde index a58433fed5b..3f43d96dbc0 100644 --- a/libraries/Ethernet/examples/BarometricPressureWebServer/BarometricPressureWebServer.pde +++ b/libraries/Ethernet/examples/BarometricPressureWebServer/BarometricPressureWebServer.pde @@ -31,12 +31,9 @@ byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // assign an IP address for the controller: -byte ip[] = { - 192,168,1,20 }; -byte gateway[] = { - 192,168,1,1}; -byte subnet[] = { - 255, 255, 255, 0 }; +IPAddress ip(192,168,1,20); +IPAddress gateway(192,168,1,1); +IPAddress subnet(255, 255, 255, 0); // Initialize the Ethernet server library diff --git a/libraries/Ethernet/examples/ChatServer/ChatServer.pde b/libraries/Ethernet/examples/ChatServer/ChatServer.pde index 50ae01408b8..8267a5dd45c 100644 --- a/libraries/Ethernet/examples/ChatServer/ChatServer.pde +++ b/libraries/Ethernet/examples/ChatServer/ChatServer.pde @@ -24,9 +24,9 @@ // The IP address will be dependent on your local network. // gateway and subnet are optional: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { 192,168,1, 177 }; -byte gateway[] = { 192,168,1, 1 }; -byte subnet[] = { 255, 255, 0, 0 }; +IPAddress ip(192,168,1, 177); +IPAddress gateway(192,168,1, 1); +IPAddress subnet(255, 255, 0, 0); // telnet defaults to port 23 Server server(23); @@ -60,4 +60,4 @@ void loop() { // echo the bytes to the server as well: Serial.print(thisChar); } -} \ No newline at end of file +} diff --git a/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde b/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde index fe94541e127..687fa437e65 100644 --- a/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde +++ b/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde @@ -23,20 +23,13 @@ #include // assign a MAC address for the ethernet controller. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield // fill in your address here: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; -// assign an IP address for the controller: -byte ip[] = { - 192,169,1,20 }; -byte gateway[] = { - 192,168,1,1}; -byte subnet[] = { - 255, 255, 255, 0 }; // The address of the server you want to connect to (pachube.com): -byte server[] = { - 209,40,205,190 }; +IPAddress server(209,40,205,190); // initialize the library instance: Client client(server, 80); @@ -46,9 +39,15 @@ boolean lastConnected = false; // state of the connection last time through const int postingInterval = 10000; //delay between updates to Pachube.com void setup() { - // start the ethernet connection and serial port: - Ethernet.begin(mac, ip); + // start serial port: Serial.begin(9600); + // start the Ethernet connection: + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + for(;;) + ; + } // give the ethernet module time to boot up: delay(1000); } diff --git a/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde b/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde index 225823ac16f..0db6e219a38 100644 --- a/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde +++ b/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde @@ -29,16 +29,12 @@ byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // assign an IP address for the controller: -byte ip[] = { - 192,169,1,20 }; -byte gateway[] = { - 192,168,1,1}; -byte subnet[] = { - 255, 255, 255, 0 }; +IPAddress ip(192,169,1,20); +IPAddress gateway(192,168,1,1); +IPAddress subnet(255, 255, 255, 0); // The address of the server you want to connect to (pachube.com): -byte server[] = { - 209,40,205,190 }; +IPAddress server(209,40,205,190); // initialize the library instance: Client client(server, 80); diff --git a/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde b/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde index aca1fa9adc5..95756d07692 100644 --- a/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde +++ b/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde @@ -24,12 +24,10 @@ // The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { - 192,168,1,177 }; +IPAddress ip(192,168,1,177); // Enter the IP address of the server you're connecting to: -byte server[] = { - 1,1,1,1 }; +IPAddress server(1,1,1,1); // Initialize the Ethernet client library // with the IP address and port of the server diff --git a/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde b/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde index f27290f5424..081d6911485 100644 --- a/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde +++ b/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde @@ -22,15 +22,10 @@ // The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { - 192,168,1,177 }; +IPAddress ip(192, 168, 1, 177); unsigned int localPort = 8888; // local port to listen on -// the next two variables are set when a packet is received -byte remoteIp[4]; // holds received packet's originating IP -unsigned int remotePort; // holds received packet's originating port - // buffers for receiving and sending data char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, char ReplyBuffer[] = "acknowledged"; // a string to send back @@ -48,19 +43,33 @@ void setup() { void loop() { // if there's data available, read a packet - int packetSize = Udp.available(); // note that this includes the UDP header + int packetSize = Udp.parsePacket(); if(packetSize) { - packetSize = packetSize - 8; // subtract the 8 byte header Serial.print("Received packet of size "); Serial.println(packetSize); - - // read the packet into packetBufffer and get the senders IP addr and port number - Udp.readPacket(packetBuffer,UDP_TX_PACKET_MAX_SIZE, remoteIp, remotePort); + Serial.print("From "); + IPAddress remote = Udp.remoteIP(); + for (int i =0; i < 4; i++) + { + Serial.print(remote[i], DEC); + if (i < 3) + { + Serial.print("."); + } + } + Serial.print(", port "); + Serial.println(Udp.remotePort()); + + // read the packet into packetBufffer + Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE); Serial.println("Contents:"); Serial.println(packetBuffer); - Udp.sendPacket( ReplyBuffer, remoteIp, remotePort); + // send a reply, to the IP address and port that sent us the packet we received + Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); + Udp.write(ReplyBuffer); + Udp.endPacket(); } delay(10); } diff --git a/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde b/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde index 22bc754c566..7c2d3ea9e97 100644 --- a/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde +++ b/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde @@ -20,18 +20,14 @@ #include #include -// Enter a MAC address and IP address for your controller below. -// The IP address will be dependent on your local network: +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { - 192,168,1,177 }; - unsigned int localPort = 8888; // local port to listen for UDP packets -byte timeServer[] = { - 192, 43, 244, 18}; // time.nist.gov NTP server +IPAddress timeServer(192, 43, 244, 18); // time.nist.gov NTP server const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message @@ -42,11 +38,16 @@ UDP Udp; void setup() { + Serial.begin(9600); + // start Ethernet and UDP - Ethernet.begin(mac,ip); + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + for(;;) + ; + } Udp.begin(localPort); - - Serial.begin(9600); } void loop() @@ -55,8 +56,9 @@ void loop() // wait to see if a reply is available delay(1000); - if ( Udp.available() ) { - Udp.readPacket(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer + if ( Udp.parsePacket() ) { + // We've received a packet, read the data from it + Udp.read(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer //the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, esxtract the two words: @@ -100,7 +102,7 @@ void loop() } // send an NTP request to the time server at the given address -unsigned long sendNTPpacket(byte *address) +unsigned long sendNTPpacket(IPAddress& address) { // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); @@ -118,7 +120,9 @@ unsigned long sendNTPpacket(byte *address) // all NTP fields have been given values, now // you can send a packet requesting a timestamp: - Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE, address, 123); //NTP requests are to port 123 + Udp.beginPacket(address, 123); //NTP requests are to port 123 + Udp.write(packetBuffer,NTP_PACKET_SIZE); + Udp.endPacket(); } diff --git a/libraries/Ethernet/examples/WebClient/WebClient.pde b/libraries/Ethernet/examples/WebClient/WebClient.pde index a2d3503115e..74d34e0cecd 100644 --- a/libraries/Ethernet/examples/WebClient/WebClient.pde +++ b/libraries/Ethernet/examples/WebClient/WebClient.pde @@ -15,11 +15,10 @@ #include #include -// Enter a MAC address and IP address for your controller below. -// The IP address will be dependent on your local network: +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { 192,168,1,177 }; -byte server[] = { 173,194,33,104 }; // Google +IPAddress server(173,194,33,104); // Google // Initialize the Ethernet client library // with the IP address and port of the server @@ -27,10 +26,15 @@ byte server[] = { 173,194,33,104 }; // Google Client client(server, 80); void setup() { - // start the Ethernet connection: - Ethernet.begin(mac, ip); // start the serial library: Serial.begin(9600); + // start the Ethernet connection: + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + for(;;) + ; + } // give the Ethernet shield a second to initialize: delay(1000); Serial.println("connecting..."); diff --git a/libraries/Ethernet/examples/WebServer/WebServer.pde b/libraries/Ethernet/examples/WebServer/WebServer.pde index 77bcb204ed8..c69a56a4040 100644 --- a/libraries/Ethernet/examples/WebServer/WebServer.pde +++ b/libraries/Ethernet/examples/WebServer/WebServer.pde @@ -21,7 +21,7 @@ // Enter a MAC address and IP address for your controller below. // The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { 192,168,1, 177 }; +IPAddress ip(192,168,1, 177); // Initialize the Ethernet server library // with the IP address and port you want to use @@ -79,4 +79,4 @@ void loop() // close the connection: client.stop(); } -} \ No newline at end of file +} diff --git a/libraries/Ethernet/keywords.txt b/libraries/Ethernet/keywords.txt index ebc57936394..7fdcedf090e 100644 --- a/libraries/Ethernet/keywords.txt +++ b/libraries/Ethernet/keywords.txt @@ -9,6 +9,7 @@ Ethernet KEYWORD1 Client KEYWORD1 Server KEYWORD1 +IPAddress KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -19,10 +20,16 @@ connect KEYWORD2 write KEYWORD2 available KEYWORD2 read KEYWORD2 +peek KEYWORD2 flush KEYWORD2 stop KEYWORD2 connected KEYWORD2 begin KEYWORD2 +beginPacket KEYWORD2 +endPacket KEYWORD2 +parsePacket KEYWORD2 +remoteIP KEYWORD2 +remotePort KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/libraries/Ethernet/util.h b/libraries/Ethernet/util.h new file mode 100644 index 00000000000..220011b8fd4 --- /dev/null +++ b/libraries/Ethernet/util.h @@ -0,0 +1,13 @@ +#ifndef UTIL_H +#define UTIL_H + +#define htons(x) ( (x)<<8 | ((x)>>8)&0xFF ) +#define ntohs(x) htons(x) + +#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ + ((x)<< 8 & 0x00FF0000UL) | \ + ((x)>> 8 & 0x0000FF00UL) | \ + ((x)>>24 & 0x000000FFUL) ) +#define ntohl(x) htonl(x) + +#endif diff --git a/libraries/Ethernet/utility/socket.cpp b/libraries/Ethernet/utility/socket.cpp index cad54a5e379..487584511e4 100644 --- a/libraries/Ethernet/utility/socket.cpp +++ b/libraries/Ethernet/utility/socket.cpp @@ -344,3 +344,58 @@ uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len) return ret; } +uint16_t bufferData(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len) +{ + uint16_t ret =0; + if (len > W5100.getTXFreeSize(s)) + { + ret = W5100.getTXFreeSize(s); // check size not to exceed MAX size. + } + else + { + ret = len; + } + W5100.send_data_processing_offset(s, offset, buf, ret); + return ret; +} + +int startUDP(SOCKET s, uint8_t* addr, uint16_t port) +{ + if + ( + ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) || + ((port == 0x00)) + ) + { + return 0; + } + else + { + W5100.writeSnDIPR(s, addr); + W5100.writeSnDPORT(s, port); + return 1; + } +} + +int sendUDP(SOCKET s) +{ + W5100.execCmdSn(s, Sock_SEND); + + /* +2008.01 bj */ + while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) + { + if (W5100.readSnIR(s) & SnIR::TIMEOUT) + { + /* +2008.01 [bj]: clear interrupt */ + W5100.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT)); + return 0; + } + } + + /* +2008.01 bj */ + W5100.writeSnIR(s, SnIR::SEND_OK); + + /* Sent ok */ + return 1; +} + diff --git a/libraries/Ethernet/utility/socket.h b/libraries/Ethernet/utility/socket.h index 48e2d877b73..37ea93f3d75 100755 --- a/libraries/Ethernet/utility/socket.h +++ b/libraries/Ethernet/utility/socket.h @@ -16,5 +16,26 @@ extern uint16_t recvfrom(SOCKET s, uint8_t * buf, uint16_t len, uint8_t * addr, extern uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len); +// Functions to allow buffered UDP send (i.e. where the UDP datagram is built up over a +// number of calls before being sent +/* + @brief This function sets up a UDP datagram, the data for which will be provided by one + or more calls to bufferData and then finally sent with sendUDP. + @return 1 if the datagram was successfully set up, or 0 if there was an error +*/ +extern int startUDP(SOCKET s, uint8_t* addr, uint16_t port); +/* + @brief This function copies up to len bytes of data from buf into a UDP datagram to be + sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls. + @return Number of bytes successfully buffered +*/ +uint16_t bufferData(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len); +/* + @brief Send a UDP datagram built up from a sequence of startUDP followed by one or more + calls to bufferData. + @return 1 if the datagram was successfully sent, or 0 if there was an error +*/ +int sendUDP(SOCKET s); + #endif /* _SOCKET_H_ */ diff --git a/libraries/Ethernet/utility/w5100.cpp b/libraries/Ethernet/utility/w5100.cpp index aaf3071f7d0..aafabaeec7b 100644 --- a/libraries/Ethernet/utility/w5100.cpp +++ b/libraries/Ethernet/utility/w5100.cpp @@ -65,10 +65,16 @@ uint16_t W5100Class::getRXReceivedSize(SOCKET s) } -void W5100Class::send_data_processing(SOCKET s, uint8_t *data, uint16_t len) +void W5100Class::send_data_processing(SOCKET s, const uint8_t *data, uint16_t len) { - uint16_t ptr = readSnTX_WR(s); + // This is same as having no offset in a call to send_data_processing_offset + send_data_processing_offset(s, 0, data, len); +} +void W5100Class::send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len) +{ + uint16_t ptr = readSnTX_WR(s); + ptr += data_offset; uint16_t offset = ptr & SMASK; uint16_t dstAddr = offset + SBASE[s]; @@ -132,7 +138,7 @@ uint8_t W5100Class::write(uint16_t _addr, uint8_t _data) return 1; } -uint16_t W5100Class::write(uint16_t _addr, uint8_t *_buf, uint16_t _len) +uint16_t W5100Class::write(uint16_t _addr, const uint8_t *_buf, uint16_t _len) { for (int i=0; i<_len; i++) { diff --git a/libraries/Ethernet/utility/w5100.h b/libraries/Ethernet/utility/w5100.h index 118e5d80d43..9872c7ccb09 100755 --- a/libraries/Ethernet/utility/w5100.h +++ b/libraries/Ethernet/utility/w5100.h @@ -146,7 +146,19 @@ class W5100Class { * This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer * register. User should read upper byte first and lower byte later to get proper value. */ - void send_data_processing(SOCKET s, uint8_t *data, uint16_t len); + void send_data_processing(SOCKET s, const uint8_t *data, uint16_t len); + /** + * @brief A copy of send_data_processing that uses the provided ptr for the + * write offset. Only needed for the "streaming" UDP API, where + * a single UDP packet is built up over a number of calls to + * send_data_processing_ptr, because TX_WR doesn't seem to get updated + * correctly in those scenarios + * @param ptr value to use in place of TX_WR. If 0, then the value is read + * in from TX_WR + * @return New value for ptr, to be used in the next call + */ +// FIXME Update documentation + void send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len); /** * @brief This function is being called by recv() also. @@ -182,7 +194,7 @@ class W5100Class { // --------------- private: static uint8_t write(uint16_t _addr, uint8_t _data); - static uint16_t write(uint16_t addr, uint8_t *buf, uint16_t len); + static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len); static uint8_t read(uint16_t addr); static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len);