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;
}
/* 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);
}
/* 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 */
void UDP::stop()
{
@ -159,3 +74,93 @@ void UDP::stop()
_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
#define udp_h
#include <Stream.h>
#include <IPAddress.h>
#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
// 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 &);
// 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);
// Finish with the UDP socket
void stop();
// 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);
// 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

View File

@ -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);
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
Udp.readPacket(packetBuffer,UDP_TX_PACKET_MAX_SIZE, remoteIp, remotePort);
// read the packet into packetBufffer
Udp.read((byte*)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);
}

View File

@ -24,14 +24,12 @@
// 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 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
@ -55,8 +53,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:
@ -118,7 +117,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();
}

View File

@ -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)

View File

@ -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;
}

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);
// 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_ */

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);
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++)
{

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
* 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 @@ public:
// ---------------
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);