Fix for issue 439. UDP API changed to derive from Stream. The old sendPacket and readPacket calls have been removed, and replaced with Stream-derived alternatives which provide more commonality with other communications classes and to allow both buffered and full-packet-at-a-time uses. Also includes the introduction of an IPAddress class to make passing them around easier (and require fewer pointers to be exposed)

This commit is contained in:
amcewen 2011-01-10 14:54:29 +00:00
parent 5009fc15fa
commit 88e858f6e3
11 changed files with 359 additions and 120 deletions

View File

@ -0,0 +1,39 @@
#include <WProgram.h>
#include <IPAddress.h>
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;
}

View File

@ -0,0 +1,55 @@
/*
*
* 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
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 uint8_t*() { return _address; };
// 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);
};
#endif

View File

@ -56,97 +56,12 @@ uint8_t UDP::begin(uint16_t port) {
return 1; 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. /* Is data available in rx buffer? Returns 0 if no, number of available bytes if yes.
* returned value includes 8 byte UDP header!*/ * returned value includes 8 byte UDP header!*/
int UDP::available() { int UDP::available() {
return W5100.getRXReceivedSize(_sock); return W5100.getRXReceivedSize(_sock);
} }
/* 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];
}
//and just read the rest byte by byte and throw it away
while(available()) {
recv(_sock,tmpBuf,1);
}
return (-1*packetLen);
//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<bufLen; i++) {
buf[i]=tmpBuf[i];
}
//free temp buffer
free(tmpBuf);
*/
}
return recvfrom(_sock,buf,bufLen,ip,port);
}
/* Read a received packet, throw away peer's ip and port. See note above. */
int UDP::readPacket(uint8_t * buf, uint16_t len) {
uint8_t ip[4];
uint16_t port[1];
return recvfrom(_sock,buf,len,ip,port);
}
int UDP::readPacket(char * buf, uint16_t bufLen, uint8_t *ip, uint16_t &port) {
uint16_t myPort;
uint16_t ret = readPacket( (byte*)buf, bufLen, ip, &myPort);
port = myPort;
return ret;
}
/* Release any resources being used by this UDP instance */ /* Release any resources being used by this UDP instance */
void UDP::stop() void UDP::stop()
{ {
@ -159,3 +74,93 @@ void UDP::stop()
_sock = MAX_SOCK_NUM; _sock = MAX_SOCK_NUM;
} }
int UDP::beginPacket(IPAddress ip, uint16_t port)
{
_offset = 0;
return startUDP(_sock, ip, port);
}
int UDP::endPacket()
{
return sendUDP(_sock);
}
void UDP::write(uint8_t byte)
{
write(&byte, 1);
}
void UDP::write(const char *str)
{
size_t len = strlen(str);
write((const uint8_t *)str, len);
}
void UDP::write(const uint8_t *buffer, size_t size)
{
uint16_t bytes_written = bufferData(_sock, _offset, buffer, size);
_offset += bytes_written;
}
int UDP::parsePacket()
{
//HACK - hand-parse the UDP packet using TCP recv method
uint8_t tmpBuf[8];
int ret =0;
//read 8 header bytes and get IP and port from it
ret = recv(_sock,tmpBuf,8);
if (ret > 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;
}
int UDP::read()
{
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;
}
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);
}
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();
}
}

View File

@ -37,28 +37,57 @@
#ifndef udp_h #ifndef udp_h
#define udp_h #define udp_h
#include <Stream.h>
#include <IPAddress.h>
#define UDP_TX_PACKET_MAX_SIZE 24 #define UDP_TX_PACKET_MAX_SIZE 24
class UDP { class UDP : public Stream {
private: private:
uint8_t _sock; // socket ID for Wiz5100 uint8_t _sock; // socket ID for Wiz5100
uint16_t _port; // local port to listen on 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: 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 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
// C-style buffer-oriented functions // Sending UDP packets
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 // Start building up a packet to send to the remote host specific in ip and port
int readPacket(uint8_t *, uint16_t); // read a received packet // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
int readPacket(uint8_t *, uint16_t, uint8_t *, uint16_t *); // read a received packet, also return sender's ip and port int beginPacket(IPAddress ip, uint16_t port);
// readPacket that fills a character string buffer // Finish off this packet and send it
int readPacket(char *, uint16_t, uint8_t *, uint16_t &); // 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);
// Finish with the UDP socket // Start processing the next available incoming packet
void stop(); // 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);
// Return the next byte from the current packet without moving on to the next byte
virtual int peek();
virtual void flush(); // Finish reading the current packet
// Return the IP address of the host who sent the current incoming packet
IPAddress remoteIP() { return _remoteIP; };
// Return the port of the host who sent the current incoming packet
uint16_t remotePort() { return _remotePort; };
}; };
#endif #endif

View File

@ -22,15 +22,10 @@
// The IP address will be dependent on your local network: // The IP address will be dependent on your local network:
byte mac[] = { byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { IPAddress ip(192, 168, 1, 177);
192,168,1,177 };
unsigned int localPort = 8888; // local port to listen on 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 // buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
char ReplyBuffer[] = "acknowledged"; // a string to send back char ReplyBuffer[] = "acknowledged"; // a string to send back
@ -48,19 +43,33 @@ void setup() {
void loop() { void loop() {
// if there's data available, read a packet // 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) if(packetSize)
{ {
packetSize = packetSize - 8; // subtract the 8 byte header
Serial.print("Received packet of size "); Serial.print("Received packet of size ");
Serial.println(packetSize); Serial.println(packetSize);
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 and get the senders IP addr and port number // read the packet into packetBufffer
Udp.readPacket(packetBuffer,UDP_TX_PACKET_MAX_SIZE, remoteIp, remotePort); Udp.read((byte*)packetBuffer,UDP_TX_PACKET_MAX_SIZE);
Serial.println("Contents:"); Serial.println("Contents:");
Serial.println(packetBuffer); 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); delay(10);
} }

View File

@ -24,14 +24,12 @@
// The IP address will be dependent on your local network: // The IP address will be dependent on your local network:
byte mac[] = { byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 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 for UDP packets unsigned int localPort = 8888; // local port to listen for UDP packets
byte timeServer[] = { IPAddress timeServer(192, 43, 244, 18); // time.nist.gov NTP server
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 const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
@ -55,8 +53,9 @@ void loop()
// wait to see if a reply is available // wait to see if a reply is available
delay(1000); delay(1000);
if ( Udp.available() ) { if ( Udp.parsePacket() ) {
Udp.readPacket(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer // 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, //the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words: // or two words, long. First, esxtract the two words:
@ -118,7 +117,9 @@ unsigned long sendNTPpacket(byte *address)
// all NTP fields have been given values, now // all NTP fields have been given values, now
// you can send a packet requesting a timestamp: // 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();
} }

View File

@ -9,6 +9,7 @@
Ethernet KEYWORD1 Ethernet KEYWORD1
Client KEYWORD1 Client KEYWORD1
Server KEYWORD1 Server KEYWORD1
IPAddress KEYWORD1
####################################### #######################################
# Methods and Functions (KEYWORD2) # Methods and Functions (KEYWORD2)
@ -19,10 +20,16 @@ connect KEYWORD2
write KEYWORD2 write KEYWORD2
available KEYWORD2 available KEYWORD2
read KEYWORD2 read KEYWORD2
peek KEYWORD2
flush KEYWORD2 flush KEYWORD2
stop KEYWORD2 stop KEYWORD2
connected KEYWORD2 connected KEYWORD2
begin KEYWORD2 begin KEYWORD2
beginPacket KEYWORD2
endPacket KEYWORD2
parsePacket KEYWORD2
remoteIP KEYWORD2
remotePort KEYWORD2
####################################### #######################################
# Constants (LITERAL1) # Constants (LITERAL1)

View File

@ -344,3 +344,58 @@ uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len)
return ret; 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;
}

View File

@ -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); 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 #endif
/* _SOCKET_H_ */ /* _SOCKET_H_ */

View File

@ -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)
{
// 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); uint16_t ptr = readSnTX_WR(s);
ptr += data_offset;
uint16_t offset = ptr & SMASK; uint16_t offset = ptr & SMASK;
uint16_t dstAddr = offset + SBASE[s]; uint16_t dstAddr = offset + SBASE[s];
@ -132,7 +138,7 @@ uint8_t W5100Class::write(uint16_t _addr, uint8_t _data)
return 1; 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++) for (int i=0; i<_len; i++)
{ {

View File

@ -146,7 +146,19 @@ public:
* This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer * 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. * 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. * @brief This function is being called by recv() also.
@ -182,7 +194,7 @@ public:
// --------------- // ---------------
private: private:
static uint8_t write(uint16_t _addr, uint8_t _data); 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 uint8_t read(uint16_t addr);
static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len); static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len);